def _compile_shape(ir_set: irast.Set, shape: typing.List[irast.Set], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: result = shapecomp.compile_shape(ir_set, shape, ctx=ctx) for element in result.elements: # The ref might have already been added by the nested shape # processing, so add it conditionally. pathctx.put_path_var_if_not_exists(ctx.rel, element.path_id, element.val, aspect='serialized', 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 compile_shape( ir_set: irast.Set, shape: typing.List[irast.Set], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: elements = [] with ctx.newscope() as shapectx: shapectx.disable_semi_join.add(ir_set.path_id) shapectx.unique_paths.add(ir_set.path_id) for el in shape: rptr = el.rptr ptrcls = rptr.ptrcls ptrdir = rptr.direction or s_pointers.PointerDirection.Outbound is_singleton = ptrcls.singular(ptrdir) if (irutils.is_subquery_set(el) or isinstance(el.scls, s_objtypes.ObjectType) or not is_singleton or not ptrcls.required): wrapper = relgen.set_as_subquery( el, as_value=True, ctx=shapectx) if not is_singleton: value = relgen.set_to_array( ir_set=el, query=wrapper, ctx=shapectx) else: value = wrapper else: value = dispatch.compile(el, ctx=shapectx) elements.append(astutils.tuple_element_for_shape_el(el, value)) return pgast.TupleVar(elements=elements, named=True)
def strip_output_var( var: pgast.OutputVar, *, optional: typing.Optional[bool] = None, nullable: typing.Optional[bool] = None) -> pgast.OutputVar: if isinstance(var, pgast.TupleVar): elements = [] for el in var.elements: if isinstance(el.name, str): val = pgast.ColumnRef(name=[el.name]) else: val = strip_output_var(el.name) elements.append( pgast.TupleElement(path_id=el.path_id, name=el.name, val=val)) result = pgast.TupleVar(elements, named=var.named) else: result = pgast.ColumnRef( name=[var.name[-1]], optional=optional if optional is not None else var.optional, nullable=nullable if nullable is not None else var.nullable, ) return result
def compile_shape(ir_set: irast.Set, shape: typing.List[irast.Set], *, 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) and ir_set.expr.iterator_stmt is not None): # 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'}. iter_path_id = ir_set.expr.iterator_stmt.path_id shapectx.path_scope[iter_path_id] = ctx.rel for el in shape: rptr = el.rptr ptrcls = rptr.ptrcls ptrdir = rptr.direction or s_pointers.PointerDirection.Outbound is_singleton = ptrcls.singular(ctx.env.schema, ptrdir) if (irutils.is_subquery_set(el) or isinstance(el.stype, s_objtypes.ObjectType) or not is_singleton or not ptrcls.get_required(ctx.env.schema)): wrapper = relgen.set_as_subquery(el, as_value=True, ctx=shapectx) if not is_singleton: value = relgen.set_to_array(ir_set=el, query=wrapper, ctx=shapectx) else: value = wrapper else: value = dispatch.compile(el, ctx=shapectx) elements.append( astutils.tuple_element_for_shape_el(el, value, ctx=shapectx)) return pgast.TupleVar(elements=elements, named=True)
def get_rvar_var(rvar: pgast.BaseRangeVar, var: pgast.OutputVar) -> pgast.OutputVar: assert isinstance(var, pgast.OutputVar) if isinstance(var, pgast.TupleVar): elements = [] for el in var.elements: val = get_rvar_var(rvar, el.name) elements.append( pgast.TupleElement(path_id=el.path_id, name=el.name, val=val)) fieldref = pgast.TupleVar(elements, named=var.named) else: fieldref = get_column(rvar, var) return fieldref
def compile_Tuple(expr: irast.Base, *, ctx: context.CompilerContextLevel) -> pgast.Base: ttype = _infer_type(expr, ctx=ctx) ttypes = ttype.element_types telems = list(ttypes) path_id = irast.PathId(ttype) elements = [] for i, e in enumerate(expr.elements): telem = telems[i] ttype = ttypes[telem] el_path_id = irutils.tuple_indirection_path_id(path_id, telem, ttype) val = dispatch.compile(e.val, ctx=ctx) elements.append(pgast.TupleElement(path_id=el_path_id, val=val)) result = pgast.TupleVar(elements=elements) return output.output_as_value(result, env=ctx.env)
def get_rvar_var( rvar: typing.Optional[pgast.BaseRangeVar], var: pgast.OutputVar, *, optional: bool=False, nullable: bool=None) \ -> typing.Union[pgast.ColumnRef, pgast.TupleVar]: assert isinstance(var, pgast.OutputVar) if isinstance(var, pgast.TupleVar): elements = [] for el in var.elements: val = get_rvar_var(rvar, el.name) elements.append( pgast.TupleElement(path_id=el.path_id, name=el.name, val=val)) fieldref = pgast.TupleVar(elements, named=var.named) else: fieldref = get_column(rvar, var, optional=optional, nullable=nullable) return fieldref
def get_rvar_fieldref( rvar: typing.Optional[pgast.BaseRangeVar], colname: typing.Union[str, pgast.TupleVar], *, optional: bool=False, nullable: bool=None) \ -> typing.Union[pgast.ColumnRef, pgast.TupleVar]: if isinstance(colname, pgast.TupleVar): elements = [] for el in colname.elements: val = get_rvar_fieldref(rvar, el.name) elements.append( pgast.TupleElement(path_id=el.path_id, name=el.name, val=val)) fieldref = pgast.TupleVar(elements, named=colname.named) else: fieldref = get_column(rvar, colname, optional=optional, nullable=nullable) return fieldref
def process_insert_body(ir_stmt: irast.MutatingStmt, wrapper: pgast.Query, insert_cte: pgast.CommonTableExpr, insert_rvar: pgast.BaseRangeVar, *, 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=['std::__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 std::id and std::__type__ # links. insert_stmt = insert_cte.query 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 = dbobj.rvar_for_rel(iterator_cte, env=ctx.env) 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 values.append( pgast.ResTarget(val=pgast.SelectStmt( target_list=[pgast.ResTarget(val=pgast.ColumnRef(name=['id']))], from_clause=[ pgast.RangeVar(relation=pgast.Relation(name='objecttype', schemaname='edgedb')) ], where_clause=astutils.new_binop( op=ast.ops.EQ, lexpr=pgast.ColumnRef(name=['name']), rexpr=pgast.Constant(val=ir_stmt.subject.scls.shortname))))) external_inserts = [] tuple_elements = [] parent_link_props = [] with ctx.newrel() as subctx: subctx.rel = select subctx.rel_hierarchy[select] = insert_stmt subctx.expr_exposed = False subctx.shape_format = context.ShapeFormat.FLAT 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 ptrcls = rptr.ptrcls.material_type() if (ptrcls.is_link_property() and rptr.source.path_id != ir_stmt.subject.path_id): parent_link_props.append(shape_el) continue ptr_info = pg_types.get_pointer_storage_info( ptrcls, schema=subctx.env.schema, 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) tuple_el = astutils.tuple_element_for_shape_el(shape_el, field) tuple_elements.append(tuple_el) values.append(pgast.ResTarget(val=insvalue)) 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_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, shape_el, props_only, wrapper, insert_cte, iterator_cte, 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.compile(shape_el, ctx=scopectx) tuple_el = astutils.tuple_element_for_shape_el(shape_el, None) 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 _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 if is_terminal_relation(rel): return _get_rel_path_output(rel, path_id, aspect=aspect, ptr_info=ptr_info, env=env) 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: rel.path_outputs[path_id, aspect] = other_output 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): assert isinstance(ref, pgast.ColumnRef) result = dbobj.get_column(None, ref) else: alias = get_path_output_alias(path_id, aspect, env=env) restarget = pgast.ResTarget(name=alias, val=ref) 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) rel.path_outputs[path_id, aspect] = result return result
def _compile_shape(ir_set: irast.Set, shape: typing.List[irast.Set], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: elements = [] with ctx.newscope() as shapectx: shapectx.disable_semi_join.add(ir_set.path_id) shapectx.unique_paths.add(ir_set.path_id) for el in shape: rptr = el.rptr ptrcls = rptr.ptrcls ptrdir = rptr.direction or s_pointers.PointerDirection.Outbound is_singleton = ptrcls.singular(ptrdir) if (irutils.is_subquery_set(el) or isinstance(el.scls, s_objtypes.ObjectType) or not is_singleton or not ptrcls.required): wrapper = relgen.set_as_subquery(el, as_value=True, ctx=shapectx) if not is_singleton: value = relgen.set_to_array(ir_set=el, query=wrapper, ctx=shapectx) else: value = wrapper else: value = dispatch.compile(el, ctx=shapectx) elements.append(astutils.tuple_element_for_shape_el(el, value)) result = pgast.TupleVar(elements=elements, named=True) pathctx.put_path_value_var(ctx.rel, ir_set.path_id, result, force=True, env=ctx.env) for element in elements: # The ref might have already been added by the nested shape # processing, so add it conditionally. pathctx.put_path_value_var_if_not_exists(ctx.rel, element.path_id, element.val, env=ctx.env) if output.in_serialization_ctx(ctx): ser_elements = [] for el in 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, env=ctx.env) pathctx.put_path_serialized_var(ctx.rel, ir_set.path_id, sval, force=True, env=ctx.env) return result
def get_path_output(rel: pgast.BaseRelation, path_id: irast.PathId, *, aspect: str, ptr_info: typing.Optional[ pg_types.PointerStorageInfo] = None, env: context.Environment) -> pgast.OutputVar: view_path_id_map = getattr(rel, 'view_path_id_map', None) if view_path_id_map: path_id = map_path_id(path_id, view_path_id_map) result = rel.path_outputs.get((path_id, aspect)) if result is not None: return result if isinstance(rel, pgast.Relation): return _get_rel_path_output(rel, path_id, aspect=aspect, ptr_info=ptr_info, env=env) 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: rel.path_outputs[path_id, aspect] = other_output 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) element = get_path_output(rel, el_path_id, aspect=aspect, 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): assert isinstance(ref, pgast.ColumnRef) result = dbobj.get_column(None, ref) else: alias = get_path_output_alias(path_id, aspect, env=env) restarget = pgast.ResTarget(name=alias, val=ref) if hasattr(rel, 'returning_list'): rel.returning_list.append(restarget) else: rel.target_list.append(restarget) if isinstance(ref, pgast.ColumnRef): nullable = ref.nullable optional = ref.optional else: nullable = rel.nullable optional = None result = pgast.ColumnRef(name=[alias], nullable=nullable, optional=optional) rel.path_outputs[path_id, aspect] = result return result