rawsql-ts 0.11.37-beta → 0.11.38-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/index.min.js +54 -51
- package/dist/esm/index.min.js.map +3 -3
- package/dist/esm/src/models/Lexeme.d.ts +8 -0
- package/dist/esm/src/models/SqlComponent.d.ts +20 -0
- package/dist/esm/src/models/SqlComponent.js +46 -0
- package/dist/esm/src/models/SqlComponent.js.map +1 -1
- package/dist/esm/src/parsers/CommandExpressionParser.d.ts +9 -0
- package/dist/esm/src/parsers/CommandExpressionParser.js +118 -14
- package/dist/esm/src/parsers/CommandExpressionParser.js.map +1 -1
- package/dist/esm/src/parsers/CommonTableParser.d.ts +8 -0
- package/dist/esm/src/parsers/CommonTableParser.js +127 -35
- package/dist/esm/src/parsers/CommonTableParser.js.map +1 -1
- package/dist/esm/src/parsers/FromClauseParser.js +2 -2
- package/dist/esm/src/parsers/FromClauseParser.js.map +1 -1
- package/dist/esm/src/parsers/FullNameParser.js +13 -1
- package/dist/esm/src/parsers/FullNameParser.js.map +1 -1
- package/dist/esm/src/parsers/FunctionExpressionParser.js +37 -3
- package/dist/esm/src/parsers/FunctionExpressionParser.js.map +1 -1
- package/dist/esm/src/parsers/JoinClauseParser.d.ts +4 -0
- package/dist/esm/src/parsers/JoinClauseParser.js +54 -20
- package/dist/esm/src/parsers/JoinClauseParser.js.map +1 -1
- package/dist/esm/src/parsers/OrderByClauseParser.js +39 -8
- package/dist/esm/src/parsers/OrderByClauseParser.js.map +1 -1
- package/dist/esm/src/parsers/ParenExpressionParser.js +1 -1
- package/dist/esm/src/parsers/ParenExpressionParser.js.map +1 -1
- package/dist/esm/src/parsers/SelectClauseParser.d.ts +10 -0
- package/dist/esm/src/parsers/SelectClauseParser.js +124 -12
- package/dist/esm/src/parsers/SelectClauseParser.js.map +1 -1
- package/dist/esm/src/parsers/SelectQueryParser.d.ts +12 -0
- package/dist/esm/src/parsers/SelectQueryParser.js +277 -115
- package/dist/esm/src/parsers/SelectQueryParser.js.map +1 -1
- package/dist/esm/src/parsers/SourceAliasExpressionParser.js +15 -4
- package/dist/esm/src/parsers/SourceAliasExpressionParser.js.map +1 -1
- package/dist/esm/src/parsers/SourceParser.js +33 -2
- package/dist/esm/src/parsers/SourceParser.js.map +1 -1
- package/dist/esm/src/parsers/SqlPrintTokenParser.d.ts +62 -0
- package/dist/esm/src/parsers/SqlPrintTokenParser.js +700 -32
- package/dist/esm/src/parsers/SqlPrintTokenParser.js.map +1 -1
- package/dist/esm/src/parsers/SqlTokenizer.d.ts +1 -4
- package/dist/esm/src/parsers/SqlTokenizer.js +108 -52
- package/dist/esm/src/parsers/SqlTokenizer.js.map +1 -1
- package/dist/esm/src/parsers/ValueParser.d.ts +4 -0
- package/dist/esm/src/parsers/ValueParser.js +119 -20
- package/dist/esm/src/parsers/ValueParser.js.map +1 -1
- package/dist/esm/src/parsers/WithClauseParser.d.ts +4 -0
- package/dist/esm/src/parsers/WithClauseParser.js +64 -35
- package/dist/esm/src/parsers/WithClauseParser.js.map +1 -1
- package/dist/esm/src/tokenReaders/CommandTokenReader.js +9 -1
- package/dist/esm/src/tokenReaders/CommandTokenReader.js.map +1 -1
- package/dist/esm/src/transformers/LinePrinter.d.ts +17 -2
- package/dist/esm/src/transformers/LinePrinter.js +39 -10
- package/dist/esm/src/transformers/LinePrinter.js.map +1 -1
- package/dist/esm/src/transformers/SqlFormatter.d.ts +10 -0
- package/dist/esm/src/transformers/SqlFormatter.js +1 -1
- package/dist/esm/src/transformers/SqlFormatter.js.map +1 -1
- package/dist/esm/src/transformers/SqlPrinter.d.ts +6 -1
- package/dist/esm/src/transformers/SqlPrinter.js +76 -31
- package/dist/esm/src/transformers/SqlPrinter.js.map +1 -1
- package/dist/esm/src/utils/CommentEditor.d.ts +12 -5
- package/dist/esm/src/utils/CommentEditor.js +67 -28
- package/dist/esm/src/utils/CommentEditor.js.map +1 -1
- package/dist/esm/src/utils/stringUtils.js +21 -9
- package/dist/esm/src/utils/stringUtils.js.map +1 -1
- package/dist/esm/tsconfig.browser.tsbuildinfo +1 -1
- package/dist/index.min.js +54 -51
- package/dist/index.min.js.map +3 -3
- package/dist/src/models/Lexeme.d.ts +8 -0
- package/dist/src/models/SqlComponent.d.ts +20 -0
- package/dist/src/models/SqlComponent.js +46 -0
- package/dist/src/models/SqlComponent.js.map +1 -1
- package/dist/src/parsers/CommandExpressionParser.d.ts +9 -0
- package/dist/src/parsers/CommandExpressionParser.js +118 -14
- package/dist/src/parsers/CommandExpressionParser.js.map +1 -1
- package/dist/src/parsers/CommonTableParser.d.ts +8 -0
- package/dist/src/parsers/CommonTableParser.js +127 -35
- package/dist/src/parsers/CommonTableParser.js.map +1 -1
- package/dist/src/parsers/FromClauseParser.js +2 -2
- package/dist/src/parsers/FromClauseParser.js.map +1 -1
- package/dist/src/parsers/FullNameParser.js +13 -1
- package/dist/src/parsers/FullNameParser.js.map +1 -1
- package/dist/src/parsers/FunctionExpressionParser.js +37 -3
- package/dist/src/parsers/FunctionExpressionParser.js.map +1 -1
- package/dist/src/parsers/JoinClauseParser.d.ts +4 -0
- package/dist/src/parsers/JoinClauseParser.js +54 -20
- package/dist/src/parsers/JoinClauseParser.js.map +1 -1
- package/dist/src/parsers/OrderByClauseParser.js +39 -8
- package/dist/src/parsers/OrderByClauseParser.js.map +1 -1
- package/dist/src/parsers/ParenExpressionParser.js +1 -1
- package/dist/src/parsers/ParenExpressionParser.js.map +1 -1
- package/dist/src/parsers/SelectClauseParser.d.ts +10 -0
- package/dist/src/parsers/SelectClauseParser.js +124 -12
- package/dist/src/parsers/SelectClauseParser.js.map +1 -1
- package/dist/src/parsers/SelectQueryParser.d.ts +12 -0
- package/dist/src/parsers/SelectQueryParser.js +280 -115
- package/dist/src/parsers/SelectQueryParser.js.map +1 -1
- package/dist/src/parsers/SourceAliasExpressionParser.js +15 -4
- package/dist/src/parsers/SourceAliasExpressionParser.js.map +1 -1
- package/dist/src/parsers/SourceParser.js +33 -2
- package/dist/src/parsers/SourceParser.js.map +1 -1
- package/dist/src/parsers/SqlPrintTokenParser.d.ts +62 -0
- package/dist/src/parsers/SqlPrintTokenParser.js +700 -32
- package/dist/src/parsers/SqlPrintTokenParser.js.map +1 -1
- package/dist/src/parsers/SqlTokenizer.d.ts +1 -4
- package/dist/src/parsers/SqlTokenizer.js +108 -52
- package/dist/src/parsers/SqlTokenizer.js.map +1 -1
- package/dist/src/parsers/ValueParser.d.ts +4 -0
- package/dist/src/parsers/ValueParser.js +119 -20
- package/dist/src/parsers/ValueParser.js.map +1 -1
- package/dist/src/parsers/WithClauseParser.d.ts +4 -0
- package/dist/src/parsers/WithClauseParser.js +64 -35
- package/dist/src/parsers/WithClauseParser.js.map +1 -1
- package/dist/src/tokenReaders/CommandTokenReader.js +9 -1
- package/dist/src/tokenReaders/CommandTokenReader.js.map +1 -1
- package/dist/src/transformers/LinePrinter.d.ts +17 -2
- package/dist/src/transformers/LinePrinter.js +39 -10
- package/dist/src/transformers/LinePrinter.js.map +1 -1
- package/dist/src/transformers/SqlFormatter.d.ts +10 -0
- package/dist/src/transformers/SqlFormatter.js +4 -1
- package/dist/src/transformers/SqlFormatter.js.map +1 -1
- package/dist/src/transformers/SqlPrinter.d.ts +6 -1
- package/dist/src/transformers/SqlPrinter.js +76 -31
- package/dist/src/transformers/SqlPrinter.js.map +1 -1
- package/dist/src/utils/CommentEditor.d.ts +12 -5
- package/dist/src/utils/CommentEditor.js +67 -28
- package/dist/src/utils/CommentEditor.js.map +1 -1
- package/dist/src/utils/stringUtils.js +21 -9
- package/dist/src/utils/stringUtils.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -118,10 +118,31 @@ export const PRESETS = {
|
|
|
118
118
|
},
|
|
119
119
|
};
|
|
120
120
|
export class SqlPrintTokenParser {
|
|
121
|
+
static getSelfHandlingComponentTypes() {
|
|
122
|
+
if (!this._selfHandlingComponentTypes) {
|
|
123
|
+
this._selfHandlingComponentTypes = new Set([
|
|
124
|
+
SimpleSelectQuery.kind,
|
|
125
|
+
SelectItem.kind,
|
|
126
|
+
CaseKeyValuePair.kind,
|
|
127
|
+
SwitchCaseArgument.kind,
|
|
128
|
+
ColumnReference.kind,
|
|
129
|
+
LiteralValue.kind,
|
|
130
|
+
ParameterExpression.kind,
|
|
131
|
+
TableSource.kind,
|
|
132
|
+
SourceAliasExpression.kind,
|
|
133
|
+
TypeValue.kind,
|
|
134
|
+
FunctionCall.kind,
|
|
135
|
+
IdentifierString.kind,
|
|
136
|
+
QualifiedName.kind
|
|
137
|
+
]);
|
|
138
|
+
}
|
|
139
|
+
return this._selfHandlingComponentTypes;
|
|
140
|
+
}
|
|
121
141
|
constructor(options) {
|
|
122
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
142
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
123
143
|
this.handlers = new Map();
|
|
124
144
|
this.index = 1;
|
|
145
|
+
this.commentStyle = 'block';
|
|
125
146
|
if (options === null || options === void 0 ? void 0 : options.preset) {
|
|
126
147
|
const preset = options.preset;
|
|
127
148
|
options = Object.assign(Object.assign({}, preset), options);
|
|
@@ -135,6 +156,7 @@ export class SqlPrintTokenParser {
|
|
|
135
156
|
start: (_e = (_d = options === null || options === void 0 ? void 0 : options.identifierEscape) === null || _d === void 0 ? void 0 : _d.start) !== null && _e !== void 0 ? _e : '"',
|
|
136
157
|
end: (_g = (_f = options === null || options === void 0 ? void 0 : options.identifierEscape) === null || _f === void 0 ? void 0 : _f.end) !== null && _g !== void 0 ? _g : '"'
|
|
137
158
|
});
|
|
159
|
+
this.commentStyle = (_h = options === null || options === void 0 ? void 0 : options.commentStyle) !== null && _h !== void 0 ? _h : 'block';
|
|
138
160
|
this.handlers.set(ValueList.kind, (expr) => this.visitValueList(expr));
|
|
139
161
|
this.handlers.set(ColumnReference.kind, (expr) => this.visitColumnReference(expr));
|
|
140
162
|
this.handlers.set(QualifiedName.kind, (expr) => this.visitQualifiedName(expr));
|
|
@@ -218,10 +240,23 @@ export class SqlPrintTokenParser {
|
|
|
218
240
|
*/
|
|
219
241
|
visitBinarySelectQuery(arg) {
|
|
220
242
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '');
|
|
221
|
-
//
|
|
222
|
-
if (arg.
|
|
223
|
-
|
|
224
|
-
|
|
243
|
+
// Handle positioned comments for BinarySelectQuery (unified spec)
|
|
244
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
245
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
246
|
+
// Clear positioned comments to prevent duplicate processing
|
|
247
|
+
arg.positionedComments = null;
|
|
248
|
+
}
|
|
249
|
+
else if (arg.headerComments && arg.headerComments.length > 0) {
|
|
250
|
+
// Fallback to legacy headerComments if no positioned comments
|
|
251
|
+
// For smart comment style, treat headerComments as a single multi-line block
|
|
252
|
+
if (this.commentStyle === 'smart' && arg.headerComments.length > 1) {
|
|
253
|
+
const mergedHeaderComment = this.createHeaderMultiLineCommentBlock(arg.headerComments);
|
|
254
|
+
token.innerTokens.push(mergedHeaderComment);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
const headerCommentBlocks = this.createCommentBlocks(arg.headerComments);
|
|
258
|
+
token.innerTokens.push(...headerCommentBlocks);
|
|
259
|
+
}
|
|
225
260
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
226
261
|
}
|
|
227
262
|
token.innerTokens.push(this.visit(arg.left));
|
|
@@ -249,7 +284,25 @@ export class SqlPrintTokenParser {
|
|
|
249
284
|
token.innerTokens.push(SqlPrintTokenParser.DOT_TOKEN);
|
|
250
285
|
}
|
|
251
286
|
}
|
|
252
|
-
|
|
287
|
+
// Handle name and its comments carefully
|
|
288
|
+
// We need to prevent double processing by temporarily clearing the name's comments,
|
|
289
|
+
// then process them at the QualifiedName level
|
|
290
|
+
const originalNameComments = arg.name.positionedComments;
|
|
291
|
+
const originalNameLegacyComments = arg.name.comments;
|
|
292
|
+
// Temporarily clear name's comments to prevent double processing
|
|
293
|
+
arg.name.positionedComments = null;
|
|
294
|
+
arg.name.comments = null;
|
|
295
|
+
const nameToken = arg.name.accept(this);
|
|
296
|
+
token.innerTokens.push(nameToken);
|
|
297
|
+
// Restore original comments
|
|
298
|
+
arg.name.positionedComments = originalNameComments;
|
|
299
|
+
arg.name.comments = originalNameLegacyComments;
|
|
300
|
+
// Apply the name's comments to the qualified name token
|
|
301
|
+
if (this.hasPositionedComments(arg.name) || this.hasLegacyComments(arg.name)) {
|
|
302
|
+
this.addComponentComments(token, arg.name);
|
|
303
|
+
}
|
|
304
|
+
// Also handle any comments directly on the QualifiedName itself
|
|
305
|
+
this.addComponentComments(token, arg);
|
|
253
306
|
return token;
|
|
254
307
|
}
|
|
255
308
|
visitPartitionByClause(arg) {
|
|
@@ -328,49 +381,312 @@ export class SqlPrintTokenParser {
|
|
|
328
381
|
// Fallback (just in case)
|
|
329
382
|
return { token, params: [] };
|
|
330
383
|
}
|
|
384
|
+
/**
|
|
385
|
+
* Check if a component handles its own comments
|
|
386
|
+
*/
|
|
387
|
+
componentHandlesOwnComments(component) {
|
|
388
|
+
// First check if component has a handlesOwnComments method
|
|
389
|
+
if ('handlesOwnComments' in component && typeof component.handlesOwnComments === 'function') {
|
|
390
|
+
return component.handlesOwnComments();
|
|
391
|
+
}
|
|
392
|
+
return SqlPrintTokenParser.getSelfHandlingComponentTypes().has(component.getKind());
|
|
393
|
+
}
|
|
331
394
|
visit(arg) {
|
|
332
395
|
const handler = this.handlers.get(arg.getKind());
|
|
333
396
|
if (handler) {
|
|
334
397
|
const token = handler(arg);
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
? arg.handlesOwnComments()
|
|
338
|
-
: arg.getKind() === SimpleSelectQuery.kind; // Fallback for existing SimpleSelectQuery
|
|
339
|
-
if (!handlesOwnComments) {
|
|
340
|
-
this.addCommentsToToken(token, arg.comments);
|
|
398
|
+
if (!this.componentHandlesOwnComments(arg)) {
|
|
399
|
+
this.addComponentComments(token, arg);
|
|
341
400
|
}
|
|
342
401
|
return token;
|
|
343
402
|
}
|
|
344
403
|
throw new Error(`[SqlPrintTokenParser] No handler for kind: ${arg.getKind().toString()}`);
|
|
345
404
|
}
|
|
405
|
+
/**
|
|
406
|
+
* Check if a component has positioned comments
|
|
407
|
+
*/
|
|
408
|
+
hasPositionedComments(component) {
|
|
409
|
+
var _a, _b;
|
|
410
|
+
return ((_b = (_a = component.positionedComments) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Check if a component has legacy comments
|
|
414
|
+
*/
|
|
415
|
+
hasLegacyComments(component) {
|
|
416
|
+
var _a, _b;
|
|
417
|
+
return ((_b = (_a = component.comments) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Centralized comment handling - checks positioned comments first, falls back to legacy
|
|
421
|
+
*/
|
|
422
|
+
addComponentComments(token, component) {
|
|
423
|
+
if (this.hasPositionedComments(component)) {
|
|
424
|
+
this.addPositionedCommentsToToken(token, component);
|
|
425
|
+
}
|
|
426
|
+
else if (this.hasLegacyComments(component)) {
|
|
427
|
+
this.addCommentsToToken(token, component.comments);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Adds positioned comment tokens to a SqlPrintToken for inline formatting
|
|
432
|
+
*/
|
|
433
|
+
addPositionedCommentsToToken(token, component) {
|
|
434
|
+
if (!this.hasPositionedComments(component)) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
// Handle 'before' comments - add inline at the beginning with spaces
|
|
438
|
+
const beforeComments = component.getPositionedComments('before');
|
|
439
|
+
if (beforeComments.length > 0) {
|
|
440
|
+
const commentBlocks = this.createCommentBlocks(beforeComments);
|
|
441
|
+
// Create a single inline sequence: /* comment */ content
|
|
442
|
+
const beforeTokens = [];
|
|
443
|
+
for (const commentBlock of commentBlocks) {
|
|
444
|
+
beforeTokens.push(commentBlock);
|
|
445
|
+
beforeTokens.push(new SqlPrintToken(SqlPrintTokenType.space, ' '));
|
|
446
|
+
}
|
|
447
|
+
// Insert before the existing content
|
|
448
|
+
token.innerTokens.unshift(...beforeTokens);
|
|
449
|
+
}
|
|
450
|
+
// Handle 'after' comments - add inline after the main content
|
|
451
|
+
const afterComments = component.getPositionedComments('after');
|
|
452
|
+
if (afterComments.length > 0) {
|
|
453
|
+
const commentBlocks = this.createCommentBlocks(afterComments);
|
|
454
|
+
// Append after comments with spaces for inline formatting
|
|
455
|
+
for (const commentBlock of commentBlocks) {
|
|
456
|
+
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.space, ' '));
|
|
457
|
+
token.innerTokens.push(commentBlock);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Clear positioned comments to prevent duplicate processing (unified spec)
|
|
461
|
+
// Only clear for specific component types that are known to have duplication issues
|
|
462
|
+
const componentsWithDuplicationIssues = [
|
|
463
|
+
SqlPrintTokenContainerType.CaseExpression,
|
|
464
|
+
SqlPrintTokenContainerType.SwitchCaseArgument,
|
|
465
|
+
SqlPrintTokenContainerType.CaseKeyValuePair,
|
|
466
|
+
SqlPrintTokenContainerType.SelectClause, // SELECT clauses have manual + automatic processing
|
|
467
|
+
SqlPrintTokenContainerType.LiteralValue,
|
|
468
|
+
SqlPrintTokenContainerType.IdentifierString,
|
|
469
|
+
SqlPrintTokenContainerType.DistinctOn,
|
|
470
|
+
SqlPrintTokenContainerType.SourceAliasExpression,
|
|
471
|
+
SqlPrintTokenContainerType.SimpleSelectQuery,
|
|
472
|
+
SqlPrintTokenContainerType.WhereClause // WHERE clauses also have duplication issues
|
|
473
|
+
];
|
|
474
|
+
if (token.containerType && componentsWithDuplicationIssues.includes(token.containerType)) {
|
|
475
|
+
component.positionedComments = null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
346
478
|
/**
|
|
347
479
|
* Adds comment tokens to a SqlPrintToken based on the comments array
|
|
348
480
|
*/
|
|
349
481
|
addCommentsToToken(token, comments) {
|
|
350
|
-
if (!comments || comments
|
|
482
|
+
if (!(comments === null || comments === void 0 ? void 0 : comments.length)) {
|
|
351
483
|
return;
|
|
352
484
|
}
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
485
|
+
// For multiple comments, create inline comment sequence instead of separate blocks
|
|
486
|
+
if (comments.length > 1) {
|
|
487
|
+
const inlineComments = this.createInlineCommentSequence(comments);
|
|
488
|
+
this.insertCommentBlocksWithSpacing(token, inlineComments);
|
|
489
|
+
}
|
|
490
|
+
else {
|
|
491
|
+
// Create CommentBlock containers for single comment
|
|
492
|
+
const commentBlocks = this.createCommentBlocks(comments);
|
|
357
493
|
this.insertCommentBlocksWithSpacing(token, commentBlocks);
|
|
358
494
|
}
|
|
359
495
|
}
|
|
496
|
+
/**
|
|
497
|
+
* Creates inline comment sequence for multiple comments without newlines
|
|
498
|
+
*/
|
|
499
|
+
createInlineCommentSequence(comments) {
|
|
500
|
+
const commentTokens = [];
|
|
501
|
+
for (let i = 0; i < comments.length; i++) {
|
|
502
|
+
const comment = comments[i];
|
|
503
|
+
if (comment.trim()) {
|
|
504
|
+
// Add comment token directly
|
|
505
|
+
const commentToken = new SqlPrintToken(SqlPrintTokenType.comment, this.formatBlockComment(comment));
|
|
506
|
+
commentTokens.push(commentToken);
|
|
507
|
+
// Add space between comments (except after last comment)
|
|
508
|
+
if (i < comments.length - 1) {
|
|
509
|
+
const spaceToken = new SqlPrintToken(SqlPrintTokenType.space, ' ');
|
|
510
|
+
commentTokens.push(spaceToken);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
return commentTokens;
|
|
515
|
+
}
|
|
360
516
|
/**
|
|
361
517
|
* Creates CommentBlock containers for the given comments.
|
|
362
518
|
* Each CommentBlock contains: Comment -> CommentNewline -> Space
|
|
363
519
|
* This structure supports both oneliner and multiline formatting modes.
|
|
364
520
|
*/
|
|
365
521
|
createCommentBlocks(comments) {
|
|
522
|
+
if (this.commentStyle === 'smart') {
|
|
523
|
+
return this.createSmartCommentBlocks(comments);
|
|
524
|
+
}
|
|
525
|
+
// Block style (default) - each comment gets its own block
|
|
366
526
|
const commentBlocks = [];
|
|
367
527
|
for (const comment of comments) {
|
|
368
|
-
|
|
528
|
+
// Accept comments that have content after trim OR are separator lines OR are empty (for structure preservation)
|
|
529
|
+
const trimmed = comment.trim();
|
|
530
|
+
const isSeparatorLine = /^[-=_+*#]+$/.test(trimmed);
|
|
531
|
+
if (trimmed || isSeparatorLine || comment === '') {
|
|
369
532
|
commentBlocks.push(this.createSingleCommentBlock(comment));
|
|
370
533
|
}
|
|
371
534
|
}
|
|
372
535
|
return commentBlocks;
|
|
373
536
|
}
|
|
537
|
+
/**
|
|
538
|
+
* Creates smart comment blocks by merging consecutive block comments into multi-line format
|
|
539
|
+
*/
|
|
540
|
+
createSmartCommentBlocks(comments) {
|
|
541
|
+
const commentBlocks = [];
|
|
542
|
+
const blockComments = [];
|
|
543
|
+
const flushBlockComments = () => {
|
|
544
|
+
if (blockComments.length > 0) {
|
|
545
|
+
if (blockComments.length === 1) {
|
|
546
|
+
// Single comment - keep as-is
|
|
547
|
+
commentBlocks.push(this.createSingleCommentBlock(blockComments[0]));
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
// Multiple consecutive comments - create multi-line block
|
|
551
|
+
commentBlocks.push(this.createMultiLineCommentBlock(blockComments));
|
|
552
|
+
}
|
|
553
|
+
blockComments.length = 0;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
for (const comment of comments) {
|
|
557
|
+
const trimmed = comment.trim();
|
|
558
|
+
const isSeparatorLine = /^[-=_+*#]+$/.test(trimmed);
|
|
559
|
+
if (!trimmed && !isSeparatorLine && comment !== '') {
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
// Check if this is a block comment that should be merged
|
|
563
|
+
if (this.shouldMergeComment(trimmed)) {
|
|
564
|
+
blockComments.push(comment);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// Flush any accumulated block comments first
|
|
568
|
+
flushBlockComments();
|
|
569
|
+
// Add this comment as-is
|
|
570
|
+
commentBlocks.push(this.createSingleCommentBlock(comment));
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
// Flush any remaining block comments
|
|
574
|
+
flushBlockComments();
|
|
575
|
+
return commentBlocks;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Determines if a comment should be merged with consecutive comments
|
|
579
|
+
*/
|
|
580
|
+
shouldMergeComment(trimmed) {
|
|
581
|
+
const isSeparatorLine = /^[-=_+*#]+$/.test(trimmed);
|
|
582
|
+
// Don't merge line comments unless they are separator-only lines
|
|
583
|
+
if (!isSeparatorLine && trimmed.startsWith('--')) {
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
// Don't merge if it's already a proper multi-line block comment
|
|
587
|
+
if (trimmed.startsWith('/*') && trimmed.endsWith('*/') && trimmed.includes('\n')) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
// Merge all other content including separator lines, plain text, and single-line block comments
|
|
591
|
+
// Separator lines within comment blocks should be merged together
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Creates a multi-line block comment structure from consecutive comments
|
|
596
|
+
* Returns a CommentBlock containing multiple comment lines for proper LinePrinter integration
|
|
597
|
+
*/
|
|
598
|
+
createMultiLineCommentBlock(comments) {
|
|
599
|
+
const lines = [];
|
|
600
|
+
for (const comment of comments) {
|
|
601
|
+
const trimmed = comment.trim();
|
|
602
|
+
// Remove existing /* */ markers if present and extract content
|
|
603
|
+
if (trimmed.startsWith('/*') && trimmed.endsWith('*/')) {
|
|
604
|
+
const content = trimmed.slice(2, -2);
|
|
605
|
+
if (content.trim()) {
|
|
606
|
+
// Sanitize the content (only remove /* and */)
|
|
607
|
+
const sanitized = content
|
|
608
|
+
.replace(/\*\//g, '*') // Remove */ sequences
|
|
609
|
+
.replace(/\/\*/g, '*'); // Remove /* sequences
|
|
610
|
+
// Split multi-line content and add each line
|
|
611
|
+
const contentLines = sanitized.split('\n').map(line => line.trim()).filter(line => line);
|
|
612
|
+
lines.push(...contentLines);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
else if (trimmed) {
|
|
616
|
+
// Sanitize plain text content
|
|
617
|
+
const sanitized = trimmed
|
|
618
|
+
.replace(/\*\//g, '*') // Remove */ sequences
|
|
619
|
+
.replace(/\/\*/g, '*'); // Remove /* sequences
|
|
620
|
+
// Split plain text content by lines
|
|
621
|
+
const contentLines = sanitized.split('\n').map(line => line.trim()).filter(line => line);
|
|
622
|
+
lines.push(...contentLines);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
// Create multi-line comment block structure
|
|
626
|
+
const commentBlock = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CommentBlock);
|
|
627
|
+
if (lines.length === 0) {
|
|
628
|
+
// Empty comment
|
|
629
|
+
const commentToken = new SqlPrintToken(SqlPrintTokenType.comment, '/* */');
|
|
630
|
+
commentBlock.innerTokens.push(commentToken);
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
// Opening /*
|
|
634
|
+
const openToken = new SqlPrintToken(SqlPrintTokenType.comment, '/*');
|
|
635
|
+
commentBlock.innerTokens.push(openToken);
|
|
636
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
|
|
637
|
+
// Content lines (each as separate comment token)
|
|
638
|
+
for (const line of lines) {
|
|
639
|
+
const lineToken = new SqlPrintToken(SqlPrintTokenType.comment, ` ${line}`);
|
|
640
|
+
commentBlock.innerTokens.push(lineToken);
|
|
641
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
|
|
642
|
+
}
|
|
643
|
+
// Closing */
|
|
644
|
+
const closeToken = new SqlPrintToken(SqlPrintTokenType.comment, '*/');
|
|
645
|
+
commentBlock.innerTokens.push(closeToken);
|
|
646
|
+
}
|
|
647
|
+
// Add final newline and space for standard structure
|
|
648
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
|
|
649
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.space, ' '));
|
|
650
|
+
return commentBlock;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Creates a multi-line comment block specifically for headerComments
|
|
654
|
+
* headerComments come as pre-split lines, so we handle them differently
|
|
655
|
+
*/
|
|
656
|
+
createHeaderMultiLineCommentBlock(headerComments) {
|
|
657
|
+
// Keep all lines including empty ones to preserve structure
|
|
658
|
+
const lines = headerComments;
|
|
659
|
+
// Create multi-line comment block structure
|
|
660
|
+
const commentBlock = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CommentBlock);
|
|
661
|
+
if (lines.length === 0) {
|
|
662
|
+
// Empty comment
|
|
663
|
+
const commentToken = new SqlPrintToken(SqlPrintTokenType.comment, '/* */');
|
|
664
|
+
commentBlock.innerTokens.push(commentToken);
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
// Opening /*
|
|
668
|
+
const openToken = new SqlPrintToken(SqlPrintTokenType.comment, '/*');
|
|
669
|
+
commentBlock.innerTokens.push(openToken);
|
|
670
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
|
|
671
|
+
// Content lines (each as separate comment token)
|
|
672
|
+
for (const line of lines) {
|
|
673
|
+
// Sanitize the line content
|
|
674
|
+
const sanitized = line
|
|
675
|
+
.replace(/\*\//g, '*') // Remove */ sequences
|
|
676
|
+
.replace(/\/\*/g, '*'); // Remove /* sequences
|
|
677
|
+
const lineToken = new SqlPrintToken(SqlPrintTokenType.comment, ` ${sanitized}`);
|
|
678
|
+
commentBlock.innerTokens.push(lineToken);
|
|
679
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
|
|
680
|
+
}
|
|
681
|
+
// Closing */
|
|
682
|
+
const closeToken = new SqlPrintToken(SqlPrintTokenType.comment, '*/');
|
|
683
|
+
commentBlock.innerTokens.push(closeToken);
|
|
684
|
+
}
|
|
685
|
+
// Add final newline and space for standard structure
|
|
686
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.commentNewline, ''));
|
|
687
|
+
commentBlock.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.space, ' '));
|
|
688
|
+
return commentBlock;
|
|
689
|
+
}
|
|
374
690
|
/**
|
|
375
691
|
* Creates a single CommentBlock with the standard structure:
|
|
376
692
|
* Comment -> CommentNewline -> Space
|
|
@@ -381,8 +697,8 @@ export class SqlPrintTokenParser {
|
|
|
381
697
|
*/
|
|
382
698
|
createSingleCommentBlock(comment) {
|
|
383
699
|
const commentBlock = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CommentBlock);
|
|
384
|
-
// Add comment token
|
|
385
|
-
const commentToken = new SqlPrintToken(SqlPrintTokenType.comment, this.
|
|
700
|
+
// Add comment token - preserve original format for line comments
|
|
701
|
+
const commentToken = new SqlPrintToken(SqlPrintTokenType.comment, this.formatComment(comment));
|
|
386
702
|
commentBlock.innerTokens.push(commentToken);
|
|
387
703
|
// Add conditional newline token for multiline mode
|
|
388
704
|
const commentNewlineToken = new SqlPrintToken(SqlPrintTokenType.commentNewline, '');
|
|
@@ -392,6 +708,40 @@ export class SqlPrintTokenParser {
|
|
|
392
708
|
commentBlock.innerTokens.push(spaceToken);
|
|
393
709
|
return commentBlock;
|
|
394
710
|
}
|
|
711
|
+
/**
|
|
712
|
+
* Formats a comment, preserving line comment format for -- comments
|
|
713
|
+
* and converting others to block format for safety
|
|
714
|
+
*/
|
|
715
|
+
formatComment(comment) {
|
|
716
|
+
const trimmed = comment.trim();
|
|
717
|
+
// Smart style processing
|
|
718
|
+
if (this.commentStyle === 'smart') {
|
|
719
|
+
return this.formatCommentSmart(trimmed);
|
|
720
|
+
}
|
|
721
|
+
// Default block style processing
|
|
722
|
+
// If it's already a line comment, preserve it
|
|
723
|
+
// But exclude separator lines (lines with only dashes, equals, etc.)
|
|
724
|
+
if (trimmed.startsWith('--') && !/^--[-=_+*#]*$/.test(trimmed)) {
|
|
725
|
+
return trimmed;
|
|
726
|
+
}
|
|
727
|
+
// If it's already a block comment, preserve it (but sanitize)
|
|
728
|
+
if (trimmed.startsWith('/*') && trimmed.endsWith('*/')) {
|
|
729
|
+
// Pass the entire comment including /* and */ for proper sanitization
|
|
730
|
+
return this.formatBlockComment(trimmed);
|
|
731
|
+
}
|
|
732
|
+
// For plain text comments, convert to block format
|
|
733
|
+
return this.formatBlockComment(trimmed);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Formats comments using smart style rules:
|
|
737
|
+
* - Only multi-line block comment merging is supported
|
|
738
|
+
* - Single-line comments remain as block comments (no dash conversion)
|
|
739
|
+
*/
|
|
740
|
+
formatCommentSmart(comment) {
|
|
741
|
+
// Smart style only affects multi-line comment merging at createSmartCommentBlocks level
|
|
742
|
+
// Individual comment formatting remains the same as block style
|
|
743
|
+
return this.formatBlockComment(comment);
|
|
744
|
+
}
|
|
395
745
|
/**
|
|
396
746
|
* Inserts comment blocks into a token and handles spacing logic.
|
|
397
747
|
* Adds separator spaces for clause-level containers and manages duplicate space removal.
|
|
@@ -417,6 +767,18 @@ export class SqlPrintTokenParser {
|
|
|
417
767
|
token.innerTokens.unshift(SqlPrintTokenParser.SPACE_TOKEN, ...commentBlocks);
|
|
418
768
|
return;
|
|
419
769
|
}
|
|
770
|
+
// Special handling for IdentifierString to add space before comment
|
|
771
|
+
if (token.containerType === SqlPrintTokenContainerType.IdentifierString) {
|
|
772
|
+
// Add space before comment if not already present
|
|
773
|
+
if (token.innerTokens.length > 0) {
|
|
774
|
+
const lastToken = token.innerTokens[token.innerTokens.length - 1];
|
|
775
|
+
if (lastToken.type !== SqlPrintTokenType.space) {
|
|
776
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
token.innerTokens.push(...commentBlocks);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
420
782
|
token.innerTokens.unshift(...commentBlocks);
|
|
421
783
|
// Add a separator space after comments only for certain container types
|
|
422
784
|
// where comments need to be separated from main content
|
|
@@ -439,6 +801,38 @@ export class SqlPrintTokenParser {
|
|
|
439
801
|
}
|
|
440
802
|
}
|
|
441
803
|
}
|
|
804
|
+
/**
|
|
805
|
+
* Handles positioned comments for ParenExpression with special spacing rules.
|
|
806
|
+
* ParenExpression comments should be adjacent to parentheses without separator spaces.
|
|
807
|
+
*/
|
|
808
|
+
addPositionedCommentsToParenExpression(token, component) {
|
|
809
|
+
if (!component.positionedComments) {
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
// For ParenExpression: (/* comment */ content /* comment */)
|
|
813
|
+
// Comments should be placed immediately after opening paren and before closing paren
|
|
814
|
+
// Handle 'before' comments - place after opening parenthesis without space
|
|
815
|
+
const beforeComments = component.getPositionedComments('before');
|
|
816
|
+
if (beforeComments.length > 0) {
|
|
817
|
+
const commentBlocks = this.createCommentBlocks(beforeComments);
|
|
818
|
+
// Insert after opening paren (index 1) without separator space
|
|
819
|
+
let insertIndex = 1;
|
|
820
|
+
for (const commentBlock of commentBlocks) {
|
|
821
|
+
token.innerTokens.splice(insertIndex, 0, commentBlock);
|
|
822
|
+
insertIndex++;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
// Handle 'after' comments - place before closing parenthesis without space
|
|
826
|
+
const afterComments = component.getPositionedComments('after');
|
|
827
|
+
if (afterComments.length > 0) {
|
|
828
|
+
const commentBlocks = this.createCommentBlocks(afterComments);
|
|
829
|
+
// Insert before closing paren (last position) without separator space
|
|
830
|
+
const insertIndex = token.innerTokens.length;
|
|
831
|
+
for (const commentBlock of commentBlocks) {
|
|
832
|
+
token.innerTokens.splice(insertIndex - 1, 0, commentBlock);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
442
836
|
/**
|
|
443
837
|
* Determines whether a separator space should be added after comments for the given container type.
|
|
444
838
|
*
|
|
@@ -478,11 +872,21 @@ export class SqlPrintTokenParser {
|
|
|
478
872
|
* Prevents SQL injection by removing dangerous comment sequences.
|
|
479
873
|
*/
|
|
480
874
|
formatBlockComment(comment) {
|
|
481
|
-
//
|
|
482
|
-
|
|
875
|
+
// Sanitize dangerous comment sequences to prevent SQL injection
|
|
876
|
+
let sanitizedComment = comment
|
|
483
877
|
.replace(/\*\//g, '*') // Remove comment close sequences
|
|
484
|
-
.replace(/\/\*/g, '*') // Remove comment open sequences
|
|
485
|
-
|
|
878
|
+
.replace(/\/\*/g, '*'); // Remove comment open sequences
|
|
879
|
+
// Check if this is a separator line (like ----------) before processing
|
|
880
|
+
const trimmed = sanitizedComment.trim();
|
|
881
|
+
const isSeparatorLine = /^[-=_+*#]+$/.test(trimmed);
|
|
882
|
+
if (isSeparatorLine) {
|
|
883
|
+
// For separator lines, preserve as-is (already sanitized above)
|
|
884
|
+
return `/* ${trimmed} */`;
|
|
885
|
+
}
|
|
886
|
+
// For multiline comments: convert newlines to spaces (security requirement)
|
|
887
|
+
sanitizedComment = sanitizedComment
|
|
888
|
+
.replace(/\r?\n/g, ' ') // Replace newlines with spaces
|
|
889
|
+
.replace(/\s+/g, ' ') // Collapse multiple spaces into single space
|
|
486
890
|
.trim(); // Remove leading/trailing whitespace
|
|
487
891
|
// Return empty string if comment becomes empty after sanitization
|
|
488
892
|
if (!sanitizedComment) {
|
|
@@ -503,10 +907,12 @@ export class SqlPrintTokenParser {
|
|
|
503
907
|
visitColumnReference(arg) {
|
|
504
908
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.ColumnReference);
|
|
505
909
|
token.innerTokens.push(arg.qualifiedName.accept(this));
|
|
910
|
+
this.addComponentComments(token, arg);
|
|
506
911
|
return token;
|
|
507
912
|
}
|
|
508
913
|
visitFunctionCall(arg) {
|
|
509
914
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.FunctionCall);
|
|
915
|
+
this.addComponentComments(token, arg);
|
|
510
916
|
token.innerTokens.push(arg.qualifiedName.accept(this));
|
|
511
917
|
token.innerTokens.push(SqlPrintTokenParser.PAREN_OPEN_TOKEN);
|
|
512
918
|
if (arg.argument) {
|
|
@@ -557,7 +963,13 @@ export class SqlPrintTokenParser {
|
|
|
557
963
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.BinaryExpression);
|
|
558
964
|
token.innerTokens.push(this.visit(arg.left));
|
|
559
965
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
560
|
-
|
|
966
|
+
// Visit the operator to handle its comments properly
|
|
967
|
+
const operatorToken = this.visit(arg.operator);
|
|
968
|
+
const operatorLower = operatorToken.text.toLowerCase();
|
|
969
|
+
if (operatorLower === 'and' || operatorLower === 'or') {
|
|
970
|
+
operatorToken.type = SqlPrintTokenType.operator;
|
|
971
|
+
}
|
|
972
|
+
token.innerTokens.push(operatorToken);
|
|
561
973
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
562
974
|
token.innerTokens.push(this.visit(arg.right));
|
|
563
975
|
return token;
|
|
@@ -578,18 +990,30 @@ export class SqlPrintTokenParser {
|
|
|
578
990
|
else {
|
|
579
991
|
text = arg.value.toString();
|
|
580
992
|
}
|
|
581
|
-
|
|
993
|
+
const token = new SqlPrintToken(SqlPrintTokenType.value, text, SqlPrintTokenContainerType.LiteralValue);
|
|
994
|
+
// Handle positioned comments for LiteralValue
|
|
995
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
996
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
997
|
+
// Clear positioned comments to prevent duplicate processing
|
|
998
|
+
arg.positionedComments = null;
|
|
999
|
+
}
|
|
1000
|
+
else if (arg.comments && arg.comments.length > 0) {
|
|
1001
|
+
this.addCommentsToToken(token, arg.comments);
|
|
1002
|
+
}
|
|
1003
|
+
return token;
|
|
582
1004
|
}
|
|
583
1005
|
visitParameterExpression(arg) {
|
|
584
1006
|
// Create a parameter token and decorate it using the parameterDecorator
|
|
585
1007
|
arg.index = this.index;
|
|
586
1008
|
const text = this.parameterDecorator.decorate(arg.name.value, arg.index);
|
|
587
1009
|
const token = new SqlPrintToken(SqlPrintTokenType.parameter, text);
|
|
1010
|
+
this.addComponentComments(token, arg);
|
|
588
1011
|
this.index++;
|
|
589
1012
|
return token;
|
|
590
1013
|
}
|
|
591
1014
|
visitSwitchCaseArgument(arg) {
|
|
592
1015
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.SwitchCaseArgument);
|
|
1016
|
+
this.addComponentComments(token, arg);
|
|
593
1017
|
// Add each WHEN/THEN clause
|
|
594
1018
|
for (const kv of arg.cases) {
|
|
595
1019
|
// Create a new line for each WHEN clause
|
|
@@ -599,14 +1023,27 @@ export class SqlPrintTokenParser {
|
|
|
599
1023
|
// Add ELSE clause if present
|
|
600
1024
|
if (arg.elseValue) {
|
|
601
1025
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
602
|
-
token.innerTokens.push(this.createElseToken(arg.elseValue));
|
|
1026
|
+
token.innerTokens.push(this.createElseToken(arg.elseValue, arg.comments));
|
|
1027
|
+
}
|
|
1028
|
+
// Add SwitchCaseArgument comments (END keyword) if present and no elseValue
|
|
1029
|
+
else if (arg.comments && arg.comments.length > 0) {
|
|
1030
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1031
|
+
const commentTokens = this.createInlineCommentSequence(arg.comments);
|
|
1032
|
+
token.innerTokens.push(...commentTokens);
|
|
603
1033
|
}
|
|
604
1034
|
return token;
|
|
605
1035
|
}
|
|
606
|
-
createElseToken(elseValue) {
|
|
1036
|
+
createElseToken(elseValue, switchCaseComments) {
|
|
607
1037
|
// Creates a token for the ELSE clause in a CASE expression.
|
|
608
1038
|
const elseToken = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.ElseClause); // Add the ELSE keyword
|
|
609
1039
|
elseToken.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'else'));
|
|
1040
|
+
// Add ELSE and END keyword comments if present
|
|
1041
|
+
// The switchCaseComments contains both ELSE and END comments in order ['e1', 'end']
|
|
1042
|
+
if (switchCaseComments && switchCaseComments.length > 0) {
|
|
1043
|
+
elseToken.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1044
|
+
const commentTokens = this.createInlineCommentSequence(switchCaseComments);
|
|
1045
|
+
elseToken.innerTokens.push(...commentTokens);
|
|
1046
|
+
}
|
|
610
1047
|
// Create a container for the ELSE value to enable proper indentation
|
|
611
1048
|
elseToken.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
612
1049
|
const elseValueContainer = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CaseElseValue);
|
|
@@ -616,12 +1053,24 @@ export class SqlPrintTokenParser {
|
|
|
616
1053
|
}
|
|
617
1054
|
visitCaseKeyValuePair(arg) {
|
|
618
1055
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CaseKeyValuePair);
|
|
1056
|
+
// Handle positioned comments for CaseKeyValuePair
|
|
1057
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1058
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1059
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1060
|
+
arg.positionedComments = null;
|
|
1061
|
+
}
|
|
619
1062
|
// Create WHEN clause
|
|
620
1063
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'when'));
|
|
621
1064
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
622
1065
|
token.innerTokens.push(this.visit(arg.key)); // Create THEN clause
|
|
623
1066
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
624
1067
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'then'));
|
|
1068
|
+
// Add THEN keyword comments if present
|
|
1069
|
+
if (arg.comments && arg.comments.length > 0) {
|
|
1070
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1071
|
+
const commentTokens = this.createInlineCommentSequence(arg.comments);
|
|
1072
|
+
token.innerTokens.push(...commentTokens);
|
|
1073
|
+
}
|
|
625
1074
|
// Create a container for the THEN value to enable proper indentation
|
|
626
1075
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
627
1076
|
const thenValueContainer = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CaseThenValue);
|
|
@@ -636,14 +1085,71 @@ export class SqlPrintTokenParser {
|
|
|
636
1085
|
visitIdentifierString(arg) {
|
|
637
1086
|
// Create an identifier token and decorate it using the identifierDecorator
|
|
638
1087
|
const text = arg.name === "*" ? arg.name : this.identifierDecorator.decorate(arg.name);
|
|
1088
|
+
// Handle positioned comments for IdentifierString
|
|
1089
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1090
|
+
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.IdentifierString);
|
|
1091
|
+
// Add positioned comments
|
|
1092
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1093
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1094
|
+
arg.positionedComments = null;
|
|
1095
|
+
// Add the identifier text as the main token
|
|
1096
|
+
const valueToken = new SqlPrintToken(SqlPrintTokenType.value, text);
|
|
1097
|
+
token.innerTokens.push(valueToken);
|
|
1098
|
+
return token;
|
|
1099
|
+
}
|
|
1100
|
+
// If there are legacy comments, create a container instead of a simple value token
|
|
1101
|
+
if (arg.comments && arg.comments.length > 0) {
|
|
1102
|
+
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.IdentifierString);
|
|
1103
|
+
// Add the identifier text as the main token
|
|
1104
|
+
const valueToken = new SqlPrintToken(SqlPrintTokenType.value, text);
|
|
1105
|
+
token.innerTokens.push(valueToken);
|
|
1106
|
+
// Add legacy comments to the token
|
|
1107
|
+
this.addComponentComments(token, arg);
|
|
1108
|
+
return token;
|
|
1109
|
+
}
|
|
639
1110
|
const token = new SqlPrintToken(SqlPrintTokenType.value, text, SqlPrintTokenContainerType.IdentifierString);
|
|
640
1111
|
return token;
|
|
641
1112
|
}
|
|
642
1113
|
visitParenExpression(arg) {
|
|
643
1114
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.ParenExpression);
|
|
1115
|
+
// Handle positioned comments for ParenExpression - check both self and inner expression
|
|
1116
|
+
const hasOwnComments = arg.positionedComments && arg.positionedComments.length > 0;
|
|
1117
|
+
const hasInnerComments = arg.expression.positionedComments && arg.expression.positionedComments.length > 0;
|
|
1118
|
+
// Store inner comments for later processing and clear to prevent duplicate processing
|
|
1119
|
+
let innerBeforeComments = [];
|
|
1120
|
+
let innerAfterComments = [];
|
|
1121
|
+
if (hasInnerComments) {
|
|
1122
|
+
innerBeforeComments = arg.expression.getPositionedComments('before');
|
|
1123
|
+
innerAfterComments = arg.expression.getPositionedComments('after');
|
|
1124
|
+
arg.expression.positionedComments = null;
|
|
1125
|
+
}
|
|
1126
|
+
// Build basic structure first
|
|
644
1127
|
token.innerTokens.push(SqlPrintTokenParser.PAREN_OPEN_TOKEN);
|
|
645
1128
|
token.innerTokens.push(this.visit(arg.expression));
|
|
646
1129
|
token.innerTokens.push(SqlPrintTokenParser.PAREN_CLOSE_TOKEN);
|
|
1130
|
+
// Now add positioned comments in the correct positions manually
|
|
1131
|
+
if (innerBeforeComments.length > 0) {
|
|
1132
|
+
const commentBlocks = this.createCommentBlocks(innerBeforeComments);
|
|
1133
|
+
// Insert after opening paren (index 1) without separator space
|
|
1134
|
+
let insertIndex = 1;
|
|
1135
|
+
for (const commentBlock of commentBlocks) {
|
|
1136
|
+
token.innerTokens.splice(insertIndex, 0, commentBlock);
|
|
1137
|
+
insertIndex++;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
if (innerAfterComments.length > 0) {
|
|
1141
|
+
const commentBlocks = this.createCommentBlocks(innerAfterComments);
|
|
1142
|
+
// Insert before closing paren (last position) without separator space
|
|
1143
|
+
const insertIndex = token.innerTokens.length;
|
|
1144
|
+
for (const commentBlock of commentBlocks) {
|
|
1145
|
+
token.innerTokens.splice(insertIndex - 1, 0, commentBlock);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (hasOwnComments) {
|
|
1149
|
+
this.addPositionedCommentsToParenExpression(token, arg);
|
|
1150
|
+
// Clear positioned comments to prevent duplicate processing in parent containers
|
|
1151
|
+
arg.positionedComments = null;
|
|
1152
|
+
}
|
|
647
1153
|
return token;
|
|
648
1154
|
}
|
|
649
1155
|
visitCastExpression(arg) {
|
|
@@ -655,6 +1161,12 @@ export class SqlPrintTokenParser {
|
|
|
655
1161
|
}
|
|
656
1162
|
visitCaseExpression(arg) {
|
|
657
1163
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CaseExpression);
|
|
1164
|
+
// Handle positioned comments for CaseExpression (unified spec: positioned comments only)
|
|
1165
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1166
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1167
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1168
|
+
arg.positionedComments = null;
|
|
1169
|
+
}
|
|
658
1170
|
// Add the CASE keyword
|
|
659
1171
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'case'));
|
|
660
1172
|
// Add the condition if exists
|
|
@@ -743,6 +1255,7 @@ export class SqlPrintTokenParser {
|
|
|
743
1255
|
}
|
|
744
1256
|
visitTypeValue(arg) {
|
|
745
1257
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.TypeValue);
|
|
1258
|
+
this.addComponentComments(token, arg);
|
|
746
1259
|
token.innerTokens.push(arg.qualifiedName.accept(this));
|
|
747
1260
|
if (arg.argument) {
|
|
748
1261
|
token.innerTokens.push(SqlPrintTokenParser.PAREN_OPEN_TOKEN);
|
|
@@ -836,7 +1349,32 @@ export class SqlPrintTokenParser {
|
|
|
836
1349
|
}
|
|
837
1350
|
visitSelectItem(arg) {
|
|
838
1351
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.SelectItem);
|
|
1352
|
+
// Preserve original positioned comments to avoid mutating the source object
|
|
1353
|
+
const originalSelectItemPositionedComments = arg.positionedComments;
|
|
1354
|
+
const originalValuePositionedComments = arg.value.positionedComments;
|
|
1355
|
+
// Clear positioned comments from the value to avoid duplication since SelectItem handles them
|
|
1356
|
+
arg.value.positionedComments = null;
|
|
1357
|
+
// Add 'before' positioned comments
|
|
1358
|
+
const beforeComments = arg.getPositionedComments('before');
|
|
1359
|
+
if (beforeComments.length > 0) {
|
|
1360
|
+
const commentTokens = this.createInlineCommentSequence(beforeComments);
|
|
1361
|
+
token.innerTokens.push(...commentTokens);
|
|
1362
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1363
|
+
}
|
|
1364
|
+
// Add the value (column name)
|
|
839
1365
|
token.innerTokens.push(this.visit(arg.value));
|
|
1366
|
+
// Add 'after' positioned comments for the value
|
|
1367
|
+
// Skip after comments if the value is ParenExpression (already handled in ParenExpression processing)
|
|
1368
|
+
const afterComments = arg.getPositionedComments('after');
|
|
1369
|
+
const isParenExpression = arg.value.constructor.name === 'ParenExpression';
|
|
1370
|
+
if (afterComments.length > 0 && !isParenExpression) {
|
|
1371
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1372
|
+
const commentTokens = this.createInlineCommentSequence(afterComments);
|
|
1373
|
+
token.innerTokens.push(...commentTokens);
|
|
1374
|
+
}
|
|
1375
|
+
// Restore original positioned comments to avoid side effects
|
|
1376
|
+
arg.positionedComments = originalSelectItemPositionedComments;
|
|
1377
|
+
arg.value.positionedComments = originalValuePositionedComments;
|
|
840
1378
|
if (!arg.identifier) {
|
|
841
1379
|
return token;
|
|
842
1380
|
}
|
|
@@ -849,13 +1387,70 @@ export class SqlPrintTokenParser {
|
|
|
849
1387
|
}
|
|
850
1388
|
// Add alias if it is different from the default name
|
|
851
1389
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1390
|
+
// Handle AS keyword positioned comments (before AS)
|
|
1391
|
+
const asKeywordPositionedComments = 'asKeywordPositionedComments' in arg ? arg.asKeywordPositionedComments : null;
|
|
1392
|
+
if (asKeywordPositionedComments) {
|
|
1393
|
+
const beforeComments = asKeywordPositionedComments.filter((pc) => pc.position === 'before');
|
|
1394
|
+
if (beforeComments.length > 0) {
|
|
1395
|
+
for (const posComment of beforeComments) {
|
|
1396
|
+
const commentTokens = this.createInlineCommentSequence(posComment.comments);
|
|
1397
|
+
token.innerTokens.push(...commentTokens);
|
|
1398
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
852
1402
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'as'));
|
|
1403
|
+
// Handle AS keyword positioned comments (after AS)
|
|
1404
|
+
if (asKeywordPositionedComments) {
|
|
1405
|
+
const afterComments = asKeywordPositionedComments.filter((pc) => pc.position === 'after');
|
|
1406
|
+
if (afterComments.length > 0) {
|
|
1407
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1408
|
+
for (const posComment of afterComments) {
|
|
1409
|
+
const commentTokens = this.createInlineCommentSequence(posComment.comments);
|
|
1410
|
+
token.innerTokens.push(...commentTokens);
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
// Fallback: Add AS keyword legacy comments if present
|
|
1415
|
+
const asKeywordComments = 'asKeywordComments' in arg ? arg.asKeywordComments : null;
|
|
1416
|
+
if (asKeywordComments && asKeywordComments.length > 0) {
|
|
1417
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1418
|
+
const commentTokens = this.createInlineCommentSequence(asKeywordComments);
|
|
1419
|
+
token.innerTokens.push(...commentTokens);
|
|
1420
|
+
}
|
|
853
1421
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
854
|
-
|
|
1422
|
+
// Visit identifier to get alias with proper spacing
|
|
1423
|
+
const identifierToken = this.visit(arg.identifier);
|
|
1424
|
+
token.innerTokens.push(identifierToken);
|
|
1425
|
+
// Handle alias positioned comments (after alias)
|
|
1426
|
+
const aliasPositionedComments = 'aliasPositionedComments' in arg ? arg.aliasPositionedComments : null;
|
|
1427
|
+
if (aliasPositionedComments) {
|
|
1428
|
+
const afterComments = aliasPositionedComments.filter((pc) => pc.position === 'after');
|
|
1429
|
+
if (afterComments.length > 0) {
|
|
1430
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1431
|
+
for (const posComment of afterComments) {
|
|
1432
|
+
const commentTokens = this.createInlineCommentSequence(posComment.comments);
|
|
1433
|
+
token.innerTokens.push(...commentTokens);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
// Fallback: Add alias legacy comments if present
|
|
1438
|
+
const aliasComments = arg.aliasComments;
|
|
1439
|
+
if (aliasComments && aliasComments.length > 0) {
|
|
1440
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1441
|
+
const commentTokens = this.createInlineCommentSequence(aliasComments);
|
|
1442
|
+
token.innerTokens.push(...commentTokens);
|
|
1443
|
+
}
|
|
855
1444
|
return token;
|
|
856
1445
|
}
|
|
857
1446
|
visitSelectClause(arg) {
|
|
858
1447
|
const token = new SqlPrintToken(SqlPrintTokenType.keyword, 'select', SqlPrintTokenContainerType.SelectClause);
|
|
1448
|
+
// Handle positioned comments for SelectClause (unified spec)
|
|
1449
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1450
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1451
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1452
|
+
arg.positionedComments = null;
|
|
1453
|
+
}
|
|
859
1454
|
// Handle hints and DISTINCT as part of the keyword line
|
|
860
1455
|
let selectKeywordText = 'select';
|
|
861
1456
|
// Add hint clauses immediately after SELECT (before DISTINCT)
|
|
@@ -903,6 +1498,12 @@ export class SqlPrintTokenParser {
|
|
|
903
1498
|
}
|
|
904
1499
|
visitDistinct(arg) {
|
|
905
1500
|
const token = new SqlPrintToken(SqlPrintTokenType.keyword, 'distinct');
|
|
1501
|
+
// Handle positioned comments for Distinct (unified spec)
|
|
1502
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1503
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1504
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1505
|
+
arg.positionedComments = null;
|
|
1506
|
+
}
|
|
906
1507
|
return token;
|
|
907
1508
|
}
|
|
908
1509
|
visitDistinctOn(arg) {
|
|
@@ -921,6 +1522,7 @@ export class SqlPrintTokenParser {
|
|
|
921
1522
|
}
|
|
922
1523
|
fullName += arg.table.accept(this).text;
|
|
923
1524
|
const token = new SqlPrintToken(SqlPrintTokenType.value, fullName);
|
|
1525
|
+
this.addComponentComments(token, arg);
|
|
924
1526
|
// alias (if present and different from table name)
|
|
925
1527
|
if (arg.identifier && arg.identifier.name !== arg.table.name) {
|
|
926
1528
|
}
|
|
@@ -971,8 +1573,38 @@ export class SqlPrintTokenParser {
|
|
|
971
1573
|
visitJoinClause(arg) {
|
|
972
1574
|
// Print join clause: [joinType] [lateral] [source] [on/using ...]
|
|
973
1575
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.JoinClause);
|
|
1576
|
+
// Handle JOIN keyword positioned comments (before JOIN)
|
|
1577
|
+
const joinKeywordPositionedComments = arg.joinKeywordPositionedComments;
|
|
1578
|
+
if (joinKeywordPositionedComments) {
|
|
1579
|
+
const beforeComments = joinKeywordPositionedComments.filter((pc) => pc.position === 'before');
|
|
1580
|
+
if (beforeComments.length > 0) {
|
|
1581
|
+
for (const posComment of beforeComments) {
|
|
1582
|
+
const commentTokens = this.createInlineCommentSequence(posComment.comments);
|
|
1583
|
+
token.innerTokens.push(...commentTokens);
|
|
1584
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
974
1588
|
// join type (e.g. inner join, left join, etc)
|
|
975
1589
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, arg.joinType.value));
|
|
1590
|
+
// Handle JOIN keyword positioned comments (after JOIN)
|
|
1591
|
+
if (joinKeywordPositionedComments) {
|
|
1592
|
+
const afterComments = joinKeywordPositionedComments.filter((pc) => pc.position === 'after');
|
|
1593
|
+
if (afterComments.length > 0) {
|
|
1594
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1595
|
+
for (const posComment of afterComments) {
|
|
1596
|
+
const commentTokens = this.createInlineCommentSequence(posComment.comments);
|
|
1597
|
+
token.innerTokens.push(...commentTokens);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
// Fallback: Add JOIN keyword legacy comments if present
|
|
1602
|
+
const joinKeywordComments = arg.joinKeywordComments;
|
|
1603
|
+
if (joinKeywordComments && joinKeywordComments.length > 0) {
|
|
1604
|
+
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1605
|
+
const commentTokens = this.createInlineCommentSequence(joinKeywordComments);
|
|
1606
|
+
token.innerTokens.push(...commentTokens);
|
|
1607
|
+
}
|
|
976
1608
|
if (arg.lateral) {
|
|
977
1609
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
978
1610
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'lateral'));
|
|
@@ -1025,10 +1657,20 @@ export class SqlPrintTokenParser {
|
|
|
1025
1657
|
}
|
|
1026
1658
|
token.innerTokens.push(SqlPrintTokenParser.PAREN_CLOSE_TOKEN);
|
|
1027
1659
|
}
|
|
1660
|
+
// Handle positioned comments for SourceAliasExpression (alias name comments)
|
|
1661
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1662
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1663
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1664
|
+
arg.positionedComments = null;
|
|
1665
|
+
}
|
|
1666
|
+
else if (arg.comments && arg.comments.length > 0) {
|
|
1667
|
+
this.addCommentsToToken(token, arg.comments);
|
|
1668
|
+
}
|
|
1028
1669
|
return token;
|
|
1029
1670
|
}
|
|
1030
1671
|
visitWhereClause(arg) {
|
|
1031
1672
|
const token = new SqlPrintToken(SqlPrintTokenType.keyword, 'where', SqlPrintTokenContainerType.WhereClause);
|
|
1673
|
+
this.addComponentComments(token, arg);
|
|
1032
1674
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1033
1675
|
token.innerTokens.push(this.visit(arg.condition));
|
|
1034
1676
|
return token;
|
|
@@ -1121,10 +1763,20 @@ export class SqlPrintTokenParser {
|
|
|
1121
1763
|
token.innerTokens.push(arg.tables[i].accept(this));
|
|
1122
1764
|
}
|
|
1123
1765
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1766
|
+
this.addComponentComments(token, arg);
|
|
1124
1767
|
return token;
|
|
1125
1768
|
}
|
|
1126
1769
|
visitCommonTable(arg) {
|
|
1127
1770
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.CommonTable);
|
|
1771
|
+
// Handle positioned comments for CommonTable (avoid duplication)
|
|
1772
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1773
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1774
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1775
|
+
arg.positionedComments = null;
|
|
1776
|
+
}
|
|
1777
|
+
else if (arg.comments && arg.comments.length > 0) {
|
|
1778
|
+
this.addCommentsToToken(token, arg.comments);
|
|
1779
|
+
}
|
|
1128
1780
|
token.innerTokens.push(arg.aliasExpression.accept(this));
|
|
1129
1781
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1130
1782
|
token.innerTokens.push(new SqlPrintToken(SqlPrintTokenType.keyword, 'as'));
|
|
@@ -1148,14 +1800,27 @@ export class SqlPrintTokenParser {
|
|
|
1148
1800
|
// query
|
|
1149
1801
|
visitSimpleQuery(arg) {
|
|
1150
1802
|
const token = new SqlPrintToken(SqlPrintTokenType.container, '', SqlPrintTokenContainerType.SimpleSelectQuery);
|
|
1151
|
-
//
|
|
1803
|
+
// Handle positioned comments for SimpleSelectQuery (unified spec)
|
|
1152
1804
|
if (arg.headerComments && arg.headerComments.length > 0) {
|
|
1153
|
-
|
|
1154
|
-
|
|
1805
|
+
// Fallback to legacy headerComments if no positioned comments
|
|
1806
|
+
// For smart comment style, treat headerComments as a single multi-line block
|
|
1807
|
+
if (this.commentStyle === 'smart' && arg.headerComments.length > 1) {
|
|
1808
|
+
const mergedHeaderComment = this.createHeaderMultiLineCommentBlock(arg.headerComments);
|
|
1809
|
+
token.innerTokens.push(mergedHeaderComment);
|
|
1810
|
+
}
|
|
1811
|
+
else {
|
|
1812
|
+
const headerCommentBlocks = this.createCommentBlocks(arg.headerComments);
|
|
1813
|
+
token.innerTokens.push(...headerCommentBlocks);
|
|
1814
|
+
}
|
|
1155
1815
|
if (arg.withClause) {
|
|
1156
1816
|
token.innerTokens.push(SqlPrintTokenParser.SPACE_TOKEN);
|
|
1157
1817
|
}
|
|
1158
1818
|
}
|
|
1819
|
+
if (arg.positionedComments && arg.positionedComments.length > 0) {
|
|
1820
|
+
this.addPositionedCommentsToToken(token, arg);
|
|
1821
|
+
// Clear positioned comments to prevent duplicate processing
|
|
1822
|
+
arg.positionedComments = null;
|
|
1823
|
+
}
|
|
1159
1824
|
if (arg.withClause) {
|
|
1160
1825
|
token.innerTokens.push(arg.withClause.accept(this));
|
|
1161
1826
|
}
|
|
@@ -1371,4 +2036,7 @@ SqlPrintTokenParser.ARGUMENT_SPLIT_COMMA_TOKEN = new SqlPrintToken(SqlPrintToken
|
|
|
1371
2036
|
SqlPrintTokenParser.PAREN_OPEN_TOKEN = new SqlPrintToken(SqlPrintTokenType.parenthesis, '(');
|
|
1372
2037
|
SqlPrintTokenParser.PAREN_CLOSE_TOKEN = new SqlPrintToken(SqlPrintTokenType.parenthesis, ')');
|
|
1373
2038
|
SqlPrintTokenParser.DOT_TOKEN = new SqlPrintToken(SqlPrintTokenType.dot, '.');
|
|
2039
|
+
// Set of component kinds that handle their own positioned comments
|
|
2040
|
+
// Note: Cannot use static readonly due to circular dependency issues with class initialization
|
|
2041
|
+
SqlPrintTokenParser._selfHandlingComponentTypes = null;
|
|
1374
2042
|
//# sourceMappingURL=SqlPrintTokenParser.js.map
|