Skip to content

Commit 30bc356

Browse files
Copilotmathiasrw
andauthored
Support ROW_NUMBER() OVER (PARTITION BY) syntax for row numbering to fix #1126 (#2208)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: mathiasrw <[email protected]> Co-authored-by: Mathias Wulff <[email protected]>
1 parent 819bf48 commit 30bc356

File tree

6 files changed

+202
-19
lines changed

6 files changed

+202
-19
lines changed

src/40select.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ yy.Select = class Select {
190190
// todo?: 3. Compile SELECT clause
191191
// For ROWNUM()
192192
query.rownums = [];
193+
query.grouprownums = [];
193194

194195
this.compileSelectGroup0(query);
195196

@@ -371,6 +372,46 @@ yy.Select = class Select {
371372
}
372373
}
373374

375+
// Handle GROUP_ROW_NUMBER() and ROW_NUMBER() OVER (PARTITION BY ...) - restart numbering when grouping column(s) change
376+
if (query.grouprownums && query.grouprownums.length > 0) {
377+
for (var j = 0, jlen = query.grouprownums.length; j < jlen; j++) {
378+
var config = query.grouprownums[j];
379+
var partitionColumns;
380+
381+
// Determine which columns to partition by
382+
if (config.partitionColumns && config.partitionColumns.length > 0) {
383+
// Use explicit PARTITION BY columns
384+
partitionColumns = config.partitionColumns;
385+
} else {
386+
// Fall back to first column for GROUP_ROW_NUMBER()
387+
var columnKeys = Object.keys(res[0] || {});
388+
partitionColumns = [columnKeys[0]];
389+
}
390+
391+
var prevValues = null;
392+
var rowNum = 0;
393+
394+
for (var i = 0, ilen = res.length; i < ilen; i++) {
395+
// Get current partition key (combination of all partition columns)
396+
var currentValues = partitionColumns
397+
.map(function (col) {
398+
return res[i][col];
399+
})
400+
.join('|');
401+
402+
// Reset counter when partition changes
403+
if (i === 0 || currentValues !== prevValues) {
404+
rowNum = 1;
405+
} else {
406+
rowNum++;
407+
}
408+
409+
res[i][config.as] = rowNum;
410+
prevValues = currentValues;
411+
}
412+
}
413+
}
414+
374415
var res2 = modify(query, res);
375416

376417
if (cb) {

src/424select.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,22 @@ yy.Select.prototype.compileSelectGroup0 = function (query) {
416416
col.funcid &&
417417
(col.funcid.toUpperCase() === 'ROWNUM' || col.funcid.toUpperCase() === 'ROW_NUMBER')
418418
) {
419-
query.rownums.push(col.as);
419+
// Check if this has OVER clause with PARTITION BY
420+
if (col.over && col.over.partition) {
421+
// Window function with partition - track for post-processing
422+
query.grouprownums.push({
423+
as: col.as,
424+
partitionColumns: col.over.partition.map(function (p) {
425+
return p.columnid || p.toString();
426+
}),
427+
});
428+
} else {
429+
// Regular ROW_NUMBER without partition
430+
query.rownums.push(col.as);
431+
}
432+
}
433+
if (col.funcid && col.funcid.toUpperCase() === 'GROUP_ROW_NUMBER') {
434+
query.grouprownums.push({as: col.as, columnIndex: 0}); // Track which column to use for grouping
420435
}
421436
// console.log("colas:",colas);
422437
// }

src/55functions.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ yy.FuncValue.prototype.toString = function () {
3030
}
3131
s += ')';
3232
}
33+
34+
// Add OVER clause if present
35+
if (this.over) {
36+
s += ' ' + this.over.toString();
37+
}
38+
3339
return s;
3440
};
3541

@@ -249,6 +255,9 @@ stdlib.ROWNUM = function () {
249255
stdlib.ROW_NUMBER = function () {
250256
return '1';
251257
};
258+
stdlib.GROUP_ROW_NUMBER = function () {
259+
return '1';
260+
};
252261

253262
stdlib.SQRT = function (s) {
254263
return 'Math.sqrt(' + s + ')';

src/alasqlparser.jison

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,21 +1377,21 @@ Aggregator
13771377
;
13781378

13791379
FuncValue
1380-
: Literal LPAR (DISTINCT|ALL)? ExprList RPAR
1380+
: Literal LPAR (DISTINCT|ALL)? ExprList RPAR OverClause
13811381
{
13821382
var funcid = $1;
13831383
var exprlist = $4;
13841384
if(exprlist.length > 1 && (funcid.toUpperCase() == 'MIN' || funcid.toUpperCase() == 'MAX')) {
1385-
$$ = new yy.FuncValue({funcid: funcid, args: exprlist});
1385+
$$ = new yy.FuncValue({funcid: funcid, args: exprlist, over: $6});
13861386
} else if(alasql.aggr[$1]) {
13871387
$$ = new yy.AggrValue({aggregatorid: 'REDUCE',
1388-
funcid: funcid, expression: exprlist.pop(),distinct:($3=='DISTINCT') });
1388+
funcid: funcid, expression: exprlist.pop(),distinct:($3=='DISTINCT'), over: $6 });
13891389
} else {
1390-
$$ = new yy.FuncValue({funcid: funcid, args: exprlist});
1390+
$$ = new yy.FuncValue({funcid: funcid, args: exprlist, over: $6});
13911391
};
13921392
}
1393-
| Literal LPAR RPAR
1394-
{ $$ = new yy.FuncValue({ funcid: $1 }) }
1393+
| Literal LPAR RPAR OverClause
1394+
{ $$ = new yy.FuncValue({ funcid: $1, over: $4 }) }
13951395
| IF LPAR ExprList RPAR
13961396
{ $$ = new yy.FuncValue({ funcid: 'IIF', args:$3 }) }
13971397
| REPLACE LPAR ExprList RPAR

0 commit comments

Comments
 (0)