Exemple #1
0
def declare_view_from_schema(
        viewcls: s_obj.Object, *,
        ctx: context.ContextLevel) -> irast.Set:
    vc = ctx.view_class_map.get(viewcls)
    if vc is not None:
        return vc

    with ctx.detached() as subctx:
        view_expr = qlparser.parse(viewcls.expr)
        declare_view(view_expr, alias=viewcls.name,
                     fully_detached=True, ctx=subctx)

        vc = subctx.aliased_views[viewcls.name]
        ctx.view_class_map[viewcls] = vc
        ctx.source_map.update(subctx.source_map)
        ctx.aliased_views[viewcls.name] = subctx.aliased_views[viewcls.name]
        ctx.view_nodes[vc.name] = vc
        ctx.view_sets[vc] = subctx.view_sets[vc]

    return vc
Exemple #2
0
def compile_to_ir(expr,
                  schema,
                  *,
                  anchors=None,
                  security_context=None,
                  modaliases=None,
                  implicit_id_in_shapes=False):
    """Compile given EdgeQL statement into EdgeDB IR."""

    if debug.flags.edgeql_compile:
        debug.header('EdgeQL TEXT')
        debug.print(expr)

    tree = ql_parser.parse(expr, modaliases)

    return compile_ast_to_ir(tree,
                             schema,
                             anchors=anchors,
                             security_context=security_context,
                             modaliases=modaliases,
                             implicit_id_in_shapes=implicit_id_in_shapes)
Exemple #3
0
    def _normalize_constraint_expr(cls,
                                   schema,
                                   module_aliases,
                                   expr,
                                   subject,
                                   *,
                                   inline_anchors=False):
        from edb.lang.edgeql import parser as edgeql_parser
        from edb.lang.edgeql import utils as edgeql_utils

        if isinstance(expr, str):
            tree = edgeql_parser.parse(expr, module_aliases)
        else:
            tree = expr

        ir, edgeql_tree, _ = edgeql_utils.normalize_tree(
            tree,
            schema,
            modaliases=module_aliases,
            anchors={qlast.Subject: subject},
            inline_anchors=inline_anchors)

        return edgeql_tree.result, ir
Exemple #4
0
def declare_view_from_schema(viewcls: s_obj.Object, *,
                             ctx: context.ContextLevel) -> irast.Set:
    vc = ctx.env.schema_view_cache.get(viewcls)
    if vc is not None:
        return vc

    with ctx.detached() as subctx:
        subctx.expr_exposed = False
        view_expr = qlparser.parse(viewcls.get_expr(ctx.env.schema))
        viewcls_name = viewcls.get_name(ctx.env.schema)
        declare_view(view_expr,
                     alias=viewcls_name,
                     fully_detached=True,
                     ctx=subctx)

        vc = subctx.aliased_views[viewcls_name]
        ctx.env.schema_view_cache[viewcls] = vc
        ctx.source_map.update(subctx.source_map)
        ctx.aliased_views[viewcls_name] = subctx.aliased_views[viewcls_name]
        ctx.view_nodes[vc.get_name(ctx.env.schema)] = vc
        ctx.view_sets[vc] = subctx.view_sets[vc]

    return vc
Exemple #5
0
def computable_ptr_set(rptr: irast.Pointer,
                       *,
                       unnest_fence: bool = False,
                       ctx: context.ContextLevel) -> irast.Set:
    """Return ir.Set for a pointer defined as a computable."""
    ptrcls = rptr.ptrcls

    # Must use an entirely separate context, as the computable
    # expression is totally independent from the surrounding query.
    subctx = stmtctx.init_context(schema=ctx.schema)
    self_ = rptr.source
    source_scls = self_.scls
    # process_view() may generate computable pointer expressions
    # in the form "self.linkname".  To prevent infinite recursion,
    # self must resolve to the parent type of the view NOT the view
    # type itself.  Similarly, when resolving computable link properties
    # make sure that we use rptr.ptrcls.derived_from.
    if source_scls.is_view():
        self_ = copy.copy(self_)
        self_.scls = source_scls.peel_view()
        self_.shape = []

        if self_.rptr is not None:
            derived_from = self_.rptr.ptrcls.derived_from
            if (derived_from is not None and not derived_from.generic()
                    and derived_from.derived_from is not None
                    and ptrcls.is_link_property()):
                self_.rptr.ptrcls = derived_from

    subctx.anchors[qlast.Source] = self_

    subctx.aliases = ctx.aliases
    subctx.stmt = ctx.stmt
    subctx.view_scls = ptrcls.target
    subctx.view_rptr = context.ViewRPtr(source_scls, ptrcls=ptrcls, rptr=rptr)
    subctx.toplevel_stmt = ctx.toplevel_stmt
    subctx.path_scope = ctx.path_scope
    subctx.pending_cardinality = ctx.pending_cardinality
    subctx.completion_work = ctx.completion_work
    subctx.pointer_derivation_map = ctx.pointer_derivation_map
    subctx.class_shapes = ctx.class_shapes
    subctx.all_sets = ctx.all_sets
    subctx.path_scope_map = ctx.path_scope_map
    subctx.scope_id_ctr = ctx.scope_id_ctr
    subctx.expr_exposed = ctx.expr_exposed

    if ptrcls.is_link_property():
        source_path_id = rptr.source.path_id.ptr_path()
    else:
        source_path_id = rptr.target.path_id.src_path()

    path_id = source_path_id.extend(ptrcls,
                                    s_pointers.PointerDirection.Outbound,
                                    ptrcls.target)

    subctx.path_scope.contain_path(path_id)

    try:
        qlexpr, qlctx = ctx.source_map[ptrcls]
    except KeyError:
        if not ptrcls.default:
            raise ValueError(
                f'{ptrcls.shortname!r} is not a computable pointer')

        if isinstance(ptrcls.default, s_expr.ExpressionText):
            qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls.default))
        else:
            qlexpr = qlast.Constant(value=ptrcls.default)

        qlctx = None
    else:
        subctx.modaliases = qlctx.modaliases.copy()
        subctx.aliased_views = qlctx.aliased_views.new_child()
        if source_scls.is_view():
            subctx.aliased_views[self_.scls.name] = None
        subctx.source_map = qlctx.source_map.copy()
        subctx.view_nodes = qlctx.view_nodes.copy()
        subctx.view_sets = qlctx.view_sets.copy()
        subctx.view_map = qlctx.view_map.new_child()
        subctx.singletons = qlctx.singletons.copy()
        subctx.path_id_namespce = qlctx.path_id_namespace

    if qlctx is None:
        # This is a schema-level computable expression, put all
        # class refs into a separate namespace.
        subctx.path_id_namespace = (subctx.aliases.get('ns'), )
    else:
        subctx.pending_stmt_own_path_id_namespace = \
            irast.WeakNamespace(ctx.aliases.get('ns'))

        subns = subctx.pending_stmt_full_path_id_namespace = \
            {subctx.pending_stmt_own_path_id_namespace}

        self_view = ctx.view_sets.get(self_.scls)
        if self_view:
            if self_view.path_id.namespace:
                subns.update(self_view.path_id.namespace)
            inner_path_id = self_view.path_id.merge_namespace(
                subctx.path_id_namespace + tuple(subns))
        else:
            if self_.path_id.namespace:
                subns.update(self_.path_id.namespace)
            inner_path_id = pathctx.get_path_id(
                self_.scls, ctx=subctx).merge_namespace(subns)

        remapped_source = new_set_from_set(rptr.source, ctx=subctx)
        remapped_source.path_id = \
            remapped_source.path_id.merge_namespace(subns)
        subctx.view_map[inner_path_id] = remapped_source

    if isinstance(qlexpr, qlast.Statement) and unnest_fence:
        subctx.stmt_metadata[qlexpr] = context.StatementMetadata(
            is_unnest_fence=True)

    comp_ir_set = dispatch.compile(qlexpr, ctx=subctx)

    if ptrcls in ctx.pending_cardinality:
        comp_ir_set_copy = copy.copy(comp_ir_set)

        stmtctx.get_pointer_cardinality_later(ptrcls=ptrcls,
                                              irexpr=comp_ir_set_copy,
                                              ctx=ctx)

        def _check_cardinality(ctx):
            if ptrcls.singular():
                stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx)

        stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx)

    comp_ir_set.scls = ptrcls.target
    comp_ir_set.path_id = path_id
    comp_ir_set.rptr = rptr

    rptr.target = comp_ir_set

    return comp_ir_set
Exemple #6
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
Exemple #7
0
def computable_ptr_set(rptr: irast.Pointer,
                       *,
                       unnest_fence: bool = False,
                       same_computable_scope: bool = False,
                       ctx: context.ContextLevel) -> irast.Set:
    """Return ir.Set for a pointer defined as a computable."""
    ptrcls = rptr.ptrcls
    source_set = rptr.source
    source_scls = source_set.stype
    # process_view() may generate computable pointer expressions
    # in the form "self.linkname".  To prevent infinite recursion,
    # self must resolve to the parent type of the view NOT the view
    # type itself.  Similarly, when resolving computable link properties
    # make sure that we use rptr.ptrcls.derived_from.
    if source_scls.is_view(ctx.env.schema):
        source_set = new_set_from_set(source_set,
                                      preserve_scope_ns=True,
                                      ctx=ctx)
        source_set.stype = source_scls.peel_view(ctx.env.schema)
        source_set.shape = []

        if source_set.rptr is not None:
            schema = ctx.env.schema
            derived_from = source_set.rptr.ptrcls.get_derived_from(schema)
            if (derived_from is not None and not derived_from.generic(schema)
                    and derived_from.get_derived_from(schema) is not None
                    and ptrcls.is_link_property(schema)):
                source_set.rptr.ptrcls = derived_from

    try:
        qlexpr, qlctx, inner_source_path_id, path_id_ns = \
            ctx.source_map[ptrcls]
    except KeyError:
        ptrcls_default = ptrcls.get_default(ctx.env.schema)
        if not ptrcls_default:
            ptrcls_sn = ptrcls.get_shortname(ctx.env.schema)
            raise ValueError(f'{ptrcls_sn!r} is not a computable pointer')

        if isinstance(ptrcls_default, s_expr.ExpressionText):
            qlexpr = astutils.ensure_qlstmt(qlparser.parse(ptrcls_default))
        else:
            qlexpr = qlast.BaseConstant.from_python(ptrcls_default)

        qlctx = None
        inner_source_path_id = None
        path_id_ns = None

    if qlctx is None:
        # Schema-level computable, completely detached context
        newctx = ctx.detached
    else:
        newctx = _get_computable_ctx(rptr=rptr,
                                     source=source_set,
                                     source_scls=source_scls,
                                     inner_source_path_id=inner_source_path_id,
                                     path_id_ns=path_id_ns,
                                     same_scope=same_computable_scope,
                                     qlctx=qlctx,
                                     ctx=ctx)

    if ptrcls.is_link_property(ctx.env.schema):
        source_path_id = rptr.source.path_id.ptr_path()
    else:
        source_path_id = rptr.target.path_id.src_path()

    path_id = source_path_id.extend(ptrcls,
                                    s_pointers.PointerDirection.Outbound,
                                    ptrcls.get_target(ctx.env.schema),
                                    ns=ctx.path_id_namespace,
                                    schema=ctx.env.schema)

    with newctx() as subctx:
        subctx.view_scls = ptrcls.get_target(ctx.env.schema)
        subctx.view_rptr = context.ViewRPtr(source_scls,
                                            ptrcls=ptrcls,
                                            rptr=rptr)
        subctx.anchors[qlast.Source] = source_set
        subctx.empty_result_type_hint = ptrcls.get_target(ctx.env.schema)

        if isinstance(qlexpr, qlast.Statement) and unnest_fence:
            subctx.stmt_metadata[qlexpr] = context.StatementMetadata(
                is_unnest_fence=True)

        comp_ir_set = dispatch.compile(qlexpr, ctx=subctx)

    if ptrcls in ctx.pending_cardinality:
        comp_ir_set_copy = copy.copy(comp_ir_set)
        specified_card, source_ctx = ctx.pending_cardinality[ptrcls]

        stmtctx.get_pointer_cardinality_later(ptrcls=ptrcls,
                                              irexpr=comp_ir_set_copy,
                                              specified_card=specified_card,
                                              source_ctx=source_ctx,
                                              ctx=ctx)

        def _check_cardinality(ctx):
            if ptrcls.singular(ctx.env.schema):
                stmtctx.enforce_singleton_now(comp_ir_set_copy, ctx=ctx)

        stmtctx.at_stmt_fini(_check_cardinality, ctx=ctx)

    comp_ir_set.stype = ptrcls.get_target(ctx.env.schema)
    comp_ir_set.path_id = path_id
    comp_ir_set.rptr = rptr

    rptr.target = comp_ir_set

    return comp_ir_set
Exemple #8
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