Esempio n. 1
0
def _cast_json_to_tuple(
        ir_set: irast.Set,
        orig_stype: s_types.Type,
        new_stype: s_types.Tuple,
        cardinality_mod: Optional[qlast.CardinalityModifier],
        *,
        srcctx: Optional[parsing.ParserContext],
        ctx: context.ContextLevel) -> irast.Set:

    with ctx.new() as subctx:
        subctx.anchors = subctx.anchors.copy()
        source_path = subctx.create_anchor(ir_set, 'a')

        # Top-level json->tuple casts should produce an empty set on
        # null inputs, but error on missing fields or null
        # subelements, so filter out json nulls directly here to
        # distinguish those cases.
        if cardinality_mod != qlast.CardinalityModifier.Required:
            pathctx.register_set_in_scope(ir_set, ctx=subctx)

            check = qlast.FunctionCall(
                func=('__std__', 'json_typeof'), args=[source_path]
            )
            filtered = qlast.SelectQuery(
                result=source_path,
                where=qlast.BinOp(
                    left=check,
                    op='!=',
                    right=qlast.StringConstant(value='null'),
                )
            )
            filtered_ir = dispatch.compile(filtered, ctx=subctx)
            source_path = subctx.create_anchor(filtered_ir, 'a')

        # TODO: try using jsonb_to_record instead of a bunch of
        # json_get calls and see if that is faster.
        elements = []
        for new_el_name, new_st in new_stype.iter_subtypes(ctx.env.schema):
            val_e = qlast.FunctionCall(
                func=('__std__', 'json_get'),
                args=[
                    source_path,
                    qlast.StringConstant(value=new_el_name),
                ],
            )

            val = dispatch.compile(val_e, ctx=subctx)

            val = compile_cast(
                val, new_st,
                cardinality_mod=qlast.CardinalityModifier.Required,
                ctx=subctx, srcctx=srcctx)

            elements.append(irast.TupleElement(name=new_el_name, val=val))

        return setgen.new_tuple_set(
            elements,
            named=new_stype.is_named(ctx.env.schema),
            ctx=subctx,
        )
Esempio n. 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])
Esempio n. 3
0
def _cast_json_to_tuple(ir_set: irast.Set, orig_stype: s_types.Type,
                        new_stype: s_types.Tuple, *,
                        srcctx: Optional[parsing.ParserContext],
                        ctx: context.ContextLevel) -> irast.Set:

    with ctx.new() as subctx:
        subctx.anchors = subctx.anchors.copy()
        source_alias = subctx.aliases.get('a')
        subctx.anchors[source_alias] = ir_set

        # TODO: try using jsonb_to_record instead of a bunch of
        # json_get calls and see if that is faster.
        elements = []
        for new_el_name, new_st in new_stype.iter_subtypes(ctx.env.schema):
            val_e = qlast.FunctionCall(
                func=('__std__', 'json_get'),
                args=[
                    qlast.Path(steps=[qlast.ObjectRef(name=source_alias)]),
                    qlast.StringConstant(value=new_el_name),
                ],
            )

            val = dispatch.compile(val_e, ctx=subctx)

            val = compile_cast(
                val,
                new_st,
                cardinality_mod=qlast.CardinalityModifier.Required,
                ctx=subctx,
                srcctx=srcctx)

            elements.append(irast.TupleElement(name=new_el_name, val=val))

        return setgen.new_tuple_set(
            elements,
            named=new_stype.is_named(ctx.env.schema),
            ctx=subctx,
        )