def resolve_ptr( near_endpoint: s_obj.Object, pointer_name: str, *, far_endpoints: Iterable[s_obj.Object] = (), direction: s_pointers.PointerDirection = ( s_pointers.PointerDirection.Outbound ), source_context: Optional[parsing.ParserContext] = None, track_ref: Optional[Union[qlast.Base, Literal[False]]], ctx: context.ContextLevel, ) -> s_pointers.Pointer: if not isinstance(near_endpoint, s_sources.Source): # Reference to a property on non-object msg = 'invalid property reference on a primitive type expression' raise errors.InvalidReferenceError(msg, context=source_context) ptr: Optional[s_pointers.Pointer] = None if direction is s_pointers.PointerDirection.Outbound: ptr = near_endpoint.maybe_get_ptr( ctx.env.schema, s_name.UnqualName(pointer_name), ) if ptr is not None: ref = ptr.get_nearest_non_derived_parent(ctx.env.schema) if track_ref is not False: ctx.env.add_schema_ref(ref, track_ref) else: ptrs = near_endpoint.getrptrs(ctx.env.schema, pointer_name, sources=far_endpoints) if ptrs: if track_ref is not False: for p in ptrs: ctx.env.add_schema_ref( p.get_nearest_non_derived_parent(ctx.env.schema), track_ref) opaque = not far_endpoints ctx.env.schema, ptr = s_pointers.get_or_create_union_pointer( ctx.env.schema, ptrname=s_name.UnqualName(pointer_name), source=near_endpoint, direction=direction, components=ptrs, opaque=opaque, modname=ctx.derived_target_module, ) if ptr is not None: return ptr if isinstance(near_endpoint, s_links.Link): vname = near_endpoint.get_verbosename(ctx.env.schema, with_parent=True) msg = f'{vname} has no property {pointer_name!r}' elif direction == s_pointers.PointerDirection.Outbound: msg = (f'{near_endpoint.get_verbosename(ctx.env.schema)} ' f'has no link or property {pointer_name!r}') else: nep_name = near_endpoint.get_displayname(ctx.env.schema) path = f'{nep_name}.{direction}{pointer_name}' msg = f'{path!r} does not resolve to any known path' err = errors.InvalidReferenceError(msg, context=source_context) if direction is s_pointers.PointerDirection.Outbound: near_enpoint_pointers = near_endpoint.get_pointers(ctx.env.schema) s_utils.enrich_schema_lookup_error( err, s_name.UnqualName(pointer_name), modaliases=ctx.modaliases, item_type=s_pointers.Pointer, collection=near_enpoint_pointers.objects(ctx.env.schema), schema=ctx.env.schema, ) raise err
def resolve_ptr( near_endpoint: s_obj.Object, pointer_name: str, *, upcoming_intersections: Sequence[s_types.Type] = (), far_endpoints: Iterable[s_obj.Object] = (), direction: s_pointers.PointerDirection = ( s_pointers.PointerDirection.Outbound), source_context: Optional[parsing.ParserContext] = None, track_ref: Optional[Union[qlast.Base, Literal[False]]], ctx: context.ContextLevel, ) -> s_pointers.Pointer: if not isinstance(near_endpoint, s_sources.Source): # Reference to a property on non-object msg = 'invalid property reference on a primitive type expression' raise errors.InvalidReferenceError(msg, context=source_context) ptr: Optional[s_pointers.Pointer] = None if direction is s_pointers.PointerDirection.Outbound: ptr = near_endpoint.maybe_get_ptr( ctx.env.schema, s_name.UnqualName(pointer_name), ) if ptr is not None: ref = ptr.get_nearest_non_derived_parent(ctx.env.schema) if track_ref is not False: ctx.env.add_schema_ref(ref, track_ref) else: ptrs = near_endpoint.getrptrs(ctx.env.schema, pointer_name, sources=far_endpoints) if ptrs: if track_ref is not False: # If this reverse pointer access is followed by # intersections, we filter out any pointers that # couldn't be picked up by the intersections. This avoids # creating spurious dependencies when reverse # links are used in schemas. dep_ptrs = { ptr for ptr in ptrs if (src := ptr.get_source(ctx.env.schema)) and all( src.issubclass(ctx.env.schema, typ) or any( dsrc.issubclass(ctx.env.schema, typ) for dsrc in src.descendants(ctx.env.schema)) for typ in upcoming_intersections) } for p in dep_ptrs: ctx.env.add_schema_ref( p.get_nearest_non_derived_parent(ctx.env.schema), track_ref) opaque = not far_endpoints ctx.env.schema, ptr = s_pointers.get_or_create_union_pointer( ctx.env.schema, ptrname=s_name.UnqualName(pointer_name), source=near_endpoint, direction=direction, components=ptrs, opaque=opaque, modname=ctx.derived_target_module, ) if ptr is not None: return ptr if isinstance(near_endpoint, s_links.Link): vname = near_endpoint.get_verbosename(ctx.env.schema, with_parent=True) msg = f'{vname} has no property {pointer_name!r}' elif direction == s_pointers.PointerDirection.Outbound: msg = (f'{near_endpoint.get_verbosename(ctx.env.schema)} ' f'has no link or property {pointer_name!r}') else: nep_name = near_endpoint.get_displayname(ctx.env.schema) path = f'{nep_name}.{direction}{pointer_name}' msg = f'{path!r} does not resolve to any known path' err = errors.InvalidReferenceError(msg, context=source_context) if direction is s_pointers.PointerDirection.Outbound: near_enpoint_pointers = near_endpoint.get_pointers(ctx.env.schema) s_utils.enrich_schema_lookup_error( err, s_name.UnqualName(pointer_name), modaliases=ctx.modaliases, item_type=s_pointers.Pointer, collection=near_enpoint_pointers.objects(ctx.env.schema), schema=ctx.env.schema, ) raise err
def derive_view(stype: s_obj.Object, source: typing.Optional[s_nodes.Node] = None, target: typing.Optional[s_nodes.Node] = None, *qualifiers, derived_name: typing.Optional[sn.SchemaName] = None, derived_name_quals: typing.Optional[typing.Sequence[str]] = (), derived_name_base: typing.Optional[str] = None, merge_bases=None, preserve_shape: bool = False, is_insert: bool = False, is_update: bool = False, attrs: typing.Optional[dict] = None, ctx: context.ContextLevel) -> s_obj.Object: if source is None: source = stype if derived_name is None and (ctx.derived_target_module or source is stype): derived_name = derive_view_name(stype=stype, derived_name_quals=derived_name_quals, derived_name_base=derived_name_base, ctx=ctx) if isinstance(stype, s_abc.Type): if is_insert: vtype = s_types.ViewType.Insert elif is_update: vtype = s_types.ViewType.Update else: vtype = s_types.ViewType.Select if attrs is None: attrs = {} else: attrs = dict(attrs) attrs['view_type'] = vtype if isinstance(stype, s_abc.Collection): ctx.env.schema, derived = stype.derive_subtype(ctx.env.schema, name=derived_name) else: if stype.get_name(ctx.env.schema) == derived_name: qualifiers = list(qualifiers) qualifiers.append(ctx.aliases.get('d')) ctx.env.schema, derived = stype.derive(ctx.env.schema, source, target, *qualifiers, merge_bases=merge_bases, name=derived_name, mark_derived=True, attrs=attrs) if not stype.generic(ctx.env.schema): if isinstance(derived, s_sources.Source): scls_pointers = stype.get_pointers(ctx.env.schema) derived_own_pointers = derived.get_own_pointers(ctx.env.schema) for pn, ptr in derived_own_pointers.items(ctx.env.schema): # This is a view of a view. Make sure query-level # computable expressions for pointers are carried over. src_ptr = scls_pointers.get(ctx.env.schema, pn) computable_data = ctx.source_map.get(src_ptr) if computable_data is not None: ctx.source_map[ptr] = computable_data if isinstance(derived, s_abc.Type): ctx.view_nodes[derived.get_name(ctx.env.schema)] = derived if preserve_shape and stype in ctx.env.view_shapes: ctx.env.view_shapes[derived] = ctx.env.view_shapes[stype] return derived