UNPKG

rawsql-ts

Version:

High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.

291 lines 11.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectOutputCollector = void 0; const Clause_1 = require("../models/Clause"); const InsertQuery_1 = require("../models/InsertQuery"); const DeleteQuery_1 = require("../models/DeleteQuery"); const MergeQuery_1 = require("../models/MergeQuery"); const SelectQuery_1 = require("../models/SelectQuery"); const UpdateQuery_1 = require("../models/UpdateQuery"); const ValueComponent_1 = require("../models/ValueComponent"); const CTECollector_1 = require("./CTECollector"); /** * Collects SELECT output columns without collapsing duplicate output names. * * This collector is intended for callers that need stable SELECT-list positions. * It keeps output order and assigns outputIndex after supported wildcard expansion. */ class SelectOutputCollector { constructor(tableColumnResolver = null, initialCommonTables = null) { this.tableColumnResolver = tableColumnResolver; this.commonTableCollector = new CTECollector_1.CTECollector(); this.commonTables = initialCommonTables !== null && initialCommonTables !== void 0 ? initialCommonTables : []; } collect(arg) { const rawColumns = this.collectRaw(arg); return rawColumns.map((item, outputIndex) => ({ ...item, outputIndex })); } collectRaw(arg) { if (arg instanceof SelectQuery_1.SimpleSelectQuery) { return this.collectSimpleSelectQuery(arg); } if (arg instanceof Clause_1.SourceExpression) { return this.collectSourceExpression(arg.getAliasName(), arg); } if (arg instanceof Clause_1.FromClause) { return this.collectFromClause(arg, true); } return []; } collectSimpleSelectQuery(query) { const previousCommonTables = this.commonTables; if (this.commonTables.length === 0) { this.commonTables = this.commonTableCollector.collect(query); } try { const outputs = []; for (const item of query.selectClause.items) { outputs.push(...this.collectSelectItem(item, query.fromClause)); } return outputs; } finally { this.commonTables = previousCommonTables; } } collectSelectItem(item, fromClause) { if (item.identifier) { return [this.createExplicitOutput(item.identifier.name, item.value, fromClause)]; } if (!(item.value instanceof ValueComponent_1.ColumnReference)) { return []; } const columnName = item.value.column.name; if (columnName === "*") { return this.expandWildcard(item.value, fromClause); } return [this.createExplicitOutput(columnName, item.value, fromClause)]; } expandWildcard(value, fromClause) { var _a; if (!fromClause) { return []; } if (value.namespaces === null) { return this.collectFromClause(fromClause, true); } const sourceName = value.getNamespace(); if (fromClause.getSourceAliasName() === sourceName) { return this.collectFromClause(fromClause, false); } const join = (_a = fromClause.joins) === null || _a === void 0 ? void 0 : _a.find(item => this.getJoinSourceName(item) === sourceName); return join ? this.collectJoinClause(join) : []; } collectFromClause(clause, joinCascade) { const outputs = this.collectSourceExpression(clause.getSourceAliasName(), clause.source); if (clause.joins && joinCascade) { for (const join of clause.joins) { outputs.push(...this.collectJoinClause(join)); } } return outputs; } collectJoinClause(clause) { return this.collectSourceExpression(this.getJoinSourceName(clause), clause.source); } getJoinSourceName(clause) { return clause.source.getAliasName(); } collectSourceExpression(sourceName, source) { var _a; if ((_a = source.aliasExpression) === null || _a === void 0 ? void 0 : _a.columns) { return source.aliasExpression.columns.map(column => ({ name: column.name, value: new ValueComponent_1.ColumnReference(sourceName ? [sourceName] : null, column.name), sourceAlias: sourceName, sourceName: null, sourceColumnName: column.name })); } const cte = this.findCommonTable(source); if (cte) { const innerCommonTables = this.commonTables.filter(item => item.getSourceAliasName() !== cte.getSourceAliasName()); const innerOutputs = this.collectCteQuery(cte.query, innerCommonTables); return this.qualifyOutputs(innerOutputs, sourceName, cte.getSourceAliasName()); } if (source.datasource instanceof Clause_1.TableSource) { return this.collectTableSource(sourceName, source.datasource); } if (source.datasource instanceof Clause_1.SubQuerySource) { const innerOutputs = this.collectNestedSelectQuery(source.datasource.query); return this.qualifyOutputs(innerOutputs, sourceName, null); } if (source.datasource instanceof Clause_1.ParenSource) { return []; } return []; } findCommonTable(source) { var _a; if (!(source.datasource instanceof Clause_1.TableSource)) { return null; } const tableName = source.datasource.getSourceName(); return (_a = this.commonTables.find(item => item.getSourceAliasName() === tableName)) !== null && _a !== void 0 ? _a : null; } collectTableSource(sourceName, source) { if (!this.tableColumnResolver) { return []; } const tableName = source.getSourceName(); const qualifier = sourceName !== null && sourceName !== void 0 ? sourceName : tableName; return this.tableColumnResolver(tableName).map(column => ({ name: column, value: new ValueComponent_1.ColumnReference([qualifier], column), sourceAlias: sourceName, sourceName: tableName, sourceColumnName: column })); } collectNestedSelectQuery(query) { const collector = new SelectOutputCollector(this.tableColumnResolver, this.commonTables); return collector.collect(query).map(({ name, value, sourceAlias, sourceName, sourceColumnName }) => ({ name, value, sourceAlias, sourceName, sourceColumnName })); } collectCteQuery(query, commonTables) { if (this.isSelectQuery(query)) { const collector = new SelectOutputCollector(this.tableColumnResolver, commonTables); return collector.collect(query).map(({ name, value, sourceAlias, sourceName, sourceColumnName }) => ({ name, value, sourceAlias, sourceName, sourceColumnName })); } return this.collectReturningOutputs(query); } collectReturningOutputs(query) { if (!(query instanceof InsertQuery_1.InsertQuery || query instanceof UpdateQuery_1.UpdateQuery || query instanceof DeleteQuery_1.DeleteQuery || query instanceof MergeQuery_1.MergeQuery)) { return []; } if (!query.returningClause) { return []; } return this.extractReturningOutputs(query.returningClause); } extractReturningOutputs(clause) { var _a, _b; const outputs = []; for (const item of clause.items) { const name = (_b = (_a = item.identifier) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : this.extractSelectItemName(item); if (name) { outputs.push(this.createExplicitOutput(name, item.value)); } } return outputs; } extractSelectItemName(item) { if (item.identifier) { return item.identifier.name; } if (item.value instanceof ValueComponent_1.ColumnReference) { return item.value.column.name; } return null; } qualifyOutputs(outputs, sourceAlias, sourceName) { return outputs.map(item => ({ name: item.name, value: new ValueComponent_1.ColumnReference(sourceAlias ? [sourceAlias] : null, item.name), sourceAlias, sourceName, sourceColumnName: item.name })); } createExplicitOutput(name, value, fromClause = null) { if (value instanceof ValueComponent_1.ColumnReference && value.column.name !== "*") { const sourceMetadata = this.resolveColumnSourceMetadata(value, fromClause); if (sourceMetadata) { return { name, value, ...sourceMetadata }; } } return { name, value, sourceAlias: null, sourceName: null, sourceColumnName: null }; } resolveColumnSourceMetadata(value, fromClause) { if (!fromClause || value.namespaces === null) { return null; } const namespace = value.getNamespace(); const matches = fromClause.getSources().filter(source => this.getSourceMatchNames(source).includes(namespace)); if (matches.length !== 1) { return null; } const source = matches[0]; const sourceAlias = this.getColumnReferenceSourceAlias(source, namespace); return { sourceAlias, sourceName: this.getColumnReferenceSourceName(source), sourceColumnName: value.column.name }; } getSourceMatchNames(source) { const names = new Set(); if (source.aliasExpression) { names.add(source.aliasExpression.table.name); return [...names]; } const aliasName = source.getAliasName(); if (aliasName) { names.add(aliasName); } if (source.datasource instanceof Clause_1.TableSource) { names.add(source.datasource.table.name); names.add(source.datasource.getSourceName()); } return [...names]; } getColumnReferenceSourceAlias(source, namespace) { if (source.aliasExpression) { return source.aliasExpression.table.name; } return namespace; } getColumnReferenceSourceName(source) { var _a; const cte = this.findCommonTable(source); if (cte) { return cte.getSourceAliasName(); } if ((_a = source.aliasExpression) === null || _a === void 0 ? void 0 : _a.columns) { return null; } if (source.datasource instanceof Clause_1.TableSource) { return source.datasource.getSourceName(); } return null; } isSelectQuery(query) { return "__selectQueryType" in query && query.__selectQueryType === "SelectQuery"; } } exports.SelectOutputCollector = SelectOutputCollector; //# sourceMappingURL=SelectOutputCollector.js.map