Exemple #1
0
    def _validate_pointer_def(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        """Check that property definition is sound."""
        super()._validate_pointer_def(schema, context)

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

        if scls.is_special_pointer(schema):
            return

        if (
            scls.is_link_property(schema)
            and not scls.is_pure_computable(schema)
        ):
            # link properties cannot be required or multi
            if self.get_attribute_value('required'):
                raise errors.InvalidPropertyDefinitionError(
                    'link properties cannot be required',
                    context=self.source_context,
                )
            if (self.get_attribute_value('cardinality')
                    is qltypes.SchemaCardinality.Many):
                raise errors.InvalidPropertyDefinitionError(
                    "multi properties aren't supported for links",
                    context=self.source_context,
                )

        target_type = scls.get_target(schema)
        if target_type is None:
            raise TypeError(f'missing target type in scls {scls}')

        if target_type.is_polymorphic(schema):
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidPropertyTargetError(
                f'invalid property type: '
                f'{target_type.get_verbosename(schema)} '
                f'is a generic type',
                context=srcctx,
            )

        if (target_type.is_object_type()
                or (isinstance(target_type, s_types.Collection)
                    and target_type.contains_object(schema))):
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidPropertyTargetError(
                f'invalid property type: expected a scalar type, '
                f'or a scalar collection, got '
                f'{target_type.get_verbosename(schema)}',
                context=srcctx,
            )
Exemple #2
0
    def _validate_pointer_def(self, schema, context):
        """Check that property definition is sound."""
        super()._validate_pointer_def(schema, context)

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

        if scls.is_special_pointer(schema):
            return

        if scls.is_link_property(schema):
            # link properties cannot be required or multi
            if self.get_attribute_value('required'):
                raise errors.InvalidPropertyDefinitionError(
                    'link properties cannot be required',
                    context=self.source_context,
                )
            if (self.get_attribute_value('cardinality')
                    is qltypes.Cardinality.MANY):
                raise errors.InvalidPropertyDefinitionError(
                    "multi properties aren't supported for links",
                    context=self.source_context,
                )

        target_type = scls.get_target(schema)

        if target_type.is_polymorphic(schema):
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidPropertyTargetError(
                f'invalid property type: '
                f'{target_type.get_verbosename(schema)} '
                f'is a generic type',
                context=srcctx,
            )

        if (target_type.is_object_type()
                or (target_type.is_collection()
                    and target_type.contains_object(schema))):
            srcctx = self.get_attribute_source_context('target')
            raise errors.InvalidPropertyTargetError(
                f'invalid property type: expected a scalar type, '
                f'or a scalar collection, got '
                f'{target_type.get_verbosename(schema)}',
                context=srcctx,
            )

        if target_type.is_collection():
            srcctx = self.get_attribute_source_context('target')
            sd.ensure_schema_collection(
                schema, target_type, self,
                src_context=srcctx,
                context=context,
            )
Exemple #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)
Exemple #4
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        modaliases = context.modaliases

        if isinstance(astnode, qlast.CreateConcreteProperty):
            target = getattr(astnode, 'target', None)

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

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

            parent_ctx = context.get(PropertySourceContext)
            source_name = parent_ctx.op.classname

            cmd.add(
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(name=source_name)))

            if isinstance(target, qlast.TypeName):
                target_ref = utils.ast_to_typeref(target,
                                                  modaliases=modaliases,
                                                  schema=schema)
            else:
                # computable
                target_ref = cmd._parse_computable(target, schema, context)

            target_type = utils.resolve_typeref(target_ref, schema=schema)

            if (not isinstance(target_type,
                               (scalars.ScalarType, types.Collection))
                    or target_type.is_polymorphic(schema)):
                raise errors.InvalidPropertyTargetError(
                    f'invalid property target, expected primitive type, '
                    f'got {target_type.get_displayname(schema)!r}',
                    context=target.context)

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

            cls._parse_default(cmd)

        return cmd
Exemple #5
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        modaliases = context.modaliases

        if isinstance(astnode, qlast.CreateConcreteProperty):
            target = getattr(astnode, 'target', None)

            cmd.add(
                sd.AlterObjectProperty(property='required',
                                       new_value=astnode.is_required or False))

            cmd.add(
                sd.AlterObjectProperty(property='cardinality',
                                       new_value=astnode.cardinality
                                       or qltypes.Cardinality.ONE))

            parent_ctx = context.get(PropertySourceContext)
            source_name = parent_ctx.op.classname

            cmd.add(
                sd.AlterObjectProperty(
                    property='source',
                    new_value=so.ObjectRef(name=source_name)))

            if isinstance(target, qlast.TypeName):
                target_ref = utils.ast_to_typeref(target,
                                                  modaliases=modaliases,
                                                  schema=schema,
                                                  metaclass=s_types.Type)
            else:
                # computable
                target_ref, base = cmd._parse_computable(
                    target, schema, context)

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

                    cmd.set_attribute_value('derived_from', base)

                    cmd.set_attribute_value('is_derived', True)

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

            target_type = utils.resolve_typeref(target_ref, schema=schema)

            if target_type.is_polymorphic(schema):
                raise errors.InvalidPropertyTargetError(
                    f'invalid property type: '
                    f'{target_type.get_displayname(schema)!r} '
                    f'is a generic type',
                    context=target.context)

            if (target_type.is_object_type()
                    or (target_type.is_collection()
                        and target_type.contains_object(schema))):
                raise errors.InvalidPropertyTargetError(
                    f'invalid property type: expected a scalar type, '
                    f'or a scalar collection, got '
                    f'{target_type.get_displayname(schema)!r}',
                    context=target.context)

            if target_type.is_collection():
                sd.ensure_schema_collection(
                    schema,
                    target_type,
                    cmd,
                    src_context=target.context,
                    context=context,
                )

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

            cls._parse_default(cmd)

        else:
            # this is an abstract property then
            if cmd.get_attribute_value('default') is not None:
                raise errors.SchemaDefinitionError(
                    f"'default' is not a valid field for an abstact property",
                    context=astnode.context)

        return cmd
Exemple #6
0
    def _parse_source_props(self, source, sourcedecl):
        for propdecl in sourcedecl.properties:
            prop_name = propdecl.name
            if len(prop_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=propdecl.context)

            if propdecl.extends:
                prop_bases = [self._get_ref_type(b) for b in propdecl.extends]
            else:
                prop_bases = [
                    self._schema.get(s_props.Property.get_default_base_name())
                ]

            prop_target = None

            if propdecl.target is not None:
                prop_target = self._get_ref_type(propdecl.target[0])
                if not isinstance(prop_target, (s_scalars.ScalarType,
                                                s_types.Collection)):
                    raise errors.InvalidPropertyTargetError(
                        f'invalid property type: expected primitive type, '
                        f'got {prop_target.__class__.__name__}',
                        context=propdecl.target[0].context
                    )

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

            name = self._get_derived_ptr_name(prop_name, source)
            self._schema, prop = prop_bases[0].derive(
                self._schema, source, prop_target,
                merge_bases=prop_bases, attrs=new_props,
                name=name)

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

            self._schema = prop.update(self._schema, {
                'declared_inherited': propdecl.inherited,
                'required': bool(propdecl.required),
                'cardinality': cardinality,
            })

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

            self._schema = source.add_pointer(self._schema, prop)

            if propdecl.annotations:
                self._parse_attr_setters(prop, propdecl.annotations)

            if propdecl.fields:
                self._parse_field_setters(prop, propdecl.fields)

            if propdecl.constraints:
                self._parse_subject_constraints(prop, propdecl)
Exemple #7
0
    def _parse_source_props(self, source, sourcedecl):
        for propdecl in sourcedecl.properties:
            prop_name = self._get_ref_name(propdecl.name)
            if len(prop_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=propdecl.context)

            prop_base = self._schema.get(prop_name,
                                         type=s_props.Property,
                                         default=None,
                                         module_aliases=self._mod_aliases)
            prop_target = None

            if prop_base is None:
                if not source.generic(self._schema):
                    # Only generic links can implicitly define properties
                    raise errors.SchemaDefinitionError(
                        f'reference to an undefined property {prop_name!r}')

                # The link property has not been defined globally.
                if not s_name.Name.is_qualified(prop_name):
                    # If the name is not fully qualified, assume inline link
                    # property definition. The only attribute that is used for
                    # global definition is the name.
                    prop_qname = s_name.Name(name=prop_name,
                                             module=source.get_name(
                                                 self._schema).module)

                    std_lprop = self._schema.get(
                        s_props.Property.get_default_base_name(),
                        module_aliases=self._mod_aliases)

                    self._schema, prop_base = \
                        s_props.Property.create_in_schema(
                            self._schema,
                            name=prop_qname,
                            bases=[std_lprop])

                else:
                    prop_qname = s_name.Name(prop_name)
            else:
                prop_qname = prop_base.get_name(self._schema)

            if propdecl.target is not None:
                prop_target = self._get_ref_type(propdecl.target[0])
                if not isinstance(prop_target,
                                  (s_scalars.ScalarType, s_types.Collection)):
                    raise errors.InvalidPropertyTargetError(
                        f'invalid property target, expected primitive type, '
                        f'got {prop_target.__class__.__name__}',
                        context=propdecl.target[0].context)

            elif not source.generic(self._schema):
                link_base = source.get_bases(self._schema).first(self._schema)
                propdef = link_base.getptr(self._schema, prop_qname.name)
                if not propdef:
                    raise errors.SchemaError(
                        'link {!r} does not define property '
                        '{!r}'.format(source.get_name(self._schema),
                                      prop_qname.name))

                prop_qname = propdef.get_shortname(self._schema)

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

            self._schema, prop = prop_base.derive(self._schema,
                                                  source,
                                                  prop_target,
                                                  attrs=new_props)

            self._schema = prop.update(
                self._schema, {
                    'declared_inherited': propdecl.inherited,
                    'required': bool(propdecl.required),
                    'cardinality': propdecl.cardinality,
                })

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

            self._schema = source.add_pointer(self._schema, prop)

            if propdecl.attributes:
                self._parse_attr_setters(prop, propdecl.attributes)

            if propdecl.fields:
                self._parse_field_setters(prop, propdecl.fields)

            if propdecl.constraints:
                self._parse_subject_constraints(prop, propdecl)
Exemple #8
0
    def _process_create_ast(cls, schema, astnode, context, cmd):
        """Handle the CREATE PROPERTY ast node.

        This may be called in the context of either Create or Alter.
        """

        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)

        parent_ctx = context.get(PropertySourceContext)
        source_name = parent_ctx.op.classname
        cmd.set_attribute_value('source', so.ObjectRef(name=source_name))

        target = getattr(astnode, 'target', None)

        if isinstance(target, qlast.TypeName):
            target_ref = utils.ast_to_typeref(
                target, modaliases=context.modaliases, schema=schema,
                metaclass=s_types.Type)
        else:
            # computable
            target_ref, base = cmd._parse_computable(
                target, 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
                    )

        target_type = utils.resolve_typeref(target_ref, schema=schema)

        if target_type.is_polymorphic(schema):
            raise errors.InvalidPropertyTargetError(
                f'invalid property type: '
                f'{target_type.get_displayname(schema)!r} '
                f'is a generic type',
                context=target.context
            )

        if (target_type.is_object_type()
                or (target_type.is_collection()
                    and target_type.contains_object(schema))):
            raise errors.InvalidPropertyTargetError(
                f'invalid property type: expected a scalar type, '
                f'or a scalar collection, got '
                f'{target_type.get_displayname(schema)!r}',
                context=target.context
            )

        if target_type.is_collection():
            sd.ensure_schema_collection(
                schema, target_type, cmd,
                src_context=target.context,
                context=context,
            )

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

            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 = SetPropertyType(classname=cmd.classname, type=target_ref)
            slt.set_attribute_value('target', target_ref)
            cmd.add(slt)

        cls._parse_default(cmd)