rawsql-ts
Version:
[beta]High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
125 lines • 6.35 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SetClauseParser = void 0;
// Provides parsing for SET clauses in UPDATE queries.
const Lexeme_1 = require("../models/Lexeme");
const Clause_1 = require("../models/Clause");
const ValueParser_1 = require("./ValueParser");
const FullNameParser_1 = require("./FullNameParser");
const LexemeCommentUtils_1 = require("./utils/LexemeCommentUtils");
/**
* Parse SET clause from lexemes (including 'SET' keyword check).
*/
class SetClauseParser {
static parseFromLexeme(lexemes, idx) {
var _a;
if (lexemes[idx].value !== "set") {
throw new Error(`Syntax error at position ${idx}: Expected 'SET' but found '${lexemes[idx].value}'.`);
}
const setLexeme = lexemes[idx];
const setKeywordComments = (0, LexemeCommentUtils_1.extractLexemeComments)(setLexeme);
idx++;
const items = [];
let pendingBeforeForNext = [...setKeywordComments.after];
const mergeUnique = (target, source) => {
for (const comment of source) {
if (!target.includes(comment)) {
target.push(comment);
}
}
};
const addUniquePositionedComments = (component, position, comments) => {
if (comments.length === 0) {
return;
}
const existing = component.getPositionedComments(position);
const newOnes = comments.filter(comment => !existing.includes(comment));
if (newOnes.length > 0) {
component.addPositionedComments(position, newOnes);
}
};
while (idx < lexemes.length) {
const currentLexeme = lexemes[idx];
if (!currentLexeme) {
break;
}
// Break once we reach the start of the next clause (e.g. WHERE, FROM, RETURNING)
if (currentLexeme.value === "where" || currentLexeme.value === "from" || currentLexeme.value === "returning") {
break;
}
if (!(currentLexeme.type & (Lexeme_1.TokenType.Identifier | Lexeme_1.TokenType.Function | Lexeme_1.TokenType.Type | Lexeme_1.TokenType.OpenBracket))) {
break;
}
const columnStartComments = (0, LexemeCommentUtils_1.extractLexemeComments)(currentLexeme);
const columnParseResult = FullNameParser_1.FullNameParser.parseFromLexeme(lexemes, idx);
idx = columnParseResult.newIndex;
const equalsLexeme = lexemes[idx];
if (!equalsLexeme || !(equalsLexeme.type & Lexeme_1.TokenType.Operator) || equalsLexeme.value !== "=") {
throw new Error(`Syntax error at position ${idx}: Expected '=' after column name in SET clause.`);
}
const equalsComments = (0, LexemeCommentUtils_1.extractLexemeComments)(equalsLexeme);
idx++;
// Parse value expression for the assignment.
const valueParseResult = ValueParser_1.ValueParser.parseFromLexeme(lexemes, idx);
idx = valueParseResult.newIndex;
const setItem = new Clause_1.SetClauseItem({ namespaces: columnParseResult.namespaces, column: columnParseResult.name }, valueParseResult.value);
// Attach comments that should appear before the assignment.
const beforeComments = [];
mergeUnique(beforeComments, pendingBeforeForNext);
mergeUnique(beforeComments, columnStartComments.before);
if (beforeComments.length > 0) {
addUniquePositionedComments(columnParseResult.name, "before", beforeComments);
}
pendingBeforeForNext = [];
// Preserve comments that trail the column identifier itself.
if (columnStartComments.after.length > 0) {
const afterComments = [];
mergeUnique(afterComments, columnStartComments.after);
addUniquePositionedComments(columnParseResult.name, "after", afterComments);
}
// Comments immediately before '=' belong to the assignment item.
if (equalsComments.before.length > 0) {
const equalsBefore = [];
mergeUnique(equalsBefore, equalsComments.before);
addUniquePositionedComments(columnParseResult.name, "after", equalsBefore);
}
// Comments captured after '=' should precede the value expression.
if (equalsComments.after.length > 0) {
const equalsAfter = [];
mergeUnique(equalsAfter, equalsComments.after);
addUniquePositionedComments(valueParseResult.value, "before", equalsAfter);
}
items.push(setItem);
if (((_a = lexemes[idx]) === null || _a === void 0 ? void 0 : _a.type) === Lexeme_1.TokenType.Comma) {
const commaLexeme = lexemes[idx];
const commaComments = (0, LexemeCommentUtils_1.extractLexemeComments)(commaLexeme);
idx++;
// Comments that appear before the comma belong to the current item.
if (commaComments.before.length > 0) {
const commaBefore = [];
mergeUnique(commaBefore, commaComments.before);
addUniquePositionedComments(setItem, "after", commaBefore);
}
const nextBefore = [];
mergeUnique(nextBefore, commaComments.after);
pendingBeforeForNext = nextBefore;
continue;
}
break;
}
if (pendingBeforeForNext.length > 0 && items.length > 0) {
const trailingComments = [];
mergeUnique(trailingComments, pendingBeforeForNext);
if (trailingComments.length > 0) {
addUniquePositionedComments(items[items.length - 1], "after", trailingComments);
}
}
const setClause = new Clause_1.SetClause(items);
if (setKeywordComments.before.length > 0) {
setClause.addPositionedComments("before", setKeywordComments.before);
}
return { setClause, newIndex: idx };
}
}
exports.SetClauseParser = SetClauseParser;
//# sourceMappingURL=SetClauseParser.js.map