Skip to content

Commit

Permalink
PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
fantix committed Dec 13, 2023
1 parent 2e2341a commit 22e1993
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 1 deletion.
10 changes: 9 additions & 1 deletion edb/pgsql/compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class CompileResult:

argmap: Dict[str, pgast.Param]

detached_params: Optional[List[Tuple[str, ...]]] = None


def compile_ir_to_sql_tree(
ir_expr: irast.Base,
Expand All @@ -70,6 +72,7 @@ def compile_ir_to_sql_tree(
]
] = None,
backend_runtime_params: Optional[pgparams.BackendRuntimeParams]=None,
detach_params: bool = False,
) -> CompileResult:
try:
# Transform to sql tree
Expand Down Expand Up @@ -117,6 +120,7 @@ def compile_ir_to_sql_tree(
scope_tree_nodes=scope_tree_nodes,
external_rvars=external_rvars,
backend_runtime_params=backend_runtime_params,
detach_params=detach_params,
)

ctx = context.CompilerContextLevel(
Expand Down Expand Up @@ -144,6 +148,8 @@ def compile_ir_to_sql_tree(
assert isinstance(qtree, pgast.Query)
clauses.fini_toplevel(qtree, ctx)

detached_params = [p for _, p in sorted(ctx.detached_params.items())]

except errors.EdgeDBError:
# Don't wrap propertly typed EdgeDB errors into
# InternalServerError; raise them as is.
Expand All @@ -156,7 +162,9 @@ def compile_ir_to_sql_tree(
args = []
raise errors.InternalServerError(*args) from e

return CompileResult(ast=qtree, env=env, argmap=ctx.argmap)
return CompileResult(
ast=qtree, env=env, argmap=ctx.argmap, detached_params=detached_params
)


def new_external_rvar(
Expand Down
7 changes: 7 additions & 0 deletions edb/pgsql/compiler/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ class CompilerContextLevel(compiler.ContextLevel):
#: needed by DML.
shapes_needed_by_dml: Set[irast.Set]

detached_params: Dict[int, Tuple[str, ...]]

def __init__(
self,
prevlevel: Optional[CompilerContextLevel],
Expand Down Expand Up @@ -349,6 +351,7 @@ def __init__(
self.shapes_needed_by_dml = set()

self.trigger_mode = False
self.detached_params = {}

else:
self.env = prevlevel.env
Expand Down Expand Up @@ -387,6 +390,7 @@ def __init__(
self.external_rels = prevlevel.external_rels

self.trigger_mode = prevlevel.trigger_mode
self.detached_params = prevlevel.detached_params

if mode is ContextSwitchMode.SUBSTMT:
if self.pending_query is not None:
Expand Down Expand Up @@ -489,6 +493,7 @@ class Environment:
external_rvars: Mapping[Tuple[irast.PathId, str], pgast.PathRangeVar]
materialized_views: Dict[uuid.UUID, irast.Set]
backend_runtime_params: pgparams.BackendRuntimeParams
detach_params: bool

#: A list of CTEs that implement constraint validation at the
#: query level.
Expand All @@ -511,6 +516,7 @@ def __init__(
Mapping[Tuple[irast.PathId, str], pgast.PathRangeVar]
] = None,
backend_runtime_params: pgparams.BackendRuntimeParams,
detach_params: bool = False,
) -> None:
self.aliases = aliases.AliasGenerator()
self.output_format = output_format
Expand All @@ -528,6 +534,7 @@ def __init__(
self.materialized_views = {}
self.check_ctes = []
self.backend_runtime_params = backend_runtime_params
self.detach_params = detach_params


# XXX: this context hack is necessary until pathctx is converted
Expand Down
4 changes: 4 additions & 0 deletions edb/pgsql/compiler/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ def compile_Parameter(
else:
index = ctx.argmap[expr.name].index
result = pgast.ParamRef(number=index, nullable=not expr.required)
if ctx.env.detach_params:
ctx.detached_params[index] = pg_types.pg_type_from_ir_typeref(
expr.typeref)
return result

return pgast.TypeCast(
arg=result,
Expand Down
12 changes: 12 additions & 0 deletions edb/pgsql/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,16 @@ def _setup_patches(patches: list[tuple[str, str]]) -> list[tuple[str, str]]:
* repair - fix up inconsistencies in *user* schemas
"""
PATCHES: list[tuple[str, str]] = _setup_patches([
(
"sql",
"""
CREATE TABLE IF NOT EXISTS "edgedb"."_query_cache" (
"key" bigint NOT NULL,
"schema_version" uuid NOT NULL,
"input" bytea NOT NULL,
"output" bytea NOT NULL,
PRIMARY KEY ("key", "schema_version")
)
"""
),
])
66 changes: 66 additions & 0 deletions edb/server/compiler/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1715,18 +1715,82 @@ def _compile_ql_query(

result_cardinality = enums.cardinality_from_ir_value(ir.cardinality)

# This low-hanging-fruit is temporary; persistent cache should cover all
# cases properly in the following changes.
use_persistent_cache = script_info is None and cacheable

sql_res = pg_compiler.compile_ir_to_sql_tree(
ir,
expected_cardinality_one=ctx.expected_cardinality_one,
output_format=_convert_format(ctx.output_format),
backend_runtime_params=ctx.backend_runtime_params,
expand_inhviews=options.expand_inhviews,
detach_params=use_persistent_cache,
)

sql_text = pg_codegen.generate_source(sql_res.ast)

pg_debug.dump_ast_and_query(sql_res.ast, ir)

cache_sql = None
if use_persistent_cache:

func = pg_dbops.Function(
name=('edgedb', '__qh_PLACEHOLDER'),
args=[(None, arg) for arg in sql_res.detached_params],
returns='',
text=sql_text,
)

match ctx.output_format:
case enums.OutputFormat.NONE:
func.returns = 'void'

case enums.OutputFormat.BINARY:
func.returns = 'record',
func.set_returning = ir.cardinality.is_multi()

case enums.OutputFormat.JSON:
func.returns = 'json'

case enums.OutputFormat.JSON_ELEMENTS:
func.returns = 'json'
func.set_returning = True

if not ir.dml_exprs:
func.volatility = 'stable'

df = pg_dbops.SQLBlock()
pg_dbops.DropFunction(
name=func.name, args=func.args or (), if_exists=True
).generate(df)

cf = pg_dbops.SQLBlock()
pg_dbops.CreateFunction(func).generate(cf)

sql_text = pg_codegen.generate_source(
pgast.SelectStmt(
target_list=[
pgast.FuncCall(
name=('edgedb', '__qh_PLACEHOLDER'),
args=[
pgast.TypeCast(
arg=pgast.ParamRef(number=i),
type_name=pgast.TypeName(
name=arg
)
)
for i, arg in enumerate(sql_res.detached_params, 1)
],
)
]
)
)
cache_sql = (
cf.to_string().encode(defines.EDGEDB_ENCODING),
df.to_string().encode(defines.EDGEDB_ENCODING),
)

if (
(mstate := current_tx.get_migration_state())
and not migration_block_query
Expand Down Expand Up @@ -1790,6 +1854,7 @@ def _compile_ql_query(
return dbstate.Query(
sql=(sql_bytes,),
sql_hash=sql_hash,
cache_sql=cache_sql,
cardinality=result_cardinality,
globals=globals,
in_type_id=in_type_id.bytes,
Expand Down Expand Up @@ -2415,6 +2480,7 @@ def _try_compile(

if isinstance(comp, dbstate.Query):
unit.sql = comp.sql
unit.cache_sql = comp.cache_sql
unit.globals = comp.globals
unit.in_type_args = comp.in_type_args

Expand Down
5 changes: 5 additions & 0 deletions edb/server/compiler/dbstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ class MigrationAction(enum.IntEnum):
class BaseQuery:

sql: Tuple[bytes, ...]
cache_sql: Optional[Tuple[bytes, bytes]] = dataclasses.field(
kw_only=True, default=None
) # (persist, evict)

@property
def is_transactional(self) -> bool:
Expand Down Expand Up @@ -216,6 +219,8 @@ class QueryUnit:
# EdgeQL queries, the status reflects the last query in the unit.
status: bytes

cache_sql: Optional[Tuple[bytes, bytes]] = None # (persist, evict)

# Output format of this query unit
output_format: enums.OutputFormat = enums.OutputFormat.NONE

Expand Down
4 changes: 4 additions & 0 deletions edb/server/protocol/execute.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ async def execute(
else:
config_ops = query_unit.config_ops

if query_unit.cache_sql:
# PoC
await be_conn.sql_execute(query_unit.cache_sql[1])
await be_conn.sql_execute(query_unit.cache_sql[0])
if query_unit.sql:
if query_unit.ddl_stmt_id:
ddl_ret = await be_conn.run_ddl(query_unit, state)
Expand Down

0 comments on commit 22e1993

Please sign in to comment.