def type_intersection_set( source_set: irast.Set, stype: s_types.Type, *, optional: bool, ctx: context.ContextLevel, ) -> irast.Set: """Return an interesection of *source_set* with type *stype*.""" arg_type = get_set_type(source_set, ctx=ctx) result = schemactx.apply_intersection(arg_type, stype, ctx=ctx) if result.stype is arg_type: return source_set poly_set = new_set(stype=result.stype, ctx=ctx) rptr = source_set.rptr rptr_specialization = [] if rptr is not None and rptr.ptrref.union_components: # This is a type intersection of a union pointer, most likely # a reverse link path specification. If so, test the union # components against the type expression and record which # components match. This information will be used later # when evaluating the path cardinality, as well as to # route link property references accordingly. for component in rptr.ptrref.union_components: component_endpoint_ref = component.dir_target ctx.env.schema, component_endpoint = irtyputils.ir_typeref_to_type( ctx.env.schema, component_endpoint_ref) if component_endpoint.issubclass(ctx.env.schema, stype): assert isinstance(component, irast.PointerRef) rptr_specialization.append(component) ptrcls = irast.TypeIntersectionLink( arg_type, result.stype, optional=optional, is_empty=result.is_empty, is_subtype=result.is_subtype, rptr_specialization=rptr_specialization, # The type intersection cannot increase the cardinality # of the input set, so semantically, the cardinality # of the type intersection "link" is, at most, ONE. cardinality=qltypes.SchemaCardinality.ONE, ) ptrref = irtyputils.ptrref_from_ptrcls( schema=ctx.env.schema, ptrcls=ptrcls, cache=ctx.env.ptr_ref_cache, typeref_cache=ctx.env.type_ref_cache, ) poly_set.path_id = source_set.path_id.extend( schema=ctx.env.schema, ptrref=ptrref, ) ptr = irast.TypeIntersectionPointer( source=source_set, target=poly_set, ptrref=ptrref, direction=poly_set.path_id.rptr_dir(), optional=optional, ) poly_set.rptr = ptr return poly_set
def computable_ptr_set( rptr: irast.Pointer, *, unnest_fence: bool=False, same_computable_scope: bool=False, from_default_expr: bool=False, ctx: context.ContextLevel) -> irast.Set: """Return ir.Set for a pointer defined as a computable.""" ptrcls = irtyputils.ptrcls_from_ptrref(rptr.ptrref, schema=ctx.env.schema) source_set = rptr.source source_scls = get_set_type(source_set, ctx=ctx) # process_view() may generate computable pointer expressions # in the form "self.linkname". To prevent infinite recursion, # self must resolve to the parent type of the view NOT the view # type itself. Similarly, when resolving computable link properties # make sure that we use ptrcls.derived_from. if source_scls.is_view(ctx.env.schema): source_set_stype = source_scls.peel_view(ctx.env.schema) source_set = new_set_from_set( source_set, stype=source_set_stype, preserve_scope_ns=True, ctx=ctx) source_set.shape = [] if source_set.rptr is not None: schema = ctx.env.schema source_rptrref = source_set.rptr.ptrref source_rptrcls = irtyputils.ptrcls_from_ptrref( source_rptrref, schema=schema) bases = source_rptrcls.get_bases(schema) if bases: base = bases.first(schema) if (not base.generic(schema) and ptrcls.is_link_property(schema)): source_rptrref = irtyputils.ptrref_from_ptrcls( source_ref=source_rptrref.dir_source, target_ref=source_rptrref.dir_target, direction=source_rptrref.direction, parent_ptr=source_rptrref.parent_ptr, ptrcls=base, schema=schema, ) source_set.rptr = irast.Pointer( source=source_set.rptr.source, target=source_set, ptrref=source_rptrref, direction=source_set.rptr.direction, ) stmtctx.ensure_ptrref_cardinality( base, source_set.rptr.ptrref, ctx=ctx) qlctx: typing.Optional[context.ContextLevel] inner_source_path_id: typing.Optional[irast.PathId] try: qlexpr, qlctx, inner_source_path_id, path_id_ns = \ ctx.source_map[ptrcls] except KeyError: if from_default_expr: comp_expr = ptrcls.get_default(ctx.env.schema) else: comp_expr = ptrcls.get_expr(ctx.env.schema) if comp_expr is None: ptrcls_sn = ptrcls.get_shortname(ctx.env.schema) raise ValueError( f'{ptrcls_sn!r} is not a computable pointer') qlexpr = qlparser.parse(comp_expr.text) # NOTE: Validation of the expression type is not the concern # of this function. For any non-object pointer target type, # the default expression must be assignment-cast into that # type. target_scls = ptrcls.get_target(ctx.env.schema) assert target_scls is not None if not target_scls.is_object_type(): qlexpr = qlast.TypeCast( type=astutils.type_to_ql_typeref( target_scls, schema=ctx.env.schema), expr=qlexpr, ) qlexpr = astutils.ensure_qlstmt(qlexpr) qlctx = None inner_source_path_id = None path_id_ns = None if qlctx is None: # Schema-level computable, completely detached context newctx = ctx.detached else: newctx = _get_computable_ctx( rptr=rptr, source=source_set, source_scls=source_scls, inner_source_path_id=inner_source_path_id, path_id_ns=path_id_ns, same_scope=same_computable_scope, qlctx=qlctx, ctx=ctx) if ptrcls.is_link_property(ctx.env.schema): source_path_id = rptr.source.path_id.ptr_path() else: source_path_id = rptr.target.path_id.src_path() result_stype = ptrcls.get_target(ctx.env.schema) result_path_id = pathctx.extend_path_id( source_path_id, ptrcls=ptrcls, direction=s_pointers.PointerDirection.Outbound, target=result_stype, ns=ctx.path_id_namespace, ctx=ctx) with newctx() as subctx: subctx.view_scls = result_stype subctx.view_rptr = context.ViewRPtr( source_scls, ptrcls=ptrcls, rptr=rptr) subctx.anchors[qlast.Source] = source_set subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema) subctx.partial_path_prefix = source_set if isinstance(qlexpr, qlast.Statement) and unnest_fence: subctx.stmt_metadata[qlexpr] = context.StatementMetadata( is_unnest_fence=True) comp_ir_set = ensure_set( dispatch.compile(qlexpr, ctx=subctx), ctx=subctx) comp_ir_set_copy = new_set_from_set(comp_ir_set, ctx=ctx) pending_cardinality = ctx.pending_cardinality.get(ptrcls) if pending_cardinality is not None and not pending_cardinality.from_parent: stmtctx.get_pointer_cardinality_later( ptrcls=ptrcls, irexpr=comp_ir_set_copy, specified_card=pending_cardinality.specified_cardinality, source_ctx=pending_cardinality.source_ctx, ctx=ctx) stmtctx.enforce_pointer_cardinality(ptrcls, comp_ir_set_copy, ctx=ctx) comp_ir_set = new_set_from_set( comp_ir_set, path_id=result_path_id, rptr=rptr, ctx=ctx) rptr.target = comp_ir_set return comp_ir_set
def computable_ptr_set(rptr: irast.Pointer, *, unnest_fence: bool = False, same_computable_scope: bool = False, ctx: context.ContextLevel) -> irast.Set: """Return ir.Set for a pointer defined as a computable.""" ptrcls = irtyputils.ptrcls_from_ptrref(rptr.ptrref, schema=ctx.env.schema) source_set = rptr.source source_scls = get_set_type(source_set, ctx=ctx) # process_view() may generate computable pointer expressions # in the form "self.linkname". To prevent infinite recursion, # self must resolve to the parent type of the view NOT the view # type itself. Similarly, when resolving computable link properties # make sure that we use ptrcls.derived_from. if source_scls.is_view(ctx.env.schema): source_set_stype = source_scls.peel_view(ctx.env.schema) source_set = new_set_from_set(source_set, stype=source_set_stype, preserve_scope_ns=True, ctx=ctx) source_set.shape = [] if source_set.rptr is not None: schema = ctx.env.schema source_rptrref = source_set.rptr.ptrref source_rptrcls = irtyputils.ptrcls_from_ptrref(source_rptrref, schema=schema) derived_from = source_rptrcls.get_derived_from(schema) if (derived_from is not None and not derived_from.generic(schema) and derived_from.get_derived_from(schema) is not None and ptrcls.is_link_property(schema)): source_set.rptr.ptrref = irtyputils.ptrref_from_ptrcls( source_ref=source_rptrref.dir_source, target_ref=source_rptrref.dir_target, direction=source_rptrref.direction, parent_ptr=source_rptrref.parent_ptr, ptrcls=derived_from, schema=schema, ) stmtctx.ensure_ptrref_cardinality(derived_from, source_set.rptr.ptrref, ctx=ctx) try: qlexpr, qlctx, inner_source_path_id, path_id_ns = \ ctx.source_map[ptrcls] except KeyError: ptrcls_default = ptrcls.get_default(ctx.env.schema) if not ptrcls_default: ptrcls_sn = ptrcls.get_shortname(ctx.env.schema) raise ValueError(f'{ptrcls_sn!r} is not a computable pointer') qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls_default.text)) qlctx = None inner_source_path_id = None path_id_ns = None if qlctx is None: # Schema-level computable, completely detached context newctx = ctx.detached else: newctx = _get_computable_ctx(rptr=rptr, source=source_set, source_scls=source_scls, inner_source_path_id=inner_source_path_id, path_id_ns=path_id_ns, same_scope=same_computable_scope, qlctx=qlctx, ctx=ctx) if ptrcls.is_link_property(ctx.env.schema): source_path_id = rptr.source.path_id.ptr_path() else: source_path_id = rptr.target.path_id.src_path() result_stype = ptrcls.get_target(ctx.env.schema) result_path_id = pathctx.extend_path_id( source_path_id, ptrcls=ptrcls, direction=s_pointers.PointerDirection.Outbound, target=result_stype, ns=ctx.path_id_namespace, ctx=ctx) with newctx() as subctx: subctx.view_scls = result_stype subctx.view_rptr = context.ViewRPtr(source_scls, ptrcls=ptrcls, rptr=rptr) subctx.anchors[qlast.Source] = source_set subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema) subctx.partial_path_prefix = source_set if isinstance(qlexpr, qlast.Statement) and unnest_fence: subctx.stmt_metadata[qlexpr] = context.StatementMetadata( is_unnest_fence=True) comp_ir_set = dispatch.compile(qlexpr, ctx=subctx) pending_cardinality = ctx.pending_cardinality.get(ptrcls) if pending_cardinality is not None and not pending_cardinality.from_parent: comp_ir_set_copy = new_set_from_set(comp_ir_set, ctx=ctx) stmtctx.get_pointer_cardinality_later( ptrcls=ptrcls, irexpr=comp_ir_set_copy, specified_card=pending_cardinality.specified_cardinality, source_ctx=pending_cardinality.source_ctx, ctx=ctx) def _check_cardinality(ctx): if ptrcls.singular(ctx.env.schema): stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx) stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx) comp_ir_set = new_set_from_set(comp_ir_set, path_id=result_path_id, rptr=rptr, ctx=ctx) rptr.target = comp_ir_set return comp_ir_set