Skip to content

Commit 3c71be8

Browse files
fix: error referencing non-returned vars in ORDER BY
- Fixes AGV2-324
1 parent f4d0c5a commit 3c71be8

File tree

5 files changed

+288
-129
lines changed

5 files changed

+288
-129
lines changed

src/backend/parser/parse_cypher_expr.c

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2526,32 +2526,67 @@ transformCypherOrderBy(ParseState *pstate, List *sortitems, List **targetlist)
25262526
foreach(lsi, sortitems)
25272527
{
25282528
SortBy *sortby = lfirst(lsi);
2529-
Node *expr;
2529+
Node *expr = NULL;
25302530
ListCell *lt;
25312531
TargetEntry *te = NULL;
25322532

2533-
expr = transformCypherExpr(pstate, sortby->node, exprKind);
2534-
2535-
foreach(lt, *targetlist)
2533+
/*
2534+
* First, check if this is a simple column reference that matches
2535+
* an alias in the target list. This allows ORDER BY to reference
2536+
* RETURN clause aliases (e.g., "RETURN p.name AS name ORDER BY name")
2537+
*/
2538+
if (sortby->node && IsA(sortby->node, ColumnRef))
25362539
{
2537-
TargetEntry *tmp;
2538-
Node *texpr;
2540+
ColumnRef *cref = (ColumnRef *) sortby->node;
25392541

2540-
tmp = lfirst(lt);
2541-
texpr = strip_implicit_coercions((Node *) tmp->expr);
2542-
if (equal(texpr, expr))
2542+
if (list_length(cref->fields) == 1 &&
2543+
IsA(linitial(cref->fields), String))
25432544
{
2544-
te = tmp;
2545-
break;
2545+
char *colname = strVal(linitial(cref->fields));
2546+
2547+
/* Try to find a matching alias in the target list */
2548+
foreach(lt, *targetlist)
2549+
{
2550+
TargetEntry *tmp = lfirst(lt);
2551+
2552+
if (tmp->resname && strcmp(tmp->resname, colname) == 0)
2553+
{
2554+
te = tmp;
2555+
break;
2556+
}
2557+
}
25462558
}
25472559
}
25482560

2561+
/*
2562+
* If not found as an alias, transform the expression and check if
2563+
* it matches an expression in the target list or add it as resjunk
2564+
*/
25492565
if (te == NULL)
25502566
{
2551-
te = transformTargetEntry(pstate, sortby->node, expr, exprKind,
2552-
NULL, true);
2567+
expr = transformCypherExpr(pstate, sortby->node, exprKind);
2568+
2569+
foreach(lt, *targetlist)
2570+
{
2571+
TargetEntry *tmp;
2572+
Node *texpr;
25532573

2554-
*targetlist = lappend(*targetlist, te);
2574+
tmp = lfirst(lt);
2575+
texpr = strip_implicit_coercions((Node *) tmp->expr);
2576+
if (equal(texpr, expr))
2577+
{
2578+
te = tmp;
2579+
break;
2580+
}
2581+
}
2582+
2583+
if (te == NULL)
2584+
{
2585+
te = transformTargetEntry(pstate, sortby->node, expr, exprKind,
2586+
NULL, true);
2587+
2588+
*targetlist = lappend(*targetlist, te);
2589+
}
25552590
}
25562591

25572592
sortgroups = addTargetToSortList(pstate, te, sortgroups, *targetlist,

src/backend/parser/parse_graph.c

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "postgres.h"
1212

1313
#include "access/htup_details.h"
14+
#include "access/stratnum.h"
1415
#include "ag_const.h"
1516
#include "catalog/ag_graph_fn.h"
1617
#include "catalog/ag_label.h"
@@ -48,6 +49,7 @@
4849
#include "access/genam.h"
4950
#include "parser/parse_shortestpath.h"
5051
#include "catalog/ag_vertex_d.h"
52+
#include "optimizer/optimizer.h"
5153
#include "catalog/ag_edge_d.h"
5254

5355
#define EDGE_UNION_START_ID "_start"
@@ -140,6 +142,7 @@ typedef struct
140142

141143
/* projection (RETURN and WITH) */
142144
static void checkNameInItems(ParseState *pstate, List *items, List *targetList);
145+
static void updateSortOperatorsForJsonb(List *sortClause, List *targetList);
143146

144147
/* MATCH - OPTIONAL */
145148
static ParseNamespaceItem *transformMatchOptional(ParseState *pstate,
@@ -451,6 +454,55 @@ transformCypherSubPattern(ParseState *pstate, CypherSubPattern *subpat)
451454
return qry;
452455
}
453456

457+
static void
458+
updateSortOperatorsForJsonb(List *sortClause, List *targetList)
459+
{
460+
ListCell *lc;
461+
462+
foreach(lc, sortClause)
463+
{
464+
SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
465+
TargetEntry *tle;
466+
Oid restype;
467+
Oid sortop;
468+
Oid eqop;
469+
bool hashable;
470+
Oid opfamily;
471+
Oid opcintype;
472+
int16 strategy;
473+
474+
tle = get_sortgroupref_tle(sortcl->tleSortGroupRef, targetList);
475+
if (!tle)
476+
continue;
477+
478+
restype = exprType((Node *) tle->expr);
479+
480+
if (!get_ordering_op_properties(sortcl->sortop,
481+
&opfamily, &opcintype, &strategy))
482+
continue;
483+
484+
if (strategy == BTLessStrategyNumber)
485+
{
486+
get_sort_group_operators(restype,
487+
true, true, false,
488+
&sortop, &eqop, NULL,
489+
&hashable);
490+
}
491+
else if (strategy == BTGreaterStrategyNumber)
492+
{
493+
get_sort_group_operators(restype,
494+
false, true, true,
495+
NULL, &eqop, &sortop,
496+
&hashable);
497+
}
498+
else
499+
continue;
500+
501+
sortcl->eqop = eqop;
502+
sortcl->sortop = sortop;
503+
}
504+
}
505+
454506
Query *
455507
transformCypherProjection(ParseState *pstate, CypherClause *clause)
456508
{
@@ -478,68 +530,43 @@ transformCypherProjection(ParseState *pstate, CypherClause *clause)
478530
qual = transformCypherWhere(pstate, where, EXPR_KIND_WHERE);
479531
qual = resolve_future_vertex(pstate, qual, 0);
480532
}
481-
else if (detail->distinct != NULL || detail->order != NULL ||
482-
detail->skip != NULL || detail->limit != NULL)
533+
else
483534
{
484-
List *distinct = detail->distinct;
485-
List *order = detail->order;
486-
Node *skip = detail->skip;
487-
Node *limit = detail->limit;
488-
489-
/*
490-
* detach options so that this function passes through this if
491-
* statement when the function is called again recursively
492-
*/
493-
detail->distinct = NIL;
494-
detail->order = NIL;
495-
detail->skip = NULL;
496-
detail->limit = NULL;
497-
nsitem = transformClause(pstate, (Node *) clause);
498-
detail->distinct = distinct;
499-
detail->order = order;
500-
detail->skip = skip;
501-
detail->limit = limit;
535+
if (clause->prev != NULL)
536+
transformClause(pstate, clause->prev);
502537

503-
qry->targetList = makeTargetListFromNSItem(pstate, nsitem);
538+
qry->targetList = transformItemList(pstate, detail->items,
539+
EXPR_KIND_SELECT_TARGET);
504540

505-
qry->sortClause = transformCypherOrderBy(pstate, order,
506-
&qry->targetList);
541+
if (detail->kind == CP_WITH)
542+
checkNameInItems(pstate, detail->items, qry->targetList);
507543

508-
if (distinct == NIL)
544+
if (detail->order)
509545
{
510-
/* intentionally blank, do nothing */
546+
qry->sortClause = transformCypherOrderBy(pstate, detail->order,
547+
&qry->targetList);
511548
}
512-
else
549+
550+
if (detail->distinct)
513551
{
514-
Assert(linitial(distinct) == NULL);
552+
Assert(linitial(detail->distinct) == NULL);
515553

516554
qry->distinctClause = transformDistinctClause(pstate,
517555
&qry->targetList,
518556
qry->sortClause,
519557
false);
520558
}
521559

522-
qry->limitOffset = transformCypherLimit(pstate, skip, EXPR_KIND_OFFSET,
523-
"SKIP");
524-
qry->limitOffset = resolve_future_vertex(pstate, qry->limitOffset, 0);
525-
526-
qry->limitCount = transformCypherLimit(pstate, limit, EXPR_KIND_LIMIT,
527-
"LIMIT");
560+
qry->limitCount = transformCypherLimit(pstate, detail->limit,
561+
EXPR_KIND_LIMIT, "LIMIT");
528562
qry->limitCount = resolve_future_vertex(pstate, qry->limitCount, 0);
529-
}
530-
else
531-
{
532-
if (clause->prev != NULL)
533-
transformClause(pstate, clause->prev);
534563

535-
qry->targetList = transformItemList(pstate, detail->items,
536-
EXPR_KIND_SELECT_TARGET);
537-
538-
if (detail->kind == CP_WITH)
539-
checkNameInItems(pstate, detail->items, qry->targetList);
564+
qry->limitOffset = transformCypherLimit(pstate, detail->skip,
565+
EXPR_KIND_OFFSET, "SKIP");
566+
qry->limitOffset = resolve_future_vertex(pstate, qry->limitOffset, 0);
540567

541568
qry->groupClause = generateGroupClause(pstate, &qry->targetList,
542-
qry->sortClause);
569+
qry->sortClause);
543570
}
544571

545572
if (detail->kind == CP_WITH)
@@ -570,6 +597,7 @@ transformCypherProjection(ParseState *pstate, CypherClause *clause)
570597
{
571598
flags = 0;
572599
}
600+
573601
qry->targetList = (List *) resolve_future_vertex(pstate,
574602
(Node *) qry->targetList,
575603
flags);
@@ -578,7 +606,16 @@ transformCypherProjection(ParseState *pstate, CypherClause *clause)
578606
qual = qualAndExpr(qual, pstate->p_resolved_qual);
579607

580608
if (detail->kind == CP_RETURN)
609+
{
581610
resolveItemList(pstate, qry->targetList);
611+
/*
612+
* After applying JSONB coercion, update sort operators in
613+
* sortClause to use JSONB comparison operators instead
614+
* of the original type's operators
615+
*/
616+
if (qry->sortClause)
617+
updateSortOperatorsForJsonb(qry->sortClause, qry->targetList);
618+
}
582619

583620
qry->rtable = pstate->p_rtable;
584621
qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
@@ -3744,6 +3781,9 @@ resolve_future_vertex(ParseState *pstate, Node *node, int flags)
37443781
{
37453782
resolve_future_vertex_context ctx;
37463783

3784+
if (node == NULL)
3785+
return NULL;
3786+
37473787
ctx.pstate = pstate;
37483788
ctx.flags = flags;
37493789
ctx.sublevels_up = 0;

src/test/regress/expected/cypher_dml.out

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4154,6 +4154,54 @@ MATCH (v1:new_v1)-[e1:new_e1]->(v2:new_v1) RETURN v1,e1,v2;
41544154
new_v1[3.3]{} | new_e1[4.2][3.3,3.4]{} | new_v1[3.4]{}
41554155
(2 rows)
41564156

4157+
--
4158+
-- AGV2-324
4159+
--
4160+
CREATE GRAPH ag324;
4161+
SET graph_path = ag324;
4162+
CREATE (:person {name: 'Alice', age: 30}),
4163+
(:person {name: 'Bob', age: 25}),
4164+
(:person {name: 'Charlie', age: 35});
4165+
MATCH (p:person)
4166+
RETURN p.name AS name
4167+
ORDER BY p.age;
4168+
name
4169+
-----------
4170+
"Bob"
4171+
"Alice"
4172+
"Charlie"
4173+
(3 rows)
4174+
4175+
MATCH (p:person)
4176+
RETURN p.name AS name
4177+
ORDER BY p.age DESC;
4178+
name
4179+
-----------
4180+
"Charlie"
4181+
"Alice"
4182+
"Bob"
4183+
(3 rows)
4184+
4185+
MATCH (p:person)
4186+
RETURN p.name AS name
4187+
ORDER BY name, p.age;
4188+
name
4189+
-----------
4190+
"Alice"
4191+
"Bob"
4192+
"Charlie"
4193+
(3 rows)
4194+
4195+
MATCH (p:person)
4196+
RETURN p.name AS name
4197+
ORDER BY p.age, name DESC;
4198+
name
4199+
-----------
4200+
"Bob"
4201+
"Alice"
4202+
"Charlie"
4203+
(3 rows)
4204+
41574205
-- cleanup
41584206
DROP GRAPH srf CASCADE;
41594207
NOTICE: drop cascades to 5 other objects
@@ -4238,6 +4286,12 @@ drop cascades to vlabel ag_vertex
42384286
drop cascades to elabel ag_edge
42394287
drop cascades to vlabel person
42404288
drop cascades to elabel knows
4289+
DROP GRAPH ag324 CASCADE;
4290+
NOTICE: drop cascades to 4 other objects
4291+
DETAIL: drop cascades to sequence ag324.ag_label_seq
4292+
drop cascades to vlabel ag_vertex
4293+
drop cascades to elabel ag_edge
4294+
drop cascades to vlabel person
42414295
SET graph_path = agens;
42424296
DROP VLABEL feature;
42434297
DROP ELABEL supported;

0 commit comments

Comments
 (0)