rawsql-ts
Version:
[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
197 lines • 10.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { BinarySelectQuery, SimpleSelectQuery } from "../models/SelectQuery";
import { SelectClauseParser } from "./SelectClauseParser";
import { FromClauseParser } from "./FromClauseParser";
import { WhereClauseParser } from "./WhereClauseParser";
import { GroupByClauseParser } from "./GroupByParser";
import { HavingClauseParser } from "./HavingParser";
import { OrderByClauseParser } from "./OrderByClauseParser";
import { WindowClauseParser } from "./WindowClauseParser";
import { LimitClauseParser } from "./LimitClauseParser";
import { ForClauseParser } from "./ForClauseParser";
import { SqlTokenizer } from "./SqlTokenizer";
import { WithClauseParser } from "./WithClauseParser";
import { ValuesQueryParser } from "./ValuesQueryParser";
import { FetchClauseParser } from "./FetchClauseParser";
import { OffsetClauseParser } from "./OffsetClauseParser";
export class SelectQueryParser {
// Parse SQL string to AST (was: parse)
static parse(query) {
const tokenizer = new 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 parseAsync(query) {
return __awaiter(this, void 0, void 0, function* () {
// 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 BinarySelectQuery(query, operator, result.value);
idx = result.newIndex;
}
else if (nextToken === 'values') {
const result = this.parseValuesQuery(lexemes, idx);
query = new 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.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.parseFromLexeme(lexemes, idx);
idx = selectClauseResult.newIndex;
// Parse FROM clause (optional)
let fromClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'from') {
fromClauseResult = FromClauseParser.parseFromLexeme(lexemes, idx);
idx = fromClauseResult.newIndex;
}
// Parse WHERE clause (optional)
let whereClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'where') {
whereClauseResult = 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 = GroupByClauseParser.parseFromLexeme(lexemes, idx);
idx = groupByClauseResult.newIndex;
}
// Parse HAVING clause (optional)
let havingClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'having') {
havingClauseResult = HavingClauseParser.parseFromLexeme(lexemes, idx);
idx = havingClauseResult.newIndex;
}
// Parse WINDOW clause (optional)
let windowClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'window') {
windowClauseResult = 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.parseFromLexeme(lexemes, idx);
idx = orderByClauseResult.newIndex;
}
// Parse LIMIT clause (optional)
let limitClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'limit') {
limitClauseResult = LimitClauseParser.parseFromLexeme(lexemes, idx);
idx = limitClauseResult.newIndex;
}
// Parse OFFSET clause (optional)
let offsetClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'offset') {
offsetClauseResult = OffsetClauseParser.parseFromLexeme(lexemes, idx);
idx = offsetClauseResult.newIndex;
}
// Parse FETCH clause (optional)
let fetchClauseResult = null;
if (idx < lexemes.length && lexemes[idx].value === 'fetch') {
fetchClauseResult = 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.parseFromLexeme(lexemes, idx);
idx = forClauseResult.newIndex;
}
// Create and return the SelectQuery object
const selectQuery = new 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.parseFromLexeme(lexemes, index);
// Return the result from ValuesQueryParser directly
return { value: result.value, newIndex: result.newIndex };
}
}
SelectQueryParser.unionCommandSet = new Set([
"union",
"union all",
"intersect",
"intersect all",
"except",
"except all",
]);
SelectQueryParser.selectCommandSet = new Set(["with", "select"]);
//# sourceMappingURL=SelectQueryParser.js.map