Пример #1
0
    def _get_ast(self, schema, context):
        value = self.new_value

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

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

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

        if new_value_empty and old_value_empty:
            return

        if isinstance(value, s_expr.Expression):
            value = edgeql.parse(value.text)
        elif utils.is_nontrivial_container(value):
            value = qlast.Tuple(
                elements=[qlast.BaseConstant.from_python(el) for el in value])
        else:
            value = qlast.BaseConstant.from_python(value)

        op = qlast.SetField(name=qlast.ObjectRef(module='',
                                                 name=self.property),
                            value=value)
        return op
Пример #2
0
def eta_expand_tuple(
    path: qlast.Path,
    stype: s_types.Tuple,
    *,
    ctx: context.ContextLevel,
) -> qlast.Expr:
    """η-expansion of tuples

    η-expansion of tuple types is straightforward and traditional:
        EXPAND(tuple<t, s>, p) = (EXPAND(t, p.0), EXPAND(s, p.1))
    is the case for pairs. n-ary and named cases are generalized in the
    obvious way.
    The one exception is that the expansion of the empty tuple type is
    `p` and not `()`, to ensure that the path appears in the output.
    """
    if not stype.get_subtypes(ctx.env.schema):
        return path

    els = [
        qlast.TupleElement(
            name=qlast.ObjectRef(name=name),
            val=eta_expand(astutils.extend_path(path, name), subtype, ctx=ctx),
        ) for name, subtype in stype.iter_subtypes(ctx.env.schema)
    ]

    if stype.is_named(ctx.env.schema):
        return qlast.NamedTuple(elements=els)
    else:
        return qlast.Tuple(elements=[el.val for el in els])
Пример #3
0
    def visit_Tuple(self, node):
        if node.named:
            result = qlast.NamedTuple(elements=[
                qlast.TupleElement(name=el.name, val=self.visit(el.val))
                for el in node.elements
            ])
        else:
            result = qlast.Tuple(
                elements=[self.visit(e.val) for e in node.elements])

        return result
Пример #4
0
    def _get_ast(self, schema, context):
        value = self.new_value

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

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

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

        if not field.allow_ddl_set:
            return

        if self.source == 'inheritance':
            return

        if new_value_empty and old_value_empty:
            return

        if isinstance(value, s_expr.Expression):
            value = value.qlast
        elif utils.is_nontrivial_container(value):
            value = qlast.Tuple(
                elements=[qlast.BaseConstant.from_python(el) for el in value])
        else:
            value = qlast.BaseConstant.from_python(value)

        op = qlast.SetField(name=qlast.ObjectRef(module='',
                                                 name=self.property),
                            value=value)
        return op
Пример #5
0
def eta_expand_array(
    path: qlast.Path,
    stype: s_types.Array,
    *,
    ctx: context.ContextLevel,
) -> qlast.Expr:
    """η-expansion of arrays

    η-expansion of array types is is a little peculiar to edgeql and less
    grounded in typed lambda calculi:
        EXPAND(array<t>, p) =
            (p, array_agg(EXPAND_ORDERED(t, array_unpack(p)))).1

    We use a similar approach for compiling casts.

    The tuple projection trick serves to make sure that we iterate over
    `p` *outside* of the array_agg (or else all the arrays would get
    aggregated together) as well as ensuring that `p` appears in the expansion
    in a non-fenced position (or else sorting it from outside wouldn't work).

    (If it wasn't for the latter requirement, we could just use a FOR.
    I find it a little unsatisfying that our η-expansion needs to use this
    trick, and the pgsql compiler needed to be hacked to make it work.)
    """

    unpacked = qlast.FunctionCall(func=('__std__', 'array_unpack'),
                                  args=[path])

    expanded = eta_expand_ordered(unpacked,
                                  stype.get_element_type(ctx.env.schema),
                                  ctx=ctx)

    agg_expr = qlast.FunctionCall(func=('__std__', 'array_agg'),
                                  args=[expanded])

    return astutils.extend_path(qlast.Tuple(elements=[path, agg_expr]), '1')
Пример #6
0
 def reduce_LPAREN_RPAREN(self, *kids):
     self.val = qlast.Tuple(elements=[])
Пример #7
0
 def reduce_LPAREN_Expr_COMMA_OptExprList_RPAREN(self, *kids):
     self.val = qlast.Tuple(elements=[kids[1].val] + kids[3].val)
Пример #8
0
def _cast_array(
        ir_set: irast.Set,
        orig_stype: s_types.Type,
        new_stype: s_types.Type, *,
        srcctx: Optional[parsing.ParserContext],
        ctx: context.ContextLevel) -> irast.Set:

    assert isinstance(orig_stype, s_types.Array)

    direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx)

    if direct_cast is None:
        if not new_stype.is_array():
            raise errors.QueryError(
                f'cannot cast {orig_stype.get_displayname(ctx.env.schema)!r} '
                f'to {new_stype.get_displayname(ctx.env.schema)!r}',
                context=srcctx)
        assert isinstance(new_stype, s_types.Array)
        el_type = new_stype.get_subtypes(ctx.env.schema)[0]
    else:
        el_type = new_stype

    orig_el_type = orig_stype.get_subtypes(ctx.env.schema)[0]

    el_cast = _find_cast(orig_el_type, el_type, srcctx=srcctx, ctx=ctx)

    if el_cast is not None and el_cast.get_from_cast(ctx.env.schema):
        # Simple cast
        return _cast_to_ir(
            ir_set, el_cast, orig_stype, new_stype, ctx=ctx)
    else:
        with ctx.new() as subctx:
            subctx.anchors = subctx.anchors.copy()
            source_path = subctx.create_anchor(ir_set, 'a')

            unpacked = qlast.FunctionCall(
                func=('__std__', 'array_unpack'),
                args=[source_path],
            )

            enumerated = dispatch.compile(
                qlast.FunctionCall(
                    func=('__std__', 'enumerate'),
                    args=[unpacked],
                ),
                ctx=subctx,
            )

            enumerated_ref = subctx.create_anchor(enumerated, 'e')

            elements = qlast.FunctionCall(
                func=('__std__', 'array_agg'),
                args=[
                    qlast.SelectQuery(
                        result=qlast.TypeCast(
                            expr=astutils.extend_path(enumerated_ref, '1'),
                            type=typegen.type_to_ql_typeref(
                                el_type,
                                ctx=subctx,
                            ),
                            cardinality_mod=qlast.CardinalityModifier.Required,
                        ),
                        orderby=[
                            qlast.SortExpr(
                                path=astutils.extend_path(enumerated_ref, '0'),
                                direction=qlast.SortOrder.Asc,
                            ),
                        ],
                    ),
                ],
            )

            # Force the elements to be correlated with whatever the
            # anchor was. (Doing it this way ensures a NULL check,
            # and just registering it in the scope would not.)
            correlated_elements = astutils.extend_path(
                qlast.Tuple(elements=[source_path, elements]), '1'
            )

            if el_type.contains_json(subctx.env.schema):
                subctx.inhibit_implicit_limit = True

            array_ir = dispatch.compile(correlated_elements, ctx=subctx)
            assert isinstance(array_ir, irast.Set)

            if direct_cast is not None:
                ctx.env.schema, array_stype = s_types.Array.from_subtypes(
                    ctx.env.schema, [el_type])
                return _cast_to_ir(
                    array_ir, direct_cast, array_stype, new_stype, ctx=ctx
                )
            else:
                return array_ir