def imprint_expr_context( qltree: qlast_.Base, modaliases: Mapping[Optional[str], str], ) -> qlast_.Base: # Imprint current module aliases as explicit # alias declarations in the expression. if (isinstance(qltree, qlast_.BaseConstant) or qltree is None or (isinstance(qltree, qlast_.Set) and not qltree.elements) or (isinstance(qltree, qlast_.Array) and all( isinstance(el, qlast_.BaseConstant) for el in qltree.elements))): # Leave constants alone. return qltree if not isinstance(qltree, qlast_.Command): qltree = qlast_.SelectQuery(result=qltree, implicit=True) else: qltree = copy.copy(qltree) qltree.aliases = list(qltree.aliases) existing_aliases: Dict[Optional[str], str] = {} for alias in qltree.aliases: if isinstance(alias, qlast_.ModuleAliasDecl): existing_aliases[alias.alias] = alias.module aliases_to_add = set(modaliases) - set(existing_aliases) for alias_name in aliases_to_add: qltree.aliases.append( qlast_.ModuleAliasDecl( alias=alias_name, module=modaliases[alias_name], )) return qltree
def transform(self, edgeql_tree, modaliases=None, deoptimize=False, strip_builtins=True, *, schema): context = Context() context.current.deoptimize = deoptimize context.current.schema = schema if modaliases: context.current.modaliases = dict(modaliases) context.current.strip_builtins = strip_builtins self._process_expr(context, edgeql_tree) nses = [] for alias, fq_name in context.current.modaliases.items(): decl = qlast.ModuleAliasDecl(module=fq_name, alias=alias) nses.append(decl) if isinstance(edgeql_tree, qlast.Statement): if deoptimize: edgeql_tree.aliases[:] = [ a for a in edgeql_tree.aliases if not isinstance(a, qlast.ModuleAliasDecl) ] else: if edgeql_tree.aliases is not None: edgeql_tree.aliases[:] = nses else: edgeql_tree.aliases = nses return edgeql_tree
def imprint_expr_context(qltree, modaliases): # Imprint current module aliases as explicit # alias declarations in the expression. if (isinstance(qltree, qlast.BaseConstant) or qltree is None or (isinstance(qltree, qlast.Array) and all(isinstance(el, qlast.BaseConstant) for el in qltree.elements))): # Leave constants alone. return qltree if not isinstance(qltree, qlast.Statement): qltree = qlast.SelectQuery(result=qltree, implicit=True) existing_aliases = {} for alias in qltree.aliases: if isinstance(alias, qlast.ModuleAliasDecl): existing_aliases[alias.alias] = alias.module aliases_to_add = set(modaliases) - set(existing_aliases) for alias in aliases_to_add: qltree.aliases.append( qlast.ModuleAliasDecl( alias=alias, module=modaliases[alias], )) return qltree
def _compile_view(self, viewdecl): view_ql = None for field_decl in viewdecl.fields: fieldname = field_decl.name.name if fieldname == 'expr': view_ql = field_decl.value break if view_ql is None: raise errors.SchemaError( 'missing required expression in view definition', context=viewdecl.context, ) if not isinstance(view_ql, qlast.Statement): view_ql = qlast.SelectQuery(result=view_ql) viewname = s_name.Name(module=self._module.get_name(self._schema), name=viewdecl.name) ir = qlcompiler.compile_ast_to_ir( view_ql, self._schema, derived_target_module=self._module.get_name(self._schema), modaliases=self._mod_aliases, result_view_name=viewname, schema_view_mode=True) self._schema = ir.schema scls = self._schema.get(viewname) self._parse_field_setters(scls, viewdecl.fields) existing_aliases = {} for alias in view_ql.aliases: if isinstance(alias, qlast.ModuleAliasDecl): existing_aliases[alias.alias] = alias.module aliases_to_add = set(self._mod_aliases) - set(existing_aliases) for alias in aliases_to_add: view_ql.aliases.append( qlast.ModuleAliasDecl( alias=alias, module=self._mod_aliases[alias], )) view_expr = qlcodegen.generate_source(view_ql, pretty=False) self._schema = scls.set_field_value(self._schema, 'expr', s_expr.Expression(text=view_expr)) self._schema = scls.set_field_value(self._schema, 'view_type', s_types.ViewType.Select)
def _register_item(decl, *, deps=None, hard_dep_exprs=None, loop_control=None, source=None, subject=None, extra_name=None, ctx): if deps: deps = set(deps) else: deps = set() op = orig_op = copy.copy(decl) if isinstance(decl, qlast.CreateConcretePointer): name = decl.name.name else: name = ctx.get_local_name(decl.name) if ctx.depstack: op.alter_if_exists = True top_parent = parent = copy.copy(ctx.depstack[0][0]) parent.commands = [] for entry, _entry_name in ctx.depstack[1:]: entry_op = copy.copy(entry) entry_op.commands = [] parent.commands.append(entry_op) parent = entry_op parent.commands.append(op) op = top_parent fq_name = ctx.depstack[-1][1] + "@" + name else: op.aliases = [qlast.ModuleAliasDecl(alias=None, module=ctx.module)] fq_name = name if extra_name is not None: fq_name = f'{fq_name}:{extra_name}' node = { "item": op, "deps": {n for _, n in ctx.depstack if n != loop_control}, } ctx.ddlgraph[fq_name] = node if hasattr(decl, "bases"): bases = set() for ref in _get_bases(decl, ctx=ctx): if ref.module == ctx.module: bases.add(ref) deps.update(bases) ctx.inhgraph[fq_name] = bases if ctx.depstack: parent_bases = ctx.inhgraph.get(ctx.depstack[-1][1]) if parent_bases: for parent_base in parent_bases: base_item = f'{parent_base}@{name}' if base_item in ctx.objects: deps.add(base_item) ast_subcommands = getattr(decl, 'commands', []) commands = [] if ast_subcommands: subcmds = [] for cmd in ast_subcommands: if isinstance(cmd, qlast.ObjectDDL): subcmds.append(cmd) elif (isinstance(cmd, qlast.SetField) and not isinstance(cmd.value, qlast.BaseConstant) and not isinstance(op, qlast.CreateView)): subcmds.append(cmd) else: commands.append(cmd) if subcmds: alter_name = f"Alter{decl.__class__.__name__[len('Create'):]}" alter_cls = getattr(qlast, alter_name) alter_cmd = alter_cls(name=decl.name) # indexes need to preserve their "on" expression if alter_name == 'AlterIndex': # find the original expr, which will be in non-normalized form for sub in op.commands: if isinstance(sub, qlast.CreateIndex): alter_cmd.expr = sub.expr break # constraints need to preserve their "on" expression elif alter_name == 'AlterConcreteConstraint': # find the original expr, which will be in non-normalized form for sub in op.commands: if isinstance(sub, qlast.CreateConcreteConstraint): alter_cmd.subjectexpr = sub.subjectexpr break if not ctx.depstack: alter_cmd.aliases = [ qlast.ModuleAliasDecl(alias=None, module=ctx.module) ] ctx.depstack.append((alter_cmd, fq_name)) for cmd in subcmds: trace_dependencies(cmd, ctx=ctx) ctx.depstack.pop() if hard_dep_exprs: for expr in hard_dep_exprs: if isinstance(expr, qlast.TypeExpr): targets = qlast.get_targets(expr) for target in targets: if not target.subtypes: name = ctx.get_ref_name(target.maintype) if name.module == ctx.module: deps.add(name) else: for dep in qltracer.trace_refs( expr, schema=ctx.schema, module=ctx.module, source=source, path_prefix=source, subject=subject or fq_name, objects=ctx.objects, ): if dep.startswith(f"{ctx.module}::"): deps.add(dep) orig_op.commands = commands if loop_control: parent_node = ctx.ddlgraph[loop_control] if 'loop-control' not in parent_node: parent_node['loop-control'] = {fq_name} else: parent_node['loop-control'].add(fq_name) node["deps"].update(deps)
def _register_item( decl: qlast.DDLOperation, *, deps: Optional[AbstractSet[s_name.QualName]] = None, hard_dep_exprs: Optional[Iterable[Dependency]] = None, loop_control: Optional[s_name.QualName] = None, source: Optional[s_name.QualName] = None, subject: Optional[s_name.QualName] = None, ctx: DepTraceContext, ) -> None: name, fq_name = ctx.get_fq_name(decl) if deps: deps = set(deps) else: deps = set() op = orig_op = copy.copy(decl) if ctx.depstack: op.sdl_alter_if_exists = True top_parent = parent = copy.copy(ctx.depstack[0][0]) _clear_nonessential_subcommands(parent) for entry, _ in ctx.depstack[1:]: entry_op = copy.copy(entry) parent.commands.append(entry_op) parent = entry_op _clear_nonessential_subcommands(parent) parent.commands.append(op) op = top_parent else: op.aliases = [qlast.ModuleAliasDecl(alias=None, module=ctx.module)] assert isinstance(op, qlast.DDLCommand) node = topological.DepGraphEntry( item=op, deps={n for _, n in ctx.depstack if n != loop_control}, extra=False, ) ctx.ddlgraph[fq_name] = node if hasattr(decl, "bases"): # add parents to dependencies parents = ctx.parents.get(fq_name) if parents is not None: deps.update(parents) if ctx.depstack: # all ancestors should be seen as dependencies ancestor_bases = ctx.ancestors.get(ctx.depstack[-1][1]) if ancestor_bases: for ancestor_base in ancestor_bases: base_item = qltracer.qualify_name(ancestor_base, name) if base_item in ctx.objects: deps.add(base_item) ast_subcommands = getattr(decl, 'commands', []) commands = [] if ast_subcommands: subcmds: List[qlast.DDLOperation] = [] for cmd in ast_subcommands: # include dependency on constraints or annotations if present if isinstance(cmd, qlast.CreateConcreteConstraint): cmd_name = ctx.get_local_name( cmd.name, type=qltracer.Constraint) if cmd_name.get_module_name() not in s_schema.STD_MODULES: deps.add(cmd_name) elif isinstance(cmd, qlast.CreateAnnotationValue): cmd_name = ctx.get_local_name( cmd.name, type=qltracer.Annotation) if cmd_name.get_module_name() not in s_schema.STD_MODULES: deps.add(cmd_name) if (isinstance(cmd, qlast.ObjectDDL) # HACK: functions don't have alters at the moment and not isinstance(decl, qlast.CreateFunction)): subcmds.append(cmd) elif (isinstance(cmd, qlast.SetField) and not cmd.special_syntax and not isinstance(cmd.value, qlast.BaseConstant) and not isinstance(op, qlast.CreateAlias)): subcmds.append(cmd) else: commands.append(cmd) if subcmds: assert isinstance(decl, qlast.ObjectDDL) alter_name = f"Alter{decl.__class__.__name__[len('Create'):]}" alter_cls: Type[qlast.ObjectDDL] = getattr(qlast, alter_name) alter_cmd = alter_cls(name=decl.name) # indexes need to preserve their "on" expression if isinstance(decl, qlast.CreateIndex): alter_cmd.expr = decl.expr # constraints need to preserve their "on" expression if isinstance(decl, qlast.CreateConcreteConstraint): alter_cmd.subjectexpr = decl.subjectexpr alter_cmd.args = decl.args if not ctx.depstack: alter_cmd.aliases = [ qlast.ModuleAliasDecl(alias=None, module=ctx.module) ] ctx.depstack.append((alter_cmd, fq_name)) for cmd in subcmds: trace_dependencies(cmd, ctx=ctx) ctx.depstack.pop() if hard_dep_exprs: for expr in hard_dep_exprs: if isinstance(expr, TypeDependency): deps |= _get_hard_deps(expr.texpr, ctx=ctx) elif isinstance(expr, ExprDependency): qlexpr = expr.expr if isinstance(expr, FunctionDependency): params = expr.params else: params = {} tdeps = qltracer.trace_refs( qlexpr, schema=ctx.schema, module=ctx.module, source=source, path_prefix=source, subject=subject or fq_name, objects=ctx.objects, params=params, ) pdeps: MutableSet[s_name.QualName] = set() for dep in tdeps: # ignore std module dependencies if dep.get_module_name() not in s_schema.STD_MODULES: # First check if the dep is a pointer that's # defined explicitly. If it's not explicitly # defined, check for ancestors and use them # instead. # # FIXME: Ideally we should use the closest # ancestor, instead of all of them, but # including all is still correct. if '@' in dep.name: pdeps |= _get_pointer_deps(dep, ctx=ctx) else: pdeps.add(dep) # Handle the pre-processed deps now. for dep in pdeps: deps.add(dep) if isinstance(decl, qlast.CreateAlias): # If the declaration is a view, we need to be # dependent on all the types and their props # used in the view. vdeps = {dep} | ctx.ancestors.get(dep, set()) for vdep in vdeps: deps |= ctx.defdeps.get(vdep, set()) elif (isinstance(decl, qlast.CreateConcretePointer) and isinstance(decl.target, qlast.Expr)): # If the declaration is a computable # pointer, we need to include the possible # constraints for every dependency that it # lists. This is so that any other # links/props that this computable uses # has all of their constraints defined # before the computable and the # cardinality can be inferred correctly. cdeps = {dep} | ctx.ancestors.get(dep, set()) for cdep in cdeps: deps |= ctx.constraints.get(cdep, set()) else: raise AssertionError(f'unexpected dependency type: {expr!r}') orig_op.commands = commands if loop_control: parent_node = ctx.ddlgraph[loop_control] parent_node.loop_control.add(fq_name) node.deps |= deps
def reduce_Identifier_AS_MODULE_ModuleName(self, *kids): self.val = qlast.ModuleAliasDecl(alias=kids[0].val, module='.'.join(kids[3].val))
def reduce_MODULE_ModuleName(self, *kids): self.val = qlast.ModuleAliasDecl(module='.'.join(kids[1].val))
def _handle_view_op(cls, schema, cmd, astnode, context): view_expr = cls._maybe_get_view_expr(astnode) if view_expr is not None: if not isinstance(view_expr, qlast.Statement): view_expr = qlast.SelectQuery(result=view_expr) existing_aliases = {} for alias in view_expr.aliases: if isinstance(alias, qlast.ModuleAliasDecl): existing_aliases[alias.alias] = alias.module aliases_to_add = set(context.modaliases) - set(existing_aliases) for alias in aliases_to_add: view_expr.aliases.append( qlast.ModuleAliasDecl( alias=alias, module=context.modaliases[alias], )) expr_ql = qlcodegen.generate_source(view_expr, pretty=False) cmd.set_attribute_value('expr', s_expr.Expression(text=expr_ql)) ir = cls._compile_view_expr(view_expr, cmd.classname, schema, context) view_types = ir.views.values() if isinstance(astnode, qlast.AlterObjectType): prev = schema.get(cmd.classname) prev_ir = cls._compile_view_expr(prev.expr, cmd.classname, schema, context) prev_view_types = prev_ir.views.values() else: prev_ir = None prev_view_types = [] derived_delta = sd.DeltaRoot() new_schema = ir.schema old_schema = prev_ir.schema if prev_ir is not None else None adds_mods, dels = so.Object._delta_sets(prev_view_types, view_types, old_schema=old_schema, new_schema=new_schema) derived_delta.update(adds_mods) derived_delta.update(dels) if ir.stype.is_view(ir.schema): for op in list(derived_delta.get_subcommands()): if op.classname == cmd.classname: for subop in op.get_subcommands(): if isinstance(subop, sd.AlterObjectProperty): cmd.discard_attribute(subop.property) cmd.add(subop) derived_delta.discard(op) cmd.update(derived_delta.get_subcommands()) cmd.discard_attribute('view_type') cmd.add( sd.AlterObjectProperty(property='view_type', new_value=s_types.ViewType.Select)) return cmd
def _register_item(decl, *, deps=None, hard_dep_exprs=None, loop_control=None, source=None, subject=None, ctx: DepTraceContext): name, fq_name = ctx.get_fq_name(decl) if deps: deps = set(deps) else: deps = set() op = orig_op = copy.copy(decl) if ctx.depstack: op.sdl_alter_if_exists = True top_parent = parent = copy.copy(ctx.depstack[0][0]) _clear_nonessential_subcommands(parent) for entry, _entry_name in ctx.depstack[1:]: entry_op = copy.copy(entry) parent.commands.append(entry_op) parent = entry_op _clear_nonessential_subcommands(parent) parent.commands.append(op) op = top_parent else: op.aliases = [qlast.ModuleAliasDecl(alias=None, module=ctx.module)] node = { "item": op, "deps": {n for _, n in ctx.depstack if n != loop_control}, } ctx.ddlgraph[fq_name] = node if hasattr(decl, "bases"): # add parents to dependencies parents = ctx.parents.get(fq_name) if parents is not None: deps.update(parents) if ctx.depstack: # all ancestors should be seen as dependencies ancestor_bases = ctx.ancestors.get(ctx.depstack[-1][1]) if ancestor_bases: for ancestor_base in ancestor_bases: base_item = f'{ancestor_base}@{name}' if base_item in ctx.objects: deps.add(base_item) ast_subcommands = getattr(decl, 'commands', []) commands = [] if ast_subcommands: subcmds = [] for cmd in ast_subcommands: # include dependency on constraints or annotations if present if isinstance(cmd, qlast.CreateConcreteConstraint): cmd_name = ctx.get_local_name(cmd.name, type=qltracer.Constraint) if cmd_name.module not in s_schema.STD_MODULES: deps.add(cmd_name) elif isinstance(cmd, qlast.CreateAnnotationValue): cmd_name = ctx.get_local_name(cmd.name, type=qltracer.Annotation) if cmd_name.module not in s_schema.STD_MODULES: deps.add(cmd_name) if (isinstance(cmd, qlast.ObjectDDL) # HACK: functions don't have alters at the moment and not isinstance(decl, qlast.CreateFunction)): subcmds.append(cmd) elif (isinstance(cmd, qlast.SetField) and not isinstance(cmd.value, qlast.BaseConstant) and not isinstance(op, qlast.CreateAlias)): subcmds.append(cmd) else: commands.append(cmd) if subcmds: alter_name = f"Alter{decl.__class__.__name__[len('Create'):]}" alter_cls = getattr(qlast, alter_name) alter_cmd = alter_cls(name=decl.name) # indexes need to preserve their "on" expression if alter_name == 'AlterIndex': alter_cmd.expr = orig_op.expr for sub in orig_op.commands: if (isinstance(sub, qlast.SetField) and sub.name == 'orig_expr'): alter_cmd.commands.append(sub) break # constraints need to preserve their "on" expression elif alter_name == 'AlterConcreteConstraint': alter_cmd.subjectexpr = orig_op.subjectexpr alter_cmd.args = orig_op.args for sub in orig_op.commands: if (isinstance(sub, qlast.SetField) and sub.name == 'orig_subjectexpr'): alter_cmd.commands.append(sub) break if not ctx.depstack: alter_cmd.aliases = [ qlast.ModuleAliasDecl(alias=None, module=ctx.module) ] ctx.depstack.append((alter_cmd, fq_name)) for cmd in subcmds: trace_dependencies(cmd, ctx=ctx) ctx.depstack.pop() if hard_dep_exprs: for expr in hard_dep_exprs: if isinstance(expr, qlast.TypeExpr): deps |= _get_hard_deps(expr, ctx=ctx) else: if isinstance(expr, tuple): qlexpr, params = expr else: qlexpr = expr params = {} tdeps = qltracer.trace_refs( qlexpr, schema=ctx.schema, module=ctx.module, source=source, path_prefix=source, subject=subject or fq_name, objects=ctx.objects, params=params, ) for dep in tdeps: # ignore std module dependencies if not STD_PREFIX_RE.match(dep): deps.add(dep) if isinstance(decl, qlast.CreateAlias): # If the declaration is a view, we need to be # dependent on all the types and their props # used in the view. vdeps = {dep} | ctx.ancestors.get(dep, set()) for vdep in vdeps: deps |= ctx.defdeps.get(vdep, set()) elif (isinstance(decl, qlast.CreateConcretePointer) and isinstance(decl.target, qlast.Expr)): # If the declaration is a computable # pointer, we need to include the possible # constraints for every dependency that it # lists. This is so that any other # links/props that this computable uses # has all of their constraints defined # before the computable and the # cardinality can be inferred correctly. cdeps = {dep} | ctx.ancestors.get(dep, set()) for cdep in cdeps: deps |= ctx.constraints.get(cdep, set()) orig_op.commands = commands if loop_control: parent_node = ctx.ddlgraph[loop_control] if 'loop-control' not in parent_node: parent_node['loop-control'] = {fq_name} else: parent_node['loop-control'].add(fq_name) node["deps"].update(deps)