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, )
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])
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, )