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
JavaScript
;
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