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