def _rewrite_config_insert(ir_set: irast.Set, *, ctx: context.CompilerContextLevel) -> irast.Set: relctx.add_type_rel_overlay( ir_set.typeref, 'replace', pgast.NullRelation(path_id=ir_set.path_id), path_id=ir_set.path_id, ctx=ctx, ) # Config objects have derived computed ids, # so the autogenerated id must not be returned. ir_set.shape = list( filter( lambda el: el[0].rptr.ptrref.shortname.name != 'id', ir_set.shape, )) for el, _ in ir_set.shape: if isinstance(el.expr, irast.InsertStmt): el.shape = list( filter( lambda e: e[0].rptr.ptrref.shortname.name != 'id', el.shape, )) result = _rewrite_config_insert(el.expr.subject, ctx=ctx) el.expr = irast.SelectStmt( result=result, parent_stmt=el.expr.parent_stmt, ) return ir_set
def ensure_stmt(expr: irast.Base, *, ctx: context.ContextLevel) -> irast.Stmt: if not isinstance(expr, irast.Stmt): expr = irast.SelectStmt( result=ensure_set(expr, ctx=ctx), implicit_wrapper=True, ) return expr
def _rewrite_config_insert(ir_set: irast.Set, *, ctx: context.CompilerContextLevel) -> irast.Set: overwrite_query = pgast.SelectStmt() id_expr = pgast.FuncCall( name=( 'edgedbext', 'uuid_generate_v1mc', ), args=[], ) pathctx.put_path_identity_var(overwrite_query, ir_set.path_id, id_expr, force=True, env=ctx.env) pathctx.put_path_value_var(overwrite_query, ir_set.path_id, id_expr, force=True, env=ctx.env) pathctx.put_path_source_rvar(overwrite_query, ir_set.path_id, relctx.rvar_for_rel(pgast.NullRelation(), ctx=ctx), env=ctx.env) relctx.add_type_rel_overlay( ir_set.typeref, 'replace', overwrite_query, path_id=ir_set.path_id, ctx=ctx, ) # Config objects have derived computed ids, # so the autogenerated id must not be returned. ir_set.shape = tuple( filter( lambda el: ((rptr := el[0].rptr) is not None and rptr.ptrref. shortname.name != 'id'), ir_set.shape, )) for el, _ in ir_set.shape: if isinstance(el.expr, irast.InsertStmt): el.shape = tuple( filter( lambda e: ((rptr := e[0].rptr) is not None and rptr.ptrref. shortname.name != 'id'), el.shape, )) result = _rewrite_config_insert(el.expr.subject, ctx=ctx) el.expr = irast.SelectStmt( result=result, parent_stmt=el.expr.parent_stmt, ) return ir_set
def compile_GroupQuery( expr: qlast.Base, *, ctx: context.ContextLevel) -> irast.Set: raise errors.UnsupportedFeatureError( "'GROUP' statement is not currently implemented", context=expr.context) with ctx.subquery() as ictx: stmt = irast.GroupStmt() init_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx) typename = s_name.Name( module='__group__', name=ctx.aliases.get('Group')) obj = ctx.env.get_track_schema_object('std::BaseObject') stmt.group_path_id = pathctx.get_path_id( obj, typename=typename, ctx=ictx) pathctx.register_set_in_scope(stmt.group_path_id, ctx=ictx) with ictx.newscope(fenced=True) as subjctx: subject_set = setgen.scoped_set( dispatch.compile(expr.subject, ctx=subjctx), ctx=subjctx) alias = expr.subject_alias or subject_set.path_id.target_name_hint stmt.subject = stmtctx.declare_inline_view( subject_set, alias, ctx=ictx) with subjctx.new() as grpctx: stmt.groupby = compile_groupby_clause( expr.groupby, ctx=grpctx) with ictx.subquery() as isctx, isctx.newscope(fenced=True) as sctx: o_stmt = sctx.stmt = irast.SelectStmt() o_stmt.result = compile_result_clause( expr.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=expr.result_alias, view_name=ctx.toplevel_result_view_name, ctx=sctx) clauses.compile_where_clause( o_stmt, expr.where, ctx=sctx) o_stmt.orderby = clauses.compile_orderby_clause( expr.orderby, ctx=sctx) o_stmt.offset = clauses.compile_limit_offset_clause( expr.offset, ctx=sctx) o_stmt.limit = clauses.compile_limit_offset_clause( expr.limit, ctx=sctx) stmt.result = setgen.scoped_set(o_stmt, ctx=sctx) result = fini_stmt(stmt, expr, ctx=ictx, parent_ctx=ctx) return result
def compile_ForQuery( qlstmt: qlast.ForQuery, *, ctx: context.ContextLevel) -> irast.Base: with ctx.subquery() as sctx: stmt = irast.SelectStmt() init_stmt(stmt, qlstmt, ctx=sctx, parent_ctx=ctx) if qlstmt.offset is not None or qlstmt.limit is not None: # LIMIT and OFFSET are infix operators with both # operands being SET OF, so we need to compile # the body of the statement behind a fence. sctx.path_scope = sctx.path_scope.attach_fence() with sctx.newscope(fenced=True) as scopectx: iterator = qlstmt.iterator if isinstance(iterator, qlast.Set) and len(iterator.elements) == 1: iterator = iterator.elements[0] iterator_view = stmtctx.declare_view( iterator, qlstmt.iterator_alias, ctx=scopectx) stmt.iterator_stmt = setgen.new_set_from_set( iterator_view, ctx=scopectx) iterator_scope = scopectx.path_scope_map.get(iterator_view) pathctx.register_set_in_scope(stmt.iterator_stmt, ctx=sctx) node = sctx.path_scope.find_descendant(stmt.iterator_stmt.path_id) node.attach_subtree(iterator_scope) stmt.result = compile_result_clause( qlstmt.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=qlstmt.result_alias, view_name=ctx.toplevel_result_view_name, forward_rptr=True, ctx=sctx) clauses.compile_where_clause( stmt, qlstmt.where, ctx=sctx) stmt.orderby = clauses.compile_orderby_clause( qlstmt.orderby, ctx=sctx) if qlstmt.offset is not None or qlstmt.limit is not None: sctx.path_scope = sctx.path_scope.parent stmt.offset = clauses.compile_limit_offset_clause( qlstmt.offset, ctx=sctx) stmt.limit = clauses.compile_limit_offset_clause( qlstmt.limit, ctx=sctx) result = fini_stmt(stmt, qlstmt, ctx=sctx, parent_ctx=ctx) return result
def compile_SelectQuery( expr: qlast.SelectQuery, *, ctx: context.ContextLevel) -> irast.Set: if astutils.is_degenerate_select(expr) and ctx.toplevel_stmt is not None: # Compile implicit "SELECT Path" as "Path" with ctx.new() as sctx: process_with_block(expr, ctx=sctx, parent_ctx=ctx) sctx.aliased_views = ctx.aliased_views.new_child() sctx.modaliases = ctx.modaliases.copy() sctx.anchors = ctx.anchors.copy() result = compile_result_clause( expr.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, view_name=ctx.toplevel_result_view_name, ctx=sctx) result = fini_stmt(result, expr, ctx=sctx, parent_ctx=ctx) return result with ctx.subquery() as sctx: stmt = irast.SelectStmt() init_stmt(stmt, expr, ctx=sctx, parent_ctx=ctx) if expr.implicit: # Make sure path prefix does not get blown away by # implicit subqueries. sctx.partial_path_prefix = ctx.partial_path_prefix stmt.result = compile_result_clause( expr.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=expr.result_alias, view_name=ctx.toplevel_result_view_name, ctx=sctx) clauses.compile_where_clause( stmt, expr.where, ctx=sctx) stmt.orderby = clauses.compile_orderby_clause( expr.orderby, ctx=sctx) stmt.offset = clauses.compile_limit_offset_clause( expr.offset, ctx=sctx) stmt.limit = clauses.compile_limit_offset_clause( expr.limit, ctx=sctx) result = fini_stmt(stmt, expr, ctx=sctx, parent_ctx=ctx) return result
def compile_SelectQuery( expr: qlast.SelectQuery, *, ctx: context.ContextLevel) -> irast.Set: with ctx.subquery() as sctx: stmt = irast.SelectStmt() init_stmt(stmt, expr, ctx=sctx, parent_ctx=ctx) if expr.implicit: # Make sure path prefix does not get blown away by # implicit subqueries. sctx.partial_path_prefix = ctx.partial_path_prefix stmt.implicit_wrapper = True if ( (ctx.expr_exposed or sctx.stmt is ctx.toplevel_stmt) and ctx.implicit_limit and expr.limit is None and not ctx.inhibit_implicit_limit ): expr.limit = qlast.IntegerConstant(value=str(ctx.implicit_limit)) stmt.result = compile_result_clause( expr.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=expr.result_alias, view_name=ctx.toplevel_result_view_name, ctx=sctx) clauses.compile_where_clause( stmt, expr.where, ctx=sctx) stmt.orderby = clauses.compile_orderby_clause( expr.orderby, ctx=sctx) stmt.offset = clauses.compile_limit_offset_clause( expr.offset, ctx=sctx) stmt.limit = clauses.compile_limit_offset_clause( expr.limit, ctx=sctx) result = fini_stmt(stmt, expr, ctx=sctx, parent_ctx=ctx) return result
def compile_SelectQuery( expr: qlast.SelectQuery, *, ctx: context.ContextLevel) -> irast.Base: if astutils.is_degenerate_select(expr) and ctx.toplevel_stmt is not None: # Compile implicit "SELECT Path" as "Path" with ctx.new() as sctx: process_with_block(expr, ctx=sctx, parent_ctx=ctx) sctx.aliased_views = ctx.aliased_views.new_child() sctx.modaliases = ctx.modaliases.copy() sctx.anchors = ctx.anchors.copy() result = compile_result_clause( expr.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, view_name=ctx.toplevel_result_view_name, ctx=sctx) result = fini_stmt(result, expr, ctx=sctx, parent_ctx=ctx) return result with ctx.subquery() as sctx: stmt = irast.SelectStmt() init_stmt(stmt, expr, ctx=sctx, parent_ctx=ctx) if expr.implicit: # Make sure path prefix does not get blown away by # implicit subqueries. sctx.partial_path_prefix = ctx.partial_path_prefix compile_limit_offset = False if expr.offset is not None or expr.limit is not None: # LIMIT and OFFSET are infix operators with both # operands being SET OF, so we need to compile # the body of the statement behind a fence. metadata = ctx.stmt_metadata.get(expr) if metadata is None: metadata = context.StatementMetadata() ctx.stmt_metadata[expr] = metadata if not metadata.ignore_offset_limit: metadata.ignore_offset_limit = True compile_limit_offset = True if compile_limit_offset: sctx.toplevel_result_view_name = ctx.toplevel_result_view_name stmt.result = compile_result_clause( expr, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, ctx=sctx) if ctx.toplevel_result_view_name: result_stype = setgen.get_set_type(stmt.result, ctx=ctx) stmt.result.path_id = pathctx.get_expression_path_id( result_stype, ctx=ctx) stmt.offset = clauses.compile_limit_offset_clause( expr.offset, ctx=sctx) stmt.limit = clauses.compile_limit_offset_clause( expr.limit, ctx=sctx) metadata.ignore_offset_limit = False else: stmt.result = compile_result_clause( expr.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=expr.result_alias, view_name=ctx.toplevel_result_view_name, ctx=sctx) clauses.compile_where_clause( stmt, expr.where, ctx=sctx) stmt.orderby = clauses.compile_orderby_clause( expr.orderby, ctx=sctx) result = fini_stmt(stmt, expr, ctx=sctx, parent_ctx=ctx) return result
def compile_DescribeStmt( ql: qlast.DescribeStmt, *, ctx: context.ContextLevel) -> irast.Set: with ctx.subquery() as ictx: stmt = irast.SelectStmt() init_stmt(stmt, ql, ctx=ictx, parent_ctx=ctx) if not ql.object: if ql.language is qltypes.DescribeLanguage.DDL: # DESCRIBE SCHEMA text = s_ddl.ddl_text_from_schema( ctx.env.schema, ) else: raise errors.QueryError( f'cannot describe full schema as {ql.language}') else: modules = [] items: List[str] = [] referenced_classes: List[s_obj.ObjectMeta] = [] objref = ql.object itemclass = objref.itemclass if itemclass is qltypes.SchemaObjectClass.MODULE: modules.append(objref.name) else: itemtype: Optional[Type[s_obj.Object]] = None found = False name: str if objref.module: name = s_name.Name(module=objref.module, name=objref.name) else: name = objref.name if itemclass is not None: itemtype = ( s_obj.ObjectMeta.get_schema_metaclass_for_ql_class( itemclass) ) if (itemclass is None or itemclass is qltypes.SchemaObjectClass.FUNCTION): try: funcs: Tuple[s_func.Function, ...] = ( ictx.env.schema.get_functions( name, module_aliases=ictx.modaliases) ) except errors.InvalidReferenceError: pass else: for func in funcs: items.append(func.get_name(ictx.env.schema)) found = True if not found: obj = schemactx.get_schema_object( objref, item_type=itemtype, ctx=ictx, ) items.append(obj.get_name(ictx.env.schema)) verbose = ql.options.get_flag('VERBOSE') if ql.language is qltypes.DescribeLanguage.DDL: method = s_ddl.ddl_text_from_schema elif ql.language is qltypes.DescribeLanguage.SDL: method = s_ddl.sdl_text_from_schema elif ql.language is qltypes.DescribeLanguage.TEXT: method = s_ddl.descriptive_text_from_schema if not verbose.val: referenced_classes = [s_links.Link, s_lprops.Property] else: raise errors.InternalServerError( f'cannot handle describe language {ql.language}' ) text = method( ctx.env.schema, included_modules=modules, included_items=items, included_ref_classes=referenced_classes, include_module_ddl=False, include_std_ddl=True, ) ct = typegen.type_to_typeref( ctx.env.get_track_schema_type('std::str'), env=ctx.env, ) stmt.result = setgen.ensure_set( irast.StringConstant(value=text, typeref=ct), ctx=ictx, ) result = fini_stmt(stmt, ql, ctx=ictx, parent_ctx=ctx) return result
def compile_ForQuery( qlstmt: qlast.ForQuery, *, ctx: context.ContextLevel) -> irast.Set: with ctx.subquery() as sctx: stmt = irast.SelectStmt() init_stmt(stmt, qlstmt, ctx=sctx, parent_ctx=ctx) with sctx.newscope(fenced=True) as scopectx: iterator_ctx = None if (ctx.expr_exposed and ctx.iterator_ctx is not None and ctx.iterator_ctx is not sctx): iterator_ctx = ctx.iterator_ctx if iterator_ctx is not None: iterator_scope_parent = iterator_ctx.path_scope path_id_ns = iterator_ctx.path_id_namespace else: iterator_scope_parent = sctx.path_scope path_id_ns = sctx.path_id_namespace iterator = qlstmt.iterator if isinstance(iterator, qlast.Set) and len(iterator.elements) == 1: iterator = iterator.elements[0] iterator_view = stmtctx.declare_view( iterator, qlstmt.iterator_alias, path_id_namespace=path_id_ns, ctx=scopectx) iterator_stmt = setgen.new_set_from_set( iterator_view, preserve_scope_ns=True, ctx=scopectx) if iterator_ctx is not None and iterator_ctx.stmt is not None: iterator_ctx.stmt.hoisted_iterators.append(iterator_stmt) stmt.iterator_stmt = iterator_stmt view_scope_info = scopectx.path_scope_map[iterator_view] iterator_scope = view_scope_info.path_scope pathctx.register_set_in_scope( iterator_stmt, path_scope=iterator_scope_parent, ctx=sctx, ) # Iterator symbol is, by construction, outside of the scope # of the UNION argument, but is perfectly legal to be referenced # inside a factoring fence that is an immediate child of this # scope. iterator_scope_parent.factoring_whitelist.add( stmt.iterator_stmt.path_id) node = iterator_scope_parent.find_descendant(iterator_stmt.path_id) if node is not None: node.attach_subtree(iterator_scope) stmt.result = compile_result_clause( qlstmt.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=qlstmt.result_alias, view_name=ctx.toplevel_result_view_name, forward_rptr=True, ctx=sctx) if ((ctx.expr_exposed or sctx.stmt is ctx.toplevel_stmt) and ctx.implicit_limit): stmt.limit = setgen.ensure_set( dispatch.compile( qlast.IntegerConstant(value=str(ctx.implicit_limit)), ctx=sctx, ), ctx=sctx, ) result = fini_stmt(stmt, qlstmt, ctx=sctx, parent_ctx=ctx) return result
def compile_DescribeStmt(ql: qlast.DescribeStmt, *, ctx: context.ContextLevel) -> irast.Set: with ctx.subquery() as ictx: stmt = irast.SelectStmt() init_stmt(stmt, ql, ctx=ictx, parent_ctx=ctx) if ql.object == qlast.DescribeGlobal.Schema: if ql.language is qltypes.DescribeLanguage.DDL: # DESCRIBE SCHEMA text = s_ddl.ddl_text_from_schema(ctx.env.schema, ) else: raise errors.QueryError( f'cannot describe full schema as {ql.language}') ct = typegen.type_to_typeref( ctx.env.get_track_schema_type('std::str'), env=ctx.env, ) stmt.result = setgen.ensure_set( irast.StringConstant(value=text, typeref=ct), ctx=ictx, ) elif ql.object == qlast.DescribeGlobal.SystemConfig: if ql.language is qltypes.DescribeLanguage.DDL: function_call = dispatch.compile(qlast.FunctionCall( func=('cfg', '_describe_system_config_as_ddl'), ), ctx=ictx) assert isinstance(function_call, irast.Set), function_call stmt.result = function_call else: raise errors.QueryError( f'cannot describe config as {ql.language}') elif ql.object == qlast.DescribeGlobal.Roles: if ql.language is qltypes.DescribeLanguage.DDL: function_call = dispatch.compile(qlast.FunctionCall( func=('sys', '_describe_roles_as_ddl'), ), ctx=ictx) assert isinstance(function_call, irast.Set), function_call stmt.result = function_call else: raise errors.QueryError( f'cannot describe roles as {ql.language}') else: assert isinstance(ql.object, qlast.ObjectRef), ql.object modules = [] items: DefaultDict[str, List[str]] = defaultdict(list) referenced_classes: List[s_obj.ObjectMeta] = [] objref = ql.object itemclass = objref.itemclass if itemclass is qltypes.SchemaObjectClass.MODULE: modules.append(objref.name) else: itemtype: Optional[Type[s_obj.Object]] = None name: str if objref.module: name = s_name.Name(module=objref.module, name=objref.name) else: name = objref.name if itemclass is not None: if itemclass is qltypes.SchemaObjectClass.ALIAS: # Look for underlying derived type. itemtype = s_types.Type else: itemtype = ( s_obj.ObjectMeta.get_schema_metaclass_for_ql_class( itemclass)) last_exc = None # Search in the current namespace AND in std. We do # this to avoid masking a `std` object/function by one # in a default module. search_ns = [ictx.modaliases] # Only check 'std' separately if the current # modaliases don't already include it. if ictx.modaliases.get(None, 'std') != 'std': search_ns.append({None: 'std'}) # Search in the current namespace AND in std. for aliases in search_ns: # Use the specific modaliases instead of the # context ones. with ictx.subquery() as newctx: newctx.modaliases = aliases # Get the default module name modname = aliases[None] # Is the current item a function is_function = (itemclass is qltypes.SchemaObjectClass.FUNCTION) # We need to check functions if we're looking for them # specifically or if this is a broad search. They are # handled separately because they allow multiple # matches for the same name. if (itemclass is None or is_function): try: funcs: Tuple[s_func.Function, ...] = ( newctx.env.schema.get_functions( name, module_aliases=aliases)) except errors.InvalidReferenceError: pass else: for func in funcs: items[f'function_{modname}'].append( func.get_name(newctx.env.schema)) # Also find an object matching the name as long as # it's not a function we're looking for specifically. if not is_function: try: if itemclass is not \ qltypes.SchemaObjectClass.ALIAS: condition = None label = None else: condition = (lambda obj: obj. get_alias_is_persistent( ctx.env.schema)) label = 'alias' obj = schemactx.get_schema_object( objref, item_type=itemtype, condition=condition, label=label, ctx=newctx, ) items[f'other_{modname}'].append( obj.get_name(newctx.env.schema)) except errors.InvalidReferenceError as exc: # Record the exception to be possibly # raised if no matches are found last_exc = exc # If we already have some results, suppress the exception, # otherwise raise the recorded exception. if not items and last_exc: raise last_exc verbose = ql.options.get_flag('VERBOSE') if ql.language is qltypes.DescribeLanguage.DDL: method = s_ddl.ddl_text_from_schema elif ql.language is qltypes.DescribeLanguage.SDL: method = s_ddl.sdl_text_from_schema elif ql.language is qltypes.DescribeLanguage.TEXT: method = s_ddl.descriptive_text_from_schema if not verbose.val: referenced_classes = [s_links.Link, s_lprops.Property] else: raise errors.InternalServerError( f'cannot handle describe language {ql.language}') # Based on the items found generate main text and a # potential comment about masked items. defmod = ictx.modaliases.get(None, 'std') default_items = [] masked_items = set() for objtype in ['function', 'other']: defkey = f'{objtype}_{defmod}' mskkey = f'{objtype}_std' default_items += items.get(defkey, []) if defkey in items and mskkey in items: # We have a match in default module and some masked. masked_items.update(items.get(mskkey, [])) else: default_items += items.get(mskkey, []) # Throw out anything in the masked set that's already in # the default. masked_items.difference_update(default_items) text = method( ctx.env.schema, included_modules=modules, included_items=default_items, included_ref_classes=referenced_classes, include_module_ddl=False, include_std_ddl=True, ) if masked_items: text += ('\n\n' '# The following builtins are masked by the above:' '\n\n') masked = method( ctx.env.schema, included_modules=modules, included_items=masked_items, included_ref_classes=referenced_classes, include_module_ddl=False, include_std_ddl=True, ) masked = textwrap.indent(masked, '# ') text += masked ct = typegen.type_to_typeref( ctx.env.get_track_schema_type('std::str'), env=ctx.env, ) stmt.result = setgen.ensure_set( irast.StringConstant(value=text, typeref=ct), ctx=ictx, ) result = fini_stmt(stmt, ql, ctx=ictx, parent_ctx=ctx) return result
def compile_ForQuery( qlstmt: qlast.ForQuery, *, ctx: context.ContextLevel) -> irast.Set: with ctx.subquery() as sctx: stmt = irast.SelectStmt(context=qlstmt.context) init_stmt(stmt, qlstmt, ctx=sctx, parent_ctx=ctx) # As an optimization, if the iterator is a singleton set, use # the element directly. iterator = qlstmt.iterator if isinstance(iterator, qlast.Set) and len(iterator.elements) == 1: iterator = iterator.elements[0] # Compile the iterator iterator_ctx = None if (ctx.expr_exposed and ctx.iterator_ctx is not None and ctx.iterator_ctx is not sctx): iterator_ctx = ctx.iterator_ctx ictx = iterator_ctx or sctx contains_dml = qlutils.contains_dml(qlstmt.result) # If the body contains DML, then we need to prohibit # correlation between the iterator and the enclosing # query, since the correlation imposes compilation issues # we aren't willing to tackle. if contains_dml: ictx.path_scope.factoring_allowlist.update( ctx.iterator_path_ids) iterator_view = stmtctx.declare_view( iterator, s_name.UnqualName(qlstmt.iterator_alias), factoring_fence=contains_dml, path_id_namespace=ictx.path_id_namespace, ctx=ictx, ) iterator_stmt = setgen.new_set_from_set( iterator_view, preserve_scope_ns=True, ctx=sctx) stmt.iterator_stmt = iterator_stmt iterator_type = setgen.get_set_type(iterator_stmt, ctx=ctx) anytype = iterator_type.find_any(ctx.env.schema) if anytype is not None: raise errors.QueryError( 'FOR statement has iterator of indeterminate type', context=ctx.env.type_origins.get(anytype), ) if iterator_ctx is not None and iterator_ctx.stmt is not None: iterator_ctx.stmt.hoisted_iterators.append(iterator_stmt) view_scope_info = sctx.path_scope_map[iterator_view] pathctx.register_set_in_scope( iterator_stmt, path_scope=ictx.path_scope, ctx=sctx, ) # Iterator symbol is, by construction, outside of the scope # of the UNION argument, but is perfectly legal to be referenced # inside a factoring fence that is an immediate child of this # scope. ictx.path_scope.factoring_allowlist.add( stmt.iterator_stmt.path_id) sctx.iterator_path_ids |= {stmt.iterator_stmt.path_id} node = ictx.path_scope.find_descendant(iterator_stmt.path_id) if node is not None: # See above about why we need a factoring fence. # We need to do this again when we move the branch so # as to preserve the fencing. # Do this by sticking the iterator subtree onto a branch # with a factoring fence. if contains_dml: node = node.attach_branch() node.factoring_fence = True node = node.attach_branch() node.attach_subtree(view_scope_info.path_scope, context=iterator.context) # Compile the body with sctx.newscope(fenced=True) as bctx: stmt.result = setgen.scoped_set( compile_result_clause( qlstmt.result, view_scls=ctx.view_scls, view_rptr=ctx.view_rptr, result_alias=qlstmt.result_alias, view_name=ctx.toplevel_result_view_name, forward_rptr=True, ctx=bctx, ), ctx=bctx, ) # Inject an implicit limit if appropriate if ((ctx.expr_exposed or sctx.stmt is ctx.toplevel_stmt) and ctx.implicit_limit): stmt.limit = setgen.ensure_set( dispatch.compile( qlast.IntegerConstant(value=str(ctx.implicit_limit)), ctx=sctx, ), ctx=sctx, ) result = fini_stmt(stmt, qlstmt, ctx=sctx, parent_ctx=ctx) return result