예제 #1
0
    def from_ast(cls, qltree, schema, modaliases):
        norm_tree = imprint_expr_context(qltree, modaliases)
        norm_text = qlcodegen.generate_source(norm_tree, pretty=False)

        return cls(
            text=norm_text,
            origtext=qlcodegen.generate_source(qltree),
            _qlast=norm_tree,
        )
예제 #2
0
    def from_ast(cls, qltree, schema, modaliases, *, as_fragment=False):
        orig_text = qlcodegen.generate_source(qltree, pretty=False)
        if not as_fragment:
            qltree = imprint_expr_context(qltree, modaliases)
        norm_text = qlcodegen.generate_source(qltree, pretty=False)

        return cls(
            text=norm_text,
            origtext=orig_text,
            _qlast=qltree,
        )
예제 #3
0
    def _parse_field_setters(self, scls,
                             field_decls: typing.List[qlast.Field]):
        fields = type(scls).get_fields()
        updates = {}

        for field_decl in field_decls:
            fieldname = field_decl.name.name

            attrfield = fields.get(fieldname)
            if attrfield is None or not attrfield.allow_ddl_set:
                raise errors.SchemaError(f'unexpected field {fieldname}',
                                         context=field_decl.context)

            if issubclass(attrfield.type, s_expr.Expression):
                updates[fieldname] = s_expr.Expression(
                    text=qlcodegen.generate_source(field_decl.value))

            else:
                updates[fieldname] = qlcompiler.evaluate_ast_to_python_val(
                    field_decl.value,
                    self._schema,
                    modaliases=self._mod_aliases)

        if updates:
            self._schema = scls.update(self._schema, updates)
예제 #4
0
    def handle_signature(self, sig, signode):
        if debug.flags.disable_docs_edgeql_validation:
            signode['eql-fullname'] = fullname = re.split(r'\(| ', sig)[0]
            signode['eql-signature'] = sig
            mod, name = fullname.split('::')
            signode['eql-module'] = mod
            signode['eql-name'] = name

            return fullname

        from edb.edgeql.parser import parser as edgeql_parser
        from edb.edgeql import ast as ql_ast
        from edb.edgeql import codegen as ql_gen

        parser = edgeql_parser.EdgeQLBlockParser()
        try:
            astnode = parser.parse(f'create abstract constraint {sig};')[0]
        except Exception as ex:
            raise self.error(
                f'could not parse constraint signature {sig!r}') from ex

        if (not isinstance(astnode, ql_ast.CreateConstraint)
                or not isinstance(astnode.name, ql_ast.ObjectRef)):
            raise self.error(f'EdgeQL parser returned unsupported AST')

        modname = astnode.name.module
        constr_name = astnode.name.name
        if not modname:
            raise self.error(
                f'Missing module in EdgeQL constraint declaration')

        constr_repr = ql_gen.generate_source(astnode)

        m = re.match(
            r'''(?xs)
            ^
            create\sabstract\sconstraint\s
            (?P<f>.*?)(?:\s*on(?P<subj>.*))?
            $
        ''', constr_repr)
        if not m or not m.group('f'):
            raise self.error(
                f'could not recreate constraint signature from AST')
        constr_repr = m.group('f')

        signode['eql-module'] = modname
        signode['eql-name'] = constr_name
        signode['eql-fullname'] = fullname = f'{modname}::{constr_name}'
        signode['eql-signature'] = constr_repr
        subject = m.group('subj')
        if subject:
            subject = subject.strip()[1:-1]
            signode['eql-subjexpr'] = subject
            signode['eql-signature'] += f' on ({subject})'

        signode += s_nodes.desc_annotation('constraint', 'constraint')
        signode += d_nodes.Text(' ')
        signode += s_nodes.desc_name(fullname, fullname)

        return fullname
예제 #5
0
파일: ops.py 프로젝트: stjordanis/edgedb
def value_to_edgeql_const(setting: spec.Setting, value: Any) -> str:
    if isinstance(setting.type, types.ConfigType):
        raise NotImplementedError(
            'cannot render non-scalar configuration value')

    ql = s_utils.const_ast_from_python(value)
    return qlcodegen.generate_source(ql)
예제 #6
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)
예제 #7
0
    def _init_constraints(self, constraints):
        for constraint, decl in constraints.items():
            attrs = {a.name.name: a.value for a in decl.fields}
            assert 'subject' not in attrs  # TODO: Add proper validation
            assert 'subjectexpr' not in attrs  # TODO: Add proper validation

            expr = attrs.pop('expr', None)
            if expr is not None:
                self._schema = constraint.set_field_value(
                    self._schema, 'expr',
                    s_expr.Expression(text=qlcodegen.generate_source(expr)))

            subjexpr = decl.subject
            if subjexpr is not None:
                self._schema = constraint.set_field_value(
                    self._schema, 'subjectexpr',
                    s_expr.Expression(
                        text=qlcodegen.generate_source(subjexpr)))

            self._schema, params = s_func.FuncParameterList.from_ast(
                self._schema,
                decl,
                self._mod_aliases,
                func_fqname=constraint.get_name(self._schema))

            for param in params.objects(self._schema):
                p_kind = param.get_kind(self._schema)
                if p_kind is qltypes.ParameterKind.NAMED_ONLY:
                    raise errors.InvalidConstraintDefinitionError(
                        'named only parameters are not allowed '
                        'in this context',
                        context=decl.context)

                if param.get_default(self._schema) is not None:
                    raise errors.InvalidConstraintDefinitionError(
                        'constraints do not support parameters '
                        'with defaults',
                        context=decl.context)

            self._schema = constraint.set_field_value(self._schema, 'params',
                                                      params)
예제 #8
0
    def _parse_subject_constraints(self, subject, subjdecl):
        # Perform initial collection of constraints defined in subject context.
        # At this point all referenced constraints should be fully initialized.

        for constrdecl in subjdecl.constraints:
            attrs = {a.name.name: a.value for a in constrdecl.fields}
            assert 'subject' not in attrs  # TODO: Add proper validation

            constr_name = self._get_ref_name(constrdecl.name)
            if constrdecl.args:
                args = [
                    s_expr.Expression(
                        text=qlcodegen.generate_source(arg, pretty=False))
                    for arg in constrdecl.args
                ]
            else:
                args = []

            modaliases = {None: subject.get_name(self._schema).module}
            if constrdecl.subject is not None:
                subjectexpr = s_expr.Expression(
                    text=qlcodegen.generate_source(constrdecl.subject))
            else:
                subjectexpr = None

            self._schema, c, _ = \
                s_constr.Constraint.create_concrete_constraint(
                    self._schema,
                    subject,
                    name=constr_name,
                    is_abstract=constrdecl.delegated,
                    sourcectx=constrdecl.context,
                    subjectexpr=subjectexpr,
                    args=args,
                    modaliases=modaliases,
                )

            self._schema = subject.add_constraint(self._schema, c)
예제 #9
0
def trace_ConcreteConstraint(node: qlast.CreateConcreteConstraint, *,
                             ctx: DepTraceContext):
    deps = set()

    base_name = ctx.get_ref_name(node.name)
    if base_name.module == ctx.module:
        deps.add(base_name)

    exprs = list(node.args)
    if node.subjectexpr:
        exprs.append(node.subjectexpr)

    for cmd in node.commands:
        if isinstance(cmd, qlast.SetField) and cmd.name == "expr":
            exprs.append(node.value)

    if isinstance(ctx.depstack[-1][0], qlast.AlterScalarType):
        # Scalars are tightly bound to their constraints, so
        # we must prohibit any possible reference to this scalar
        # type from within the constraint.
        loop_control = ctx.depstack[-1][1]
    else:
        loop_control = None

    if exprs:
        texts = [qlcodegen.generate_source(e) for e in exprs]
        m = hashlib.sha1()
        m.update('|'.join(texts).encode())
        extra_name = m.hexdigest()
    else:
        extra_name = None

    _register_item(
        node,
        deps=deps,
        hard_dep_exprs=exprs,
        loop_control=loop_control,
        subject=ctx.depstack[-1][1],
        extra_name=extra_name,
        ctx=ctx,
    )
예제 #10
0
    def get_fq_name(self, decl: qlast.CreateObject) -> Tuple[str, str]:
        # Get the basic name form.
        if isinstance(decl, qlast.CreateConcretePointer):
            name = decl.name.name
        elif isinstance(decl, qlast.BaseSetField):
            name = decl.name
        else:
            name = self.get_local_name(decl.name)

        # The name may need to be augmented if it's a pointer.
        if self.depstack:
            fq_name = self.depstack[-1][1] + "@" + name
        else:
            fq_name = name

        # Additionally, functions and concrete constraints may need an
        # extra name piece.
        extra_name = None
        if isinstance(decl, qlast.CreateFunction):
            # Functions are defined by their name + call signature, so we
            # need to add that to the "extra_name".
            extra_name = f'({qlcodegen.generate_source(decl.params)})'

        elif isinstance(decl, qlast.CreateConcreteConstraint):
            # Concrete constraints are defined by their expr, so we need
            # to add that to the "extra_name".
            exprs = list(decl.args)
            if decl.subjectexpr:
                exprs.append(decl.subjectexpr)

            for cmd in decl.commands:
                if isinstance(cmd, qlast.SetField) and cmd.name == "expr":
                    exprs.append(decl.value)

            extra_name = '|'.join(qlcodegen.generate_source(e) for e in exprs)

        if extra_name is not None:
            fq_name = f'{fq_name}:{extra_name}'

        return name, fq_name
예제 #11
0
    def handle_signature(self, sig, signode):
        if debug.flags.disable_docs_edgeql_validation:
            signode['eql-fullname'] = fullname = sig.split('(')[0]
            signode['eql-signature'] = sig
            mod, name = fullname.split('::')
            signode['eql-module'] = mod
            signode['eql-name'] = name

            return fullname

        from edb.edgeql.parser import parser as edgeql_parser
        from edb.edgeql import ast as ql_ast
        from edb.edgeql import codegen as ql_gen
        from edb.edgeql import qltypes

        parser = edgeql_parser.EdgeQLBlockParser()
        try:
            astnode = parser.parse(
                f'create function {sig} using SQL function "xxx";')[0]
        except Exception as ex:
            raise self.error(
                f'could not parse function signature {sig!r}') from ex

        if (not isinstance(astnode, ql_ast.CreateFunction)
                or not isinstance(astnode.name, ql_ast.ObjectRef)):
            raise self.error(f'EdgeQL parser returned unsupported AST')

        modname = astnode.name.module
        funcname = astnode.name.name
        if not modname:
            raise self.error(
                f'EdgeQL function declaration is missing namespace')

        func_repr = ql_gen.generate_source(astnode)
        m = re.match(
            r'''(?xs)
            ^
            create\sfunction\s
            (?P<f>.*?)
            \susing\ssql\sfunction
            .*$
        ''', func_repr)
        if not m or not m.group('f'):
            raise self.error(f'could not recreate function signature from AST')
        func_repr = m.group('f')

        signode['eql-module'] = modname
        signode['eql-name'] = funcname
        signode['eql-fullname'] = fullname = f'{modname}::{funcname}'
        signode['eql-signature'] = func_repr

        signode += s_nodes.desc_annotation('function', 'function')
        signode += d_nodes.Text(' ')
        signode += s_nodes.desc_name(fullname, fullname)

        ret_repr = ql_gen.generate_source(astnode.returning)
        if astnode.returning_typemod is qltypes.TypeModifier.SetOfType:
            ret_repr = f'set of {ret_repr}'
        elif astnode.returning_typemod is qltypes.TypeModifier.OptionalType:
            ret_repr = f'optional {ret_repr}'
        signode += s_nodes.desc_returns(ret_repr, ret_repr)

        return fullname