Ejemplo n.º 1
0
async def _get_dbs_and_roles(
    pgconn: asyncpg.Connection, ) -> Tuple[List[str], List[str]]:
    compiler = edbcompiler.Compiler()
    await compiler.initialize_from_pg(pgconn)
    compilerctx = edbcompiler.new_compiler_context(
        user_schema=s_schema.FlatSchema(),
        global_schema=s_schema.FlatSchema(),
        expected_cardinality_one=False,
        single_statement=True,
        output_format=edbcompiler.IoFormat.JSON,
        bootstrap_mode=True,
    )

    _, get_databases_sql = edbcompiler.compile_edgeql_script(
        compiler,
        compilerctx,
        'SELECT sys::Database.name',
    )

    databases = list(
        sorted(
            json.loads(await pgconn.fetchval(get_databases_sql)),
            key=lambda dname: edbdef.EDGEDB_TEMPLATE_DB in dname,
        ))

    _, get_roles_sql = edbcompiler.compile_edgeql_script(
        compiler,
        compilerctx,
        '''SELECT sys::Role {
            name,
            parents := .member_of.name,
        }''',
    )

    roles = json.loads(await pgconn.fetchval(get_roles_sql))
    sorted_roles = list(
        topological.sort({
            r['name']: topological.DepGraphEntry(
                item=r['name'],
                deps=r['parents'],
                extra=False,
            )
            for r in roles
        }))

    return databases, sorted_roles
Ejemplo n.º 2
0
async def _get_dbs_and_roles(pgconn) -> Tuple[List[str], List[str]]:
    compiler = edbcompiler.Compiler({})
    await compiler.ensure_initialized(pgconn)
    schema = compiler.get_std_schema()
    compilerctx = edbcompiler.new_compiler_context(
        schema,
        expected_cardinality_one=False,
        single_statement=True,
        output_format=edbcompiler.IoFormat.JSON,
    )

    schema, get_databases_sql = edbcompiler.compile_edgeql_script(
        compiler,
        compilerctx,
        'SELECT sys::Database.name',
    )

    databases = list(
        sorted(
            json.loads(await pgconn.fetchval(get_databases_sql)),
            key=lambda dname: dname == edbdef.EDGEDB_TEMPLATE_DB,
        ))

    schema, get_roles_sql = edbcompiler.compile_edgeql_script(
        compiler,
        compilerctx,
        '''SELECT sys::Role {
            name,
            parents := .member_of.name,
        }''',
    )

    roles = json.loads(await pgconn.fetchval(get_roles_sql))
    sorted_roles = list(
        topological.sort({
            r['name']: topological.DepGraphEntry(
                item=r['name'],
                deps=r['parents'],
                extra=False,
            )
            for r in roles
        }))

    return databases, sorted_roles
Ejemplo n.º 3
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
Ejemplo n.º 4
0
def _trace_item_layout(
    node: qlast.CreateObject,
    *,
    obj: Optional[qltracer.NamedObject] = None,
    fq_name: Optional[s_name.QualName] = None,
    ctx: LayoutTraceContext,
) -> None:
    if obj is None:
        fq_name = ctx.get_local_name(node.name)
        local_obj = ctx.objects[fq_name]
        assert isinstance(local_obj, qltracer.NamedObject)
        obj = local_obj

    assert fq_name is not None

    if isinstance(node, qlast.BasesMixin):
        bases = []
        # construct the parents set, used later in ancestors graph
        parents = set()

        for ref in _get_bases(node, ctx=ctx):
            bases.append(ref)

            # ignore std modules dependencies
            if ref.get_module_name() not in s_schema.STD_MODULES:
                parents.add(ref)

            if (
                ref.module not in ctx.local_modules
                and ref not in ctx.inh_graph
            ):
                base_obj = type(obj)(name=ref)
                ctx.inh_graph[ref] = topological.DepGraphEntry(item=base_obj)

                base = ctx.schema.get(ref)
                if isinstance(base, s_sources.Source):
                    assert isinstance(base_obj, qltracer.Source)
                    base_pointers = base.get_pointers(ctx.schema)
                    for pn, p in base_pointers.items(ctx.schema):
                        base_obj.pointers[pn] = qltracer.Pointer(
                            s_name.QualName('__', pn.name),
                            source=base,
                            target=p.get_target(ctx.schema),
                        )

        ctx.parents[fq_name] = parents
        ctx.inh_graph[fq_name] = topological.DepGraphEntry(
            item=obj,
            deps=set(bases),
            merge=set(bases),
        )

    for decl in node.commands:
        if isinstance(decl, qlast.CreateConcretePointer):
            assert isinstance(obj, qltracer.Source)
            target: Optional[qltracer.TypeLike]
            if isinstance(decl.target, qlast.TypeExpr):
                target = _resolve_type_expr(decl.target, ctx=ctx)
            else:
                target = None

            pn = s_utils.ast_ref_to_unqualname(decl.name)
            ptr = qltracer.Pointer(
                s_name.QualName('__', pn.name),
                source=obj,
                target=target,
            )
            obj.pointers[pn] = ptr
            ptr_name = s_name.QualName(
                module=fq_name.module,
                name=f'{fq_name.name}@{decl.name.name}',
            )
            ctx.objects[ptr_name] = ptr
            ctx.defdeps[fq_name].add(ptr_name)

            _trace_item_layout(
                decl, obj=ptr, fq_name=ptr_name, ctx=ctx)

        elif isinstance(decl, qlast.CreateConcreteConstraint):
            # Validate that the constraint exists at all.
            _validate_schema_ref(decl, ctx=ctx)
            _, con_fq_name = ctx.get_fq_name(decl)

            con_name = s_name.QualName(
                module=fq_name.module,
                name=f'{fq_name.name}@{con_fq_name}',
            )
            ctx.objects[con_name] = qltracer.ConcreteConstraint(con_name)
            ctx.constraints[fq_name].add(con_name)

        elif isinstance(decl, qlast.CreateAnnotationValue):
            # Validate that the constraint exists at all.
            _validate_schema_ref(decl, ctx=ctx)