UNPKG

rawsql-ts

Version:

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

241 lines 11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CreateIndexParser = void 0; const SqlTokenizer_1 = require("./SqlTokenizer"); const DDLStatements_1 = require("../models/DDLStatements"); const Lexeme_1 = require("../models/Lexeme"); const FullNameParser_1 = require("./FullNameParser"); const ValueComponent_1 = require("../models/ValueComponent"); const ValueParser_1 = require("./ValueParser"); const ParserStringUtils_1 = require("../utils/ParserStringUtils"); /** * Parses CREATE INDEX statements. */ class CreateIndexParser { static parse(sql) { const tokenizer = new SqlTokenizer_1.SqlTokenizer(sql); const lexemes = tokenizer.readLexemes(); const result = this.parseFromLexeme(lexemes, 0); if (result.newIndex < lexemes.length) { throw new Error(`[CreateIndexParser] Unexpected token "${lexemes[result.newIndex].value}" after CREATE INDEX.`); } return result.value; } static parseFromLexeme(lexemes, index) { var _a, _b, _c, _d, _e, _f, _g, _h, _j; let idx = index; const firstToken = (_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.value.toLowerCase(); if (firstToken !== "create index" && firstToken !== "create unique index") { throw new Error(`[CreateIndexParser] Expected CREATE INDEX at index ${idx}.`); } const unique = firstToken === "create unique index"; idx++; let concurrently = false; if (((_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.value.toLowerCase()) === "concurrently") { concurrently = true; idx++; } let ifNotExists = false; if (((_c = lexemes[idx]) === null || _c === void 0 ? void 0 : _c.value.toLowerCase()) === "if not exists") { ifNotExists = true; idx++; } const indexNameResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); const indexName = new ValueComponent_1.QualifiedName(indexNameResult.namespaces, indexNameResult.name); idx = indexNameResult.newIndex; if (((_d = lexemes[idx]) === null || _d === void 0 ? void 0 : _d.value.toLowerCase()) !== "on") { throw new Error(`[CreateIndexParser] Expected ON keyword before table name at index ${idx}.`); } idx++; const tableResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); const tableName = new ValueComponent_1.QualifiedName(tableResult.namespaces, tableResult.name); idx = tableResult.newIndex; let usingMethod = null; if (((_e = lexemes[idx]) === null || _e === void 0 ? void 0 : _e.value.toLowerCase()) === "using") { idx++; const methodResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); usingMethod = methodResult.name; idx = methodResult.newIndex; } const columnsResult = this.parseIndexColumnList(lexemes, idx); const columns = columnsResult.columns; idx = columnsResult.newIndex; let include = null; if (((_f = lexemes[idx]) === null || _f === void 0 ? void 0 : _f.value.toLowerCase()) === "include") { idx++; const includeResult = this.parseIdentifierList(lexemes, idx); include = includeResult.identifiers; idx = includeResult.newIndex; } let withOptions = null; if (((_g = lexemes[idx]) === null || _g === void 0 ? void 0 : _g.value.toLowerCase()) === "with") { const withResult = this.parseWithOptions(lexemes, idx); withOptions = withResult.options; idx = withResult.newIndex; } let tablespace = null; if (((_h = lexemes[idx]) === null || _h === void 0 ? void 0 : _h.value.toLowerCase()) === "tablespace") { idx++; const tablespaceResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); tablespace = tablespaceResult.name; idx = tablespaceResult.newIndex; } let whereClause; if (((_j = lexemes[idx]) === null || _j === void 0 ? void 0 : _j.value.toLowerCase()) === "where") { idx++; const whereResult = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); whereClause = whereResult.value; idx = whereResult.newIndex; } return { value: new DDLStatements_1.CreateIndexStatement({ unique, concurrently, ifNotExists, indexName, tableName, usingMethod, columns, include, withOptions, tablespace, where: whereClause }), newIndex: idx }; } static parseIndexColumnList(lexemes, index) { var _a, _b, _c, _d; let idx = index; if (((_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.type) !== Lexeme_1.TokenType.OpenParen) { throw new Error(`[CreateIndexParser] Expected '(' starting column list at index ${idx}.`); } idx++; const columns = []; while (idx < lexemes.length) { const expressionResult = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx); idx = expressionResult.newIndex; let sortOrder = null; let nullsOrder = null; let collation = null; let operatorClass = null; while (idx < lexemes.length) { const tokenValue = lexemes[idx].value.toLowerCase(); if (tokenValue === "asc" || tokenValue === "desc") { sortOrder = tokenValue; idx++; continue; } if (tokenValue === "nulls first" || tokenValue === "nulls last") { nullsOrder = tokenValue.endsWith("first") ? "first" : "last"; idx++; continue; } if (tokenValue === "collate") { idx++; const collateResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); collation = new ValueComponent_1.QualifiedName(collateResult.namespaces, collateResult.name); idx = collateResult.newIndex; continue; } if (this.isClauseTerminator(tokenValue) || (lexemes[idx].type & (Lexeme_1.TokenType.Comma | Lexeme_1.TokenType.CloseParen))) { break; } if (lexemes[idx].type & (Lexeme_1.TokenType.Identifier | Lexeme_1.TokenType.Type | Lexeme_1.TokenType.Function)) { const opClassResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); operatorClass = new ValueComponent_1.QualifiedName(opClassResult.namespaces, opClassResult.name); idx = opClassResult.newIndex; continue; } break; } columns.push(new DDLStatements_1.IndexColumnDefinition({ expression: expressionResult.value, sortOrder, nullsOrder, collation, operatorClass })); if (((_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.type) === Lexeme_1.TokenType.Comma) { idx++; continue; } if (((_c = lexemes[idx]) === null || _c === void 0 ? void 0 : _c.type) === Lexeme_1.TokenType.CloseParen) { idx++; break; } } if (((_d = lexemes[idx - 1]) === null || _d === void 0 ? void 0 : _d.type) !== Lexeme_1.TokenType.CloseParen) { throw new Error(`[CreateIndexParser] Expected ')' to close column list starting at index ${index}.`); } return { columns, newIndex: idx }; } static parseIdentifierList(lexemes, index) { var _a, _b, _c, _d; let idx = index; if (((_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.type) !== Lexeme_1.TokenType.OpenParen) { throw new Error(`[CreateIndexParser] Expected '(' starting identifier list at index ${idx}.`); } idx++; const identifiers = []; while (idx < lexemes.length) { const result = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx); identifiers.push(result.name); idx = result.newIndex; if (((_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.type) === Lexeme_1.TokenType.Comma) { idx++; continue; } if (((_c = lexemes[idx]) === null || _c === void 0 ? void 0 : _c.type) === Lexeme_1.TokenType.CloseParen) { idx++; break; } } if (((_d = lexemes[idx - 1]) === null || _d === void 0 ? void 0 : _d.type) !== Lexeme_1.TokenType.CloseParen) { throw new Error(`[CreateIndexParser] Expected ')' to close identifier list starting at index ${index}.`); } return { identifiers, newIndex: idx }; } static parseWithOptions(lexemes, index) { var _a, _b; let idx = index; const start = idx; if (((_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.value.toLowerCase()) !== "with") { throw new Error(`[CreateIndexParser] Expected WITH keyword at index ${idx}.`); } idx++; if (((_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.type) !== Lexeme_1.TokenType.OpenParen) { throw new Error(`[CreateIndexParser] Expected '(' after WITH at index ${idx}.`); } let depth = 0; while (idx < lexemes.length) { if (lexemes[idx].type === Lexeme_1.TokenType.OpenParen) { depth++; } else if (lexemes[idx].type === Lexeme_1.TokenType.CloseParen) { depth--; if (depth === 0) { idx++; break; } } idx++; } if (depth !== 0) { throw new Error(`[CreateIndexParser] Unterminated WITH options starting at index ${start}; unbalanced parentheses.`); } const text = (0, ParserStringUtils_1.joinLexemeValues)(lexemes, start, idx); return { options: new ValueComponent_1.RawString(text), newIndex: idx }; } static isClauseTerminator(value) { return value === "include" || value === "with" || value === "where" || value === "tablespace"; } } exports.CreateIndexParser = CreateIndexParser; //# sourceMappingURL=CreateIndexParser.js.map