Пример #1
0
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
Пример #2
0
    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
Пример #3
0
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
Пример #4
0
    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)
Пример #5
0
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)
Пример #6
0
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
Пример #7
0
 def reduce_Identifier_AS_MODULE_ModuleName(self, *kids):
     self.val = qlast.ModuleAliasDecl(alias=kids[0].val,
                                      module='.'.join(kids[3].val))
Пример #8
0
 def reduce_MODULE_ModuleName(self, *kids):
     self.val = qlast.ModuleAliasDecl(module='.'.join(kids[1].val))
Пример #9
0
    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
Пример #10
0
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)