def _new_mapped_pointer_rvar( ir_ptr: irast.Pointer, *, ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar: ptrcls = ir_ptr.ptrcls ptr_rvar = dbobj.range_for_pointer(ir_ptr, env=ctx.env) # 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, 'source'), resolve_type=False, schema=ctx.env.schema) src_col = src_ptr_info.column_name else: src_col = 'source' source_ref = pgast.ColumnRef(name=[src_col], nullable=False) 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, 'target'), resolve_type=False, schema=ctx.env.schema) tgt_col = tgt_ptr_info.column_name else: tgt_col = 'target' target_ref = pgast.ColumnRef( name=[tgt_col], nullable=not ptrcls.get_required(ctx.env.schema)) 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 src_pid = ir_ptr.source.path_id tgt_pid = ir_ptr.target.path_id ptr_pid = tgt_pid.ptr_path() ptr_rvar.query.path_id = ptr_pid pathctx.put_rvar_path_bond(ptr_rvar, 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(): pathctx.put_rvar_path_bond(ptr_rvar, tgt_pid) pathctx.put_rvar_path_output(ptr_rvar, tgt_pid, aspect='identity', var=far_ref, env=ctx.env) return ptr_rvar
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 new_root_rvar( ir_set: irast.Set, *, ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar: if not isinstance(ir_set.stype, s_objtypes.ObjectType): raise ValueError('cannot create root rvar for non-object path') set_rvar = dbobj.range_for_set(ir_set, env=ctx.env) pathctx.put_rvar_path_bond(set_rvar, ir_set.path_id) set_rvar.value_scope.add(ir_set.path_id) if ir_set.rptr and ir_set.rptr.is_inbound: ptrcls = ir_set.rptr.ptrcls ptr_info = pg_types.get_pointer_storage_info( ptrcls, resolve_type=False, link_bias=False, schema=ctx.env.schema) if ptr_info.table_type == 'ObjectType': # Inline link rref = pgast.ColumnRef( name=[ptr_info.column_name], nullable=not ptrcls.get_required(ctx.env.schema)) pathctx.put_rvar_path_bond( set_rvar, ir_set.path_id.src_path()) pathctx.put_rvar_path_output( set_rvar, ir_set.path_id.src_path(), aspect='identity', var=rref, env=ctx.env) return set_rvar
def semi_join(stmt: pgast.Query, ir_set: irast.Set, src_rvar: pgast.BaseRangeVar, *, ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar: """Join an IR Set using semi-join.""" rptr = ir_set.rptr ptrcls = rptr.ptrcls ptr_info = pg_types.get_pointer_storage_info(ptrcls, resolve_type=False, link_bias=False) is_inline_ref = ptr_info.table_type == 'ObjectType' # Target set range. set_rvar = new_root_rvar(ir_set, ctx=ctx) # Link range. map_rvar = new_pointer_rvar(rptr, src_rvar=src_rvar, ctx=ctx) # Target identity in the target range. if rptr.is_inbound and is_inline_ref: tgt_pid = ir_set.path_id.extend(ptrcls) else: tgt_pid = ir_set.path_id tgt_ref = pathctx.get_rvar_path_identity_var(set_rvar, tgt_pid, env=ctx.env) include_rvar(ctx.rel, map_rvar, path_id=ir_set.path_id.ptr_path(), ctx=ctx) pathctx.get_path_identity_output(ctx.rel, ir_set.path_id, env=ctx.env) cond = astutils.new_binop(tgt_ref, ctx.rel, 'IN') stmt.where_clause = astutils.extend_binop(stmt.where_clause, cond) return set_rvar
def new_pointer_rvar( ir_ptr: irast.Pointer, *, nullable: bool=False, link_bias: bool=False, src_rvar: pgast.BaseRangeVar, ctx: context.CompilerContextLevel) -> pgast.BaseRangeVar: ptrcls = ir_ptr.ptrcls ptr_info = pg_types.get_pointer_storage_info( ptrcls, resolve_type=False, link_bias=link_bias) if ptr_info.table_type == 'ObjectType': # Inline link return _new_inline_pointer_rvar( ir_ptr, nullable=nullable, ptr_info=ptr_info, src_rvar=src_rvar, ctx=ctx) else: return _new_mapped_pointer_rvar(ir_ptr, nullable, ctx=ctx)
def _get_rel_path_output(rel: pgast.BaseRelation, path_id: irast.PathId, *, aspect: str, ptr_info: typing.Optional[ pg_types.PointerStorageInfo] = None, env: context.Environment) -> pgast.OutputVar: if path_id.is_objtype_path(): if aspect == 'identity': aspect = 'value' if aspect != 'value': raise LookupError( f'invalid request for non-scalar path {path_id} {aspect}') if (path_id == rel.path_id or (rel.path_id.is_type_indirection_path() and path_id == rel.path_id.src_path())): path_id = irutils.get_id_path_id(path_id, schema=env.schema) else: if aspect == 'identity': raise LookupError( f'invalid request for scalar path {path_id} {aspect}') if path_id.rptr_dir() != s_pointers.PointerDirection.Outbound: raise LookupError( f'{path_id} is an inbound pointer and cannot be resolved ' f'on a base relation') ptrcls = path_id.rptr() if ptrcls is None: raise ValueError( f'could not resolve trailing pointer class for {path_id}') ptr_info = pg_types.get_pointer_storage_info(ptrcls, resolve_type=False, link_bias=False) result = pgast.ColumnRef(name=[ptr_info.column_name], nullable=rel.nullable or not ptrcls.required) rel.path_outputs[path_id, aspect] = result return result
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 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)
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_var(rel: pgast.BaseRelation, path_id: irast.PathId, *, aspect: str, env: context.Environment) -> pgast.OutputVar: """Return an OutputVar for a given *path_id* in a given *rel*.""" if isinstance(rel, pgast.CommonTableExpr): rel = rel.query # Check if we already have a var, before remapping the path_id. # This is useful for serialized aspect disambiguation in tuples, # since process_set_as_tuple() records serialized vars with # original path_id. if (path_id, aspect) in rel.path_namespace: return rel.path_namespace[path_id, aspect] if rel.view_path_id_map: path_id = map_path_id(path_id, rel.view_path_id_map) if (path_id, aspect) in rel.path_namespace: return rel.path_namespace[path_id, aspect] ptrcls = path_id.rptr() if ptrcls is not None: ptr_info = pg_types.get_pointer_storage_info(ptrcls, resolve_type=False, link_bias=False) ptr_dir = path_id.rptr_dir() is_inbound = ptr_dir == s_pointers.PointerDirection.Inbound if is_inbound: src_path_id = path_id else: src_path_id = path_id.src_path() else: ptr_info = None src_path_id = None ptr_dir = None if astutils.is_set_op_query(rel): cb = functools.partial(get_path_output_or_null, env=env, path_id=path_id, aspect=aspect) outputs = astutils.for_each_query_in_set(rel, cb) first = None optional = False all_null = True nullable = False for colref, is_null in outputs: if colref.nullable: nullable = True if first is None: first = colref if is_null: optional = True else: all_null = False if all_null: raise LookupError(f'cannot find refs for ' f'path {path_id} {aspect} in {rel}') # Path vars produced by UNION expressions can be "optional", # i.e the record is accepted as-is when such var is NULL. # This is necessary to correctly join heterogeneous UNIONs. var = dbobj.get_rvar_var(None, first, optional=optional, nullable=optional or nullable) put_path_var(rel, path_id, var, aspect=aspect, env=env) return var if ptrcls is None: if len(path_id) == 1: # This is an scalar set derived from an expression. src_path_id = path_id elif ptrcls.is_link_property(): if ptr_info.table_type != 'link' and not is_inbound: # This is a link prop that is stored in source rel, # step back to link source rvar. src_path_id = path_id.src_path().src_path() elif ptr_info.table_type != 'ObjectType' and not is_inbound: # Ref is in the mapping rvar. src_path_id = path_id.ptr_path() rel_rvar = maybe_get_path_rvar(rel, path_id, aspect=aspect, env=env) if rel_rvar is None: alt_aspect = get_less_specific_aspect(path_id, aspect) if alt_aspect is not None: rel_rvar = maybe_get_path_rvar(rel, path_id, aspect=alt_aspect, env=env) else: alt_aspect = None if rel_rvar is None: if src_path_id.is_objtype_path(): if aspect == 'identity': src_aspect = 'value' else: src_aspect = 'source' else: src_aspect = aspect if isinstance(src_path_id.rptr(), irutils.TupleIndirectionLink): rel_rvar = maybe_get_path_rvar(rel, src_path_id, aspect=src_aspect, env=env) if rel_rvar is None: rel_rvar = maybe_get_path_rvar(rel, src_path_id.src_path(), aspect=src_aspect, env=env) else: rel_rvar = maybe_get_path_rvar(rel, src_path_id, aspect=src_aspect, env=env) if src_aspect != 'source' and path_id != src_path_id: rel_rvar = maybe_get_path_rvar(rel, src_path_id, aspect='source', env=env) if rel_rvar is None and alt_aspect is not None: # There is no source range var for the requested aspect, # check if there is a cached var with less specificity. var = rel.path_namespace.get((path_id, alt_aspect)) if var is not None: put_path_var(rel, path_id, var, aspect=aspect, env=env) return var if rel_rvar is None: raise LookupError(f'there is no range var for ' f'{src_path_id} {src_aspect} in {rel}') source_rel = rel_rvar.query drilldown_path_id = map_path_id(path_id, rel.view_path_id_map) if source_rel in env.root_rels and len(source_rel.path_scope) == 1: if not drilldown_path_id.is_objtype_path() and ptrcls is not None: outer_path_id = drilldown_path_id.src_path() else: outer_path_id = drilldown_path_id path_id_map = {outer_path_id: next(iter(source_rel.path_scope))} drilldown_path_id = map_path_id(drilldown_path_id, path_id_map) outvar = get_path_output(source_rel, drilldown_path_id, ptr_info=ptr_info, aspect=aspect, env=env) var = dbobj.get_rvar_var(rel_rvar, outvar) put_path_var(rel, path_id, var, aspect=aspect, env=env) if isinstance(var, pgast.TupleVar): for element in var.elements: put_path_var(rel, element.path_id, element.val, aspect=aspect, env=env) return var
def _get_rel_path_output(rel: pgast.BaseRelation, path_id: irast.PathId, *, aspect: str, ptr_info: typing.Optional[ pg_types.PointerStorageInfo] = None, env: context.Environment) -> pgast.OutputVar: if path_id.is_objtype_path(): if aspect == 'identity': aspect = 'value' if aspect != 'value': raise LookupError( f'invalid request for non-scalar path {path_id} {aspect}') if (path_id == rel.path_id or (rel.path_id.is_type_indirection_path() and path_id == rel.path_id.src_path())): path_id = irutils.get_id_path_id(path_id, schema=env.schema) else: if aspect == 'identity': raise LookupError( f'invalid request for scalar path {path_id} {aspect}') elif aspect == 'serialized': aspect = 'value' var = rel.path_outputs.get((path_id, aspect)) if var is not None: return var ptrcls = path_id.rptr() rptr_dir = path_id.rptr_dir() if (rptr_dir is not None and rptr_dir != s_pointers.PointerDirection.Outbound): raise LookupError( f'{path_id} is an inbound pointer and cannot be resolved ' f'on a base relation') if isinstance(rel, pgast.NullRelation): if ptrcls is not None: target = ptrcls.target else: target = path_id.target if ptr_info is not None: name = ptr_info.column_name else: name = env.aliases.get('v') val = typecomp.cast(pgast.Constant(val=None, nullable=True), source_type=target, target_type=target, force=True, env=env) rel.target_list.append(pgast.ResTarget(name=name, val=val)) result = pgast.ColumnRef(name=[name], nullable=True) else: if ptrcls is None: raise ValueError( f'could not resolve trailing pointer class for {path_id}') ptr_info = pg_types.get_pointer_storage_info(ptrcls, resolve_type=False, link_bias=False) result = pgast.ColumnRef(name=[ptr_info.column_name], nullable=not ptrcls.required) rel.path_outputs[path_id, aspect] = result return result
def range_for_ptrcls( ptrcls: s_links.Link, direction: s_pointers.PointerDirection, *, include_overlays: bool=True, env: context.Environment) -> pgast.BaseRangeVar: """"Return a Range subclass corresponding to a given ptr step. If `ptrcls` is a generic link, then a simple RangeVar is returned, otherwise the return value may potentially be a UNION of all tables corresponding to a set of specialized links computed from the given `ptrcls` taking source inheritance into account. """ linkname = ptrcls.shortname endpoint = ptrcls.source tgt_col = pgtypes.get_pointer_storage_info( ptrcls, resolve_type=False, link_bias=True).column_name cols = [ 'std::source', tgt_col ] set_ops = [] ptrclses = set() for source in {endpoint} | set(endpoint.descendants(env.schema)): # Sift through the descendants to see who has this link try: src_ptrcls = source.pointers[linkname].material_type() except KeyError: # This source has no such link, skip it continue else: if src_ptrcls in ptrclses: # Seen this link already continue ptrclses.add(src_ptrcls) table = table_from_ptrcls(src_ptrcls, env=env) qry = pgast.SelectStmt() qry.from_clause.append(table) qry.rptr_rvar = table # Make sure all property references are pulled up properly for colname in cols: selexpr = pgast.ColumnRef( name=[table.alias.aliasname, colname]) qry.target_list.append( pgast.ResTarget(val=selexpr, name=colname)) set_ops.append(('union', qry)) overlays = env.rel_overlays.get(src_ptrcls.shortname) if overlays and include_overlays: for op, cte in overlays: rvar = pgast.RangeVar( relation=cte, alias=pgast.Alias( aliasname=env.aliases.get(cte.name) ) ) qry = pgast.SelectStmt( target_list=[ pgast.ResTarget( val=pgast.ColumnRef( name=[col] ) ) for col in cols ], from_clause=[rvar], ) set_ops.append((op, qry)) rvar = range_from_queryset(set_ops, ptrcls, env=env) return rvar