예제 #1
0
    def validate_object(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        """Check that link definition is sound."""
        super().validate_object(schema, context)

        scls = self.scls
        assert isinstance(scls, Link)

        if not scls.get_owned(schema):
            return

        target = scls.get_target(schema)
        assert target is not None

        if not target.is_object_type():
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidLinkTargetError(
                f'invalid link target, expected object type, got '
                f'{target.get_schema_class_displayname()}',
                context=srcctx,
            )

        if (not scls.is_pure_computable(schema)
                and not scls.get_from_alias(schema)
                and target.is_view(schema)):
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidLinkTargetError(
                f'invalid link type: {target.get_displayname(schema)!r}'
                f' is an expression alias, not a proper object type',
                context=srcctx,
            )
예제 #2
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)
예제 #3
0
def _interpret_wrong_object_type(code, schema, err_details, hint):
    if err_details.message == 'covariance error':
        ptr = schema.get_by_id(uuidgen.UUID(err_details.column_name))
        wrong_obj = schema.get_by_id(uuidgen.UUID(err_details.table_name))

        vn = ptr.get_verbosename(schema, with_parent=True)
        return errors.InvalidLinkTargetError(
            f"invalid target for {vn}: '{wrong_obj.get_name(schema)}'"
            f" (expecting '{ptr.get_target(schema).get_name(schema)}')")

    return errors.InternalServerError(err_details.message)
예제 #4
0
파일: links.py 프로젝트: haikyuu/edgedb
    def _validate_pointer_def(self, schema, context):
        """Check that link definition is sound."""
        super()._validate_pointer_def(schema, context)

        scls = self.scls
        if not scls.get_is_local(schema):
            return

        target = scls.get_target(schema)

        if not target.is_object_type():
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidLinkTargetError(
                f'invalid link target, expected object type, got '
                f'{target.get_schema_class_displayname()}',
                context=srcctx,
            )
예제 #5
0
파일: links.py 프로젝트: zhutony/edgedb
    def _validate_pointer_def(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        """Check that link definition is sound."""
        super()._validate_pointer_def(schema, context)

        scls = self.scls
        assert isinstance(scls, Link)

        if not scls.get_is_local(schema):
            return

        target = scls.get_target(schema)
        assert target is not None

        if not target.is_object_type():
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidLinkTargetError(
                f'invalid link target, expected object type, got '
                f'{target.get_schema_class_displayname()}',
                context=srcctx,
            )
예제 #6
0
    def _init_objtypes(self, objtypes):
        for objtype, objtypedecl in objtypes.items():
            self._parse_source_props(objtype, objtypedecl)

            if objtypedecl.fields:
                self._parse_field_setters(objtype, objtypedecl.fields)

            for linkdecl in objtypedecl.links:
                link_name = linkdecl.name
                if len(link_name) > s_pointers.MAX_NAME_LENGTH:
                    raise errors.SchemaDefinitionError(
                        f'link or property name length exceeds the maximum of '
                        f'{s_pointers.MAX_NAME_LENGTH} characters',
                        context=linkdecl.context)

                if linkdecl.extends:
                    link_bases = [
                        self._get_ref_type(b) for b in linkdecl.extends
                    ]
                else:
                    link_bases = [
                        self._schema.get(s_links.Link.get_default_base_name())
                    ]

                if linkdecl.expr is not None:
                    # This is a computable, but we cannot interpret
                    # the expression yet, so set the target to `any`
                    # temporarily.
                    _targets = [s_pseudo.Any.instance]

                else:
                    _targets = [self._get_ref_type(t) for t in linkdecl.target]

                if len(_targets) == 1:
                    # Usual case, just one target
                    spectargets = None
                    target = _targets[0]
                else:
                    # Multiple explicit targets, create common virtual
                    # parent and use it as target.
                    spectargets = s_obj.ObjectSet.create(
                        self._schema, _targets)

                    self._schema, target = s_inh.create_virtual_parent(
                        self._schema, _targets,
                        module_name=self._module.get_name(self._schema))

                    self._schema = target.set_field_value(
                        self._schema, 'is_derived', True)

                if (not target.is_any() and
                        not isinstance(target, s_objtypes.ObjectType)):
                    raise errors.InvalidLinkTargetError(
                        f'invalid link target, expected object type, got '
                        f'{target.__class__.__name__}',
                        context=linkdecl.target[0].context
                    )

                new_props = {
                    'sourcectx': linkdecl.context,
                }

                name = self._get_derived_ptr_name(link_name, objtype)
                self._schema, link = link_bases[0].derive(
                    self._schema, objtype, target,
                    attrs=new_props, merge_bases=link_bases,
                    apply_defaults=not linkdecl.inherited,
                    name=name)

                if linkdecl.cardinality is None:
                    if linkdecl.expr is None:
                        cardinality = qltypes.Cardinality.ONE
                    else:
                        cardinality = None
                else:
                    cardinality = linkdecl.cardinality

                self._schema = link.update(self._schema, {
                    'spectargets': spectargets,
                    'required': bool(linkdecl.required),
                    'cardinality': cardinality,
                    'declared_inherited': linkdecl.inherited,
                })

                if linkdecl.on_target_delete is not None:
                    self._schema = link.set_field_value(
                        self._schema,
                        'on_target_delete',
                        linkdecl.on_target_delete.cascade)

                if linkdecl.expr is not None:
                    self._schema = link.set_field_value(
                        self._schema, 'computable', True)

                self._parse_source_props(link, linkdecl)
                self._schema = objtype.add_pointer(self._schema, link)

        for objtype, objtypedecl in objtypes.items():
            if objtypedecl.indexes:
                self._parse_subject_indexes(objtype, objtypedecl)

            if objtypedecl.constraints:
                self._parse_subject_constraints(objtype, objtypedecl)
예제 #7
0
    def _process_link_create_or_alter(cls, schema, astnode, context, cmd):
        from . import objtypes as s_objtypes

        parent_ctx = context.get(LinkSourceCommandContext)

        if isinstance(astnode, qlast.CreateConcreteLink):
            # "source" attribute is set automatically as a refdict back-attr
            source_name = parent_ctx.op.classname

            if astnode.is_required is not None:
                cmd.set_attribute_value('required', astnode.is_required)

            if astnode.cardinality is not None:
                cmd.set_attribute_value('cardinality', astnode.cardinality)

            # FIXME: this is an approximate solution
            targets = qlast.get_targets(astnode.target)

            if len(targets) > 1:
                new_targets = [
                    utils.ast_to_typeref(
                        t, modaliases=context.modaliases,
                        schema=schema)
                    for t in targets
                ]

                target = cls._create_union_target(
                    schema, context, new_targets, module=source_name.module)
            else:
                target_expr = targets[0]
                if isinstance(target_expr, qlast.TypeName):
                    target = utils.ast_to_typeref(
                        target_expr, modaliases=context.modaliases,
                        schema=schema)
                else:
                    # computable
                    target, base = cmd._parse_computable(
                        target_expr, schema, context)

                    if base is not None:
                        cmd.set_attribute_value(
                            'bases', so.ObjectList.create(schema, [base]),
                        )

                        cmd.set_attribute_value(
                            'is_derived', True
                        )

                        if context.declarative:
                            cmd.set_attribute_value(
                                'declared_inherited', True
                            )

                if (isinstance(target, so.ObjectRef) and
                        target.name == 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:
                    target_type = utils.resolve_typeref(target, schema=schema)
                    if not isinstance(target_type, s_objtypes.ObjectType):
                        raise errors.InvalidLinkTargetError(
                            f'invalid link target, expected object type, got '
                            f'{target_type.__class__.__name__}',
                            context=astnode.target.context
                        )

            if isinstance(cmd, sd.CreateObject):
                cmd.set_attribute_value('target', target)

                if cmd.get_attribute_value('cardinality') is None:
                    cmd.set_attribute_value(
                        'cardinality', qltypes.Cardinality.ONE)

                if cmd.get_attribute_value('required') is None:
                    cmd.set_attribute_value(
                        'required', False)
            else:
                slt = SetLinkType(classname=cmd.classname, type=target)
                slt.set_attribute_value('target', target)
                cmd.add(slt)

            cls._parse_default(cmd)

        if parent_ctx is None:
            # this is an abstract link then
            if cmd.get_attribute_value('default') is not None:
                raise errors.SchemaDefinitionError(
                    f"'default' is not a valid field for an abstact link",
                    context=astnode.context)
예제 #8
0
    def _init_objtypes(self, objtypes):
        for objtype, objtypedecl in objtypes.items():
            self._parse_source_props(objtype, objtypedecl)

            if objtypedecl.fields:
                self._parse_field_setters(objtype, objtypedecl.fields)

            for linkdecl in objtypedecl.links:
                link_name = self._get_ref_name(linkdecl.name)
                if len(link_name) > s_pointers.MAX_NAME_LENGTH:
                    raise errors.SchemaDefinitionError(
                        f'link or property name length exceeds the maximum of '
                        f'{s_pointers.MAX_NAME_LENGTH} characters',
                        context=linkdecl.context)

                link_base = self._schema.get(link_name,
                                             type=s_links.Link,
                                             default=None,
                                             module_aliases=self._mod_aliases)
                if link_base is None:
                    # The link has not been defined globally.
                    if not s_name.Name.is_qualified(link_name):
                        # If the name is not fully qualified, assume inline
                        # link definition. The only attribute that is used for
                        # global definition is the name.
                        link_qname = s_name.Name(name=link_name,
                                                 module=objtype.get_name(
                                                     self._schema).module)

                        std_link = self._schema.get(
                            s_links.Link.get_default_base_name(),
                            module_aliases=self._mod_aliases)

                        self._schema, link_base = \
                            s_links.Link.create_in_schema(
                                self._schema,
                                name=link_qname,
                                bases=[std_link])

                    else:
                        link_qname = s_name.Name(link_name)
                else:
                    link_qname = link_base.get_name(self._schema)

                if linkdecl.expr is not None:
                    # This is a computable, but we cannot interpret
                    # the expression yet, so set the target to `any`
                    # temporarily.
                    _targets = [s_pseudo.Any.create()]

                else:
                    _targets = [self._get_ref_type(t) for t in linkdecl.target]

                if len(_targets) == 1:
                    # Usual case, just one target
                    spectargets = None
                    target = _targets[0]
                else:
                    # Multiple explicit targets, create common virtual
                    # parent and use it as target.
                    spectargets = s_obj.ObjectSet.create(
                        self._schema, _targets)
                    self._schema, target = link_base.create_common_target(
                        self._schema, _targets)

                if (not target.is_any()
                        and not isinstance(target, s_objtypes.ObjectType)):
                    raise errors.InvalidLinkTargetError(
                        f'invalid link target, expected object type, got '
                        f'{target.__class__.__name__}',
                        context=linkdecl.target[0].context)

                new_props = {
                    'sourcectx': linkdecl.context,
                }

                self._schema, link = link_base.derive(
                    self._schema,
                    objtype,
                    target,
                    attrs=new_props,
                    apply_defaults=not linkdecl.inherited)

                self._schema = link.update(
                    self._schema, {
                        'spectargets': spectargets,
                        'required': bool(linkdecl.required),
                        'cardinality': linkdecl.cardinality,
                        'declared_inherited': linkdecl.inherited,
                    })

                if linkdecl.on_target_delete is not None:
                    self._schema = link.set_field_value(
                        self._schema, 'on_target_delete',
                        linkdecl.on_target_delete.cascade)

                if linkdecl.expr is not None:
                    self._schema = link.set_field_value(
                        self._schema, 'computable', True)

                self._parse_source_props(link, linkdecl)
                self._schema = objtype.add_pointer(self._schema, link)

        for objtype, objtypedecl in objtypes.items():
            if objtypedecl.indexes:
                self._parse_subject_indexes(objtype, objtypedecl)

            if objtypedecl.constraints:
                self._parse_subject_constraints(objtype, objtypedecl)
예제 #9
0
def interpret_backend_error(schema, fields):
    err_details = get_error_details(fields)
    hint = None
    details = None
    if err_details.detail_json:
        hint = err_details.detail_json.get('hint')

    # all generic errors are static and have been handled by this point

    if err_details.code == pgerrors.ERROR_NOT_NULL_VIOLATION:
        colname = err_details.column_name
        if colname:
            if colname.startswith('??'):
                ptr_id, *_ = colname[2:].partition('_')
            else:
                ptr_id = colname
            pointer = common.get_object_from_backend_name(
                schema, s_pointers.Pointer, ptr_id)
            pname = pointer.get_verbosename(schema, with_parent=True)
        else:
            pname = None

        if pname is not None:
            if err_details.detail_json:
                object_id = err_details.detail_json.get('object_id')
                if object_id is not None:
                    details = f'Failing object id is {str(object_id)!r}.'

            return errors.MissingRequiredError(
                f'missing value for required {pname}',
                details=details,
                hint=hint,
            )
        else:
            return errors.InternalServerError(err_details.message)

    elif err_details.code in constraint_errors:
        error_type = None
        match = None

        for errtype, ere in constraint_res.items():
            m = ere.match(err_details.message)
            if m:
                error_type = errtype
                match = m
                break
        # no need for else clause since it would have been handled by
        # the static version

        if error_type == 'constraint':
            # similarly, if we're here it's because we have a constraint_id
            constraint_id, _, _ = err_details.constraint_name.rpartition(';')
            constraint_id = uuidgen.UUID(constraint_id)

            constraint = schema.get_by_id(constraint_id)

            return errors.ConstraintViolationError(
                constraint.format_error_message(schema))
        elif error_type == 'newconstraint':
            # If we're here, it means that we already validated that
            # schema_name, table_name and column_name all exist.
            tabname = (err_details.schema_name, err_details.table_name)
            source = common.get_object_from_backend_name(
                schema, s_objtypes.ObjectType, tabname)
            source_name = source.get_displayname(schema)
            pointer = common.get_object_from_backend_name(
                schema, s_pointers.Pointer, err_details.column_name)
            pointer_name = pointer.get_shortname(schema).name

            return errors.ConstraintViolationError(
                f'Existing {source_name}.{pointer_name} '
                f'values violate the new constraint')
        elif error_type == 'scalar':
            domain_name = match.group(1)
            stype_name = types.base_type_name_map_r.get(domain_name)
            if stype_name:
                if match.group(2) in range_constraints:
                    msg = f'{str(stype_name)!r} value out of range'
                else:
                    msg = f'invalid value for scalar type {str(stype_name)!r}'
            else:
                msg = translate_pgtype(schema, err_details.message)
            return errors.InvalidValueError(msg)

    elif err_details.code == pgerrors.ERROR_INVALID_TEXT_REPRESENTATION:
        return errors.InvalidValueError(
            translate_pgtype(schema, err_details.message))

    elif err_details.code == pgerrors.ERROR_NUMERIC_VALUE_OUT_OF_RANGE:
        return errors.NumericOutOfRangeError(
            translate_pgtype(schema, err_details.message))

    elif err_details.code in {pgerrors.ERROR_INVALID_DATETIME_FORMAT,
                              pgerrors.ERROR_DATETIME_FIELD_OVERFLOW}:
        return errors.InvalidValueError(
            translate_pgtype(schema, err_details.message),
            hint=hint)

    elif (
        err_details.code == pgerrors.ERROR_WRONG_OBJECT_TYPE
        and err_details.message == 'covariance error'
    ):
        ptr = schema.get_by_id(uuidgen.UUID(err_details.column_name))
        wrong_obj = schema.get_by_id(uuidgen.UUID(err_details.table_name))

        vn = ptr.get_verbosename(schema, with_parent=True)
        return errors.InvalidLinkTargetError(
            f"invalid target for {vn}: '{wrong_obj.get_name(schema)}'"
            f" (expecting '{ptr.get_target(schema).get_name(schema)}')"
        )

    return errors.InternalServerError(err_details.message)
예제 #10
0
파일: links.py 프로젝트: dungeon2567/edgedb
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        from . import objtypes as s_objtypes

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

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

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

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

            # FIXME: this is an approximate solution
            targets = qlast.get_targets(astnode.target)

            if len(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 targets
                        ])
                    )
                )

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

                target = so.ObjectRef(name=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(name=sn.Name(
                                module='std', name='Object'
                            ))
                        ])
                    ),
                    sd.AlterObjectProperty(
                        property='name',
                        new_value=target_name
                    ),
                    sd.AlterObjectProperty(
                        property='is_virtual',
                        new_value=True
                    )
                ))

                delta_ctx = context.get(sd.DeltaRootContext)

                for cc in delta_ctx.op.get_subcommands(
                        type=s_objtypes.CreateObjectType):
                    if cc.classname == create_virt_parent.classname:
                        break
                else:
                    delta_ctx.op.add(create_virt_parent)
            else:
                target_expr = targets[0]
                if isinstance(target_expr, qlast.TypeName):
                    target = utils.ast_to_typeref(
                        target_expr, modaliases=context.modaliases,
                        schema=schema)
                else:
                    # computable
                    target = cmd._parse_computable(
                        target_expr, schema, context)

            if (isinstance(target, so.ObjectRef) and
                    target.name == 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 errors.InvalidLinkTargetError(
                        f'invalid link target, expected object type, got '
                        f'{target_type.__class__.__name__}',
                        context=astnode.target.context
                    )

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

            base_prop_name = sn.Name('std::source')
            s_name = sn.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(
                            name=base_prop_name
                        )
                    ]
                ),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(
                        name=cmd.classname
                    )
                ),
                sd.AlterObjectProperty(
                    property='target',
                    new_value=so.ObjectRef(
                        name=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 = sn.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(
                            name=base_prop_name
                        )
                    ]
                ),
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(
                        name=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