Exemple #1
0
    def _get_ast(self, context):
        value = self.new_value

        new_value_empty = \
            (value is None or
                (isinstance(value, collections.Container) and not value))

        old_value_empty = \
            (self.old_value is None or
                (isinstance(self.old_value, collections.Container) and
                    not self.old_value))

        if new_value_empty and not old_value_empty:
            op = qlast.DropAttributeValue(
                name=qlast.ObjectRef(module='', name=self.property))
            return op

        if new_value_empty and old_value_empty:
            return

        if isinstance(value, s_expr.ExpressionText):
            value = edgeql.parse(str(value))
        elif utils.is_nontrivial_container(value):
            value = qlast.Tuple(elements=[
                qlast.Constant(value=el) for el in value
            ])
        elif isinstance(value, nlang.WordCombination):
            forms = value.as_dict()
            if len(forms) > 1:
                items = []
                for k, v in forms.items():
                    items.append((
                        qlast.Constant(value=k),
                        qlast.Constant(value=v)
                    ))
                value = qlast.Array(elements=[
                    qlast.Tuple(elements=[k, v]) for k, v in items
                ])
            else:
                value = qlast.Constant(value=str(value))
        else:
            value = qlast.Constant(value=value)

        as_expr = isinstance(value, qlast.ExpressionText)
        op = qlast.CreateAttributeValue(
            name=qlast.ObjectRef(module='', name=self.property),
            value=value, as_expr=as_expr)
        return op
Exemple #2
0
 def _apply_field_ast(self, context, node, op):
     if op.property == 'spectargets':
         if op.new_value:
             node.commands.append(
                 qlast.AlterTarget(targets=[
                     qlast.ObjectRef(name=t.classname.name,
                                     module=t.classname.module)
                     for t in op.new_value
                 ]))
     elif op.property == 'target':
         if op.new_value:
             node.commands.append(
                 qlast.AlterTarget(targets=[
                     qlast.ObjectRef(name=op.new_value.classname.name,
                                     module=op.new_value.classname.module)
                 ]))
     elif op.property == 'source':
         pass
     elif op.property == 'search':
         if op.new_value:
             v = qlast.Constant(value=str(op.new_value.weight))
             self._set_attribute_ast(context, node, 'search_weight', v)
         else:
             self._drop_attribute_ast(context, node, 'search_weight')
     else:
         super()._apply_field_ast(context, node, op)
Exemple #3
0
    def _apply_field_ast(self, context, node, op):
        objtype = context.get(LinkSourceCommandContext)

        if op.property == 'is_derived':
            pass
        elif op.property == 'spectargets':
            if op.new_value:
                node.targets = [
                    qlast.ObjectRef(name=t.classname.name,
                                    module=t.classname.module)
                    for t in op.new_value
                ]
        elif op.property == 'default':
            self._encode_default(context, node, op)
        elif op.property == 'required':
            node.is_required = op.new_value
        elif op.property == 'source':
            pass
        elif op.property == 'search':
            if op.new_value:
                v = qlast.Constant(value=str(op.new_value.weight))
                self._set_attribute_ast(context, node, 'search_weight', v)
        elif op.property == 'target' and objtype:
            if not node.targets:
                t = op.new_value
                node.targets = [utils.typeref_to_ast(t)]
        else:
            super()._apply_field_ast(context, node, op)
Exemple #4
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)
Exemple #5
0
 def _apply_field_ast(self, context, node, op):
     if op.property == 'value':
         node.value = qlast.Constant(value=op.new_value)
     elif op.property == 'is_derived':
         pass
     elif op.property == 'attribute':
         pass
     elif op.property == 'subject':
         pass
     else:
         super()._apply_field_ast(context, node, op)
Exemple #6
0
 def visit_Constant(self, node):
     return qlast.Constant(value=node.value)
Exemple #7
0
 def reduce_FALSE(self, *kids):
     self.val = qlast.Constant(value=False)
Exemple #8
0
 def reduce_TRUE(self, *kids):
     self.val = qlast.Constant(value=True)
Exemple #9
0
 def reduce_SCONST(self, *kids):
     self.val = qlast.Constant(value=str(kids[0].string))
Exemple #10
0
 def reduce_FCONST(self, *kids):
     self.val = qlast.Constant(value=float(kids[0].val))
Exemple #11
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 #12
0
 def reduce_COLONGT_NL_INDENT_RawString_NL_DEDENT(self, *kids):
     text = kids[3].val.value
     text = textwrap.dedent(text).strip()
     self.val = qlast.Constant(value=text)
Exemple #13
0
 def _apply_fields_ast(self, context, node):
     super()._apply_fields_ast(context, node)
     for op in self(sd.AlterObjectProperty):
         if op.property == 'value':
             node.value = qlast.Constant(value=op.new_value)
Exemple #14
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
    source_set = rptr.source
    source_scls = source_set.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():
        source_set = new_set_from_set(source_set,
                                      preserve_scope_ns=True,
                                      ctx=ctx)
        source_set.scls = source_scls.peel_view()
        source_set.shape = []

        if source_set.rptr is not None:
            derived_from = source_set.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()):
                source_set.rptr.ptrcls = derived_from

    try:
        qlexpr, qlctx, inner_source_path_id = 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
        inner_source_path_id = 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,
                                     qlctx=qlctx,
                                     ctx=ctx)

    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)

    with newctx() as subctx:
        subctx.view_scls = ptrcls.target
        subctx.view_rptr = context.ViewRPtr(source_scls,
                                            ptrcls=ptrcls,
                                            rptr=rptr)
        subctx.anchors[qlast.Source] = source_set
        subctx.path_scope.contain_path(path_id)
        subctx.empty_result_type_hint = ptrcls.target

        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 #15
0
    def _visit_arguments(self, arguments):
        where = offset = limit = None
        orderby = []
        first = last = before = after = None

        for arg in arguments:
            try:
                if arg.name == 'filter':
                    where = self.visit(arg.value)
                elif arg.name == 'order':
                    orderby = self.visit_order(arg.value)
                elif arg.name == 'first':
                    first = arg.value.value
                    if first < 0:
                        raise ValueError(f"{arg.name!r} cannot be negative")
                elif arg.name == 'last':
                    last = arg.value.value
                    last_context = arg.context
                    if last < 0:
                        raise ValueError(f"{arg.name!r} cannot be negative")
                elif arg.name == 'before':
                    before = int(arg.value.value)
                    if before < 0:
                        raise ValueError(f"{arg.name!r} cannot be negative")
                elif arg.name == 'after':
                    after = int(arg.value.value)
                    if after < 0:
                        raise ValueError(f"{arg.name!r} cannot be negative")
                    # The +1 is to make 'after' into an appropriate index.
                    #
                    # 0--a--1--b--2--c--3-- ... we call element at
                    # index 0 (or "element 0" for short), the element
                    # immediately after the mark 0. So after "element
                    # 0" really means after "index 1".
                    after += 1
            except Exception:
                raise g_errors.GraphQLValidationError(
                    f"invalid value for {arg.name!r}: {arg.value.value!r}",
                    context=arg.context) from None

        # convert before, after, first and last into offset and limit
        if after is not None:
            offset = after
        if before is not None:
            limit = before - (after or 0)
        if first is not None:
            if limit is None:
                limit = first
            else:
                limit = min(first, limit)
        if last is not None:
            if limit is not None:
                if last < limit:
                    offset = (offset or 0) + limit - last
                    limit = last
            else:
                # FIXME: there wasn't any limit, so we can define last
                # in terms of offset alone without negative OFFSET
                # implementation
                raise g_errors.GraphQLTranslationError(
                    f'last={last} translates to a negative OFFSET in '
                    f'EdgeQL which is currently unsupported',
                    context=last_context)

        # convert integers into qlast literals
        if offset is not None and not isinstance(offset, qlast.Base):
            offset = qlast.Constant(value=max(0, offset))
        if limit is not None:
            limit = qlast.Constant(value=max(0, limit))

        return where, orderby, offset, limit