def create_constr_trigger(self, table_name, constraint, proc_name): cmds = [] cname = constraint.raw_constraint_name() ins_trigger_name = common.edgedb_name_to_pg_name(cname + '_instrigger') ins_trigger = dbops.Trigger(name=ins_trigger_name, table_name=table_name, events=('insert', ), procedure=proc_name, is_constraint=True, inherit=True) cr_ins_trigger = dbops.CreateTrigger(ins_trigger) cmds.append(cr_ins_trigger) disable_ins_trigger = dbops.DisableTrigger(ins_trigger, self_only=True) cmds.append(disable_ins_trigger) upd_trigger_name = common.edgedb_name_to_pg_name(cname + '_updtrigger') condition = constraint.get_trigger_condition() upd_trigger = dbops.Trigger(name=upd_trigger_name, table_name=table_name, events=('update', ), procedure=proc_name, condition=condition, is_constraint=True, inherit=True) cr_upd_trigger = dbops.CreateTrigger(upd_trigger) cmds.append(cr_upd_trigger) disable_upd_trigger = dbops.DisableTrigger(upd_trigger, self_only=True) cmds.append(disable_upd_trigger) return cmds
def _compile_set_in_singleton_mode( node: irast.Set, *, ctx: context.CompilerContextLevel) -> pgast.Base: if isinstance(node, irast.EmptySet): return pgast.Constant(value=None) elif node.expr is not None: return dispatch.compile(node.expr, ctx=ctx) else: if node.rptr: ptrcls = node.rptr.ptrcls source = node.rptr.source if not ptrcls.is_link_property(): if source.rptr: raise RuntimeError('unexpectedly long path in simple expr') colref = pgast.ColumnRef( name=[common.edgedb_name_to_pg_name(ptrcls.shortname)]) elif isinstance(node.scls, s_scalars.ScalarType): colref = pgast.ColumnRef( name=[common.edgedb_name_to_pg_name(node.scls.name)]) else: colref = pgast.ColumnRef( name=[common.edgedb_name_to_pg_name(node.scls.name)]) return colref
def _new_mapped_pointer_rvar( ir_ptr: irast.Pointer, nullable: bool=False, *, ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar: ptrcls = ir_ptr.ptrcls ptr_rvar = dbobj.range_for_pointer(ir_ptr, env=ctx.env) ptr_rvar.nullable = nullable # Set up references according to the link direction. if isinstance(ptrcls, s_links.Link): # XXX: fix this once Properties are Sources src_ptr_info = pg_types.get_pointer_storage_info( ptrcls.getptr(ctx.env.schema, 'std::source'), resolve_type=False) src_col = src_ptr_info.column_name else: src_col = common.edgedb_name_to_pg_name('std::source') source_ref = dbobj.get_column(None, src_col) if isinstance(ptrcls, s_links.Link): # XXX: fix this once Properties are Sources tgt_ptr_info = pg_types.get_pointer_storage_info( ptrcls.getptr(ctx.env.schema, 'std::target'), resolve_type=False) tgt_col = tgt_ptr_info.column_name else: tgt_col = common.edgedb_name_to_pg_name('std::target') target_ref = dbobj.get_column(None, tgt_col) if ir_ptr.direction == s_pointers.PointerDirection.Inbound: near_ref = target_ref far_ref = source_ref else: near_ref = source_ref far_ref = target_ref ptr_rvar.query.path_id = ir_ptr.target.path_id.ptr_path() ptr_rvar.path_scope.add(ptr_rvar.query.path_id) src_pid = ir_ptr.source.path_id tgt_pid = ir_ptr.target.path_id ptr_rvar.path_scope.add(src_pid) pathctx.put_rvar_path_output(ptr_rvar, src_pid, aspect='identity', var=near_ref, env=ctx.env) pathctx.put_rvar_path_output(ptr_rvar, src_pid, aspect='value', var=near_ref, env=ctx.env) pathctx.put_rvar_path_output(ptr_rvar, tgt_pid, aspect='value', var=far_ref, env=ctx.env) if tgt_pid.is_objtype_path(): ptr_rvar.path_scope.add(tgt_pid) pathctx.put_rvar_path_output(ptr_rvar, tgt_pid, aspect='identity', var=far_ref, env=ctx.env) return ptr_rvar
def get_id(self): raw_name = self.constraint.raw_constraint_name() name = common.edgedb_name_to_pg_name('{}#{}'.format( raw_name, self.index)) name = common.quote_ident(name) return '{} ON {} {}'.format(name, self.constraint.get_subject_type(), self.constraint.get_subject_name())
def process_linkprop_update(ir_stmt: irast.MutatingStmt, ir_expr: irast.Base, wrapper: pgast.Query, dml_cte: pgast.CommonTableExpr, *, ctx: context.CompilerContextLevel) -> None: """Perform link property updates to a link relation. :param ir_stmt: IR of the statement. :param ir_expr: IR of the UPDATE body element. :param wrapper: Top-level SQL query. :param dml_cte: CTE representing the SQL UPDATE to the main relation of the Object. """ toplevel = ctx.toplevel_stmt rptr = ir_expr.rptr ptrcls = rptr.ptrcls target_tab = dbobj.range_for_ptrcls(ptrcls, '>', include_overlays=False, env=ctx.env) dml_cte_rvar = pgast.RangeVar( relation=dml_cte, alias=pgast.Alias(aliasname=ctx.env.aliases.get('m'))) cond = astutils.new_binop( pathctx.get_rvar_path_identity_var(dml_cte_rvar, ir_stmt.subject.path_id, env=ctx.env), dbobj.get_column(target_tab, 'std::source', nullable=False), ast.ops.EQ) targets = [] for prop_el in ir_expr.shape: ptrname = prop_el.rptr.ptrcls.shortname with ctx.new() as input_rel_ctx: input_rel_ctx.expr_exposed = False input_rel = dispatch.compile(prop_el.expr, ctx=input_rel_ctx) targets.append( pgast.UpdateTarget(name=common.edgedb_name_to_pg_name(ptrname), val=input_rel)) updstmt = pgast.UpdateStmt(relation=target_tab, where_clause=cond, targets=targets, from_clause=[dml_cte_rvar]) updcte = pgast.CommonTableExpr(query=updstmt, name=ctx.env.aliases.get( ptrcls.shortname.name)) toplevel.ctes.append(updcte)
def cols_for_pointer(pointer: s_pointers.Pointer, *, env: context.Environment) -> typing.List[str]: cols = ['ptr_item_id'] if isinstance(pointer, s_links.Link): for ptr in pointer.pointers.values(): cols.append(common.edgedb_name_to_pg_name(ptr.shortname)) else: cols.extend(('std::source', 'std::target')) return cols
def new_static_class_rvar( ir_set: irast.Set, *, lateral: bool=True, ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar: set_rvar = new_root_rvar(ir_set, ctx=ctx) clsname = pgast.Constant(val=ir_set.rptr.source.scls.material_type().name) nameref = dbobj.get_column( set_rvar, common.edgedb_name_to_pg_name('schema::name')) condition = astutils.new_binop(nameref, clsname, op='=') substmt = pgast.SelectStmt() include_rvar(substmt, set_rvar, ir_set.path_id, ctx=ctx) substmt.where_clause = astutils.extend_binop( substmt.where_clause, condition) return new_rel_rvar(ir_set, substmt, ctx=ctx)
def _compile_set_in_singleton_mode( node: irast.Set, *, ctx: context.CompilerContextLevel) -> pgast.Base: if isinstance(node, irast.EmptySet): return pgast.NullConstant() elif node.expr is not None: return dispatch.compile(node.expr, ctx=ctx) else: if node.rptr: ptrcls = node.rptr.ptrcls source = node.rptr.source if not ptrcls.is_link_property(ctx.env.schema): if source.rptr: raise RuntimeError( 'unexpectedly long path in simple expr') ptr_stor_info = pg_types.get_pointer_storage_info( ptrcls, schema=ctx.env.schema, resolve_type=False) colref = pgast.ColumnRef(name=[ptr_stor_info.column_name]) elif isinstance(node.stype, s_scalars.ScalarType): colref = pgast.ColumnRef( name=[ common.edgedb_name_to_pg_name( node.stype.get_name(ctx.env.schema)) ] ) else: colref = pgast.ColumnRef( name=[ common.edgedb_name_to_pg_name( node.stype.get_name(ctx.env.schema)) ] ) return colref
def drop_constr_trigger(self, table_name, constraint): cname = constraint.raw_constraint_name() ins_trigger_name = common.edgedb_name_to_pg_name(cname + '_instrigger') ins_trigger = dbops.Trigger(name=ins_trigger_name, table_name=table_name, events=('insert', ), procedure='null', is_constraint=True, inherit=True) drop_ins_trigger = dbops.DropTrigger(ins_trigger) upd_trigger_name = common.edgedb_name_to_pg_name(cname + '_updtrigger') upd_trigger = dbops.Trigger(name=upd_trigger_name, table_name=table_name, events=('update', ), procedure='null', is_constraint=True, inherit=True) drop_upd_trigger = dbops.DropTrigger(upd_trigger) return [drop_ins_trigger, drop_upd_trigger]
def rename_constr_trigger(self, table_name): constraint = self._constraint new_constr = self._new_constraint cname = constraint.raw_constraint_name() ncname = new_constr.raw_constraint_name() ins_trigger_name = common.edgedb_name_to_pg_name(cname + '_instrigger') new_ins_trg_name = common.edgedb_name_to_pg_name(ncname + '_instrigger') ins_trigger = dbops.Trigger(name=ins_trigger_name, table_name=table_name, events=('insert', ), procedure='null', is_constraint=True, inherit=True) rn_ins_trigger = dbops.AlterTriggerRenameTo(ins_trigger, new_name=new_ins_trg_name) upd_trigger_name = common.edgedb_name_to_pg_name(cname + '_updtrigger') new_upd_trg_name = common.edgedb_name_to_pg_name(ncname + '_updtrigger') upd_trigger = dbops.Trigger(name=upd_trigger_name, table_name=table_name, events=('update', ), procedure='null', is_constraint=True, inherit=True) rn_upd_trigger = dbops.AlterTriggerRenameTo(upd_trigger, new_name=new_upd_trg_name) return (rn_ins_trigger, rn_upd_trigger)
def __init__(self, *, scls, field, expr, conditions=None, neg_conditions=None, priority=0): super().__init__(conditions=conditions, neg_conditions=neg_conditions, priority=priority) self.name = scls.name self.table = metaschema.get_metaclass_table(scls.__class__) self.field = common.edgedb_name_to_pg_name(field) self.expr = expr
def drop_constraint(self, constraint): if not constraint.is_natively_inherited(): self.add_commands(self.drop_constr_trigger(self.name, constraint)) # Drop trigger function # proc_name = constraint.raw_constraint_name() + '_trigproc' proc_name = self.name[0], common.edgedb_name_to_pg_name(proc_name) self.add_commands(self.drop_constr_trigger_function(proc_name)) # Drop the constraint normally from our table # my_alter = dbops.AlterTable(self.name) drop_constr = AlterTableDropMultiConstraint(constraint=constraint) my_alter.add_command(drop_constr) self.add_command(my_alter)
async def creation_code(self, context): link_map = await context.get_class_map() ids = tuple(sorted(list(link_map[n] for n in self.link_names))) id_str = '_'.join(str(i) for i in ids) name = '%s_%s_%s_cardinality_idx' % (self.name_prefix, id_str, self.cardinality) name = common.edgedb_name_to_pg_name(name) predicate = 'ptr_item_id IN (%s)' % ', '.join(str(id) for id in ids) code = ''' CREATE {unique} INDEX {name} ON {table}s ({cols}) {predicate} '''.format(unique='UNIQUE', name=common.qname(name), table=common.qname(*self.table_name), cols=', '.join(common.quote_ident(c) for c in self.columns), predicate=('WHERE {}'.format(predicate))) return code
def compile_FunctionCall(expr: irast.Base, *, ctx: context.CompilerContextLevel) -> pgast.Base: funcobj = expr.func if funcobj.aggregate: raise RuntimeError( 'aggregate functions are not supported in simple expressions') if funcobj.set_returning: raise RuntimeError( 'set returning functions are not supported in simple expressions') args = [dispatch.compile(a, ctx=ctx) for a in expr.args] if funcobj.from_function: name = (funcobj.from_function, ) else: name = (common.edgedb_module_name_to_schema_name( funcobj.shortname.module), common.edgedb_name_to_pg_name(funcobj.shortname.name)) result = pgast.FuncCall(name=name, args=args) return result
def constraint_name(self, quote=True): name = self.raw_constraint_name() name = common.edgedb_name_to_pg_name(name) return common.quote_ident(name) if quote else name
def get_trigger_procname(self): schema = common.edgedb_module_name_to_schema_name( self.schema_constraint_name().module) proc_name = common.edgedb_name_to_pg_name(self.raw_constraint_name() + '_trigproc') return schema, proc_name
def init_dml_stmt( ir_stmt: irast.MutatingStmt, dml_stmt: pgast.DML, *, ctx: context.CompilerContextLevel, parent_ctx: context.CompilerContextLevel) \ -> typing.Tuple[pgast.Query, pgast.CommonTableExpr, pgast.CommonTableExpr]: """Prepare the common structure of the query representing a DML stmt. :param ir_stmt: IR of the statement. :param dml_stmt: SQL DML node instance. :return: A (*wrapper*, *dml_cte*, *range_cte*) tuple, where *wrapper* the the wrapping SQL statement, *dml_cte* is the CTE representing the SQL DML operation in the main relation of the Object, and *range_cte* is the CTE for the subset affected by the statement. *range_cte* is None for INSERT statmenets. """ wrapper = ctx.rel clauses.init_stmt(ir_stmt, ctx, parent_ctx) target_ir_set = ir_stmt.subject dml_stmt.relation = dbobj.range_for_set(ir_stmt.subject, include_overlays=False, env=ctx.env) pathctx.put_path_value_rvar(dml_stmt, target_ir_set.path_id, dml_stmt.relation, env=ctx.env) pathctx.put_path_source_rvar(dml_stmt, target_ir_set.path_id, dml_stmt.relation, env=ctx.env) dml_stmt.path_scope.add(target_ir_set.path_id) dml_cte = pgast.CommonTableExpr(query=dml_stmt, name=ctx.env.aliases.get(hint='m')) if isinstance(ir_stmt, (irast.UpdateStmt, irast.DeleteStmt)): # UPDATE and DELETE operate over a range, so generate # the corresponding CTE and connect it to the DML query. range_cte = get_dml_range(ir_stmt, dml_stmt, ctx=ctx) range_rvar = pgast.RangeVar( relation=range_cte, alias=pgast.Alias(aliasname=ctx.env.aliases.get(hint='range'))) relctx.pull_path_namespace(target=dml_stmt, source=range_rvar, ctx=ctx) # Auxillary relations are always joined via the WHERE # clause due to the structure of the UPDATE/DELETE SQL statments. id_col = common.edgedb_name_to_pg_name('std::id') dml_stmt.where_clause = astutils.new_binop( lexpr=pgast.ColumnRef( name=[dml_stmt.relation.alias.aliasname, id_col]), op=ast.ops.EQ, rexpr=pathctx.get_rvar_path_identity_var(range_rvar, target_ir_set.path_id, env=ctx.env)) # UPDATE has "FROM", while DELETE has "USING". if hasattr(dml_stmt, 'from_clause'): dml_stmt.from_clause.append(range_rvar) else: dml_stmt.using_clause.append(range_rvar) else: range_cte = None # Due to the fact that DML statements are structured # as a flat list of CTEs instead of nested range vars, # the top level path scope must be empty. The necessary # range vars will be injected explicitly in all rels that # need them. ctx.path_scope.clear() pathctx.put_path_value_rvar(dml_stmt, ir_stmt.subject.path_id, dml_stmt.relation, env=ctx.env) pathctx.put_path_source_rvar(dml_stmt, ir_stmt.subject.path_id, dml_stmt.relation, env=ctx.env) dml_rvar = pgast.RangeVar( relation=dml_cte, alias=pgast.Alias(aliasname=parent_ctx.env.aliases.get('d'))) relctx.include_rvar(wrapper, dml_rvar, ir_stmt.subject.path_id, ctx=ctx) pathctx.put_path_bond(wrapper, ir_stmt.subject.path_id) return wrapper, dml_cte, dml_rvar, range_cte
def numbered_constraint_name(self, i, quote=True): raw_name = self.raw_constraint_name() name = common.edgedb_name_to_pg_name('{}#{}'.format(raw_name, i)) return common.quote_ident(name) if quote else name
def process_link_values( ir_stmt, ir_expr, target_tab, tab_cols, col_data, dml_rvar, sources, props_only, target_is_scalar, iterator_cte, *, ctx=context.CompilerContext) -> \ typing.Tuple[pgast.CommonTableExpr, typing.List[str]]: """Unpack data from an update expression into a series of selects. :param ir_expr: IR of the INSERT/UPDATE body element. :param target_tab: The link table being updated. :param tab_cols: A sequence of columns in the table being updated. :param col_data: Expressions used to populate well-known columns of the link table such as std::source and std::__type__. :param sources: A list of relations which must be joined into the data query to resolve expressions in *col_data*. :param props_only: Whether this link update only touches link properties. :param target_is_scalar: Whether the link target is an ScalarType. :param iterator_cte: CTE representing the iterator range in the FOR clause of the EdgeQL DML statement. """ with ctx.newscope() as newscope, newscope.newrel() as subrelctx: row_query = subrelctx.rel relctx.include_rvar(row_query, dml_rvar, path_id=ir_stmt.subject.path_id, ctx=subrelctx) subrelctx.path_scope[ir_stmt.subject.path_id] = row_query if iterator_cte is not None: iterator_rvar = dbobj.rvar_for_rel(iterator_cte, lateral=True, env=subrelctx.env) relctx.include_rvar(row_query, iterator_rvar, path_id=iterator_cte.query.path_id, ctx=subrelctx) with subrelctx.newscope() as sctx, sctx.subrel() as input_rel_ctx: input_rel = input_rel_ctx.rel if iterator_cte is not None: input_rel_ctx.path_scope[iterator_cte.query.path_id] = \ row_query input_rel_ctx.expr_exposed = False input_rel_ctx.shape_format = context.ShapeFormat.FLAT input_rel_ctx.volatility_ref = pathctx.get_path_identity_var( row_query, ir_stmt.subject.path_id, env=input_rel_ctx.env) dispatch.compile(ir_expr, ctx=input_rel_ctx) input_stmt = input_rel input_rvar = pgast.RangeSubselect( subquery=input_rel, lateral=True, alias=pgast.Alias(aliasname=ctx.env.aliases.get('val'))) source_data = {} if input_stmt.op is not None: # UNION input_stmt = input_stmt.rarg path_id = ir_expr.path_id output = pathctx.get_path_value_output(input_stmt, path_id, env=ctx.env) if isinstance(output, pgast.TupleVar): for element in output.elements: name = element.path_id.rptr_name() if name is None: name = element.path_id[-1].name colname = common.edgedb_name_to_pg_name(name) source_data.setdefault(colname, dbobj.get_column(input_rvar, element.name)) else: if target_is_scalar: target_ref = pathctx.get_rvar_path_value_var(input_rvar, path_id, env=ctx.env) else: target_ref = pathctx.get_rvar_path_identity_var(input_rvar, path_id, env=ctx.env) source_data['std::target'] = target_ref if not target_is_scalar and 'std::target' not in source_data: target_ref = pathctx.get_rvar_path_identity_var(input_rvar, path_id, env=ctx.env) source_data['std::target'] = target_ref specified_cols = [] for col in tab_cols: expr = col_data.get(col) if expr is None: expr = source_data.get(col) if expr is not None: row_query.target_list.append(pgast.ResTarget(val=expr, name=col)) specified_cols.append(col) row_query.from_clause += list(sources) + [input_rvar] link_rows = pgast.CommonTableExpr(query=row_query, name=ctx.env.aliases.get(hint='r')) return link_rows, specified_cols
def get(self, hint=None): alias = super().get(hint) return common.edgedb_name_to_pg_name(alias)