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

        if isinstance(astnode, qlast.CreateConcreteConstraint):
            if astnode.args:
                args = []

                for arg in astnode.args:
                    arg_expr = s_expr.ExpressionText(
                        edgeql.generate_source(arg.arg, pretty=False))
                    args.append(arg_expr)

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

        elif isinstance(astnode, qlast.CreateConstraint):
            if astnode.args:
                paramnames, paramdefaults, paramtypes, paramkinds, variadic = \
                    s_func.parameters_from_ast(
                        astnode, context.modaliases, schema)

                if variadic is not None:
                    cmd.add(
                        sd.AlterObjectProperty(property='varparam',
                                               new_value=variadic))

                for pname, pdefault, ptype in zip(paramnames, paramdefaults,
                                                  paramtypes):
                    if pname is not None:
                        raise ql_errors.EdgeQLError(
                            'constraints do not support named parameters',
                            context=astnode.context)

                    if pdefault is not None:
                        raise ql_errors.EdgeQLError(
                            'constraints do not support parameters '
                            'with defaults',
                            context=astnode.context)

                    if ptype is None:
                        raise ql_errors.EdgeQLError('untyped parameter',
                                                    context=astnode.context)

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

        # 'subject' can be present in either astnode type
        if astnode.subject:
            subjectexpr = s_expr.ExpressionText(
                edgeql.generate_source(astnode.subject, pretty=False))

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

        cls._validate_subcommands(astnode)

        return cmd
Esempio n. 2
0
def translate(schema,
              graphql,
              *,
              variables=None,
              operation_name=None,
              modules=None):
    if variables is None:
        variables = {}

    if modules is None:
        modules = set(modules) | {'default'}

    # HACK
    query = re.sub(r'@edgedb\(.*?\)', '', graphql)
    schema2 = gt.GQLCoreSchema(schema, *modules)._gql_schema

    parser = gqlparser.GraphQLParser()
    gqltree = parser.parse(graphql)
    context = GraphQLTranslatorContext(schema=schema,
                                       gqlcore=schema2,
                                       query=query,
                                       variables=variables,
                                       operation_name=operation_name,
                                       modules=modules)
    edge_forest_map = GraphQLTranslator(context=context).visit(gqltree)
    code = []
    for name, (tree, critvars) in sorted(edge_forest_map.items()):
        if name:
            code.append(f'# {name}')
        if critvars:
            crit = [f'{vname}={val!r}' for vname, val in critvars]
            code.append(f'# critical variables: {", ".join(crit)}')
        code += [edgeql.generate_source(tree), ';']

    return '\n'.join(code)
Esempio n. 3
0
def translate(schema, graphql, *, variables=None, operation_name=None):
    if variables is None:
        variables = {}

    gql_vars = {}
    for n, v in variables.items():
        gql_vars[n] = gqlast.BaseLiteral.from_python(v)

    # HACK
    query = re.sub(r'@edgedb\(.*?\)', '', graphql)
    gqlcore = gt.GQLCoreSchema(schema)

    parser = gqlparser.GraphQLParser()
    gqltree = parser.parse(graphql)
    context = GraphQLTranslatorContext(schema=schema,
                                       gqlcore=gqlcore,
                                       query=query,
                                       variables=gql_vars,
                                       operation_name=operation_name)
    edge_forest_map = GraphQLTranslator(context=context).visit(gqltree)
    code = []
    for name, (tree, critvars) in sorted(edge_forest_map.items()):
        if name:
            code.append(f'# {name}')
        if critvars:
            crit = [f'{vname}={val.value}' for vname, val in critvars]
            code.append(f'# critical variables: {", ".join(crit)}')
        code += [edgeql.generate_source(tree), ';']

    return '\n'.join(code)
Esempio n. 4
0
    def normalize_constraint_expr(cls,
                                  schema,
                                  module_aliases,
                                  expr,
                                  *,
                                  subject=None,
                                  constraint,
                                  expr_context=None,
                                  enforce_boolean=False):
        from edb.lang.ir import utils as irutils

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

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

        if enforce_boolean:
            bool_t = schema.get('std::bool')
            expr_type = irutils.infer_type(ir_result, schema)
            if not expr_type.issubclass(bool_t):
                raise s_errors.SchemaDefinitionError(
                    f'{constraint.displayname} constraint expression expected '
                    f'to return a bool value, got {expr_type.name.name!r}',
                    context=expr_context)

        expr = edgeql.generate_source(edgeql_tree, pretty=False)
        # XXX: check that expr has boolean result
        return expr
Esempio n. 5
0
    def _cmd_tree_from_ast(cls, astnode, context, schema):
        propname = astnode.name.name
        if astnode.name.module:
            propname = astnode.name.module + '::' + propname

        assert '::' not in propname

        if isinstance(astnode, qlast.DropAttributeValue):
            parent_ctx = context.get(CommandContextToken)
            parent_cls = parent_ctx.op.get_schema_metaclass()

            field = parent_cls._fields.get(propname)
            if (field is not None and
                    issubclass(field.type[0], typed.AbstractTypedCollection)):
                value = field.type[0]()
            else:
                value = None

            return cls(property=propname, new_value=value)

        if astnode.as_expr:
            new_value = s_expr.ExpressionText(
                edgeql.generate_source(astnode.value, pretty=False))
        else:
            if isinstance(astnode.value, qlast.Constant):
                new_value = astnode.value.value
            elif isinstance(astnode.value, qlast.Tuple):
                new_value = tuple(el.value for el in astnode.value.elements)
            else:
                raise ValueError(
                    f'unexpected value in attribute: {astnode.value!r}')

        return cls(property=propname, new_value=new_value)
Esempio n. 6
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
Esempio n. 7
0
    def _parse_default(cls, cmd):
        return
        for sub in cmd(sd.AlterObjectProperty):
            if sub.property == 'default':
                if isinstance(sub.new_value, sexpr.ExpressionText):
                    expr = edgeql.parse(sub.new_value)

                    if expr.op == qlast.UNION:
                        candidates = []
                        cls._extract_union_operands(expr, candidates)
                        deflt = []

                        for candidate in candidates:
                            cexpr = candidate.result
                            if isinstance(cexpr, qlast.Constant):
                                deflt.append(cexpr.value)
                            else:
                                text = edgeql.generate_source(candidate,
                                                              pretty=False)
                                deflt.append(sexpr.ExpressionText(text))
                    else:
                        deflt = [sub.new_value]

                else:
                    deflt = [sub.new_value]

                sub.new_value = deflt
Esempio n. 8
0
    def _encode_default(self, context, node, op):
        if op.new_value:
            expr = op.new_value
            if not isinstance(expr, sexpr.ExpressionText):
                expr_t = qlast.SelectQuery(result=qlast.Constant(value=expr))
                expr = edgeql.generate_source(expr_t, pretty=False)

                op.new_value = sexpr.ExpressionText(expr)
            super()._apply_field_ast(context, node, op)
Esempio n. 9
0
    def _constraint_args_from_ast(cls, schema, astnode):
        args = []

        if astnode.args:
            for arg in astnode.args:
                arg_expr = s_expr.ExpressionText(
                    edgeql.generate_source(arg.arg, pretty=False))
                args.append(arg_expr)

        return args
Esempio n. 10
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        if isinstance(astnode, qlast.CreateConcreteConstraint):
            args = cls._constraint_args_from_ast(schema, astnode)
            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,
                ))

        # 'subject' can be present in either astnode type
        if astnode.subject:
            subjectexpr = s_expr.ExpressionText(
                edgeql.generate_source(astnode.subject, pretty=False))

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

        cls._validate_subcommands(astnode)

        return cmd
Esempio n. 11
0
    def _cmd_tree_from_ast(cls, astnode, context, schema):
        cmd = super()._cmd_tree_from_ast(astnode, context, schema)

        parent_ctx = context.get(sd.CommandContextToken)
        subject_name = parent_ctx.op.classname

        cmd.update(
            (sd.AlterObjectProperty(
                property='subject',
                new_value=so.ObjectRef(classname=subject_name)),
             sd.AlterObjectProperty(property='expr',
                                    new_value=expr.ExpressionText(
                                        edgeql.generate_source(
                                            astnode.expr, pretty=False)))))

        return cmd
Esempio n. 12
0
def ddl_text_from_delta_command(delta):
    """Return DDL text for a delta command tree."""
    if isinstance(delta, s_db.AlterDatabase):
        commands = delta
    else:
        commands = [delta]

    text = []
    for command in commands:
        delta_ast = ddl_from_delta(command)
        if delta_ast:
            stmt_text = edgeql.generate_source(
                edgeql.optimize(delta_ast, strip_builtins=False))
            text.append(stmt_text + ';')

    return '\n'.join(text)
Esempio n. 13
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)
Esempio n. 14
0
    def _cmd_tree_from_ast(cls, schema, astnode, context):
        cmd = super()._cmd_tree_from_ast(schema, astnode, context)

        parent_ctx = context.get(sd.CommandContextToken)
        subject = parent_ctx.scls

        cmd.update((
            sd.AlterObjectProperty(
                property='subject',
                new_value=s_utils.reduce_to_typeref(schema, subject)
            ),
            sd.AlterObjectProperty(
                property='expr',
                new_value=s_expr.ExpressionText(
                    edgeql.generate_source(astnode.expr, pretty=False))
            )
        ))

        return cmd
Esempio n. 15
0
    def _classname_quals_from_ast(cls, schema, astnode, base_name,
                                  referrer_name, context):
        subject = schema.get(referrer_name, None)
        if subject is None:
            return ()

        props = {}
        args = cls._constraint_args_from_ast(schema, astnode)
        if args:
            props['args'] = args
        if astnode.subject:
            props['subjectexpr'] = s_expr.ExpressionText(
                edgeql.generate_source(astnode.subject, pretty=False))

        _, attrs = Constraint.get_concrete_constraint_attrs(
            schema,
            subject,
            name=base_name,
            sourcectx=astnode.context,
            modaliases=context.modaliases,
            **props)

        return (Constraint._name_qual_from_expr(schema, attrs['finalexpr']), )
Esempio n. 16
0
    def get_concrete_constraint_attrs(cls,
                                      schema,
                                      subject,
                                      *,
                                      name,
                                      subjectexpr=None,
                                      sourcectx=None,
                                      args=[],
                                      modaliases=None,
                                      **kwargs):
        from edb.lang.edgeql import utils as edgeql_utils
        from edb.lang.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 != base_subjectexpr:
            raise errors.InvalidConstraintDefinitionError(
                'subjectexpr is already defined for ' + f'{str(name)!r}')

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

        expr = 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, 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, 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)

            args = list(args_map.values())

        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'] = expr_text

        return constr_base, attrs
Esempio n. 17
0
    def process_specialized_constraint(cls, schema, constraint, params=None):
        from edb.lang.edgeql import utils as edgeql_utils
        from edb.lang.edgeql import parser as edgeql_parser

        assert constraint.subject is not None

        module_aliases = {}

        # check to make sure that the specialized constraint doesn't redefine
        # an already defined subjectexpr
        if constraint.subjectexpr is not None:
            for base in constraint.bases:
                base_se = base.get_field_value('subjectexpr')
                if base_se and base_se != constraint.subjectexpr:
                    raise s_errors.InvalidConstraintDefinitionError(
                        'subjectexpr is already defined for ' +
                        f'{constraint.name!r}')

        subject = constraint.subject
        subjectexpr = constraint.get_field_value('subjectexpr')
        if subjectexpr:
            subject, _ = cls._normalize_constraint_expr(
                schema, {}, subjectexpr, subject)

        expr = constraint.get_field_value('expr')
        if not expr:
            raise s_errors.InvalidConstraintDefinitionError(
                f'missing constraint expression in {constraint.name!r}')

        expr_ql = edgeql_parser.parse(expr, module_aliases)

        if params:
            args = params
        else:
            args = constraint.get_field_value('args')

        args_map = None
        if args:
            if constraint.varparam is not None:
                varparam = constraint.varparam
            else:
                varparam = None

            args_ql = [
                edgeql_parser.parse(arg, module_aliases) for arg in args
            ]

            args_map = edgeql_utils.index_parameters(args_ql,
                                                     varparam=varparam)

            edgeql_utils.inline_parameters(expr_ql, args_map)

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

            constraint.errmessage = constraint.errmessage.format(
                __subject__='{__subject__}', **args_map)

            args = list(args_map.values())

        if expr == '__subject__':
            expr_context = \
                constraint.get_attribute_source_context('subjectexpr')
        else:
            expr_context = \
                constraint.get_attribute_source_context('expr')

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

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

        constraint.expr = expr_text
        constraint.localfinalexpr = expr_text
        constraint.finalexpr = expr_text

        constraint.args = args or None