def visit_order(self, node): if not isinstance(node, gql_ast.ObjectValue): raise g_errors.GraphQLTranslationError( f'an object is expected for "order"') # if there is no specific ordering, then order by id if not node.fields: return [ qlast.SortExpr( path=qlast.Path( steps=[qlast.Ptr(ptr=qlast.ObjectRef(name='id'))], partial=True, ), direction=qlast.SortAsc, ) ] # Ordering is handled by specifying a list of special Ordering objects. # Validation is already handled by this point. orderby = [] for enum in node.fields: name, direction, nulls = self._visit_order_item(enum) orderby.append( qlast.SortExpr( path=qlast.Path( steps=[qlast.Ptr(ptr=qlast.ObjectRef(name=name))], partial=True, ), direction=direction, nones_order=nulls, )) return orderby
def eta_expand_ordered( expr: qlast.Expr, stype: s_types.Type, *, ctx: context.ContextLevel, ) -> qlast.Expr: """Do an order-preserving η-expansion Unlike in the lambda calculus, edgeql is a set-based language with a notion of ordering, which we need to preserve. We do this by using enumerate and ORDER BY on it: EXPAND_ORDERED(t, e) = WITH enum := enumerate(e) SELECT EXPAND(t, enum.1) ORDER BY enum.0 """ enumerated = qlast.FunctionCall(func=('__std__', 'enumerate'), args=[expr]) enumerated_alias, enumerated_path = _get_alias('enum', ctx=ctx) element_path = astutils.extend_path(enumerated_path, '1') result_expr = eta_expand(element_path, stype, ctx=ctx) return qlast.SelectQuery( result=result_expr, orderby=[ qlast.SortExpr(path=astutils.extend_path(enumerated_path, '0')) ], aliases=[qlast.AliasedExpr(alias=enumerated_alias, expr=enumerated)], )
def _cast_array(ir_set: irast.Set, orig_stype: s_types.Type, new_stype: s_types.Type, *, srcctx: Optional[parsing.ParserContext], ctx: context.ContextLevel) -> irast.Set: assert isinstance(orig_stype, s_types.Array) direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx) if direct_cast is None: if not new_stype.is_array(): 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) assert isinstance(new_stype, s_types.Array) el_type = new_stype.get_subtypes(ctx.env.schema)[0] else: el_type = new_stype orig_el_type = orig_stype.get_subtypes(ctx.env.schema)[0] el_cast = _find_cast(orig_el_type, el_type, srcctx=srcctx, ctx=ctx) if el_cast is not None and el_cast.get_from_cast(ctx.env.schema): # Simple cast return _cast_to_ir(ir_set, el_cast, orig_stype, new_stype, ctx=ctx) else: pathctx.register_set_in_scope(ir_set, ctx=ctx) with ctx.new() as subctx: subctx.anchors = subctx.anchors.copy() source_alias = subctx.aliases.get('a') subctx.anchors[source_alias] = ir_set unpacked = qlast.FunctionCall( func=('__std__', 'array_unpack'), args=[ qlast.Path(steps=[qlast.ObjectRef(name=source_alias)], ), ], ) enumerated = setgen.ensure_set( dispatch.compile( qlast.FunctionCall( func=('__std__', 'enumerate'), args=[unpacked], ), ctx=subctx, ), ctx=subctx, ) enumerated_alias = subctx.aliases.get('e') subctx.anchors[enumerated_alias] = enumerated enumerated_ref = qlast.Path( steps=[qlast.ObjectRef(name=enumerated_alias)], ) elements = qlast.FunctionCall( func=('__std__', 'array_agg'), args=[ qlast.SelectQuery( result=qlast.TypeCast( expr=qlast.Path(steps=[ enumerated_ref, qlast.Ptr(ptr=qlast.ObjectRef( name='1', direction='>', ), ), ], ), type=typegen.type_to_ql_typeref( el_type, ctx=subctx, ), cardinality_mod=qlast.CardinalityModifier.Required, ), orderby=[ qlast.SortExpr( path=qlast.Path(steps=[ enumerated_ref, qlast.Ptr(ptr=qlast.ObjectRef( name='0', direction='>', ), ), ], ), direction=qlast.SortOrder.Asc, ), ], ), ], ) array_ir = dispatch.compile(elements, ctx=subctx) assert isinstance(array_ir, irast.Set) if direct_cast is not None: ctx.env.schema, array_stype = s_types.Array.from_subtypes( ctx.env.schema, [el_type]) return _cast_to_ir(array_ir, direct_cast, array_stype, new_stype, ctx=ctx) else: return array_ir
def reduce_Expr_OptDirection_OptNonesOrder(self, *kids): self.val = qlast.SortExpr(path=kids[0].val, direction=kids[1].val, nones_order=kids[2].val)
def visit_SortExpr(self, node): result = qlast.SortExpr(path=self.visit(node.expr), direction=node.direction, nones_order=node.nones_order) return result
def _cast_array( ir_set: irast.Set, orig_stype: s_types.Type, new_stype: s_types.Type, *, srcctx: Optional[parsing.ParserContext], ctx: context.ContextLevel) -> irast.Set: assert isinstance(orig_stype, s_types.Array) direct_cast = _find_cast(orig_stype, new_stype, srcctx=srcctx, ctx=ctx) if direct_cast is None: if not new_stype.is_array(): 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) assert isinstance(new_stype, s_types.Array) el_type = new_stype.get_subtypes(ctx.env.schema)[0] else: el_type = new_stype orig_el_type = orig_stype.get_subtypes(ctx.env.schema)[0] el_cast = _find_cast(orig_el_type, el_type, srcctx=srcctx, ctx=ctx) if el_cast is not None and el_cast.get_from_cast(ctx.env.schema): # Simple cast return _cast_to_ir( ir_set, el_cast, orig_stype, new_stype, ctx=ctx) else: with ctx.new() as subctx: subctx.anchors = subctx.anchors.copy() source_path = subctx.create_anchor(ir_set, 'a') unpacked = qlast.FunctionCall( func=('__std__', 'array_unpack'), args=[source_path], ) enumerated = dispatch.compile( qlast.FunctionCall( func=('__std__', 'enumerate'), args=[unpacked], ), ctx=subctx, ) enumerated_ref = subctx.create_anchor(enumerated, 'e') elements = qlast.FunctionCall( func=('__std__', 'array_agg'), args=[ qlast.SelectQuery( result=qlast.TypeCast( expr=astutils.extend_path(enumerated_ref, '1'), type=typegen.type_to_ql_typeref( el_type, ctx=subctx, ), cardinality_mod=qlast.CardinalityModifier.Required, ), orderby=[ qlast.SortExpr( path=astutils.extend_path(enumerated_ref, '0'), direction=qlast.SortOrder.Asc, ), ], ), ], ) # Force the elements to be correlated with whatever the # anchor was. (Doing it this way ensures a NULL check, # and just registering it in the scope would not.) correlated_elements = astutils.extend_path( qlast.Tuple(elements=[source_path, elements]), '1' ) if el_type.contains_json(subctx.env.schema): subctx.inhibit_implicit_limit = True array_ir = dispatch.compile(correlated_elements, ctx=subctx) assert isinstance(array_ir, irast.Set) if direct_cast is not None: ctx.env.schema, array_stype = s_types.Array.from_subtypes( ctx.env.schema, [el_type]) return _cast_to_ir( array_ir, direct_cast, array_stype, new_stype, ctx=ctx ) else: return array_ir