UNPKG

rawsql-ts

Version:

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

431 lines 23.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SqlParser = void 0; const SqlTokenizer_1 = require("./SqlTokenizer"); const SelectQueryParser_1 = require("./SelectQueryParser"); const InsertQueryParser_1 = require("./InsertQueryParser"); const UpdateQueryParser_1 = require("./UpdateQueryParser"); const DeleteQueryParser_1 = require("./DeleteQueryParser"); const CreateTableParser_1 = require("./CreateTableParser"); const MergeQueryParser_1 = require("./MergeQueryParser"); const WithClauseParser_1 = require("./WithClauseParser"); const DropTableParser_1 = require("./DropTableParser"); const DropIndexParser_1 = require("./DropIndexParser"); const CreateIndexParser_1 = require("./CreateIndexParser"); const AlterTableParser_1 = require("./AlterTableParser"); const DropConstraintParser_1 = require("./DropConstraintParser"); const AnalyzeStatementParser_1 = require("./AnalyzeStatementParser"); const ExplainStatementParser_1 = require("./ExplainStatementParser"); const SequenceParser_1 = require("./SequenceParser"); /** * Canonical entry point for SQL parsing. * Delegates to dedicated parsers for SELECT, INSERT, UPDATE, and DELETE statements, and is designed to embrace additional statement types next. */ class SqlParser { static parse(sql, options = {}) { var _a, _b; const skipEmpty = (_a = options.skipEmptyStatements) !== null && _a !== void 0 ? _a : true; const mode = (_b = options.mode) !== null && _b !== void 0 ? _b : 'single'; const tokenizer = new SqlTokenizer_1.SqlTokenizer(sql); // Acquire the first meaningful statement so future dispatching can inspect its leading keyword. const first = this.consumeNextStatement(tokenizer, 0, skipEmpty); if (!first) { throw new Error('[SqlParser] No SQL statements found in input.'); } const parsed = this.dispatchParse(first.segment, 1); if (mode === 'single') { // Ensure callers opting into single-statement mode are protected against trailing statements. const remainder = this.consumeNextStatement(tokenizer, first.nextCursor, skipEmpty); if (remainder) { throw new Error('[SqlParser] Unexpected additional statement detected at index 2. Use parseMany or set mode to "multiple" to allow multiple statements.'); } } return parsed; } static parseMany(sql, options = {}) { var _a; const skipEmpty = (_a = options.skipEmptyStatements) !== null && _a !== void 0 ? _a : true; const tokenizer = new SqlTokenizer_1.SqlTokenizer(sql); const statements = []; let cursor = 0; let carry = null; let index = 0; while (true) { // Collect the next logical statement segment, carrying forward detached comments when necessary. const segment = tokenizer.readNextStatement(cursor, carry); carry = null; if (!segment) { break; } cursor = segment.nextPosition; if (segment.lexemes.length === 0) { // Preserve dangling comments so they can attach to the next real statement. if (segment.leadingComments && segment.leadingComments.length > 0) { carry = segment.leadingComments; } if (skipEmpty || segment.rawText.trim().length === 0) { continue; } } index++; statements.push(this.dispatchParse(segment, index)); } return statements; } static dispatchParse(segment, statementIndex) { if (segment.lexemes.length === 0) { throw new Error(`[SqlParser] Statement ${statementIndex} does not contain any tokens.`); } const firstToken = segment.lexemes[0].value.toLowerCase(); switch (firstToken) { case 'select': case 'values': return this.parseSelectStatement(segment, statementIndex); case 'with': { const commandAfterWith = this.getCommandAfterWith(segment.lexemes); switch (commandAfterWith) { case 'insert into': return this.parseInsertStatement(segment, statementIndex); case 'update': return this.parseUpdateStatement(segment, statementIndex); case 'delete from': return this.parseDeleteStatement(segment, statementIndex); case 'merge into': return this.parseMergeStatement(segment, statementIndex); default: return this.parseSelectStatement(segment, statementIndex); } } case 'insert into': return this.parseInsertStatement(segment, statementIndex); case 'update': return this.parseUpdateStatement(segment, statementIndex); case 'delete from': return this.parseDeleteStatement(segment, statementIndex); case 'create table': case 'create temporary table': return this.parseCreateTableStatement(segment, statementIndex); case 'merge into': return this.parseMergeStatement(segment, statementIndex); case 'create index': case 'create unique index': return this.parseCreateIndexStatement(segment, statementIndex); case 'create sequence': case 'create temporary sequence': case 'create temp sequence': return this.parseCreateSequenceStatement(segment, statementIndex); case 'drop table': return this.parseDropTableStatement(segment, statementIndex); case 'drop index': return this.parseDropIndexStatement(segment, statementIndex); case 'alter table': return this.parseAlterTableStatement(segment, statementIndex); case 'alter sequence': return this.parseAlterSequenceStatement(segment, statementIndex); case 'drop constraint': return this.parseDropConstraintStatement(segment, statementIndex); case 'analyze': return this.parseAnalyzeStatement(segment, statementIndex); case 'explain': return this.parseExplainStatement(segment, statementIndex); default: throw new Error(`[SqlParser] Statement ${statementIndex} starts with unsupported token "${segment.lexemes[0].value}".`); } } static parseSelectStatement(segment, statementIndex) { var _a, _b; try { const result = SelectQueryParser_1.SelectQueryParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse SELECT statement ${statementIndex}: ${message}`); } } static parseExplainStatement(segment, statementIndex) { var _a, _b; try { const result = ExplainStatementParser_1.ExplainStatementParser.parseFromLexeme(segment.lexemes, 0, (lexemes, nestedStart) => { if (nestedStart >= lexemes.length) { throw new Error("[ExplainStatementParser] Missing statement after EXPLAIN options."); } const nestedSegment = { lexemes: lexemes.slice(nestedStart), statementStart: segment.statementStart, statementEnd: segment.statementEnd, nextPosition: segment.nextPosition, rawText: segment.rawText, leadingComments: segment.leadingComments, }; const statement = this.dispatchParse(nestedSegment, statementIndex); return { value: statement, newIndex: lexemes.length }; }); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in EXPLAIN statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse EXPLAIN statement ${statementIndex}: ${message}`); } } static parseInsertStatement(segment, statementIndex) { var _a, _b; try { const result = InsertQueryParser_1.InsertQueryParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse INSERT statement ${statementIndex}: ${message}`); } } static parseUpdateStatement(segment, statementIndex) { var _a, _b; try { const result = UpdateQueryParser_1.UpdateQueryParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse UPDATE statement ${statementIndex}: ${message}`); } } static parseDeleteStatement(segment, statementIndex) { var _a, _b; try { const result = DeleteQueryParser_1.DeleteQueryParser.parseFromLexeme(segment.lexemes, 0); // Guard against trailing tokens that would indicate multiple statements in DELETE parsing. if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse DELETE statement ${statementIndex}: ${message}`); } } static parseCreateTableStatement(segment, statementIndex) { var _a, _b; try { const result = CreateTableParser_1.CreateTableParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse CREATE TABLE statement ${statementIndex}: ${message}`); } } static parseDropTableStatement(segment, statementIndex) { var _a, _b; try { const result = DropTableParser_1.DropTableParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse DROP TABLE statement ${statementIndex}: ${message}`); } } static parseDropIndexStatement(segment, statementIndex) { var _a, _b; try { const result = DropIndexParser_1.DropIndexParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse DROP INDEX statement ${statementIndex}: ${message}`); } } static parseCreateIndexStatement(segment, statementIndex) { var _a, _b; try { const result = CreateIndexParser_1.CreateIndexParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse CREATE INDEX statement ${statementIndex}: ${message}`); } } static parseCreateSequenceStatement(segment, statementIndex) { var _a, _b; try { const result = SequenceParser_1.CreateSequenceParser.parseFromLexeme(segment.lexemes, 0); // Ensure no trailing lexemes remain after the CREATE SEQUENCE clause. if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse CREATE SEQUENCE statement ${statementIndex}: ${message}`); } } static parseAlterSequenceStatement(segment, statementIndex) { var _a, _b; try { const result = SequenceParser_1.AlterSequenceParser.parseFromLexeme(segment.lexemes, 0); // Validate that the ALTER SEQUENCE statement consumed all available tokens. if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse ALTER SEQUENCE statement ${statementIndex}: ${message}`); } } static parseAlterTableStatement(segment, statementIndex) { var _a, _b; try { const result = AlterTableParser_1.AlterTableParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse ALTER TABLE statement ${statementIndex}: ${message}`); } } static parseDropConstraintStatement(segment, statementIndex) { var _a, _b; try { const result = DropConstraintParser_1.DropConstraintParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse DROP CONSTRAINT statement ${statementIndex}: ${message}`); } } static parseAnalyzeStatement(segment, statementIndex) { var _a, _b; try { // Delegate lexeme interpretation to the ANALYZE-specific parser. const result = AnalyzeStatementParser_1.AnalyzeStatementParser.parseFromLexeme(segment.lexemes, 0); // Ensure parsing consumed every lexeme belonging to this statement. if (result.newIndex < segment.lexemes.length) { const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse ANALYZE statement ${statementIndex}: ${message}`); } } static parseMergeStatement(segment, statementIndex) { var _a, _b; try { const result = MergeQueryParser_1.MergeQueryParser.parseFromLexeme(segment.lexemes, 0); if (result.newIndex < segment.lexemes.length) { // Guard against trailing tokens that would indicate parsing stopped prematurely. const unexpected = segment.lexemes[result.newIndex]; const position = (_b = (_a = unexpected.position) === null || _a === void 0 ? void 0 : _a.startPosition) !== null && _b !== void 0 ? _b : segment.statementStart; throw new Error(`[SqlParser] Unexpected token "${unexpected.value}" in statement ${statementIndex} at character ${position}.`); } return result.value; } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new Error(`[SqlParser] Failed to parse MERGE statement ${statementIndex}: ${message}`); } } static getCommandAfterWith(lexemes) { var _a; try { const withResult = WithClauseParser_1.WithClauseParser.parseFromLexeme(lexemes, 0); const next = lexemes[withResult.newIndex]; return (_a = next === null || next === void 0 ? void 0 : next.value.toLowerCase()) !== null && _a !== void 0 ? _a : null; } catch { return null; } } static consumeNextStatement(tokenizer, cursor, skipEmpty) { let localCursor = cursor; let carry = null; // Advance until a statement with tokens is found or the input ends. while (true) { const segment = tokenizer.readNextStatement(localCursor, carry); carry = null; if (!segment) { return null; } localCursor = segment.nextPosition; if (segment.lexemes.length === 0) { // Retain comments so the next statement can inherit them when appropriate. if (segment.leadingComments && segment.leadingComments.length > 0) { carry = segment.leadingComments; } if (skipEmpty || segment.rawText.trim().length === 0) { continue; } } return { segment, nextCursor: localCursor }; } } } exports.SqlParser = SqlParser; //# sourceMappingURL=SqlParser.js.map