Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/40select.js
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,16 @@ yy.Select = class Select {
}
return nq;
});

// Subquery indices (queriesidx) are assigned against the statement-level
// queries list, so a subquery that references another subquery
// (e.g. a scalar subquery nested inside a subquery's WHERE clause)
// needs access to the same compiled list
query.queriesfn.forEach(function (nq) {
if (!nq.query.queriesfn) {
nq.query.queriesfn = query.queriesfn;
}
});
}
};

Expand Down
11 changes: 10 additions & 1 deletion src/420from.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,18 @@ yy.Select.prototype.compileFrom = function (query) {
if (typeof source.subquery.query.modifier === 'undefined') {
source.subquery.query.modifier = 'RECORDSET';
}
source.columns = source.subquery.query.columns;
// If the subquery could not resolve all its columns at compile time
// (e.g. SELECT * over parameter data), its columns list is incomplete,
// so leave the columns unknown and let SELECT * expand from the data
source.columns = source.subquery.query.dirtyColumns ? [] : source.subquery.query.columns;

source.datafn = (query, params, cb, idx, alasql) => {
// Subqueries parsed inside this FROM subselect (e.g. a scalar
// subquery in its column list) are hoisted to the statement-level
// queries list, so share the compiled list with the subselect
if (!source.subquery.query.queriesfn && query.queriesfn) {
source.subquery.query.queriesfn = query.queriesfn;
}
let res;
source.subquery(query.params, data => {
res = data.data;
Expand Down
116 changes: 116 additions & 0 deletions test/test1264.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
if (typeof exports === 'object') {
var assert = require('assert');
var alasql = require('..');
}

let testId = '1264';

describe(`Test ${testId} - UNION with nested SELECT`, function () {
before(function () {
alasql('create database test' + testId);
alasql('use test' + testId);
});

after(function () {
alasql('drop database test' + testId);
});

var t1 = [
{id: '1', a: 'one'},
{id: '2', a: 'two'},
{id: '3', a: 'three'},
{id: '4', a: 'four'},
];
var t2 = [
{id: '1', b: 'A'},
{id: '2', b: 'B'},
{id: '5', b: 'E'},
{id: '6', b: 'F'},
];

var expected = [
{id: '1', b: 'A'},
{id: '2', b: 'B'},
{id: '5', b: 'E'},
{id: '6', b: 'F'},
{id: '1', a: 'one', c: 4},
{id: '2', a: 'two', c: 4},
{id: '3', a: 'three', c: 4},
{id: '4', a: 'four', c: 4},
];

it('A) UNION CORRESPONDING with a scalar subquery column', function () {
var res = alasql(
'SELECT *, (SELECT COUNT(*) FROM ?) AS c FROM ? T1 UNION CORRESPONDING SELECT * FROM ?',
[t1, t1, t2]
);
assert.deepEqual(res, expected);
});

it('B) UNION CORRESPONDING with a scalar subquery column, nested in FROM', function () {
var res = alasql(
'SELECT * FROM (SELECT *, (SELECT COUNT(*) FROM ?) AS c FROM ? T1 UNION CORRESPONDING SELECT * FROM ?)',
[t1, t1, t2]
);
assert.deepEqual(res, expected);
});

it('C) UNION ALL CORRESPONDING with a scalar subquery column, nested in FROM', function () {
var res = alasql(
'SELECT * FROM (SELECT *, (SELECT COUNT(*) FROM ?) AS c FROM ? T1 UNION ALL CORRESPONDING SELECT * FROM ?)',
[t1, t1, t2]
);
assert.deepEqual(res, [
{id: '1', a: 'one', c: 4},
{id: '2', a: 'two', c: 4},
{id: '3', a: 'three', c: 4},
{id: '4', a: 'four', c: 4},
{id: '1', b: 'A'},
{id: '2', b: 'B'},
{id: '5', b: 'E'},
{id: '6', b: 'F'},
]);
});

it('D) scalar subquery column on the right side of the UNION', function () {
var res = alasql(
'SELECT * FROM (SELECT * FROM ? UNION CORRESPONDING SELECT *, (SELECT COUNT(*) FROM ?) AS c FROM ?)',
[t2, t1, t1]
);
assert.deepEqual(res, [
{id: '1', a: 'one', c: 4},
{id: '2', a: 'two', c: 4},
{id: '3', a: 'three', c: 4},
{id: '4', a: 'four', c: 4},
{id: '1', b: 'A'},
{id: '2', b: 'B'},
{id: '5', b: 'E'},
{id: '6', b: 'F'},
]);
});

it('E) scalar subquery column in a FROM subselect without UNION', function () {
var res = alasql('SELECT * FROM (SELECT *, (SELECT COUNT(*) FROM ?) AS c FROM ?)', [t1, t1]);
assert.deepEqual(res, [
{id: '1', a: 'one', c: 4},
{id: '2', a: 'two', c: 4},
{id: '3', a: 'three', c: 4},
{id: '4', a: 'four', c: 4},
]);
});

it('F) scalar subquery nested inside another scalar subquery', function () {
var data = [
{TYPE: 'CAUSE', PARENT: 'FL1', FAILURECODE: '999', FAILURELIST: 'FLX'},
{TYPE: 'PROBLEM', PARENT: 'FL0', FAILURECODE: '123', FAILURELIST: 'FL1'},
{TYPE: '', PARENT: '', FAILURECODE: '234', FAILURELIST: 'FL0'},
];
var res = alasql(
"SELECT * FROM ? WHERE TYPE = 'CAUSE' AND PARENT = " +
"(SELECT FAILURELIST FROM ? WHERE FAILURECODE = '123' AND TYPE = 'PROBLEM' AND PARENT = " +
"(SELECT FAILURELIST FROM ? WHERE FAILURECODE = '234' AND TYPE = '' AND PARENT = ''))",
[data, data, data]
);
assert.deepEqual(res, [{TYPE: 'CAUSE', PARENT: 'FL1', FAILURECODE: '999', FAILURELIST: 'FLX'}]);
});
});