Skip to content

Commit 28b1be2

Browse files
Copilotmathiasrw
andauthored
ORDER BY with numeric column ref on SELECT * to fix #125 (#2189)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: mathiasrw <[email protected]> Co-authored-by: Mathias Wulff <[email protected]>
1 parent 291a762 commit 28b1be2

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

src/424select.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,11 @@ yy.Select.prototype.compileSelect2 = function (query, params) {
357357
if (this.orderColumns && this.orderColumns.length > 0) {
358358
this.orderColumns.forEach(function (v, idx) {
359359
var key = '$$$' + idx;
360-
if (v instanceof yy.Column && query.xcolumns[v.columnid]) {
360+
// Handle positional column reference (for SELECT * with ORDER BY numeric)
361+
if (v._useColumnIndex !== undefined) {
362+
// Use Object.keys to get column names and access by index
363+
s += "var keys=Object.keys(r);r['" + key + "']=r[keys[" + v.columnIndex + ']];';
364+
} else if (v instanceof yy.Column && query.xcolumns[v.columnid]) {
361365
s += "r['" + key + "']=r['" + v.columnid + "'];";
362366
} else if (v instanceof yy.ParamValue && query.xcolumns[params[v.param]]) {
363367
s += "r['" + key + "']=r['" + params[v.param] + "'];";
@@ -528,7 +532,11 @@ yy.Select.prototype.compileSelectGroup2 = function (query) {
528532
// console.log(411,v);
529533
var key = '$$$' + idx;
530534
// console.log(427,v,query.groupColumns,query.xgroupColumns);
531-
if (v instanceof yy.Column && query.groupColumns[v.columnid]) {
535+
// Handle positional column reference (for SELECT * with ORDER BY numeric)
536+
if (v._useColumnIndex !== undefined) {
537+
// Use Object.keys to get column names and access by index
538+
s += "var keys=Object.keys(r);r['" + key + "']=r[keys[" + v.columnIndex + ']];';
539+
} else if (v instanceof yy.Column && query.groupColumns[v.columnid]) {
532540
s += "r['" + key + "']=r['" + v.columnid + "'];";
533541
} else {
534542
s += "r['" + key + "']=" + v.toJS('g', '') + ';';

src/426orderby.js

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,36 @@ yy.Select.prototype.compileOrder = function (query, params) {
3535
// console.log(ord.expression instanceof yy.Column);
3636

3737
if (ord.expression instanceof yy.NumValue) {
38-
if (ord.expression.value > self.columns.length) {
38+
// Validate that column number is at least 1
39+
if (ord.expression.value < 1) {
3940
throw new Error(
40-
`You are trying to order by column number ${ord.expression.value} but you have only selected ${self.columns.length} columns.`
41+
`Invalid column number ${ord.expression.value}. Column numbers must be at least 1.`
4142
);
4243
}
44+
4345
var v = self.columns[ord.expression.value - 1];
46+
// Check if we're dealing with SELECT * case
47+
var hasWildcard =
48+
self.columns.length === 1 &&
49+
self.columns[0] instanceof yy.Column &&
50+
self.columns[0].columnid === '*';
51+
52+
if (hasWildcard) {
53+
// With SELECT *, use positional ordering (resolved at runtime)
54+
// Skip validation as we don't know the column count at compile time
55+
v = {_useColumnIndex: true, columnIndex: ord.expression.value - 1};
56+
} else {
57+
// With explicit columns, validate the column number
58+
if (ord.expression.value > self.columns.length) {
59+
throw new Error(
60+
`You are trying to order by column number ${ord.expression.value} but you have only selected ${self.columns.length} columns.`
61+
);
62+
}
63+
// Also check if the resolved column is a wildcard (shouldn't happen but be safe)
64+
if (v instanceof yy.Column && v.columnid === '*') {
65+
v = {_useColumnIndex: true, columnIndex: ord.expression.value - 1};
66+
}
67+
}
4468
} else {
4569
var v = ord.expression;
4670
}

test/test125B.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
if (typeof exports === 'object') {
2+
var assert = require('assert');
3+
var alasql = require('..');
4+
}
5+
6+
describe('Test 125B - ORDER BY with numeric column reference', function () {
7+
it('1. ORDER BY 1 with simple SELECT', function (done) {
8+
var data = [
9+
{name: 'John', age: 30, city: 'New York'},
10+
{name: 'Jane', age: 25, city: 'Boston'},
11+
{name: 'Bob', age: 35, city: 'Chicago'},
12+
];
13+
14+
var res = alasql('SELECT name, age, city FROM ? ORDER BY 1', [data]);
15+
assert.deepEqual(res, [
16+
{name: 'Bob', age: 35, city: 'Chicago'},
17+
{name: 'Jane', age: 25, city: 'Boston'},
18+
{name: 'John', age: 30, city: 'New York'},
19+
]);
20+
done();
21+
});
22+
23+
it('2. ORDER BY 2 DESC with simple SELECT', function (done) {
24+
var data = [
25+
{name: 'John', age: 30, city: 'New York'},
26+
{name: 'Jane', age: 25, city: 'Boston'},
27+
{name: 'Bob', age: 35, city: 'Chicago'},
28+
];
29+
30+
var res = alasql('SELECT name, age, city FROM ? ORDER BY 2 DESC', [data]);
31+
assert.deepEqual(res, [
32+
{name: 'Bob', age: 35, city: 'Chicago'},
33+
{name: 'John', age: 30, city: 'New York'},
34+
{name: 'Jane', age: 25, city: 'Boston'},
35+
]);
36+
done();
37+
});
38+
39+
it('3. ORDER BY 1, 2 with multiple columns', function (done) {
40+
var data = [
41+
{name: 'John', age: 30, city: 'New York'},
42+
{name: 'Jane', age: 25, city: 'Boston'},
43+
{name: 'John', age: 25, city: 'Chicago'},
44+
];
45+
46+
var res = alasql('SELECT name, age FROM ? ORDER BY 1, 2', [data]);
47+
assert.deepEqual(res, [
48+
{name: 'Jane', age: 25},
49+
{name: 'John', age: 25},
50+
{name: 'John', age: 30},
51+
]);
52+
done();
53+
});
54+
55+
it('4. ORDER BY 1 with SELECT *', function (done) {
56+
var data = [
57+
{name: 'John', age: 30},
58+
{name: 'Jane', age: 25},
59+
{name: 'Bob', age: 35},
60+
];
61+
62+
var res = alasql('SELECT * FROM ? ORDER BY 1', [data]);
63+
assert.deepEqual(res, [
64+
{name: 'Bob', age: 35},
65+
{name: 'Jane', age: 25},
66+
{name: 'John', age: 30},
67+
]);
68+
done();
69+
});
70+
});

0 commit comments

Comments
 (0)