def copy(self): result = super().copy() result.ops = ordered.OrderedSet( op.copy() for op in self.ops) result.before_ops = ordered.OrderedSet( op.copy() for op in self.before_ops) return result
def get_deps(key: DepGraphKey) -> DepGraphEntry: try: item = depgraph[key] except KeyError: item = depgraph[key] = DepGraphEntry( item=(), deps=ordered.OrderedSet(), weak_deps=ordered.OrderedSet(), ) return item
def collect_grouping_atoms( els: List[qlast.GroupingElement]) -> AbstractSet[str]: atoms: ordered.OrderedSet[str] = ordered.OrderedSet() def _collect_atom(el: qlast.GroupingAtom) -> None: if isinstance(el, qlast.GroupingIdentList): for at in el.elements: _collect_atom(at) else: assert isinstance(el, qlast.ObjectRef) atoms.add(el.name) def _collect_el(el: qlast.GroupingElement) -> None: if isinstance(el, qlast.GroupingSets): for sub in el.sets: _collect_el(sub) elif isinstance(el, qlast.GroupingOperation): for at in el.elements: _collect_atom(at) elif isinstance(el, qlast.GroupingSimple): _collect_atom(el.element) else: raise AssertionError('Unknown GroupingElement') for el in els: _collect_el(el) return atoms
def sort_subcommands_by_type(self): def _key(c): if isinstance(c, CreateObject): return 0 else: return 2 self.ops = ordered.OrderedSet(sorted(self.ops, key=_key))
def iter_columns(self, writable_only=False, only_self=False): cols = collections.OrderedDict() cols.update((c.name, c) for c in self._columns if not writable_only or not c.readonly) if not only_self: for c in reversed(self.bases): cols.update((name, bc) for name, bc in c.columns.items() if not writable_only or not bc.readonly) return ordered.OrderedSet(cols.values())
def sort_objects(schema, objects): from . import inheriting g = {} for obj in sorted(objects, key=lambda o: o.get_name(schema)): g[obj.get_name(schema)] = { 'item': obj, 'merge': [], 'deps': [] } if isinstance(obj, inheriting.InheritingObject): obj_bases = obj.get_bases(schema) derived_from = obj.get_derived_from(schema) else: obj_bases = None derived_from = None deps = g[obj.get_name(schema)]['deps'] if obj_bases: deps.extend( b.get_name(schema) for b in obj_bases.objects(schema)) for base in obj_bases.objects(schema): base_name = base.get_name(schema) if base_name.module != obj.get_name(schema).module: g[base_name] = {'item': base, 'merge': [], 'deps': []} if derived_from is not None: deps.append(derived_from.get_name(schema)) if not g: return ordered.OrderedSet() item = next(iter(g.values()))['item'] modname = item.get_name(schema).module objs = topological.sort(g) return ordered.OrderedSet(filter( lambda obj: getattr(obj.get_name(schema), 'module', None) == modname, objs))
def sort_expression_fields(self, schema): """Transform the subcommands to reduce changes of cycles.""" def _key(c): if isinstance(c, CreateObject): return 0 else: return 2 self.ops = ordered.OrderedSet(sorted(self.ops, key=_key)) context = CommandContext() with context(DeltaRootContext(self)): for op in sorted(self.ops, key=_key): op.extract_ref_ops(schema, context)
def __init__(self, name, table_name, unique=True, expr=None, predicate=None, inherit=False, metadata=None, columns=None): super().__init__(inherit=inherit, metadata=metadata) assert table_name[1] != 'feature' self.name = name self.table_name = table_name self._columns = ordered.OrderedSet() if columns: self.add_columns(columns) self.predicate = predicate self.unique = unique self.expr = expr if self.name_in_catalog != self.name: self.add_metadata('fullname', self.name)
def __init__(self, name, columns=()): super().__init__(name) self._columns = ordered.OrderedSet(columns)
def __init__(self, name, *, columns=None, bases=None, constraints=None): self.constraints = ordered.OrderedSet(constraints or []) self.bases = ordered.OrderedSet(bases or []) self.data = [] super().__init__(name, columns=columns)
def __init__(self, **kwargs): super().__init__(**kwargs) self.ops = ordered.OrderedSet() self.before_ops = ordered.OrderedSet()
def _trace_op( op: sd.Command, opstack: List[sd.Command], depgraph: Dict[Tuple[str, str], Dict[str, Any]], renames: Dict[str, str], renames_r: Dict[str, str], strongrefs: Dict[str, str], old_schema: Optional[s_schema.Schema], new_schema: s_schema.Schema, ) -> None: deps: ordered.OrderedSet[Tuple[str, str]] = ordered.OrderedSet() graph_key: str implicit_ancestors: List[str] = [] if isinstance(op, sd.CreateObject): tag = 'create' elif isinstance(op, sd.AlterObject): tag = 'alter' elif isinstance(op, sd.RenameObject): tag = 'rename' elif isinstance(op, inheriting.RebaseInheritingObject): tag = 'rebase' elif isinstance(op, sd.DeleteObject): tag = 'delete' elif isinstance(op, referencing.AlterOwned): tag = 'alterowned' elif isinstance(op, sd.AlterObjectProperty): tag = 'field' else: raise RuntimeError( f'unexpected delta command type at top level: {op!r}') if isinstance(op, (sd.DeleteObject, referencing.AlterOwned)): assert old_schema is not None obj = get_object(old_schema, op) refs = _get_referrers(old_schema, obj, strongrefs) for ref in refs: ref_name = ref.get_name(old_schema) if ((isinstance(obj, referencing.ReferencedObject) and obj.get_referrer(old_schema) == ref)): # If the referrer is enclosing the object # (i.e. the reference is a refdict reference), # we sort the referrer operation first. try: ref_item = depgraph[('delete', ref_name)] except KeyError: ref_item = depgraph[('delete', ref_name)] = {'deps': set()} ref_item['deps'].add((tag, op.classname)) elif (isinstance(ref, referencing.ReferencedInheritingObject) and (op.classname in { b.get_name(old_schema) for b in ref.get_implicit_ancestors(old_schema) }) and (not isinstance(ref, s_pointers.Pointer) or not ref.get_is_from_alias(old_schema))): # If the ref is an implicit descendant (i.e. an inherited ref), # we also sort it _after_ the parent, because we'll pull # it as a child of the parent op at the time of tree # reassembly. try: ref_item = depgraph[('delete', ref_name)] except KeyError: ref_item = depgraph[('delete', ref_name)] = {'deps': set()} ref_item['deps'].add((tag, op.classname)) elif (isinstance(ref, referencing.ReferencedObject) and ref.get_referrer(old_schema) == obj): # Skip refdict.backref_attr to avoid dependency cycles. continue else: # Otherwise, things must be deleted _after_ their referrers # have been deleted or altered. deps.add(('delete', ref.get_name(old_schema))) if isinstance(obj, referencing.ReferencedObject): referrer = obj.get_referrer(old_schema) if referrer is not None: assert isinstance(referrer, so.QualifiedObject) referrer_name: str = referrer.get_name(old_schema) if referrer_name in renames_r: referrer_name = renames_r[referrer_name] deps.add(('rebase', referrer_name)) if (isinstance(obj, referencing.ReferencedInheritingObject) and (not isinstance(obj, s_pointers.Pointer) or not obj.get_is_from_alias(old_schema))): for ancestor in obj.get_implicit_ancestors(old_schema): ancestor_name = ancestor.get_name(old_schema) implicit_ancestors.append(ancestor_name) try: anc_item = depgraph[('delete', ancestor_name)] except KeyError: anc_item = {'deps': set()} depgraph[('delete', ancestor_name)] = anc_item anc_item['deps'].add(( 'alterowned', op.classname, )) graph_key = op.classname elif isinstance(op, sd.AlterObjectProperty): if isinstance(op.new_value, so.Object): new_value_name = op.new_value.get_name(new_schema) deps.add(('create', new_value_name)) deps.add(('alter', new_value_name)) elif isinstance(op.new_value, so.ObjectShell): nvn = op.new_value.get_name(new_schema) if nvn is not None: deps.add(('create', nvn)) deps.add(('alter', nvn)) parent_op = opstack[-2] assert isinstance(parent_op, sd.ObjectCommand) graph_key = f'{parent_op.classname}%%{op.property}' deps.add(('create', parent_op.classname)) if isinstance(op.old_value, (so.Object, so.ObjectShell)): assert old_schema is not None ovn = op.old_value.get_name(old_schema) nvn = op.new_value.get_name(new_schema) if ovn != nvn: try: ov_item = depgraph[('delete', ovn)] except KeyError: ov_item = depgraph[('delete', ovn)] = {'deps': set()} ov_item['deps'].add((tag, graph_key)) elif isinstance(op, sd.ObjectCommand): # If the object was renamed, use the new name, else use regular. name = renames.get(op.classname, op.classname) obj = get_object(new_schema, op, name) refs = _get_referrers(new_schema, obj, strongrefs) for ref in refs: ref_name = ref.get_name(new_schema) if ref_name in renames_r: ref_name = renames_r[ref_name] if ((isinstance(ref, referencing.ReferencedObject) and ref.get_referrer(new_schema) == obj) or (isinstance(obj, referencing.ReferencedObject) and obj.get_referrer(new_schema) == ref)): # Ignore refs generated by refdict backref. continue try: item = depgraph[('create', ref_name)] except KeyError: item = depgraph[('create', ref_name)] = { 'deps': set(), } item['deps'].add(('create', op.classname)) item['deps'].add(('alter', op.classname)) item['deps'].add(('rename', op.classname)) try: item = depgraph[('alter', ref_name)] except KeyError: item = depgraph[('alter', ref_name)] = { 'deps': set(), } item['deps'].add(('create', op.classname)) item['deps'].add(('alter', op.classname)) item['deps'].add(('rename', op.classname)) try: item = depgraph[('rebase', ref_name)] except KeyError: item = depgraph[('rebase', ref_name)] = { 'deps': set(), } item['deps'].add(('create', op.classname)) item['deps'].add(('alter', op.classname)) try: item = depgraph[('rename', ref_name)] except KeyError: item = depgraph[('rename', ref_name)] = { 'deps': set(), } item['deps'].add(('create', op.classname)) item['deps'].add(('alter', op.classname)) if tag in ('create', 'alter'): # In a delete/create cycle, deletion must obviously # happen first. deps.add(('delete', op.classname)) if isinstance(obj, s_func.Function) and old_schema is not None: old_funcs = old_schema.get_functions( sn.shortname_from_fullname(op.classname), default=(), ) for old_func in old_funcs: deps.add(('delete', old_func.get_name(old_schema))) if tag == 'alter': # Alteration must happen after creation, if any. deps.add(('create', op.classname)) deps.add(('rename', op.classname)) deps.add(('rebase', op.classname)) if isinstance(obj, referencing.ReferencedObject): referrer = obj.get_referrer(new_schema) if referrer is not None: assert isinstance(referrer, so.QualifiedObject) referrer_name = referrer.get_name(new_schema) if referrer_name in renames_r: referrer_name = renames_r[referrer_name] deps.add(('create', referrer_name)) deps.add(('rebase', referrer_name)) if isinstance(obj, referencing.ReferencedInheritingObject): implicit_ancestors = [ b.get_name(new_schema) for b in obj.get_implicit_ancestors(new_schema) ] if not isinstance(op, sd.CreateObject): assert old_schema is not None old_obj = get_object(old_schema, op) assert isinstance( old_obj, referencing.ReferencedInheritingObject, ) implicit_ancestors += [ b.get_name(old_schema) for b in old_obj.get_implicit_ancestors(old_schema) ] graph_key = op.classname else: raise AssertionError(f'unexpected op type: {op!r}') try: item = depgraph[(tag, graph_key)] except KeyError: item = depgraph[(tag, graph_key)] = {'deps': set()} item['item'] = opstack item['op'] = op item['tag'] = tag item['deps'].update(deps) item['implicit_ancestors'] = implicit_ancestors
def __init__(self, name, columns=None): super().__init__() self.name = name self._columns = ordered.OrderedSet() self.add_columns(columns or [])
def _trace_op( op: sd.Command, opbranch: List[sd.Command], depgraph: DepGraph, renames: Dict[sn.Name, sn.Name], renames_r: Dict[sn.Name, sn.Name], strongrefs: Dict[sn.Name, sn.Name], old_schema: Optional[s_schema.Schema], new_schema: s_schema.Schema, ) -> None: def get_deps(key: DepGraphKey) -> DepGraphEntry: try: item = depgraph[key] except KeyError: item = depgraph[key] = DepGraphEntry( item=(), deps=ordered.OrderedSet(), weak_deps=ordered.OrderedSet(), ) return item def record_field_deps( op: sd.AlterObjectProperty, parent_op: sd.ObjectCommand[so.Object], ) -> str: if isinstance(op.new_value, (so.Object, so.ObjectShell)): obj = op.new_value nvn = obj.get_name(new_schema) if nvn is not None: deps.add(('create', str(nvn))) deps.add(('alter', str(nvn))) if nvn in renames_r: deps.add(('rename', str(renames_r[nvn]))) if isinstance(obj, so.ObjectShell): obj = obj.resolve(new_schema) # For SET TYPE, we want to finish any rebasing into the # target type before we change the type. if isinstance(obj, so.InheritingObject): for desc in obj.descendants(new_schema): deps.add(('rebase', str(desc.get_name(new_schema)))) graph_key = f'{parent_op.classname}%%{op.property}' deps.add(('create', str(parent_op.classname))) deps.add(('alter', str(parent_op.classname))) if isinstance(op.old_value, (so.Object, so.ObjectShell)): assert old_schema is not None ovn = op.old_value.get_name(old_schema) nvn = op.new_value.get_name(new_schema) if ovn != nvn: ov_item = get_deps(('delete', str(ovn))) ov_item.deps.add((tag, graph_key)) return graph_key def write_dep_matrix( dependent: str, dependent_tags: Tuple[str, ...], dependency: str, dependency_tags: Tuple[str, ...], *, as_weak: bool = False, ) -> None: for dependent_tag in dependent_tags: item = get_deps((dependent_tag, dependent)) for dependency_tag in dependency_tags: if as_weak: item.weak_deps.add((dependency_tag, dependency)) else: item.deps.add((dependency_tag, dependency)) def write_ref_deps( ref: so.Object, obj: so.Object, this_name_str: str, ) -> None: ref_name = ref.get_name(new_schema) if ref_name in renames_r: ref_name = renames_r[ref_name] ref_name_str = str(ref_name) if ((isinstance(ref, referencing.ReferencedObject) and ref.get_referrer(new_schema) == obj) or (isinstance(obj, referencing.ReferencedObject) and obj.get_referrer(new_schema) == ref)): # Mostly ignore refs generated by refdict backref, but # make create/alter depend on renames of the backref. # This makes sure that a rename is done before the innards are # modified. DDL doesn't actually require this but some of the # internals for producing the DDL do (since otherwise we can # generate references to the renamed type in our delta before # it is renamed). if tag in ('create', 'alter'): deps.add(('rename', ref_name_str)) return write_dep_matrix( dependent=ref_name_str, dependent_tags=('create', 'alter', 'rebase'), dependency=this_name_str, dependency_tags=('create', 'alter', 'rename'), ) item = get_deps(('rename', ref_name_str)) item.deps.add(('create', this_name_str)) item.deps.add(('alter', this_name_str)) item.deps.add(('rename', this_name_str)) if isinstance(ref, s_pointers.Pointer): # The current item is a type referred to by # a link or property in another type. Set the referring # type and its descendants as weak dependents of the current # item to reduce the number of unnecessary ALTERs in the # final delta, especially ones that might result in SET TYPE # commands being generated. ref_src = ref.get_source(new_schema) if isinstance(ref_src, s_pointers.Pointer): ref_src_src = ref_src.get_source(new_schema) if ref_src_src is not None: ref_src = ref_src_src if ref_src is not None: for desc in ref_src.descendants(new_schema) | {ref_src}: desc_name = str(desc.get_name(new_schema)) write_dep_matrix( dependent=desc_name, dependent_tags=('create', 'alter'), dependency=this_name_str, dependency_tags=('create', 'alter', 'rename'), as_weak=True, ) deps: ordered.OrderedSet[Tuple[str, str]] = ordered.OrderedSet() graph_key: str implicit_ancestors: List[sn.Name] = [] if isinstance(op, sd.CreateObject): tag = 'create' elif isinstance(op, sd.AlterObject): tag = 'alter' elif isinstance(op, sd.RenameObject): tag = 'rename' elif isinstance(op, inheriting.RebaseInheritingObject): tag = 'rebase' elif isinstance(op, sd.DeleteObject): tag = 'delete' elif isinstance(op, referencing.AlterOwned): if op.get_attribute_value('owned'): tag = 'setowned' else: tag = 'dropowned' elif isinstance(op, (sd.AlterObjectProperty, sd.AlterSpecialObjectField)): tag = 'field' else: raise RuntimeError( f'unexpected delta command type at top level: {op!r}') if isinstance(op, (sd.DeleteObject, referencing.AlterOwned)): assert old_schema is not None try: obj = get_object(old_schema, op) except errors.InvalidReferenceError: if isinstance(op, sd.DeleteObject) and op.if_exists: # If this is conditional deletion and the object isn't there, # then don't bother with analysis, since this command wouldn't # get executed. return else: raise refs = _get_referrers(old_schema, obj, strongrefs) for ref in refs: ref_name_str = str(ref.get_name(old_schema)) if ((isinstance(obj, referencing.ReferencedObject) and obj.get_referrer(old_schema) == ref)): # If the referrer is enclosing the object # (i.e. the reference is a refdict reference), # we sort the enclosed operation first. ref_item = get_deps(('delete', ref_name_str)) ref_item.deps.add((tag, str(op.classname))) elif (isinstance(ref, referencing.ReferencedInheritingObject) and (op.classname in { b.get_name(old_schema) for b in ref.get_implicit_ancestors(old_schema) }) and (not isinstance(ref, s_pointers.Pointer) or not ref.get_from_alias(old_schema))): # If the ref is an implicit descendant (i.e. an inherited ref), # we also sort it _after_ the parent, because we'll pull # it as a child of the parent op at the time of tree # reassembly. ref_item = get_deps(('delete', ref_name_str)) ref_item.deps.add((tag, str(op.classname))) elif (isinstance(ref, referencing.ReferencedObject) and ref.get_referrer(old_schema) == obj): # Skip refdict.backref_attr to avoid dependency cycles. continue else: # Otherwise, things must be deleted _after_ their referrers # have been deleted or altered. deps.add(('delete', ref_name_str)) # (except for aliases, which in the collection case # specifically need the old target deleted before the # new one is created) if not isinstance(ref, s_expraliases.Alias): deps.add(('alter', ref_name_str)) if type(ref) == type(obj): deps.add(('rebase', ref_name_str)) # The deletion of any implicit ancestors needs to come after # the deletion of any referrers also. if isinstance(obj, referencing.ReferencedInheritingObject): for ancestor in obj.get_implicit_ancestors(old_schema): ancestor_name = ancestor.get_name(old_schema) anc_item = get_deps(('delete', str(ancestor_name))) anc_item.deps.add(('delete', ref_name_str)) if isinstance(obj, referencing.ReferencedObject): referrer = obj.get_referrer(old_schema) if referrer is not None: assert isinstance(referrer, so.QualifiedObject) referrer_name: sn.Name = referrer.get_name(old_schema) if referrer_name in renames_r: referrer_name = renames_r[referrer_name] # A drop needs to come *before* drop owned on the referrer # which will do it itself. if tag == 'delete': ref_item = get_deps(('dropowned', str(referrer_name))) ref_item.deps.add(('delete', str(op.classname))) # For SET OWNED, we need any rebase of the enclosing # object to come *after*, because otherwise obj could # get dropped before the SET OWNED takes effect. # DROP, also. if tag in ('setowned', 'delete'): ref_item = get_deps(('rebase', str(referrer_name))) ref_item.deps.add((tag, str(op.classname))) else: deps.add(('rebase', str(referrer_name))) if (isinstance(obj, referencing.ReferencedInheritingObject) and (not isinstance(obj, s_pointers.Pointer) or not obj.get_from_alias(old_schema))): for ancestor in obj.get_implicit_ancestors(old_schema): ancestor_name = ancestor.get_name(old_schema) implicit_ancestors.append(ancestor_name) if isinstance(op, referencing.AlterOwned): anc_item = get_deps(('delete', str(ancestor_name))) anc_item.deps.add((tag, str(op.classname))) if tag == 'setowned': # SET OWNED must come before ancestor rebases too anc_item = get_deps(('rebase', str(ancestor_name))) anc_item.deps.add(('setowned', str(op.classname))) if tag == 'dropowned': deps.add(('alter', str(op.classname))) graph_key = str(op.classname) elif isinstance(op, sd.AlterObjectProperty): parent_op = opbranch[-2] assert isinstance(parent_op, sd.ObjectCommand) graph_key = record_field_deps(op, parent_op) elif isinstance(op, sd.AlterSpecialObjectField): parent_op = opbranch[-2] assert isinstance(parent_op, sd.ObjectCommand) field_op = op._get_attribute_set_cmd(op._field) assert field_op is not None graph_key = record_field_deps(field_op, parent_op) elif isinstance(op, sd.ObjectCommand): # If the object was renamed, use the new name, else use regular. name = renames.get(op.classname, op.classname) obj = get_object(new_schema, op, name) this_name_str = str(op.classname) if tag == 'rename': # On renames, we want to delete any references before we # do the rename. This is because for functions and # constraints we implicitly rename the object when # something it references is renamed, and this implicit # rename can interfere with a CREATE/DELETE pair. So we # make sure to put the DELETE before the RENAME of a # referenced object. (An improvement would be to elide a # CREATE/DELETE pair when it could be implicitly handled # by a rename). assert old_schema old_obj = get_object(old_schema, op, op.classname) for ref in _get_referrers(old_schema, old_obj, strongrefs): deps.add(('delete', str(ref.get_name(old_schema)))) refs = _get_referrers(new_schema, obj, strongrefs) for ref in refs: write_ref_deps(ref, obj, this_name_str) if tag in ('create', 'alter'): # In a delete/create cycle, deletion must obviously # happen first. deps.add(('delete', str(op.classname))) # Renaming also deps.add(('rename', str(op.classname))) if isinstance(obj, s_func.Function) and old_schema is not None: old_funcs = old_schema.get_functions( sn.shortname_from_fullname(op.classname), default=(), ) for old_func in old_funcs: deps.add(('delete', str(old_func.get_name(old_schema)))) if tag == 'alter': # Alteration must happen after creation, if any. deps.add(('create', this_name_str)) deps.add(('rename', this_name_str)) deps.add(('rebase', this_name_str)) if isinstance(obj, referencing.ReferencedObject): referrer = obj.get_referrer(new_schema) if referrer is not None: assert isinstance(referrer, so.QualifiedObject) referrer_name = referrer.get_name(new_schema) if referrer_name in renames_r: referrer_name = renames_r[referrer_name] ref_name_str = str(referrer_name) deps.add(('create', ref_name_str)) if op.ast_ignore_ownership() or tag == 'rename': ref_item = get_deps(('rebase', ref_name_str)) ref_item.deps.add((tag, this_name_str)) else: deps.add(('rebase', ref_name_str)) # Addition and removal of constraints can cause # changes to the cardinality of expressions that refer # to them. Add the appropriate dependencies in. if (isinstance(obj, s_constraints.Constraint) and isinstance(referrer, s_pointers.Pointer)): refs = _get_referrers(new_schema, referrer, strongrefs) for ref in refs: write_ref_deps(ref, referrer, this_name_str) if (isinstance(obj, referencing.ReferencedInheritingObject) # Changes to owned objects can't necessarily be merged # in with parents, so we make sure not to. and not obj.get_owned(new_schema)): implicit_ancestors = [ b.get_name(new_schema) for b in obj.get_implicit_ancestors(new_schema) ] if not isinstance(op, sd.CreateObject): assert old_schema is not None name = renames_r.get(op.classname, op.classname) old_obj = get_object(old_schema, op, name) assert isinstance( old_obj, referencing.ReferencedInheritingObject, ) implicit_ancestors += [ b.get_name(old_schema) for b in old_obj.get_implicit_ancestors(old_schema) ] graph_key = this_name_str else: raise AssertionError(f'unexpected op type: {op!r}') item = get_deps((tag, graph_key)) item.item = tuple(opbranch) item.deps |= deps item.extra = DepGraphEntryExtra( implicit_ancestors=[renames_r.get(a, a) for a in implicit_ancestors], )
def _fixup_materialized_sets(ir: irast.Base, *, ctx: context.ContextLevel) -> List[irast.Set]: # Make sure that all materialized sets have their views compiled flt = lambda n: isinstance(n, irast.Stmt) children: List[irast.Stmt] = ast_visitor.find_children(ir, flt) for nobe in ctx.source_map.values(): if nobe.irexpr: children += ast_visitor.find_children(nobe.irexpr, flt) to_clear = [] for stmt in ordered.OrderedSet(children): if not stmt.materialized_sets: continue for key in list(stmt.materialized_sets): mat_set = stmt.materialized_sets[key] assert not mat_set.finalized if len(mat_set.uses) <= 1: del stmt.materialized_sets[key] continue ir_set = mat_set.materialized assert ir_set.path_scope_id is not None new_scope = ctx.env.scope_tree_nodes[ir_set.path_scope_id] assert new_scope.parent parent = new_scope.parent good_reason = False for x in mat_set.reason: if isinstance(x, irast.MaterializeVolatile): good_reason = True elif isinstance(x, irast.MaterializeVisible): # If any of the bindings that the set uses are # *visible* at the definition point and *not # visible* from at least one use point, we need to # materialize, to make sure that the use site sees # the same value for the binding as the definition # point. If it's not visible, then it's just being # used internally and we don't need any special # work. use_scopes = [ ctx.env.scope_tree_nodes.get(x.path_scope_id) if x.path_scope_id is not None else None for x in mat_set.use_sets ] for b, _ in x.sets: if parent.is_visible(b, allow_group=True) and not all( use_scope and use_scope.parent and use_scope. parent.is_visible(b, allow_group=True) for use_scope in use_scopes): good_reason = True break if not good_reason: del stmt.materialized_sets[key] continue # Compile the view shapes in the set with ctx.new() as subctx: subctx.implicit_tid_in_shapes = False subctx.implicit_tname_in_shapes = False subctx.path_scope = new_scope viewgen.late_compile_view_shapes(ir_set, ctx=subctx) for use_set in mat_set.use_sets: if use_set != mat_set.materialized: use_set.is_materialized_ref = True # XXX: Deleting it on linkprops breaks a bunch of # linkprop related DML... if not use_set.path_id.is_linkprop_path(): to_clear.append(use_set) assert (not any(use.src_path() for use in mat_set.uses) or mat_set.materialized.rptr ), f"materialized ptr {mat_set.uses} missing rptr" mat_set.finalized = True return to_clear