UNPKG

rawsql-ts

Version:

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

190 lines 9.82 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectQueryParser = void 0; const SelectQuery_1 = require("../models/SelectQuery"); const SelectClauseParser_1 = require("./SelectClauseParser"); const FromClauseParser_1 = require("./FromClauseParser"); const WhereClauseParser_1 = require("./WhereClauseParser"); const GroupByParser_1 = require("./GroupByParser"); const HavingParser_1 = require("./HavingParser"); const OrderByClauseParser_1 = require("./OrderByClauseParser"); const WindowClauseParser_1 = require("./WindowClauseParser"); const LimitClauseParser_1 = require("./LimitClauseParser"); const ForClauseParser_1 = require("./ForClauseParser"); const SqlTokenizer_1 = require("./SqlTokenizer"); const WithClauseParser_1 = require("./WithClauseParser"); const ValuesQueryParser_1 = require("./ValuesQueryParser"); const FetchClauseParser_1 = require("./FetchClauseParser"); const OffsetClauseParser_1 = require("./OffsetClauseParser"); class SelectQueryParser { // Parse SQL string to AST (was: parse) static parse(query) { const tokenizer = new SqlTokenizer_1.SqlTokenizer(query); const lexemes = tokenizer.readLexmes(); // Parse const result = this.parseFromLexeme(lexemes, 0); // Error if there are remaining tokens if (result.newIndex < lexemes.length) { throw new Error(`[SelectQueryParser] Syntax error: Unexpected token "${lexemes[result.newIndex].value}" at position ${result.newIndex}. The SELECT query is complete but there are additional tokens.`); } return result.value; } /** * Asynchronously parse SQL string to AST. * This method wraps the synchronous parse logic in a Promise for future extensibility. * @param query SQL string to parse * @returns Promise<SelectQuery> */ static async parseAsync(query) { // For now, just wrap the sync parse in a resolved Promise return Promise.resolve(this.parse(query)); } // Parse from lexeme array (was: parse) static parseFromLexeme(lexemes, index) { let idx = index; if (idx >= lexemes.length) { throw new Error(`Syntax error: Unexpected end of input at position ${index}.`); } // Check if the first token is a SELECT keyword or VALUES const firstToken = lexemes[idx].value; if (!this.selectCommandSet.has(firstToken) && firstToken !== 'values') { throw new Error(`Syntax error at position ${idx}: Expected 'SELECT' or 'VALUES' keyword but found "${lexemes[idx].value}".`); } let firstResult = this.selectCommandSet.has(firstToken) ? this.parseSimpleSelectQuery(lexemes, idx) : this.parseValuesQuery(lexemes, idx); let query = firstResult.value; idx = firstResult.newIndex; // check 'union' while (idx < lexemes.length && this.unionCommandSet.has(lexemes[idx].value.toLowerCase())) { const operator = lexemes[idx].value.toLowerCase(); idx++; if (idx >= lexemes.length) { throw new Error(`Syntax error at position ${idx}: Expected a query after '${operator.toUpperCase()}' but found end of input.`); } const nextToken = lexemes[idx].value.toLowerCase(); if (this.selectCommandSet.has(nextToken)) { const result = this.parseSimpleSelectQuery(lexemes, idx); query = new SelectQuery_1.BinarySelectQuery(query, operator, result.value); idx = result.newIndex; } else if (nextToken === 'values') { const result = this.parseValuesQuery(lexemes, idx); query = new SelectQuery_1.BinarySelectQuery(query, operator, result.value); idx = result.newIndex; } else { throw new Error(`Syntax error at position ${idx}: Expected 'SELECT' or 'VALUES' after '${operator.toUpperCase()}' but found "${lexemes[idx].value}".`); } } return { value: query, newIndex: idx }; } static parseSimpleSelectQuery(lexemes, index) { let idx = index; let withClauseResult = null; // Parse optional WITH clause if (idx < lexemes.length && lexemes[idx].value === 'with') { withClauseResult = WithClauseParser_1.WithClauseParser.parseFromLexeme(lexemes, idx); idx = withClauseResult.newIndex; } // Parse SELECT clause (required) if (idx >= lexemes.length || lexemes[idx].value !== 'select') { throw new Error(`Syntax error at position ${idx}: Expected 'SELECT' keyword but found "${idx < lexemes.length ? lexemes[idx].value : 'end of input'}". SELECT queries must start with the SELECT keyword.`); } const selectClauseResult = SelectClauseParser_1.SelectClauseParser.parseFromLexeme(lexemes, idx); idx = selectClauseResult.newIndex; // Parse FROM clause (optional) let fromClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'from') { fromClauseResult = FromClauseParser_1.FromClauseParser.parseFromLexeme(lexemes, idx); idx = fromClauseResult.newIndex; } // Parse WHERE clause (optional) let whereClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'where') { whereClauseResult = WhereClauseParser_1.WhereClauseParser.parseFromLexeme(lexemes, idx); idx = whereClauseResult.newIndex; } // Parse GROUP BY clause (optional) let groupByClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'group by') { groupByClauseResult = GroupByParser_1.GroupByClauseParser.parseFromLexeme(lexemes, idx); idx = groupByClauseResult.newIndex; } // Parse HAVING clause (optional) let havingClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'having') { havingClauseResult = HavingParser_1.HavingClauseParser.parseFromLexeme(lexemes, idx); idx = havingClauseResult.newIndex; } // Parse WINDOW clause (optional) let windowClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'window') { windowClauseResult = WindowClauseParser_1.WindowClauseParser.parseFromLexeme(lexemes, idx); idx = windowClauseResult.newIndex; } // Parse ORDER BY clause (optional) let orderByClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'order by') { orderByClauseResult = OrderByClauseParser_1.OrderByClauseParser.parseFromLexeme(lexemes, idx); idx = orderByClauseResult.newIndex; } // Parse LIMIT clause (optional) let limitClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'limit') { limitClauseResult = LimitClauseParser_1.LimitClauseParser.parseFromLexeme(lexemes, idx); idx = limitClauseResult.newIndex; } // Parse OFFSET clause (optional) let offsetClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'offset') { offsetClauseResult = OffsetClauseParser_1.OffsetClauseParser.parseFromLexeme(lexemes, idx); idx = offsetClauseResult.newIndex; } // Parse FETCH clause (optional) let fetchClauseResult = null; if (idx < lexemes.length && lexemes[idx].value === 'fetch') { fetchClauseResult = FetchClauseParser_1.FetchClauseParser.parseFromLexeme(lexemes, idx); idx = fetchClauseResult.newIndex; } // Parse FOR clause (optional) let forClauseResult = null; if (idx < lexemes.length && lexemes[idx].value.toLowerCase() === 'for') { forClauseResult = ForClauseParser_1.ForClauseParser.parseFromLexeme(lexemes, idx); idx = forClauseResult.newIndex; } // Create and return the SelectQuery object const selectQuery = new SelectQuery_1.SimpleSelectQuery({ withClause: withClauseResult ? withClauseResult.value : null, selectClause: selectClauseResult.value, fromClause: fromClauseResult ? fromClauseResult.value : null, whereClause: whereClauseResult ? whereClauseResult.value : null, groupByClause: groupByClauseResult ? groupByClauseResult.value : null, havingClause: havingClauseResult ? havingClauseResult.value : null, orderByClause: orderByClauseResult ? orderByClauseResult.value : null, windowClause: windowClauseResult ? windowClauseResult.value : null, limitClause: limitClauseResult ? limitClauseResult.value : null, offsetClause: offsetClauseResult ? offsetClauseResult.value : null, fetchClause: fetchClauseResult ? fetchClauseResult.value : null, forClause: forClauseResult ? forClauseResult.value : null }); return { value: selectQuery, newIndex: idx }; } static parseValuesQuery(lexemes, index) { // Use ValuesQueryParser to parse VALUES clause const result = ValuesQueryParser_1.ValuesQueryParser.parseFromLexeme(lexemes, index); // Return the result from ValuesQueryParser directly return { value: result.value, newIndex: result.newIndex }; } } exports.SelectQueryParser = SelectQueryParser; SelectQueryParser.unionCommandSet = new Set([ "union", "union all", "intersect", "intersect all", "except", "except all", ]); SelectQueryParser.selectCommandSet = new Set(["with", "select"]); //# sourceMappingURL=SelectQueryParser.js.map