rawsql-ts
Version:
High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
94 lines • 4.55 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnalyzeStatementParser = 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");
/**
* Parses ANALYZE statements.
*/
class AnalyzeStatementParser {
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(`[AnalyzeStatementParser] Unexpected token "${lexemes[result.newIndex].value}" after ANALYZE statement.`);
}
return result.value;
}
static parseFromLexeme(lexemes, index) {
var _a, _b, _c, _d, _e;
let idx = index;
if (((_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.value.toLowerCase()) !== "analyze") {
throw new Error(`[AnalyzeStatementParser] Expected ANALYZE at index ${idx}.`);
}
idx++;
// Capture optional VERBOSE modifier immediately after ANALYZE.
let verbose = false;
if (((_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.value.toLowerCase()) === "verbose") {
verbose = true;
idx++;
}
// Parse optional target relation name if present.
let target = null;
if (this.canStartQualifiedName(lexemes[idx])) {
const { namespaces, name, newIndex } = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx);
target = new ValueComponent_1.QualifiedName(namespaces, name);
idx = newIndex;
}
// Parse optional column list guarded by parentheses.
let columns = null;
if (((_c = lexemes[idx]) === null || _c === void 0 ? void 0 : _c.type) & Lexeme_1.TokenType.OpenParen) {
if (!target) {
throw new Error("[AnalyzeStatementParser] Column list requires a target relation before '('.");
}
idx++; // consume '('
columns = [];
// Loop through comma-separated column identifiers until closing parenthesis.
while (idx < lexemes.length) {
if (lexemes[idx].type & Lexeme_1.TokenType.CloseParen) {
if (columns.length === 0) {
throw new Error("[AnalyzeStatementParser] Column list must include at least one column identifier.");
}
idx++; // consume ')'
break;
}
const { name, newIndex } = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx);
columns.push(name);
idx = newIndex;
if (((_d = lexemes[idx]) === null || _d === void 0 ? void 0 : _d.type) & Lexeme_1.TokenType.Comma) {
idx++;
continue;
}
if (((_e = lexemes[idx]) === null || _e === void 0 ? void 0 : _e.type) & Lexeme_1.TokenType.CloseParen) {
idx++; // consume ')'
break;
}
throw new Error(`[AnalyzeStatementParser] Expected ',' or ')' after column identifier at index ${idx}.`);
}
if (columns === null || columns.length === 0) {
throw new Error("[AnalyzeStatementParser] Column list cannot be empty.");
}
}
// Reject trailing identifiers when no target was provided.
if (!target && lexemes[idx] && !(lexemes[idx].type & Lexeme_1.TokenType.CloseParen)) {
throw new Error(`[AnalyzeStatementParser] Unexpected token "${lexemes[idx].value}" after ANALYZE clause.`);
}
const statement = new DDLStatements_1.AnalyzeStatement({ verbose, target, columns });
return { value: statement, newIndex: idx };
}
static canStartQualifiedName(lexeme) {
if (!lexeme) {
return false;
}
if (lexeme.type & (Lexeme_1.TokenType.Identifier | Lexeme_1.TokenType.Command | Lexeme_1.TokenType.Function | Lexeme_1.TokenType.Type)) {
return true;
}
return (lexeme.type & Lexeme_1.TokenType.OpenBracket) !== 0;
}
}
exports.AnalyzeStatementParser = AnalyzeStatementParser;
//# sourceMappingURL=AnalyzeStatementParser.js.map