def compile_shape(ir_set: irast.Set, shape: List[Tuple[irast.Set, qlast.ShapeOp]], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: elements = [] with ctx.newscope() as shapectx: shapectx.disable_semi_join.add(ir_set.path_id) if isinstance(ir_set.expr, irast.Stmt): # The source set for this shape is a FOR statement, # which is special in that besides set path_id it # should also expose the path_id of the FOR iterator # so that shape element expressions that might contain # an iterator reference find it properly. # # So, for: # SELECT Bar { # foo := (FOR x := ... UNION Foo { spam := x }) # } # # the path scope when processing the shape of Bar.foo # should be {'Bar.foo', 'x'}. iterator = ir_set.expr.iterator_stmt if iterator: shapectx.path_scope[iterator.path_id] = ctx.rel for el, _ in shape: rptr = el.rptr assert rptr is not None ptrref = rptr.ptrref is_singleton = ptrref.dir_cardinality.is_single() value: pgast.BaseExpr if (irutils.is_subquery_set(el) or el.path_id.is_objtype_path() or not is_singleton or not ptrref.required): wrapper = relgen.set_as_subquery(el, as_value=True, ctx=shapectx) if not is_singleton: value = relgen.set_to_array(ir_set=el, query=wrapper, ctx=shapectx) else: value = wrapper else: value = dispatch.compile(el, ctx=shapectx) tuple_el = astutils.tuple_element_for_shape_el(el, value, ctx=shapectx) assert isinstance(tuple_el, pgast.TupleElement) elements.append(tuple_el) return pgast.TupleVar(elements=elements, named=True)
def compile_shape(ir_set: irast.Set, shape: Sequence[Tuple[irast.Set, qlast.ShapeOp]], *, ctx: context.CompilerContextLevel) -> pgast.TupleVar: elements = [] # If the object identity is potentially nullable, filter it out # to prevent shapes with bogusly null insides. var = pathctx.get_path_value_var(ctx.rel, path_id=ir_set.path_id, env=ctx.env) if var.nullable: ctx.rel.where_clause = astutils.extend_binop( ctx.rel.where_clause, pgast.NullTest(arg=var, negated=True)) with ctx.newscope() as shapectx: shapectx.disable_semi_join |= {ir_set.path_id} if isinstance(ir_set.expr, irast.Stmt): # The source set for this shape is a FOR statement, # which is special in that besides set path_id it # should also expose the path_id of the FOR iterator # so that shape element expressions that might contain # an iterator reference find it properly. # # So, for: # SELECT Bar { # foo := (FOR x := ... UNION Foo { spam := x }) # } # # the path scope when processing the shape of Bar.foo # should be {'Bar.foo', 'x'}. iterator = ir_set.expr.iterator_stmt if iterator: shapectx.path_scope[iterator.path_id] = ctx.rel for el, op in shape: if op == qlast.ShapeOp.MATERIALIZE and not ctx.materializing: continue rptr = el.rptr assert rptr is not None ptrref = rptr.ptrref # As an implementation expedient, we currently represent # AT_MOST_ONE materialized values with arrays card = rptr.dir_cardinality is_singleton = (card.is_single() and (not ctx.materializing or not card.can_be_zero())) value: pgast.BaseExpr if (irutils.is_subquery_set(el) or el.path_id.is_objtype_path() or not is_singleton or not ptrref.required): wrapper = relgen.set_as_subquery(el, as_value=True, ctx=shapectx) if not is_singleton: value = relctx.set_to_array(path_id=el.path_id, query=wrapper, ctx=shapectx) else: value = wrapper else: value = dispatch.compile(el, ctx=shapectx) tuple_el = astutils.tuple_element_for_shape_el(el, value, ctx=shapectx) assert isinstance(tuple_el, pgast.TupleElement) elements.append(tuple_el) return pgast.TupleVar(elements=elements, named=True)