Example #1
0
    def _validate(self, schema, context):
        implicit_bases = [
            b for b in self.scls.get_bases(schema).objects(schema)
            if not b.generic(schema)
        ]

        referrer_ctx = self.get_referrer_context(context)
        objcls = self.get_schema_metaclass()
        referrer_class = referrer_ctx.op.get_schema_metaclass()
        refdict = referrer_class.get_refdict_for_class(objcls)

        if context.declarative and self.scls.get_is_local(schema):
            if (implicit_bases and refdict.requires_explicit_inherit
                    and not self.get_attribute_value('declared_inherited')):

                ancestry = [obj.get_referrer(schema) for obj in implicit_bases]

                raise errors.SchemaDefinitionError(
                    f'{self.scls.get_verbosename(schema, with_parent=True)} '
                    f'must be declared using the `inherited` keyword because '
                    f'it is defined in the following ancestor(s): '
                    f'{", ".join(a.get_shortname(schema) for a in ancestry)}',
                    context=self.source_context,
                )
            elif (not implicit_bases
                  and self.get_attribute_value('declared_inherited')):

                raise errors.SchemaDefinitionError(
                    f'{self.scls.get_verbosename(schema, with_parent=True)}: '
                    f'cannot be declared `inherited` as there are no '
                    f'ancestors defining it.',
                    context=self.source_context,
                )
Example #2
0
    def _validate_pointer_def(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        """Check that pointer definition is sound."""
        from edb.ir import ast as irast

        referrer_ctx = self.get_referrer_context(context)
        if referrer_ctx is None:
            return

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

        default_expr = scls.get_default(schema)

        if default_expr is not None:
            if default_expr.irast is None:
                default_expr = default_expr.compiled(default_expr, schema)

            assert isinstance(default_expr.irast, irast.Statement)

            default_type = default_expr.irast.stype
            assert default_type is not None
            ptr_target = scls.get_target(schema)
            assert ptr_target is not None

            source_context = self.get_attribute_source_context('default')
            if not default_type.assignment_castable_to(ptr_target, schema):
                raise errors.SchemaDefinitionError(
                    f'default expression is of invalid type: '
                    f'{default_type.get_displayname(schema)}, '
                    f'expected {ptr_target.get_displayname(schema)}',
                    context=source_context,
                )
            # "required" status of defaults should not be enforced
            # because it's impossible to actually guarantee that any
            # SELECT involving a path is non-empty
            ptr_cardinality = scls.get_cardinality(schema)
            default_required, default_cardinality = \
                default_expr.irast.cardinality.to_schema_value()

            if (ptr_cardinality is qltypes.SchemaCardinality.ONE
                    and default_cardinality != ptr_cardinality):
                raise errors.SchemaDefinitionError(
                    f'possibly more than one element returned by '
                    f'the default expression for '
                    f'{scls.get_verbosename(schema)} declared as '
                    f"'single'",
                    context=source_context,
                )
Example #3
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        from edb.edgeql import compiler as qlcompiler

        propname = astnode.name.name

        parent_ctx = context.get(CommandContextToken)
        parent_op = parent_ctx.op
        field = parent_op.get_schema_metaclass().get_field(propname)
        if field is None:
            raise errors.SchemaDefinitionError(
                f'{propname!r} is not a valid field',
                context=astnode.context)

        if not (isinstance(astnode, qlast.SetInternalField)
                or field.allow_ddl_set
                or context.stdmode
                or context.testmode):
            raise errors.SchemaDefinitionError(
                f'{propname!r} is not a valid field',
                context=astnode.context)

        if field.type is s_expr.Expression:
            new_value = s_expr.Expression.from_ast(
                astnode.value,
                schema,
                context.modaliases,
            )
        else:
            if isinstance(astnode.value, qlast.Tuple):
                new_value = tuple(
                    qlcompiler.evaluate_ast_to_python_val(
                        el.value, schema=schema)
                    for el in astnode.value.elements
                )

            elif isinstance(astnode.value, qlast.ObjectRef):

                new_value = utils.ast_objref_to_objref(
                    astnode.value, modaliases=context.modaliases,
                    schema=schema)

            elif (isinstance(astnode.value, qlast.Set)
                    and not astnode.value.elements):
                # empty set
                new_value = None

            else:
                new_value = qlcompiler.evaluate_ast_to_python_val(
                    astnode.value, schema=schema)

        return cls(property=propname, new_value=new_value)
Example #4
0
    def canonicalize_attributes(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> s_schema.Schema:
        from edb.ir import ast as irast

        schema = super().canonicalize_attributes(schema, context)

        parent_ctx = self.get_referrer_context_or_die(context)
        source = parent_ctx.op.scls
        pol_name = self.get_verbosename(parent=source.get_verbosename(schema))

        for field in ('expr', 'condition'):
            if (expr := self.get_local_attribute_value(field)) is None:
                continue

            vname = 'when' if field == 'condition' else 'using'

            expression = self.compile_expr_field(
                schema,
                context,
                field=AccessPolicy.get_field(field),
                value=expr,
            )
            assert isinstance(expression.irast, irast.Statement)

            zero = expression.irast.cardinality.can_be_zero()
            if zero or expression.irast.cardinality.is_multi():
                srcctx = self.get_attribute_source_context(field)
                if zero:
                    problem = 'an empty set'
                else:
                    problem = 'more than one element'
                raise errors.SchemaDefinitionError(
                    f'possibly {problem} returned by {vname} '
                    f'expression for the {pol_name} ',
                    context=srcctx)

            target = schema.get(sn.QualName('std', 'bool'), type=s_types.Type)
            expr_type = expression.irast.stype
            if not expr_type.issubclass(schema, target):
                srcctx = self.get_attribute_source_context(field)
                raise errors.SchemaDefinitionError(
                    f'{vname} expression for {pol_name} is of invalid type: '
                    f'{expr_type.get_displayname(schema)}, '
                    f'expected {target.get_displayname(schema)}',
                    context=srcctx,
                )
Example #5
0
    def _classname_from_ast(
        cls,
        schema: s_schema.Schema,
        astnode: qlast.NamedDDL,
        context: sd.CommandContext,
    ) -> sn.Name:
        referrer_ctx = cls.get_referrer_context(context)
        if referrer_ctx is not None:

            referrer_name = referrer_ctx.op.classname
            assert isinstance(referrer_name, sn.Name)

            shortname = sn.Name(
                module='__',
                name=astnode.name.name,
            )

            name = sn.Name(
                module=referrer_name.module,
                name=sn.get_specialized_name(
                    shortname,
                    referrer_name,
                ),
            )
        else:
            name = super()._classname_from_ast(schema, astnode, context)

        shortname = sn.shortname_from_fullname(name)
        if len(shortname.name) > s_def.MAX_NAME_LENGTH:
            raise errors.SchemaDefinitionError(
                f'link or property name length exceeds the maximum of '
                f'{s_def.MAX_NAME_LENGTH} characters',
                context=astnode.context)
        return name
Example #6
0
def get_schema_object(
        name: Union[str, qlast.BaseObjectRef],
        module: Optional[str] = None,
        *,
        item_type: Optional[Type[s_obj.Object]] = None,
        condition: Optional[Callable[[s_obj.Object], bool]] = None,
        label: Optional[str] = None,
        ctx: context.ContextLevel,
        srcctx: Optional[parsing.ParserContext] = None) -> s_obj.Object:

    if isinstance(name, qlast.ObjectRef):
        if srcctx is None:
            srcctx = name.context
        module = name.module
        name = name.name
    elif isinstance(name, qlast.AnyType):
        return s_pseudo.PseudoType.get(ctx.env.schema, 'anytype')
    elif isinstance(name, qlast.AnyTuple):
        return s_pseudo.PseudoType.get(ctx.env.schema, 'anytuple')
    elif isinstance(name, qlast.BaseObjectRef):
        raise AssertionError(f"Unhandled BaseObjectRef subclass: {name!r}")

    if module:
        name = sn.Name(name=name, module=module)

    elif isinstance(name, str):
        view = _get_type_variant(name, ctx)
        if view is not None:
            return view

    try:
        stype = ctx.env.get_track_schema_object(
            name=name,
            modaliases=ctx.modaliases,
            type=item_type,
            condition=condition,
            label=label,
        )

    except errors.QueryError as e:
        s_utils.enrich_schema_lookup_error(e,
                                           name,
                                           modaliases=ctx.modaliases,
                                           schema=ctx.env.schema,
                                           item_type=item_type,
                                           condition=condition,
                                           context=srcctx)
        raise

    view = _get_type_variant(stype.get_name(ctx.env.schema), ctx)
    if view is not None:
        return view
    elif stype == ctx.defining_view:
        # stype is the view in process of being defined and as such is
        # not yet a valid schema object
        raise errors.SchemaDefinitionError(
            f'illegal self-reference in definition of {name!r}',
            context=srcctx)
    else:
        return stype
Example #7
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
Example #8
0
    def _rename_begin(self, schema: s_schema.Schema,
                      context: sd.CommandContext) -> s_schema.Schema:
        orig_schema = schema
        schema = super()._rename_begin(schema, context)
        scls = self.scls

        if not context.canonical and not scls.generic(schema):
            implicit_bases = scls.get_implicit_bases(schema)
            non_renamed_bases = set(implicit_bases) - context.renamed_objs

            # This object is inherited from one or more ancestors that
            # are not renamed in the same op, and this is an error.
            if non_renamed_bases:
                bases_str = ', '.join(
                    b.get_verbosename(schema, with_parent=True)
                    for b in non_renamed_bases)

                verb = 'are' if len(non_renamed_bases) > 1 else 'is'
                vn = scls.get_verbosename(orig_schema, with_parent=True)

                raise errors.SchemaDefinitionError(
                    f'cannot rename inherited {vn}',
                    details=(f'{vn} is inherited from '
                             f'{bases_str}, which {verb} not being renamed'),
                    context=self.source_context,
                )

            schema = self._propagate_ref_rename(schema, context, scls)

        return schema
Example #9
0
    def _classname_from_ast(cls, schema, astnode, context):
        referrer_ctx = cls.get_referrer_context(context)
        if referrer_ctx is not None:

            referrer_name = referrer_ctx.op.classname

            shortname = sn.Name(
                module='__',
                name=astnode.name.name,
            )

            name = sn.Name(
                module=referrer_name.module,
                name=sn.get_specialized_name(
                    shortname,
                    referrer_name,
                ),
            )
        else:
            name = super()._classname_from_ast(schema, astnode, context)

        shortname = sn.shortname_from_fullname(name)
        if len(shortname.name) > MAX_NAME_LENGTH:
            raise errors.SchemaDefinitionError(
                f'link or property name length exceeds the maximum of '
                f'{MAX_NAME_LENGTH} characters',
                context=astnode.context)
        return name
Example #10
0
def merge_cardinality(
    target: Pointer,
    sources: List[Pointer],
    field_name: str,
    *,
    ignore_local: bool,
    schema: s_schema.Schema,
) -> Any:
    current = None
    current_from = None

    if ignore_local:
        pointers = list(sources)
    else:
        pointers = [target] + list(sources)

    for source in pointers:
        nextval = source.get_explicit_field_value(schema, field_name, None)
        if nextval is not None:
            if current is None:
                current = nextval
                current_from = source
            elif current is not nextval:
                tgt_repr = target.get_verbosename(schema, with_parent=True)
                cf_repr = current_from.get_verbosename(schema,
                                                       with_parent=True)
                other_repr = source.get_verbosename(schema, with_parent=True)

                raise errors.SchemaDefinitionError(
                    f'cannot redefine the target cardinality of '
                    f'{tgt_repr}: it is defined '
                    f'as {current.as_ptr_qual()!r} in {cf_repr} and '
                    f'as {nextval.as_ptr_qual()!r} in {other_repr}.')

    return current
Example #11
0
def init_stmt(
        irstmt: irast.Stmt, qlstmt: qlast.Statement, *,
        ctx: context.ContextLevel, parent_ctx: context.ContextLevel) -> None:

    if isinstance(irstmt, irast.MutatingStmt):
        # This is some kind of mutation, so we need to check if it is
        # allowed.
        if ctx.env.options.in_ddl_context_name is not None:
            raise errors.SchemaDefinitionError(
                f'invalid mutation in {ctx.env.options.in_ddl_context_name}',
                context=qlstmt.context,
            )
        elif ((dv := ctx.defining_view) is not None and
                dv.get_expr_type(ctx.env.schema) is s_types.ExprType.Select and
                not ctx.env.options.allow_top_level_shape_dml):
            # This is some shape in a regular query. Although
            # DML is not allowed in the computable, but it may
            # be possible to refactor it.
            raise errors.QueryError(
                f'invalid mutation in a shape computable',
                hint=(
                    f'To resolve this try to factor out the mutation '
                    f'expression into the top-level WITH block.'
                ),
                context=qlstmt.context,
            )
Example #12
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        from edb.lang.edgeql import compiler as qlcompiler

        propname = astnode.name.name

        parent_ctx = context.get(CommandContextToken)
        parent_op = parent_ctx.op
        field = parent_op.get_schema_metaclass().get_field(propname)
        if field is None:
            raise errors.SchemaDefinitionError(
                f'{propname!r} is not a valid field',
                context=astnode.context)

        if not (isinstance(astnode, qlast.SetInternalField)
                or field.allow_ddl_set
                or context.stdmode
                or context.testmode):
            raise errors.SchemaDefinitionError(
                f'{propname!r} is not a valid field',
                context=astnode.context)

        if astnode.as_expr:
            new_value = s_expr.ExpressionText(
                edgeql.generate_source(astnode.value, pretty=False))
        else:
            if isinstance(astnode.value, qlast.BaseConstant):
                new_value = qlcompiler.evaluate_ast_to_python_val(
                    astnode.value, schema=schema)

            elif isinstance(astnode.value, qlast.Tuple):
                new_value = tuple(
                    qlcompiler.evaluate_ast_to_python_val(
                        el.value, schema=schema)
                    for el in astnode.value.elements
                )

            elif isinstance(astnode.value, qlast.ObjectRef):

                new_value = utils.ast_objref_to_objref(
                    astnode.value, modaliases=context.modaliases,
                    schema=schema)

            else:
                raise ValueError(
                    f'unexpected value in attribute: {astnode.value!r}')

        return cls(property=propname, new_value=new_value)
Example #13
0
    def _alter_begin(self, schema, context):
        schema = super()._alter_begin(schema, context)
        scls = self.scls

        context.altered_targets.add(scls)

        # Type alters of pointers used in expressions is prohibited.
        # Eventually we may be able to relax this by allowing to
        # alter to the type that is compatible (i.e. does not change)
        # with all expressions it is used in.
        vn = scls.get_verbosename(schema)
        self._prohibit_if_expr_refs(schema,
                                    context,
                                    action=f'alter the type of {vn}')

        if not context.canonical:
            implicit_bases = scls.get_implicit_bases(schema)
            non_altered_bases = []

            tgt = scls.get_target(schema)
            for base in set(implicit_bases) - context.altered_targets:
                base_tgt = base.get_target(schema)
                if not tgt.issubclass(schema, base_tgt):
                    non_altered_bases.append(base)

            # This pointer is inherited from one or more ancestors that
            # are not altered in the same op, and this is an error.
            if non_altered_bases:
                bases_str = ', '.join(
                    b.get_verbosename(schema, with_parent=True)
                    for b in non_altered_bases)

                vn = scls.get_verbosename(schema)

                raise errors.SchemaDefinitionError(
                    f'cannot change the target type of inherited {vn}',
                    details=(f'{vn} is inherited from '
                             f'{bases_str}'),
                    context=self.source_context,
                )

            if context.enable_recursion:
                tgt = self.get_attribute_value('target')

                def _set_type(alter_cmd, refname):
                    s_t = type(self)(classname=alter_cmd.classname, )
                    s_t.set_attribute_value('target', tgt)
                    alter_cmd.add(s_t)

                schema = self._propagate_ref_op(schema,
                                                context,
                                                self.scls,
                                                cb=_set_type)

        else:
            for op in self.get_subcommands(type=sd.ObjectCommand):
                schema = op.apply(schema, context)

        return schema
Example #14
0
    def _check_expr(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> s_schema.Schema:
        from edb.ir import ast as irast

        expression = self.get_attribute_value('expr')
        assert isinstance(expression, s_expr.Expression)
        assert isinstance(expression.irast, irast.Statement)

        required, card = expression.irast.cardinality.to_schema_value()

        spec_required: Optional[bool] = (self.get_specified_attribute_value(
            'required', schema, context))
        spec_card: Optional[qltypes.SchemaCardinality] = (
            self.get_specified_attribute_value('cardinality', schema, context))

        glob_name = self.get_verbosename()

        if spec_required and not required:
            srcctx = self.get_attribute_source_context('target')
            raise errors.SchemaDefinitionError(
                f'possibly an empty set returned by an '
                f'expression for the computed '
                f'{glob_name} '
                f"explicitly declared as 'required'",
                context=srcctx)

        if (spec_card is qltypes.SchemaCardinality.One
                and card is not qltypes.SchemaCardinality.One):
            srcctx = self.get_attribute_source_context('target')
            raise errors.SchemaDefinitionError(
                f'possibly more than one element returned by an '
                f'expression for the computed '
                f'{glob_name} '
                f"explicitly declared as 'single'",
                context=srcctx)

        if spec_card is None:
            self.set_attribute_value('cardinality', card, computed=True)

        if spec_required is None:
            self.set_attribute_value('required', required, computed=True)

        return schema
Example #15
0
 def _classname_from_ast(cls, schema, astnode, context):
     name = super()._classname_from_ast(schema, astnode, context)
     shortname = sn.shortname_from_fullname(name)
     if len(shortname.name) > MAX_NAME_LENGTH:
         raise errors.SchemaDefinitionError(
             f'link or property name length exceeds the maximum of '
             f'{MAX_NAME_LENGTH} characters',
             context=astnode.context)
     return name
Example #16
0
def compile_GlobalExpr(
        expr: qlast.GlobalExpr, *, ctx: context.ContextLevel) -> irast.Set:
    glob = ctx.env.get_track_schema_object(
        s_utils.ast_ref_to_name(expr.name), expr.name,
        modaliases=ctx.modaliases, type=s_globals.Global)
    assert isinstance(glob, s_globals.Global)

    if glob.is_computable(ctx.env.schema):
        obj_ref = s_utils.name_to_ast_ref(
            glob.get_target(ctx.env.schema).get_name(ctx.env.schema))
        return dispatch.compile(qlast.Path(steps=[obj_ref]), ctx=ctx)

    objctx = ctx.env.options.schema_object_context
    if objctx in (s_constr.Constraint, s_indexes.Index):
        typname = objctx.get_schema_class_displayname()
        raise errors.SchemaDefinitionError(
            f'global variables cannot be referenced from {typname}',
            context=expr.context)

    default = glob.get_default(ctx.env.schema)

    param_set: qlast.Expr | irast.Set
    present_set: qlast.Expr | irast.Set | None
    if ctx.env.options.func_params is None:
        param_set, present_set = setgen.get_global_param_sets(glob, ctx=ctx)
    else:
        param_set, present_set = setgen.get_func_global_param_sets(
            glob, ctx=ctx)

    if default and not present_set:
        # If we have a default value and the global is required,
        # then we can use the param being {} as a signal to use
        # the default.
        with ctx.new() as subctx:
            subctx.anchors = subctx.anchors.copy()
            main_param = subctx.maybe_create_anchor(param_set, 'glob')
            param_set = func.compile_operator(
                expr, op_name='std::??',
                qlargs=[main_param, default.qlast], ctx=subctx)
    elif default and present_set:
        # ... but if {} is a valid value for the global, we need to
        # stick in an extra parameter to indicate whether to use
        # the default.
        with ctx.new() as subctx:
            subctx.anchors = subctx.anchors.copy()
            main_param = subctx.maybe_create_anchor(param_set, 'glob')

            present_param = subctx.maybe_create_anchor(present_set, 'present')

            param_set = func.compile_operator(
                expr, op_name='std::IF',
                qlargs=[main_param, present_param, default.qlast], ctx=subctx)
    elif not isinstance(param_set, irast.Set):
        param_set = dispatch.compile(param_set, ctx=ctx)

    return param_set
Example #17
0
    def _validate_pointer_def(self, schema, context):
        """Check that pointer definition is sound."""

        referrer_ctx = self.get_referrer_context(context)
        if referrer_ctx is None:
            return

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

        default_expr = scls.get_default(schema)

        if default_expr is not None:
            if default_expr.irast is None:
                default_expr = default_expr.compiled(default_expr, schema)
            default_type = default_expr.irast.stype
            ptr_target = scls.get_target(schema)
            set_cmd = self.get_attribute_set_cmd('default')
            if set_cmd:
                source_context = set_cmd.source_context
            else:
                source_context = None

            if not default_type.assignment_castable_to(ptr_target, schema):
                raise errors.SchemaDefinitionError(
                    f'default expression is of invalid type: '
                    f'{default_type.get_displayname(schema)}, '
                    f'expected {ptr_target.get_displayname(schema)}',
                    context=source_context,
                )

            ptr_cardinality = scls.get_cardinality(schema)
            default_cardinality = default_expr.irast.cardinality
            if (ptr_cardinality is qltypes.Cardinality.ONE
                    and default_cardinality is qltypes.Cardinality.MANY):
                raise errors.SchemaDefinitionError(
                    f'possibly more than one element returned by '
                    f'the default expression for '
                    f'{scls.get_verbosename(schema)} declared as '
                    f'\'single\'',
                    context=source_context,
                )
Example #18
0
    def _classname_from_ast(cls, schema, astnode, context):
        nqname = cls._get_ast_name(schema, astnode, context)
        module = context.modaliases.get(astnode.name.module,
                                        astnode.name.module)
        if module is None:
            raise errors.SchemaDefinitionError(
                f'unqualified name and no default module set',
                context=astnode.name.context)

        return sn.Name(module=module, name=nqname)
Example #19
0
 def _cmd_tree_from_ast(cls, schema, astnode, context):
     cmd = super()._cmd_tree_from_ast(schema, astnode, context)
     if isinstance(astnode, qlast.CreateConcreteLink):
         cmd._process_create_or_alter_ast(schema, astnode, context)
     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 abstract link",
                 context=astnode.context)
     return cmd
Example #20
0
    def _validate(self, schema: s_schema.Schema,
                  context: sd.CommandContext) -> None:
        scls = self.scls
        implicit_bases = [
            b for b in scls.get_bases(schema).objects(schema)
            if not b.generic(schema)
        ]

        referrer_ctx = self.get_referrer_context_or_die(context)
        objcls = self.get_schema_metaclass()
        referrer_class = referrer_ctx.op.get_schema_metaclass()
        refdict = referrer_class.get_refdict_for_class(objcls)

        if context.declarative and scls.get_is_owned(schema):
            if (implicit_bases and refdict.requires_explicit_overloaded
                    and not self.get_attribute_value('declared_overloaded')):

                ancestry = []

                for obj in implicit_bases:
                    bref = obj.get_referrer(schema)
                    assert bref is not None
                    ancestry.append(bref)

                raise errors.SchemaDefinitionError(
                    f'{self.scls.get_verbosename(schema, with_parent=True)} '
                    f'must be declared using the `overloaded` keyword because '
                    f'it is defined in the following ancestor(s): '
                    f'{", ".join(a.get_shortname(schema) for a in ancestry)}',
                    context=self.source_context,
                )
            elif (not implicit_bases
                  and self.get_attribute_value('declared_overloaded')):

                raise errors.SchemaDefinitionError(
                    f'{self.scls.get_verbosename(schema, with_parent=True)}: '
                    f'cannot be declared `overloaded` as there are no '
                    f'ancestors defining it.',
                    context=self.source_context,
                )
Example #21
0
    def _parse_attr_setters(
            self, scls, attrdecls: typing.List[qlast.Annotation]):
        for attrdecl in attrdecls:
            attr = self._get_ref_obj(attrdecl.name, s_anno.Annotation)
            value = qlcompiler.evaluate_ast_to_python_val(
                attrdecl.value, self._schema, modaliases=self._mod_aliases)

            if not isinstance(value, str):
                raise errors.SchemaDefinitionError(
                    'annotation value is not a string',
                    context=attrdecl.value.context)

            self._schema = scls.set_annotation(self._schema, attr, value)
Example #22
0
 def _validate_name(
     self,
     schema: s_schema.Schema,
     context: sd.CommandContext,
 ) -> None:
     name = self.get_attribute_value('name')
     if len(str(name)) > s_def.MAX_NAME_LENGTH:
         source_context = self.get_attribute_source_context('name')
         raise errors.SchemaDefinitionError(
             f'Role names longer than {s_def.MAX_NAME_LENGTH} '
             f'characters are not supported',
             context=source_context,
         )
Example #23
0
    def _validate_legal_command(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        super()._validate_legal_command(schema, context)

        if (not context.stdmode and not context.testmode
                and (modname := self.classname) in s_schema.STD_MODULES):
            raise errors.SchemaDefinitionError(
                f'cannot {self._delta_action} {self.get_verbosename()}: '
                f'module {modname} is read-only',
                context=self.source_context)
Example #24
0
    def compile_expr_field(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
        field: so.Field[Any],
        value: s_expr.Expression,
        track_schema_ref_exprs: bool = False,
    ) -> s_expr.Expression:
        singletons: List[s_types.Type]
        if field.name == 'expr':
            # type ignore below, for the class is used as mixin
            parent_ctx = context.get_ancestor(
                IndexSourceCommandContext,  # type: ignore
                self)
            assert parent_ctx is not None
            assert isinstance(parent_ctx.op, sd.ObjectCommand)
            subject = parent_ctx.op.get_object(schema, context)

            expr = type(value).compiled(
                value,
                schema=schema,
                options=qlcompiler.CompilerOptions(
                    modaliases=context.modaliases,
                    schema_object_context=self.get_schema_metaclass(),
                    anchors={qlast.Subject().name: subject},
                    path_prefix_anchor=qlast.Subject().name,
                    singletons=frozenset([subject]),
                    apply_query_rewrites=not context.stdmode,
                    track_schema_ref_exprs=track_schema_ref_exprs,
                ),
            )

            # Check that the inferred cardinality is no more than 1
            from edb.ir import ast as ir_ast
            assert isinstance(expr.irast, ir_ast.Statement)
            if expr.irast.cardinality.is_multi():
                raise errors.ResultCardinalityMismatchError(
                    f'possibly more than one element returned by '
                    f'the index expression where only singletons '
                    f'are allowed')

            if expr.irast.volatility != qltypes.Volatility.Immutable:
                raise errors.SchemaDefinitionError(
                    f'index expressions must be immutable',
                    context=value.qlast.context,
                )

            return expr
        else:
            return super().compile_expr_field(schema, context, field, value,
                                              track_schema_ref_exprs)
Example #25
0
def merge_readonly(
    target: Pointer,
    sources: List[Pointer],
    field_name: str,
    *,
    ignore_local: bool,
    schema: s_schema.Schema,
) -> Any:

    current = None
    current_from = None

    # The target field value is only relevant if it is explicit,
    # otherwise it should be based on the inherited value.
    if not ignore_local:
        current = target.get_explicit_field_value(schema, field_name, None)
        if current is not None:
            current_from = target

    for source in list(sources):
        # ignore abstract pointers
        if source.generic(schema):
            continue

        # We want the field value including the default, not just
        # explicit value.
        nextval = source.get_field_value(schema, field_name)
        if nextval is not None:
            if current is None:
                current = nextval
                current_from = source
            elif current is not nextval:
                assert current_from is not None

                tgt_repr = target.get_verbosename(
                    schema, with_parent=True)
                cf_repr = current_from.get_verbosename(
                    schema, with_parent=True)
                other_repr = source.get_verbosename(
                    schema, with_parent=True)

                raise errors.SchemaDefinitionError(
                    f'cannot redefine the readonly flag of '
                    f'{tgt_repr}: it is defined '
                    f'as {current} in {cf_repr} and '
                    f'as {nextval} in {other_repr}.'
                )

    return current
Example #26
0
    def _parse_computable(self, expr, schema, context) -> so.ObjectRef:
        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
            )

        expr = s_expr.Expression.compiled(
            s_expr.Expression.from_ast(expr, schema, context.modaliases),
            schema=schema,
            modaliases=context.modaliases,
            anchors={qlast.Source: source},
            path_prefix_anchor=qlast.Source,
            singletons=[source],
        )

        target = utils.reduce_to_typeref(schema, expr.irast.stype)

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

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

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

        return target
Example #27
0
    def get_struct_properties(self, schema):
        result = {}
        metaclass = self.get_schema_metaclass()

        for op in self.get_subcommands(type=AlterObjectProperty):
            field = metaclass.get_field(op.property)
            if field is None:
                raise errors.SchemaDefinitionError(
                    f'got AlterObjectProperty command for '
                    f'invalid field: {metaclass.__name__}.{op.property}')

            result[op.property] = self._resolve_attr_value(
                op.new_value, op.property, field, schema)

        return result
Example #28
0
 def _cmd_tree_from_ast(
     cls,
     schema: s_schema.Schema,
     astnode: qlast.DDLOperation,
     context: sd.CommandContext,
 ) -> sd.Command:
     cmd = super()._cmd_tree_from_ast(schema, astnode, context)
     if isinstance(astnode, qlast.CreateConcreteLink):
         assert isinstance(cmd, pointers.PointerCommand)
         cmd._process_create_or_alter_ast(schema, astnode, context)
     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 abstract link",
                 context=astnode.context)
     assert isinstance(cmd, sd.Command)
     return cmd
Example #29
0
    def _create_begin(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> s_schema.Schema:
        schema = super()._create_begin(schema, context)

        # Validate that the database name is fewer than 64 characters
        name = self.get_attribute_value('name')
        if len(str(name)) > s_def.MAX_NAME_LENGTH:
            source_context = self.get_attribute_source_context('name')
            raise errors.SchemaDefinitionError(
                f'Database names longer than {s_def.MAX_NAME_LENGTH} '
                f'characters are not supported',
                context=source_context,
            )

        return schema
Example #30
0
    def _validate_legal_command(self, schema, context):
        from . import functions as s_functions

        if (not context.stdmode and not context.testmode
                and not isinstance(self, s_functions.ParameterCommand)):

            if isinstance(self.classname, sn.Name):
                shortname = sn.shortname_from_fullname(self.classname)
                modname = self.classname.module
            else:
                # modules have classname as simple strings
                shortname = modname = self.classname

            if modname in s_schema.STD_MODULES:
                raise errors.SchemaDefinitionError(
                    f'cannot {self._delta_action} `{shortname}`: '
                    f'module {modname} is read-only',
                    context=self.source_context)