async def run_ddl_command(self, ddl_plan): schema = await self.getschema() if debug.flags.delta_plan_input: debug.header('Delta Plan Input') debug.dump(ddl_plan) # Do a dry-run on test_schema to canonicalize # the schema delta-commands. test_schema = await self._intro_mech.readschema() context = sd.CommandContext() canonical_ddl_plan = ddl_plan.copy() canonical_ddl_plan.apply(test_schema, context=context) # Apply and adapt delta, build native delta plan, which # will also update the schema. plan = self.process_delta(canonical_ddl_plan, schema) context = delta_cmds.CommandContext(self.connection) try: if not isinstance(plan, (s_db.CreateDatabase, s_db.DropDatabase)): async with self.connection.transaction(): # Execute all pgsql/delta commands. await plan.execute(context) else: await plan.execute(context) except Exception as e: raise RuntimeError('failed to apply delta to data backend') from e finally: # Exception or not, re-read the schema from Postgres. await self.invalidate_schema_cache() await self.getschema()
async def run_ddl_command(self, ddl_plan): schema = self.schema if debug.flags.delta_plan_input: debug.header('Delta Plan Input') debug.dump(ddl_plan) # Do a dry-run on test_schema to canonicalize # the schema delta-commands. test_schema = schema context = self.create_context() canonical_ddl_plan = ddl_plan.copy() canonical_ddl_plan.apply(test_schema, context=context) # Apply and adapt delta, build native delta plan, which # will also update the schema. schema, plan = self.process_delta(canonical_ddl_plan, schema) context = self.create_context(delta_cmds) if isinstance(plan, (s_db.CreateDatabase, s_db.DropDatabase)): block = dbops.SQLBlock() else: block = dbops.PLTopBlock() plan.generate(block) ql_text = block.to_string() await self._execute_ddl(ql_text) self.schema = schema
async def execute(self, context): code, vars = await self.get_code_and_vars(context) if code: extra = await self.extra(context) extra_before = extra_after = None if isinstance(extra, dict): extra_before = extra.get('before') extra_after = extra.get('after') else: extra_after = extra if extra_before: for cmd in extra_before: await cmd.execute(context) if debug.flags.delta_execute: debug.header('Executing DDL') debug.print(repr(self)) debug.print('CODE:', code) debug.print('VARS:', vars) stmt = await context.db.prepare(code) result = await stmt.fetch(*vars) if extra_after: for cmd in extra_after: await cmd.execute(context) return result
def _compile_and_apply_ddl_command(self, ctx: CompileContext, cmd): current_tx = ctx.state.current_tx() schema = current_tx.get_schema() if debug.flags.delta_plan_input: debug.header('Delta Plan Input') debug.dump(cmd) # Do a dry-run on test_schema to canonicalize # the schema delta-commands. test_schema = schema context = self._new_delta_context(ctx) cmd.apply(test_schema, context=context) # Apply and adapt delta, build native delta plan, which # will also update the schema. schema, plan = self._process_delta(ctx, cmd, schema) if isinstance(plan, (s_db.CreateDatabase, s_db.DropDatabase)): block = pg_dbops.SQLBlock() else: block = pg_dbops.PLTopBlock() plan.generate(block) sql = block.to_string().encode('utf-8') current_tx.update_schema(schema) return dbstate.DDLQuery(sql=sql)
async def _load_std(self): schema = s_schema.Schema() current_block = None std_texts = [] for modname in s_schema.STD_LIB + ['stdgraphql']: std_texts.append(s_std.get_std_module_text(modname)) ddl_text = '\n'.join(std_texts) for ddl_cmd in edgeql.parse_block(ddl_text): delta_command = s_ddl.delta_from_ddl( ddl_cmd, schema=schema, modaliases={None: 'std'}, stdmode=True) if debug.flags.delta_plan_input: debug.header('Delta Plan Input') debug.dump(delta_command) # Do a dry-run on test_schema to canonicalize # the schema delta-commands. test_schema = schema context = self.create_context(stdmode=True) canonical_delta = delta_command.copy() canonical_delta.apply(test_schema, context=context) # Apply and adapt delta, build native delta plan, which # will also update the schema. schema, plan = self.process_delta(canonical_delta, schema, stdmode=True) if isinstance(plan, (s_db.CreateDatabase, s_db.DropDatabase)): if (current_block is not None and not isinstance(current_block, dbops.SQLBlock)): raise errors.QueryError( 'cannot mix DATABASE commands with regular DDL ' 'commands in a single block') if current_block is None: current_block = dbops.SQLBlock() else: if (current_block is not None and not isinstance(current_block, dbops.PLTopBlock)): raise errors.QueryError( 'cannot mix DATABASE commands with regular DDL ' 'commands in a single block') if current_block is None: current_block = dbops.PLTopBlock() plan.generate(current_block) sql_text = current_block.to_string() return schema, sql_text
async def _execute_ddl(self, sql_text): try: if debug.flags.delta_execute: debug.header('Delta Script') debug.dump_code(sql_text, lexer='sql') await self.connection.execute(sql_text) except Exception as e: position = getattr(e, 'position', None) internal_position = getattr(e, 'internal_position', None) context = getattr(e, 'context', '') if context: pl_func_line = re.search( r'^PL/pgSQL function inline_code_block line (\d+).*', context, re.M) if pl_func_line: pl_func_line = int(pl_func_line.group(1)) else: pl_func_line = None point = None if position is not None: position = int(position) point = parser_context.SourcePoint( None, None, position) text = e.query if text is None: # Parse errors text = sql_text elif internal_position is not None: internal_position = int(internal_position) point = parser_context.SourcePoint( None, None, internal_position) text = e.internal_query elif pl_func_line: point = parser_context.SourcePoint( pl_func_line, None, None ) text = sql_text if point is not None: context = parser_context.ParserContext( 'query', text, start=point, end=point) exceptions.replace_context(e, context) raise
def process_delta(self, delta, schema, *, stdmode=None): """Adapt and process the delta command.""" if debug.flags.delta_plan: debug.header('Delta Plan') debug.dump(delta, schema=schema) delta = self.adapt_delta(delta) context = self.create_context(delta_cmds, stdmode) schema, _ = delta.apply(schema, context) if debug.flags.delta_pgsql_plan: debug.header('PgSQL Delta Plan') debug.dump(delta, schema=schema) return schema, delta
def process_delta(self, delta, schema): """Adapt and process the delta command.""" if debug.flags.delta_plan: debug.header('Delta Plan') debug.dump(delta) delta = self.adapt_delta(delta) context = delta_cmds.CommandContext(self.connection) delta.apply(schema, context) if debug.flags.delta_pgsql_plan: debug.header('PgSQL Delta Plan') debug.dump(delta) return delta
def _process_delta(self, ctx: CompileContext, delta, schema): """Adapt and process the delta command.""" if debug.flags.delta_plan: debug.header('Delta Plan') debug.dump(delta, schema=schema) delta = pg_delta.CommandMeta.adapt(delta) context = self._new_delta_context(ctx) schema, _ = delta.apply(schema, context) if debug.flags.delta_pgsql_plan: debug.header('PgSQL Delta Plan') debug.dump(delta, schema=schema) return schema, delta
def compile_ir_to_sql( ir_expr: irast.Base, *, schema: s_schema.Schema, output_format: typing.Optional[OutputFormat] = None, ignore_shapes: bool = False, timer=None, use_named_params: bool = False, pretty: bool = True) -> typing.Tuple[str, typing.Dict[str, int]]: if timer is None: qtree = compile_ir_to_sql_tree(ir_expr, schema=schema, output_format=output_format, ignore_shapes=ignore_shapes, use_named_params=use_named_params) else: with timer.timeit('compile_ir_to_sql'): qtree = compile_ir_to_sql_tree(ir_expr, schema=schema, output_format=output_format, ignore_shapes=ignore_shapes, use_named_params=use_named_params) if debug.flags.edgeql_compile: # pragma: no cover debug.header('SQL Tree') debug.dump(qtree, schema=schema) argmap = qtree.argnames # Generate query text if timer is None: codegen = _run_codegen(qtree, pretty=pretty) else: with timer.timeit('compile_ir_to_sql'): codegen = _run_codegen(qtree, pretty=pretty) sql_text = ''.join(codegen.result) if debug.flags.edgeql_compile: # pragma: no cover debug.header('SQL') debug.dump_code(sql_text, lexer='sql') return sql_text, argmap
def compile_to_ir(expr, schema, *, anchors=None, security_context=None, modaliases=None, implicit_id_in_shapes=False): """Compile given EdgeQL statement into EdgeDB IR.""" if debug.flags.edgeql_compile: debug.header('EdgeQL TEXT') debug.print(expr) tree = ql_parser.parse(expr, modaliases) return compile_ast_to_ir(tree, schema, anchors=anchors, security_context=security_context, modaliases=modaliases, implicit_id_in_shapes=implicit_id_in_shapes)
def send_error(self, err): try: srcctx = exceptions.get_context(err, parsing.ParserContext) except LookupError: srcctx = None try: hintctx = exceptions.get_context( err, exceptions.DefaultExceptionContext) except LookupError: hintctx = None if debug.flags.server: debug.header('Error') debug.dump(err) self.send_message({ '__type__': 'error', 'data': { 'C': getattr(err, 'code', 0), 'M': str(err), 'D': hintctx.details if hintctx is not None else None, 'H': hintctx.hint if hintctx is not None else None, 'P': (srcctx.start.pointer if srcctx is not None and srcctx.start is not None else None), 'p': (srcctx.end.pointer if srcctx is not None and srcctx.end is not None else None), 'Q': markup.dumps(srcctx) if srcctx is not None else None, 'T': traceback.format_tb(err.__traceback__), } })
def compile_ast_to_ir(tree, schema, *, anchors=None, singletons=None, func=None, security_context=None, derived_target_module=None, result_view_name=None, modaliases=None, implicit_id_in_shapes=False, schema_view_mode=False): """Compile given EdgeQL AST into EdgeDB IR.""" if debug.flags.edgeql_compile: debug.header('EdgeQL AST') debug.dump(tree, schema=schema) ctx = stmtctx.init_context(schema=schema, anchors=anchors, singletons=singletons, modaliases=modaliases, security_context=security_context, func=func, derived_target_module=derived_target_module, result_view_name=result_view_name, implicit_id_in_shapes=implicit_id_in_shapes, schema_view_mode=schema_view_mode) ir_set = dispatch.compile(tree, ctx=ctx) ir_expr = stmtctx.fini_expression(ir_set, ctx=ctx) if ctx.env.query_parameters: first_argname = next(iter(ctx.env.query_parameters)) if first_argname.isdecimal(): args_decnames = {int(arg) for arg in ctx.env.query_parameters} args_tpl = set(range(len(ctx.env.query_parameters))) if args_decnames != args_tpl: missing_args = args_tpl - args_decnames missing_args_repr = ', '.join(f'${a}' for a in missing_args) raise errors.QueryError( f'missing {missing_args_repr} positional argument' f'{"s" if len(missing_args) > 1 else ""}') if debug.flags.edgeql_compile: debug.header('Scope Tree') if ctx.path_scope is not None: print(ctx.path_scope.pdebugformat()) else: print('N/A') debug.header('EdgeDB IR') debug.dump(ir_expr, schema=getattr(ir_expr, 'schema', None)) return ir_expr
def compile_ast_to_ir(tree, schema, *, anchors=None, arg_types=None, security_context=None, derived_target_module=None, result_view_name=None, modaliases=None, implicit_id_in_shapes=False): """Compile given EdgeQL AST into EdgeDB IR.""" if debug.flags.edgeql_compile: debug.header('EdgeQL AST') debug.dump(tree) ctx = stmtctx.init_context(schema=schema, anchors=anchors, modaliases=modaliases, security_context=security_context, arg_types=arg_types, derived_target_module=derived_target_module, result_view_name=result_view_name, implicit_id_in_shapes=implicit_id_in_shapes) ir_set = dispatch.compile(tree, ctx=ctx) ir_expr = stmtctx.fini_expression(ir_set, ctx=ctx) if debug.flags.edgeql_compile: debug.header('Scope Tree') if ctx.path_scope is not None: print(ctx.path_scope.pdebugformat()) else: print('N/A') debug.header('EdgeDB IR') debug.dump(ir_expr) return ir_expr
def compile_func_to_ir(func, schema, *, anchors=None, security_context=None, modaliases=None, implicit_id_in_shapes=False): """Compile an EdgeQL function into EdgeDB IR.""" if debug.flags.edgeql_compile: debug.header('EdgeQL Function') debug.print(func.get_code(schema)) trees = ql_parser.parse_block(func.get_code(schema) + ';') if len(trees) != 1: raise errors.InvalidFunctionDefinitionError( 'functions can only contain one statement') tree = trees[0] if modaliases: ql_parser.append_module_aliases(tree, modaliases) if anchors is None: anchors = {} anchors['__defaults_mask__'] = irast.Parameter( name='__defaults_mask__', stype=schema.get('std::bytes')) func_params = func.get_params(schema) pg_params = s_func.PgParams.from_params(schema, func_params) for pi, p in enumerate(pg_params.params): p_shortname = p.get_shortname(schema) anchors[p_shortname] = irast.Parameter(name=p_shortname, stype=p.get_type(schema)) if p.get_default(schema) is None: continue tree.aliases.append( qlast.AliasedExpr( alias=p_shortname, expr=qlast. IfElse(condition=qlast.BinOp(left=qlast.FunctionCall( func=('std', 'bytes_get_bit'), args=[ qlast.FuncArg(arg=qlast.Path( steps=[qlast.ObjectRef( name='__defaults_mask__')])), qlast.FuncArg(arg=qlast.IntegerConstant(value=str(pi))) ]), right=qlast.IntegerConstant( value='0'), op='='), if_expr=qlast.Path( steps=[qlast.ObjectRef(name=p_shortname)]), else_expr=qlast._Optional( expr=p.get_ql_default(schema))))) ir = compile_ast_to_ir(tree, schema, anchors=anchors, func=func, security_context=security_context, modaliases=modaliases, implicit_id_in_shapes=implicit_id_in_shapes) return ir
async def _execute_block(conn, block: dbops.PLBlock) -> None: sql_text = block.to_string() if debug.flags.bootstrap: debug.header('Bootstrap') debug.dump_code(sql_text, lexer='sql') await _execute(conn, sql_text)