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
39 changes: 39 additions & 0 deletions src/44defcols.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,45 @@ yy.Select.prototype.compileDefCols = function (query, databaseid) {
}
});
}
// Subqueries in FROM / JOIN have no table definition, so register the named
// columns of their SELECT list. Without this, an unqualified column of a joined
// subquery used in an expression (e.g. c - b) resolves to the first source (#832).
// This is only done when the columns of every source are known (tables with
// column definitions, subqueries without *). With a schemaless source (FROM ?,
// CSV(), SELECT *) an unqualified name may belong to that source, so the
// previous resolution is kept. Only names not defined yet are filled.
var hasStar = function (select) {
return (select.columns || []).some(function (col) {
return col instanceof yy.Column && col.columnid === '*';
});
};
var tableHasColumns = function (tbl) {
var db = alasql.databases[tbl.databaseid || databaseid];
var table = db && db.tables[tbl.tableid];
return !!(table && table.columns && table.columns.length);
};
var subqueries = [];
var allColumnsKnown = true;
(this.from || []).forEach(function (fr) {
if (fr instanceof yy.Select && !hasStar(fr)) subqueries.push([fr, fr.as]);
else if (!(fr instanceof yy.Table && tableHasColumns(fr))) allColumnsKnown = false;
});
(this.joins || []).forEach(function (jn) {
if (jn.select && !hasStar(jn.select)) subqueries.push([jn.select, jn.as]);
else if (!(jn.table && tableHasColumns(jn.table))) allColumnsKnown = false;
});
if (allColumnsKnown) {
subqueries.forEach(function ([select, alias]) {
if (!alias || !select.columns) return;
select.columns.forEach(function (col) {
var name = col.as || (col instanceof yy.Column ? col.columnid : undefined);
if (name && name !== '_' && !(name in defcols)) {
defcols[name] = alias;
}
});
});
}

// for(var k in defcols) {
// if(defcols[k] == '-') defcols[k] = undefined;
// }
Expand Down
81 changes: 81 additions & 0 deletions test/test832.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
if (typeof exports === 'object') {
var assert = require('assert');
var alasql = require('..');
}

let testId = '832'; // Unqualified columns from a joined subquery in expressions

describe(`Test ${testId} - unqualified columns of subqueries in expressions`, function () {
before(function () {
alasql('create database test' + testId);
alasql('use test' + testId);
alasql('CREATE TABLE one (a INT, b INT)');
alasql('INSERT INTO one VALUES (1,10),(2,20)');
alasql('CREATE TABLE two (a INT, c INT)');
alasql('INSERT INTO two VALUES (1,100),(2,200)');
});

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

it('A) Expression over columns of two joined subqueries (issue example)', function () {
const res = alasql(
'SELECT t0.`a` AS a, c - b AS bc FROM (SELECT a,b FROM one) t0 JOIN (SELECT a,c FROM two) t1 ON t0.`a`=t1.`a`'
);
assert.deepStrictEqual(res, [
{a: 1, bc: 90},
{a: 2, bc: 180},
]);
});

it('B) Table joined with a subquery', function () {
const res = alasql(
'SELECT t0.a AS a, c - b AS bc FROM one t0 JOIN (SELECT a, c FROM two) t1 ON t0.a = t1.a'
);
assert.deepStrictEqual(res, [
{a: 1, bc: 90},
{a: 2, bc: 180},
]);
});

it('C) Aliased subquery columns in WHERE and ORDER BY expressions', function () {
const res = alasql(
`SELECT t0.a AS a, dbl FROM one t0
JOIN (SELECT a, c * 2 AS dbl FROM two) t1 ON t0.a = t1.a
WHERE dbl + b > 300 ORDER BY dbl + 0 DESC`
);
assert.deepStrictEqual(res, [{a: 2, dbl: 400}]);
});

it('D) Table columns keep their resolution', function () {
const res = alasql(
'SELECT t0.a AS a, b + 1 AS b1 FROM (SELECT a, c FROM two) t1 JOIN one t0 ON t0.a = t1.a'
);
assert.deepStrictEqual(res, [
{a: 1, b1: 11},
{a: 2, b1: 21},
]);
});

it('E) With a schemaless source an unqualified name keeps resolving to the first source', function () {
const A = [
{id: 1, x: 10},
{id: 2, x: 20},
];
const B = [
{id: 1, x: 100},
{id: 2, x: 200},
];
assert.deepStrictEqual(
alasql('SELECT x + 0 AS v FROM ? t0 JOIN (SELECT id, x FROM ?) t1 ON t0.id = t1.id', [A, B]),
[{v: 10}, {v: 20}]
);
assert.deepStrictEqual(
alasql(
'SELECT a + 0 AS v FROM (SELECT * FROM one) t0 JOIN (SELECT a, c FROM two) t1 ON t0.a = t1.a'
),
[{v: 1}, {v: 2}]
);
});
});