Exemplo n.º 1
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        if isinstance(astnode, qlast.CreateConcreteConstraint):
            if astnode.delegated:
                cmd.set_attribute_value('delegated', astnode.delegated)

            args = cls._constraint_args_from_ast(schema, astnode, context)
            if args:
                cmd.add(sd.AlterObjectProperty(property='args',
                                               new_value=args))

        elif isinstance(astnode, qlast.CreateConstraint):
            params = cls._get_param_desc_from_ast(schema, context.modaliases,
                                                  astnode)

            for param in params:
                if param.get_kind(schema) is ft.ParameterKind.NAMED_ONLY:
                    raise errors.InvalidConstraintDefinitionError(
                        'named only parameters are not allowed '
                        'in this context',
                        context=astnode.context)

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

        if cmd.get_attribute_value('return_type') is None:
            cmd.add(
                sd.AlterObjectProperty(property='return_type',
                                       new_value=utils.reduce_to_typeref(
                                           schema, schema.get('std::bool'))))

        if cmd.get_attribute_value('return_typemod') is None:
            cmd.add(
                sd.AlterObjectProperty(
                    property='return_typemod',
                    new_value=ft.TypeModifier.SINGLETON,
                ))

        # 'subjectexpr' can be present in either astnode type
        if astnode.subjectexpr:
            orig_text = cls.get_orig_expr_text(schema, astnode, 'subjectexpr')

            subjectexpr = s_expr.Expression.from_ast(
                astnode.subjectexpr,
                schema,
                context.modaliases,
                orig_text=orig_text,
            )

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

        cls._validate_subcommands(astnode)

        return cmd
Exemplo n.º 2
0
    def validate_alter(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        super().validate_alter(schema, context)

        self_delegated = self.get_attribute_value('delegated')
        if not self_delegated:
            return

        concrete_bases = [
            b for b in self.scls.get_bases(schema).objects(schema)
            if not b.generic(schema) and not b.get_delegated(schema)
        ]
        if concrete_bases:
            tgt_repr = self.scls.get_verbosename(schema, with_parent=True)
            bases_repr = ', '.join(
                b.get_subject(schema).get_verbosename(schema, with_parent=True)
                for b in concrete_bases)
            raise errors.InvalidConstraintDefinitionError(
                f'cannot redefine {tgt_repr} as delegated:'
                f' it is defined as non-delegated in {bases_repr}',
                context=self.source_context,
            )
Exemplo n.º 3
0
    def normalize_constraint_expr(cls,
                                  schema,
                                  module_aliases,
                                  expr,
                                  *,
                                  subject=None,
                                  constraint_name,
                                  expr_context=None,
                                  enforce_boolean=False):

        if subject is None:
            subject = cls._dummy_subject(schema)

        edgeql_tree, ir_result = cls._normalize_constraint_expr(
            schema, module_aliases, expr, subject)

        if enforce_boolean:
            bool_t = schema.get('std::bool')
            expr_type = ir_result.stype
            if not expr_type.issubclass(schema, bool_t):
                raise errors.InvalidConstraintDefinitionError(
                    f'{constraint_name} constraint expression expected '
                    f'to return a bool value, got '
                    f'{expr_type.get_name(schema).name!r}',
                    context=expr_context)

        expr = edgeql.generate_source(edgeql_tree, pretty=False)
        return expr
Exemplo n.º 4
0
 def _validate_subcommands(cls, astnode):
     # check that 'subject' and 'subjectexpr' are not set as attributes
     for command in astnode.commands:
         if cls._is_special_name(command.name):
             raise errors.InvalidConstraintDefinitionError(
                 f'{command.name.name} is not a valid constraint attribute',
                 context=command.context)
Exemplo n.º 5
0
 def _validate_subcommands(cls, astnode):
     # check that 'subject' and 'subjectexpr' are not set as annotations
     for command in astnode.commands:
         cname = command.name
         if cname in {'subject', 'subjectexpr'}:
             raise errors.InvalidConstraintDefinitionError(
                 f'{cname} is not a valid constraint annotation',
                 context=command.context)
Exemplo n.º 6
0
    def _init_constraints(self, constraints):
        for constraint, decl in constraints.items():
            attrs = {a.name.name: a.value for a in decl.fields}
            assert 'subject' not in attrs  # TODO: Add proper validation
            assert 'subjectexpr' not in attrs  # TODO: Add proper validation

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

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

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

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

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

            self._schema = constraint.set_field_value(
                self._schema, 'params', params)
Exemplo n.º 7
0
 def _validate_subcommands(
     cls,
     astnode: qlast.DDLOperation,
 ) -> None:
     # check that 'subject' and 'subjectexpr' are not set as annotations
     for command in astnode.commands:
         assert isinstance(command, (qlast.NamedDDL, qlast.BaseSetField))
         cname = command.name
         if cname in {'subject', 'subjectexpr'}:
             raise errors.InvalidConstraintDefinitionError(
                 f'{cname} is not a valid constraint annotation',
                 context=command.context)
Exemplo n.º 8
0
def compile_Parameter(expr: qlast.Base, *,
                      ctx: context.ContextLevel) -> irast.Set:

    if ctx.env.options.func_params is not None:
        if ctx.env.options.schema_object_context is s_constr.Constraint:
            raise errors.InvalidConstraintDefinitionError(
                f'dollar-prefixed "$parameters" cannot be used here',
                context=expr.context)
        else:
            raise errors.InvalidFunctionDefinitionError(
                f'dollar-prefixed "$parameters" cannot be used here',
                context=expr.context)

    raise errors.QueryError(f'missing a type cast before the parameter',
                            context=expr.context)
Exemplo n.º 9
0
 def visit_Parameter(self, node):
     raise errors.InvalidConstraintDefinitionError(
         f'dollar-prefixed "$parameters" are not supported in constraints')
Exemplo n.º 10
0
    def get_concrete_constraint_attrs(
            cls, schema, subject, *, name, subjectexpr=None,
            sourcectx=None, args=[], modaliases=None, **kwargs):
        from edb.edgeql import parser as qlparser
        from edb.edgeql import utils as qlutils

        constr_base = schema.get(name, module_aliases=modaliases)
        module_aliases = {}

        orig_subjectexpr = subjectexpr
        orig_subject = subject
        base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr')
        if subjectexpr is None:
            subjectexpr = base_subjectexpr
        elif (base_subjectexpr is not None
                and subjectexpr.text != base_subjectexpr.text):
            raise errors.InvalidConstraintDefinitionError(
                'subjectexpr is already defined for ' +
                f'{str(name)!r}')

        if subjectexpr is not None:
            subject_ql = subjectexpr.qlast
            if subject_ql is None:
                subject_ql = qlparser.parse(subjectexpr.text, module_aliases)

            subject = subject_ql

        expr: s_expr.Expression = constr_base.get_field_value(schema, 'expr')
        if not expr:
            raise errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {name!r}')

        expr_ql = qlparser.parse(expr.text, module_aliases)

        if not args:
            args = constr_base.get_field_value(schema, 'args')

        attrs = dict(kwargs)
        inherited = dict()
        if orig_subjectexpr is not None:
            attrs['subjectexpr'] = orig_subjectexpr
        else:
            base_subjectexpr = constr_base.get_subjectexpr(schema)
            if base_subjectexpr is not None:
                attrs['subjectexpr'] = base_subjectexpr

        errmessage = attrs.get('errmessage')
        if not errmessage:
            errmessage = constr_base.get_errmessage(schema)
            inherited['errmessage'] = True

        attrs['errmessage'] = errmessage

        if subject is not orig_subject:
            # subject has been redefined
            qlutils.inline_anchors(expr_ql, anchors={qlast.Subject: subject})
            subject = orig_subject

        args_map = None
        if args:
            args_ql = [
                qlast.Path(steps=[qlast.Subject()]),
            ]

            args_ql.extend(
                qlparser.parse(arg.text, module_aliases) for arg in args
            )

            args_map = qlutils.index_parameters(
                args_ql,
                parameters=constr_base.get_params(schema),
                schema=schema)

            qlutils.inline_parameters(expr_ql, args_map)

            args_map = {name: edgeql.generate_source(val, pretty=False)
                        for name, val in args_map.items()}

            args_map['__subject__'] = '{__subject__}'
            attrs['errmessage'] = attrs['errmessage'].format(**args_map)
            inherited.pop('errmessage', None)

        attrs['args'] = args

        if expr == '__subject__':
            expr_context = sourcectx
        else:
            expr_context = None

        final_expr = s_expr.Expression.compiled(
            s_expr.Expression.from_ast(expr_ql, schema, module_aliases),
            schema=schema,
            modaliases=module_aliases,
            anchors={qlast.Subject: subject},
        )

        bool_t = schema.get('std::bool')
        expr_type = final_expr.irast.stype
        if not expr_type.issubclass(schema, bool_t):
            raise errors.InvalidConstraintDefinitionError(
                f'{name} constraint expression expected '
                f'to return a bool value, got '
                f'{expr_type.get_name(schema).name!r}',
                context=expr_context
            )

        attrs['return_type'] = constr_base.get_return_type(schema)
        attrs['return_typemod'] = constr_base.get_return_typemod(schema)
        attrs['finalexpr'] = final_expr
        attrs['params'] = constr_base.get_params(schema)

        return constr_base, attrs, inherited
Exemplo n.º 11
0
    def _populate_concrete_constraint_attrs(
            self,
            schema: s_schema.Schema,
            context: sd.CommandContext,
            subject_obj: Optional[so.Object],
            *,
            name: sn.QualName,
            subjectexpr: Optional[s_expr.Expression] = None,
            sourcectx: Optional[c_parsing.ParserContext] = None,
            args: Any = None,
            **kwargs: Any) -> None:
        from edb.ir import ast as ir_ast
        from edb.ir import utils as ir_utils

        constr_base = schema.get(name, type=Constraint)

        orig_subjectexpr = subjectexpr
        orig_subject = subject_obj
        base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr')
        if subjectexpr is None:
            subjectexpr = base_subjectexpr
        elif (base_subjectexpr is not None
              and subjectexpr.text != base_subjectexpr.text):
            raise errors.InvalidConstraintDefinitionError(
                f'subjectexpr is already defined for {name}')

        if (isinstance(subject_obj, s_scalars.ScalarType)
                and constr_base.get_is_aggregate(schema)):
            raise errors.InvalidConstraintDefinitionError(
                f'{constr_base.get_verbosename(schema)} may not '
                f'be used on scalar types')

        if subjectexpr is not None:
            subject_ql = subjectexpr.qlast
            subject = subject_ql
        else:
            subject = subject_obj

        expr: s_expr.Expression = constr_base.get_field_value(schema, 'expr')
        if not expr:
            raise errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {name}')

        # Re-parse instead of using expr.qlast, because we mutate
        # the AST below.
        expr_ql = qlparser.parse(expr.text)

        if not args:
            args = constr_base.get_field_value(schema, 'args')

        attrs = dict(kwargs)
        inherited = dict()
        if orig_subjectexpr is not None:
            attrs['subjectexpr'] = orig_subjectexpr
        else:
            base_subjectexpr = constr_base.get_subjectexpr(schema)
            if base_subjectexpr is not None:
                attrs['subjectexpr'] = base_subjectexpr
                inherited['subjectexpr'] = True

        errmessage = attrs.get('errmessage')
        if not errmessage:
            errmessage = constr_base.get_errmessage(schema)
            inherited['errmessage'] = True

        attrs['errmessage'] = errmessage

        if subject is not orig_subject:
            # subject has been redefined
            assert isinstance(subject, qlast.Base)
            qlutils.inline_anchors(expr_ql,
                                   anchors={qlast.Subject().name: subject})
            subject = orig_subject

        if args:
            args_ql: List[qlast.Base] = [
                qlast.Path(steps=[qlast.Subject()]),
            ]
            args_ql.extend(arg.qlast for arg in args)
            args_map = qlutils.index_parameters(
                args_ql,
                parameters=constr_base.get_params(schema),
                schema=schema,
            )
            qlutils.inline_parameters(expr_ql, args_map)

        attrs['args'] = args

        assert subject is not None
        path_prefix_anchor = (qlast.Subject().name if isinstance(
            subject, s_types.Type) else None)

        final_expr = s_expr.Expression.compiled(
            s_expr.Expression.from_ast(expr_ql, schema, {}),
            schema=schema,
            options=qlcompiler.CompilerOptions(
                anchors={qlast.Subject().name: subject},
                path_prefix_anchor=path_prefix_anchor,
                apply_query_rewrites=not context.stdmode,
            ),
        )

        bool_t = schema.get('std::bool', type=s_scalars.ScalarType)
        assert isinstance(final_expr.irast, ir_ast.Statement)

        expr_type = final_expr.irast.stype
        if not expr_type.issubclass(schema, bool_t):
            raise errors.InvalidConstraintDefinitionError(
                f'{name} constraint expression expected '
                f'to return a bool value, got '
                f'{expr_type.get_verbosename(schema)}',
                context=sourcectx)

        if subjectexpr is not None:
            if (isinstance(subject_obj, s_types.Type)
                    and subject_obj.is_object_type()):
                singletons = frozenset({subject_obj})
            else:
                singletons = frozenset()

            final_subjectexpr = s_expr.Expression.compiled(
                subjectexpr,
                schema=schema,
                options=qlcompiler.CompilerOptions(
                    anchors={qlast.Subject().name: subject},
                    path_prefix_anchor=path_prefix_anchor,
                    singletons=singletons,
                    apply_query_rewrites=not context.stdmode,
                ),
            )
            assert isinstance(final_subjectexpr.irast, ir_ast.Statement)

            refs = ir_utils.get_longest_paths(final_expr.irast)
            has_multi = False
            for ref in refs:
                while ref.rptr:
                    rptr = ref.rptr
                    if rptr.ptrref.dir_cardinality.is_multi():
                        has_multi = True
                    if (not isinstance(rptr.ptrref,
                                       ir_ast.TupleIndirectionPointerRef)
                            and rptr.ptrref.source_ptr is None
                            and rptr.source.rptr is not None):
                        raise errors.InvalidConstraintDefinitionError(
                            "constraints cannot contain paths with more "
                            "than one hop",
                            context=sourcectx)

                    ref = rptr.source

            if has_multi and len(refs) > 1:
                raise errors.InvalidConstraintDefinitionError(
                    "cannot reference multiple links or properties in a "
                    "constraint where at least one link or property is MULTI",
                    context=sourcectx)

            if has_multi and ir_utils.contains_set_of_op(
                    final_subjectexpr.irast):
                raise errors.InvalidConstraintDefinitionError(
                    "cannot use aggregate functions or operators "
                    "in a non-aggregating constraint",
                    context=sourcectx)

        attrs['return_type'] = constr_base.get_return_type(schema)
        attrs['return_typemod'] = constr_base.get_return_typemod(schema)
        attrs['finalexpr'] = final_expr
        attrs['params'] = constr_base.get_params(schema)
        attrs['abstract'] = False

        for k, v in attrs.items():
            self.set_attribute_value(k, v, inherited=bool(inherited.get(k)))
Exemplo n.º 12
0
    def _init_constraints(self, constraints):
        for constraint, decl in constraints.items():
            attrs = {a.name.name: a.value for a in decl.fields}
            assert 'subject' not in attrs  # TODO: Add proper validation
            assert 'subjectexpr' not in attrs  # TODO: Add proper validation

            self._schema, params = s_func.FuncParameterList.from_ast(
                self._schema,
                decl,
                self._mod_aliases,
                func_fqname=constraint.get_name(self._schema),
                prepend=[
                    qlast.FuncParam(
                        name='__subject__',
                        type=qlast.TypeName(maintype=qlast.AnyType(), ),
                        typemod=qltypes.TypeModifier.SINGLETON,
                        kind=qltypes.ParameterKind.POSITIONAL,
                        default=None,
                    ),
                ],
            )

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

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

            anchors, _ = qlcompiler.get_param_anchors_for_callable(
                params, self._schema)

            expr = attrs.pop('expr', None)
            if expr is not None:
                self._schema = constraint.set_field_value(
                    self._schema,
                    'expr',
                    s_expr.Expression.compiled(
                        s_expr.Expression.from_ast(expr, self._schema,
                                                   self._mod_aliases),
                        schema=self._schema,
                        modaliases=self._mod_aliases,
                        anchors=anchors,
                        func_params=params,
                        allow_generic_type_output=True,
                        parent_object_type=type(constraint),
                    ),
                )

            subjexpr = decl.subject
            if subjexpr is not None:
                self._schema = constraint.set_field_value(
                    self._schema,
                    'subjectexpr',
                    s_expr.Expression.compiled(
                        s_expr.Expression.from_ast(subjexpr, self._schema,
                                                   self._mod_aliases),
                        schema=self._schema,
                        modaliases=self._mod_aliases,
                        anchors=anchors,
                        func_params=params,
                        allow_generic_type_output=True,
                        parent_object_type=type(constraint),
                    ),
                )

            self._schema = constraint.set_field_value(self._schema, 'params',
                                                      params)
Exemplo n.º 13
0
    def _populate_concrete_constraint_attrs(
            self,
            schema: s_schema.Schema,
            subject_obj: Optional[so.Object],
            *,
            name: str,
            subjectexpr: Optional[s_expr.Expression] = None,
            sourcectx: Optional[c_parsing.ParserContext] = None,
            args: Any = None,
            **kwargs: Any) -> None:
        from edb.ir import ast as ir_ast
        from edb.ir import utils as ir_utils

        constr_base = schema.get(name, type=Constraint)

        orig_subjectexpr = subjectexpr
        orig_subject = subject_obj
        base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr')
        if subjectexpr is None:
            subjectexpr = base_subjectexpr
        elif (base_subjectexpr is not None
              and subjectexpr.text != base_subjectexpr.text):
            raise errors.InvalidConstraintDefinitionError(
                f'subjectexpr is already defined for {name!r}')

        if (isinstance(subject_obj, s_scalars.ScalarType)
                and constr_base.get_is_aggregate(schema)):
            raise errors.InvalidConstraintDefinitionError(
                f'{constr_base.get_verbosename(schema)} may not '
                f'be used on scalar types')

        if subjectexpr is not None:
            subject_ql = subjectexpr.qlast
            subject = subject_ql
        else:
            subject = subject_obj

        expr: s_expr.Expression = constr_base.get_field_value(schema, 'expr')
        if not expr:
            raise errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {name!r}')

        # Re-parse instead of using expr.qlast, because we mutate
        # the AST below.
        expr_ql = qlparser.parse(expr.text)

        if not args:
            args = constr_base.get_field_value(schema, 'args')

        attrs = dict(kwargs)
        inherited = dict()
        if orig_subjectexpr is not None:
            attrs['subjectexpr'] = orig_subjectexpr
        else:
            base_subjectexpr = constr_base.get_subjectexpr(schema)
            if base_subjectexpr is not None:
                attrs['subjectexpr'] = base_subjectexpr
                inherited['subjectexpr'] = True

        errmessage = attrs.get('errmessage')
        if not errmessage:
            errmessage = constr_base.get_errmessage(schema)
            inherited['errmessage'] = True

        attrs['errmessage'] = errmessage

        if subject is not orig_subject:
            # subject has been redefined
            assert isinstance(subject, qlast.Base)
            qlutils.inline_anchors(expr_ql,
                                   anchors={qlast.Subject().name: subject})
            subject = orig_subject

        if args:
            args_ql: List[qlast.Base] = [
                qlast.Path(steps=[qlast.Subject()]),
            ]
            args_ql.extend(arg.qlast for arg in args)
            args_map = qlutils.index_parameters(
                args_ql,
                parameters=constr_base.get_params(schema),
                schema=schema,
            )
            qlutils.inline_parameters(expr_ql, args_map)

        attrs['args'] = args

        if expr == '__subject__':
            expr_context = sourcectx
        else:
            expr_context = None

        assert subject is not None
        final_expr = s_expr.Expression.compiled(
            s_expr.Expression.from_ast(expr_ql, schema, {}),
            schema=schema,
            options=qlcompiler.CompilerOptions(
                anchors={qlast.Subject().name: subject}, ),
        )

        bool_t: s_scalars.ScalarType = schema.get('std::bool')
        assert isinstance(final_expr.irast, ir_ast.Statement)

        expr_type = final_expr.irast.stype
        if not expr_type.issubclass(schema, bool_t):
            raise errors.InvalidConstraintDefinitionError(
                f'{name} constraint expression expected '
                f'to return a bool value, got '
                f'{expr_type.get_verbosename(schema)}',
                context=expr_context)

        if (subjectexpr is not None and isinstance(subject_obj, s_types.Type)
                and subject_obj.is_object_type()):
            final_subjectexpr = s_expr.Expression.compiled(
                subjectexpr,
                schema=schema,
                options=qlcompiler.CompilerOptions(
                    anchors={qlast.Subject().name: subject},
                    singletons=frozenset({subject_obj}),
                ),
            )
            assert isinstance(final_subjectexpr.irast, ir_ast.Statement)

            if final_subjectexpr.irast.cardinality.is_multi():
                refs = ir_utils.get_longest_paths(final_expr.irast)
                if len(refs) > 1:
                    raise errors.InvalidConstraintDefinitionError(
                        "Constraint with multi cardinality may not "
                        "reference multiple links or properties",
                        context=expr_context)

        attrs['return_type'] = constr_base.get_return_type(schema)
        attrs['return_typemod'] = constr_base.get_return_typemod(schema)
        attrs['finalexpr'] = final_expr
        attrs['params'] = constr_base.get_params(schema)
        attrs['is_abstract'] = False

        for k, v in attrs.items():
            self.set_attribute_value(k, v, inherited=bool(inherited.get(k)))
Exemplo n.º 14
0
    def _populate_concrete_constraint_attrs(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
        subject_obj: Optional[so.Object],
        *,
        name: sn.QualName,
        subjectexpr: Optional[s_expr.Expression] = None,
        subjectexpr_inherited: bool = False,
        sourcectx: Optional[c_parsing.ParserContext] = None,
        args: Any = None,
        **kwargs: Any
    ) -> None:
        from edb.ir import ast as ir_ast
        from edb.ir import utils as ir_utils
        from . import pointers as s_pointers
        from . import links as s_links
        from . import scalars as s_scalars

        bases = self.get_resolved_attribute_value(
            'bases', schema=schema, context=context,
        )
        if not bases:
            bases = self.scls.get_bases(schema)
        constr_base = bases.objects(schema)[0]
        # If we have a concrete base, then we should inherit all of
        # these attrs through the normal inherit_fields() mechanisms,
        # and populating them ourselves will just mess up
        # inherited_fields.
        if not constr_base.generic(schema):
            return

        orig_subjectexpr = subjectexpr
        orig_subject = subject_obj
        base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr')
        if subjectexpr is None:
            subjectexpr = base_subjectexpr
        elif (base_subjectexpr is not None
                and subjectexpr.text != base_subjectexpr.text):
            raise errors.InvalidConstraintDefinitionError(
                f'subjectexpr is already defined for {name}'
            )

        if (isinstance(subject_obj, s_scalars.ScalarType)
                and constr_base.get_is_aggregate(schema)):
            raise errors.InvalidConstraintDefinitionError(
                f'{constr_base.get_verbosename(schema)} may not '
                f'be used on scalar types'
            )

        if subjectexpr is not None:
            subject_ql = subjectexpr.qlast
            subject = subject_ql
        else:
            subject = subject_obj

        expr: s_expr.Expression = constr_base.get_field_value(schema, 'expr')
        if not expr:
            raise errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {name}')

        # Re-parse instead of using expr.qlast, because we mutate
        # the AST below.
        expr_ql = qlparser.parse(expr.text)

        if not args:
            args = constr_base.get_field_value(schema, 'args')

        attrs = dict(kwargs)
        inherited = dict()
        if orig_subjectexpr is not None:
            attrs['subjectexpr'] = orig_subjectexpr
            inherited['subjectexpr'] = subjectexpr_inherited
        else:
            base_subjectexpr = constr_base.get_subjectexpr(schema)
            if base_subjectexpr is not None:
                attrs['subjectexpr'] = base_subjectexpr
                inherited['subjectexpr'] = True

        errmessage = attrs.get('errmessage')
        if not errmessage:
            errmessage = constr_base.get_errmessage(schema)
            inherited['errmessage'] = True

        attrs['errmessage'] = errmessage

        if subject is not orig_subject:
            # subject has been redefined
            assert isinstance(subject, qlast.Base)
            qlutils.inline_anchors(
                expr_ql, anchors={qlast.Subject().name: subject})
            subject = orig_subject

        if args:
            args_ql: List[qlast.Base] = [
                qlast.Path(steps=[qlast.Subject()]),
            ]
            args_ql.extend(arg.qlast for arg in args)
            args_map = qlutils.index_parameters(
                args_ql,
                parameters=constr_base.get_params(schema),
                schema=schema,
            )
            qlutils.inline_parameters(expr_ql, args_map)

        attrs['args'] = args

        assert subject is not None
        final_expr = s_expr.Expression.compiled(
            s_expr.Expression.from_ast(expr_ql, schema, {}),
            schema=schema,
            options=qlcompiler.CompilerOptions(
                anchors={qlast.Subject().name: subject},
                path_prefix_anchor=qlast.Subject().name,
                apply_query_rewrites=not context.stdmode,
            ),
        )

        bool_t = schema.get('std::bool', type=s_scalars.ScalarType)
        assert isinstance(final_expr.irast, ir_ast.Statement)

        expr_type = final_expr.irast.stype
        if not expr_type.issubclass(schema, bool_t):
            raise errors.InvalidConstraintDefinitionError(
                f'{name} constraint expression expected '
                f'to return a bool value, got '
                f'{expr_type.get_verbosename(schema)}',
                context=sourcectx
            )

        if subjectexpr is not None:
            assert isinstance(subject_obj, (s_types.Type, s_pointers.Pointer))
            singletons = frozenset({subject_obj})

            final_subjectexpr = s_expr.Expression.compiled(
                subjectexpr,
                schema=schema,
                options=qlcompiler.CompilerOptions(
                    anchors={qlast.Subject().name: subject},
                    path_prefix_anchor=qlast.Subject().name,
                    singletons=singletons,
                    apply_query_rewrites=not context.stdmode,
                ),
            )
            assert isinstance(final_subjectexpr.irast, ir_ast.Statement)

            refs = ir_utils.get_longest_paths(final_expr.irast)
            has_multi = False
            for ref in refs:
                while ref.rptr:
                    rptr = ref.rptr
                    if rptr.dir_cardinality.is_multi():
                        has_multi = True

                    # We don't need to look further than the subject,
                    # which is always valid. (And which is a singleton
                    # in a constraint expression if it is itself a
                    # singleton, regardless of other parts of the path.)
                    if (
                        isinstance(rptr.ptrref, ir_ast.PointerRef)
                        and rptr.ptrref.id == subject_obj.id
                    ):
                        break

                    if (not isinstance(rptr.ptrref,
                                       ir_ast.TupleIndirectionPointerRef)
                            and rptr.ptrref.source_ptr is None
                            and rptr.source.rptr is not None):
                        if isinstance(subject, s_links.Link):
                            raise errors.InvalidConstraintDefinitionError(
                                "link constraints may not access "
                                "the link target",
                                context=sourcectx
                            )
                        else:
                            raise errors.InvalidConstraintDefinitionError(
                                "constraints cannot contain paths with more "
                                "than one hop",
                                context=sourcectx
                            )

                    ref = rptr.source

            if has_multi and len(refs) > 1:
                raise errors.InvalidConstraintDefinitionError(
                    "cannot reference multiple links or properties in a "
                    "constraint where at least one link or property is MULTI",
                    context=sourcectx
                )

            if has_multi and ir_utils.contains_set_of_op(
                    final_subjectexpr.irast):
                raise errors.InvalidConstraintDefinitionError(
                    "cannot use aggregate functions or operators "
                    "in a non-aggregating constraint",
                    context=sourcectx
                )

        attrs['finalexpr'] = final_expr
        attrs['params'] = constr_base.get_params(schema)
        inherited['params'] = True
        attrs['abstract'] = False

        for k, v in attrs.items():
            self.set_attribute_value(k, v, inherited=bool(inherited.get(k)))
Exemplo n.º 15
0
    def get_concrete_constraint_attrs(cls,
                                      schema,
                                      subject,
                                      *,
                                      name,
                                      subjectexpr=None,
                                      sourcectx=None,
                                      args=[],
                                      modaliases=None,
                                      **kwargs):
        from edb.edgeql import utils as edgeql_utils
        from edb.edgeql import parser as edgeql_parser

        constr_base = schema.get(name, module_aliases=modaliases)
        module_aliases = {}

        orig_subject = subject
        base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr')
        if subjectexpr is None:
            subjectexpr = base_subjectexpr
        elif (base_subjectexpr is not None
              and subjectexpr.text != base_subjectexpr.text):
            raise errors.InvalidConstraintDefinitionError(
                'subjectexpr is already defined for ' + f'{str(name)!r}')

        if subjectexpr is not None:
            subject, _ = cls._normalize_constraint_expr(
                schema, {}, subjectexpr.text, subject)

        expr: s_expr.Expression = constr_base.get_field_value(schema, 'expr')
        if not expr:
            raise errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {name!r}')

        expr_ql = edgeql_parser.parse(expr.text, module_aliases)

        if not args:
            args = constr_base.get_field_value(schema, 'args')

        attrs = dict(kwargs)

        args_map = None
        if args:
            args_ql = [
                edgeql_parser.parse(arg.text, module_aliases) for arg in args
            ]

            args_map = edgeql_utils.index_parameters(
                args_ql,
                parameters=constr_base.get_params(schema),
                schema=schema)

            edgeql_utils.inline_parameters(expr_ql, args_map)

            args_map = {
                name: edgeql.generate_source(val, pretty=False)
                for name, val in args_map.items()
            }

            errmessage = attrs.get('errmessage')
            if not errmessage:
                errmessage = constr_base.get_errmessage(schema)

            attrs['errmessage'] = errmessage.format(
                __subject__='{__subject__}', **args_map)

        attrs['args'] = args

        if expr == '__subject__':
            expr_context = sourcectx
        else:
            expr_context = None

        if subject is not orig_subject:
            # subject has been redefined
            subject_anchor = qlast.SubExpr(
                expr=subject, anchors={qlast.Subject: orig_subject})
        else:
            subject_anchor = subject

        expr_text = cls.normalize_constraint_expr(schema,
                                                  module_aliases,
                                                  expr_ql,
                                                  subject=subject_anchor,
                                                  constraint_name=name,
                                                  enforce_boolean=True,
                                                  expr_context=expr_context)

        attrs['finalexpr'] = s_expr.Expression(text=expr_text)

        return constr_base, attrs
Exemplo n.º 16
0
    def get_concrete_constraint_attrs(
        cls,
        schema: s_schema.Schema,
        subject: Optional[so.Object],
        *,
        name: str,
        subjectexpr: Optional[s_expr.Expression] = None,
        sourcectx: Optional[c_parsing.ParserContext] = None,
        args: Any = None,
        modaliases: Optional[Mapping[Optional[str], str]] = None,
        **kwargs: Any
    ) -> Tuple[Any, Dict[str, Any], Dict[str, bool]]:
        # constr_base, attrs, inherited
        from edb.edgeql import parser as qlparser
        from edb.edgeql import utils as qlutils
        from edb.ir import ast as ir_ast

        constr_base: Constraint = schema.get(name, module_aliases=modaliases)

        module_aliases: Mapping[Optional[str], str] = {}

        orig_subjectexpr = subjectexpr
        orig_subject = subject
        base_subjectexpr = constr_base.get_field_value(schema, 'subjectexpr')
        if subjectexpr is None:
            subjectexpr = base_subjectexpr
        elif (base_subjectexpr is not None
                and subjectexpr.text != base_subjectexpr.text):
            raise errors.InvalidConstraintDefinitionError(
                'subjectexpr is already defined for ' +
                f'{str(name)!r}')

        if subjectexpr is not None:
            subject_ql = subjectexpr.qlast
            if subject_ql is None:
                subject_ql = qlparser.parse(subjectexpr.text, module_aliases)

            subject = subject_ql

        expr: s_expr.Expression = constr_base.get_field_value(schema, 'expr')
        if not expr:
            raise errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {name!r}')

        expr_ql = qlparser.parse(expr.text, module_aliases)

        if not args:
            args = constr_base.get_field_value(schema, 'args')

        attrs = dict(kwargs)
        inherited = dict()
        if orig_subjectexpr is not None:
            attrs['subjectexpr'] = orig_subjectexpr
        else:
            base_subjectexpr = constr_base.get_subjectexpr(schema)
            if base_subjectexpr is not None:
                attrs['subjectexpr'] = base_subjectexpr
                inherited['subjectexpr'] = True

        errmessage = attrs.get('errmessage')
        if not errmessage:
            errmessage = constr_base.get_errmessage(schema)
            inherited['errmessage'] = True

        attrs['errmessage'] = errmessage

        if subject is not orig_subject:
            # subject has been redefined
            assert isinstance(subject, qlast.Base)
            qlutils.inline_anchors(
                expr_ql, anchors={qlast.Subject().name: subject})
            subject = orig_subject

        if args:
            args_map = None
            args_ql: List[qlast.Base] = [
                qlast.Path(steps=[qlast.Subject()]),
            ]

            args_ql.extend(
                qlparser.parse(arg.text, module_aliases) for arg in args
            )

            args_map = qlutils.index_parameters(
                args_ql,
                parameters=constr_base.get_params(schema),
                schema=schema)

            qlutils.inline_parameters(expr_ql, args_map)

        attrs['args'] = args

        if expr == '__subject__':
            expr_context = sourcectx
        else:
            expr_context = None

        assert subject is not None
        final_expr = s_expr.Expression.compiled(
            s_expr.Expression.from_ast(expr_ql, schema, module_aliases),
            schema=schema,
            options=qlcompiler.CompilerOptions(
                modaliases=module_aliases,
                anchors={qlast.Subject().name: subject},
            ),
        )

        bool_t: s_scalars.ScalarType = schema.get('std::bool')
        assert isinstance(final_expr.irast, ir_ast.Statement)

        expr_type = final_expr.irast.stype
        if not expr_type.issubclass(schema, bool_t):
            raise errors.InvalidConstraintDefinitionError(
                f'{name} constraint expression expected '
                f'to return a bool value, got '
                f'{expr_type.get_verbosename(schema)}',
                context=expr_context
            )

        attrs['return_type'] = constr_base.get_return_type(schema)
        attrs['return_typemod'] = constr_base.get_return_typemod(schema)
        attrs['finalexpr'] = final_expr
        attrs['params'] = constr_base.get_params(schema)
        attrs['is_abstract'] = False

        return constr_base, attrs, inherited
Exemplo n.º 17
0
    def validate_create(
        self,
        schema: s_schema.Schema,
        context: sd.CommandContext,
    ) -> None:
        super().validate_create(schema, context)

        if self.get_referrer_context(context) is not None:
            # The checks below apply only to abstract constraints.
            return

        base_params: Optional[s_func.FuncParameterList] = None
        base_with_params: Optional[Constraint] = None

        bases = self.get_resolved_attribute_value(
            'bases',
            schema=schema,
            context=context,
        )

        for base in bases.objects(schema):
            params = base.get_params(schema)
            if params and len(params) > 1:
                # All constraints have __subject__ parameter
                # auto-injected, hence the "> 1" check.
                if base_params is not None:
                    raise errors.InvalidConstraintDefinitionError(
                        f'{self.get_verbosename()} '
                        f'extends multiple constraints '
                        f'with parameters',
                        context=self.source_context,
                    )
                base_params = params
                base_with_params = base

        if base_params:
            assert base_with_params is not None

            params = self._get_params(schema, context)
            if not params or len(params) == 1:
                # All constraints have __subject__ parameter
                # auto-injected, hence the "== 1" check.
                raise errors.InvalidConstraintDefinitionError(
                    f'{self.get_verbosename()} '
                    f'must define parameters to reflect parameters of '
                    f'the {base_with_params.get_verbosename(schema)} '
                    f'it extends',
                    context=self.source_context,
                )

            if len(params) < len(base_params):
                raise errors.InvalidConstraintDefinitionError(
                    f'{self.get_verbosename()} '
                    f'has fewer parameters than the '
                    f'{base_with_params.get_verbosename(schema)} '
                    f'it extends',
                    context=self.source_context,
                )

            # Skipping the __subject__ param
            for base_param, param in zip(
                    base_params.objects(schema)[1:],
                    params.objects(schema)[1:]):

                param_name = param.get_parameter_name(schema)
                base_param_name = base_param.get_parameter_name(schema)

                if param_name != base_param_name:
                    raise errors.InvalidConstraintDefinitionError(
                        f'the {param_name!r} parameter of the '
                        f'{self.get_verbosename()} '
                        f'must be renamed to {base_param_name!r} '
                        f'to match the signature of the base '
                        f'{base_with_params.get_verbosename(schema)} ',
                        context=self.source_context,
                    )

                param_type = param.get_type(schema)
                base_param_type = base_param.get_type(schema)

                if (not base_param_type.is_polymorphic(schema)
                        and param_type.is_polymorphic(schema)):
                    raise errors.InvalidConstraintDefinitionError(
                        f'the {param_name!r} parameter of the '
                        f'{self.get_verbosename()} cannot '
                        f'be of generic type because the corresponding '
                        f'parameter of the '
                        f'{base_with_params.get_verbosename(schema)} '
                        f'it extends has a concrete type',
                        context=self.source_context,
                    )

                if (not base_param_type.is_polymorphic(schema)
                        and not param_type.is_polymorphic(schema)
                        and not param_type.implicitly_castable_to(
                            base_param_type, schema)):
                    raise errors.InvalidConstraintDefinitionError(
                        f'the {param_name!r} parameter of the '
                        f'{self.get_verbosename()} has type of '
                        f'{param_type.get_displayname(schema)} that '
                        f'is not implicitly castable to the '
                        f'corresponding parameter of the '
                        f'{base_with_params.get_verbosename(schema)} with '
                        f'type {base_param_type.get_displayname(schema)}',
                        context=self.source_context,
                    )
Exemplo n.º 18
0
    def _cmd_tree_from_ast(
        cls,
        schema: s_schema.Schema,
        astnode: qlast.DDLOperation,
        context: sd.CommandContext,
    ) -> CreateConstraint:
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        if isinstance(astnode, qlast.CreateConcreteConstraint):
            if astnode.delegated:
                cmd.set_attribute_value('delegated', astnode.delegated)

            args = cls._constraint_args_from_ast(schema, astnode, context)
            if args:
                cmd.set_attribute_value('args', args)

        elif isinstance(astnode, qlast.CreateConstraint):
            params = cls._get_param_desc_from_ast(schema, context.modaliases,
                                                  astnode)

            for param in params:
                if param.get_kind(schema) is ft.ParameterKind.NamedOnlyParam:
                    raise errors.InvalidConstraintDefinitionError(
                        'named only parameters are not allowed '
                        'in this context',
                        context=astnode.context)

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

        if cmd.get_attribute_value('return_type') is None:
            cmd.set_attribute_value(
                'return_type',
                schema.get('std::bool'),
            )

        if cmd.get_attribute_value('return_typemod') is None:
            cmd.set_attribute_value(
                'return_typemod',
                ft.TypeModifier.SingletonType,
            )

        assert isinstance(
            astnode, (qlast.CreateConstraint, qlast.CreateConcreteConstraint))
        # 'subjectexpr' can be present in either astnode type
        if astnode.subjectexpr:
            orig_text = cls.get_orig_expr_text(schema, astnode, 'subjectexpr')

            if (orig_text is not None and context.compat_ver_is_before(
                (1, 0, verutils.VersionStage.ALPHA, 6))):
                # Versions prior to a6 used a different expression
                # normalization strategy, so we must renormalize the
                # expression.
                expr_ql = qlcompiler.renormalize_compat(
                    astnode.subjectexpr,
                    orig_text,
                    schema=schema,
                    localnames=context.localnames,
                )
            else:
                expr_ql = astnode.subjectexpr

            subjectexpr = s_expr.Expression.from_ast(
                expr_ql,
                schema,
                context.modaliases,
                context.localnames,
            )

            cmd.set_attribute_value(
                'subjectexpr',
                subjectexpr,
            )

        cls._validate_subcommands(astnode)
        assert isinstance(cmd, CreateConstraint)
        return cmd
Exemplo n.º 19
0
    def _cmd_tree_from_ast(
        cls,
        schema: s_schema.Schema,
        astnode: qlast.DDLOperation,
        context: sd.CommandContext,
    ) -> CreateConstraint:
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        if isinstance(astnode, qlast.CreateConcreteConstraint):
            if astnode.delegated:
                cmd.set_attribute_value('delegated', astnode.delegated)

            args = cls._constraint_args_from_ast(schema, astnode, context)
            if args:
                cmd.set_attribute_value('args', args)

        elif isinstance(astnode, qlast.CreateConstraint):
            params = cls._get_param_desc_from_ast(schema, context.modaliases,
                                                  astnode)

            for param in params:
                if param.get_kind(schema) is ft.ParameterKind.NAMED_ONLY:
                    raise errors.InvalidConstraintDefinitionError(
                        'named only parameters are not allowed '
                        'in this context',
                        context=astnode.context)

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

        if cmd.get_attribute_value('return_type') is None:
            cmd.set_attribute_value(
                'return_type',
                schema.get('std::bool'),
            )

        if cmd.get_attribute_value('return_typemod') is None:
            cmd.set_attribute_value(
                'return_typemod',
                ft.TypeModifier.SINGLETON,
            )

        assert isinstance(
            astnode, (qlast.CreateConstraint, qlast.CreateConcreteConstraint))
        # 'subjectexpr' can be present in either astnode type
        if astnode.subjectexpr:
            orig_text = cls.get_orig_expr_text(schema, astnode, 'subjectexpr')

            subjectexpr = s_expr.Expression.from_ast(
                astnode.subjectexpr,
                schema,
                context.modaliases,
                orig_text=orig_text,
            )

            cmd.set_attribute_value(
                'subjectexpr',
                subjectexpr,
            )

        cls._validate_subcommands(astnode)
        assert isinstance(cmd, CreateConstraint)
        return cmd
Exemplo n.º 20
0
    def schema_constraint_to_backend_constraint(cls, subject, constraint,
                                                schema, context,
                                                source_context):
        assert constraint.get_subject(schema) is not None

        constraint_origin = cls._get_constraint_origin(schema, constraint)
        if constraint_origin != constraint:
            origin_subject = constraint_origin.get_subject(schema)
        else:
            origin_subject = subject

        path_prefix_anchor = (qlast.Subject().name if isinstance(
            subject, s_types.Type) else None)

        ir = qlcompiler.compile_ast_to_ir(
            constraint.get_finalexpr(schema).qlast,
            schema,
            options=qlcompiler.CompilerOptions(
                anchors={qlast.Subject().name: subject},
                path_prefix_anchor=path_prefix_anchor,
                apply_query_rewrites=not context.stdmode,
            ),
        )

        terminal_refs = ir_utils.get_longest_paths(ir.expr.expr.result)
        ref_tables = get_ref_storage_info(ir.schema, terminal_refs)

        if len(ref_tables) > 1:
            raise errors.InvalidConstraintDefinitionError(
                f'Constraint {constraint.get_displayname(schema)} on '
                f'{subject.get_displayname(schema)} is not supported '
                f'because it would depend on multiple objects',
                context=source_context,
            )
        elif ref_tables:
            subject_db_name, _ = next(iter(ref_tables.items()))
        else:
            subject_db_name = common.get_backend_name(schema,
                                                      subject,
                                                      catenate=False)

        exclusive_expr_refs = cls._get_exclusive_refs(ir)

        pg_constr_data = {
            'subject_db_name': subject_db_name,
            'expressions': [],
            'origin_expressions': [],
        }

        if constraint_origin != constraint:
            origin_path_prefix_anchor = (qlast.Subject().name if isinstance(
                origin_subject, s_types.Type) else None)
            origin_ir = qlcompiler.compile_ast_to_ir(
                constraint_origin.get_finalexpr(schema).qlast,
                schema,
                options=qlcompiler.CompilerOptions(
                    anchors={qlast.Subject().name: origin_subject},
                    path_prefix_anchor=origin_path_prefix_anchor,
                    apply_query_rewrites=not context.stdmode,
                ),
            )

            origin_terminal_refs = ir_utils.get_longest_paths(
                origin_ir.expr.expr.result)
            origin_ref_tables = get_ref_storage_info(origin_ir.schema,
                                                     origin_terminal_refs)

            if origin_ref_tables:
                origin_subject_db_name, _ = (next(
                    iter(origin_ref_tables.items())))
            else:
                origin_subject_db_name = common.get_backend_name(
                    schema,
                    origin_subject,
                    catenate=False,
                )

            origin_exclusive_expr_refs = cls._get_exclusive_refs(origin_ir)
            pg_constr_data['origin_subject_db_name'] = origin_subject_db_name
        else:
            origin_exclusive_expr_refs = None
            pg_constr_data['origin_subject_db_name'] = subject_db_name

        if exclusive_expr_refs:
            for ref in exclusive_expr_refs:
                exprdata = cls._edgeql_ref_to_pg_constr(
                    subject, origin_subject, ref, schema)
                pg_constr_data['expressions'].append(exprdata)

            if origin_exclusive_expr_refs:
                for ref in origin_exclusive_expr_refs:
                    exprdata = cls._edgeql_ref_to_pg_constr(
                        subject, origin_subject, ref, schema)
                    pg_constr_data['origin_expressions'].append(exprdata)
            else:
                pg_constr_data['origin_expressions'] = (
                    pg_constr_data['expressions'])

            pg_constr_data['scope'] = 'relation'
            pg_constr_data['type'] = 'unique'
        else:
            exprdata = cls._edgeql_ref_to_pg_constr(subject, origin_subject,
                                                    ir, schema)
            pg_constr_data['expressions'].append(exprdata)

            pg_constr_data['scope'] = 'row'
            pg_constr_data['type'] = 'check'

        if isinstance(constraint.get_subject(schema), s_scalars.ScalarType):
            constraint = SchemaDomainConstraint(subject=subject,
                                                constraint=constraint,
                                                pg_constr_data=pg_constr_data,
                                                schema=schema)
        else:
            constraint = SchemaTableConstraint(subject=subject,
                                               constraint=constraint,
                                               pg_constr_data=pg_constr_data,
                                               schema=schema)
        return constraint