def compile_Tuple(expr: irast.Tuple, *, ctx: context.CompilerContextLevel) -> pgast.BaseExpr: ttype = expr.typeref ttypes = {} for i, st in enumerate(ttype.subtypes): if st.element_name: ttypes[st.element_name] = st else: ttypes[str(i)] = st telems = list(ttypes) elements = [] for i, e in enumerate(expr.elements): telem = telems[i] ttype = ttypes[telem] val = dispatch.compile(e.val, ctx=ctx) elements.append(pgast.TupleElement(path_id=e.path_id, val=val)) result = pgast.TupleVar(elements=elements, typeref=ttype) return output.output_as_value(result, env=ctx.env)
def _compile_shape(ir_set: irast.Set, shape: List[Tuple[irast.Set, qlast.ShapeOp]], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: result = shapecomp.compile_shape(ir_set, shape, ctx=ctx) for element in result.elements: # We want to force, because the path id might already exist # serialized with a different shape, and we need ours to be # visible. (Anything needing the old one needs to have pulled # it already: see the "unfortunate hack" in # process_set_as_tuple.) pathctx.put_path_serialized_var(ctx.rel, element.path_id, element.val, force=True, env=ctx.env) ser_elements = [] for el in result.elements: ser_val = pathctx.get_path_serialized_or_value_var(ctx.rel, el.path_id, env=ctx.env) ser_elements.append( pgast.TupleElement(path_id=el.path_id, name=el.name, val=ser_val)) ser_result = pgast.TupleVar(elements=ser_elements, named=True) sval = output.serialize_expr(ser_result, path_id=ir_set.path_id, env=ctx.env) pathctx.put_path_serialized_var(ctx.rel, ir_set.path_id, sval, force=True, env=ctx.env) return result
def process_insert_body(ir_stmt: irast.MutatingStmt, wrapper: pgast.Query, insert_cte: pgast.CommonTableExpr, insert_rvar: pgast.PathRangeVar, *, ctx: context.CompilerContextLevel) -> None: """Generate SQL DML CTEs from an InsertStmt IR. :param ir_stmt: IR of the statement. :param wrapper: Top-level SQL query. :param insert_cte: CTE representing the SQL INSERT to the main relation of the Object. """ cols = [pgast.ColumnRef(name=['__type__'])] select = pgast.SelectStmt(target_list=[]) values = select.target_list # The main INSERT query of this statement will always be # present to insert at least the `id` and `__type__` # properties. insert_stmt = insert_cte.query assert isinstance(insert_stmt, pgast.InsertStmt) insert_stmt.cols = cols insert_stmt.select_stmt = select if ir_stmt.parent_stmt is not None: iterator_set = ir_stmt.parent_stmt.iterator_stmt else: iterator_set = None if iterator_set is not None: with ctx.substmt() as ictx: ictx.path_scope = ictx.path_scope.new_child() ictx.path_scope[iterator_set.path_id] = ictx.rel clauses.compile_iterator_expr(ictx.rel, iterator_set, ctx=ictx) ictx.rel.path_id = iterator_set.path_id pathctx.put_path_bond(ictx.rel, iterator_set.path_id) iterator_cte = pgast.CommonTableExpr( query=ictx.rel, name=ctx.env.aliases.get('iter')) ictx.toplevel_stmt.ctes.append(iterator_cte) iterator_rvar = relctx.rvar_for_rel(iterator_cte, ctx=ctx) relctx.include_rvar(select, iterator_rvar, path_id=ictx.rel.path_id, ctx=ctx) iterator_id = pathctx.get_path_identity_var(select, iterator_set.path_id, env=ctx.env) else: iterator_cte = None iterator_id = None typeref = ir_stmt.subject.typeref if typeref.material_type is not None: typeref = typeref.material_type values.append( pgast.ResTarget(val=pgast.TypeCast( arg=pgast.StringConstant(val=str(typeref.id)), type_name=pgast.TypeName(name=('uuid', ))), )) external_inserts = [] parent_link_props = [] with ctx.newrel() as subctx: subctx.rel = select subctx.rel_hierarchy[select] = insert_stmt subctx.expr_exposed = False if iterator_cte is not None: subctx.path_scope = ctx.path_scope.new_child() subctx.path_scope[iterator_cte.query.path_id] = select # Process the Insert IR and separate links that go # into the main table from links that are inserted into # a separate link table. for shape_el in ir_stmt.subject.shape: rptr = shape_el.rptr ptrref = rptr.ptrref if ptrref.material_ptr is not None: ptrref = ptrref.material_ptr if (ptrref.parent_ptr is not None and rptr.source.path_id != ir_stmt.subject.path_id): parent_link_props.append(shape_el) continue ptr_info = pg_types.get_ptrref_storage_info(ptrref, resolve_type=True, link_bias=False) props_only = False # First, process all local link inserts. if ptr_info.table_type == 'ObjectType': props_only = True field = pgast.ColumnRef(name=[ptr_info.column_name]) cols.append(field) insvalue = insert_value_for_shape_element(insert_stmt, wrapper, ir_stmt, shape_el, iterator_id, ptr_info=ptr_info, ctx=subctx) values.append(pgast.ResTarget(val=insvalue)) ptr_info = pg_types.get_ptrref_storage_info(ptrref, resolve_type=False, link_bias=True) if ptr_info and ptr_info.table_type == 'link': external_inserts.append((shape_el, props_only)) if iterator_cte is not None: cols.append(pgast.ColumnRef(name=['__edb_token'])) values.append(pgast.ResTarget(val=iterator_id)) pathctx.put_path_identity_var(insert_stmt, iterator_set.path_id, cols[-1], force=True, env=subctx.env) pathctx.put_path_bond(insert_stmt, iterator_set.path_id) toplevel = ctx.toplevel_stmt toplevel.ctes.append(insert_cte) # Process necessary updates to the link tables. for shape_el, props_only in external_inserts: process_link_update(ir_stmt=ir_stmt, ir_set=shape_el, props_only=props_only, wrapper=wrapper, dml_cte=insert_cte, iterator_cte=iterator_cte, is_insert=True, ctx=ctx) if parent_link_props: prop_elements = [] with ctx.newscope() as scopectx: scopectx.rel = wrapper for shape_el in parent_link_props: rptr = shape_el.rptr scopectx.path_scope[rptr.source.path_id] = wrapper pathctx.put_path_rvar_if_not_exists(wrapper, rptr.source.path_id, insert_rvar, aspect='value', env=scopectx.env) dispatch.visit(shape_el, ctx=scopectx) tuple_el = astutils.tuple_element_for_shape_el(shape_el, None, ctx=scopectx) prop_elements.append(tuple_el) valtuple = pgast.TupleVar(elements=prop_elements, named=True) pathctx.put_path_value_var(wrapper, ir_stmt.subject.path_id, valtuple, force=True, env=ctx.env)
def compile_shape(ir_set: irast.Set, shape: Sequence[Tuple[irast.Set, qlast.ShapeOp]], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: elements = [] with ctx.newscope() as shapectx: shapectx.disable_semi_join.add(ir_set.path_id) if isinstance(ir_set.expr, irast.Stmt): # The source set for this shape is a FOR statement, # which is special in that besides set path_id it # should also expose the path_id of the FOR iterator # so that shape element expressions that might contain # an iterator reference find it properly. # # So, for: # SELECT Bar { # foo := (FOR x := ... UNION Foo { spam := x }) # } # # the path scope when processing the shape of Bar.foo # should be {'Bar.foo', 'x'}. iterator = ir_set.expr.iterator_stmt if iterator: shapectx.path_scope[iterator.path_id] = ctx.rel for el, op in shape: if op == qlast.ShapeOp.MATERIALIZE and not ctx.materializing: continue rptr = el.rptr assert rptr is not None ptrref = rptr.ptrref # As an implementation expedient, we currently represent # AT_MOST_ONE materialized values with arrays card = ptrref.dir_cardinality is_singleton = (card.is_single() and (op != qlast.ShapeOp.MATERIALIZE or not card.can_be_zero())) value: pgast.BaseExpr if (irutils.is_subquery_set(el) or el.path_id.is_objtype_path() or not is_singleton or not ptrref.required): wrapper = relgen.set_as_subquery(el, as_value=True, ctx=shapectx) if not is_singleton: value = relctx.set_to_array(path_id=el.path_id, query=wrapper, ctx=shapectx) else: value = wrapper else: value = dispatch.compile(el, ctx=shapectx) tuple_el = astutils.tuple_element_for_shape_el(el, value, ctx=shapectx) assert isinstance(tuple_el, pgast.TupleElement) elements.append(tuple_el) return pgast.TupleVar(elements=elements, named=True)
def _get_path_output(rel: pgast.BaseRelation, path_id: irast.PathId, *, aspect: str, allow_nullable: bool = True, ptr_info: typing.Optional[ pg_types.PointerStorageInfo] = None, env: context.Environment) -> pgast.OutputVar: result = rel.path_outputs.get((path_id, aspect)) if result is not None: return result alias = None rptr = path_id.rptr() if rptr is not None and irtyputils.is_id_ptrref(rptr): # A value reference to Object.id is the same as a value # reference to the Object itself. src_path_id = path_id.src_path() id_output = rel.path_outputs.get((src_path_id, 'value')) if id_output is not None: _put_path_output_var(rel, path_id, aspect, id_output, env=env) return id_output if is_terminal_relation(rel): return _get_rel_path_output(rel, path_id, aspect=aspect, ptr_info=ptr_info, env=env) elif is_values_relation(rel): # The VALUES() construct seems to always expose its # value as "column1". alias = 'column1' ref = pgast.ColumnRef(name=[alias]) else: ref = get_path_var(rel, path_id, aspect=aspect, env=env) other_output = find_path_output(rel, path_id, ref, env=env) if other_output is not None: _put_path_output_var(rel, path_id, aspect, other_output, env=env) return other_output if isinstance(ref, pgast.TupleVar): elements = [] for el in ref.elements: el_path_id = reverse_map_path_id(el.path_id, rel.view_path_id_map) try: # Similarly to get_path_var(), check for outer path_id # first for tuple serialized var disambiguation. element = _get_path_output(rel, el_path_id, aspect=aspect, allow_nullable=False, env=env) except LookupError: element = get_path_output(rel, el_path_id, aspect=aspect, allow_nullable=False, env=env) elements.append( pgast.TupleElement(path_id=el_path_id, name=element)) result = pgast.TupleVar(elements=elements, named=ref.named) else: if astutils.is_set_op_query(rel): result = dbobj.strip_output_var(ref) else: if alias is None: alias = get_path_output_alias(path_id, aspect, env=env) restarget = pgast.ResTarget(name=alias, val=ref, ser_safe=getattr( ref, 'ser_safe', False)) if hasattr(rel, 'returning_list'): rel.returning_list.append(restarget) else: rel.target_list.append(restarget) nullable = is_nullable(ref, env=env) if isinstance(ref, pgast.ColumnRef): optional = ref.optional else: optional = None if nullable and not allow_nullable: var = get_path_var(rel, path_id, aspect=aspect, env=env) rel.where_clause = astutils.extend_binop( rel.where_clause, pgast.NullTest(arg=var, negated=True)) nullable = False result = pgast.ColumnRef(name=[alias], nullable=nullable, optional=optional) _put_path_output_var(rel, path_id, aspect, result, env=env) if (aspect == 'identity' and path_id.is_objtype_path() and (path_id, 'value') not in rel.path_outputs): _put_path_output_var(rel, path_id, 'value', result, env=env) return result