Skip to content

Commit

Permalink
fix: Handle JOIN table dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
mbaumgartl committed Aug 1, 2024
1 parent a6c4843 commit 10ebae2
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 11 deletions.
33 changes: 22 additions & 11 deletions lib/sql-query-optimizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,34 @@ const getAttributeExpressions = filterTree(
* @private
*/
function getRequiredTables(ast) {
const tableExpressions = ['columns', 'where', 'groupby', 'orderby']
const tblExpressions = ['columns', 'where', 'groupby', 'orderby']
.filter((clause) => Object.hasOwn(ast, clause) && ast[clause] !== null)
.flatMap((clause) => getTableExpressions(ast[clause]));

if (ast.from.length > 1) {
ast.from
// extract joined tables that are not marked as required
.filter((expr) => {
const table = expr.as !== null ? expr.as : expr.table;
return expr.join !== undefined && tableExpressions.some((tblExpr) => tblExpr.table === table);
})
// get tables used in joins
.forEach((join) => tableExpressions.push(...getTableExpressions(join.on)));
const joins = ast.from.filter((expr) => expr.on !== undefined);
const joinTblDeps = joins.reduce((tblDeps, expr) => {
const depTblExprs = getTableExpressions(expr).filter((tblExpr) => expr.table !== tblExpr.table);
return {
...tblDeps,
...(depTblExprs.length ? { [expr.table]: depTblExprs } : {})
};
}, {});

tblExpressions.push(
// add joined tables not marked as required yet
...joins
.filter((expr) => {
const table = expr.as !== null ? expr.as : expr.table;
return tblExpressions.some((tblExpr) => tblExpr.table === table);
})
.flatMap((expr) => getTableExpressions(expr.on))
);

tblExpressions.forEach((tblExpr) => tblExpressions.push(...(joinTblDeps[tblExpr.table] ?? [])));
}

const requiredTables = tableExpressions.map(({ table }) => table);

const requiredTables = tblExpressions.map(({ table }) => table);
return unique(requiredTables);
}

Expand Down
73 changes: 73 additions & 0 deletions test/unit/sql-query-optimizer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,79 @@ describe('SQL query optimizer', () => {
assert.deepEqual(optimizedAst.from, [{ db: null, table: 't', as: null }]);
});

it('should not remove JOIN dependencies from AST', () => {
/* should not remove t1 LEFT JOIN because it's required for JOINs on t2/t3
* SELECT t.id
* FROM t
* LEFT JOIN t1 ON t.id = t1.id
* LEFT JOIN t2 ON t1.id = t2.id
* LEFT JOIN t3 ON t2.id = t3.id
* WHERE t3.id = 1
*/
const from = [
{ db: null, table: 't', as: null },
{
db: null,
table: 't1',
as: null,
join: 'LEFT JOIN',
on: {
type: 'binary_expr',
operator: '=',
left: { type: 'column_ref', table: 't', column: 'id' },
right: { type: 'column_ref', table: 't1', column: 'id' }
}
},
{
db: null,
table: 't2',
as: null,
join: 'LEFT JOIN',
on: {
type: 'binary_expr',
operator: '=',
left: { type: 'column_ref', table: 't1', column: 'id' },
right: { type: 'column_ref', table: 't2', column: 'id' }
}
},
{
db: null,
table: 't3',
as: null,
join: 'LEFT JOIN',
on: {
type: 'binary_expr',
operator: '=',
left: { type: 'column_ref', table: 't2', column: 'id' },
right: { type: 'column_ref', table: 't3', column: 'id' }
}
}
];
const optimizedAst = optimize(
{
with: null,
type: 'select',
options: null,
distinct: null,
columns: [{ expr: { type: 'column_ref', table: 't', column: 'id' }, as: null }],
from,
where: {
type: 'binary_expr',
operator: '=',
left: { type: 'column_ref', table: 't3', column: 'id' },
right: { type: 'number', value: 1 }
},
groupby: null,
having: null,
orderby: null,
limit: null
},
['id']
);

assert.deepEqual(optimizedAst.from, from);
});

it('should pay attention to table aliases', () => {
/**
* t3 must not be removed because it's used by it's alias
Expand Down

0 comments on commit 10ebae2

Please sign in to comment.