def tuple_indirection_set( path_tip: irast.Set, *, source: s_sources.Source, ptr_name: typing.Tuple[str, str], source_context: parsing.ParserContext, ctx: context.ContextLevel) -> irast.Set: if ptr_name[0] is not None: el_name = '::'.join(ptr_name) else: el_name = ptr_name[1] if el_name in source.element_types: path_id = irutils.tuple_indirection_path_id( path_tip.path_id, el_name, source.element_types[el_name]) expr = irast.TupleIndirection( expr=path_tip, name=el_name, path_id=path_id, context=source_context) else: raise errors.EdgeQLReferenceError( f'{el_name} is not a member of a tuple', context=source_context) return generated_set(expr, ctx=ctx)
def _cast_expr(ql_type: qlast.TypeName, ir_expr: irast.Base, *, source_context: parsing.ParserContext, ctx: context.ContextLevel) -> irast.Base: try: orig_type = irutils.infer_type(ir_expr, ctx.schema) except errors.EdgeQLError: # It is possible that the source expression is unresolved # if the expr is an empty set (or a coalesce of empty sets). orig_type = None if isinstance(orig_type, s_types.Tuple): # For tuple-to-tuple casts we generate a new tuple # to simplify things on sqlgen side. new_type = typegen.ql_typeref_to_type(ql_type, ctx=ctx) if not isinstance(new_type, s_types.Tuple): raise errors.EdgeQLError(f'cannot cast tuple to {new_type.name}', context=source_context) if len(orig_type.element_types) != len(new_type.element_types): raise errors.EdgeQLError( f'cannot cast to {new_type.name}: ' f'number of elements is not the same', context=source_context) new_names = list(new_type.element_types) elements = [] for i, n in enumerate(orig_type.element_types): val = setgen.generated_set(irast.TupleIndirection(expr=ir_expr, name=n), ctx=ctx) val.path_id = irutils.tuple_indirection_path_id( ir_expr.path_id, n, orig_type.element_types[n]) val_type = irutils.infer_type(val, ctx.schema) new_el_name = new_names[i] if val_type != new_type.element_types[new_el_name]: # Element cast val = _cast_expr(ql_type.subtypes[i], val, ctx=ctx, source_context=source_context) elements.append(irast.TupleElement(name=new_el_name, val=val)) return irast.Tuple(named=new_type.named, elements=elements) elif isinstance(ir_expr, irast.EmptySet): # For the common case of casting an empty set, we simply # generate a new EmptySet node of the requested type. scls = typegen.ql_typeref_to_type(ql_type, ctx=ctx) return irutils.new_empty_set(ctx.schema, scls=scls, alias=ir_expr.path_id.target.name.name) else: typ = typegen.ql_typeref_to_ir_typeref(ql_type, ctx=ctx) return setgen.ensure_set(irast.TypeCast(expr=ir_expr, type=typ), ctx=ctx)
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 = irutils.tuple_indirection_path_id(path_tip.path_id, el_norm_name, el_type, schema=ctx.env.schema) expr = irast.TupleIndirection(expr=path_tip, name=el_norm_name, path_id=path_id, context=source_context) return generated_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: direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx) 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 in enumerate(orig_stype.element_types): val = setgen.generated_set(irast.TupleIndirection(expr=ir_set, name=n), ctx=ctx) val.path_id = irutils.tuple_indirection_path_id( ir_set.path_id, n, orig_stype.element_types[n], schema=ctx.env.schema) 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.ensure_set(astutils.make_tuple( elements, named=orig_stype.named, ctx=ctx), 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) if len(orig_stype.element_types) != len(new_stype.element_types): 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. new_names = list(new_stype.element_types) elements = [] for i, n in enumerate(orig_stype.element_types): val = setgen.generated_set(irast.TupleIndirection(expr=ir_set, name=n), ctx=ctx) val.path_id = irutils.tuple_indirection_path_id( ir_set.path_id, n, orig_stype.element_types[n], schema=ctx.env.schema) val_type = inference.infer_type(val, ctx.env) new_el_name = new_names[i] new_subtypes = list(new_stype.get_subtypes()) if val_type != new_stype.element_types[new_el_name]: # Element cast val = compile_cast(val, new_subtypes[i], ctx=ctx, srcctx=srcctx) elements.append(irast.TupleElement(name=new_el_name, val=val)) return setgen.ensure_set(astutils.make_tuple(named=new_stype.named, elements=elements, ctx=ctx), ctx=ctx)