def tuple_indirection_set(path_tip: irast.Set, *, source: s_sources.Source, ptr_name: str, source_context: parsing.ParserContext, ctx: context.ContextLevel) -> irast.Set: el_name = ptr_name el_norm_name = source.normalize_index(ctx.env.schema, el_name) el_type = source.get_subtype(ctx.env.schema, el_name) path_id = pathctx.get_tuple_indirection_path_id(path_tip.path_id, el_norm_name, el_type, ctx=ctx) expr = irast.TupleIndirection(expr=path_tip, name=el_norm_name, path_id=path_id, context=source_context) return expression_set(expr, ctx=ctx)
def _cast_tuple(ir_set: irast.Base, orig_stype: s_types.Type, new_stype: s_types.Type, *, srcctx: parsing.ParserContext, ctx: context.ContextLevel) -> irast.Base: # Make sure the source tuple expression is pinned in the scope, # so that we don't generate a cross-product of it by evaluating # the tuple indirections. pathctx.register_set_in_scope(ir_set, ctx=ctx) direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx) orig_subtypes = dict(orig_stype.iter_subtypes(ctx.env.schema)) if direct_cast is not None: # Direct casting to non-tuple involves casting each tuple # element and also keeping the cast around the whole tuple. # This is to trigger the downstream logic of casting # objects (in elements of the tuple). elements = [] for i, (n, st) in enumerate(orig_subtypes.items()): path_id = pathctx.get_tuple_indirection_path_id(ir_set.path_id, n, st, ctx=ctx) val = setgen.ensure_set(irast.TupleIndirection(expr=ir_set, name=n), path_id=path_id, ctx=ctx) val_type = inference.infer_type(val, ctx.env) # Element cast val = compile_cast(val, new_stype, ctx=ctx, srcctx=srcctx) elements.append(irast.TupleElement(name=n, val=val)) new_tuple = setgen.new_tuple_set(elements, named=orig_stype.named, ctx=ctx) return _cast_to_ir(new_tuple, direct_cast, orig_stype, new_stype, ctx=ctx) if not new_stype.is_tuple(): 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) new_subtypes = list(new_stype.iter_subtypes(ctx.env.schema)) if len(orig_subtypes) != len(new_subtypes): raise errors.QueryError( f'cannot cast {orig_stype.get_displayname(ctx.env.schema)!r} ' f'to {new_stype.get_displayname(ctx.env.schema)!r}: ', f'the number of elements is not the same', context=srcctx) # For tuple-to-tuple casts we generate a new tuple # to simplify things on sqlgen side. elements = [] for i, (n, orig_st) in enumerate(orig_subtypes.items()): path_id = pathctx.get_tuple_indirection_path_id(ir_set.path_id, n, orig_st, ctx=ctx) val = setgen.ensure_set(irast.TupleIndirection(expr=ir_set, name=n), path_id=path_id, ctx=ctx) val_type = inference.infer_type(val, ctx.env) new_el_name, new_st = new_subtypes[i] if val_type != new_st: # Element cast val = compile_cast(val, new_st, ctx=ctx, srcctx=srcctx) elements.append(irast.TupleElement(name=new_el_name, val=val)) return setgen.new_tuple_set(elements, named=new_stype.named, ctx=ctx)