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, 'source', nullable=False), op='=', ) targets = [] for prop_el in ir_expr.shape: ptrname = prop_el.rptr.ptrcls.get_shortname(ctx.env.schema) 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=ptrname.name, 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.get_shortname(ctx.env.schema).name)) toplevel.ctes.append(updcte)
def process_update_body(ir_stmt: irast.MutatingStmt, wrapper: pgast.Query, update_cte: pgast.CommonTableExpr, range_cte: pgast.CommonTableExpr, *, ctx: context.CompilerContextLevel): """Generate SQL DML CTEs from an UpdateStmt IR. :param ir_stmt: IR of the statement. :param wrapper: Top-level SQL query. :param update_cte: CTE representing the SQL UPDATE to the main relation of the Object. :param range_cte: CTE representing the range affected by the statement. """ update_stmt = update_cte.query external_updates = [] toplevel = ctx.toplevel_stmt toplevel.ctes.append(range_cte) toplevel.ctes.append(update_cte) with ctx.newscope() as subctx: # It is necessary to process the expressions in # the UpdateStmt shape body in the context of the # UPDATE statement so that references to the current # values of the updated object are resolved correctly. subctx.path_scope[ir_stmt.subject.path_id] = update_stmt subctx.rel = update_stmt subctx.expr_exposed = False subctx.shape_format = context.ShapeFormat.FLAT for shape_el in ir_stmt.subject.shape: with subctx.newscope() as scopectx: ptrcls = shape_el.rptr.ptrcls updvalue = shape_el.expr ptr_info = pg_types.get_pointer_storage_info( ptrcls, schema=scopectx.env.schema, resolve_type=True, link_bias=False) # First, process all internal link updates if ptr_info.table_type == 'ObjectType': updvalue = pgast.TypeCast( arg=dispatch.compile(updvalue, ctx=scopectx), type_name=typecomp.type_node(ptr_info.column_type)) update_stmt.targets.append( pgast.UpdateTarget(name=ptr_info.column_name, val=updvalue)) props_only = is_props_only_update(shape_el) ptr_info = pg_types.get_pointer_storage_info(ptrcls, resolve_type=False, link_bias=True) if ptr_info and ptr_info.table_type == 'link': external_updates.append((shape_el, props_only)) if not update_stmt.targets: # No updates directly to the set target table, # so convert the UPDATE statement into a SELECT. update_cte.query = pgast.SelectStmt( ctes=update_stmt.ctes, target_list=update_stmt.returning_list, from_clause=[update_stmt.relation] + update_stmt.from_clause, where_clause=update_stmt.where_clause, path_namespace=update_stmt.path_namespace, path_outputs=update_stmt.path_outputs, path_scope=update_stmt.path_scope, path_rvar_map=update_stmt.path_rvar_map.copy(), view_path_id_map=update_stmt.view_path_id_map.copy(), ptr_join_map=update_stmt.ptr_join_map.copy(), ) # Process necessary updates to the link tables. for expr, props_only in external_updates: if props_only: process_linkprop_update(ir_stmt, expr, wrapper, update_cte, ctx=ctx) else: process_link_update(ir_stmt, expr, False, wrapper, update_cte, None, ctx=ctx)