Exemplo n.º 1
0
def trace_SetField(node: qlast.SetField, *, ctx: DepTraceContext):
    deps = set()

    for dep in qltracer.trace_refs(
            node.value,
            schema=ctx.schema,
            module=ctx.module,
            objects=ctx.objects,
    ):
        if dep.startswith(f"{ctx.module}::"):
            deps.add(dep)

    _register_item(node, deps=deps, ctx=ctx)
Exemplo n.º 2
0
def trace_SetField(node: qlast.SetField, *, ctx: DepTraceContext):
    deps = set()

    for dep in qltracer.trace_refs(
            node.value,
            schema=ctx.schema,
            module=ctx.module,
            objects=ctx.objects,
    ):
        # ignore std module dependencies
        if not STD_PREFIX_RE.match(dep):
            deps.add(dep)

    _register_item(node, deps=deps, ctx=ctx)
Exemplo n.º 3
0
def trace_SetField(
    node: qlast.SetField,
    *,
    ctx: DepTraceContext,
) -> None:
    deps = set()

    assert node.value, "sdl SetField should always have value"
    for dep in qltracer.trace_refs(
        node.value,
        schema=ctx.schema,
        module=ctx.module,
        objects=ctx.objects,
        params={},
    ):
        # ignore std module dependencies
        if dep.get_module_name() not in s_schema.STD_MODULES:
            deps.add(dep)

    _register_item(node, deps=deps, ctx=ctx)
Exemplo n.º 4
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)
Exemplo n.º 5
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
Exemplo n.º 6
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)