rawsql-ts
Version:
High-performance SQL parser and AST analyzer written in TypeScript. Provides fast parsing and advanced transformation capabilities.
231 lines • 8.97 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AstCommentAttachmentExtractor = void 0;
exports.extractAstCommentAttachments = extractAstCommentAttachments;
const SelectQuery_1 = require("../models/SelectQuery");
const Clause_1 = require("../models/Clause");
const SqlComponent_1 = require("../models/SqlComponent");
/**
* Extracts comment attachment facts already represented in the SQL AST.
*
* @remarks This API does not infer product-specific meanings or rewrite
* comments. It returns a deterministic flat list without mutating the input AST.
*/
class AstCommentAttachmentExtractor {
constructor() {
this.attachments = [];
this.visited = new Set();
this.suppressedDetachedCommentArrays = new WeakSet();
}
/**
* Extract comment attachment facts from a SQL AST component.
*/
static extract(input) {
if (!input) {
return [];
}
const extractor = new AstCommentAttachmentExtractor();
extractor.visit(input);
return extractor.attachments;
}
visit(value) {
if (!value || typeof value !== "object") {
return;
}
if (this.visited.has(value)) {
return;
}
this.visited.add(value);
if (Array.isArray(value)) {
for (const item of value) {
this.visit(item);
}
return;
}
if (value instanceof SqlComponent_1.SqlComponent) {
this.visitSqlComponent(value);
return;
}
}
visitSqlComponent(component) {
this.emitHeaderComments(component);
this.emitPositionedComments(component, "before");
if (!(component instanceof SelectQuery_1.SimpleSelectQuery)) {
this.emitLegacyComments(component);
}
if (component instanceof Clause_1.SelectItem) {
this.emitPositionedComments(component, "after");
this.emitSelectItemKeywordComments(component);
this.visit(component.value);
this.visit(component.identifier);
return;
}
if (component instanceof Clause_1.CommonTable) {
this.visit(component.aliasExpression);
this.visit(component.query);
this.emitPositionedComments(component, "after");
return;
}
if (component instanceof Clause_1.WithClause) {
this.visit(component.tables);
this.emitDetachedComments(component.trailingComments);
this.emitDetachedComments(component.globalComments);
this.emitPositionedComments(component, "after");
return;
}
if (component instanceof SelectQuery_1.SimpleSelectQuery) {
this.suppressWithClauseTrailingCommentsAlreadyCopiedToQuery(component);
this.visit(component.withClause);
this.emitLegacyComments(component);
this.visit(component.selectClause);
this.visit(component.fromClause);
this.visit(component.whereClause);
this.visit(component.groupByClause);
this.visit(component.havingClause);
this.visit(component.windowClause);
this.visit(component.orderByClause);
this.visit(component.limitClause);
this.visit(component.offsetClause);
this.visit(component.fetchClause);
this.visit(component.forClause);
this.emitPositionedComments(component, "after");
return;
}
if (component instanceof SelectQuery_1.BinarySelectQuery) {
this.visit(component.left);
this.visit(component.right);
this.emitPositionedComments(component, "after");
return;
}
if (component instanceof SelectQuery_1.ValuesQuery) {
this.visit(component.withClause);
this.visit(component.tuples);
this.emitPositionedComments(component, "after");
return;
}
if (component instanceof Clause_1.SelectClause) {
this.visit(component.distinct);
this.visit(component.hints);
this.visit(component.items);
this.emitPositionedComments(component, "after");
return;
}
if (component instanceof Clause_1.SourceAliasExpression) {
this.visit(component.table);
this.visit(component.columns);
this.emitPositionedComments(component, "after");
return;
}
this.visitFallbackProperties(component);
this.emitPositionedComments(component, "after");
}
visitFallbackProperties(component) {
for (const key of Object.keys(component)) {
if (this.shouldSkipProperty(key)) {
continue;
}
this.visit(component[key]);
}
}
shouldSkipProperty(key) {
return key === "comments" ||
key === "positionedComments" ||
key === "headerComments" ||
key === "trailingComments" ||
key === "globalComments" ||
key === "asKeywordPositionedComments" ||
key === "asKeywordComments" ||
key === "aliasPositionedComments" ||
key === "aliasComments" ||
key === "cteNameCache";
}
emitHeaderComments(component) {
if (!this.isSelectQuery(component)) {
return;
}
this.emitComments(component.headerComments, "leading", component);
}
emitLegacyComments(component) {
this.emitComments(component.comments, "detached");
}
emitPositionedComments(component, position) {
if (!component.positionedComments) {
return;
}
for (const positionedComment of component.positionedComments) {
if (positionedComment.position !== position) {
continue;
}
this.emitComments(positionedComment.comments, position === "before" ? "leading" : "trailing", component);
}
}
emitSelectItemKeywordComments(selectItem) {
const itemWithCommentFields = selectItem;
this.emitKeywordPositionedComments(itemWithCommentFields.asKeywordPositionedComments, selectItem, "inner");
this.emitComments(itemWithCommentFields.asKeywordComments, "inner", selectItem);
this.emitKeywordPositionedComments(itemWithCommentFields.aliasPositionedComments, selectItem);
this.emitComments(itemWithCommentFields.aliasComments, "trailing", selectItem);
}
emitKeywordPositionedComments(positionedComments, targetNode, placementOverride) {
if (!positionedComments) {
return;
}
for (const positionedComment of positionedComments) {
const placement = placementOverride !== null && placementOverride !== void 0 ? placementOverride : (positionedComment.position === "before" ? "leading" : "trailing");
this.emitComments(positionedComment.comments, placement, targetNode);
}
}
emitDetachedComments(comments) {
if (comments && this.suppressedDetachedCommentArrays.has(comments)) {
return;
}
this.emitComments(comments, "detached");
}
emitComments(comments, placement, targetNode) {
if (!comments) {
return;
}
for (const text of comments) {
this.attachments.push({
text,
sourceOrder: this.attachments.length,
placement,
...(targetNode ? { targetNode } : {})
});
}
}
isSelectQuery(component) {
return "__selectQueryType" in component &&
component.__selectQueryType === "SelectQuery";
}
suppressWithClauseTrailingCommentsAlreadyCopiedToQuery(query) {
var _a;
const trailingComments = (_a = query.withClause) === null || _a === void 0 ? void 0 : _a.trailingComments;
if (!trailingComments || !query.comments) {
return;
}
if (this.containsCommentSequence(query.comments, trailingComments)) {
this.suppressedDetachedCommentArrays.add(trailingComments);
}
}
containsCommentSequence(source, candidate) {
if (candidate.length === 0) {
return true;
}
for (let start = 0; start <= source.length - candidate.length; start++) {
const matches = candidate.every((comment, index) => source[start + index] === comment);
if (matches) {
return true;
}
}
return false;
}
}
exports.AstCommentAttachmentExtractor = AstCommentAttachmentExtractor;
/**
* Extract comment attachment facts from a SQL AST component.
*/
function extractAstCommentAttachments(input) {
return AstCommentAttachmentExtractor.extract(input);
}
//# sourceMappingURL=AstCommentAttachmentExtractor.js.map