示例#1
0
    def _normalize_objtype_expressions(self, objtype, typedecl):
        """Interpret and validate EdgeQL expressions in type declaration."""
        for ptrdecl in itertools.chain(typedecl.links, typedecl.properties):
            link_name = self._get_ref_name(ptrdecl.name)
            generic_link = self._schema.get(link_name,
                                            module_aliases=self._mod_aliases)
            spec_link = objtype.getptr(
                self._schema,
                generic_link.get_name(self._schema).name)

            if ptrdecl.expr is not None:
                # Computable
                self._normalize_ptr_default(ptrdecl.expr, objtype, spec_link,
                                            ptrdecl)

            for attr in ptrdecl.fields:
                name = attr.name.name
                if name == 'default':
                    if isinstance(attr.value, edgeql.ast.SelectQuery):
                        self._normalize_ptr_default(attr.value, objtype,
                                                    spec_link, ptrdecl)
                    else:
                        expr = attr.value
                        _, _, default = qlutils.normalize_tree(
                            expr, self._schema)
                        self._schema = spec_link.set_field_value(
                            self._schema, 'default', default)
示例#2
0
    def _parse_computable(self, expr, schema, context) -> so.ObjectRef:
        from edb.lang.edgeql import utils as ql_utils
        from . import sources as s_sources

        # "source" attribute is set automatically as a refdict back-attr
        parent_ctx = context.get(s_sources.SourceCommandContext)
        source_name = parent_ctx.op.classname

        source = schema.get(source_name, default=None)
        if source is None:
            raise errors.SchemaDefinitionError(
                f'cannot define link/property computables in CREATE TYPE',
                hint='Perform a CREATE TYPE without the link '
                'followed by ALTER TYPE defining the computable',
                context=expr.context)

        ir, _, target_expr = ql_utils.normalize_tree(
            expr, schema, anchors={qlast.Source: source}, singletons=[source])

        target = utils.reduce_to_typeref(schema, ir.stype)

        self.add(
            sd.AlterObjectProperty(property='default', new_value=target_expr))

        self.add(sd.AlterObjectProperty(property='computable', new_value=True))

        self.add(
            sd.AlterObjectProperty(property='cardinality',
                                   new_value=ir.cardinality))

        return target
示例#3
0
    def _normalize_ptr_default(self, expr, source, ptr, ptrdecl):
        module_aliases = {None: source.get_name(self._schema).module}

        ir, _, expr_text = qlutils.normalize_tree(
            expr,
            self._schema,
            modaliases=module_aliases,
            anchors={qlast.Source: source},
            singletons=[source])

        expr_type = ir.stype

        self._schema = ptr.set_field_value(self._schema, 'default', expr_text)

        if ptr.is_pure_computable(self._schema):
            # Pure computable without explicit target.
            # Fixup pointer target and target property.
            self._schema = ptr.set_field_value(self._schema, 'target',
                                               expr_type)

            if isinstance(ptr, s_links.Link):
                if not isinstance(expr_type, s_objtypes.ObjectType):
                    raise errors.InvalidLinkTargetError(
                        f'invalid link target, expected object type, got '
                        f'{expr_type.__class__.__name__}',
                        context=ptrdecl.expr.context)
            else:
                if not isinstance(expr_type,
                                  (s_scalars.ScalarType, s_types.Collection)):
                    raise errors.InvalidPropertyTargetError(
                        f'invalid property target, expected primitive type, '
                        f'got {expr_type.__class__.__name__}',
                        context=ptrdecl.expr.context)

            if isinstance(ptr, s_links.Link):
                tgt_prop = ptr.getptr(self._schema, 'target')
                self._schema = tgt_prop.set_field_value(
                    self._schema, 'target', expr_type)

            self._schema = ptr.set_field_value(self._schema, 'cardinality',
                                               ir.cardinality)

            if ptrdecl.cardinality is not ptr.get_cardinality(self._schema):
                if ptrdecl.cardinality is qlast.Cardinality.ONE:
                    raise errors.SchemaError(
                        f'computable expression possibly returns more than '
                        f'one value, but the {ptr.schema_class_displayname!r} '
                        f'is declared as "single"',
                        context=expr.context)

        if (not isinstance(expr_type, s_abc.Type)
                or (ptr.get_target(self._schema) is not None
                    and not expr_type.issubclass(
                        self._schema, ptr.get_target(self._schema)))):
            raise errors.SchemaError(
                'default value query must yield a single result of '
                'type {!r}'.format(
                    ptr.get_target(self._schema).get_name(self._schema)),
                context=expr.context)
示例#4
0
    def _normalize_constraint_expr(cls,
                                   schema,
                                   module_aliases,
                                   expr,
                                   subject,
                                   *,
                                   inline_anchors=False):
        from edb.lang.edgeql import parser as edgeql_parser
        from edb.lang.edgeql import utils as edgeql_utils

        if isinstance(expr, str):
            tree = edgeql_parser.parse(expr, module_aliases)
        else:
            tree = expr

        ir, edgeql_tree, _ = edgeql_utils.normalize_tree(
            tree,
            schema,
            modaliases=module_aliases,
            anchors={qlast.Subject: subject},
            inline_anchors=inline_anchors)

        return edgeql_tree.result, ir
示例#5
0
    def _parse_subject_indexes(self, subject, subjdecl):
        module_aliases = {None: subject.get_name(self._schema).module}

        for indexdecl in subjdecl.indexes:
            index_name = self._get_ref_name(indexdecl.name)
            index_name = subject.get_name(self._schema) + '.' + index_name
            local_name = s_name.get_specialized_name(
                index_name, subject.get_name(self._schema))

            der_name = s_name.Name(name=local_name,
                                   module=subject.get_name(
                                       self._schema).module)

            _, _, index_expr = qlutils.normalize_tree(
                indexdecl.expression,
                self._schema,
                modaliases=module_aliases,
                anchors={qlast.Subject: subject},
                inline_anchors=True)

            self._schema, index = s_indexes.SourceIndex.create_in_schema(
                self._schema, name=der_name, expr=index_expr, subject=subject)

            self._schema = subject.add_index(self._schema, index)
示例#6
0
文件: links.py 项目: versada/edgedb
    def _cmd_tree_from_ast(cls, astnode, context, schema):
        from edb.lang.edgeql import utils as ql_utils
        from edb.lang.ir import ast as irast
        from edb.lang.ir import inference as ir_inference
        from edb.lang.ir import utils as ir_utils
        from . import objtypes as s_objtypes

        cmd = super()._cmd_tree_from_ast(astnode, context, schema)

        if isinstance(astnode, qlast.CreateConcreteLink):
            cmd.add(
                sd.AlterObjectProperty(property='required',
                                       new_value=astnode.is_required))

            # "source" attribute is set automatically as a refdict back-attr
            parent_ctx = context.get(LinkSourceCommandContext)
            source_name = parent_ctx.op.classname
            target_type = None

            if len(astnode.targets) > 1:
                cmd.add(
                    sd.AlterObjectProperty(
                        property='spectargets',
                        new_value=so.ObjectList([
                            utils.ast_to_typeref(t,
                                                 modaliases=context.modaliases,
                                                 schema=schema)
                            for t in astnode.targets
                        ])))

                target_name = sources.Source.gen_virt_parent_name(
                    (sn.Name(module=t.maintype.module, name=t.maintype.name)
                     for t in astnode.targets),
                    module=source_name.module)

                target = so.ObjectRef(classname=target_name)

                create_virt_parent = s_objtypes.CreateObjectType(
                    classname=target_name, metaclass=s_objtypes.ObjectType)

                create_virt_parent.update(
                    (sd.AlterObjectProperty(
                        property='bases',
                        new_value=so.ObjectList([
                            so.ObjectRef(
                                classname=sn.Name(module='std', name='Object'))
                        ])),
                     sd.AlterObjectProperty(property='name',
                                            new_value=target_name),
                     sd.AlterObjectProperty(property='is_virtual',
                                            new_value=True)))

                alter_db_ctx = context.get(s_db.DatabaseCommandContext)

                for cc in alter_db_ctx.op.get_subcommands(
                        type=s_objtypes.CreateObjectType):
                    if cc.classname == create_virt_parent.classname:
                        break
                else:
                    alter_db_ctx.op.add(create_virt_parent)
            else:
                target_expr = astnode.targets[0]
                if isinstance(target_expr, qlast.TypeName):
                    target = utils.ast_to_typeref(
                        target_expr,
                        modaliases=context.modaliases,
                        schema=schema)
                else:
                    # computable
                    source = schema.get(source_name, default=None)
                    if source is None:
                        raise s_err.SchemaDefinitionError(
                            f'cannot define link computables in CREATE TYPE',
                            hint='Perform a CREATE TYPE without the link '
                            'followed by ALTER TYPE defining the '
                            'computable',
                            context=target_expr.context)

                    ir, _, target_expr = ql_utils.normalize_tree(
                        target_expr, schema, anchors={qlast.Source: source})

                    try:
                        target_type = ir_utils.infer_type(ir, schema)
                    except edgeql.EdgeQLError as e:
                        raise s_err.SchemaDefinitionError(
                            'could not determine the result type of '
                            'computable expression',
                            context=target_expr.context) from e

                    target = utils.reduce_to_typeref(target_type)

                    cmd.add(
                        sd.AlterObjectProperty(property='default',
                                               new_value=target_expr))

                    cmd.add(
                        sd.AlterObjectProperty(property='computable',
                                               new_value=True))

                    singletons = {irast.PathId(source)}

                    cardinality = ir_inference.infer_cardinality(
                        ir, singletons, schema)

                    if cardinality == qlast.Cardinality.ONE:
                        link_card = pointers.PointerCardinality.ManyToOne
                    else:
                        link_card = pointers.PointerCardinality.ManyToMany

                    cmd.add(
                        sd.AlterObjectProperty(property='cardinality',
                                               new_value=link_card))

            if (isinstance(target, so.ObjectRef)
                    and target.classname == source_name):
                # Special case for loop links.  Since the target
                # is the same as the source, we know it's a proper
                # type.
                pass
            else:
                if target_type is None:
                    target_type = utils.resolve_typeref(target, schema=schema)

                if not isinstance(target_type, s_objtypes.ObjectType):
                    raise s_err.SchemaDefinitionError(
                        f'invalid link target, expected object type, got '
                        f'{target_type.__class__.__name__}',
                        context=astnode.targets[0].context)

            cmd.add(sd.AlterObjectProperty(property='target',
                                           new_value=target))

            base_prop_name = sn.Name('std::source')
            s_name = lproperties.Property.get_specialized_name(
                base_prop_name, cmd.classname)
            src_prop_name = sn.Name(name=s_name, module=cmd.classname.module)

            src_prop = lproperties.CreateProperty(
                classname=src_prop_name, metaclass=lproperties.Property)
            src_prop.update((
                sd.AlterObjectProperty(property='name',
                                       new_value=src_prop_name),
                sd.AlterObjectProperty(
                    property='bases',
                    new_value=[so.ObjectRef(classname=base_prop_name)]),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(classname=cmd.classname)),
                sd.AlterObjectProperty(
                    property='target',
                    new_value=so.ObjectRef(classname=source_name)),
                sd.AlterObjectProperty(property='required', new_value=True),
                sd.AlterObjectProperty(property='readonly', new_value=True),
            ))

            cmd.add(src_prop)

            base_prop_name = sn.Name('std::target')
            s_name = lproperties.Property.get_specialized_name(
                base_prop_name, cmd.classname)
            tgt_prop_name = sn.Name(name=s_name, module=cmd.classname.module)

            tgt_prop = lproperties.CreateProperty(
                classname=tgt_prop_name, metaclass=lproperties.Property)
            tgt_prop.update((
                sd.AlterObjectProperty(property='name',
                                       new_value=tgt_prop_name),
                sd.AlterObjectProperty(
                    property='bases',
                    new_value=[so.ObjectRef(classname=base_prop_name)]),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(classname=cmd.classname)),
                sd.AlterObjectProperty(property='target', new_value=target),
                sd.AlterObjectProperty(property='required', new_value=False),
                sd.AlterObjectProperty(property='readonly', new_value=True),
            ))

            cmd.add(tgt_prop)

            cls._parse_default(cmd)

        return cmd