pgsql-deparser 15.1.0 → 15.2.0

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.
@@ -5,10 +5,29 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.Deparser = void 0;
8
- const base_1 = require("./visitors/base");
9
- const sql_formatter_1 = require("./utils/sql-formatter");
10
- const quote_utils_1 = require("./utils/quote-utils");
8
+ const quotes_1 = require("@pgsql/quotes");
11
9
  const list_utils_1 = require("./utils/list-utils");
10
+ const sql_formatter_1 = require("./utils/sql-formatter");
11
+ const base_1 = require("./visitors/base");
12
+ // WindowDef.frameOptions bit flags (src/include/nodes/parsenodes.h)
13
+ const FRAMEOPTION_NONDEFAULT = 0x00001;
14
+ const FRAMEOPTION_RANGE = 0x00002;
15
+ const FRAMEOPTION_ROWS = 0x00004;
16
+ const FRAMEOPTION_GROUPS = 0x00008;
17
+ const FRAMEOPTION_BETWEEN = 0x00010;
18
+ const FRAMEOPTION_START_UNBOUNDED_PRECEDING = 0x00020;
19
+ const FRAMEOPTION_END_UNBOUNDED_PRECEDING = 0x00040;
20
+ const FRAMEOPTION_START_UNBOUNDED_FOLLOWING = 0x00080;
21
+ const FRAMEOPTION_END_UNBOUNDED_FOLLOWING = 0x00100;
22
+ const FRAMEOPTION_START_CURRENT_ROW = 0x00200;
23
+ const FRAMEOPTION_END_CURRENT_ROW = 0x00400;
24
+ const FRAMEOPTION_START_OFFSET_PRECEDING = 0x00800;
25
+ const FRAMEOPTION_END_OFFSET_PRECEDING = 0x01000;
26
+ const FRAMEOPTION_START_OFFSET_FOLLOWING = 0x02000;
27
+ const FRAMEOPTION_END_OFFSET_FOLLOWING = 0x04000;
28
+ const FRAMEOPTION_EXCLUDE_CURRENT_ROW = 0x08000;
29
+ const FRAMEOPTION_EXCLUDE_GROUP = 0x10000;
30
+ const FRAMEOPTION_EXCLUDE_TIES = 0x20000;
12
31
  /**
13
32
  * List of real PostgreSQL built-in types as they appear in pg_catalog.pg_type.typname.
14
33
  * These are stored in lowercase in PostgreSQL system catalogs.
@@ -107,6 +126,36 @@ function isWrappedParseResult(obj) {
107
126
  * The deparser automatically detects bare ParseResult objects for backward
108
127
  * compatibility and wraps them internally for consistent processing.
109
128
  */
129
+ function formatBitString(bsval) {
130
+ const quote = (value) => value.replace(/'/g, "''");
131
+ if (/^x[0-9A-Fa-f]*$/.test(bsval)) {
132
+ return `x'${bsval.substring(1)}'`;
133
+ }
134
+ if (bsval.startsWith('b')) {
135
+ return `b'${quote(bsval.substring(1))}'`;
136
+ }
137
+ return `b'${quote(bsval)}'`;
138
+ }
139
+ // A dollar-quoted body ends at the first occurrence of its tag, including one
140
+ // formed by the body's trailing characters plus the closing tag (`...$` + `$$`).
141
+ function dollarTagConflicts(body, tag) {
142
+ return (body + tag).indexOf(tag) < body.length;
143
+ }
144
+ const NUMERIC_LITERAL = /^[+-]?(?:0[xX](?:_?[0-9A-Fa-f])+|0[oO](?:_?[0-7])+|0[bB](?:_?[01])+|(?:\d(?:_?\d)*(?:\.(?:\d(?:_?\d)*)?)?|\.\d(?:_?\d)*)(?:[eE][+-]?\d(?:_?\d)*)?)$/;
145
+ function formatInteger(ival) {
146
+ const value = String(ival);
147
+ if (!/^[+-]?\d+$/.test(value)) {
148
+ throw new Error(`Invalid integer literal: ${value}`);
149
+ }
150
+ return value;
151
+ }
152
+ function formatNumeric(fval) {
153
+ const value = String(fval);
154
+ if (!NUMERIC_LITERAL.test(value)) {
155
+ throw new Error(`Invalid numeric literal: ${value}`);
156
+ }
157
+ return value;
158
+ }
110
159
  class Deparser {
111
160
  tree;
112
161
  options;
@@ -166,10 +215,133 @@ class Deparser {
166
215
  */
167
216
  getFunctionDelimiter(body) {
168
217
  const delimiter = this.options.functionDelimiter || '$$';
169
- if (body.includes(delimiter)) {
170
- return this.options.functionDelimiterFallback || '$EOFCODE$';
218
+ if (!dollarTagConflicts(body, delimiter)) {
219
+ return delimiter;
220
+ }
221
+ const fallback = this.options.functionDelimiterFallback || '$EOFCODE$';
222
+ if (!dollarTagConflicts(body, fallback)) {
223
+ return fallback;
224
+ }
225
+ const base = fallback.slice(0, -1);
226
+ let counter = 1;
227
+ while (dollarTagConflicts(body, `${base}${counter}$`)) {
228
+ counter++;
229
+ }
230
+ return `${base}${counter}$`;
231
+ }
232
+ /**
233
+ * Maps ObjectType enum values to their corresponding SQL keywords
234
+ * Used by AlterOwnerStmt, AlterObjectSchemaStmt, and other statements that need object type keywords
235
+ */
236
+ getObjectTypeKeyword(objectType) {
237
+ switch (objectType) {
238
+ case 'OBJECT_TABLE':
239
+ return 'TABLE';
240
+ case 'OBJECT_VIEW':
241
+ return 'VIEW';
242
+ case 'OBJECT_INDEX':
243
+ return 'INDEX';
244
+ case 'OBJECT_SEQUENCE':
245
+ return 'SEQUENCE';
246
+ case 'OBJECT_FUNCTION':
247
+ return 'FUNCTION';
248
+ case 'OBJECT_PROCEDURE':
249
+ return 'PROCEDURE';
250
+ case 'OBJECT_SCHEMA':
251
+ return 'SCHEMA';
252
+ case 'OBJECT_DATABASE':
253
+ return 'DATABASE';
254
+ case 'OBJECT_DOMAIN':
255
+ return 'DOMAIN';
256
+ case 'OBJECT_AGGREGATE':
257
+ return 'AGGREGATE';
258
+ case 'OBJECT_CONVERSION':
259
+ return 'CONVERSION';
260
+ case 'OBJECT_LANGUAGE':
261
+ return 'LANGUAGE';
262
+ case 'OBJECT_OPERATOR':
263
+ return 'OPERATOR';
264
+ case 'OBJECT_OPFAMILY':
265
+ return 'OPERATOR FAMILY';
266
+ case 'OBJECT_OPCLASS':
267
+ return 'OPERATOR CLASS';
268
+ case 'OBJECT_TSDICTIONARY':
269
+ return 'TEXT SEARCH DICTIONARY';
270
+ case 'OBJECT_TSCONFIGURATION':
271
+ return 'TEXT SEARCH CONFIGURATION';
272
+ case 'OBJECT_EVENT_TRIGGER':
273
+ return 'EVENT TRIGGER';
274
+ case 'OBJECT_FDW':
275
+ return 'FOREIGN DATA WRAPPER';
276
+ case 'OBJECT_FOREIGN_SERVER':
277
+ return 'SERVER';
278
+ case 'OBJECT_TYPE':
279
+ return 'TYPE';
280
+ case 'OBJECT_COLLATION':
281
+ return 'COLLATION';
282
+ case 'OBJECT_PUBLICATION':
283
+ return 'PUBLICATION';
284
+ case 'OBJECT_ACCESS_METHOD':
285
+ return 'ACCESS METHOD';
286
+ case 'OBJECT_AMOP':
287
+ return 'OPERATOR CLASS';
288
+ case 'OBJECT_AMPROC':
289
+ return 'OPERATOR CLASS';
290
+ case 'OBJECT_ATTRIBUTE':
291
+ return 'ATTRIBUTE';
292
+ case 'OBJECT_CAST':
293
+ return 'CAST';
294
+ case 'OBJECT_COLUMN':
295
+ return 'COLUMN';
296
+ case 'OBJECT_DEFAULT':
297
+ return 'DEFAULT';
298
+ case 'OBJECT_DEFACL':
299
+ return 'DEFAULT PRIVILEGES';
300
+ case 'OBJECT_DOMCONSTRAINT':
301
+ return 'DOMAIN';
302
+ case 'OBJECT_EXTENSION':
303
+ return 'EXTENSION';
304
+ case 'OBJECT_FOREIGN_TABLE':
305
+ return 'FOREIGN TABLE';
306
+ case 'OBJECT_LARGEOBJECT':
307
+ return 'LARGE OBJECT';
308
+ case 'OBJECT_MATVIEW':
309
+ return 'MATERIALIZED VIEW';
310
+ case 'OBJECT_PARAMETER_ACL':
311
+ return 'PARAMETER';
312
+ case 'OBJECT_POLICY':
313
+ return 'POLICY';
314
+ case 'OBJECT_PUBLICATION_NAMESPACE':
315
+ return 'PUBLICATION';
316
+ case 'OBJECT_PUBLICATION_REL':
317
+ return 'PUBLICATION';
318
+ case 'OBJECT_ROLE':
319
+ return 'ROLE';
320
+ case 'OBJECT_ROUTINE':
321
+ return 'ROUTINE';
322
+ case 'OBJECT_RULE':
323
+ return 'RULE';
324
+ case 'OBJECT_STATISTIC_EXT':
325
+ return 'STATISTICS';
326
+ case 'OBJECT_SUBSCRIPTION':
327
+ return 'SUBSCRIPTION';
328
+ case 'OBJECT_TABCONSTRAINT':
329
+ return 'CONSTRAINT';
330
+ case 'OBJECT_TABLESPACE':
331
+ return 'TABLESPACE';
332
+ case 'OBJECT_TRANSFORM':
333
+ return 'TRANSFORM';
334
+ case 'OBJECT_TRIGGER':
335
+ return 'TRIGGER';
336
+ case 'OBJECT_TSPARSER':
337
+ return 'TEXT SEARCH PARSER';
338
+ case 'OBJECT_TSTEMPLATE':
339
+ return 'TEXT SEARCH TEMPLATE';
340
+ case 'OBJECT_USER_MAPPING':
341
+ return 'USER MAPPING';
342
+ default:
343
+ throw new Error(`Unsupported objectType: ${objectType}`);
171
344
  }
172
- return delimiter;
173
345
  }
174
346
  deparse(node, context) {
175
347
  if (node == null) {
@@ -332,12 +504,7 @@ class Deparser {
332
504
  // Check if single target is complex - if so, use multiline format
333
505
  if (this.isComplexSelectTarget(targetNode)) {
334
506
  output.push('SELECT' + distinctPart);
335
- if (this.containsMultilineStringLiteral(target)) {
336
- output.push(target);
337
- }
338
- else {
339
- output.push(context.indent(target));
340
- }
507
+ output.push(context.indent(target));
341
508
  }
342
509
  else {
343
510
  output.push('SELECT' + distinctPart + ' ' + target);
@@ -347,9 +514,6 @@ class Deparser {
347
514
  const targetStrings = targetList
348
515
  .map(e => {
349
516
  const targetStr = this.visit(e, context.spawn('SelectStmt', { select: true }));
350
- if (this.containsMultilineStringLiteral(targetStr)) {
351
- return targetStr;
352
- }
353
517
  return context.indent(targetStr);
354
518
  });
355
519
  const formattedTargets = targetStrings.join(',' + context.newline());
@@ -401,9 +565,6 @@ class Deparser {
401
565
  return context.parens(values.join(', '));
402
566
  });
403
567
  const indentedTuples = lists.map(tuple => {
404
- if (this.containsMultilineStringLiteral(tuple)) {
405
- return tuple;
406
- }
407
568
  return context.indent(tuple);
408
569
  });
409
570
  output.push(indentedTuples.join(',\n'));
@@ -423,9 +584,6 @@ class Deparser {
423
584
  const groupItems = groupList
424
585
  .map(e => {
425
586
  const groupStr = this.visit(e, context.spawn('SelectStmt', { group: true, indentLevel: context.indentLevel + 1 }));
426
- if (this.containsMultilineStringLiteral(groupStr)) {
427
- return groupStr;
428
- }
429
587
  return context.indent(groupStr);
430
588
  })
431
589
  .join(',' + context.newline());
@@ -444,12 +602,7 @@ class Deparser {
444
602
  if (context.isPretty()) {
445
603
  output.push('HAVING');
446
604
  const havingStr = this.visit(node.havingClause, context);
447
- if (this.containsMultilineStringLiteral(havingStr)) {
448
- output.push(havingStr);
449
- }
450
- else {
451
- output.push(context.indent(havingStr));
452
- }
605
+ output.push(context.indent(havingStr));
453
606
  }
454
607
  else {
455
608
  output.push('HAVING');
@@ -470,9 +623,6 @@ class Deparser {
470
623
  const sortItems = sortList
471
624
  .map(e => {
472
625
  const sortStr = this.visit(e, context.spawn('SelectStmt', { sort: true, indentLevel: context.indentLevel + 1 }));
473
- if (this.containsMultilineStringLiteral(sortStr)) {
474
- return sortStr;
475
- }
476
626
  return context.indent(sortStr);
477
627
  })
478
628
  .join(',' + context.newline());
@@ -548,10 +698,15 @@ class Deparser {
548
698
  return context.format([leftExpr, operator, rightExpr]);
549
699
  }
550
700
  else if (rexpr) {
551
- return context.format([
552
- this.deparseOperatorName(name, context),
553
- this.visit(rexpr, context)
554
- ]);
701
+ // Unary operator (e.g., unary minus: - expr)
702
+ const operator = this.deparseOperatorName(name, context);
703
+ let rightExpr = this.visit(rexpr, context);
704
+ // Wrap in parentheses if rexpr is a binary A_Expr to preserve semantics
705
+ // e.g., -(a - b) must stay -(a - b), not become -a - b
706
+ if (rexpr && 'A_Expr' in rexpr && rexpr.A_Expr?.kind === 'AEXPR_OP' && rexpr.A_Expr?.lexpr) {
707
+ rightExpr = context.parens(rightExpr);
708
+ }
709
+ return context.format([operator, rightExpr]);
555
710
  }
556
711
  break;
557
712
  case 'AEXPR_OP_ANY':
@@ -731,15 +886,15 @@ class Deparser {
731
886
  getOperatorPrecedence(operator) {
732
887
  const precedence = {
733
888
  '||': 1, // string concatenation
734
- 'OR': 2, // logical OR
735
- 'AND': 3, // logical AND
736
- 'NOT': 4, // logical NOT
737
- 'IS': 5, // IS NULL, IS NOT NULL, etc.
738
- 'IN': 5, // IN, NOT IN
739
- 'BETWEEN': 5, // BETWEEN, NOT BETWEEN
740
- 'LIKE': 5, // LIKE, ILIKE, SIMILAR TO
741
- 'ILIKE': 5,
742
- 'SIMILAR': 5,
889
+ OR: 2, // logical OR
890
+ AND: 3, // logical AND
891
+ NOT: 4, // logical NOT
892
+ IS: 5, // IS NULL, IS NOT NULL, etc.
893
+ IN: 5, // IN, NOT IN
894
+ BETWEEN: 5, // BETWEEN, NOT BETWEEN
895
+ LIKE: 5, // LIKE, ILIKE, SIMILAR TO
896
+ ILIKE: 5,
897
+ SIMILAR: 5,
743
898
  '<': 6, // comparison operators
744
899
  '<=': 6,
745
900
  '>': 6,
@@ -869,6 +1024,12 @@ class Deparser {
869
1024
  output.push(context.parens(columnNames.join(', ')));
870
1025
  }
871
1026
  }
1027
+ if (node.override === 'OVERRIDING_USER_VALUE') {
1028
+ output.push('OVERRIDING USER VALUE');
1029
+ }
1030
+ else if (node.override === 'OVERRIDING_SYSTEM_VALUE') {
1031
+ output.push('OVERRIDING SYSTEM VALUE');
1032
+ }
872
1033
  if (node.selectStmt) {
873
1034
  output.push(this.visit(node.selectStmt, context));
874
1035
  }
@@ -911,7 +1072,14 @@ class Deparser {
911
1072
  else {
912
1073
  const updateContext = context.spawn('UpdateStmt', { update: true });
913
1074
  const targets = targetList.map(target => this.visit(target, updateContext));
914
- output.push(targets.join(', '));
1075
+ if (context.isPretty()) {
1076
+ // Put each assignment on its own line for pretty printing
1077
+ const indentedTargets = targets.map(target => context.indent(target));
1078
+ output[output.length - 1] += '\n' + indentedTargets.join(',\n');
1079
+ }
1080
+ else {
1081
+ output.push(targets.join(', '));
1082
+ }
915
1083
  }
916
1084
  }
917
1085
  if (node.onConflictClause.whereClause) {
@@ -923,11 +1091,8 @@ class Deparser {
923
1091
  output.push('DO NOTHING');
924
1092
  }
925
1093
  }
926
- if (node.returningList) {
927
- output.push('RETURNING');
928
- const returningList = list_utils_1.ListUtils.unwrapList(node.returningList);
929
- const returns = returningList.map(ret => this.visit(ret, context));
930
- output.push(returns.join(', '));
1094
+ if (node.returningClause) {
1095
+ output.push(this.ReturningClause(node.returningClause, context));
931
1096
  }
932
1097
  return output.join(' ');
933
1098
  }
@@ -941,38 +1106,8 @@ class Deparser {
941
1106
  output.push(this.RangeVar(node.relation, context));
942
1107
  }
943
1108
  output.push('SET');
944
- const targetList = list_utils_1.ListUtils.unwrapList(node.targetList);
945
- if (targetList && targetList.length) {
946
- const firstTarget = targetList[0];
947
- const processedTargets = new Set();
948
- const assignmentParts = [];
949
- for (let i = 0; i < targetList.length; i++) {
950
- if (processedTargets.has(i))
951
- continue;
952
- const target = targetList[i];
953
- const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
954
- if (multiAssignRef) {
955
- const relatedTargets = [];
956
- for (let j = i; j < targetList.length; j++) {
957
- const otherTarget = targetList[j];
958
- const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
959
- if (otherMultiAssignRef &&
960
- JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
961
- relatedTargets.push(otherTarget);
962
- processedTargets.add(j);
963
- }
964
- }
965
- const names = relatedTargets.map(t => t.ResTarget.name);
966
- const multiAssignment = `${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`;
967
- assignmentParts.push(multiAssignment);
968
- }
969
- else {
970
- // Handle regular single-column assignment
971
- assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
972
- processedTargets.add(i);
973
- }
974
- }
975
- output.push(assignmentParts.join(','));
1109
+ if (node.targetList && node.targetList.length > 0) {
1110
+ output.push(this.formatUpdateAssignments(node.targetList, context));
976
1111
  }
977
1112
  if (node.fromClause) {
978
1113
  output.push('FROM');
@@ -984,12 +1119,41 @@ class Deparser {
984
1119
  output.push('WHERE');
985
1120
  output.push(this.visit(node.whereClause, context));
986
1121
  }
987
- if (node.returningList) {
988
- output.push('RETURNING');
989
- output.push(this.deparseReturningList(node.returningList, context));
1122
+ if (node.returningClause) {
1123
+ output.push(this.ReturningClause(node.returningClause, context));
990
1124
  }
991
1125
  return output.join(' ');
992
1126
  }
1127
+ formatUpdateAssignments(targets, context) {
1128
+ const targetList = list_utils_1.ListUtils.unwrapList(targets);
1129
+ const processedTargets = new Set();
1130
+ const assignmentParts = [];
1131
+ for (let i = 0; i < targetList.length; i++) {
1132
+ if (processedTargets.has(i))
1133
+ continue;
1134
+ const target = targetList[i];
1135
+ const multiAssignRef = target.ResTarget?.val?.MultiAssignRef;
1136
+ if (multiAssignRef) {
1137
+ const relatedTargets = [];
1138
+ for (let j = i; j < targetList.length; j++) {
1139
+ const otherTarget = targetList[j];
1140
+ const otherMultiAssignRef = otherTarget.ResTarget?.val?.MultiAssignRef;
1141
+ if (otherMultiAssignRef &&
1142
+ JSON.stringify(otherMultiAssignRef.source) === JSON.stringify(multiAssignRef.source)) {
1143
+ relatedTargets.push(otherTarget);
1144
+ processedTargets.add(j);
1145
+ }
1146
+ }
1147
+ const names = relatedTargets.map(t => quotes_1.QuoteUtils.quoteIdentifier(t.ResTarget.name));
1148
+ assignmentParts.push(`${context.parens(names.join(', '))} = ${this.visit(multiAssignRef.source, context)}`);
1149
+ }
1150
+ else {
1151
+ assignmentParts.push(this.visit(target, context.spawn('UpdateStmt', { update: true })));
1152
+ processedTargets.add(i);
1153
+ }
1154
+ }
1155
+ return assignmentParts.join(', ');
1156
+ }
993
1157
  DeleteStmt(node, context) {
994
1158
  try {
995
1159
  const output = [];
@@ -1037,13 +1201,12 @@ class Deparser {
1037
1201
  throw new Error(`Error deparsing DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1038
1202
  }
1039
1203
  }
1040
- if (node.returningList) {
1041
- output.push('RETURNING');
1204
+ if (node.returningClause) {
1042
1205
  try {
1043
- output.push(this.deparseReturningList(node.returningList, context));
1206
+ output.push(this.ReturningClause(node.returningClause, context));
1044
1207
  }
1045
1208
  catch (error) {
1046
- console.warn(`Error processing returningList in DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1209
+ console.warn(`Error processing returningClause in DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1047
1210
  throw new Error(`Error deparsing DeleteStmt: ${error instanceof Error ? error.message : String(error)}`);
1048
1211
  }
1049
1212
  }
@@ -1064,9 +1227,6 @@ class Deparser {
1064
1227
  const cteStrings = ctes.map((cte, index) => {
1065
1228
  const cteStr = this.visit(cte, context);
1066
1229
  const prefix = index === 0 ? context.newline() : ',' + context.newline();
1067
- if (this.containsMultilineStringLiteral(cteStr)) {
1068
- return prefix + cteStr;
1069
- }
1070
1230
  return prefix + context.indent(cteStr);
1071
1231
  });
1072
1232
  output.push(cteStrings.join(''));
@@ -1081,12 +1241,12 @@ class Deparser {
1081
1241
  ResTarget(node, context) {
1082
1242
  const output = [];
1083
1243
  if (context.update && node.name) {
1084
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
1244
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
1085
1245
  // Handle indirection (array indexing, field access, etc.)
1086
1246
  if (node.indirection && node.indirection.length > 0) {
1087
1247
  const indirectionStrs = list_utils_1.ListUtils.unwrapList(node.indirection).map(item => {
1088
1248
  if (item.String) {
1089
- return `.${quote_utils_1.QuoteUtils.quote(item.String.sval || item.String.str)}`;
1249
+ return `.${quotes_1.QuoteUtils.quoteIdentifierAfterDot(item.String.sval || item.String.str)}`;
1090
1250
  }
1091
1251
  return this.visit(item, context);
1092
1252
  });
@@ -1098,12 +1258,12 @@ class Deparser {
1098
1258
  }
1099
1259
  }
1100
1260
  else if (context.insertColumns && node.name) {
1101
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
1261
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
1102
1262
  // Handle indirection for INSERT column lists (e.g., q.c1.r)
1103
1263
  if (node.indirection && node.indirection.length > 0) {
1104
1264
  const indirectionStrs = list_utils_1.ListUtils.unwrapList(node.indirection).map(item => {
1105
1265
  if (item.String) {
1106
- return `.${quote_utils_1.QuoteUtils.quote(item.String.sval || item.String.str)}`;
1266
+ return `.${quotes_1.QuoteUtils.quoteIdentifierAfterDot(item.String.sval || item.String.str)}`;
1107
1267
  }
1108
1268
  return this.visit(item, context);
1109
1269
  });
@@ -1116,11 +1276,53 @@ class Deparser {
1116
1276
  }
1117
1277
  if (node.name) {
1118
1278
  output.push('AS');
1119
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
1279
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
1280
+ }
1281
+ }
1282
+ return output.join(' ');
1283
+ }
1284
+ ATAlterConstraint(node, context) {
1285
+ const output = ['ALTER CONSTRAINT'];
1286
+ if (node.conname) {
1287
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.conname));
1288
+ }
1289
+ if (node.alterEnforceability) {
1290
+ output.push(node.is_enforced ? 'ENFORCED' : 'NOT ENFORCED');
1291
+ }
1292
+ if (node.alterDeferrability) {
1293
+ output.push(node.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
1294
+ if (node.initdeferred) {
1295
+ output.push('INITIALLY DEFERRED');
1120
1296
  }
1121
1297
  }
1298
+ if (node.alterInheritability) {
1299
+ output.push(node.noinherit ? 'NO INHERIT' : 'INHERIT');
1300
+ }
1301
+ return output.join(' ');
1302
+ }
1303
+ deparseKeyAttrs(attrs, withPeriod, context) {
1304
+ const items = list_utils_1.ListUtils.unwrapList(attrs).map(attr => this.visit(attr, context));
1305
+ if (withPeriod && items.length > 0) {
1306
+ items[items.length - 1] = `PERIOD ${items[items.length - 1]}`;
1307
+ }
1308
+ return items.join(', ');
1309
+ }
1310
+ ReturningClause(node, context) {
1311
+ const output = ['RETURNING'];
1312
+ if (node.options && node.options.length > 0) {
1313
+ const options = list_utils_1.ListUtils.unwrapList(node.options)
1314
+ .map(option => this.visit(option, context));
1315
+ output.push(`WITH (${options.join(', ')})`);
1316
+ }
1317
+ if (node.exprs && node.exprs.length > 0) {
1318
+ output.push(this.deparseReturningList(node.exprs, context));
1319
+ }
1122
1320
  return output.join(' ');
1123
1321
  }
1322
+ ReturningOption(node, context) {
1323
+ const keyword = node.option === 'RETURNING_OPTION_NEW' ? 'NEW' : 'OLD';
1324
+ return `${keyword} AS ${quotes_1.QuoteUtils.quoteIdentifier(node.value)}`;
1325
+ }
1124
1326
  deparseReturningList(list, context) {
1125
1327
  return list_utils_1.ListUtils.unwrapList(list)
1126
1328
  .filter(item => item != null && this.getNodeType(item) !== 'undefined')
@@ -1130,7 +1332,7 @@ class Deparser {
1130
1332
  if (this.getNodeType(item) === 'ResTarget') {
1131
1333
  const resTarget = this.getNodeData(item);
1132
1334
  const val = resTarget.val ? this.visit(resTarget.val, context) : '';
1133
- const alias = resTarget.name ? ` AS ${quote_utils_1.QuoteUtils.quote(resTarget.name)}` : '';
1335
+ const alias = resTarget.name ? ` AS ${quotes_1.QuoteUtils.quoteIdentifier(resTarget.name)}` : '';
1134
1336
  return val + alias;
1135
1337
  }
1136
1338
  else {
@@ -1185,7 +1387,8 @@ class Deparser {
1185
1387
  FuncCall(node, context) {
1186
1388
  const funcname = list_utils_1.ListUtils.unwrapList(node.funcname);
1187
1389
  const args = list_utils_1.ListUtils.unwrapList(node.args);
1188
- const name = funcname.map(n => this.visit(n, context)).join('.');
1390
+ const funcnameParts = funcname.map((n) => n.String?.sval || n.String?.str || '').filter((s) => s);
1391
+ const name = quotes_1.QuoteUtils.quoteDottedName(funcnameParts);
1189
1392
  // Handle special SQL syntax functions like XMLEXISTS and EXTRACT
1190
1393
  if (node.funcformat === 'COERCE_SQL_SYNTAX' && name === 'pg_catalog.xmlexists' && args.length >= 2) {
1191
1394
  const xpath = this.visit(args[0], context);
@@ -1194,8 +1397,15 @@ class Deparser {
1194
1397
  }
1195
1398
  // Handle EXTRACT function with SQL syntax
1196
1399
  if (node.funcformat === 'COERCE_SQL_SYNTAX' && name === 'pg_catalog.extract' && args.length >= 2) {
1197
- const field = this.visit(args[0], context);
1400
+ let field = this.visit(args[0], context);
1198
1401
  const source = this.visit(args[1], context);
1402
+ // EXTRACT requires an unquoted uppercase keyword (EPOCH, YEAR, etc.)
1403
+ // but A_Const sval deparsing wraps it in single quotes — strip and uppercase
1404
+ // only when it is spelled the way the bare-keyword form parses back
1405
+ if (/^'(?:year|month|day|hour|minute|second)'$/.test(field)
1406
+ || (/^'[a-z_][a-z0-9_]*'$/.test(field) && (0, quotes_1.keywordKindOf)(field.slice(1, -1)) === 'NO_KEYWORD')) {
1407
+ field = field.slice(1, -1).toUpperCase();
1408
+ }
1199
1409
  return `EXTRACT(${field} FROM ${source})`;
1200
1410
  }
1201
1411
  // Handle TRIM function with SQL syntax (TRIM TRAILING/LEADING/BOTH)
@@ -1355,43 +1565,41 @@ class Deparser {
1355
1565
  result += ` FILTER (WHERE ${this.visit(node.agg_filter, context)})`;
1356
1566
  }
1357
1567
  if (node.over) {
1358
- // Handle named window references first
1359
- if (node.over.name) {
1360
- result += ` OVER ${node.over.name}`;
1361
- }
1362
- else {
1363
- const windowParts = [];
1364
- if (node.over.partitionClause) {
1365
- const partitions = list_utils_1.ListUtils.unwrapList(node.over.partitionClause);
1366
- const partitionStrs = partitions.map(p => this.visit(p, context));
1367
- windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
1368
- }
1369
- if (node.over.orderClause) {
1370
- const orders = list_utils_1.ListUtils.unwrapList(node.over.orderClause);
1371
- const orderStrs = orders.map(o => this.visit(o, context));
1372
- windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1373
- }
1374
- // Handle window frame specifications using the dedicated formatWindowFrame method
1375
- const frameClause = this.formatWindowFrame(node.over, context.spawn('FuncCall'));
1376
- if (frameClause) {
1377
- windowParts.push(frameClause);
1378
- }
1379
- if (windowParts.length > 0) {
1380
- if (context.isPretty() && windowParts.length > 1) {
1381
- const formattedParts = windowParts.map(part => context.indent(part));
1382
- result += ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1383
- }
1384
- else {
1385
- result += ` OVER (${windowParts.join(' ')})`;
1386
- }
1387
- }
1388
- else {
1389
- result += ` OVER ()`;
1390
- }
1391
- }
1568
+ result += this.formatOverClause(node.over, context);
1392
1569
  }
1393
1570
  return result;
1394
1571
  }
1572
+ formatOverClause(over, context) {
1573
+ if (over.name) {
1574
+ return ` OVER ${over.name}`;
1575
+ }
1576
+ const windowParts = [];
1577
+ if (over.refname) {
1578
+ windowParts.push(over.refname);
1579
+ }
1580
+ if (over.partitionClause) {
1581
+ const partitions = list_utils_1.ListUtils.unwrapList(over.partitionClause);
1582
+ const partitionStrs = partitions.map(p => this.visit(p, context));
1583
+ windowParts.push(`PARTITION BY ${partitionStrs.join(', ')}`);
1584
+ }
1585
+ if (over.orderClause) {
1586
+ const orders = list_utils_1.ListUtils.unwrapList(over.orderClause);
1587
+ const orderStrs = orders.map(o => this.visit(o, context));
1588
+ windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
1589
+ }
1590
+ const frameClause = this.formatWindowFrame(over, context.spawn('FuncCall'));
1591
+ if (frameClause) {
1592
+ windowParts.push(frameClause);
1593
+ }
1594
+ if (windowParts.length === 0) {
1595
+ return ' OVER ()';
1596
+ }
1597
+ if (context.isPretty() && windowParts.length > 1) {
1598
+ const formattedParts = windowParts.map(part => context.indent(part));
1599
+ return ` OVER (${context.newline()}${formattedParts.join(context.newline())}${context.newline()})`;
1600
+ }
1601
+ return ` OVER (${windowParts.join(' ')})`;
1602
+ }
1395
1603
  FuncExpr(node, context) {
1396
1604
  const funcName = `func_${node.funcid}`;
1397
1605
  const args = node.args ? list_utils_1.ListUtils.unwrapList(node.args).map(arg => this.visit(arg, context)).join(', ') : '';
@@ -1402,61 +1610,61 @@ class Deparser {
1402
1610
  if (nodeAny.ival !== undefined) {
1403
1611
  if (typeof nodeAny.ival === 'object' && nodeAny.ival !== null) {
1404
1612
  if (nodeAny.ival.ival !== undefined) {
1405
- return nodeAny.ival.ival.toString();
1613
+ return formatInteger(nodeAny.ival.ival);
1406
1614
  }
1407
1615
  else if (Object.keys(nodeAny.ival).length === 0) {
1408
1616
  return '0';
1409
1617
  }
1410
1618
  else {
1411
- return nodeAny.ival.toString();
1619
+ return formatInteger(nodeAny.ival);
1412
1620
  }
1413
1621
  }
1414
1622
  else if (nodeAny.ival === null) {
1415
1623
  return 'NULL';
1416
1624
  }
1417
1625
  else {
1418
- return nodeAny.ival.toString();
1626
+ return formatInteger(nodeAny.ival);
1419
1627
  }
1420
1628
  }
1421
1629
  else if (nodeAny.fval !== undefined) {
1422
1630
  if (typeof nodeAny.fval === 'object' && nodeAny.fval !== null) {
1423
1631
  if (nodeAny.fval.fval !== undefined) {
1424
- return nodeAny.fval.fval.toString();
1632
+ return formatNumeric(nodeAny.fval.fval);
1425
1633
  }
1426
1634
  else if (Object.keys(nodeAny.fval).length === 0) {
1427
1635
  return '0.0';
1428
1636
  }
1429
1637
  else {
1430
- return nodeAny.fval.toString();
1638
+ return formatNumeric(nodeAny.fval);
1431
1639
  }
1432
1640
  }
1433
1641
  else if (nodeAny.fval === null) {
1434
1642
  return 'NULL';
1435
1643
  }
1436
1644
  else {
1437
- return nodeAny.fval.toString();
1645
+ return formatNumeric(nodeAny.fval);
1438
1646
  }
1439
1647
  }
1440
1648
  else if (nodeAny.sval !== undefined) {
1441
1649
  if (typeof nodeAny.sval === 'object' && nodeAny.sval !== null) {
1442
1650
  if (nodeAny.sval.sval !== undefined) {
1443
- return quote_utils_1.QuoteUtils.formatEString(nodeAny.sval.sval);
1651
+ return quotes_1.QuoteUtils.formatEString(nodeAny.sval.sval);
1444
1652
  }
1445
1653
  else if (nodeAny.sval.String && nodeAny.sval.String.sval !== undefined) {
1446
- return quote_utils_1.QuoteUtils.formatEString(nodeAny.sval.String.sval);
1654
+ return quotes_1.QuoteUtils.formatEString(nodeAny.sval.String.sval);
1447
1655
  }
1448
1656
  else if (Object.keys(nodeAny.sval).length === 0) {
1449
1657
  return "''";
1450
1658
  }
1451
1659
  else {
1452
- return quote_utils_1.QuoteUtils.formatEString(nodeAny.sval.toString());
1660
+ return quotes_1.QuoteUtils.formatEString(nodeAny.sval.toString());
1453
1661
  }
1454
1662
  }
1455
1663
  else if (nodeAny.sval === null) {
1456
1664
  return 'NULL';
1457
1665
  }
1458
1666
  else {
1459
- return quote_utils_1.QuoteUtils.formatEString(nodeAny.sval);
1667
+ return quotes_1.QuoteUtils.formatEString(nodeAny.sval);
1460
1668
  }
1461
1669
  }
1462
1670
  else if (nodeAny.boolval !== undefined) {
@@ -1481,15 +1689,7 @@ class Deparser {
1481
1689
  else if (nodeAny.bsval !== undefined) {
1482
1690
  if (typeof nodeAny.bsval === 'object' && nodeAny.bsval !== null) {
1483
1691
  if (nodeAny.bsval.bsval !== undefined) {
1484
- const bsval = nodeAny.bsval.bsval;
1485
- // Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
1486
- if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
1487
- return `x'${bsval.substring(1)}'`;
1488
- }
1489
- if (bsval.startsWith('b')) {
1490
- return `b'${bsval.substring(1)}'`;
1491
- }
1492
- return `b'${bsval}'`;
1692
+ return formatBitString(nodeAny.bsval.bsval);
1493
1693
  }
1494
1694
  else if (Object.keys(nodeAny.bsval).length === 0) {
1495
1695
  return "''";
@@ -1502,32 +1702,24 @@ class Deparser {
1502
1702
  return 'NULL';
1503
1703
  }
1504
1704
  else {
1505
- const bsval = nodeAny.bsval;
1506
- // Check if this is a hexadecimal bit string (starts with x and contains only hex digits)
1507
- if (bsval.startsWith('x') && /^x[0-9A-Fa-f]+$/.test(bsval)) {
1508
- return `x'${bsval.substring(1)}'`;
1509
- }
1510
- if (bsval.startsWith('b')) {
1511
- return `b'${bsval.substring(1)}'`;
1512
- }
1513
- return `b'${bsval}'`;
1705
+ return formatBitString(nodeAny.bsval);
1514
1706
  }
1515
1707
  }
1516
1708
  if (nodeAny.val) {
1517
1709
  if (nodeAny.val.Integer?.ival !== undefined) {
1518
- return nodeAny.val.Integer.ival.toString();
1710
+ return formatInteger(nodeAny.val.Integer.ival);
1519
1711
  }
1520
1712
  else if (nodeAny.val.Float?.fval !== undefined) {
1521
- return nodeAny.val.Float.fval.toString();
1713
+ return formatNumeric(nodeAny.val.Float.fval);
1522
1714
  }
1523
1715
  else if (nodeAny.val.String?.sval !== undefined) {
1524
- return quote_utils_1.QuoteUtils.escape(nodeAny.val.String.sval);
1716
+ return quotes_1.QuoteUtils.escape(nodeAny.val.String.sval);
1525
1717
  }
1526
1718
  else if (nodeAny.val.Boolean?.boolval !== undefined) {
1527
1719
  return nodeAny.val.Boolean.boolval ? 'true' : 'false';
1528
1720
  }
1529
1721
  else if (nodeAny.val.BitString?.bsval !== undefined) {
1530
- return nodeAny.val.BitString.bsval;
1722
+ return formatBitString(nodeAny.val.BitString.bsval);
1531
1723
  }
1532
1724
  }
1533
1725
  if (nodeAny.isnull === true) {
@@ -1539,21 +1731,21 @@ class Deparser {
1539
1731
  }
1540
1732
  if (nodeAny.Integer !== undefined) {
1541
1733
  if (typeof nodeAny.Integer === 'object' && nodeAny.Integer.ival !== undefined) {
1542
- return nodeAny.Integer.ival.toString();
1734
+ return formatInteger(nodeAny.Integer.ival);
1543
1735
  }
1544
- return nodeAny.Integer.toString();
1736
+ return formatInteger(nodeAny.Integer);
1545
1737
  }
1546
1738
  if (nodeAny.Float !== undefined) {
1547
1739
  if (typeof nodeAny.Float === 'object' && nodeAny.Float.fval !== undefined) {
1548
- return nodeAny.Float.fval.toString();
1740
+ return formatNumeric(nodeAny.Float.fval);
1549
1741
  }
1550
- return nodeAny.Float.toString();
1742
+ return formatNumeric(nodeAny.Float);
1551
1743
  }
1552
1744
  if (nodeAny.String !== undefined) {
1553
1745
  if (typeof nodeAny.String === 'object' && nodeAny.String.sval !== undefined) {
1554
- return quote_utils_1.QuoteUtils.escape(nodeAny.String.sval);
1746
+ return quotes_1.QuoteUtils.escape(nodeAny.String.sval);
1555
1747
  }
1556
- return quote_utils_1.QuoteUtils.escape(nodeAny.String);
1748
+ return quotes_1.QuoteUtils.escape(nodeAny.String);
1557
1749
  }
1558
1750
  if (Object.keys(nodeAny).length === 0) {
1559
1751
  return 'NULL';
@@ -1567,7 +1759,7 @@ class Deparser {
1567
1759
  const fields = list_utils_1.ListUtils.unwrapList(node.fields);
1568
1760
  return fields.map(field => {
1569
1761
  if (field.String) {
1570
- return quote_utils_1.QuoteUtils.quote(field.String.sval || field.String.str);
1762
+ return quotes_1.QuoteUtils.quoteIdentifier(field.String.sval || field.String.str);
1571
1763
  }
1572
1764
  else if (field.A_Star) {
1573
1765
  return '*';
@@ -1644,8 +1836,7 @@ class Deparser {
1644
1836
  }
1645
1837
  return output.join(' ');
1646
1838
  }
1647
- const quotedTypeName = quote_utils_1.QuoteUtils.quote(typeName);
1648
- let result = mods(quotedTypeName, args);
1839
+ let result = mods(quotes_1.QuoteUtils.quoteIdentifierTypeName(typeName), args);
1649
1840
  if (node.arrayBounds && node.arrayBounds.length > 0) {
1650
1841
  result += formatArrayBounds(node.arrayBounds);
1651
1842
  }
@@ -1741,8 +1932,9 @@ class Deparser {
1741
1932
  return output.join(' ');
1742
1933
  }
1743
1934
  }
1744
- const quotedNames = names.map((name) => quote_utils_1.QuoteUtils.quote(name));
1745
- let result = mods(quotedNames.join('.'), args);
1935
+ // Use type-name quoting for non-pg_catalog types
1936
+ // This allows keywords like 'json', 'int', 'boolean' to remain unquoted in type positions
1937
+ let result = mods(quotes_1.QuoteUtils.quoteTypeDottedName(names), args);
1746
1938
  if (node.arrayBounds && node.arrayBounds.length > 0) {
1747
1939
  result += formatArrayBounds(node.arrayBounds);
1748
1940
  }
@@ -1780,17 +1972,17 @@ class Deparser {
1780
1972
  }
1781
1973
  let tableName = '';
1782
1974
  if (node.catalogname) {
1783
- tableName = quote_utils_1.QuoteUtils.quote(node.catalogname);
1975
+ tableName = quotes_1.QuoteUtils.quoteIdentifier(node.catalogname);
1784
1976
  if (node.schemaname) {
1785
- tableName += '.' + quote_utils_1.QuoteUtils.quote(node.schemaname);
1977
+ tableName += '.' + quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.schemaname);
1786
1978
  }
1787
- tableName += '.' + quote_utils_1.QuoteUtils.quote(node.relname);
1979
+ tableName += '.' + quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.relname);
1788
1980
  }
1789
1981
  else if (node.schemaname) {
1790
- tableName = quote_utils_1.QuoteUtils.quote(node.schemaname) + '.' + quote_utils_1.QuoteUtils.quote(node.relname);
1982
+ tableName = quotes_1.QuoteUtils.quoteQualifiedIdentifier(node.schemaname, node.relname);
1791
1983
  }
1792
1984
  else {
1793
- tableName = quote_utils_1.QuoteUtils.quote(node.relname);
1985
+ tableName = quotes_1.QuoteUtils.quoteIdentifier(node.relname);
1794
1986
  }
1795
1987
  output.push(tableName);
1796
1988
  if (node.alias) {
@@ -1944,7 +2136,7 @@ class Deparser {
1944
2136
  if (size != null) {
1945
2137
  return 'char';
1946
2138
  }
1947
- return 'pg_catalog.bpchar';
2139
+ return 'bpchar';
1948
2140
  case 'varchar':
1949
2141
  return 'varchar';
1950
2142
  case 'numeric':
@@ -1958,7 +2150,7 @@ class Deparser {
1958
2150
  case 'int8':
1959
2151
  return 'bigint';
1960
2152
  case 'real':
1961
- return 'pg_catalog.float4';
2153
+ return 'float4';
1962
2154
  case 'time':
1963
2155
  return 'time';
1964
2156
  case 'timestamp':
@@ -1968,7 +2160,7 @@ class Deparser {
1968
2160
  case 'bit':
1969
2161
  return 'bit';
1970
2162
  default:
1971
- return `pg_catalog.${typeName}`;
2163
+ return typeName;
1972
2164
  }
1973
2165
  }
1974
2166
  isPgCatalogType(typeName) {
@@ -2009,14 +2201,16 @@ class Deparser {
2009
2201
  A_Indirection(node, context) {
2010
2202
  let argStr = this.visit(node.arg, context);
2011
2203
  const argType = this.getNodeType(node.arg);
2012
- if (argType === 'TypeCast' || argType === 'SubLink' || argType === 'A_Expr' || argType === 'FuncCall' || argType === 'A_Indirection' || argType === 'ColumnRef' || argType === 'RowExpr') {
2204
+ // Grammar: only a parameter takes indirection bare; every other target is
2205
+ // `'(' a_expr ')' opt_indirection` (a plain column subscript is the same node).
2206
+ if (argType !== 'ParamRef') {
2013
2207
  argStr = `(${argStr})`;
2014
2208
  }
2015
2209
  const output = [argStr];
2016
2210
  const indirection = list_utils_1.ListUtils.unwrapList(node.indirection);
2017
2211
  for (const subnode of indirection) {
2018
2212
  if (subnode.String || subnode.A_Star) {
2019
- const value = subnode.A_Star ? '*' : quote_utils_1.QuoteUtils.quote(subnode.String.sval || subnode.String.str);
2213
+ const value = subnode.A_Star ? '*' : quotes_1.QuoteUtils.quoteIdentifier(subnode.String.sval || subnode.String.str);
2020
2214
  output.push(`.${value}`);
2021
2215
  }
2022
2216
  else {
@@ -2037,21 +2231,11 @@ class Deparser {
2037
2231
  if (context.isPretty() && args.length > 0) {
2038
2232
  for (const arg of args) {
2039
2233
  const whenClause = this.visit(arg, context);
2040
- if (this.containsMultilineStringLiteral(whenClause)) {
2041
- output.push(context.newline() + whenClause);
2042
- }
2043
- else {
2044
- output.push(context.newline() + context.indent(whenClause));
2045
- }
2234
+ output.push(context.newline() + context.indent(whenClause));
2046
2235
  }
2047
2236
  if (node.defresult) {
2048
2237
  const elseResult = this.visit(node.defresult, context);
2049
- if (this.containsMultilineStringLiteral(elseResult)) {
2050
- output.push(context.newline() + 'ELSE ' + elseResult);
2051
- }
2052
- else {
2053
- output.push(context.newline() + context.indent('ELSE ' + elseResult));
2054
- }
2238
+ output.push(context.newline() + context.indent('ELSE ' + elseResult));
2055
2239
  }
2056
2240
  output.push(context.newline() + 'END');
2057
2241
  return output.join(' ');
@@ -2092,10 +2276,13 @@ class Deparser {
2092
2276
  const isFunctionCall = argType === 'FuncCall';
2093
2277
  if (isSimpleArgument || isFunctionCall) {
2094
2278
  // For simple arguments, avoid :: syntax if they have complex structure
2095
- if (isSimpleArgument && (arg.includes('(') || arg.startsWith('-'))) {
2096
- }
2097
- else {
2279
+ const shouldUseCastSyntax = isSimpleArgument && (arg.includes('(') || arg.startsWith('-'));
2280
+ if (!shouldUseCastSyntax) {
2098
2281
  const cleanTypeName = typeName.replace('pg_catalog.', '');
2282
+ // Wrap FuncCall arguments in parentheses to prevent operator precedence issues
2283
+ if (isFunctionCall) {
2284
+ return `${context.parens(arg)}::${cleanTypeName}`;
2285
+ }
2099
2286
  return `${arg}::${cleanTypeName}`;
2100
2287
  }
2101
2288
  }
@@ -2158,51 +2345,27 @@ class Deparser {
2158
2345
  }
2159
2346
  return output.join(' ');
2160
2347
  }
2161
- static RESERVED_WORDS = new Set([
2162
- 'all', 'analyse', 'analyze', 'and', 'any', 'array', 'as', 'asc', 'asymmetric', 'both',
2163
- 'case', 'cast', 'check', 'collate', 'column', 'constraint', 'create', 'current_catalog',
2164
- 'current_date', 'current_role', 'current_time', 'current_timestamp', 'current_user',
2165
- 'default', 'deferrable', 'desc', 'distinct', 'do', 'else', 'end', 'except', 'false',
2166
- 'fetch', 'for', 'foreign', 'from', 'grant', 'group', 'having', 'in', 'initially',
2167
- 'intersect', 'into', 'lateral', 'leading', 'limit', 'localtime', 'localtimestamp',
2168
- 'not', 'null', 'offset', 'on', 'only', 'or', 'order', 'placing', 'primary',
2169
- 'references', 'returning', 'select', 'session_user', 'some', 'symmetric', 'table',
2170
- 'then', 'to', 'trailing', 'true', 'union', 'unique', 'user', 'using', 'variadic',
2171
- 'when', 'where', 'window', 'with'
2172
- ]);
2173
- static needsQuotes(value) {
2174
- if (!value)
2175
- return false;
2176
- const needsQuotesRegex = /[a-z]+[\W\w]*[A-Z]+|[A-Z]+[\W\w]*[a-z]+|\W/;
2177
- const isAllUppercase = /^[A-Z]+$/.test(value);
2178
- return needsQuotesRegex.test(value) ||
2179
- Deparser.RESERVED_WORDS.has(value.toLowerCase()) ||
2180
- isAllUppercase;
2181
- }
2182
2348
  quoteIfNeeded(value) {
2183
- if (Deparser.needsQuotes(value)) {
2184
- return `"${value}"`;
2185
- }
2186
- return value;
2349
+ return quotes_1.QuoteUtils.quoteIdentifier(value);
2187
2350
  }
2188
2351
  preserveOperatorDefElemCase(defName) {
2189
2352
  const caseMap = {
2190
- 'leftarg': 'Leftarg',
2191
- 'rightarg': 'Rightarg',
2192
- 'procedure': 'Procedure',
2193
- 'function': 'Function',
2194
- 'commutator': 'Commutator',
2195
- 'negator': 'Negator',
2196
- 'restrict': 'Restrict',
2197
- 'join': 'Join',
2198
- 'hashes': 'Hashes',
2199
- 'merges': 'Merges'
2353
+ leftarg: 'Leftarg',
2354
+ rightarg: 'Rightarg',
2355
+ procedure: 'Procedure',
2356
+ function: 'Function',
2357
+ commutator: 'Commutator',
2358
+ negator: 'Negator',
2359
+ restrict: 'Restrict',
2360
+ join: 'Join',
2361
+ hashes: 'Hashes',
2362
+ merges: 'Merges'
2200
2363
  };
2201
2364
  return caseMap[defName.toLowerCase()] || defName;
2202
2365
  }
2203
2366
  String(node, context) {
2204
2367
  if (context.isStringLiteral || context.isEnumValue) {
2205
- return quote_utils_1.QuoteUtils.formatEString(node.sval || '');
2368
+ return quotes_1.QuoteUtils.formatEString(node.sval || '');
2206
2369
  }
2207
2370
  const value = node.sval || '';
2208
2371
  if (context.parentNodeTypes.includes('DefElem') ||
@@ -2216,27 +2379,19 @@ class Deparser {
2216
2379
  return value; // Don't quote pure operator symbols like "=" or "-"
2217
2380
  }
2218
2381
  }
2219
- return Deparser.needsQuotes(value) ? `"${value}"` : value;
2382
+ return quotes_1.QuoteUtils.quoteIdentifier(value);
2220
2383
  }
2221
2384
  Integer(node, context) {
2222
- return node.ival?.toString() || '0';
2385
+ return node.ival === undefined || node.ival === null ? '0' : formatInteger(node.ival);
2223
2386
  }
2224
2387
  Float(node, context) {
2225
- return node.fval || '0.0';
2388
+ return node.fval ? formatNumeric(node.fval) : '0.0';
2226
2389
  }
2227
2390
  Boolean(node, context) {
2228
2391
  return node.boolval ? 'true' : 'false';
2229
2392
  }
2230
2393
  BitString(node, context) {
2231
- // Check if this is a hexadecimal bit string (starts with x)
2232
- if (node.bsval.startsWith('x')) {
2233
- return `x'${node.bsval.substring(1)}'`;
2234
- }
2235
- if (node.bsval.startsWith('b')) {
2236
- return `b'${node.bsval.substring(1)}'`;
2237
- }
2238
- // Fallback for raw values without prefix
2239
- return `b'${node.bsval}'`;
2394
+ return formatBitString(node.bsval);
2240
2395
  }
2241
2396
  Null(node, context) {
2242
2397
  return 'NULL';
@@ -2275,7 +2430,7 @@ class Deparser {
2275
2430
  output.push(context.parens(elementStrs.join(', ')));
2276
2431
  }
2277
2432
  }
2278
- else if (node.tableElts) {
2433
+ else if (node.tableElts && !node.partbound) {
2279
2434
  const elements = list_utils_1.ListUtils.unwrapList(node.tableElts);
2280
2435
  const elementStrs = elements.map(el => {
2281
2436
  return this.deparse(el, context.spawn('CreateStmt'));
@@ -2303,7 +2458,16 @@ class Deparser {
2303
2458
  const inherits = list_utils_1.ListUtils.unwrapList(node.inhRelations);
2304
2459
  const inheritStrs = inherits.map(rel => this.visit(rel, context));
2305
2460
  output.push(inheritStrs[0]);
2306
- if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
2461
+ if (node.tableElts) {
2462
+ const elementStrs = list_utils_1.ListUtils.unwrapList(node.tableElts).map(el => {
2463
+ return this.deparse(el, context.spawn('CreateStmt'));
2464
+ });
2465
+ output.push(context.parens(elementStrs.join(', ')));
2466
+ }
2467
+ if (node.partbound.is_default) {
2468
+ output.push('DEFAULT');
2469
+ }
2470
+ else if (node.partbound.strategy === 'l' && node.partbound.listdatums) {
2307
2471
  output.push('FOR VALUES IN');
2308
2472
  const listValues = list_utils_1.ListUtils.unwrapList(node.partbound.listdatums)
2309
2473
  .map(datum => this.visit(datum, context))
@@ -2326,13 +2490,9 @@ class Deparser {
2326
2490
  output.push(`(${upperValues})`);
2327
2491
  }
2328
2492
  }
2329
- else if (node.partbound.strategy === 'h' && node.partbound.modulus !== undefined) {
2493
+ else if (node.partbound.strategy === 'h') {
2330
2494
  output.push('FOR VALUES WITH');
2331
- const remainder = node.partbound.remainder !== undefined ? node.partbound.remainder : 0;
2332
- output.push(`(MODULUS ${node.partbound.modulus}, REMAINDER ${remainder})`);
2333
- }
2334
- else if (node.partbound.is_default) {
2335
- output.push('DEFAULT');
2495
+ output.push(`(MODULUS ${node.partbound.modulus ?? 0}, REMAINDER ${node.partbound.remainder ?? 0})`);
2336
2496
  }
2337
2497
  }
2338
2498
  else if (node.inhRelations) {
@@ -2393,11 +2553,17 @@ class Deparser {
2393
2553
  ColumnDef(node, context) {
2394
2554
  const output = [];
2395
2555
  if (node.colname) {
2396
- output.push(quote_utils_1.QuoteUtils.quote(node.colname));
2556
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.colname));
2397
2557
  }
2398
2558
  if (node.typeName) {
2399
2559
  output.push(this.TypeName(node.typeName, context));
2400
2560
  }
2561
+ if (node.storage_name) {
2562
+ output.push('STORAGE', node.storage_name.toUpperCase());
2563
+ }
2564
+ if (node.compression) {
2565
+ output.push('COMPRESSION', quotes_1.QuoteUtils.quoteIdentifier(node.compression));
2566
+ }
2401
2567
  if (node.fdwoptions && node.fdwoptions.length > 0) {
2402
2568
  output.push('OPTIONS');
2403
2569
  const columnContext = context.spawn('ColumnDef');
@@ -2432,9 +2598,9 @@ class Deparser {
2432
2598
  Constraint(node, context) {
2433
2599
  const output = [];
2434
2600
  // Handle constraint name if present
2435
- if (node.conname && (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_FOREIGN')) {
2601
+ if (node.conname && (node.contype === 'CONSTR_CHECK' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_NOTNULL' || node.contype === 'CONSTR_EXCLUSION')) {
2436
2602
  output.push('CONSTRAINT');
2437
- output.push(quote_utils_1.QuoteUtils.quote(node.conname));
2603
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.conname));
2438
2604
  }
2439
2605
  switch (node.contype) {
2440
2606
  case 'CONSTR_NULL':
@@ -2442,11 +2608,24 @@ class Deparser {
2442
2608
  break;
2443
2609
  case 'CONSTR_NOTNULL':
2444
2610
  output.push('NOT NULL');
2611
+ if (node.keys && node.keys.length > 0 && !context.isColumnConstraint) {
2612
+ const keyList = list_utils_1.ListUtils.unwrapList(node.keys)
2613
+ .map(key => this.visit(key, context))
2614
+ .join(', ');
2615
+ output.push(keyList);
2616
+ }
2617
+ if (node.is_no_inherit) {
2618
+ output.push('NO INHERIT');
2619
+ }
2620
+ if (node.skip_validation) {
2621
+ output.push('NOT VALID');
2622
+ }
2445
2623
  break;
2446
2624
  case 'CONSTR_DEFAULT':
2447
2625
  output.push('DEFAULT');
2448
2626
  if (node.raw_expr) {
2449
- output.push(this.visit(node.raw_expr, context));
2627
+ const defaultExpr = this.visit(node.raw_expr, context);
2628
+ output.push(this.needsBExprParens(node.raw_expr) ? `(${defaultExpr})` : defaultExpr);
2450
2629
  }
2451
2630
  break;
2452
2631
  case 'CONSTR_CHECK':
@@ -2470,8 +2649,13 @@ class Deparser {
2470
2649
  output.push(context.parens(this.visit(node.raw_expr, context)));
2471
2650
  }
2472
2651
  }
2473
- // Handle NOT VALID for check constraints
2474
- if (node.skip_validation) {
2652
+ // PG18: constraints are enforced unless explicitly NOT ENFORCED
2653
+ // (libpg-query omits false booleans, so undefined means not enforced)
2654
+ if (node.is_enforced !== true && !context.isDomainConstraint) {
2655
+ output.push('NOT ENFORCED');
2656
+ }
2657
+ else if (node.skip_validation) {
2658
+ // NOT ENFORCED implies NOT VALID, so only emit NOT VALID when enforced
2475
2659
  output.push('NOT VALID');
2476
2660
  }
2477
2661
  // Handle NO INHERIT for check constraints - only for table constraints, not domain constraints
@@ -2491,7 +2675,7 @@ class Deparser {
2491
2675
  if (node.raw_expr) {
2492
2676
  output.push(context.parens(this.visit(node.raw_expr, context)));
2493
2677
  }
2494
- output.push('STORED');
2678
+ output.push(node.generated_kind === 'v' ? 'VIRTUAL' : 'STORED');
2495
2679
  break;
2496
2680
  case 'CONSTR_IDENTITY':
2497
2681
  output.push('GENERATED');
@@ -2567,10 +2751,12 @@ class Deparser {
2567
2751
  case 'CONSTR_PRIMARY':
2568
2752
  output.push('PRIMARY KEY');
2569
2753
  if (node.keys && node.keys.length > 0) {
2570
- const keyList = list_utils_1.ListUtils.unwrapList(node.keys)
2571
- .map(key => this.visit(key, context))
2572
- .join(', ');
2573
- output.push(`(${keyList})`);
2754
+ const keyItems = list_utils_1.ListUtils.unwrapList(node.keys)
2755
+ .map(key => this.visit(key, context));
2756
+ if (node.without_overlaps) {
2757
+ keyItems[keyItems.length - 1] += ' WITHOUT OVERLAPS';
2758
+ }
2759
+ output.push(`(${keyItems.join(', ')})`);
2574
2760
  }
2575
2761
  if (node.indexname) {
2576
2762
  output.push('USING INDEX');
@@ -2588,10 +2774,12 @@ class Deparser {
2588
2774
  output.push('NULLS NOT DISTINCT');
2589
2775
  }
2590
2776
  if (node.keys && node.keys.length > 0) {
2591
- const keyList = list_utils_1.ListUtils.unwrapList(node.keys)
2592
- .map(key => this.visit(key, context))
2593
- .join(', ');
2594
- output.push(`(${keyList})`);
2777
+ const keyItems = list_utils_1.ListUtils.unwrapList(node.keys)
2778
+ .map(key => this.visit(key, context));
2779
+ if (node.without_overlaps) {
2780
+ keyItems[keyItems.length - 1] += ' WITHOUT OVERLAPS';
2781
+ }
2782
+ output.push(`(${keyItems.join(', ')})`);
2595
2783
  }
2596
2784
  if (node.indexname) {
2597
2785
  output.push('USING INDEX');
@@ -2604,20 +2792,14 @@ class Deparser {
2604
2792
  if (context.isPretty()) {
2605
2793
  output.push('\n' + context.indent('FOREIGN KEY'));
2606
2794
  if (node.fk_attrs && node.fk_attrs.length > 0) {
2607
- const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2608
- .map(attr => this.visit(attr, context))
2609
- .join(', ');
2610
- output.push(`(${fkAttrs})`);
2795
+ output.push(`(${this.deparseKeyAttrs(node.fk_attrs, node.fk_with_period, context)})`);
2611
2796
  }
2612
2797
  output.push('\n' + context.indent('REFERENCES'));
2613
2798
  }
2614
2799
  else {
2615
2800
  output.push('FOREIGN KEY');
2616
2801
  if (node.fk_attrs && node.fk_attrs.length > 0) {
2617
- const fkAttrs = list_utils_1.ListUtils.unwrapList(node.fk_attrs)
2618
- .map(attr => this.visit(attr, context))
2619
- .join(', ');
2620
- output.push(`(${fkAttrs})`);
2802
+ output.push(`(${this.deparseKeyAttrs(node.fk_attrs, node.fk_with_period, context)})`);
2621
2803
  }
2622
2804
  output.push('REFERENCES');
2623
2805
  }
@@ -2640,9 +2822,7 @@ class Deparser {
2640
2822
  }
2641
2823
  }
2642
2824
  if (node.pk_attrs && node.pk_attrs.length > 0) {
2643
- const pkAttrs = list_utils_1.ListUtils.unwrapList(node.pk_attrs)
2644
- .map(attr => this.visit(attr, context))
2645
- .join(', ');
2825
+ const pkAttrs = this.deparseKeyAttrs(node.pk_attrs, node.pk_with_period, context);
2646
2826
  if (context.isPretty() && !context.isColumnConstraint) {
2647
2827
  const lastIndex = output.length - 1;
2648
2828
  if (lastIndex >= 0) {
@@ -2713,6 +2893,15 @@ class Deparser {
2713
2893
  deleteClause += 'SET DEFAULT';
2714
2894
  break;
2715
2895
  }
2896
+ if ((node.fk_del_action === 'n' || node.fk_del_action === 'd') && node.fk_del_set_cols && node.fk_del_set_cols.length > 0) {
2897
+ const setColumns = list_utils_1.ListUtils.unwrapList(node.fk_del_set_cols)
2898
+ .map(column => {
2899
+ const stringNode = column.String;
2900
+ return quotes_1.QuoteUtils.quoteIdentifier(stringNode.sval || stringNode.str || '');
2901
+ })
2902
+ .join(', ');
2903
+ deleteClause += ` (${setColumns})`;
2904
+ }
2716
2905
  if (context.isPretty()) {
2717
2906
  output.push('\n' + context.indent(deleteClause));
2718
2907
  }
@@ -2721,8 +2910,18 @@ class Deparser {
2721
2910
  output.push(deleteClause.replace('ON DELETE ', ''));
2722
2911
  }
2723
2912
  }
2724
- // Handle NOT VALID for foreign key constraints - only for table constraints, not domain constraints
2725
- if (node.skip_validation && !context.isDomainConstraint) {
2913
+ // PG18: constraints are enforced unless explicitly NOT ENFORCED
2914
+ // (libpg-query omits false booleans, so undefined means not enforced)
2915
+ if (node.is_enforced !== true && !context.isDomainConstraint) {
2916
+ if (context.isPretty() && !context.isColumnConstraint) {
2917
+ output.push('\n' + context.indent('NOT ENFORCED'));
2918
+ }
2919
+ else {
2920
+ output.push('NOT ENFORCED');
2921
+ }
2922
+ }
2923
+ else if (node.skip_validation && !context.isDomainConstraint) {
2924
+ // NOT ENFORCED implies NOT VALID, so only emit NOT VALID when enforced
2726
2925
  if (context.isPretty() && !context.isColumnConstraint) {
2727
2926
  output.push('\n' + context.indent('NOT VALID'));
2728
2927
  }
@@ -2743,6 +2942,12 @@ class Deparser {
2743
2942
  case 'CONSTR_ATTR_IMMEDIATE':
2744
2943
  output.push('INITIALLY IMMEDIATE');
2745
2944
  break;
2945
+ case 'CONSTR_ATTR_ENFORCED':
2946
+ output.push('ENFORCED');
2947
+ break;
2948
+ case 'CONSTR_ATTR_NOT_ENFORCED':
2949
+ output.push('NOT ENFORCED');
2950
+ break;
2746
2951
  case 'CONSTR_EXCLUSION':
2747
2952
  output.push('EXCLUDE');
2748
2953
  if (node.access_method) {
@@ -2777,10 +2982,14 @@ class Deparser {
2777
2982
  });
2778
2983
  output.push(`(${exclusionElements.join(', ')})`);
2779
2984
  }
2985
+ if (node.where_clause) {
2986
+ output.push('WHERE');
2987
+ output.push(context.parens(this.visit(node.where_clause, context)));
2988
+ }
2780
2989
  break;
2781
2990
  }
2782
2991
  // Handle deferrable constraints for all constraint types that support it
2783
- if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN') {
2992
+ if (node.contype === 'CONSTR_PRIMARY' || node.contype === 'CONSTR_UNIQUE' || node.contype === 'CONSTR_FOREIGN' || node.contype === 'CONSTR_EXCLUSION') {
2784
2993
  if (node.deferrable) {
2785
2994
  if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2786
2995
  output.push('\n' + context.indent('DEFERRABLE'));
@@ -2809,6 +3018,9 @@ class Deparser {
2809
3018
  output.push('NOT DEFERRABLE');
2810
3019
  }
2811
3020
  }
3021
+ else if (node.initdeferred) {
3022
+ output.push('INITIALLY DEFERRED');
3023
+ }
2812
3024
  }
2813
3025
  if (context.isPretty() && node.contype === 'CONSTR_FOREIGN') {
2814
3026
  let result = '';
@@ -2874,6 +3086,9 @@ class Deparser {
2874
3086
  output.push(node.name);
2875
3087
  }
2876
3088
  const windowParts = [];
3089
+ if (node.refname) {
3090
+ windowParts.push(node.refname);
3091
+ }
2877
3092
  if (node.partitionClause) {
2878
3093
  const partitions = list_utils_1.ListUtils.unwrapList(node.partitionClause);
2879
3094
  const partitionStrs = partitions.map(p => this.visit(p, context));
@@ -2884,12 +3099,9 @@ class Deparser {
2884
3099
  const orderStrs = orders.map(o => this.visit(o, context));
2885
3100
  windowParts.push(`ORDER BY ${orderStrs.join(', ')}`);
2886
3101
  }
2887
- // Only add frame clause if frameOptions indicates non-default framing
2888
- if (node.frameOptions && node.frameOptions !== 1058) {
2889
- const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
2890
- if (frameClause) {
2891
- windowParts.push(frameClause);
2892
- }
3102
+ const frameClause = this.formatWindowFrame(node, context.spawn('WindowDef'));
3103
+ if (frameClause) {
3104
+ windowParts.push(frameClause);
2893
3105
  }
2894
3106
  if (windowParts.length > 0) {
2895
3107
  if (node.name) {
@@ -2906,103 +3118,97 @@ class Deparser {
2906
3118
  }
2907
3119
  return output.join(' ');
2908
3120
  }
3121
+ formatIdentityColumnOptions(def, context) {
3122
+ const seqContext = context.spawn('AlterSeqStmt');
3123
+ return list_utils_1.ListUtils.unwrapList(def).map(item => {
3124
+ const el = item.DefElem;
3125
+ if (!el)
3126
+ return this.visit(item, context);
3127
+ if (el.defname === 'generated') {
3128
+ const ival = el.arg?.Integer?.ival ?? 0;
3129
+ return ival === 97 ? 'SET GENERATED ALWAYS' : 'SET GENERATED BY DEFAULT';
3130
+ }
3131
+ if (el.defname === 'restart') {
3132
+ return el.arg ? `RESTART WITH ${this.visit(el.arg, context)}` : 'RESTART';
3133
+ }
3134
+ return `SET ${this.visit(item, seqContext)}`;
3135
+ }).join(' ');
3136
+ }
3137
+ // b_expr (used by DEFAULT) excludes IN/LIKE/BETWEEN and boolean operators.
3138
+ needsBExprParens(node) {
3139
+ const nodeType = this.getNodeType(node);
3140
+ if (nodeType === 'BoolExpr')
3141
+ return true;
3142
+ if (nodeType === 'A_Expr') {
3143
+ const kind = this.getNodeData(node).kind;
3144
+ return kind !== 'AEXPR_OP' && kind !== 'AEXPR_DISTINCT' && kind !== 'AEXPR_NOT_DISTINCT';
3145
+ }
3146
+ return false;
3147
+ }
2909
3148
  formatWindowFrame(node, context) {
2910
- if (!node.frameOptions)
3149
+ if (!node.frameOptions || !(node.frameOptions & FRAMEOPTION_NONDEFAULT))
2911
3150
  return null;
2912
3151
  const frameOptions = node.frameOptions;
2913
3152
  const frameParts = [];
2914
- if (frameOptions & 0x01) { // FRAMEOPTION_NONDEFAULT
2915
- if (frameOptions & 0x02) { // FRAMEOPTION_RANGE
2916
- frameParts.push('RANGE');
2917
- }
2918
- else if (frameOptions & 0x04) { // FRAMEOPTION_ROWS
2919
- frameParts.push('ROWS');
2920
- }
2921
- else if (frameOptions & 0x08) { // FRAMEOPTION_GROUPS
2922
- frameParts.push('GROUPS');
2923
- }
3153
+ if (frameOptions & FRAMEOPTION_RANGE) {
3154
+ frameParts.push('RANGE');
3155
+ }
3156
+ else if (frameOptions & FRAMEOPTION_ROWS) {
3157
+ frameParts.push('ROWS');
3158
+ }
3159
+ else if (frameOptions & FRAMEOPTION_GROUPS) {
3160
+ frameParts.push('GROUPS');
2924
3161
  }
2925
3162
  if (frameParts.length === 0)
2926
3163
  return null;
2927
- const boundsParts = [];
2928
- // Handle specific frameOptions values that have known mappings
2929
- if (frameOptions === 789) {
2930
- boundsParts.push('CURRENT ROW');
2931
- boundsParts.push('AND UNBOUNDED FOLLOWING');
3164
+ let start = null;
3165
+ if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING) {
3166
+ start = 'UNBOUNDED PRECEDING';
2932
3167
  }
2933
- else if (frameOptions === 1077) {
2934
- boundsParts.push('UNBOUNDED PRECEDING');
2935
- boundsParts.push('AND CURRENT ROW');
3168
+ else if (frameOptions & FRAMEOPTION_START_UNBOUNDED_FOLLOWING) {
3169
+ start = 'UNBOUNDED FOLLOWING';
2936
3170
  }
2937
- else if (frameOptions === 18453) {
2938
- if (node.startOffset && node.endOffset) {
2939
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
2940
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2941
- }
3171
+ else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW) {
3172
+ start = 'CURRENT ROW';
2942
3173
  }
2943
- else if (frameOptions === 1557) {
2944
- boundsParts.push('CURRENT ROW');
2945
- boundsParts.push('AND CURRENT ROW');
3174
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING && node.startOffset) {
3175
+ start = `${this.visit(node.startOffset, context)} PRECEDING`;
2946
3176
  }
2947
- else if (frameOptions === 16917) {
2948
- boundsParts.push('CURRENT ROW');
2949
- if (node.endOffset) {
2950
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2951
- }
3177
+ else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING && node.startOffset) {
3178
+ start = `${this.visit(node.startOffset, context)} FOLLOWING`;
2952
3179
  }
2953
- else if (frameOptions === 1058) {
2954
- return null;
3180
+ let end = null;
3181
+ if (frameOptions & FRAMEOPTION_END_UNBOUNDED_PRECEDING) {
3182
+ end = 'UNBOUNDED PRECEDING';
2955
3183
  }
2956
- else {
2957
- // Handle start bound - prioritize explicit offset values over bit flags
2958
- if (node.startOffset) {
2959
- if (frameOptions & 0x400) { // FRAMEOPTION_START_VALUE_PRECEDING
2960
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
2961
- }
2962
- else if (frameOptions & 0x800) { // FRAMEOPTION_START_VALUE_FOLLOWING
2963
- boundsParts.push(`${this.visit(node.startOffset, context)} FOLLOWING`);
2964
- }
2965
- else {
2966
- boundsParts.push(`${this.visit(node.startOffset, context)} PRECEDING`);
2967
- }
2968
- }
2969
- else if (frameOptions & 0x10) { // FRAMEOPTION_START_UNBOUNDED_PRECEDING
2970
- boundsParts.push('UNBOUNDED PRECEDING');
2971
- }
2972
- else if (frameOptions & 0x20) { // FRAMEOPTION_START_CURRENT_ROW
2973
- boundsParts.push('CURRENT ROW');
2974
- }
2975
- // Handle end bound - prioritize explicit offset values over bit flags
2976
- if (node.endOffset) {
2977
- if (boundsParts.length > 0) {
2978
- if (frameOptions & 0x1000) { // FRAMEOPTION_END_VALUE_PRECEDING
2979
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} PRECEDING`);
2980
- }
2981
- else if (frameOptions & 0x2000) { // FRAMEOPTION_END_VALUE_FOLLOWING
2982
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2983
- }
2984
- else {
2985
- boundsParts.push(`AND ${this.visit(node.endOffset, context)} FOLLOWING`);
2986
- }
2987
- }
2988
- }
2989
- else if (frameOptions & 0x80) { // FRAMEOPTION_END_UNBOUNDED_FOLLOWING
2990
- if (boundsParts.length > 0) {
2991
- boundsParts.push('AND UNBOUNDED FOLLOWING');
2992
- }
2993
- }
2994
- else if (frameOptions & 0x100) { // FRAMEOPTION_END_CURRENT_ROW
2995
- if (boundsParts.length > 0) {
2996
- boundsParts.push('AND CURRENT ROW');
2997
- }
3184
+ else if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING) {
3185
+ end = 'UNBOUNDED FOLLOWING';
3186
+ }
3187
+ else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW) {
3188
+ end = 'CURRENT ROW';
3189
+ }
3190
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING && node.endOffset) {
3191
+ end = `${this.visit(node.endOffset, context)} PRECEDING`;
3192
+ }
3193
+ else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING && node.endOffset) {
3194
+ end = `${this.visit(node.endOffset, context)} FOLLOWING`;
3195
+ }
3196
+ if (start) {
3197
+ if (frameOptions & FRAMEOPTION_BETWEEN) {
3198
+ frameParts.push(`BETWEEN ${start} AND ${end ?? 'CURRENT ROW'}`);
2998
3199
  }
2999
- else if (boundsParts.length > 0) {
3000
- boundsParts.push('AND CURRENT ROW');
3200
+ else {
3201
+ frameParts.push(start);
3001
3202
  }
3002
3203
  }
3003
- if (boundsParts.length > 0) {
3004
- frameParts.push('BETWEEN');
3005
- frameParts.push(boundsParts.join(' '));
3204
+ if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW) {
3205
+ frameParts.push('EXCLUDE CURRENT ROW');
3206
+ }
3207
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP) {
3208
+ frameParts.push('EXCLUDE GROUP');
3209
+ }
3210
+ else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES) {
3211
+ frameParts.push('EXCLUDE TIES');
3006
3212
  }
3007
3213
  return frameParts.join(' ');
3008
3214
  }
@@ -3080,8 +3286,35 @@ class Deparser {
3080
3286
  if (node.ctequery) {
3081
3287
  output.push(context.parens(this.visit(node.ctequery, context)));
3082
3288
  }
3289
+ if (node.search_clause) {
3290
+ const sc = node.search_clause;
3291
+ const cols = list_utils_1.ListUtils.unwrapList(sc.search_col_list).map(c => this.visit(c, context));
3292
+ output.push('SEARCH', sc.search_breadth_first ? 'BREADTH' : 'DEPTH', 'FIRST BY');
3293
+ output.push(cols.join(', '));
3294
+ output.push('SET', quotes_1.QuoteUtils.quoteIdentifier(sc.search_seq_column));
3295
+ }
3296
+ if (node.cycle_clause) {
3297
+ const cc = node.cycle_clause;
3298
+ const cols = list_utils_1.ListUtils.unwrapList(cc.cycle_col_list).map(c => this.visit(c, context));
3299
+ output.push('CYCLE', cols.join(', '));
3300
+ output.push('SET', quotes_1.QuoteUtils.quoteIdentifier(cc.cycle_mark_column));
3301
+ if (cc.cycle_mark_value && cc.cycle_mark_default) {
3302
+ output.push('TO', this.formatAexprConst(cc.cycle_mark_value, context));
3303
+ output.push('DEFAULT', this.formatAexprConst(cc.cycle_mark_default, context));
3304
+ }
3305
+ output.push('USING', quotes_1.QuoteUtils.quoteIdentifier(cc.cycle_path_column));
3306
+ }
3083
3307
  return output.join(' ');
3084
3308
  }
3309
+ // Renders a constant in the restricted AexprConst grammar (e.g. `point '(1,1)'`
3310
+ // rather than `CAST('(1,1)' AS point)`).
3311
+ formatAexprConst(node, context) {
3312
+ const cast = node.TypeCast;
3313
+ if (cast?.arg?.A_Const?.sval && cast.typeName) {
3314
+ return `${this.TypeName(cast.typeName, context)} ${this.visit(cast.arg, context)}`;
3315
+ }
3316
+ return this.visit(node, context);
3317
+ }
3085
3318
  ParamRef(node, context) {
3086
3319
  return `$${node.number}`;
3087
3320
  }
@@ -3331,7 +3564,7 @@ class Deparser {
3331
3564
  output.push('IF NOT EXISTS');
3332
3565
  }
3333
3566
  if (node.idxname) {
3334
- output.push(quote_utils_1.QuoteUtils.quote(node.idxname));
3567
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.idxname));
3335
3568
  }
3336
3569
  output.push('ON');
3337
3570
  if (node.relation) {
@@ -3350,9 +3583,8 @@ class Deparser {
3350
3583
  output.push('INCLUDE');
3351
3584
  output.push(context.parens(includeStrs.join(', ')));
3352
3585
  }
3353
- if (node.whereClause) {
3354
- output.push('WHERE');
3355
- output.push(this.visit(node.whereClause, context));
3586
+ if (node.nulls_not_distinct) {
3587
+ output.push('NULLS NOT DISTINCT');
3356
3588
  }
3357
3589
  if (node.options && node.options.length > 0) {
3358
3590
  const indexContext = context.spawn('IndexStmt');
@@ -3360,19 +3592,20 @@ class Deparser {
3360
3592
  output.push('WITH');
3361
3593
  output.push(context.parens(optionStrs.join(', ')));
3362
3594
  }
3363
- if (node.nulls_not_distinct) {
3364
- output.push('NULLS NOT DISTINCT');
3365
- }
3366
3595
  if (node.tableSpace) {
3367
3596
  output.push('TABLESPACE');
3368
- output.push(quote_utils_1.QuoteUtils.quote(node.tableSpace));
3597
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.tableSpace));
3369
3598
  }
3370
- return output.join(' ');
3371
- }
3599
+ if (node.whereClause) {
3600
+ output.push('WHERE');
3601
+ output.push(this.visit(node.whereClause, context));
3602
+ }
3603
+ return output.join(' ');
3604
+ }
3372
3605
  IndexElem(node, context) {
3373
3606
  const output = [];
3374
3607
  if (node.name) {
3375
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
3608
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
3376
3609
  }
3377
3610
  else if (node.expr) {
3378
3611
  output.push(context.parens(this.visit(node.expr, context)));
@@ -3390,7 +3623,7 @@ class Deparser {
3390
3623
  const opclassOpts = list_utils_1.ListUtils.unwrapList(node.opclassopts).map(opt => {
3391
3624
  if (opt.DefElem && opt.DefElem.arg && this.getNodeType(opt.DefElem.arg) === 'String') {
3392
3625
  const stringData = this.getNodeData(opt.DefElem.arg);
3393
- return `${opt.DefElem.defname}='${stringData.sval}'`;
3626
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(opt.DefElem.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
3394
3627
  }
3395
3628
  return this.visit(opt, context.spawn('IndexElem'));
3396
3629
  });
@@ -3423,7 +3656,7 @@ class Deparser {
3423
3656
  PartitionElem(node, context) {
3424
3657
  const output = [];
3425
3658
  if (node.name) {
3426
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
3659
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
3427
3660
  }
3428
3661
  else if (node.expr) {
3429
3662
  output.push(context.parens(this.visit(node.expr, context)));
@@ -3441,12 +3674,12 @@ class Deparser {
3441
3674
  }
3442
3675
  PartitionCmd(node, context) {
3443
3676
  const output = [];
3677
+ if (node.name) {
3678
+ output.push(this.RangeVar(node.name, context));
3679
+ }
3444
3680
  if (node.concurrent) {
3445
3681
  output.push('CONCURRENTLY');
3446
3682
  }
3447
- if (node.name) {
3448
- output.push(this.visit(node.name, context));
3449
- }
3450
3683
  if (node.bound) {
3451
3684
  if (node.bound.strategy === 'l' && node.bound.listdatums) {
3452
3685
  output.push('FOR VALUES IN');
@@ -3471,9 +3704,9 @@ class Deparser {
3471
3704
  output.push(`(${upperValues})`);
3472
3705
  }
3473
3706
  }
3474
- else if (node.bound.strategy === 'h' && node.bound.modulus !== undefined && node.bound.remainder !== undefined) {
3707
+ else if (node.bound.strategy === 'h') {
3475
3708
  output.push('FOR VALUES WITH');
3476
- output.push(`(modulus ${node.bound.modulus}, remainder ${node.bound.remainder})`);
3709
+ output.push(`(MODULUS ${node.bound.modulus ?? 0}, REMAINDER ${node.bound.remainder ?? 0})`);
3477
3710
  }
3478
3711
  else if (node.bound.is_default) {
3479
3712
  output.push('DEFAULT');
@@ -3604,12 +3837,7 @@ class Deparser {
3604
3837
  if (context.isPretty()) {
3605
3838
  // For complex JOIN conditions, format with proper indentation
3606
3839
  if (qualsStr.includes('AND') || qualsStr.includes('OR') || qualsStr.length > 50) {
3607
- if (this.containsMultilineStringLiteral(qualsStr)) {
3608
- output.push(` ON ${qualsStr}`);
3609
- }
3610
- else {
3611
- output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
3612
- }
3840
+ output.push(` ON${context.newline()}${context.indent(qualsStr)}`);
3613
3841
  }
3614
3842
  else {
3615
3843
  output.push(` ON ${qualsStr}`);
@@ -3673,37 +3901,37 @@ class Deparser {
3673
3901
  case 'TRANS_STMT_SAVEPOINT':
3674
3902
  output.push('SAVEPOINT');
3675
3903
  if (node.savepoint_name) {
3676
- output.push(quote_utils_1.QuoteUtils.quote(node.savepoint_name));
3904
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.savepoint_name));
3677
3905
  }
3678
3906
  break;
3679
3907
  case 'TRANS_STMT_RELEASE':
3680
3908
  output.push('RELEASE SAVEPOINT');
3681
3909
  if (node.savepoint_name) {
3682
- output.push(quote_utils_1.QuoteUtils.quote(node.savepoint_name));
3910
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.savepoint_name));
3683
3911
  }
3684
3912
  break;
3685
3913
  case 'TRANS_STMT_ROLLBACK_TO':
3686
3914
  output.push('ROLLBACK TO');
3687
3915
  if (node.savepoint_name) {
3688
- output.push(quote_utils_1.QuoteUtils.quote(node.savepoint_name));
3916
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.savepoint_name));
3689
3917
  }
3690
3918
  break;
3691
3919
  case 'TRANS_STMT_PREPARE':
3692
3920
  output.push('PREPARE TRANSACTION');
3693
3921
  if (node.gid) {
3694
- output.push(`'${node.gid}'`);
3922
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3695
3923
  }
3696
3924
  break;
3697
3925
  case 'TRANS_STMT_COMMIT_PREPARED':
3698
3926
  output.push('COMMIT PREPARED');
3699
3927
  if (node.gid) {
3700
- output.push(`'${node.gid}'`);
3928
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3701
3929
  }
3702
3930
  break;
3703
3931
  case 'TRANS_STMT_ROLLBACK_PREPARED':
3704
3932
  output.push('ROLLBACK PREPARED');
3705
3933
  if (node.gid) {
3706
- output.push(`'${node.gid}'`);
3934
+ output.push(quotes_1.QuoteUtils.escape(node.gid));
3707
3935
  }
3708
3936
  break;
3709
3937
  default:
@@ -3770,28 +3998,39 @@ class Deparser {
3770
3998
  switch (node.kind) {
3771
3999
  case 'VAR_SET_VALUE':
3772
4000
  const localPrefix = node.is_local ? 'LOCAL ' : '';
4001
+ if (node.jumble_args && node.name === 'xmloption' && node.args) {
4002
+ const xmlValue = this.getNodeData(list_utils_1.ListUtils.unwrapList(node.args)[0]);
4003
+ const xmlOption = (typeof xmlValue.sval === 'object' ? xmlValue.sval.sval : xmlValue.sval) || '';
4004
+ return `SET ${localPrefix}XML OPTION ${xmlOption.toUpperCase()}`;
4005
+ }
3773
4006
  const args = node.args ? list_utils_1.ListUtils.unwrapList(node.args).map(arg => {
3774
4007
  const nodeData = this.getNodeData(arg);
3775
4008
  if (nodeData.sval !== undefined) {
3776
4009
  const svalValue = typeof nodeData.sval === 'object' ? nodeData.sval.sval : nodeData.sval;
3777
- if (svalValue === '' || svalValue.includes(' ') || svalValue.includes('-') || /[A-Z]/.test(svalValue) || /^\d/.test(svalValue) || svalValue.includes('.') || svalValue.includes('$') || svalValue.toLowerCase() === 'all' || /^[+-]\d/.test(svalValue)) {
3778
- return `'${svalValue}'`;
4010
+ if (!/^[a-z_][a-z0-9_]*$/.test(svalValue) || svalValue.includes(' ') || svalValue.includes('-') || /[A-Z]/.test(svalValue) || /^\d/.test(svalValue) || svalValue.includes('.') || svalValue.includes('$') || svalValue.toLowerCase() === 'all' || /^[+-]\d/.test(svalValue)) {
4011
+ return quotes_1.QuoteUtils.escape(svalValue);
3779
4012
  }
3780
4013
  return svalValue;
3781
4014
  }
3782
4015
  return this.visit(arg, context);
3783
4016
  }).join(', ') : '';
4017
+ if (node.jumble_args && node.name === 'timezone') {
4018
+ return `SET ${localPrefix}TIME ZONE ${args}`;
4019
+ }
3784
4020
  // Handle args - always include TO clause if args exist (even if empty string)
3785
- const paramName = node.name && (node.name.includes('.') || node.name.includes('-') || /[A-Z]/.test(node.name)) ? `"${node.name}"` : node.name;
4021
+ const paramName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
3786
4022
  if (!node.args || node.args.length === 0) {
3787
4023
  return `SET ${localPrefix}${paramName}`;
3788
4024
  }
3789
4025
  return `SET ${localPrefix}${paramName} TO ${args}`;
3790
4026
  case 'VAR_SET_DEFAULT':
3791
- const defaultParamName = node.name && (node.name.includes('.') || node.name.includes('-') || /[A-Z]/.test(node.name)) ? `"${node.name}"` : node.name;
4027
+ if (node.jumble_args && node.name === 'timezone') {
4028
+ return `SET ${node.is_local ? 'LOCAL ' : ''}TIME ZONE DEFAULT`;
4029
+ }
4030
+ const defaultParamName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
3792
4031
  return `SET ${defaultParamName} TO DEFAULT`;
3793
4032
  case 'VAR_SET_CURRENT':
3794
- const currentParamName = node.name && (node.name.includes('.') || node.name.includes('-') || /[A-Z]/.test(node.name)) ? `"${node.name}"` : node.name;
4033
+ const currentParamName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
3795
4034
  return `SET ${currentParamName} FROM CURRENT`;
3796
4035
  case 'VAR_SET_MULTI':
3797
4036
  if (node.name === 'TRANSACTION' || node.name === 'SESSION CHARACTERISTICS') {
@@ -3865,7 +4104,7 @@ class Deparser {
3865
4104
  return `SET ${assignments}`;
3866
4105
  }
3867
4106
  case 'VAR_RESET':
3868
- const resetParamName = node.name && (node.name.includes('.') || node.name.includes('-') || /[A-Z]/.test(node.name)) ? `"${node.name}"` : node.name;
4107
+ const resetParamName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
3869
4108
  return `RESET ${resetParamName}`;
3870
4109
  case 'VAR_RESET_ALL':
3871
4110
  return 'RESET ALL';
@@ -3885,7 +4124,7 @@ class Deparser {
3885
4124
  output.push('IF NOT EXISTS');
3886
4125
  }
3887
4126
  if (node.schemaname) {
3888
- output.push(node.schemaname);
4127
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.schemaname));
3889
4128
  }
3890
4129
  if (node.authrole) {
3891
4130
  output.push('AUTHORIZATION');
@@ -4092,7 +4331,7 @@ class Deparser {
4092
4331
  if (objList && objList.List && objList.List.items) {
4093
4332
  const items = objList.List.items.map((item) => {
4094
4333
  if (item.String && item.String.sval) {
4095
- return item.String.sval;
4334
+ return quotes_1.QuoteUtils.quoteIdentifier(item.String.sval);
4096
4335
  }
4097
4336
  return this.visit(item, context);
4098
4337
  });
@@ -4134,7 +4373,7 @@ class Deparser {
4134
4373
  if (objList && objList.List && objList.List.items) {
4135
4374
  const items = objList.List.items.map((item) => {
4136
4375
  if (item.String && item.String.sval) {
4137
- return quote_utils_1.QuoteUtils.quote(item.String.sval);
4376
+ return quotes_1.QuoteUtils.quoteIdentifier(item.String.sval);
4138
4377
  }
4139
4378
  return this.visit(item, context);
4140
4379
  }).filter((name) => name && name.trim());
@@ -4168,13 +4407,13 @@ class Deparser {
4168
4407
  if (items.length === 2) {
4169
4408
  const accessMethod = items[0];
4170
4409
  const objectName = items[1];
4171
- return `${quote_utils_1.QuoteUtils.quote(objectName)} USING ${accessMethod}`;
4410
+ return `${quotes_1.QuoteUtils.quoteIdentifier(objectName)} USING ${accessMethod}`;
4172
4411
  }
4173
4412
  else if (items.length === 3) {
4174
4413
  const accessMethod = items[0];
4175
4414
  const schemaName = items[1];
4176
4415
  const objectName = items[2];
4177
- return `${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(objectName)} USING ${accessMethod}`;
4416
+ return `${quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, objectName)} USING ${accessMethod}`;
4178
4417
  }
4179
4418
  return items.join('.');
4180
4419
  }
@@ -4217,7 +4456,7 @@ class Deparser {
4217
4456
  if (objList && objList.List && objList.List.items) {
4218
4457
  const items = objList.List.items.map((item) => {
4219
4458
  if (item.String && item.String.sval) {
4220
- return quote_utils_1.QuoteUtils.quote(item.String.sval);
4459
+ return quotes_1.QuoteUtils.quoteIdentifier(item.String.sval);
4221
4460
  }
4222
4461
  return this.visit(item, context);
4223
4462
  }).filter((name) => name && name.trim());
@@ -4265,7 +4504,7 @@ class Deparser {
4265
4504
  PLAssignStmt(node, context) {
4266
4505
  const output = [];
4267
4506
  if (node.name) {
4268
- let nameWithIndirection = quote_utils_1.QuoteUtils.quote(node.name);
4507
+ let nameWithIndirection = quotes_1.QuoteUtils.quoteIdentifier(node.name);
4269
4508
  if (node.indirection && node.indirection.length > 0) {
4270
4509
  const indirectionStr = node.indirection
4271
4510
  .map((ind) => this.visit(ind, context))
@@ -4315,10 +4554,10 @@ class Deparser {
4315
4554
  output.push('PROGRAM');
4316
4555
  }
4317
4556
  if (node.filename) {
4318
- output.push(`'${node.filename}'`);
4557
+ output.push(quotes_1.QuoteUtils.escape(node.filename));
4319
4558
  }
4320
4559
  else {
4321
- output.push('STDIN');
4560
+ output.push(node.is_from ? 'STDIN' : 'STDOUT');
4322
4561
  }
4323
4562
  if (node.options && node.options.length > 0) {
4324
4563
  output.push('WITH');
@@ -4414,7 +4653,7 @@ class Deparser {
4414
4653
  const parts = [];
4415
4654
  const indentedParts = [];
4416
4655
  if (colDefData.colname) {
4417
- parts.push(quote_utils_1.QuoteUtils.quote(colDefData.colname));
4656
+ parts.push(quotes_1.QuoteUtils.quoteIdentifier(colDefData.colname));
4418
4657
  }
4419
4658
  if (colDefData.typeName) {
4420
4659
  parts.push(this.TypeName(colDefData.typeName, context));
@@ -4472,7 +4711,7 @@ class Deparser {
4472
4711
  else {
4473
4712
  const parts = [];
4474
4713
  if (colDefData.colname) {
4475
- parts.push(quote_utils_1.QuoteUtils.quote(colDefData.colname));
4714
+ parts.push(quotes_1.QuoteUtils.quoteIdentifier(colDefData.colname));
4476
4715
  }
4477
4716
  if (colDefData.typeName) {
4478
4717
  parts.push(this.TypeName(colDefData.typeName, context));
@@ -4526,7 +4765,7 @@ class Deparser {
4526
4765
  }
4527
4766
  }
4528
4767
  if (node.name) {
4529
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4768
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4530
4769
  }
4531
4770
  if (node.behavior === 'DROP_CASCADE') {
4532
4771
  output.push('CASCADE');
@@ -4543,13 +4782,16 @@ class Deparser {
4543
4782
  output.push('ALTER COLUMN');
4544
4783
  }
4545
4784
  if (node.name) {
4546
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4785
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4547
4786
  }
4548
4787
  output.push('TYPE');
4549
4788
  if (node.def) {
4550
4789
  const nodeData = this.getNodeData(node.def);
4551
4790
  if (nodeData && nodeData.typeName) {
4552
4791
  output.push(this.TypeName(nodeData.typeName, context));
4792
+ if (nodeData.collClause) {
4793
+ output.push(this.CollateClause(nodeData.collClause, context));
4794
+ }
4553
4795
  // Handle USING clause (stored in raw_default for ALTER COLUMN TYPE)
4554
4796
  if (nodeData.raw_default) {
4555
4797
  output.push('USING');
@@ -4570,7 +4812,7 @@ class Deparser {
4570
4812
  case 'AT_SetTableSpace':
4571
4813
  output.push('SET TABLESPACE');
4572
4814
  if (node.name) {
4573
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4815
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4574
4816
  }
4575
4817
  break;
4576
4818
  case 'AT_AddConstraint':
@@ -4588,7 +4830,7 @@ class Deparser {
4588
4830
  output.push('DROP CONSTRAINT');
4589
4831
  }
4590
4832
  if (node.name) {
4591
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4833
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4592
4834
  }
4593
4835
  if (node.behavior === 'DROP_CASCADE') {
4594
4836
  output.push('CASCADE');
@@ -4626,7 +4868,7 @@ class Deparser {
4626
4868
  case 'AT_ColumnDefault':
4627
4869
  output.push('ALTER COLUMN');
4628
4870
  if (node.name) {
4629
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4871
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4630
4872
  }
4631
4873
  if (node.def) {
4632
4874
  output.push('SET DEFAULT');
@@ -4639,7 +4881,7 @@ class Deparser {
4639
4881
  case 'AT_SetStorage':
4640
4882
  output.push('ALTER COLUMN');
4641
4883
  if (node.name) {
4642
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4884
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4643
4885
  }
4644
4886
  output.push('SET STORAGE');
4645
4887
  if (node.def) {
@@ -4650,7 +4892,7 @@ class Deparser {
4650
4892
  case 'AT_ClusterOn':
4651
4893
  output.push('CLUSTER ON');
4652
4894
  if (node.name) {
4653
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4895
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4654
4896
  }
4655
4897
  break;
4656
4898
  case 'AT_DropCluster':
@@ -4677,21 +4919,21 @@ class Deparser {
4677
4919
  case 'AT_SetNotNull':
4678
4920
  output.push('ALTER COLUMN');
4679
4921
  if (node.name) {
4680
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4922
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4681
4923
  }
4682
4924
  output.push('SET NOT NULL');
4683
4925
  break;
4684
4926
  case 'AT_DropNotNull':
4685
4927
  output.push('ALTER COLUMN');
4686
4928
  if (node.name) {
4687
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4929
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4688
4930
  }
4689
4931
  output.push('DROP NOT NULL');
4690
4932
  break;
4691
4933
  case 'AT_SetStatistics':
4692
4934
  output.push('ALTER COLUMN');
4693
4935
  if (node.name) {
4694
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4936
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4695
4937
  }
4696
4938
  else if (node.num !== undefined && node.num !== null) {
4697
4939
  output.push(node.num.toString());
@@ -4704,7 +4946,7 @@ class Deparser {
4704
4946
  case 'AT_SetOptions':
4705
4947
  output.push('ALTER COLUMN');
4706
4948
  if (node.name) {
4707
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4949
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4708
4950
  }
4709
4951
  output.push('SET');
4710
4952
  if (node.def) {
@@ -4721,7 +4963,7 @@ class Deparser {
4721
4963
  case 'AT_ResetOptions':
4722
4964
  output.push('ALTER COLUMN');
4723
4965
  if (node.name) {
4724
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4966
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4725
4967
  }
4726
4968
  output.push('RESET');
4727
4969
  if (node.def) {
@@ -4738,7 +4980,7 @@ class Deparser {
4738
4980
  case 'AT_SetCompression':
4739
4981
  output.push('ALTER COLUMN');
4740
4982
  if (node.name) {
4741
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4983
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4742
4984
  }
4743
4985
  output.push('SET COMPRESSION');
4744
4986
  if (node.def) {
@@ -4748,31 +4990,31 @@ class Deparser {
4748
4990
  case 'AT_ValidateConstraint':
4749
4991
  output.push('VALIDATE CONSTRAINT');
4750
4992
  if (node.name) {
4751
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4993
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4752
4994
  }
4753
4995
  break;
4754
4996
  case 'AT_EnableTrig':
4755
4997
  output.push('ENABLE TRIGGER');
4756
4998
  if (node.name) {
4757
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4999
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4758
5000
  }
4759
5001
  break;
4760
5002
  case 'AT_EnableAlwaysTrig':
4761
5003
  output.push('ENABLE ALWAYS TRIGGER');
4762
5004
  if (node.name) {
4763
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5005
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4764
5006
  }
4765
5007
  break;
4766
5008
  case 'AT_EnableReplicaTrig':
4767
5009
  output.push('ENABLE REPLICA TRIGGER');
4768
5010
  if (node.name) {
4769
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5011
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4770
5012
  }
4771
5013
  break;
4772
5014
  case 'AT_DisableTrig':
4773
5015
  output.push('DISABLE TRIGGER');
4774
5016
  if (node.name) {
4775
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5017
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4776
5018
  }
4777
5019
  break;
4778
5020
  case 'AT_EnableTrigAll':
@@ -4790,31 +5032,31 @@ class Deparser {
4790
5032
  case 'AT_EnableRule':
4791
5033
  output.push('ENABLE RULE');
4792
5034
  if (node.name) {
4793
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5035
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4794
5036
  }
4795
5037
  break;
4796
5038
  case 'AT_EnableAlwaysRule':
4797
5039
  output.push('ENABLE ALWAYS RULE');
4798
5040
  if (node.name) {
4799
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5041
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4800
5042
  }
4801
5043
  break;
4802
5044
  case 'AT_EnableReplicaRule':
4803
5045
  output.push('ENABLE REPLICA RULE');
4804
5046
  if (node.name) {
4805
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5047
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4806
5048
  }
4807
5049
  break;
4808
5050
  case 'AT_DisableRule':
4809
5051
  output.push('DISABLE RULE');
4810
5052
  if (node.name) {
4811
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5053
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4812
5054
  }
4813
5055
  break;
4814
5056
  case 'AT_SetAccessMethod':
4815
5057
  output.push('SET ACCESS METHOD');
4816
5058
  if (node.name) {
4817
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5059
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4818
5060
  }
4819
5061
  else {
4820
5062
  // Handle DEFAULT access method case
@@ -4868,7 +5110,7 @@ class Deparser {
4868
5110
  case 'AT_CookedColumnDefault':
4869
5111
  output.push('ALTER COLUMN');
4870
5112
  if (node.name) {
4871
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5113
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4872
5114
  }
4873
5115
  if (node.def) {
4874
5116
  output.push('SET DEFAULT');
@@ -4881,26 +5123,23 @@ class Deparser {
4881
5123
  case 'AT_SetExpression':
4882
5124
  output.push('ALTER COLUMN');
4883
5125
  if (node.name) {
4884
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5126
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4885
5127
  }
4886
5128
  output.push('SET EXPRESSION');
4887
5129
  if (node.def) {
4888
- output.push(this.visit(node.def, context));
5130
+ output.push('AS');
5131
+ output.push(context.parens(this.visit(node.def, context)));
4889
5132
  }
4890
5133
  break;
4891
5134
  case 'AT_DropExpression':
4892
5135
  output.push('ALTER COLUMN');
4893
5136
  if (node.name) {
4894
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5137
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4895
5138
  }
4896
5139
  output.push('DROP EXPRESSION');
4897
- break;
4898
- case 'AT_CheckNotNull':
4899
- output.push('ALTER COLUMN');
4900
- if (node.name) {
4901
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5140
+ if (node.missing_ok) {
5141
+ output.push('IF EXISTS');
4902
5142
  }
4903
- output.push('SET NOT NULL');
4904
5143
  break;
4905
5144
  case 'AT_AddIndex':
4906
5145
  output.push('ADD');
@@ -4927,23 +5166,28 @@ class Deparser {
4927
5166
  }
4928
5167
  break;
4929
5168
  case 'AT_AlterConstraint':
4930
- output.push('ALTER CONSTRAINT');
4931
- if (node.def && this.getNodeType(node.def) === 'Constraint') {
4932
- const constraintData = this.getNodeData(node.def);
4933
- if (constraintData.conname) {
4934
- output.push(quote_utils_1.QuoteUtils.quote(constraintData.conname));
4935
- if (constraintData.deferrable !== undefined) {
4936
- output.push(constraintData.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
4937
- }
4938
- if (constraintData.initdeferred !== undefined) {
4939
- output.push(constraintData.initdeferred ? 'INITIALLY DEFERRED' : 'INITIALLY IMMEDIATE');
5169
+ if (node.def && this.getNodeType(node.def) === 'ATAlterConstraint') {
5170
+ output.push(this.ATAlterConstraint(this.getNodeData(node.def), context));
5171
+ }
5172
+ else {
5173
+ output.push('ALTER CONSTRAINT');
5174
+ if (node.def && this.getNodeType(node.def) === 'Constraint') {
5175
+ const constraintData = this.getNodeData(node.def);
5176
+ if (constraintData.conname) {
5177
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(constraintData.conname));
5178
+ if (constraintData.deferrable !== undefined) {
5179
+ output.push(constraintData.deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE');
5180
+ }
5181
+ if (constraintData.initdeferred !== undefined) {
5182
+ output.push(constraintData.initdeferred ? 'INITIALLY DEFERRED' : 'INITIALLY IMMEDIATE');
5183
+ }
4940
5184
  }
4941
5185
  }
4942
- }
4943
- else if (node.name) {
4944
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
4945
- if (node.def) {
4946
- output.push(this.visit(node.def, context));
5186
+ else if (node.name) {
5187
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5188
+ if (node.def) {
5189
+ output.push(this.visit(node.def, context));
5190
+ }
4947
5191
  }
4948
5192
  }
4949
5193
  break;
@@ -4962,7 +5206,7 @@ class Deparser {
4962
5206
  case 'AT_AlterColumnGenericOptions':
4963
5207
  output.push('ALTER COLUMN');
4964
5208
  if (node.name) {
4965
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5209
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
4966
5210
  }
4967
5211
  output.push('OPTIONS');
4968
5212
  if (node.def) {
@@ -5016,7 +5260,7 @@ class Deparser {
5016
5260
  case 'AT_AddIdentity':
5017
5261
  output.push('ALTER COLUMN');
5018
5262
  if (node.name) {
5019
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5263
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5020
5264
  }
5021
5265
  output.push('ADD');
5022
5266
  if (node.def) {
@@ -5026,24 +5270,20 @@ class Deparser {
5026
5270
  case 'AT_SetIdentity':
5027
5271
  output.push('ALTER COLUMN');
5028
5272
  if (node.name) {
5029
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5273
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5030
5274
  }
5031
- output.push('SET');
5032
5275
  if (node.def) {
5033
- output.push(this.visit(node.def, context));
5276
+ output.push(this.formatIdentityColumnOptions(node.def, context));
5034
5277
  }
5035
5278
  break;
5036
5279
  case 'AT_DropIdentity':
5037
5280
  output.push('ALTER COLUMN');
5038
5281
  if (node.name) {
5039
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5282
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5040
5283
  }
5041
5284
  output.push('DROP IDENTITY');
5042
- if (node.behavior === 'DROP_CASCADE') {
5043
- output.push('CASCADE');
5044
- }
5045
- else if (node.behavior === 'DROP_RESTRICT') {
5046
- output.push('RESTRICT');
5285
+ if (node.missing_ok) {
5286
+ output.push('IF EXISTS');
5047
5287
  }
5048
5288
  break;
5049
5289
  case 'AT_ReAddStatistics':
@@ -5070,7 +5310,8 @@ class Deparser {
5070
5310
  output.push('FUNCTION');
5071
5311
  }
5072
5312
  if (node.funcname && node.funcname.length > 0) {
5073
- const funcName = node.funcname.map((name) => this.visit(name, context)).join('.');
5313
+ const funcnameParts = node.funcname.map((name) => name.String?.sval || name.String?.str || '').filter((s) => s);
5314
+ const funcName = quotes_1.QuoteUtils.quoteDottedName(funcnameParts);
5074
5315
  if (node.parameters && node.parameters.length > 0) {
5075
5316
  const params = node.parameters
5076
5317
  .filter((param) => {
@@ -5079,7 +5320,13 @@ class Deparser {
5079
5320
  })
5080
5321
  .map((param) => this.visit(param, context));
5081
5322
  if (params.length > 0) {
5082
- output.push(funcName + '(' + params.join(', ') + ')');
5323
+ if (context.isPretty()) {
5324
+ const formattedParams = params.map(p => context.indent(p)).join(',' + context.newline());
5325
+ output.push(funcName + '(' + context.newline() + formattedParams + context.newline() + ')');
5326
+ }
5327
+ else {
5328
+ output.push(funcName + '(' + params.join(', ') + ')');
5329
+ }
5083
5330
  }
5084
5331
  else {
5085
5332
  output.push(funcName + '()');
@@ -5094,15 +5341,21 @@ class Deparser {
5094
5341
  return paramData.mode === 'FUNC_PARAM_TABLE';
5095
5342
  });
5096
5343
  if (hasTableParams) {
5097
- output.push('RETURNS TABLE (');
5098
5344
  const tableParams = node.parameters
5099
5345
  .filter((param) => {
5100
5346
  const paramData = this.getNodeData(param);
5101
5347
  return paramData.mode === 'FUNC_PARAM_TABLE';
5102
5348
  })
5103
5349
  .map((param) => this.visit(param, context));
5104
- output.push(tableParams.join(', '));
5105
- output.push(')');
5350
+ if (context.isPretty()) {
5351
+ const formattedTableParams = tableParams.map(p => context.indent(p)).join(',' + context.newline());
5352
+ output.push('RETURNS TABLE (' + context.newline() + formattedTableParams + context.newline() + ')');
5353
+ }
5354
+ else {
5355
+ output.push('RETURNS TABLE (');
5356
+ output.push(tableParams.join(', '));
5357
+ output.push(')');
5358
+ }
5106
5359
  }
5107
5360
  else if (node.returnType) {
5108
5361
  output.push('RETURNS');
@@ -5124,6 +5377,7 @@ class Deparser {
5124
5377
  if (bodyType === 'List') {
5125
5378
  const statements = list_utils_1.ListUtils.unwrapList(node.sql_body);
5126
5379
  if (statements.length === 0 || (statements.length === 1 && Object.keys(statements[0]).length === 0)) {
5380
+ // empty body: nothing to emit
5127
5381
  }
5128
5382
  else {
5129
5383
  // Handle nested List structure in BEGIN ATOMIC blocks
@@ -5175,7 +5429,7 @@ class Deparser {
5175
5429
  }
5176
5430
  }
5177
5431
  if (node.name) {
5178
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
5432
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5179
5433
  }
5180
5434
  if (node.argType) {
5181
5435
  output.push(this.TypeName(node.argType, context));
@@ -5247,7 +5501,7 @@ class Deparser {
5247
5501
  output.push('ROLE');
5248
5502
  }
5249
5503
  if (node.role) {
5250
- const roleName = Deparser.needsQuotes(node.role) ? `"${node.role}"` : node.role;
5504
+ const roleName = quotes_1.QuoteUtils.quoteIdentifier(node.role);
5251
5505
  output.push(roleName);
5252
5506
  }
5253
5507
  if (node.options) {
@@ -5271,25 +5525,28 @@ class Deparser {
5271
5525
  if (node.arg && this.getNodeType(node.arg) === 'List') {
5272
5526
  const listData = this.getNodeData(node.arg);
5273
5527
  const listItems = list_utils_1.ListUtils.unwrapList(listData.items);
5528
+ const preservedName = this.preserveOperatorDefElemCase(node.defname);
5274
5529
  if (listItems.length === 1 && listItems[0].String) {
5275
- const preservedName = this.preserveOperatorDefElemCase(node.defname);
5276
5530
  return `${preservedName} = ${listItems[0].String.sval}`;
5277
5531
  }
5532
+ if (listItems.length > 1 && listItems.every((item) => item.String)) {
5533
+ return `${preservedName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
5534
+ }
5278
5535
  }
5279
5536
  }
5280
5537
  // Handle IndexElem opclassopts - MUST be first to preserve string types
5281
5538
  if (context.parentNodeTypes.includes('IndexElem')) {
5282
5539
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5283
5540
  const stringData = this.getNodeData(node.arg);
5284
- return `${node.defname}='${stringData.sval}'`;
5541
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5285
5542
  }
5286
- return `${node.defname}=${this.visit(node.arg, context.spawn('DefElem'))}`;
5543
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, this.visit(node.arg, context.spawn('DefElem')))}`;
5287
5544
  }
5288
5545
  // Handle CREATE OPERATOR boolean flags - MUST be first to preserve case
5289
5546
  if (context.parentNodeTypes.includes('DefineStmt') &&
5290
5547
  ['hashes', 'merges'].includes(node.defname.toLowerCase()) && !node.arg) {
5291
5548
  if (node.defname !== node.defname.toLowerCase() && node.defname !== node.defname.toUpperCase()) {
5292
- return `"${node.defname}"`;
5549
+ return quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5293
5550
  }
5294
5551
  return node.defname.charAt(0).toUpperCase() + node.defname.slice(1).toLowerCase();
5295
5552
  }
@@ -5308,12 +5565,8 @@ class Deparser {
5308
5565
  const defElemContext = context.spawn('DefElem');
5309
5566
  const argValue = this.visit(node.arg, defElemContext);
5310
5567
  if (context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
5311
- const finalValue = typeof argValue === 'string' && !argValue.startsWith("'")
5312
- ? `'${argValue}'`
5313
- : argValue;
5314
- const quotedDefname = node.defname.includes(' ') || node.defname.includes('-') || Deparser.needsQuotes(node.defname)
5315
- ? `"${node.defname}"`
5316
- : node.defname;
5568
+ const finalValue = this.quoteDefElemArg(node.arg, argValue);
5569
+ const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5317
5570
  if (node.defaction === 'DEFELEM_ADD') {
5318
5571
  return `ADD ${quotedDefname} ${finalValue}`;
5319
5572
  }
@@ -5325,9 +5578,7 @@ class Deparser {
5325
5578
  }
5326
5579
  return `${quotedDefname} ${finalValue}`;
5327
5580
  }
5328
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5329
- ? `'${argValue}'`
5330
- : argValue;
5581
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5331
5582
  if (node.defaction === 'DEFELEM_ADD') {
5332
5583
  return `ADD ${node.defname} ${quotedValue}`;
5333
5584
  }
@@ -5337,9 +5588,7 @@ class Deparser {
5337
5588
  else if (node.defaction === 'DEFELEM_SET') {
5338
5589
  return `SET ${node.defname} ${quotedValue}`;
5339
5590
  }
5340
- const quotedDefname = node.defname.includes(' ') || node.defname.includes('-')
5341
- ? `"${node.defname}"`
5342
- : node.defname;
5591
+ const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5343
5592
  return `${quotedDefname} ${quotedValue}`;
5344
5593
  }
5345
5594
  else if (node.defaction === 'DEFELEM_DROP') {
@@ -5361,9 +5610,7 @@ class Deparser {
5361
5610
  }
5362
5611
  const defElemContext = context.spawn('DefElem');
5363
5612
  const argValue = this.visit(node.arg, defElemContext);
5364
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5365
- ? `'${argValue}'`
5366
- : argValue;
5613
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5367
5614
  return `PASSWORD ${quotedValue}`;
5368
5615
  }
5369
5616
  }
@@ -5386,22 +5633,23 @@ class Deparser {
5386
5633
  if (listItems.length === 1 && listItems[0].String) {
5387
5634
  return `${node.defname} = ${listItems[0].String.sval}`;
5388
5635
  }
5636
+ if (listItems.length > 1 && listItems.every((item) => item.String)) {
5637
+ return `${node.defname} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
5638
+ }
5389
5639
  }
5390
5640
  }
5391
5641
  if (context.parentNodeTypes.includes('CreatedbStmt') || context.parentNodeTypes.includes('DropdbStmt')) {
5392
5642
  const quotedValue = typeof argValue === 'string'
5393
- ? quote_utils_1.QuoteUtils.escape(argValue)
5643
+ ? quotes_1.QuoteUtils.escape(argValue)
5394
5644
  : argValue;
5395
5645
  return `${node.defname} = ${quotedValue}`;
5396
5646
  }
5397
5647
  // CreateForeignServerStmt and AlterForeignServerStmt use space format like CreateFdwStmt
5398
5648
  if (context.parentNodeTypes.includes('CreateForeignServerStmt') || context.parentNodeTypes.includes('AlterForeignServerStmt')) {
5399
5649
  const quotedValue = typeof argValue === 'string'
5400
- ? quote_utils_1.QuoteUtils.escape(argValue)
5650
+ ? quotes_1.QuoteUtils.escape(argValue)
5401
5651
  : argValue;
5402
- const quotedDefname = node.defname.includes(' ') || node.defname.includes('-')
5403
- ? `"${node.defname}"`
5404
- : node.defname;
5652
+ const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5405
5653
  return `${quotedDefname} ${quotedValue}`;
5406
5654
  }
5407
5655
  if (context.parentNodeTypes.includes('CreateRoleStmt') || context.parentNodeTypes.includes('AlterRoleStmt')) {
@@ -5430,9 +5678,7 @@ class Deparser {
5430
5678
  }
5431
5679
  }
5432
5680
  if (node.defname === 'validUntil') {
5433
- const quotedValue = typeof argValue === 'string' && !argValue.startsWith("'")
5434
- ? `'${argValue}'`
5435
- : argValue;
5681
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5436
5682
  return `VALID UNTIL ${quotedValue}`;
5437
5683
  }
5438
5684
  if (node.defname === 'adminmembers') {
@@ -5471,15 +5717,15 @@ class Deparser {
5471
5717
  if (node.arg && this.getNodeType(node.arg) === 'List') {
5472
5718
  const listData = this.getNodeData(node.arg);
5473
5719
  const listItems = list_utils_1.ListUtils.unwrapList(listData.items);
5720
+ if (listItems.length === 1 && listItems[0].String?.sval === 'none') {
5721
+ return 'OWNED BY NONE';
5722
+ }
5474
5723
  const parts = listItems.map(item => {
5475
5724
  const itemData = this.getNodeData(item);
5476
5725
  if (this.getNodeType(item) === 'String') {
5477
5726
  // Check if this identifier needs quotes to preserve case
5478
5727
  const value = itemData.sval;
5479
- if (Deparser.needsQuotes(value)) {
5480
- return `"${value}"`;
5481
- }
5482
- return value;
5728
+ return quotes_1.QuoteUtils.quoteIdentifier(value);
5483
5729
  }
5484
5730
  return this.visit(item, context);
5485
5731
  });
@@ -5610,11 +5856,13 @@ class Deparser {
5610
5856
  if (context.parentNodeTypes.includes('CreateExtensionStmt') || context.parentNodeTypes.includes('AlterExtensionStmt') || context.parentNodeTypes.includes('CreateFdwStmt') || context.parentNodeTypes.includes('AlterFdwStmt')) {
5611
5857
  // AlterExtensionStmt specific cases
5612
5858
  if (context.parentNodeTypes.includes('AlterExtensionStmt')) {
5613
- if (node.defname === 'to') {
5614
- return `TO ${argValue}`;
5859
+ if (node.defname === 'new_version') {
5860
+ // argValue is unquoted due to DefElem context, so we need to quote it
5861
+ const quotedValue = this.quoteDefElemArg(node.arg, argValue);
5862
+ return `TO ${quotedValue}`;
5615
5863
  }
5616
5864
  if (node.defname === 'schema') {
5617
- return `SCHEMA ${argValue}`;
5865
+ return `SET SCHEMA ${argValue}`;
5618
5866
  }
5619
5867
  }
5620
5868
  // CreateFdwStmt specific cases
@@ -5623,7 +5871,7 @@ class Deparser {
5623
5871
  return `${node.defname.toUpperCase()} ${argValue}`;
5624
5872
  }
5625
5873
  const quotedValue = typeof argValue === 'string'
5626
- ? quote_utils_1.QuoteUtils.escape(argValue)
5874
+ ? quotes_1.QuoteUtils.escape(argValue)
5627
5875
  : argValue;
5628
5876
  return `${node.defname} ${quotedValue}`;
5629
5877
  }
@@ -5631,28 +5879,37 @@ class Deparser {
5631
5879
  if (node.defname === 'cascade') {
5632
5880
  return argValue === 'true' ? 'CASCADE' : '';
5633
5881
  }
5882
+ if (node.defname === 'new_version') {
5883
+ return `VERSION ${this.quoteDefElemArg(node.arg, argValue)}`;
5884
+ }
5885
+ if (node.defname === 'schema' && node.arg && this.getNodeType(node.arg) === 'String') {
5886
+ return `SCHEMA ${quotes_1.QuoteUtils.quoteIdentifier(this.getNodeData(node.arg).sval)}`;
5887
+ }
5634
5888
  return `${node.defname.toUpperCase()} ${argValue}`;
5635
5889
  }
5636
5890
  // Handle IndexStmt WITH clause options - no quotes, compact formatting
5637
5891
  if (context.parentNodeTypes.includes('IndexStmt')) {
5638
- return `${node.defname}=${argValue}`;
5892
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5639
5893
  }
5640
5894
  // Handle IndexElem opclassopts - preserve string values as strings
5641
5895
  if (context.parentNodeTypes.includes('IndexElem')) {
5642
5896
  if (node.arg && this.getNodeType(node.arg) === 'String') {
5643
5897
  const stringData = this.getNodeData(node.arg);
5644
- return `${node.defname}='${stringData.sval}'`;
5898
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5645
5899
  }
5646
- return `${node.defname}=${argValue}`;
5900
+ return `${quotes_1.QuoteUtils.quoteIdentifierAfterDot(node.defname)}=${this.formatOptionValue(node.arg, argValue)}`;
5647
5901
  }
5648
5902
  // Handle CreateStmt table options - no quotes, compact formatting
5649
5903
  if (context.parentNodeTypes.includes('CreateStmt')) {
5904
+ const relOptName = node.defnamespace
5905
+ ? `${quotes_1.QuoteUtils.quoteIdentifier(node.defnamespace)}.${quotes_1.QuoteUtils.quoteIdentifier(node.defname)}`
5906
+ : quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5650
5907
  // For numeric values, use the raw value without quotes
5651
5908
  if (node.arg && this.getNodeType(node.arg) === 'Integer') {
5652
5909
  const integerData = this.getNodeData(node.arg);
5653
- return `${node.defname}=${integerData.ival}`;
5910
+ return `${relOptName}=${formatInteger(integerData.ival)}`;
5654
5911
  }
5655
- return `${node.defname}=${argValue}`;
5912
+ return `${relOptName}=${this.formatOptionValue(node.arg, argValue)}`;
5656
5913
  }
5657
5914
  // Handle CreateEventTrigStmt WHEN clause - use IN syntax for List arguments
5658
5915
  if (context.parentNodeTypes.includes('CreateEventTrigStmt')) {
@@ -5662,7 +5919,7 @@ class Deparser {
5662
5919
  const values = listItems.map(item => {
5663
5920
  if (this.getNodeType(item) === 'String') {
5664
5921
  const stringData = this.getNodeData(item);
5665
- return `'${stringData.sval || ''}'`;
5922
+ return quotes_1.QuoteUtils.escape(stringData.sval || '');
5666
5923
  }
5667
5924
  return this.visit(item, context);
5668
5925
  });
@@ -5677,7 +5934,7 @@ class Deparser {
5677
5934
  if (node.arg && this.getNodeType(node.arg) === 'TypeName') {
5678
5935
  return `${optionName} = ${argValue}`;
5679
5936
  }
5680
- return `${optionName} = ${argValue}`;
5937
+ return `${optionName} = ${this.formatOptionValue(node.arg, argValue)}`;
5681
5938
  }
5682
5939
  // Handle ViewStmt WITH options - don't quote numeric values
5683
5940
  if (context.parentNodeTypes.includes('ViewStmt')) {
@@ -5685,21 +5942,33 @@ class Deparser {
5685
5942
  return `${node.defname}=${argValue}`;
5686
5943
  }
5687
5944
  const quotedValue = typeof argValue === 'string'
5688
- ? quote_utils_1.QuoteUtils.escape(argValue)
5945
+ ? quotes_1.QuoteUtils.escape(argValue)
5689
5946
  : argValue;
5690
5947
  return `${node.defname} = ${quotedValue}`;
5691
5948
  }
5692
5949
  // Handle CopyStmt WITH clause options - uppercase format without quotes
5693
5950
  if (context.parentNodeTypes.includes('CopyStmt')) {
5694
- if (node.defname === 'format' && node.arg && this.getNodeType(node.arg) === 'String') {
5695
- const stringData = this.getNodeData(node.arg);
5696
- return `FORMAT ${stringData.sval.toUpperCase()}`;
5951
+ const optName = node.defname.toUpperCase();
5952
+ if (!node.arg) {
5953
+ return optName;
5697
5954
  }
5698
- // Handle other COPY options with uppercase defname
5699
- if (node.arg) {
5700
- return `${node.defname.toUpperCase()} ${argValue}`;
5955
+ const copyArgType = this.getNodeType(node.arg);
5956
+ if (copyArgType === 'Boolean' && this.getNodeData(node.arg).boolval !== false) {
5957
+ return optName;
5701
5958
  }
5702
- return node.defname.toUpperCase();
5959
+ if (copyArgType === 'List') {
5960
+ const items = list_utils_1.ListUtils.unwrapList(this.getNodeData(node.arg).items)
5961
+ .map(item => this.visit(item, context));
5962
+ return `${optName} (${items.join(', ')})`;
5963
+ }
5964
+ if (copyArgType === 'String') {
5965
+ const sval = this.getNodeData(node.arg).sval;
5966
+ if (node.defname === 'format') {
5967
+ return `${optName} ${sval}`;
5968
+ }
5969
+ return `${optName} ${quotes_1.QuoteUtils.escape(sval)}`;
5970
+ }
5971
+ return `${optName} ${argValue}`;
5703
5972
  }
5704
5973
  // Handle CREATE OPERATOR and CREATE TYPE context
5705
5974
  if (context.parentNodeTypes.includes('DefineStmt')) {
@@ -5723,13 +5992,13 @@ class Deparser {
5723
5992
  // Handle boolean flags (no arguments) - preserve quoted case
5724
5993
  if (['hashes', 'merges'].includes(node.defname.toLowerCase())) {
5725
5994
  if (node.defname !== node.defname.toLowerCase() && node.defname !== node.defname.toUpperCase()) {
5726
- return `"${node.defname}"`;
5995
+ return quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5727
5996
  }
5728
5997
  return preservedName.toUpperCase();
5729
5998
  }
5730
5999
  // Handle CREATE AGGREGATE quoted identifiers - preserve quotes when needed
5731
- if (Deparser.needsQuotes(node.defname)) {
5732
- const quotedDefname = `"${node.defname}"`;
6000
+ const quotedDefname = quotes_1.QuoteUtils.quoteIdentifier(node.defname);
6001
+ if (quotedDefname !== node.defname) {
5733
6002
  if (node.arg) {
5734
6003
  if (this.getNodeType(node.arg) === 'String') {
5735
6004
  const stringData = this.getNodeData(node.arg);
@@ -5738,7 +6007,7 @@ class Deparser {
5738
6007
  return `${quotedDefname} = ${stringData.sval}`;
5739
6008
  }
5740
6009
  // Regular string literals get single quotes
5741
- return `${quotedDefname} = '${stringData.sval}'`;
6010
+ return `${quotedDefname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5742
6011
  }
5743
6012
  return `${quotedDefname} = ${argValue}`;
5744
6013
  }
@@ -5759,7 +6028,7 @@ class Deparser {
5759
6028
  return `${node.defname} = ${stringData.sval}`;
5760
6029
  }
5761
6030
  // Regular string literals get single quotes
5762
- return `${node.defname} = '${stringData.sval}'`;
6031
+ return `${node.defname} = ${quotes_1.QuoteUtils.escape(stringData.sval)}`;
5763
6032
  }
5764
6033
  if (node.arg && this.getNodeType(node.arg) === 'Boolean') {
5765
6034
  const boolData = this.getNodeData(node.arg);
@@ -5780,8 +6049,12 @@ class Deparser {
5780
6049
  return `${node.defname} = ${argValue}`;
5781
6050
  }
5782
6051
  }
6052
+ const argType = this.getNodeType(node.arg);
6053
+ if (argType === 'TypeName' || argType === 'Integer' || argType === 'Float' || argType === 'Boolean') {
6054
+ return `${node.defname} = ${argValue}`;
6055
+ }
5783
6056
  const quotedValue = typeof argValue === 'string'
5784
- ? quote_utils_1.QuoteUtils.escape(argValue)
6057
+ ? quotes_1.QuoteUtils.escape(argValue)
5785
6058
  : argValue;
5786
6059
  return `${node.defname} = ${quotedValue}`;
5787
6060
  }
@@ -5789,7 +6062,7 @@ class Deparser {
5789
6062
  if (context.parentNodeTypes.includes('DefineStmt') && !node.arg) {
5790
6063
  // Check if the original defname appears to be quoted (mixed case that's not all upper/lower)
5791
6064
  if (node.defname !== node.defname.toLowerCase() && node.defname !== node.defname.toUpperCase()) {
5792
- return `"${node.defname}"`;
6065
+ return quotes_1.QuoteUtils.quoteIdentifier(node.defname);
5793
6066
  }
5794
6067
  }
5795
6068
  return node.defname.toUpperCase();
@@ -5805,7 +6078,7 @@ class Deparser {
5805
6078
  }
5806
6079
  if (node.location) {
5807
6080
  output.push('LOCATION');
5808
- output.push(`'${node.location}'`);
6081
+ output.push(quotes_1.QuoteUtils.escape(node.location));
5809
6082
  }
5810
6083
  if (node.options && node.options.length > 0) {
5811
6084
  output.push('WITH');
@@ -5869,6 +6142,7 @@ class Deparser {
5869
6142
  if (node.extname) {
5870
6143
  output.push(this.quoteIfNeeded(node.extname));
5871
6144
  }
6145
+ output.push('UPDATE');
5872
6146
  if (node.options && node.options.length > 0) {
5873
6147
  const extContext = context.spawn('AlterExtensionStmt');
5874
6148
  const options = list_utils_1.ListUtils.unwrapList(node.options)
@@ -5878,6 +6152,34 @@ class Deparser {
5878
6152
  }
5879
6153
  return output.join(' ');
5880
6154
  }
6155
+ AlterExtensionContentsStmt(node, context) {
6156
+ const output = ['ALTER', 'EXTENSION'];
6157
+ if (node.extname) {
6158
+ output.push(this.quoteIfNeeded(node.extname));
6159
+ }
6160
+ // Handle action: 1 = ADD, -1 = DROP
6161
+ if (node.action === 1) {
6162
+ output.push('ADD');
6163
+ }
6164
+ else if (node.action === -1) {
6165
+ output.push('DROP');
6166
+ }
6167
+ // Add object type keyword
6168
+ if (node.objtype) {
6169
+ try {
6170
+ output.push(this.getObjectTypeKeyword(node.objtype));
6171
+ }
6172
+ catch {
6173
+ // Fallback to the raw objtype if not supported
6174
+ output.push(node.objtype.toString());
6175
+ }
6176
+ }
6177
+ // Add the object itself
6178
+ if (node.object) {
6179
+ output.push(this.visit(node.object, context));
6180
+ }
6181
+ return output.join(' ');
6182
+ }
5881
6183
  CreateFdwStmt(node, context) {
5882
6184
  const output = ['CREATE', 'FOREIGN', 'DATA', 'WRAPPER'];
5883
6185
  if (node.fdwname) {
@@ -5945,7 +6247,7 @@ class Deparser {
5945
6247
  case 'REPLICA_IDENTITY_INDEX':
5946
6248
  output.push('USING', 'INDEX');
5947
6249
  if (node.name) {
5948
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
6250
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
5949
6251
  }
5950
6252
  break;
5951
6253
  default:
@@ -6004,7 +6306,7 @@ class Deparser {
6004
6306
  output.push('IF', 'EXISTS');
6005
6307
  }
6006
6308
  if (node.name) {
6007
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
6309
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
6008
6310
  }
6009
6311
  if (node.behavior === 'DROP_CASCADE') {
6010
6312
  output.push('CASCADE');
@@ -6013,7 +6315,7 @@ class Deparser {
6013
6315
  case 'AT_ValidateConstraint':
6014
6316
  output.push('VALIDATE', 'CONSTRAINT');
6015
6317
  if (node.name) {
6016
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
6318
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
6017
6319
  }
6018
6320
  break;
6019
6321
  case 'C':
@@ -6030,7 +6332,7 @@ class Deparser {
6030
6332
  output.push('IF', 'EXISTS');
6031
6333
  }
6032
6334
  if (node.name) {
6033
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
6335
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
6034
6336
  }
6035
6337
  if (node.behavior === 'DROP_CASCADE') {
6036
6338
  output.push('CASCADE');
@@ -6039,7 +6341,7 @@ class Deparser {
6039
6341
  case 'V':
6040
6342
  output.push('VALIDATE', 'CONSTRAINT');
6041
6343
  if (node.name) {
6042
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
6344
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
6043
6345
  }
6044
6346
  break;
6045
6347
  case 'O':
@@ -6110,7 +6412,7 @@ class Deparser {
6110
6412
  }
6111
6413
  if (node.payload !== null && node.payload !== undefined) {
6112
6414
  output.push(',');
6113
- output.push(`'${node.payload}'`);
6415
+ output.push(quotes_1.QuoteUtils.escape(node.payload));
6114
6416
  }
6115
6417
  return output.join(' ');
6116
6418
  }
@@ -6138,7 +6440,7 @@ class Deparser {
6138
6440
  if (!node.filename) {
6139
6441
  throw new Error('LoadStmt requires filename');
6140
6442
  }
6141
- return `LOAD '${node.filename}'`;
6443
+ return `LOAD ${quotes_1.QuoteUtils.escape(node.filename)}`;
6142
6444
  }
6143
6445
  DiscardStmt(node, context) {
6144
6446
  switch (node.target) {
@@ -6154,6 +6456,13 @@ class Deparser {
6154
6456
  throw new Error(`Unsupported DiscardStmt target: ${node.target}`);
6155
6457
  }
6156
6458
  }
6459
+ /** `schema.op` for an operator name list: schema parts quoted, the operator symbol as is. */
6460
+ qualifiedOperatorName(objname) {
6461
+ const names = list_utils_1.ListUtils.unwrapList(objname ?? []).map((n) => (n.String ? n.String.sval : 'unknown'));
6462
+ if (names.length === 0)
6463
+ return 'unknown';
6464
+ return [...names.slice(0, -1).map((n) => quotes_1.QuoteUtils.quoteIdentifier(n)), names[names.length - 1]].join('.');
6465
+ }
6157
6466
  CommentStmt(node, context) {
6158
6467
  const output = ['COMMENT ON'];
6159
6468
  if (node.objtype) {
@@ -6185,6 +6494,9 @@ class Deparser {
6185
6494
  case 'OBJECT_TABCONSTRAINT':
6186
6495
  output.push('CONSTRAINT');
6187
6496
  break;
6497
+ case 'OBJECT_DOMCONSTRAINT':
6498
+ output.push('CONSTRAINT');
6499
+ break;
6188
6500
  case 'OBJECT_TRIGGER':
6189
6501
  output.push('TRIGGER');
6190
6502
  break;
@@ -6261,23 +6573,30 @@ class Deparser {
6261
6573
  output.push(objectParts.join('.'));
6262
6574
  }
6263
6575
  }
6264
- else if (node.objtype === 'OBJECT_TRIGGER') {
6576
+ else if (node.objtype === 'OBJECT_DOMCONSTRAINT') {
6265
6577
  if (objectParts.length === 2) {
6266
- const [table, trigger] = objectParts;
6267
- output.push(trigger);
6578
+ const [domain, constraint] = objectParts;
6579
+ output.push(constraint, 'ON DOMAIN', domain);
6580
+ }
6581
+ else {
6582
+ output.push(objectParts.join('.'));
6583
+ }
6584
+ }
6585
+ else if (node.objtype === 'OBJECT_TRIGGER') {
6586
+ if (objectParts.length >= 2) {
6587
+ output.push(objectParts[objectParts.length - 1]);
6268
6588
  output.push('ON');
6269
- output.push(table);
6589
+ output.push(objectParts.slice(0, -1).join('.'));
6270
6590
  }
6271
6591
  else {
6272
6592
  output.push(objectParts.join('.'));
6273
6593
  }
6274
6594
  }
6275
6595
  else if (node.objtype === 'OBJECT_RULE') {
6276
- if (objectParts.length === 2) {
6277
- const [table, rule] = objectParts;
6278
- output.push(rule);
6596
+ if (objectParts.length >= 2) {
6597
+ output.push(objectParts[objectParts.length - 1]);
6279
6598
  output.push('ON');
6280
- output.push(table);
6599
+ output.push(objectParts.slice(0, -1).join('.'));
6281
6600
  }
6282
6601
  else {
6283
6602
  output.push(objectParts.join('.'));
@@ -6288,8 +6607,7 @@ class Deparser {
6288
6607
  // For operators, we need to handle ObjectWithArgs structure
6289
6608
  if (node.object && node.object.ObjectWithArgs) {
6290
6609
  const objWithArgs = node.object.ObjectWithArgs;
6291
- let operatorName = objWithArgs.objname && objWithArgs.objname[0] && objWithArgs.objname[0].String
6292
- ? objWithArgs.objname[0].String.sval : 'unknown';
6610
+ let operatorName = this.qualifiedOperatorName(objWithArgs.objname);
6293
6611
  if (operatorName.startsWith('"') && operatorName.endsWith('"')) {
6294
6612
  operatorName = operatorName.slice(1, -1);
6295
6613
  }
@@ -6327,7 +6645,13 @@ class Deparser {
6327
6645
  }
6328
6646
  else if (node.objtype === 'OBJECT_POLICY') {
6329
6647
  // Handle POLICY syntax: COMMENT ON POLICY policy_name ON table_name IS 'comment'
6330
- if (objectParts.length === 2) {
6648
+ if (objectParts.length === 3) {
6649
+ const [schema, table, policy] = objectParts;
6650
+ output.push(policy);
6651
+ output.push('ON');
6652
+ output.push(`${schema}.${table}`);
6653
+ }
6654
+ else if (objectParts.length === 2) {
6331
6655
  const [table, policy] = objectParts;
6332
6656
  output.push(policy);
6333
6657
  output.push('ON');
@@ -6350,6 +6674,9 @@ class Deparser {
6350
6674
  output.push(objectParts.join('.'));
6351
6675
  }
6352
6676
  }
6677
+ else if (node.objtype === 'OBJECT_CAST' && objectParts.length === 2) {
6678
+ output.push(`(${objectParts[0]} AS ${objectParts[1]})`);
6679
+ }
6353
6680
  else {
6354
6681
  output.push(objectParts.join('.'));
6355
6682
  }
@@ -6358,8 +6685,7 @@ class Deparser {
6358
6685
  else if (node.objtype === 'OBJECT_OPERATOR' && node.object && node.object.ObjectWithArgs) {
6359
6686
  // Handle direct ObjectWithArgs for OPERATOR syntax: COMMENT ON OPERATOR -(NONE, integer) IS 'comment'
6360
6687
  const objWithArgs = node.object.ObjectWithArgs;
6361
- let operatorName = objWithArgs.objname && objWithArgs.objname[0] && objWithArgs.objname[0].String
6362
- ? objWithArgs.objname[0].String.sval : 'unknown';
6688
+ let operatorName = this.qualifiedOperatorName(objWithArgs.objname);
6363
6689
  // Remove quotes from operator name if present
6364
6690
  if (operatorName.startsWith('"') && operatorName.endsWith('"')) {
6365
6691
  operatorName = operatorName.slice(1, -1);
@@ -6391,7 +6717,7 @@ class Deparser {
6391
6717
  output.push('NULL');
6392
6718
  }
6393
6719
  else if (node.comment) {
6394
- output.push(quote_utils_1.QuoteUtils.formatEString(node.comment));
6720
+ output.push(quotes_1.QuoteUtils.formatEString(node.comment));
6395
6721
  }
6396
6722
  return output.join(' ');
6397
6723
  }
@@ -6428,7 +6754,7 @@ class Deparser {
6428
6754
  const output = [];
6429
6755
  const initialParts = ['CREATE', 'POLICY'];
6430
6756
  if (node.policy_name) {
6431
- initialParts.push(quote_utils_1.QuoteUtils.quote(node.policy_name));
6757
+ initialParts.push(quotes_1.QuoteUtils.quoteIdentifier(node.policy_name));
6432
6758
  }
6433
6759
  output.push(initialParts.join(' '));
6434
6760
  // Add ON clause on new line in pretty mode
@@ -6505,7 +6831,7 @@ class Deparser {
6505
6831
  AlterPolicyStmt(node, context) {
6506
6832
  const output = ['ALTER', 'POLICY'];
6507
6833
  if (node.policy_name) {
6508
- output.push(quote_utils_1.QuoteUtils.quote(node.policy_name));
6834
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.policy_name));
6509
6835
  }
6510
6836
  if (node.table) {
6511
6837
  output.push('ON');
@@ -6541,7 +6867,7 @@ class Deparser {
6541
6867
  }
6542
6868
  output.push('SERVER');
6543
6869
  if (node.servername) {
6544
- output.push(`"${node.servername}"`);
6870
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.servername));
6545
6871
  }
6546
6872
  if (node.options && node.options.length > 0) {
6547
6873
  output.push('OPTIONS');
@@ -6589,10 +6915,10 @@ class Deparser {
6589
6915
  CreatePublicationStmt(node, context) {
6590
6916
  const output = ['CREATE', 'PUBLICATION'];
6591
6917
  if (node.pubname) {
6592
- output.push(`"${node.pubname}"`);
6918
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.pubname));
6593
6919
  }
6594
6920
  if (node.pubobjects && node.pubobjects.length > 0) {
6595
- output.push('FOR', 'TABLE');
6921
+ output.push('FOR');
6596
6922
  const tables = list_utils_1.ListUtils.unwrapList(node.pubobjects).map(table => this.visit(table, context));
6597
6923
  output.push(tables.join(', '));
6598
6924
  }
@@ -6609,11 +6935,11 @@ class Deparser {
6609
6935
  CreateSubscriptionStmt(node, context) {
6610
6936
  const output = ['CREATE', 'SUBSCRIPTION'];
6611
6937
  if (node.subname) {
6612
- output.push(`"${node.subname}"`);
6938
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
6613
6939
  }
6614
6940
  output.push('CONNECTION');
6615
6941
  if (node.conninfo) {
6616
- output.push(`'${node.conninfo}'`);
6942
+ output.push(quotes_1.QuoteUtils.escape(node.conninfo));
6617
6943
  }
6618
6944
  output.push('PUBLICATION');
6619
6945
  if (node.publication && node.publication.length > 0) {
@@ -6630,10 +6956,11 @@ class Deparser {
6630
6956
  AlterPublicationStmt(node, context) {
6631
6957
  const output = ['ALTER', 'PUBLICATION'];
6632
6958
  if (node.pubname) {
6633
- output.push(`"${node.pubname}"`);
6959
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.pubname));
6634
6960
  }
6635
- if (node.action) {
6636
- switch (node.action) {
6961
+ const hasObjects = node.pubobjects && node.pubobjects.length > 0;
6962
+ if (hasObjects) {
6963
+ switch (node.action || 'AP_AddObjects') {
6637
6964
  case 'AP_AddObjects':
6638
6965
  output.push('ADD');
6639
6966
  break;
@@ -6646,17 +6973,11 @@ class Deparser {
6646
6973
  default:
6647
6974
  throw new Error(`Unsupported AlterPublicationStmt action: ${node.action}`);
6648
6975
  }
6649
- }
6650
- if (node.for_all_tables) {
6651
- output.push('FOR ALL TABLES');
6652
- }
6653
- else if (node.pubobjects && node.pubobjects.length > 0) {
6654
- output.push('FOR TABLE');
6655
6976
  const objects = list_utils_1.ListUtils.unwrapList(node.pubobjects).map(obj => this.visit(obj, context));
6656
6977
  output.push(objects.join(', '));
6657
6978
  }
6658
- if (node.options && node.options.length > 0) {
6659
- output.push('WITH');
6979
+ else if (node.options && node.options.length > 0) {
6980
+ output.push('SET');
6660
6981
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
6661
6982
  output.push(`(${options.join(', ')})`);
6662
6983
  }
@@ -6665,44 +6986,69 @@ class Deparser {
6665
6986
  AlterSubscriptionStmt(node, context) {
6666
6987
  const output = ['ALTER', 'SUBSCRIPTION'];
6667
6988
  if (node.subname) {
6668
- output.push(`"${node.subname}"`);
6669
- }
6670
- if (node.kind) {
6671
- switch (node.kind) {
6989
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
6990
+ }
6991
+ const options = node.options && node.options.length > 0
6992
+ ? `(${list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context)).join(', ')})`
6993
+ : null;
6994
+ const publications = node.publication && node.publication.length > 0
6995
+ ? list_utils_1.ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context)).join(', ')
6996
+ : null;
6997
+ {
6998
+ switch (node.kind || 'ALTER_SUBSCRIPTION_OPTIONS') {
6672
6999
  case 'ALTER_SUBSCRIPTION_OPTIONS':
6673
7000
  output.push('SET');
7001
+ if (options)
7002
+ output.push(options);
6674
7003
  break;
6675
7004
  case 'ALTER_SUBSCRIPTION_CONNECTION':
6676
7005
  output.push('CONNECTION');
6677
7006
  if (node.conninfo) {
6678
- output.push(`'${node.conninfo}'`);
7007
+ output.push(quotes_1.QuoteUtils.escape(node.conninfo));
6679
7008
  }
6680
7009
  break;
6681
7010
  case 'ALTER_SUBSCRIPTION_SET_PUBLICATION':
6682
7011
  output.push('SET PUBLICATION');
6683
- if (node.publication && node.publication.length > 0) {
6684
- const publications = list_utils_1.ListUtils.unwrapList(node.publication).map(pub => this.visit(pub, context));
6685
- output.push(publications.join(', '));
6686
- }
7012
+ if (publications)
7013
+ output.push(publications);
7014
+ if (options)
7015
+ output.push('WITH', options);
7016
+ break;
7017
+ case 'ALTER_SUBSCRIPTION_ADD_PUBLICATION':
7018
+ output.push('ADD PUBLICATION');
7019
+ if (publications)
7020
+ output.push(publications);
7021
+ if (options)
7022
+ output.push('WITH', options);
7023
+ break;
7024
+ case 'ALTER_SUBSCRIPTION_DROP_PUBLICATION':
7025
+ output.push('DROP PUBLICATION');
7026
+ if (publications)
7027
+ output.push(publications);
7028
+ if (options)
7029
+ output.push('WITH', options);
6687
7030
  break;
6688
7031
  case 'ALTER_SUBSCRIPTION_REFRESH':
6689
7032
  output.push('REFRESH PUBLICATION');
7033
+ if (options)
7034
+ output.push('WITH', options);
6690
7035
  break;
6691
- case 'ALTER_SUBSCRIPTION_ENABLED':
6692
- output.push('ENABLE');
7036
+ case 'ALTER_SUBSCRIPTION_ENABLED': {
7037
+ const enabled = list_utils_1.ListUtils.unwrapList(node.options).find(opt => opt.DefElem?.defname === 'enabled');
7038
+ const arg = enabled?.DefElem?.arg;
7039
+ const isEnabled = arg?.Boolean ? arg.Boolean.boolval !== false : true;
7040
+ output.push(isEnabled ? 'ENABLE' : 'DISABLE');
6693
7041
  break;
7042
+ }
6694
7043
  case 'ALTER_SUBSCRIPTION_SKIP':
6695
7044
  output.push('SKIP');
7045
+ if (options)
7046
+ output.push(options);
6696
7047
  break;
6697
7048
  default:
6698
7049
  throw new Error(`Unsupported AlterSubscriptionStmt kind: ${node.kind}`);
6699
7050
  }
6700
7051
  }
6701
- if (node.options && node.options.length > 0) {
6702
- output.push('WITH');
6703
- const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
6704
- output.push(`(${options.join(', ')})`);
6705
- }
6706
7052
  return output.join(' ');
6707
7053
  }
6708
7054
  DropSubscriptionStmt(node, context) {
@@ -6711,7 +7057,7 @@ class Deparser {
6711
7057
  output.push('IF EXISTS');
6712
7058
  }
6713
7059
  if (node.subname) {
6714
- output.push(`"${node.subname}"`);
7060
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
6715
7061
  }
6716
7062
  if (node.behavior) {
6717
7063
  switch (node.behavior) {
@@ -6956,7 +7302,7 @@ class Deparser {
6956
7302
  ClosePortalStmt(node, context) {
6957
7303
  const output = ['CLOSE'];
6958
7304
  if (node.portalname) {
6959
- output.push(quote_utils_1.QuoteUtils.quote(node.portalname));
7305
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.portalname));
6960
7306
  }
6961
7307
  else {
6962
7308
  output.push('ALL');
@@ -7014,7 +7360,7 @@ class Deparser {
7014
7360
  output.push('ALL');
7015
7361
  }
7016
7362
  if (node.portalname) {
7017
- output.push(quote_utils_1.QuoteUtils.quote(node.portalname));
7363
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.portalname));
7018
7364
  }
7019
7365
  return output.join(' ');
7020
7366
  }
@@ -7084,7 +7430,7 @@ class Deparser {
7084
7430
  AlterFdwStmt(node, context) {
7085
7431
  const output = ['ALTER', 'FOREIGN', 'DATA', 'WRAPPER'];
7086
7432
  if (node.fdwname) {
7087
- output.push(quote_utils_1.QuoteUtils.quote(node.fdwname));
7433
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.fdwname));
7088
7434
  }
7089
7435
  if (node.func_options && node.func_options.length > 0) {
7090
7436
  const fdwContext = context.spawn('AlterFdwStmt');
@@ -7105,16 +7451,16 @@ class Deparser {
7105
7451
  output.push('IF', 'NOT', 'EXISTS');
7106
7452
  }
7107
7453
  if (node.servername) {
7108
- output.push(quote_utils_1.QuoteUtils.quote(node.servername));
7454
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.servername));
7109
7455
  }
7110
7456
  if (node.servertype) {
7111
- output.push('TYPE', quote_utils_1.QuoteUtils.escape(node.servertype));
7457
+ output.push('TYPE', quotes_1.QuoteUtils.escape(node.servertype));
7112
7458
  }
7113
7459
  if (node.version) {
7114
- output.push('VERSION', quote_utils_1.QuoteUtils.escape(node.version));
7460
+ output.push('VERSION', quotes_1.QuoteUtils.escape(node.version));
7115
7461
  }
7116
7462
  if (node.fdwname) {
7117
- output.push('FOREIGN', 'DATA', 'WRAPPER', quote_utils_1.QuoteUtils.quote(node.fdwname));
7463
+ output.push('FOREIGN', 'DATA', 'WRAPPER', quotes_1.QuoteUtils.quoteIdentifier(node.fdwname));
7118
7464
  }
7119
7465
  if (node.options && node.options.length > 0) {
7120
7466
  output.push('OPTIONS');
@@ -7129,10 +7475,10 @@ class Deparser {
7129
7475
  AlterForeignServerStmt(node, context) {
7130
7476
  const output = ['ALTER', 'SERVER'];
7131
7477
  if (node.servername) {
7132
- output.push(quote_utils_1.QuoteUtils.quote(node.servername));
7478
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.servername));
7133
7479
  }
7134
7480
  if (node.version) {
7135
- output.push('VERSION', quote_utils_1.QuoteUtils.escape(node.version));
7481
+ output.push('VERSION', quotes_1.QuoteUtils.escape(node.version));
7136
7482
  }
7137
7483
  if (node.options && node.options.length > 0) {
7138
7484
  output.push('OPTIONS');
@@ -7154,7 +7500,7 @@ class Deparser {
7154
7500
  }
7155
7501
  output.push('SERVER');
7156
7502
  if (node.servername) {
7157
- output.push(quote_utils_1.QuoteUtils.quote(node.servername));
7503
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.servername));
7158
7504
  }
7159
7505
  if (node.options && node.options.length > 0) {
7160
7506
  output.push('OPTIONS');
@@ -7178,14 +7524,14 @@ class Deparser {
7178
7524
  }
7179
7525
  output.push('SERVER');
7180
7526
  if (node.servername) {
7181
- output.push(quote_utils_1.QuoteUtils.quote(node.servername));
7527
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.servername));
7182
7528
  }
7183
7529
  return output.join(' ');
7184
7530
  }
7185
7531
  ImportForeignSchemaStmt(node, context) {
7186
7532
  const output = ['IMPORT', 'FOREIGN', 'SCHEMA'];
7187
7533
  if (node.remote_schema) {
7188
- output.push(quote_utils_1.QuoteUtils.quote(node.remote_schema));
7534
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.remote_schema));
7189
7535
  }
7190
7536
  if (node.list_type) {
7191
7537
  switch (node.list_type) {
@@ -7211,11 +7557,11 @@ class Deparser {
7211
7557
  }
7212
7558
  output.push('FROM', 'SERVER');
7213
7559
  if (node.server_name) {
7214
- output.push(quote_utils_1.QuoteUtils.quote(node.server_name));
7560
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.server_name));
7215
7561
  }
7216
7562
  output.push('INTO');
7217
7563
  if (node.local_schema) {
7218
- output.push(quote_utils_1.QuoteUtils.quote(node.local_schema));
7564
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.local_schema));
7219
7565
  }
7220
7566
  if (node.options && node.options.length > 0) {
7221
7567
  const importSchemaContext = context.spawn('ImportForeignSchemaStmt');
@@ -7229,7 +7575,7 @@ class Deparser {
7229
7575
  if (node.relation) {
7230
7576
  output.push(this.RangeVar(node.relation, context));
7231
7577
  if (node.indexname) {
7232
- output.push('USING', `"${node.indexname}"`);
7578
+ output.push('USING', quotes_1.QuoteUtils.quoteIdentifier(node.indexname));
7233
7579
  }
7234
7580
  }
7235
7581
  if (node.params && node.params.length > 0) {
@@ -7293,7 +7639,7 @@ class Deparser {
7293
7639
  output.push(this.RangeVar(node.relation, context));
7294
7640
  }
7295
7641
  if (node.name) {
7296
- output.push(`"${node.name}"`);
7642
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
7297
7643
  }
7298
7644
  return output.join(' ');
7299
7645
  }
@@ -7333,7 +7679,7 @@ class Deparser {
7333
7679
  if (!node.dbname) {
7334
7680
  throw new Error('CreatedbStmt requires dbname');
7335
7681
  }
7336
- output.push(`"${node.dbname}"`);
7682
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.dbname));
7337
7683
  if (node.options && node.options.length > 0) {
7338
7684
  const options = list_utils_1.ListUtils.unwrapList(node.options)
7339
7685
  .map(option => this.visit(option, context))
@@ -7350,7 +7696,7 @@ class Deparser {
7350
7696
  if (!node.dbname) {
7351
7697
  throw new Error('DropdbStmt requires dbname');
7352
7698
  }
7353
- output.push(`"${node.dbname}"`);
7699
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.dbname));
7354
7700
  if (node.options && node.options.length > 0) {
7355
7701
  const options = list_utils_1.ListUtils.unwrapList(node.options)
7356
7702
  .map(option => this.visit(option, context))
@@ -7448,7 +7794,7 @@ class Deparser {
7448
7794
  case 'OBJECT_POLICY':
7449
7795
  output.push('POLICY');
7450
7796
  if (node.subname) {
7451
- output.push(quote_utils_1.QuoteUtils.quote(node.subname));
7797
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
7452
7798
  }
7453
7799
  break;
7454
7800
  case 'OBJECT_PUBLICATION':
@@ -7506,7 +7852,7 @@ class Deparser {
7506
7852
  }
7507
7853
  // Handle OBJECT_RULE special case: rule_name ON table_name format
7508
7854
  if (node.renameType === 'OBJECT_RULE' && node.subname && node.relation) {
7509
- output.push(quote_utils_1.QuoteUtils.quote(node.subname));
7855
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname));
7510
7856
  output.push('ON');
7511
7857
  output.push(this.RangeVar(node.relation, context));
7512
7858
  }
@@ -7525,12 +7871,21 @@ class Deparser {
7525
7871
  if (items.length === 2) {
7526
7872
  const accessMethod = items[0].String?.sval || '';
7527
7873
  const objectName = items[1].String?.sval || '';
7528
- output.push(`${quote_utils_1.QuoteUtils.quote(objectName)} USING ${accessMethod}`);
7874
+ output.push(`${quotes_1.QuoteUtils.quoteIdentifier(objectName)} USING ${accessMethod}`);
7529
7875
  }
7530
7876
  else {
7531
7877
  output.push(this.visit(node.object, context));
7532
7878
  }
7533
7879
  }
7880
+ else if ((node.renameType === 'OBJECT_TYPE' || node.renameType === 'OBJECT_DOMAIN') && node.object.List) {
7881
+ // Qualified type names - join List parts with dots
7882
+ const items = list_utils_1.ListUtils.unwrapList(node.object);
7883
+ const parts = items
7884
+ .map((item) => item.String?.sval)
7885
+ .filter((s) => typeof s === 'string')
7886
+ .map((s) => this.quoteIfNeeded(s));
7887
+ output.push(parts.join('.'));
7888
+ }
7534
7889
  else if (node.renameType === 'OBJECT_SCHEMA' && node.object.List) {
7535
7890
  // Handle schema names - extract from List structure
7536
7891
  const items = list_utils_1.ListUtils.unwrapList(node.object);
@@ -7546,19 +7901,19 @@ class Deparser {
7546
7901
  }
7547
7902
  }
7548
7903
  if (node.renameType === 'OBJECT_COLUMN' && node.subname) {
7549
- output.push('RENAME COLUMN', `"${node.subname}"`, 'TO');
7904
+ output.push('RENAME COLUMN', quotes_1.QuoteUtils.quoteIdentifier(node.subname), 'TO');
7550
7905
  }
7551
7906
  else if (node.renameType === 'OBJECT_DOMCONSTRAINT' && node.subname) {
7552
- output.push('RENAME CONSTRAINT', `"${node.subname}"`, 'TO');
7907
+ output.push('RENAME CONSTRAINT', quotes_1.QuoteUtils.quoteIdentifier(node.subname), 'TO');
7553
7908
  }
7554
7909
  else if (node.renameType === 'OBJECT_TABCONSTRAINT' && node.subname) {
7555
- output.push('RENAME CONSTRAINT', `"${node.subname}"`, 'TO');
7910
+ output.push('RENAME CONSTRAINT', quotes_1.QuoteUtils.quoteIdentifier(node.subname), 'TO');
7556
7911
  }
7557
7912
  else if (node.renameType === 'OBJECT_ATTRIBUTE' && node.subname) {
7558
- output.push('RENAME ATTRIBUTE', `"${node.subname}"`, 'TO');
7913
+ output.push('RENAME ATTRIBUTE', quotes_1.QuoteUtils.quoteIdentifier(node.subname), 'TO');
7559
7914
  }
7560
7915
  else if (node.renameType === 'OBJECT_ROLE' && node.subname) {
7561
- output.push(`"${node.subname}"`, 'RENAME TO');
7916
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.subname), 'RENAME TO');
7562
7917
  }
7563
7918
  else if (node.renameType === 'OBJECT_SCHEMA' && node.subname) {
7564
7919
  output.push(this.quoteIfNeeded(node.subname), 'RENAME TO');
@@ -7572,7 +7927,7 @@ class Deparser {
7572
7927
  if (!node.newname) {
7573
7928
  throw new Error('RenameStmt requires newname');
7574
7929
  }
7575
- output.push(quote_utils_1.QuoteUtils.quote(node.newname));
7930
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.newname));
7576
7931
  // Handle CASCADE/RESTRICT behavior for RENAME operations
7577
7932
  if (node.behavior === 'DROP_CASCADE') {
7578
7933
  output.push('CASCADE');
@@ -7584,76 +7939,7 @@ class Deparser {
7584
7939
  if (!node.objectType) {
7585
7940
  throw new Error('AlterOwnerStmt requires objectType');
7586
7941
  }
7587
- switch (node.objectType) {
7588
- case 'OBJECT_TABLE':
7589
- output.push('TABLE');
7590
- break;
7591
- case 'OBJECT_VIEW':
7592
- output.push('VIEW');
7593
- break;
7594
- case 'OBJECT_INDEX':
7595
- output.push('INDEX');
7596
- break;
7597
- case 'OBJECT_SEQUENCE':
7598
- output.push('SEQUENCE');
7599
- break;
7600
- case 'OBJECT_FUNCTION':
7601
- output.push('FUNCTION');
7602
- break;
7603
- case 'OBJECT_PROCEDURE':
7604
- output.push('PROCEDURE');
7605
- break;
7606
- case 'OBJECT_SCHEMA':
7607
- output.push('SCHEMA');
7608
- break;
7609
- case 'OBJECT_DATABASE':
7610
- output.push('DATABASE');
7611
- break;
7612
- case 'OBJECT_DOMAIN':
7613
- output.push('DOMAIN');
7614
- break;
7615
- case 'OBJECT_AGGREGATE':
7616
- output.push('AGGREGATE');
7617
- break;
7618
- case 'OBJECT_CONVERSION':
7619
- output.push('CONVERSION');
7620
- break;
7621
- case 'OBJECT_LANGUAGE':
7622
- output.push('LANGUAGE');
7623
- break;
7624
- case 'OBJECT_OPERATOR':
7625
- output.push('OPERATOR');
7626
- break;
7627
- case 'OBJECT_OPFAMILY':
7628
- output.push('OPERATOR FAMILY');
7629
- break;
7630
- case 'OBJECT_OPCLASS':
7631
- output.push('OPERATOR CLASS');
7632
- break;
7633
- case 'OBJECT_TSDICTIONARY':
7634
- output.push('TEXT SEARCH DICTIONARY');
7635
- break;
7636
- case 'OBJECT_TSCONFIGURATION':
7637
- output.push('TEXT SEARCH CONFIGURATION');
7638
- break;
7639
- case 'OBJECT_EVENT_TRIGGER':
7640
- output.push('EVENT TRIGGER');
7641
- break;
7642
- case 'OBJECT_FDW':
7643
- output.push('FOREIGN DATA WRAPPER');
7644
- break;
7645
- case 'OBJECT_FOREIGN_SERVER':
7646
- output.push('SERVER');
7647
- break;
7648
- case 'OBJECT_TYPE':
7649
- output.push('TYPE');
7650
- break;
7651
- case 'OBJECT_COLLATION':
7652
- output.push('COLLATION');
7653
- break;
7654
- default:
7655
- throw new Error(`Unsupported AlterOwnerStmt objectType: ${node.objectType}`);
7656
- }
7942
+ output.push(this.getObjectTypeKeyword(node.objectType));
7657
7943
  if (node.relation) {
7658
7944
  output.push(this.RangeVar(node.relation, context));
7659
7945
  }
@@ -7664,7 +7950,7 @@ class Deparser {
7664
7950
  if (items.length === 2) {
7665
7951
  const accessMethod = items[0].String?.sval || '';
7666
7952
  const objectName = items[1].String?.sval || '';
7667
- output.push(`${quote_utils_1.QuoteUtils.quote(objectName)} USING ${accessMethod}`);
7953
+ output.push(`${quotes_1.QuoteUtils.quoteIdentifier(objectName)} USING ${accessMethod}`);
7668
7954
  }
7669
7955
  else {
7670
7956
  output.push(this.visit(node.object, context));
@@ -7739,6 +8025,9 @@ class Deparser {
7739
8025
  else if (node.objtype === 'OBJECT_LANGUAGE') {
7740
8026
  output.push('LANGUAGE');
7741
8027
  }
8028
+ else if (node.objtype === 'OBJECT_SEQUENCE') {
8029
+ output.push('SEQUENCE');
8030
+ }
7742
8031
  else if (node.objtype === 'OBJECT_FUNCTION') {
7743
8032
  output.push('FUNCTION');
7744
8033
  }
@@ -7764,8 +8053,11 @@ class Deparser {
7764
8053
  output.push('DATABASE');
7765
8054
  }
7766
8055
  if (node.objects && node.objects.length > 0) {
8056
+ const qualifiedKinds = ['OBJECT_TYPE', 'OBJECT_DOMAIN'];
7767
8057
  const objects = list_utils_1.ListUtils.unwrapList(node.objects)
7768
- .map(obj => this.visit(obj, context))
8058
+ .map(obj => qualifiedKinds.includes(node.objtype) && obj.List
8059
+ ? list_utils_1.ListUtils.unwrapList(obj.List.items).map((item) => this.visit(item, context)).join('.')
8060
+ : this.visit(obj, context))
7769
8061
  .join(', ');
7770
8062
  output.push(objects);
7771
8063
  }
@@ -7804,8 +8096,11 @@ class Deparser {
7804
8096
  break;
7805
8097
  default:
7806
8098
  if (node.objects && node.objects.length > 0) {
8099
+ const qualifiedKinds = ['OBJECT_TYPE', 'OBJECT_DOMAIN'];
7807
8100
  const objects = list_utils_1.ListUtils.unwrapList(node.objects)
7808
- .map(obj => this.visit(obj, context))
8101
+ .map(obj => qualifiedKinds.includes(node.objtype) && obj.List
8102
+ ? list_utils_1.ListUtils.unwrapList(obj.List.items).map((item) => this.visit(item, context)).join('.')
8103
+ : this.visit(obj, context))
7809
8104
  .join(', ');
7810
8105
  output.push(objects);
7811
8106
  }
@@ -7825,6 +8120,9 @@ class Deparser {
7825
8120
  if (node.grant_option && node.is_grant) {
7826
8121
  output.push('WITH GRANT OPTION');
7827
8122
  }
8123
+ if (node.grantor) {
8124
+ output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
8125
+ }
7828
8126
  // Only add behavior clauses for REVOKE statements, not for GRANT statements in ALTER DEFAULT PRIVILEGES
7829
8127
  if (!node.is_grant) {
7830
8128
  if (node.behavior === 'DROP_CASCADE') {
@@ -7838,57 +8136,34 @@ class Deparser {
7838
8136
  }
7839
8137
  GrantRoleStmt(node, context) {
7840
8138
  const output = [];
7841
- // Check for inherit, admin, and set options first to place them correctly
7842
- let hasInheritOption = false;
7843
- let hasAdminOption = false;
7844
- let hasSetOption = false;
7845
- let inheritValue;
7846
- let adminValue;
7847
- let setValue;
7848
- if (node.opt && node.opt.length > 0) {
7849
- const options = list_utils_1.ListUtils.unwrapList(node.opt);
7850
- const inheritOption = options.find(opt => opt.DefElem && opt.DefElem.defname === 'inherit');
7851
- const adminOption = options.find(opt => (opt.String && opt.String.sval === 'admin') ||
7852
- (opt.DefElem && opt.DefElem.defname === 'admin'));
7853
- const setOption = options.find(opt => opt.DefElem && opt.DefElem.defname === 'set');
7854
- if (inheritOption && inheritOption.DefElem) {
7855
- hasInheritOption = true;
7856
- inheritValue = inheritOption.DefElem.arg?.Boolean?.boolval;
7857
- }
7858
- if (adminOption) {
7859
- hasAdminOption = true;
7860
- if (adminOption.DefElem && adminOption.DefElem.arg) {
7861
- adminValue = adminOption.DefElem.arg.Boolean?.boolval;
7862
- }
7863
- }
7864
- if (setOption && setOption.DefElem) {
7865
- hasSetOption = true;
7866
- setValue = setOption.DefElem.arg?.Boolean?.boolval;
7867
- }
7868
- }
8139
+ const options = list_utils_1.ListUtils.unwrapList(node.opt)
8140
+ .map(opt => opt.DefElem)
8141
+ .filter((opt) => !!opt && !!opt.defname);
8142
+ const optionValue = (opt) => {
8143
+ if (!opt.arg)
8144
+ return true;
8145
+ const argType = this.getNodeType(opt.arg);
8146
+ const argData = this.getNodeData(opt.arg);
8147
+ if (argType === 'Boolean')
8148
+ return argData.boolval !== false;
8149
+ if (argType === 'String')
8150
+ return argData.sval !== 'false';
8151
+ return true;
8152
+ };
8153
+ const roles = list_utils_1.ListUtils.unwrapList(node.granted_roles)
8154
+ .map(role => role.AccessPriv?.priv_name
8155
+ ? quotes_1.QuoteUtils.quoteIdentifier(role.AccessPriv.priv_name)
8156
+ : this.visit(role, context))
8157
+ .join(', ');
7869
8158
  if (node.is_grant) {
7870
- output.push('GRANT');
8159
+ output.push('GRANT', roles, 'TO');
7871
8160
  }
7872
8161
  else {
7873
8162
  output.push('REVOKE');
7874
- if (hasInheritOption) {
7875
- output.push('INHERIT OPTION FOR');
7876
- }
7877
- else if (hasAdminOption) {
7878
- output.push('ADMIN OPTION FOR');
8163
+ if (options.length > 0) {
8164
+ output.push(`${options[0].defname.toUpperCase()} OPTION FOR`);
7879
8165
  }
7880
- }
7881
- if (node.granted_roles && node.granted_roles.length > 0) {
7882
- const roles = list_utils_1.ListUtils.unwrapList(node.granted_roles)
7883
- .map(role => this.visit(role, context))
7884
- .join(', ');
7885
- output.push(roles);
7886
- }
7887
- if (node.is_grant) {
7888
- output.push('TO');
7889
- }
7890
- else {
7891
- output.push('FROM');
8166
+ output.push(roles, 'FROM');
7892
8167
  }
7893
8168
  if (node.grantee_roles && node.grantee_roles.length > 0) {
7894
8169
  const grantees = list_utils_1.ListUtils.unwrapList(node.grantee_roles)
@@ -7896,45 +8171,22 @@ class Deparser {
7896
8171
  .join(', ');
7897
8172
  output.push(grantees);
7898
8173
  }
7899
- if (node.is_grant) {
7900
- const withOptions = [];
7901
- if (hasAdminOption) {
7902
- if (adminValue === true) {
7903
- withOptions.push('ADMIN OPTION');
7904
- }
7905
- else if (adminValue === false) {
7906
- withOptions.push('ADMIN FALSE');
7907
- }
7908
- else {
7909
- withOptions.push('ADMIN OPTION');
7910
- }
7911
- }
7912
- if (hasInheritOption) {
7913
- if (inheritValue === true) {
7914
- withOptions.push('INHERIT OPTION');
7915
- }
7916
- else if (inheritValue === false) {
7917
- withOptions.push('INHERIT FALSE');
7918
- }
7919
- }
7920
- if (hasSetOption) {
7921
- if (setValue === true) {
7922
- withOptions.push('SET TRUE');
7923
- }
7924
- else if (setValue === false) {
7925
- withOptions.push('SET FALSE');
7926
- }
7927
- }
7928
- if (withOptions.length > 0) {
7929
- output.push('WITH', withOptions.join(', '));
7930
- }
8174
+ if (node.is_grant && options.length > 0) {
8175
+ const withOptions = options.map(opt => `${opt.defname.toUpperCase()} ${optionValue(opt) ? 'TRUE' : 'FALSE'}`);
8176
+ output.push('WITH', withOptions.join(', '));
8177
+ }
8178
+ if (node.grantor) {
8179
+ output.push('GRANTED BY', this.RoleSpec(node.grantor, context));
8180
+ }
8181
+ if (!node.is_grant && node.behavior === 'DROP_CASCADE') {
8182
+ output.push('CASCADE');
7931
8183
  }
7932
8184
  return output.join(' ');
7933
8185
  }
7934
8186
  SecLabelStmt(node, context) {
7935
8187
  const output = ['SECURITY LABEL'];
7936
8188
  if (node.provider) {
7937
- output.push('FOR', `"${node.provider}"`);
8189
+ output.push('FOR', quotes_1.QuoteUtils.quoteIdentifier(node.provider));
7938
8190
  }
7939
8191
  output.push('ON');
7940
8192
  if (node.objtype) {
@@ -7962,11 +8214,21 @@ class Deparser {
7962
8214
  }
7963
8215
  }
7964
8216
  if (node.object) {
7965
- output.push(this.visit(node.object, context));
8217
+ // Qualified names arrive as a List of String nodes and must be
8218
+ // dot-joined (visiting the List directly would comma-join them).
8219
+ if (typeof node.object === 'object' && 'List' in node.object) {
8220
+ const list = node.object.List;
8221
+ const objectParts = list_utils_1.ListUtils.unwrapList(list.items)
8222
+ .map(item => this.visit(item, context));
8223
+ output.push(objectParts.join('.'));
8224
+ }
8225
+ else {
8226
+ output.push(this.visit(node.object, context));
8227
+ }
7966
8228
  }
7967
8229
  output.push('IS');
7968
8230
  if (node.label) {
7969
- output.push(`'${node.label}'`);
8231
+ output.push(quotes_1.QuoteUtils.escape(node.label));
7970
8232
  }
7971
8233
  else {
7972
8234
  output.push('NULL');
@@ -8023,10 +8285,10 @@ class Deparser {
8023
8285
  output.push(conversionName);
8024
8286
  }
8025
8287
  if (node.for_encoding_name) {
8026
- output.push('FOR', `'${node.for_encoding_name}'`);
8288
+ output.push('FOR', quotes_1.QuoteUtils.escape(node.for_encoding_name));
8027
8289
  }
8028
8290
  if (node.to_encoding_name) {
8029
- output.push('TO', `'${node.to_encoding_name}'`);
8291
+ output.push('TO', quotes_1.QuoteUtils.escape(node.to_encoding_name));
8030
8292
  }
8031
8293
  if (node.func_name && node.func_name.length > 0) {
8032
8294
  output.push('FROM');
@@ -8087,7 +8349,7 @@ class Deparser {
8087
8349
  }
8088
8350
  output.push('LANGUAGE');
8089
8351
  if (node.plname) {
8090
- output.push(quote_utils_1.QuoteUtils.quote(node.plname));
8352
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.plname));
8091
8353
  }
8092
8354
  if (node.plhandler && node.plhandler.length > 0) {
8093
8355
  output.push('HANDLER');
@@ -8123,7 +8385,7 @@ class Deparser {
8123
8385
  }
8124
8386
  output.push('LANGUAGE');
8125
8387
  if (node.lang) {
8126
- output.push(quote_utils_1.QuoteUtils.quote(node.lang));
8388
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.lang));
8127
8389
  }
8128
8390
  output.push('(');
8129
8391
  const transforms = [];
@@ -8151,7 +8413,7 @@ class Deparser {
8151
8413
  }
8152
8414
  output.push('TRIGGER');
8153
8415
  if (node.trigname) {
8154
- output.push(quote_utils_1.QuoteUtils.quote(node.trigname));
8416
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.trigname));
8155
8417
  }
8156
8418
  if (context.isPretty()) {
8157
8419
  const components = [];
@@ -8189,6 +8451,9 @@ class Deparser {
8189
8451
  .join(' ');
8190
8452
  components.push(context.indent('REFERENCING ' + transitionClauses));
8191
8453
  }
8454
+ if (node.constrrel) {
8455
+ components.push(context.indent('FROM ' + this.RangeVar(node.constrrel, context)));
8456
+ }
8192
8457
  if (node.deferrable) {
8193
8458
  components.push(context.indent('DEFERRABLE'));
8194
8459
  }
@@ -8210,7 +8475,7 @@ class Deparser {
8210
8475
  const funcName = list_utils_1.ListUtils.unwrapList(node.funcname)
8211
8476
  .map(name => this.visit(name, context))
8212
8477
  .join('.');
8213
- executeStr += ' PROCEDURE ' + funcName;
8478
+ executeStr += ' FUNCTION ' + funcName;
8214
8479
  }
8215
8480
  if (node.args && node.args.length > 0) {
8216
8481
  const argContext = context.spawn('CreateTrigStmt', { isStringLiteral: true });
@@ -8315,14 +8580,14 @@ class Deparser {
8315
8580
  output.push('OLD TABLE AS');
8316
8581
  }
8317
8582
  if (node.name) {
8318
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
8583
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
8319
8584
  }
8320
8585
  return output.join(' ');
8321
8586
  }
8322
8587
  CreateEventTrigStmt(node, context) {
8323
8588
  const output = ['CREATE EVENT TRIGGER'];
8324
8589
  if (node.trigname) {
8325
- output.push(quote_utils_1.QuoteUtils.quote(node.trigname));
8590
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.trigname));
8326
8591
  }
8327
8592
  output.push('ON');
8328
8593
  if (node.eventname) {
@@ -8348,7 +8613,7 @@ class Deparser {
8348
8613
  AlterEventTrigStmt(node, context) {
8349
8614
  const output = ['ALTER EVENT TRIGGER'];
8350
8615
  if (node.trigname) {
8351
- output.push(quote_utils_1.QuoteUtils.quote(node.trigname));
8616
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.trigname));
8352
8617
  }
8353
8618
  if (node.tgenabled) {
8354
8619
  switch (node.tgenabled) {
@@ -8468,6 +8733,74 @@ class Deparser {
8468
8733
  .join(' ');
8469
8734
  output.push(whenClauses);
8470
8735
  }
8736
+ if (node.returningClause) {
8737
+ output.push(this.ReturningClause(node.returningClause, context));
8738
+ }
8739
+ return output.join(' ');
8740
+ }
8741
+ MergeSupportFunc(node, context) {
8742
+ return 'merge_action()';
8743
+ }
8744
+ MergeWhenClause(node, context) {
8745
+ const output = ['WHEN'];
8746
+ switch (node.matchKind) {
8747
+ case 'MERGE_WHEN_MATCHED':
8748
+ output.push('MATCHED');
8749
+ break;
8750
+ case 'MERGE_WHEN_NOT_MATCHED_BY_SOURCE':
8751
+ output.push('NOT MATCHED BY SOURCE');
8752
+ break;
8753
+ case 'MERGE_WHEN_NOT_MATCHED_BY_TARGET':
8754
+ output.push('NOT MATCHED');
8755
+ break;
8756
+ }
8757
+ if (node.condition) {
8758
+ output.push('AND');
8759
+ output.push(this.visit(node.condition, context));
8760
+ }
8761
+ output.push('THEN');
8762
+ switch (node.commandType) {
8763
+ case 'CMD_UPDATE': {
8764
+ output.push('UPDATE SET');
8765
+ if (node.targetList && node.targetList.length > 0) {
8766
+ output.push(this.formatUpdateAssignments(node.targetList, context));
8767
+ }
8768
+ break;
8769
+ }
8770
+ case 'CMD_INSERT': {
8771
+ output.push('INSERT');
8772
+ const targetList = list_utils_1.ListUtils.unwrapList(node.targetList);
8773
+ if (targetList.length > 0) {
8774
+ const columns = targetList
8775
+ .map(target => quotes_1.QuoteUtils.quoteIdentifier(target.ResTarget?.name))
8776
+ .join(', ');
8777
+ output.push(`(${columns})`);
8778
+ }
8779
+ if (node.override === 'OVERRIDING_USER_VALUE') {
8780
+ output.push('OVERRIDING USER VALUE');
8781
+ }
8782
+ else if (node.override === 'OVERRIDING_SYSTEM_VALUE') {
8783
+ output.push('OVERRIDING SYSTEM VALUE');
8784
+ }
8785
+ const values = list_utils_1.ListUtils.unwrapList(node.values);
8786
+ if (values.length > 0) {
8787
+ const valueItems = values
8788
+ .map(value => this.visit(value, context))
8789
+ .join(', ');
8790
+ output.push(`VALUES (${valueItems})`);
8791
+ }
8792
+ else {
8793
+ output.push('DEFAULT VALUES');
8794
+ }
8795
+ break;
8796
+ }
8797
+ case 'CMD_DELETE':
8798
+ output.push('DELETE');
8799
+ break;
8800
+ case 'CMD_NOTHING':
8801
+ output.push('DO NOTHING');
8802
+ break;
8803
+ }
8471
8804
  return output.join(' ');
8472
8805
  }
8473
8806
  AlterTableMoveAllStmt(node, context) {
@@ -8483,11 +8816,11 @@ class Deparser {
8483
8816
  }
8484
8817
  output.push('ALL', 'IN', 'TABLESPACE');
8485
8818
  if (node.orig_tablespacename) {
8486
- output.push(quote_utils_1.QuoteUtils.quote(node.orig_tablespacename));
8819
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.orig_tablespacename));
8487
8820
  }
8488
8821
  output.push('SET', 'TABLESPACE');
8489
8822
  if (node.new_tablespacename) {
8490
- output.push(quote_utils_1.QuoteUtils.quote(node.new_tablespacename));
8823
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.new_tablespacename));
8491
8824
  }
8492
8825
  if (node.nowait) {
8493
8826
  output.push('NOWAIT');
@@ -8511,10 +8844,10 @@ class Deparser {
8511
8844
  const sequenceName = [];
8512
8845
  const seq = node.sequence;
8513
8846
  if (seq.schemaname) {
8514
- sequenceName.push(quote_utils_1.QuoteUtils.quote(seq.schemaname));
8847
+ sequenceName.push(quotes_1.QuoteUtils.quoteIdentifier(seq.schemaname));
8515
8848
  }
8516
8849
  if (seq.relname) {
8517
- sequenceName.push(quote_utils_1.QuoteUtils.quote(seq.relname));
8850
+ sequenceName.push(quotes_1.QuoteUtils.quoteIdentifier(seq.relname));
8518
8851
  }
8519
8852
  output.push(sequenceName.join('.'));
8520
8853
  }
@@ -8548,10 +8881,10 @@ class Deparser {
8548
8881
  const sequenceName = [];
8549
8882
  const seq = node.sequence;
8550
8883
  if (seq.schemaname) {
8551
- sequenceName.push(quote_utils_1.QuoteUtils.quote(seq.schemaname));
8884
+ sequenceName.push(quotes_1.QuoteUtils.quoteIdentifier(seq.schemaname));
8552
8885
  }
8553
8886
  if (seq.relname) {
8554
- sequenceName.push(quote_utils_1.QuoteUtils.quote(seq.relname));
8887
+ sequenceName.push(quotes_1.QuoteUtils.quoteIdentifier(seq.relname));
8555
8888
  }
8556
8889
  output.push(sequenceName.join('.'));
8557
8890
  }
@@ -8836,6 +9169,13 @@ class Deparser {
8836
9169
  output.push('USING');
8837
9170
  output.push(node.into.accessMethod);
8838
9171
  }
9172
+ if (node.into && node.into.options && node.into.options.length > 0) {
9173
+ output.push('WITH');
9174
+ const options = list_utils_1.ListUtils.unwrapList(node.into.options)
9175
+ .map(option => this.visit(option, context.spawn('CreateStmt')))
9176
+ .join(', ');
9177
+ output.push(`(${options})`);
9178
+ }
8839
9179
  if (node.into && node.into.onCommit && node.into.onCommit !== 'ONCOMMIT_NOOP') {
8840
9180
  output.push('ON COMMIT');
8841
9181
  switch (node.into.onCommit) {
@@ -8850,17 +9190,13 @@ class Deparser {
8850
9190
  break;
8851
9191
  }
8852
9192
  }
9193
+ if (node.into && node.into.tableSpaceName) {
9194
+ output.push('TABLESPACE', quotes_1.QuoteUtils.quoteIdentifier(node.into.tableSpaceName));
9195
+ }
8853
9196
  output.push('AS');
8854
9197
  if (node.query) {
8855
9198
  output.push(this.visit(node.query, context));
8856
9199
  }
8857
- if (node.into && node.into.options && node.into.options.length > 0) {
8858
- output.push('WITH');
8859
- const options = list_utils_1.ListUtils.unwrapList(node.into.options)
8860
- .map(option => this.visit(option, context))
8861
- .join(', ');
8862
- output.push(`(${options})`);
8863
- }
8864
9200
  if (node.into && node.into.skipData) {
8865
9201
  output.push('WITH NO DATA');
8866
9202
  }
@@ -8898,7 +9234,7 @@ class Deparser {
8898
9234
  }
8899
9235
  aliasname(node, context) {
8900
9236
  if (typeof node === 'string') {
8901
- return quote_utils_1.QuoteUtils.quote(node);
9237
+ return quotes_1.QuoteUtils.quoteIdentifier(node);
8902
9238
  }
8903
9239
  return this.visit(node, context);
8904
9240
  }
@@ -8930,18 +9266,16 @@ class Deparser {
8930
9266
  const defName = defElem.defname;
8931
9267
  const defValue = defElem.arg;
8932
9268
  if (defName && defValue) {
8933
- let preservedDefName;
8934
- if (Deparser.needsQuotes(defName)) {
8935
- preservedDefName = `"${defName}"`;
8936
- }
8937
- else {
8938
- preservedDefName = this.preserveOperatorDefElemCase(defName);
8939
- }
9269
+ const quotedDefName = quotes_1.QuoteUtils.quoteIdentifier(defName);
9270
+ const preservedDefName = quotedDefName !== defName ? quotedDefName : this.preserveOperatorDefElemCase(defName);
8940
9271
  if ((defName.toLowerCase() === 'commutator' || defName.toLowerCase() === 'negator') && defValue.List) {
8941
9272
  const listItems = list_utils_1.ListUtils.unwrapList(defValue.List.items);
8942
9273
  if (listItems.length === 1 && listItems[0].String) {
8943
9274
  return `${preservedDefName} = ${listItems[0].String.sval}`;
8944
9275
  }
9276
+ if (listItems.length > 1 && listItems.every(item => item.String)) {
9277
+ return `${preservedDefName} = OPERATOR(${this.qualifiedOperatorName(listItems)})`;
9278
+ }
8945
9279
  }
8946
9280
  // For commutator/negator, we already handled them above
8947
9281
  if ((defName.toLowerCase() === 'commutator' || defName.toLowerCase() === 'negator')) {
@@ -8952,7 +9286,7 @@ class Deparser {
8952
9286
  else if (defName && !defValue) {
8953
9287
  // Handle boolean flags like HASHES, MERGES - preserve original case
8954
9288
  if (defName === 'Hashes' || defName === 'Merges') {
8955
- return `"${defName}"`;
9289
+ return quotes_1.QuoteUtils.quoteIdentifier(defName);
8956
9290
  }
8957
9291
  return this.preserveOperatorDefElemCase(defName).toUpperCase();
8958
9292
  }
@@ -9091,16 +9425,11 @@ class Deparser {
9091
9425
  const defName = defElem.defname;
9092
9426
  const defValue = defElem.arg;
9093
9427
  if (defName && defValue) {
9094
- let preservedDefName;
9095
- if (Deparser.needsQuotes(defName)) {
9096
- preservedDefName = `"${defName}"`;
9097
- }
9098
- else {
9099
- preservedDefName = defName;
9100
- }
9428
+ const quotedDefName = quotes_1.QuoteUtils.quoteIdentifier(defName);
9429
+ const preservedDefName = quotedDefName !== defName ? quotedDefName : defName;
9101
9430
  // Handle String arguments with single quotes for string literals
9102
9431
  if (defValue.String) {
9103
- return `${preservedDefName} = '${defValue.String.sval}'`;
9432
+ return `${preservedDefName} = ${quotes_1.QuoteUtils.escape(defValue.String.sval)}`;
9104
9433
  }
9105
9434
  return `${preservedDefName} = ${this.visit(defValue, context)}`;
9106
9435
  }
@@ -9223,7 +9552,7 @@ class Deparser {
9223
9552
  // For CREATE COLLATION, ensure String nodes are quoted as string literals
9224
9553
  let valueStr;
9225
9554
  if (defValue.String) {
9226
- valueStr = `'${defValue.String.sval}'`;
9555
+ valueStr = quotes_1.QuoteUtils.escape(defValue.String.sval);
9227
9556
  }
9228
9557
  else {
9229
9558
  valueStr = this.visit(defValue, context);
@@ -9252,7 +9581,7 @@ class Deparser {
9252
9581
  AlterDatabaseStmt(node, context) {
9253
9582
  const output = ['ALTER', 'DATABASE'];
9254
9583
  if (node.dbname) {
9255
- output.push(quote_utils_1.QuoteUtils.quote(node.dbname));
9584
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.dbname));
9256
9585
  }
9257
9586
  if (node.options && node.options.length > 0) {
9258
9587
  const options = list_utils_1.ListUtils.unwrapList(node.options).map(opt => this.visit(opt, context));
@@ -9263,7 +9592,7 @@ class Deparser {
9263
9592
  AlterDatabaseRefreshCollStmt(node, context) {
9264
9593
  const output = ['ALTER', 'DATABASE'];
9265
9594
  if (node.dbname) {
9266
- output.push(quote_utils_1.QuoteUtils.quote(node.dbname));
9595
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.dbname));
9267
9596
  }
9268
9597
  output.push('REFRESH', 'COLLATION', 'VERSION');
9269
9598
  return output.join(' ');
@@ -9271,7 +9600,7 @@ class Deparser {
9271
9600
  AlterDatabaseSetStmt(node, context) {
9272
9601
  const output = ['ALTER', 'DATABASE'];
9273
9602
  if (node.dbname) {
9274
- output.push(quote_utils_1.QuoteUtils.quote(node.dbname));
9603
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.dbname));
9275
9604
  }
9276
9605
  if (node.setstmt) {
9277
9606
  const setClause = this.VariableSetStmt(node.setstmt, context);
@@ -9282,7 +9611,7 @@ class Deparser {
9282
9611
  DeclareCursorStmt(node, context) {
9283
9612
  const output = ['DECLARE'];
9284
9613
  if (node.portalname) {
9285
- output.push(quote_utils_1.QuoteUtils.quote(node.portalname));
9614
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.portalname));
9286
9615
  }
9287
9616
  // Handle cursor options before CURSOR keyword
9288
9617
  const cursorOptions = [];
@@ -9324,7 +9653,7 @@ class Deparser {
9324
9653
  else if (node.pubobjtype === 'PUBLICATIONOBJ_TABLES_IN_SCHEMA') {
9325
9654
  output.push('TABLES IN SCHEMA');
9326
9655
  if (node.name) {
9327
- output.push(quote_utils_1.QuoteUtils.quote(node.name));
9656
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.name));
9328
9657
  }
9329
9658
  }
9330
9659
  else if (node.pubobjtype === 'PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA') {
@@ -9343,14 +9672,14 @@ class Deparser {
9343
9672
  }
9344
9673
  if (node.whereClause) {
9345
9674
  output.push('WHERE');
9346
- output.push(this.visit(node.whereClause, context));
9675
+ output.push(`(${this.visit(node.whereClause, context)})`);
9347
9676
  }
9348
9677
  return output.join(' ');
9349
9678
  }
9350
9679
  CreateAmStmt(node, context) {
9351
9680
  const output = ['CREATE', 'ACCESS', 'METHOD'];
9352
9681
  if (node.amname) {
9353
- output.push(quote_utils_1.QuoteUtils.quote(node.amname));
9682
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.amname));
9354
9683
  }
9355
9684
  output.push('TYPE');
9356
9685
  switch (node.amtype) {
@@ -9410,7 +9739,7 @@ class Deparser {
9410
9739
  }
9411
9740
  }
9412
9741
  if (node.tableSpaceName) {
9413
- output.push('TABLESPACE', quote_utils_1.QuoteUtils.quote(node.tableSpaceName));
9742
+ output.push('TABLESPACE', quotes_1.QuoteUtils.quoteIdentifier(node.tableSpaceName));
9414
9743
  }
9415
9744
  return output.join(' ');
9416
9745
  }
@@ -9490,6 +9819,23 @@ class Deparser {
9490
9819
  }
9491
9820
  return output.join(' ');
9492
9821
  }
9822
+ quoteDefElemArg(arg, argValue) {
9823
+ if (arg && this.getNodeType(arg) === 'String') {
9824
+ return quotes_1.QuoteUtils.escape(this.getNodeData(arg).sval || '');
9825
+ }
9826
+ return typeof argValue === 'string' && !argValue.startsWith("'")
9827
+ ? quotes_1.QuoteUtils.escape(argValue)
9828
+ : argValue;
9829
+ }
9830
+ formatOptionValue(arg, argValue) {
9831
+ if (arg && this.getNodeType(arg) === 'String') {
9832
+ const sval = this.getNodeData(arg).sval || '';
9833
+ return /^[a-z_][a-z0-9_]*$/.test(sval) && (0, quotes_1.keywordKindOf)(sval) === 'NO_KEYWORD'
9834
+ ? sval
9835
+ : quotes_1.QuoteUtils.escape(sval);
9836
+ }
9837
+ return argValue;
9838
+ }
9493
9839
  Var(node, context) {
9494
9840
  if (node.varno && node.varattno) {
9495
9841
  return `$${node.varno}.${node.varattno}`;
@@ -9529,7 +9875,7 @@ class Deparser {
9529
9875
  }
9530
9876
  if (node.rowexpr) {
9531
9877
  output.push(',');
9532
- output.push(`'${this.visit(node.rowexpr, context)}'`);
9878
+ output.push(this.visit(node.rowexpr, context));
9533
9879
  }
9534
9880
  if (node.colexprs && node.colexprs.length > 0) {
9535
9881
  output.push('COLUMNS');
@@ -9546,29 +9892,44 @@ class Deparser {
9546
9892
  if (node.lateral) {
9547
9893
  output.push('LATERAL');
9548
9894
  }
9549
- if (node.docexpr) {
9550
- output.push(this.visit(node.docexpr, context));
9895
+ const args = [];
9896
+ if (node.namespaces && node.namespaces.length > 0) {
9897
+ const namespaces = list_utils_1.ListUtils.unwrapList(node.namespaces).map(ns => {
9898
+ const target = this.getNodeData(ns);
9899
+ const uri = this.visit(target.val, context);
9900
+ return target.name ? `${uri} AS ${quotes_1.QuoteUtils.quoteIdentifier(target.name)}` : `DEFAULT ${uri}`;
9901
+ });
9902
+ args.push(`XMLNAMESPACES(${namespaces.join(', ')}),`);
9551
9903
  }
9552
9904
  if (node.rowexpr) {
9553
- output.push('PASSING');
9554
- output.push(this.visit(node.rowexpr, context));
9905
+ args.push(this.formatXmlTableExpr(node.rowexpr, context));
9906
+ }
9907
+ if (node.docexpr) {
9908
+ args.push('PASSING');
9909
+ args.push(this.formatXmlTableExpr(node.docexpr, context));
9555
9910
  }
9556
9911
  if (node.columns && node.columns.length > 0) {
9557
- output.push('COLUMNS');
9558
- const columns = list_utils_1.ListUtils.unwrapList(node.columns)
9912
+ args.push('COLUMNS');
9913
+ args.push(list_utils_1.ListUtils.unwrapList(node.columns)
9559
9914
  .map(col => this.visit(col, context))
9560
- .join(', ');
9561
- output.push(`(${columns})`);
9915
+ .join(', '));
9562
9916
  }
9917
+ output.push(`XMLTABLE(${args.join(' ')})`);
9563
9918
  if (node.alias) {
9564
9919
  output.push(this.Alias(node.alias, context));
9565
9920
  }
9566
9921
  return output.join(' ');
9567
9922
  }
9923
+ formatXmlTableExpr(expr, context) {
9924
+ const value = this.visit(expr, context);
9925
+ return ['A_Const', 'ColumnRef', 'ParamRef', 'FuncCall'].includes(this.getNodeType(expr))
9926
+ ? value
9927
+ : `(${value})`;
9928
+ }
9568
9929
  RangeTableFuncCol(node, context) {
9569
9930
  const output = [];
9570
9931
  if (node.colname) {
9571
- output.push(quote_utils_1.QuoteUtils.quote(node.colname));
9932
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.colname));
9572
9933
  }
9573
9934
  if (node.for_ordinality) {
9574
9935
  output.push('FOR ORDINALITY');
@@ -9578,32 +9939,193 @@ class Deparser {
9578
9939
  }
9579
9940
  if (node.colexpr) {
9580
9941
  output.push('PATH');
9581
- output.push(`'${this.visit(node.colexpr, context)}'`);
9942
+ output.push(this.visit(node.colexpr, context));
9582
9943
  }
9583
9944
  if (node.coldefexpr) {
9584
9945
  output.push('DEFAULT');
9585
9946
  output.push(this.visit(node.coldefexpr, context));
9586
9947
  }
9948
+ if (node.is_not_null) {
9949
+ output.push('NOT NULL');
9950
+ }
9587
9951
  return output.join(' ');
9588
9952
  }
9953
+ formatJsonFormat(format) {
9954
+ if (!format || !format.format_type || format.format_type === 'JS_FORMAT_DEFAULT') {
9955
+ return null;
9956
+ }
9957
+ const parts = ['FORMAT JSON'];
9958
+ switch (format.encoding) {
9959
+ case 'JS_ENC_UTF8':
9960
+ parts.push('ENCODING UTF8');
9961
+ break;
9962
+ case 'JS_ENC_UTF16':
9963
+ parts.push('ENCODING UTF16');
9964
+ break;
9965
+ case 'JS_ENC_UTF32':
9966
+ parts.push('ENCODING UTF32');
9967
+ break;
9968
+ }
9969
+ return parts.join(' ');
9970
+ }
9971
+ formatJsonOutput(output, context) {
9972
+ if (!output || !output.typeName)
9973
+ return null;
9974
+ const parts = ['RETURNING', this.TypeName(output.typeName, context)];
9975
+ const format = this.formatJsonFormat(output.returning?.format);
9976
+ if (format)
9977
+ parts.push(format);
9978
+ return parts.join(' ');
9979
+ }
9980
+ formatJsonNullClause(absentOnNull) {
9981
+ return absentOnNull ? 'ABSENT ON NULL' : 'NULL ON NULL';
9982
+ }
9983
+ formatJsonUniqueClause(unique) {
9984
+ return unique ? 'WITH UNIQUE KEYS' : null;
9985
+ }
9986
+ JsonValueExpr(node, context) {
9987
+ const parts = [];
9988
+ if (node.raw_expr) {
9989
+ parts.push(this.visit(node.raw_expr, context));
9990
+ }
9991
+ const format = this.formatJsonFormat(node.format);
9992
+ if (format)
9993
+ parts.push(format);
9994
+ return parts.join(' ');
9995
+ }
9996
+ JsonKeyValue(node, context) {
9997
+ const key = node.key ? this.visit(node.key, context) : '';
9998
+ const value = node.value ? this.JsonValueExpr(node.value, context) : '';
9999
+ return `${key} : ${value}`;
10000
+ }
10001
+ JsonObjectConstructor(node, context) {
10002
+ const args = [];
10003
+ if (node.exprs && node.exprs.length > 0) {
10004
+ args.push(list_utils_1.ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
10005
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10006
+ const unique = this.formatJsonUniqueClause(node.unique);
10007
+ if (unique)
10008
+ args.push(unique);
10009
+ }
10010
+ const output = this.formatJsonOutput(node.output, context);
10011
+ if (output)
10012
+ args.push(output);
10013
+ return `JSON_OBJECT(${args.join(' ')})`;
10014
+ }
10015
+ JsonArrayConstructor(node, context) {
10016
+ const args = [];
10017
+ if (node.exprs && node.exprs.length > 0) {
10018
+ args.push(list_utils_1.ListUtils.unwrapList(node.exprs).map(e => this.visit(e, context)).join(', '));
10019
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10020
+ }
10021
+ const output = this.formatJsonOutput(node.output, context);
10022
+ if (output)
10023
+ args.push(output);
10024
+ return `JSON_ARRAY(${args.join(' ')})`;
10025
+ }
9589
10026
  JsonArrayQueryConstructor(node, context) {
9590
- const output = ['JSON_ARRAYAGG'];
10027
+ const args = [];
9591
10028
  if (node.query) {
9592
- output.push(`(${this.visit(node.query, context)})`);
10029
+ args.push(this.visit(node.query, context));
10030
+ }
10031
+ const format = this.formatJsonFormat(node.format);
10032
+ if (format)
10033
+ args.push(format);
10034
+ const output = this.formatJsonOutput(node.output, context);
10035
+ if (output)
10036
+ args.push(output);
10037
+ return `JSON_ARRAY(${args.join(' ')})`;
10038
+ }
10039
+ formatJsonAggSuffix(ctor, context) {
10040
+ if (!ctor)
10041
+ return '';
10042
+ let result = '';
10043
+ if (ctor.agg_filter) {
10044
+ result += ` FILTER (WHERE ${this.visit(ctor.agg_filter, context)})`;
9593
10045
  }
9594
- if (node.format) {
9595
- output.push('FORMAT JSON');
10046
+ if (ctor.over) {
10047
+ result += this.formatOverClause(ctor.over, context);
9596
10048
  }
9597
- if (node.output) {
9598
- output.push('RETURNING TEXT');
10049
+ return result;
10050
+ }
10051
+ JsonObjectAgg(node, context) {
10052
+ const args = [];
10053
+ if (node.arg) {
10054
+ args.push(this.JsonKeyValue(node.arg, context));
10055
+ }
10056
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10057
+ const unique = this.formatJsonUniqueClause(node.unique);
10058
+ if (unique)
10059
+ args.push(unique);
10060
+ const output = this.formatJsonOutput(node.constructor?.output, context);
10061
+ if (output)
10062
+ args.push(output);
10063
+ return `JSON_OBJECTAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
10064
+ }
10065
+ JsonArrayAgg(node, context) {
10066
+ const args = [];
10067
+ if (node.arg) {
10068
+ args.push(this.JsonValueExpr(node.arg, context));
9599
10069
  }
9600
- if (node.absent_on_null) {
9601
- output.push('ABSENT ON NULL');
10070
+ const aggOrder = node.constructor?.agg_order;
10071
+ if (aggOrder && aggOrder.length > 0) {
10072
+ const orderItems = list_utils_1.ListUtils.unwrapList(aggOrder).map(item => this.visit(item, context));
10073
+ args.push(`ORDER BY ${orderItems.join(', ')}`);
9602
10074
  }
9603
- else {
9604
- output.push('NULL ON NULL');
10075
+ args.push(this.formatJsonNullClause(node.absent_on_null));
10076
+ const output = this.formatJsonOutput(node.constructor?.output, context);
10077
+ if (output)
10078
+ args.push(output);
10079
+ return `JSON_ARRAYAGG(${args.join(' ')})${this.formatJsonAggSuffix(node.constructor, context)}`;
10080
+ }
10081
+ JsonParseExpr(node, context) {
10082
+ const args = [];
10083
+ if (node.expr) {
10084
+ args.push(this.JsonValueExpr(node.expr, context));
9605
10085
  }
9606
- return output.join(' ');
10086
+ const unique = this.formatJsonUniqueClause(node.unique_keys);
10087
+ if (unique)
10088
+ args.push(unique);
10089
+ return `JSON(${args.join(' ')})`;
10090
+ }
10091
+ JsonScalarExpr(node, context) {
10092
+ const expr = node.expr ? this.visit(node.expr, context) : '';
10093
+ return `JSON_SCALAR(${expr})`;
10094
+ }
10095
+ JsonSerializeExpr(node, context) {
10096
+ const args = [];
10097
+ if (node.expr) {
10098
+ args.push(this.JsonValueExpr(node.expr, context));
10099
+ }
10100
+ const output = this.formatJsonOutput(node.output, context);
10101
+ if (output)
10102
+ args.push(output);
10103
+ return `JSON_SERIALIZE(${args.join(' ')})`;
10104
+ }
10105
+ JsonIsPredicate(node, context) {
10106
+ const parts = [];
10107
+ if (node.expr) {
10108
+ parts.push(this.visit(node.expr, context));
10109
+ }
10110
+ const format = this.formatJsonFormat(node.format);
10111
+ if (format)
10112
+ parts.push(format);
10113
+ parts.push('IS JSON');
10114
+ switch (node.item_type) {
10115
+ case 'JS_TYPE_OBJECT':
10116
+ parts.push('OBJECT');
10117
+ break;
10118
+ case 'JS_TYPE_ARRAY':
10119
+ parts.push('ARRAY');
10120
+ break;
10121
+ case 'JS_TYPE_SCALAR':
10122
+ parts.push('SCALAR');
10123
+ break;
10124
+ }
10125
+ if (node.unique_keys) {
10126
+ parts.push('WITH UNIQUE KEYS');
10127
+ }
10128
+ return parts.join(' ');
9607
10129
  }
9608
10130
  RangeFunction(node, context) {
9609
10131
  const output = [];
@@ -9714,10 +10236,10 @@ class Deparser {
9714
10236
  if (node.op === 'IS_XMLPI') {
9715
10237
  if (node.name && node.args && node.args.length > 0) {
9716
10238
  const argStrs = list_utils_1.ListUtils.unwrapList(node.args).map(arg => this.visit(arg, context));
9717
- return `xmlpi(name ${quote_utils_1.QuoteUtils.quote(node.name)}, ${argStrs.join(', ')})`;
10239
+ return `xmlpi(name ${quotes_1.QuoteUtils.quoteIdentifier(node.name)}, ${argStrs.join(', ')})`;
9718
10240
  }
9719
10241
  else if (node.name) {
9720
- return `xmlpi(name ${quote_utils_1.QuoteUtils.quote(node.name)})`;
10242
+ return `xmlpi(name ${quotes_1.QuoteUtils.quoteIdentifier(node.name)})`;
9721
10243
  }
9722
10244
  else {
9723
10245
  return 'XMLPI()';
@@ -9732,7 +10254,7 @@ class Deparser {
9732
10254
  output.push('XMLELEMENT');
9733
10255
  const elementParts = [];
9734
10256
  if (node.name) {
9735
- elementParts.push(`NAME ${quote_utils_1.QuoteUtils.quote(node.name)}`);
10257
+ elementParts.push(`NAME ${quotes_1.QuoteUtils.quoteIdentifier(node.name)}`);
9736
10258
  }
9737
10259
  if (node.named_args && node.named_args.length > 0) {
9738
10260
  const namedArgStrs = list_utils_1.ListUtils.unwrapList(node.named_args).map(arg => this.visit(arg, context));
@@ -9797,6 +10319,7 @@ class Deparser {
9797
10319
  rootParts.push('STANDALONE NO VALUE');
9798
10320
  }
9799
10321
  else if (standaloneArg.A_Const.ival.ival === 3) {
10322
+ // STANDALONE omitted
9800
10323
  }
9801
10324
  else if (Object.keys(standaloneArg.A_Const.ival).length === 0) {
9802
10325
  rootParts.push('STANDALONE YES');
@@ -9832,7 +10355,7 @@ class Deparser {
9832
10355
  // Handle name and args for operations that don't have special handling
9833
10356
  if (node.op !== 'IS_XMLELEMENT' && node.op !== 'IS_XMLPARSE' && node.op !== 'IS_XMLROOT' && node.op !== 'IS_DOCUMENT') {
9834
10357
  if (node.name) {
9835
- const quotedName = quote_utils_1.QuoteUtils.quote(node.name);
10358
+ const quotedName = quotes_1.QuoteUtils.quoteIdentifier(node.name);
9836
10359
  output.push(`NAME ${quotedName}`);
9837
10360
  }
9838
10361
  if (node.args && node.args.length > 0) {
@@ -9853,26 +10376,26 @@ class Deparser {
9853
10376
  }
9854
10377
  schemaname(node, context) {
9855
10378
  if (typeof node === 'string') {
9856
- return quote_utils_1.QuoteUtils.quote(node);
10379
+ return quotes_1.QuoteUtils.quoteIdentifier(node);
9857
10380
  }
9858
10381
  if (node && node.String && node.String.sval) {
9859
- return quote_utils_1.QuoteUtils.quote(node.String.sval);
10382
+ return quotes_1.QuoteUtils.quoteIdentifier(node.String.sval);
9860
10383
  }
9861
10384
  // Handle other node types without recursion
9862
10385
  if (node && typeof node === 'object') {
9863
10386
  if (node.sval !== undefined) {
9864
- return quote_utils_1.QuoteUtils.quote(node.sval);
10387
+ return quotes_1.QuoteUtils.quoteIdentifier(node.sval);
9865
10388
  }
9866
10389
  // Handle List nodes that might contain schema names
9867
10390
  if (node.List && Array.isArray(node.List.items)) {
9868
10391
  const items = node.List.items;
9869
10392
  if (items.length > 0 && items[0].String && items[0].String.sval) {
9870
- return quote_utils_1.QuoteUtils.quote(items[0].String.sval);
10393
+ return quotes_1.QuoteUtils.quoteIdentifier(items[0].String.sval);
9871
10394
  }
9872
10395
  }
9873
10396
  // For other complex nodes, try to extract string value without recursion
9874
10397
  if (node.val !== undefined) {
9875
- return quote_utils_1.QuoteUtils.quote(node.val);
10398
+ return quotes_1.QuoteUtils.quoteIdentifier(node.val);
9876
10399
  }
9877
10400
  return '';
9878
10401
  }
@@ -9935,7 +10458,7 @@ class Deparser {
9935
10458
  }
9936
10459
  output.push('RULE');
9937
10460
  if (node.rulename) {
9938
- output.push(quote_utils_1.QuoteUtils.quote(node.rulename));
10461
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.rulename));
9939
10462
  }
9940
10463
  output.push('AS ON');
9941
10464
  if (node.event) {
@@ -10002,42 +10525,42 @@ class Deparser {
10002
10525
  }
10003
10526
  relname(node, context) {
10004
10527
  if (typeof node === 'string') {
10005
- return quote_utils_1.QuoteUtils.quote(node);
10528
+ return quotes_1.QuoteUtils.quoteIdentifier(node);
10006
10529
  }
10007
10530
  if (node && node.String && node.String.sval) {
10008
- return quote_utils_1.QuoteUtils.quote(node.String.sval);
10531
+ return quotes_1.QuoteUtils.quoteIdentifier(node.String.sval);
10009
10532
  }
10010
10533
  if (node && typeof node === 'object' && node.relname) {
10011
- return quote_utils_1.QuoteUtils.quote(node.relname);
10534
+ return quotes_1.QuoteUtils.quoteIdentifier(node.relname);
10012
10535
  }
10013
10536
  return this.visit(node, context);
10014
10537
  }
10015
10538
  rel(node, context) {
10016
10539
  if (typeof node === 'string') {
10017
- return quote_utils_1.QuoteUtils.quote(node);
10540
+ return quotes_1.QuoteUtils.quoteIdentifier(node);
10018
10541
  }
10019
10542
  if (node && node.String && node.String.sval) {
10020
- return quote_utils_1.QuoteUtils.quote(node.String.sval);
10543
+ return quotes_1.QuoteUtils.quoteIdentifier(node.String.sval);
10021
10544
  }
10022
10545
  if (node && node.RangeVar) {
10023
10546
  return this.RangeVar(node.RangeVar, context);
10024
10547
  }
10025
10548
  if (node && typeof node === 'object' && node.relname) {
10026
- return quote_utils_1.QuoteUtils.quote(node.relname);
10549
+ return quotes_1.QuoteUtils.quoteIdentifier(node.relname);
10027
10550
  }
10028
10551
  return this.visit(node, context);
10029
10552
  }
10030
10553
  objname(node, context) {
10031
10554
  if (typeof node === 'string') {
10032
- return quote_utils_1.QuoteUtils.quote(node);
10555
+ return quotes_1.QuoteUtils.quoteIdentifier(node);
10033
10556
  }
10034
10557
  if (node && node.String && node.String.sval) {
10035
- return quote_utils_1.QuoteUtils.quote(node.String.sval);
10558
+ return quotes_1.QuoteUtils.quoteIdentifier(node.String.sval);
10036
10559
  }
10037
10560
  if (Array.isArray(node)) {
10038
10561
  const parts = node.map(part => {
10039
10562
  if (part && part.String && part.String.sval) {
10040
- return quote_utils_1.QuoteUtils.quote(part.String.sval);
10563
+ return quotes_1.QuoteUtils.quoteIdentifier(part.String.sval);
10041
10564
  }
10042
10565
  return this.visit(part, context);
10043
10566
  });
@@ -10108,7 +10631,7 @@ class Deparser {
10108
10631
  CurrentOfExpr(node, context) {
10109
10632
  const output = ['CURRENT OF'];
10110
10633
  if (node.cursor_name) {
10111
- output.push(quote_utils_1.QuoteUtils.quote(node.cursor_name));
10634
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.cursor_name));
10112
10635
  }
10113
10636
  if (node.cursor_param > 0) {
10114
10637
  output.push(`$${node.cursor_param}`);
@@ -10171,66 +10694,12 @@ class Deparser {
10171
10694
  }
10172
10695
  AlterObjectSchemaStmt(node, context) {
10173
10696
  const output = ['ALTER'];
10174
- switch (node.objectType) {
10175
- case 'OBJECT_TABLE':
10176
- output.push('TABLE');
10177
- break;
10178
- case 'OBJECT_VIEW':
10179
- output.push('VIEW');
10180
- break;
10181
- case 'OBJECT_FUNCTION':
10182
- output.push('FUNCTION');
10183
- break;
10184
- case 'OBJECT_TYPE':
10185
- output.push('TYPE');
10186
- break;
10187
- case 'OBJECT_DOMAIN':
10188
- output.push('DOMAIN');
10189
- break;
10190
- case 'OBJECT_SEQUENCE':
10191
- output.push('SEQUENCE');
10192
- break;
10193
- case 'OBJECT_OPCLASS':
10194
- output.push('OPERATOR CLASS');
10195
- break;
10196
- case 'OBJECT_OPFAMILY':
10197
- output.push('OPERATOR FAMILY');
10198
- break;
10199
- case 'OBJECT_OPERATOR':
10200
- output.push('OPERATOR');
10201
- break;
10202
- case 'OBJECT_TYPE':
10203
- output.push('TYPE');
10204
- break;
10205
- case 'OBJECT_COLLATION':
10206
- output.push('COLLATION');
10207
- break;
10208
- case 'OBJECT_CONVERSION':
10209
- output.push('CONVERSION');
10210
- break;
10211
- case 'OBJECT_TSPARSER':
10212
- output.push('TEXT SEARCH PARSER');
10213
- break;
10214
- case 'OBJECT_TSCONFIGURATION':
10215
- output.push('TEXT SEARCH CONFIGURATION');
10216
- break;
10217
- case 'OBJECT_TSTEMPLATE':
10218
- output.push('TEXT SEARCH TEMPLATE');
10219
- break;
10220
- case 'OBJECT_TSDICTIONARY':
10221
- output.push('TEXT SEARCH DICTIONARY');
10222
- break;
10223
- case 'OBJECT_AGGREGATE':
10224
- output.push('AGGREGATE');
10225
- break;
10226
- case 'OBJECT_FOREIGN_TABLE':
10227
- output.push('FOREIGN TABLE');
10228
- break;
10229
- case 'OBJECT_MATVIEW':
10230
- output.push('MATERIALIZED VIEW');
10231
- break;
10232
- default:
10233
- output.push(node.objectType.toString());
10697
+ try {
10698
+ output.push(this.getObjectTypeKeyword(node.objectType));
10699
+ }
10700
+ catch {
10701
+ // Fallback to objectType string if not supported
10702
+ output.push(node.objectType.toString());
10234
10703
  }
10235
10704
  if (node.missing_ok) {
10236
10705
  output.push('IF EXISTS');
@@ -10245,7 +10714,7 @@ class Deparser {
10245
10714
  if (items.length === 2) {
10246
10715
  const schemaName = items[0].String?.sval || '';
10247
10716
  const domainName = items[1].String?.sval || '';
10248
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(domainName)}`);
10717
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, domainName));
10249
10718
  }
10250
10719
  else {
10251
10720
  output.push(this.visit(node.object, context));
@@ -10257,7 +10726,7 @@ class Deparser {
10257
10726
  if (items.length === 2) {
10258
10727
  const schemaName = items[0].String?.sval || '';
10259
10728
  const typeName = items[1].String?.sval || '';
10260
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(typeName)}`);
10729
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, typeName));
10261
10730
  }
10262
10731
  else {
10263
10732
  output.push(this.visit(node.object, context));
@@ -10269,7 +10738,7 @@ class Deparser {
10269
10738
  if (items.length === 2) {
10270
10739
  const schemaName = items[0].String?.sval || '';
10271
10740
  const conversionName = items[1].String?.sval || '';
10272
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(conversionName)}`);
10741
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, conversionName));
10273
10742
  }
10274
10743
  else {
10275
10744
  output.push(this.visit(node.object, context));
@@ -10281,7 +10750,7 @@ class Deparser {
10281
10750
  if (items.length === 2) {
10282
10751
  const schemaName = items[0].String?.sval || '';
10283
10752
  const parserName = items[1].String?.sval || '';
10284
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(parserName)}`);
10753
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, parserName));
10285
10754
  }
10286
10755
  else {
10287
10756
  output.push(this.visit(node.object, context));
@@ -10293,7 +10762,7 @@ class Deparser {
10293
10762
  if (items.length === 2) {
10294
10763
  const schemaName = items[0].String?.sval || '';
10295
10764
  const configName = items[1].String?.sval || '';
10296
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(configName)}`);
10765
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, configName));
10297
10766
  }
10298
10767
  else {
10299
10768
  output.push(this.visit(node.object, context));
@@ -10305,7 +10774,7 @@ class Deparser {
10305
10774
  if (items.length === 2) {
10306
10775
  const schemaName = items[0].String?.sval || '';
10307
10776
  const templateName = items[1].String?.sval || '';
10308
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(templateName)}`);
10777
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, templateName));
10309
10778
  }
10310
10779
  else {
10311
10780
  output.push(this.visit(node.object, context));
@@ -10317,7 +10786,7 @@ class Deparser {
10317
10786
  if (items.length === 2) {
10318
10787
  const schemaName = items[0].String?.sval || '';
10319
10788
  const dictionaryName = items[1].String?.sval || '';
10320
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(dictionaryName)}`);
10789
+ output.push(quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, dictionaryName));
10321
10790
  }
10322
10791
  else {
10323
10792
  output.push(this.visit(node.object, context));
@@ -10329,13 +10798,13 @@ class Deparser {
10329
10798
  if (items.length === 2) {
10330
10799
  const accessMethod = items[0].String?.sval || '';
10331
10800
  const opClassName = items[1].String?.sval || '';
10332
- output.push(`${quote_utils_1.QuoteUtils.quote(opClassName)} USING ${accessMethod}`);
10801
+ output.push(`${quotes_1.QuoteUtils.quoteIdentifier(opClassName)} USING ${accessMethod}`);
10333
10802
  }
10334
10803
  else if (items.length === 3) {
10335
10804
  const accessMethod = items[0].String?.sval || '';
10336
10805
  const schemaName = items[1].String?.sval || '';
10337
10806
  const opClassName = items[2].String?.sval || '';
10338
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(opClassName)} USING ${accessMethod}`);
10807
+ output.push(`${quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, opClassName)} USING ${accessMethod}`);
10339
10808
  }
10340
10809
  else {
10341
10810
  output.push(this.visit(node.object, context));
@@ -10347,13 +10816,13 @@ class Deparser {
10347
10816
  if (items.length === 2) {
10348
10817
  const accessMethod = items[0].String?.sval || '';
10349
10818
  const opFamilyName = items[1].String?.sval || '';
10350
- output.push(`${quote_utils_1.QuoteUtils.quote(opFamilyName)} USING ${accessMethod}`);
10819
+ output.push(`${quotes_1.QuoteUtils.quoteIdentifier(opFamilyName)} USING ${accessMethod}`);
10351
10820
  }
10352
10821
  else if (items.length === 3) {
10353
10822
  const accessMethod = items[0].String?.sval || '';
10354
10823
  const schemaName = items[1].String?.sval || '';
10355
10824
  const opFamilyName = items[2].String?.sval || '';
10356
- output.push(`${quote_utils_1.QuoteUtils.quote(schemaName)}.${quote_utils_1.QuoteUtils.quote(opFamilyName)} USING ${accessMethod}`);
10825
+ output.push(`${quotes_1.QuoteUtils.quoteQualifiedIdentifier(schemaName, opFamilyName)} USING ${accessMethod}`);
10357
10826
  }
10358
10827
  else {
10359
10828
  output.push(this.visit(node.object, context));
@@ -10365,7 +10834,7 @@ class Deparser {
10365
10834
  }
10366
10835
  output.push('SET SCHEMA');
10367
10836
  if (node.newschema) {
10368
- output.push(quote_utils_1.QuoteUtils.quote(node.newschema));
10837
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.newschema));
10369
10838
  }
10370
10839
  return output.join(' ');
10371
10840
  }
@@ -10434,7 +10903,7 @@ class Deparser {
10434
10903
  }
10435
10904
  if (node.servername) {
10436
10905
  output.push('SERVER');
10437
- output.push(quote_utils_1.QuoteUtils.quote(node.servername));
10906
+ output.push(quotes_1.QuoteUtils.quoteIdentifier(node.servername));
10438
10907
  }
10439
10908
  if (node.options && node.options.length > 0) {
10440
10909
  const foreignTableContext = context.spawn('CreateForeignTableStmt');
@@ -10443,9 +10912,5 @@ class Deparser {
10443
10912
  }
10444
10913
  return output.join(' ');
10445
10914
  }
10446
- containsMultilineStringLiteral(content) {
10447
- const stringLiteralRegex = /'[^']*\n[^']*'/g;
10448
- return stringLiteralRegex.test(content);
10449
- }
10450
10915
  }
10451
10916
  exports.Deparser = Deparser;