UNPKG

rawsql-ts

Version:

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

104 lines 6.02 kB
// Provides parsing for RETURNING clauses in SQL (used in UPDATE, INSERT, DELETE, etc.) import { TokenType } from "../models/Lexeme"; import { ReturningAlias, ReturningClause } from "../models/Clause"; import { extractLexemeComments } from "./utils/LexemeCommentUtils"; import { SelectItemParser } from "./SelectClauseParser"; import { FullNameParser } from "./FullNameParser"; export class ReturningClauseParser { /** * Parse RETURNING clause from lexemes, starting at the given index. * Returns a ReturningClause instance and the new index after parsing. */ 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) !== "returning") { throw new Error(`Syntax error at position ${idx}: Expected 'RETURNING' but found '${(_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.value}'.`); } const returningLexeme = lexemes[idx]; const returningComments = extractLexemeComments(returningLexeme); idx++; const aliasResult = this.parseReturningAliases(lexemes, idx); const aliases = aliasResult.aliases; idx = aliasResult.newIndex; const items = []; // Parse first item const firstItemResult = SelectItemParser.parseItem(lexemes, idx); items.push(firstItemResult.value); idx = firstItemResult.newIndex; // Parse subsequent items separated by commas while (idx < lexemes.length && (lexemes[idx].type & TokenType.Comma)) { const commaLexeme = lexemes[idx]; // We might want to attach comma comments to the previous item or next item // SelectItemParser handles its own leading comments, but comma trailing comments might need handling. // For simplicity, we let SelectItemParser handle comments attached to the item start. // If we want to preserve comma comments precisely, we might need more logic, // but SelectClauseParser doesn't seem to do anything special for comma comments other than skipping them. // However, ReturningClauseParser had complex comment handling. // Let's rely on SelectItemParser for now as it is robust for SELECT lists. idx++; // skip comma const itemResult = SelectItemParser.parseItem(lexemes, idx); items.push(itemResult.value); idx = itemResult.newIndex; } if (items.length === 0) { const position = (_e = (_d = (_c = lexemes[idx]) === null || _c === void 0 ? void 0 : _c.position) === null || _d === void 0 ? void 0 : _d.startPosition) !== null && _e !== void 0 ? _e : idx; throw new Error(`[ReturningClauseParser] Expected a column or '*' after RETURNING at position ${position}.`); } const clause = new ReturningClause(items, aliases); if (returningComments.before.length > 0) { clause.addPositionedComments("before", returningComments.before); } // returningComments.after are usually attached to the first item by SelectItemParser if they are immediately before it? // Actually extractLexemeComments(returningLexeme) gets comments attached to RETURNING token. // If there are comments after RETURNING, they should be attached to the clause or the first item. // In the original parser: // let pendingBeforeForNext: string[] = [...returningComments.after]; // And then added to the first column. if (returningComments.after.length > 0 && items.length > 0) { items[0].addPositionedComments("before", returningComments.after); } return { value: clause, newIndex: idx }; } static parseReturningAliases(lexemes, index) { var _a, _b, _c, _d; let idx = index; const aliases = []; if (((_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.value) !== "with") { return { aliases, newIndex: idx }; } idx++; if (idx >= lexemes.length || !(lexemes[idx].type & TokenType.OpenParen)) { throw new Error(`[ReturningClauseParser] Expected '(' after RETURNING WITH at position ${idx}.`); } idx++; while (idx < lexemes.length) { const kind = (_c = (_b = lexemes[idx]) === null || _b === void 0 ? void 0 : _b.value) === null || _c === void 0 ? void 0 : _c.toLowerCase(); if (kind !== "old" && kind !== "new") { throw new Error(`[ReturningClauseParser] Expected OLD or NEW in RETURNING WITH alias list at position ${idx}.`); } idx++; if (((_d = lexemes[idx]) === null || _d === void 0 ? void 0 : _d.value) !== "as") { throw new Error(`[ReturningClauseParser] Expected AS after ${kind.toUpperCase()} in RETURNING WITH alias list at position ${idx}.`); } idx++; const aliasResult = FullNameParser.parseFromLexeme(lexemes, idx); if (aliasResult.namespaces && aliasResult.namespaces.length > 0) { throw new Error(`[ReturningClauseParser] RETURNING WITH alias must be an unqualified identifier at position ${idx}.`); } aliases.push(new ReturningAlias(kind, aliasResult.name)); idx = aliasResult.newIndex; if (idx < lexemes.length && (lexemes[idx].type & TokenType.Comma)) { idx++; continue; } if (idx < lexemes.length && (lexemes[idx].type & TokenType.CloseParen)) { idx++; return { aliases, newIndex: idx }; } throw new Error(`[ReturningClauseParser] Expected ',' or ')' in RETURNING WITH alias list at position ${idx}.`); } throw new Error(`[ReturningClauseParser] Missing closing ')' in RETURNING WITH alias list.`); } } //# sourceMappingURL=ReturningClauseParser.js.map