def process_view( *, stype: s_nodes.Node, path_id: irast.PathId, elements: typing.List[qlast.ShapeElement], view_rptr: typing.Optional[context.ViewRPtr]=None, view_name: typing.Optional[sn.SchemaName]=None, is_insert: bool=False, is_update: bool=False, ctx: context.CompilerContext) -> s_nodes.Node: cache_key = tuple(elements) view_scls = ctx.shape_type_cache.get(cache_key) if view_scls is not None: return view_scls with ctx.newscope(fenced=True, temporary=True) as scopectx: scopectx.path_scope.attach_path(path_id) if ctx.expr_exposed or is_insert or is_update: view_path_id_ns = irast.WeakNamespace(ctx.aliases.get('ns')) scopectx.path_id_namespace |= {view_path_id_ns} scopectx.path_scope.namespaces.add(view_path_id_ns) else: view_path_id_ns = None view_scls = _process_view( stype=stype, path_id=path_id, elements=elements, view_rptr=view_rptr, view_name=view_name, is_insert=is_insert, is_update=is_update, path_id_namespace=view_path_id_ns, ctx=scopectx ) ctx.shape_type_cache[cache_key] = view_scls return view_scls
def newctx(): with ctx.new() as subctx: subctx.class_view_overrides = {} subctx.partial_path_prefix = None subctx.modaliases = qlctx.modaliases.copy() subctx.aliased_views = qlctx.aliased_views.new_child() if source_scls.is_view(): subctx.aliased_views[source.scls.name] = None subctx.source_map = qlctx.source_map.copy() subctx.view_nodes = qlctx.view_nodes.copy() subctx.view_sets = qlctx.view_sets.copy() subctx.view_map = qlctx.view_map.new_child() source_scope = pathctx.get_set_scope(rptr.source, ctx=ctx) if source_scope and source_scope.namespaces: subctx.path_id_namespace += tuple(source_scope.namespaces) subctx.pending_stmt_own_path_id_namespace = \ irast.WeakNamespace(ctx.aliases.get('ns')) subns = subctx.pending_stmt_full_path_id_namespace = \ {subctx.pending_stmt_own_path_id_namespace} self_view = ctx.view_sets.get(source.scls) if self_view: if self_view.path_id.namespace: subns.update(self_view.path_id.namespace) inner_path_id = self_view.path_id.merge_namespace( subctx.path_id_namespace + tuple(subns)) else: if source.path_id.namespace: subns.update(source.path_id.namespace) if inner_source_path_id is not None: # The path id recorded in the source map may # contain namespaces referring to a temporary # scope subtree used by `process_view()`. # Since we recompile the computable expression # using the current path id namespace, the # original source path id needs to be fixed. inner_path_id = inner_source_path_id \ .strip_namespace(qlctx.path_id_namespace) \ .merge_namespace(subctx.path_id_namespace) else: inner_path_id = pathctx.get_path_id(source.scls, ctx=subctx) inner_path_id = inner_path_id.merge_namespace(subns) remapped_source = new_set_from_set(rptr.source, ctx=subctx) remapped_source.path_id = \ remapped_source.path_id.merge_namespace(subns) remapped_source.rptr = rptr.source.rptr subctx.view_map[inner_path_id] = remapped_source yield subctx
def declare_view(expr: qlast.Base, alias: str, *, fully_detached: bool = False, temporary_scope: bool = True, ctx: context.ContextLevel) -> irast.Set: with ctx.newscope(temporary=temporary_scope, fenced=True) as subctx: if not fully_detached: cached_view_set = ctx.expr_view_cache.get((expr, alias)) # Detach the view namespace and record the prefix # in the parent statement's fence node. add_ns = (irast.WeakNamespace(ctx.aliases.get('ns')), ) subctx.path_id_namespace = subctx.path_id_namespace + add_ns ctx.path_scope.namespaces.add(subctx.path_id_namespace[-1]) else: cached_view_set = None if ctx.stmt is not None: subctx.stmt = ctx.stmt.parent_stmt if cached_view_set is not None: subctx.view_scls = cached_view_set.scls view_name = cached_view_set.scls.name else: if isinstance(alias, s_name.SchemaName): basename = alias else: basename = s_name.SchemaName(module='__view__', name=alias) view_name = s_name.SchemaName( module=ctx.derived_target_module or '_', name=s_obj.NamedObject.get_specialized_name( basename, ctx.aliases.get('w'))) subctx.toplevel_result_view_name = view_name view_set = dispatch.compile(astutils.ensure_qlstmt(expr), ctx=subctx) # The view path id _itself_ should not be in the nested namespace. view_set.path_id = view_set.path_id.replace_namespace( ctx.path_id_namespace) ctx.aliased_views[alias] = view_set.scls ctx.path_scope_map[view_set] = subctx.path_scope ctx.expr_view_cache[expr, alias] = view_set return view_set
def computable_ptr_set(rptr: irast.Pointer, *, unnest_fence: bool = False, ctx: context.ContextLevel) -> irast.Set: """Return ir.Set for a pointer defined as a computable.""" ptrcls = rptr.ptrcls # Must use an entirely separate context, as the computable # expression is totally independent from the surrounding query. subctx = stmtctx.init_context(schema=ctx.schema) self_ = rptr.source source_scls = self_.scls # 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 rptr.ptrcls.derived_from. if source_scls.is_view(): self_ = copy.copy(self_) self_.scls = source_scls.peel_view() self_.shape = [] if self_.rptr is not None: derived_from = self_.rptr.ptrcls.derived_from if (derived_from is not None and not derived_from.generic() and derived_from.derived_from is not None and ptrcls.is_link_property()): self_.rptr.ptrcls = derived_from subctx.anchors[qlast.Source] = self_ subctx.aliases = ctx.aliases subctx.stmt = ctx.stmt subctx.view_scls = ptrcls.target subctx.view_rptr = context.ViewRPtr(source_scls, ptrcls=ptrcls, rptr=rptr) subctx.toplevel_stmt = ctx.toplevel_stmt subctx.path_scope = ctx.path_scope subctx.pending_cardinality = ctx.pending_cardinality subctx.completion_work = ctx.completion_work subctx.pointer_derivation_map = ctx.pointer_derivation_map subctx.class_shapes = ctx.class_shapes subctx.all_sets = ctx.all_sets subctx.path_scope_map = ctx.path_scope_map subctx.scope_id_ctr = ctx.scope_id_ctr subctx.expr_exposed = ctx.expr_exposed if ptrcls.is_link_property(): source_path_id = rptr.source.path_id.ptr_path() else: source_path_id = rptr.target.path_id.src_path() path_id = source_path_id.extend(ptrcls, s_pointers.PointerDirection.Outbound, ptrcls.target) subctx.path_scope.contain_path(path_id) try: qlexpr, qlctx = ctx.source_map[ptrcls] except KeyError: if not ptrcls.default: raise ValueError( f'{ptrcls.shortname!r} is not a computable pointer') if isinstance(ptrcls.default, s_expr.ExpressionText): qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls.default)) else: qlexpr = qlast.Constant(value=ptrcls.default) qlctx = None else: subctx.modaliases = qlctx.modaliases.copy() subctx.aliased_views = qlctx.aliased_views.new_child() if source_scls.is_view(): subctx.aliased_views[self_.scls.name] = None subctx.source_map = qlctx.source_map.copy() subctx.view_nodes = qlctx.view_nodes.copy() subctx.view_sets = qlctx.view_sets.copy() subctx.view_map = qlctx.view_map.new_child() subctx.singletons = qlctx.singletons.copy() subctx.path_id_namespce = qlctx.path_id_namespace if qlctx is None: # This is a schema-level computable expression, put all # class refs into a separate namespace. subctx.path_id_namespace = (subctx.aliases.get('ns'), ) else: subctx.pending_stmt_own_path_id_namespace = \ irast.WeakNamespace(ctx.aliases.get('ns')) subns = subctx.pending_stmt_full_path_id_namespace = \ {subctx.pending_stmt_own_path_id_namespace} self_view = ctx.view_sets.get(self_.scls) if self_view: if self_view.path_id.namespace: subns.update(self_view.path_id.namespace) inner_path_id = self_view.path_id.merge_namespace( subctx.path_id_namespace + tuple(subns)) else: if self_.path_id.namespace: subns.update(self_.path_id.namespace) inner_path_id = pathctx.get_path_id( self_.scls, ctx=subctx).merge_namespace(subns) remapped_source = new_set_from_set(rptr.source, ctx=subctx) remapped_source.path_id = \ remapped_source.path_id.merge_namespace(subns) subctx.view_map[inner_path_id] = remapped_source 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) if ptrcls in ctx.pending_cardinality: comp_ir_set_copy = copy.copy(comp_ir_set) stmtctx.get_pointer_cardinality_later(ptrcls=ptrcls, irexpr=comp_ir_set_copy, ctx=ctx) def _check_cardinality(ctx): if ptrcls.singular(): stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx) stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx) comp_ir_set.scls = ptrcls.target comp_ir_set.path_id = path_id comp_ir_set.rptr = rptr rptr.target = comp_ir_set return comp_ir_set