def compile_DeleteStmt(stmt: irast.DeleteStmt, *, ctx: context.CompilerContextLevel) -> pgast.Query: parent_ctx = ctx with parent_ctx.substmt() as ctx: # Common DML bootstrap wrapper, delete_cte, delete_rvar, range_cte = dml.init_dml_stmt( stmt, pgast.DeleteStmt(), parent_ctx=parent_ctx, ctx=ctx) ctx.toplevel_stmt.ctes.append(range_cte) ctx.toplevel_stmt.ctes.append(delete_cte) # Wrap up. return dml.fini_dml_stmt(stmt, wrapper, delete_cte, delete_rvar, parent_ctx=parent_ctx, ctx=ctx)
def process_link_update( ir_stmt: irast.MutatingStmt, ir_expr: irast.Base, props_only: bool, wrapper: pgast.Query, dml_cte: pgast.CommonTableExpr, iterator_cte: pgast.CommonTableExpr, *, ctx: context.CompilerContextLevel) -> typing.Optional[pgast.Query]: """Perform updates to a link relation as part of a DML statement. :param ir_stmt: IR of the statement. :param ir_expr: IR of the INSERT/UPDATE body element. :param props_only: Whether this link update only touches link properties. :param wrapper: Top-level SQL query. :param dml_cte: CTE representing the SQL INSERT or UPDATE to the main relation of the Object. :param iterator_cte: CTE representing the iterator range in the FOR clause of the EdgeQL DML statement. """ toplevel = ctx.toplevel_stmt edgedb_ptr_tab = pgast.RangeVar( relation=pgast.Relation(schemaname='edgedb', name='pointer'), alias=pgast.Alias(aliasname=ctx.env.aliases.get(hint='ptr'))) ltab_alias = edgedb_ptr_tab.alias.aliasname rptr = ir_expr.rptr ptrcls = rptr.ptrcls target_is_scalar = isinstance(ptrcls.target, s_scalars.ScalarType) path_id = rptr.source.path_id.extend(ptrcls, rptr.direction, rptr.target.scls) # The links in the dml class shape have been derived, # but we must use the correct specialized link class for the # base material type. mptrcls = ptrcls.material_type() # Lookup link class id by link name. lname_to_id = pgast.CommonTableExpr(query=pgast.SelectStmt( from_clause=[edgedb_ptr_tab], target_list=[ pgast.ResTarget(val=pgast.ColumnRef(name=[ltab_alias, 'id'])) ], where_clause=astutils.new_binop( lexpr=pgast.ColumnRef(name=[ltab_alias, 'name']), rexpr=pgast.Constant(val=mptrcls.name), op=ast.ops.EQ)), name=ctx.env.aliases.get(hint='lid')) lname_to_id_rvar = pgast.RangeVar(relation=lname_to_id) toplevel.ctes.append(lname_to_id) target_rvar = dbobj.range_for_ptrcls(mptrcls, '>', include_overlays=False, env=ctx.env) target_alias = target_rvar.alias.aliasname target_tab_name = (target_rvar.relation.schemaname, target_rvar.relation.name) tab_cols = dbobj.cols_for_pointer(mptrcls, env=ctx.env) dml_cte_rvar = pgast.RangeVar( relation=dml_cte, alias=pgast.Alias(aliasname=ctx.env.aliases.get('m'))) col_data = { 'ptr_item_id': pgast.ColumnRef(name=[lname_to_id.name, 'id']), 'std::source': pathctx.get_rvar_path_identity_var(dml_cte_rvar, ir_stmt.subject.path_id, env=ctx.env) } # Drop all previous link records for this source. delcte = pgast.CommonTableExpr(query=pgast.DeleteStmt( relation=target_rvar, where_clause=astutils.new_binop( lexpr=col_data['std::source'], op=ast.ops.EQ, rexpr=pgast.ColumnRef(name=[target_alias, 'std::source'])), using_clause=[dml_cte_rvar], returning_list=[ pgast.ResTarget(val=pgast.ColumnRef( name=[target_alias, pgast.Star()])) ]), name=ctx.env.aliases.get(hint='d')) pathctx.put_path_value_rvar(delcte.query, path_id.ptr_path(), target_rvar, env=ctx.env) # Record the effect of this removal in the relation overlay # context to ensure that the RETURNING clause potentially # referencing this link yields the expected results. overlays = ctx.env.rel_overlays[ptrcls.shortname] overlays.append(('except', delcte)) toplevel.ctes.append(delcte) # Turn the IR of the expression on the right side of := # into a subquery returning records for the link table. data_cte, specified_cols = process_link_values(ir_stmt, ir_expr, target_tab_name, tab_cols, col_data, dml_cte_rvar, [lname_to_id_rvar], props_only, target_is_scalar, iterator_cte, ctx=ctx) toplevel.ctes.append(data_cte) data_select = pgast.SelectStmt( target_list=[ pgast.ResTarget(val=pgast.ColumnRef( name=[data_cte.name, pgast.Star()])) ], from_clause=[pgast.RangeVar(relation=data_cte)]) # Inserting rows into the link table may produce cardinality # constraint violations, since the INSERT into the link table # is executed in the snapshot where the above DELETE from # the link table is not visible. Hence, we need to use # the ON CONFLICT clause to resolve this. conflict_cols = ['std::source', 'std::target', 'ptr_item_id'] conflict_inference = [] conflict_exc_row = [] for col in conflict_cols: conflict_inference.append(pgast.ColumnRef(name=[col])) conflict_exc_row.append(pgast.ColumnRef(name=['excluded', col])) conflict_data = pgast.SelectStmt( target_list=[ pgast.ResTarget(val=pgast.ColumnRef( name=[data_cte.name, pgast.Star()])) ], from_clause=[pgast.RangeVar(relation=data_cte)], where_clause=astutils.new_binop( lexpr=pgast.ImplicitRowExpr(args=conflict_inference), rexpr=pgast.ImplicitRowExpr(args=conflict_exc_row), op='=')) cols = [pgast.ColumnRef(name=[col]) for col in specified_cols] updcte = pgast.CommonTableExpr( name=ctx.env.aliases.get(hint='i'), query=pgast.InsertStmt( relation=target_rvar, select_stmt=data_select, cols=cols, on_conflict=pgast.OnConflictClause( action='update', infer=pgast.InferClause(index_elems=conflict_inference), target_list=[ pgast.MultiAssignRef(columns=cols, source=conflict_data) ]), returning_list=[ pgast.ResTarget(val=pgast.ColumnRef(name=[pgast.Star()])) ])) pathctx.put_path_value_rvar(updcte.query, path_id.ptr_path(), target_rvar, env=ctx.env) # Record the effect of this insertion in the relation overlay # context to ensure that the RETURNING clause potentially # referencing this link yields the expected results. overlays = ctx.env.rel_overlays[ptrcls.shortname] overlays.append(('union', updcte)) toplevel.ctes.append(updcte) return data_cte