relq 1.0.5 → 1.0.7

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.
Files changed (86) hide show
  1. package/dist/cjs/cli/commands/add.cjs +257 -17
  2. package/dist/cjs/cli/commands/commit.cjs +13 -2
  3. package/dist/cjs/cli/commands/export.cjs +25 -19
  4. package/dist/cjs/cli/commands/import.cjs +219 -100
  5. package/dist/cjs/cli/commands/init.cjs +86 -14
  6. package/dist/cjs/cli/commands/pull.cjs +104 -23
  7. package/dist/cjs/cli/commands/push.cjs +38 -3
  8. package/dist/cjs/cli/index.cjs +9 -1
  9. package/dist/cjs/cli/utils/ast/codegen/builder.cjs +297 -0
  10. package/dist/cjs/cli/utils/ast/codegen/constraints.cjs +185 -0
  11. package/dist/cjs/cli/utils/ast/codegen/defaults.cjs +311 -0
  12. package/dist/cjs/cli/utils/ast/codegen/index.cjs +24 -0
  13. package/dist/cjs/cli/utils/ast/codegen/type-map.cjs +116 -0
  14. package/dist/cjs/cli/utils/ast/codegen/utils.cjs +69 -0
  15. package/dist/cjs/cli/utils/ast/index.cjs +19 -0
  16. package/dist/cjs/cli/utils/ast/transformer/helpers.cjs +154 -0
  17. package/dist/cjs/cli/utils/ast/transformer/index.cjs +25 -0
  18. package/dist/cjs/cli/utils/ast/types.cjs +2 -0
  19. package/dist/cjs/cli/utils/ast-codegen.cjs +949 -0
  20. package/dist/cjs/cli/utils/ast-transformer.cjs +916 -0
  21. package/dist/cjs/cli/utils/change-tracker.cjs +50 -1
  22. package/dist/cjs/cli/utils/cli-utils.cjs +151 -0
  23. package/dist/cjs/cli/utils/fast-introspect.cjs +149 -23
  24. package/dist/cjs/cli/utils/pg-parser.cjs +1 -0
  25. package/dist/cjs/cli/utils/repo-manager.cjs +121 -4
  26. package/dist/cjs/cli/utils/schema-comparator.cjs +98 -14
  27. package/dist/cjs/cli/utils/schema-introspect.cjs +56 -19
  28. package/dist/cjs/cli/utils/snapshot-manager.cjs +0 -1
  29. package/dist/cjs/cli/utils/sql-generator.cjs +353 -64
  30. package/dist/cjs/cli/utils/type-generator.cjs +114 -15
  31. package/dist/cjs/config/config.cjs +29 -10
  32. package/dist/cjs/core/relq-client.cjs +22 -6
  33. package/dist/cjs/schema-definition/column-types.cjs +149 -13
  34. package/dist/cjs/schema-definition/defaults.cjs +72 -0
  35. package/dist/cjs/schema-definition/index.cjs +15 -1
  36. package/dist/cjs/schema-definition/introspection.cjs +7 -3
  37. package/dist/cjs/schema-definition/pg-relations.cjs +169 -0
  38. package/dist/cjs/schema-definition/pg-view.cjs +30 -0
  39. package/dist/cjs/schema-definition/table-definition.cjs +110 -4
  40. package/dist/cjs/types/config-types.cjs +13 -4
  41. package/dist/cjs/utils/aws-dsql.cjs +177 -0
  42. package/dist/config.d.ts +147 -2
  43. package/dist/esm/cli/commands/add.js +255 -18
  44. package/dist/esm/cli/commands/commit.js +13 -2
  45. package/dist/esm/cli/commands/export.js +25 -19
  46. package/dist/esm/cli/commands/import.js +221 -102
  47. package/dist/esm/cli/commands/init.js +86 -14
  48. package/dist/esm/cli/commands/pull.js +106 -25
  49. package/dist/esm/cli/commands/push.js +39 -4
  50. package/dist/esm/cli/index.js +9 -1
  51. package/dist/esm/cli/utils/ast/codegen/builder.js +291 -0
  52. package/dist/esm/cli/utils/ast/codegen/constraints.js +176 -0
  53. package/dist/esm/cli/utils/ast/codegen/defaults.js +305 -0
  54. package/dist/esm/cli/utils/ast/codegen/index.js +6 -0
  55. package/dist/esm/cli/utils/ast/codegen/type-map.js +111 -0
  56. package/dist/esm/cli/utils/ast/codegen/utils.js +60 -0
  57. package/dist/esm/cli/utils/ast/index.js +3 -0
  58. package/dist/esm/cli/utils/ast/transformer/helpers.js +141 -0
  59. package/dist/esm/cli/utils/ast/transformer/index.js +2 -0
  60. package/dist/esm/cli/utils/ast/types.js +1 -0
  61. package/dist/esm/cli/utils/ast-codegen.js +945 -0
  62. package/dist/esm/cli/utils/ast-transformer.js +907 -0
  63. package/dist/esm/cli/utils/change-tracker.js +50 -1
  64. package/dist/esm/cli/utils/cli-utils.js +147 -0
  65. package/dist/esm/cli/utils/fast-introspect.js +149 -23
  66. package/dist/esm/cli/utils/pg-parser.js +1 -0
  67. package/dist/esm/cli/utils/repo-manager.js +114 -4
  68. package/dist/esm/cli/utils/schema-comparator.js +98 -14
  69. package/dist/esm/cli/utils/schema-introspect.js +56 -19
  70. package/dist/esm/cli/utils/snapshot-manager.js +0 -1
  71. package/dist/esm/cli/utils/sql-generator.js +353 -64
  72. package/dist/esm/cli/utils/type-generator.js +114 -15
  73. package/dist/esm/config/config.js +29 -10
  74. package/dist/esm/core/relq-client.js +23 -7
  75. package/dist/esm/schema-definition/column-types.js +146 -12
  76. package/dist/esm/schema-definition/defaults.js +69 -0
  77. package/dist/esm/schema-definition/index.js +3 -0
  78. package/dist/esm/schema-definition/introspection.js +7 -3
  79. package/dist/esm/schema-definition/pg-relations.js +161 -0
  80. package/dist/esm/schema-definition/pg-view.js +24 -0
  81. package/dist/esm/schema-definition/table-definition.js +110 -4
  82. package/dist/esm/types/config-types.js +12 -4
  83. package/dist/esm/utils/aws-dsql.js +139 -0
  84. package/dist/index.d.ts +159 -1
  85. package/dist/schema-builder.d.ts +1314 -32
  86. package/package.json +1 -1
@@ -38,7 +38,8 @@ const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
39
  const config_loader_1 = require("../utils/config-loader.cjs");
40
40
  const fast_introspect_1 = require("../utils/fast-introspect.cjs");
41
- const type_generator_1 = require("../utils/type-generator.cjs");
41
+ const ast_transformer_1 = require("../utils/ast-transformer.cjs");
42
+ const ast_codegen_1 = require("../utils/ast-codegen.cjs");
42
43
  const env_loader_1 = require("../utils/env-loader.cjs");
43
44
  const cli_utils_1 = require("../utils/cli-utils.cjs");
44
45
  const relqignore_1 = require("../utils/relqignore.cjs");
@@ -181,8 +182,8 @@ async function pullCommand(context) {
181
182
  const author = config.author || 'Relq CLI';
182
183
  const schemaPathRaw = typeof config.schema === 'string' ? config.schema : './db/schema.ts';
183
184
  const schemaPath = path.resolve(projectRoot, schemaPathRaw);
184
- const includeFunctions = config.generate?.includeFunctions ?? false;
185
- const includeTriggers = config.generate?.includeTriggers ?? false;
185
+ const includeFunctions = config.includeFunctions ?? true;
186
+ const includeTriggers = config.includeTriggers ?? true;
186
187
  const spinner = (0, cli_utils_1.createSpinner)();
187
188
  const startTime = Date.now();
188
189
  console.log('');
@@ -238,12 +239,36 @@ async function pullCommand(context) {
238
239
  (0, repo_manager_1.setFetchHead)(remoteHead, projectRoot);
239
240
  }
240
241
  spinner.succeed(`Fetched ${remoteCommits.length} remote commits`);
241
- spinner.start('Introspecting database...');
242
+ console.log('');
243
+ console.log(cli_utils_1.colors.bold('Introspecting database...'));
244
+ console.log('');
245
+ const progress = (0, cli_utils_1.createMultiProgress)();
246
+ const progressItems = [
247
+ { id: 'tables', label: 'tables', status: 'pending', count: 0 },
248
+ { id: 'columns', label: 'columns', status: 'pending', count: 0 },
249
+ { id: 'constraints', label: 'constraints', status: 'pending', count: 0 },
250
+ { id: 'indexes', label: 'indexes', status: 'pending', count: 0 },
251
+ { id: 'checks', label: 'checks', status: 'pending', count: 0 },
252
+ { id: 'enums', label: 'enums', status: 'pending', count: 0 },
253
+ { id: 'partitions', label: 'partitions', status: 'pending', count: 0 },
254
+ { id: 'extensions', label: 'extensions', status: 'pending', count: 0 },
255
+ ...(includeFunctions ? [{ id: 'functions', label: 'functions', status: 'pending', count: 0 }] : []),
256
+ ...(includeTriggers ? [{ id: 'triggers', label: 'triggers', status: 'pending', count: 0 }] : []),
257
+ { id: 'collations', label: 'collations', status: 'pending', count: 0 },
258
+ { id: 'foreign_servers', label: 'foreign servers', status: 'pending', count: 0 },
259
+ { id: 'foreign_tables', label: 'foreign tables', status: 'pending', count: 0 },
260
+ ];
261
+ progress.setItems(progressItems);
262
+ progress.start();
242
263
  const dbSchema = await (0, fast_introspect_1.fastIntrospectDatabase)(connection, undefined, {
243
264
  includeFunctions,
244
265
  includeTriggers,
266
+ onDetailedProgress: (update) => {
267
+ progress.updateItem(update.step, { status: update.status, count: update.count });
268
+ },
245
269
  });
246
- spinner.succeed(`Found ${dbSchema.tables.length} tables`);
270
+ progress.complete();
271
+ console.log('');
247
272
  const ignorePatterns = (0, relqignore_1.loadRelqignore)(projectRoot);
248
273
  const filteredTables = dbSchema.tables
249
274
  .filter(t => !(0, relqignore_1.isTableIgnored)(t.name, ignorePatterns).ignored)
@@ -277,12 +302,21 @@ async function pullCommand(context) {
277
302
  default: c.defaultValue,
278
303
  primaryKey: c.isPrimaryKey,
279
304
  unique: c.isUnique,
305
+ maxLength: c.maxLength ?? null,
306
+ precision: c.precision ?? null,
307
+ scale: c.scale ?? null,
308
+ comment: c.comment || undefined,
309
+ isGenerated: c.isGenerated || false,
310
+ generationExpression: c.generationExpression ?? null,
280
311
  })),
281
312
  indexes: t.indexes.map(i => ({
282
313
  name: i.name,
283
314
  columns: i.columns,
284
315
  unique: i.isUnique,
285
316
  type: i.type,
317
+ definition: i.definition || undefined,
318
+ whereClause: i.whereClause || undefined,
319
+ comment: i.comment || undefined,
286
320
  })),
287
321
  constraints: t.constraints.map(c => ({
288
322
  name: c.name,
@@ -292,6 +326,12 @@ async function pullCommand(context) {
292
326
  isPartitioned: t.isPartitioned,
293
327
  partitionType: t.partitionType,
294
328
  partitionKey: normalizePartitionKey(t.partitionKey),
329
+ comment: t.comment || undefined,
330
+ partitions: t.childPartitions?.map((p) => ({
331
+ name: p.name,
332
+ bound: p.partitionBound || '',
333
+ boundType: p.partitionType,
334
+ })) || undefined,
295
335
  })),
296
336
  enums: filteredEnums.map(e => ({
297
337
  name: e.name,
@@ -395,24 +435,43 @@ async function pullCommand(context) {
395
435
  }
396
436
  }
397
437
  else if (!schemaExists) {
398
- console.log('First pull - creating schema');
438
+ console.log(cli_utils_1.colors.bold('Creating schema') + cli_utils_1.colors.dim(' (first pull)'));
399
439
  console.log('');
440
+ const tableCount = filteredTables.length;
441
+ const columnCount = filteredTables.reduce((sum, t) => sum + t.columns.length, 0);
400
442
  const indexCount = filteredTables.reduce((sum, t) => sum + (t.indexes?.filter(i => !i.isPrimary).length || 0), 0);
401
- const partitionCount = filteredTables.filter(t => t.isPartitioned).length;
443
+ const partitionedCount = filteredTables.filter(t => t.isPartitioned).length;
444
+ const childPartitionCount = filteredTables.reduce((sum, t) => sum + (t.childPartitions?.length || 0), 0);
402
445
  const tableCommentCount = filteredTables.filter(t => t.comment).length;
403
446
  const columnCommentCount = filteredTables.reduce((sum, t) => sum + t.columns.filter(c => c.comment).length, 0);
404
- console.log('Schema Summary:');
405
- console.log(` ${cli_utils_1.colors.green(String(filteredTables.length))} tables`);
447
+ const indexCommentCount = filteredTables.reduce((sum, t) => sum + t.indexes.filter(i => i.comment).length, 0);
448
+ const summaryItems = [];
449
+ summaryItems.push({ count: tableCount, label: 'tables' });
450
+ summaryItems.push({ count: columnCount, label: 'columns' });
406
451
  if (indexCount > 0)
407
- console.log(` ${cli_utils_1.colors.green(String(indexCount))} indexes`);
408
- if (partitionCount > 0)
409
- console.log(` ${cli_utils_1.colors.green(String(partitionCount))} partitioned tables`);
410
- if (tableCommentCount > 0)
411
- console.log(` ${cli_utils_1.colors.green(String(tableCommentCount))} table comments`);
412
- if (columnCommentCount > 0)
413
- console.log(` ${cli_utils_1.colors.green(String(columnCommentCount))} column comments`);
452
+ summaryItems.push({ count: indexCount, label: 'indexes' });
453
+ if (partitionedCount > 0) {
454
+ const partLabel = childPartitionCount > 0
455
+ ? `partitioned (${childPartitionCount} child partitions)`
456
+ : 'partitioned';
457
+ summaryItems.push({ count: partitionedCount, label: partLabel });
458
+ }
459
+ if (tableCommentCount > 0 || columnCommentCount > 0 || indexCommentCount > 0) {
460
+ const commentParts = [];
461
+ if (tableCommentCount > 0)
462
+ commentParts.push(`${tableCommentCount} table`);
463
+ if (columnCommentCount > 0)
464
+ commentParts.push(`${columnCommentCount} column`);
465
+ if (indexCommentCount > 0)
466
+ commentParts.push(`${indexCommentCount} index`);
467
+ summaryItems.push({ count: tableCommentCount + columnCommentCount + indexCommentCount, label: `comments (${commentParts.join(', ')})` });
468
+ }
414
469
  if (dbSchema.extensions.length > 0)
415
- console.log(` ${cli_utils_1.colors.green(String(dbSchema.extensions.length))} extensions`);
470
+ summaryItems.push({ count: dbSchema.extensions.length, label: 'extensions' });
471
+ for (const item of summaryItems) {
472
+ const countStr = String(item.count).padStart(4);
473
+ console.log(`${cli_utils_1.colors.green('[+]')} ${cli_utils_1.colors.green(countStr)} ${item.label}`);
474
+ }
416
475
  console.log('');
417
476
  }
418
477
  if (dryRun) {
@@ -421,13 +480,10 @@ async function pullCommand(context) {
421
480
  return;
422
481
  }
423
482
  spinner.start('Generating TypeScript schema...');
424
- const typescript = (0, type_generator_1.generateTypeScript)(dbSchema, {
425
- includeDefineTables: true,
426
- includeSchema: true,
427
- includeIndexes: true,
428
- includeFunctions,
429
- includeTriggers,
483
+ const parsedSchema = await (0, ast_transformer_1.introspectedToParsedSchema)(dbSchema);
484
+ const typescript = (0, ast_codegen_1.generateTypeScriptFromAST)(parsedSchema, {
430
485
  camelCase: config.generate?.camelCase ?? true,
486
+ importPath: 'relq/schema-builder',
431
487
  });
432
488
  spinner.succeed('Generated TypeScript schema');
433
489
  const schemaDir = path.dirname(schemaPath);
@@ -530,6 +586,7 @@ async function pullCommand(context) {
530
586
  triggers: filteredTriggers || [],
531
587
  };
532
588
  const schemaChanges = (0, schema_comparator_1.compareSchemas)(beforeSchema, afterSchema);
589
+ applyTrackingIdsToSnapshot(typescript, currentSchema);
533
590
  (0, repo_manager_1.saveSnapshot)(currentSchema, projectRoot);
534
591
  const duration = Date.now() - startTime;
535
592
  if (noCommit) {
@@ -552,6 +609,7 @@ async function pullCommand(context) {
552
609
  const connectionDesc = (0, env_loader_1.getConnectionDescription)(connection);
553
610
  const commitMessage = `pull: sync from ${connectionDesc}`;
554
611
  const commit = (0, repo_manager_1.createCommit)(currentSchema, author, commitMessage, projectRoot);
612
+ (0, repo_manager_1.markCommitAsPulled)(commit.hash, connection, projectRoot);
555
613
  (0, repo_manager_1.clearWorkingState)(projectRoot);
556
614
  console.log('');
557
615
  console.log(`Pull completed in ${(0, cli_utils_1.formatDuration)(duration)}`);
@@ -634,3 +692,26 @@ function detectObjectConflicts(local, remote) {
634
692
  }
635
693
  return conflicts;
636
694
  }
695
+ function applyTrackingIdsToSnapshot(typescript, snapshot) {
696
+ for (const table of snapshot.tables) {
697
+ const tablePattern = new RegExp(`defineTable\\s*\\(\\s*['"]${table.name}['"]\\s*,\\s*\\{[^}]+\\}\\s*,\\s*\\{[^}]*\\$trackingId:\\s*['"]([^'"]+)['"]`, 's');
698
+ const tableMatch = typescript.match(tablePattern);
699
+ if (tableMatch) {
700
+ table.trackingId = tableMatch[1];
701
+ }
702
+ for (const col of table.columns) {
703
+ const colPattern = new RegExp(`(?:${col.tsName}|${col.name}):\\s*\\w+\\([^)]*\\)[^\\n]*\\.\\\$id\\(['"]([^'"]+)['"]\\)`);
704
+ const colMatch = typescript.match(colPattern);
705
+ if (colMatch) {
706
+ col.trackingId = colMatch[1];
707
+ }
708
+ }
709
+ for (const idx of table.indexes) {
710
+ const idxPattern = new RegExp(`index\\s*\\(\\s*['"]${idx.name}['"]\\s*\\)[^\\n]*\\.\\\$id\\(['"]([^'"]+)['"]\\)`);
711
+ const idxMatch = typescript.match(idxPattern);
712
+ if (idxMatch) {
713
+ idx.trackingId = idxMatch[1];
714
+ }
715
+ }
716
+ }
717
+ }
@@ -77,7 +77,15 @@ async function pushCommand(context) {
77
77
  const localCommits = (0, repo_manager_1.getAllCommits)(projectRoot);
78
78
  const remoteHashes = new Set(remoteCommits.map(c => c.hash));
79
79
  const localHashes = new Set(localCommits.map(c => c.hash));
80
- const toPush = localCommits.filter(c => !remoteHashes.has(c.hash));
80
+ const toPush = localCommits.filter(c => {
81
+ if (remoteHashes.has(c.hash))
82
+ return false;
83
+ const syncStatus = (0, repo_manager_1.isCommitSyncedWith)(c, connection);
84
+ if (syncStatus.synced) {
85
+ return false;
86
+ }
87
+ return true;
88
+ });
81
89
  const remoteMissing = remoteCommits.filter(c => !localHashes.has(c.hash));
82
90
  spinner.succeed('Checked remote commits');
83
91
  spinner.start('Introspecting remote database...');
@@ -167,9 +175,10 @@ async function pushCommand(context) {
167
175
  const commitsToProcess = [...toPush].reverse();
168
176
  spinner.start(`Pushing ${toPush.length} commit(s)...`);
169
177
  for (const commit of commitsToProcess) {
170
- await (0, repo_manager_1.pushCommit)(connection, commit);
178
+ await (0, repo_manager_1.pushCommit)(connection, commit, projectRoot);
179
+ (0, repo_manager_1.markCommitAsPushed)(commit.hash, connection, projectRoot);
171
180
  }
172
- spinner.succeed(`Pushed ${toPush.length} commit(s)`);
181
+ spinner.succeed(`Pushed ${toPush.length} commit(s) to ${(0, repo_manager_1.getConnectionLabel)(connection)}`);
173
182
  }
174
183
  if (applySQL) {
175
184
  spinner.start('Applying SQL changes...');
@@ -209,6 +218,32 @@ async function pushCommand(context) {
209
218
  }
210
219
  await client.query('COMMIT');
211
220
  spinner.succeed(`Applied ${statementsRun} statement(s) from ${sqlExecuted} commit(s)`);
221
+ let hasRenameOperations = false;
222
+ for (const commit of commitsToProcess) {
223
+ const commitPath = path.join(projectRoot, '.relq', 'commits', `${commit.hash}.json`);
224
+ if (fs.existsSync(commitPath)) {
225
+ const enhancedCommit = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
226
+ if (enhancedCommit.changes?.some((c) => c.type === 'RENAME')) {
227
+ hasRenameOperations = true;
228
+ break;
229
+ }
230
+ }
231
+ }
232
+ if (hasRenameOperations) {
233
+ spinner.start('Updating snapshot after RENAME...');
234
+ try {
235
+ const updatedSchema = await (0, fast_introspect_1.fastIntrospectDatabase)(connection, undefined, {
236
+ includeFunctions,
237
+ includeTriggers,
238
+ });
239
+ const snapshotPath = path.join(projectRoot, '.relq', 'snapshot.json');
240
+ fs.writeFileSync(snapshotPath, JSON.stringify(updatedSchema, null, 2));
241
+ spinner.succeed('Snapshot updated with renamed objects');
242
+ }
243
+ catch (e) {
244
+ spinner.warn('Could not update snapshot after RENAME - run "relq pull" manually');
245
+ }
246
+ }
212
247
  }
213
248
  catch (error) {
214
249
  try {
@@ -98,7 +98,14 @@ function parseArgs(argv) {
98
98
  flags[arg.slice(2, eqIndex)] = arg.slice(eqIndex + 1);
99
99
  }
100
100
  else {
101
- flags[arg.slice(2)] = true;
101
+ const nextArg = argv[i + 1];
102
+ if (nextArg && !nextArg.startsWith('-')) {
103
+ flags[arg.slice(2)] = nextArg;
104
+ i++;
105
+ }
106
+ else {
107
+ flags[arg.slice(2)] = true;
108
+ }
102
109
  }
103
110
  }
104
111
  else if (arg.startsWith('-') && arg.length === 2) {
@@ -211,6 +218,7 @@ async function main() {
211
218
  printHelp();
212
219
  process.exit(1);
213
220
  }
221
+ console.log('Command:', command);
214
222
  let config = null;
215
223
  let resolvedProjectRoot = process.cwd();
216
224
  if (requiresConfig(command)) {
@@ -0,0 +1,297 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KNOWN_BUILDER_FUNCTIONS = void 0;
4
+ exports.mapFunctionToBuilder = mapFunctionToBuilder;
5
+ exports.astToBuilder = astToBuilder;
6
+ exports.formatGeneratedExpression = formatGeneratedExpression;
7
+ const utils_1 = require("./utils.cjs");
8
+ exports.KNOWN_BUILDER_FUNCTIONS = {
9
+ 'setweight': 'setweight',
10
+ 'to_tsvector': 'toTsvector',
11
+ 'to_tsquery': 'toTsquery',
12
+ 'plainto_tsquery': 'plainToTsquery',
13
+ 'phraseto_tsquery': 'phraseToTsquery',
14
+ 'websearch_to_tsquery': 'websearchToTsquery',
15
+ 'ts_headline': 'tsHeadline',
16
+ 'ts_rank': 'tsRank',
17
+ 'ts_rank_cd': 'tsRankCd',
18
+ 'coalesce': 'coalesce',
19
+ 'nullif': 'nullif',
20
+ 'greatest': 'greatest',
21
+ 'least': 'least',
22
+ 'concat': 'concat',
23
+ 'concat_ws': 'concatWs',
24
+ 'lower': 'lower',
25
+ 'upper': 'upper',
26
+ 'trim': 'trim',
27
+ 'ltrim': 'ltrim',
28
+ 'rtrim': 'rtrim',
29
+ 'length': 'length',
30
+ 'substring': 'substring',
31
+ 'replace': 'replace',
32
+ 'regexp_replace': 'regexpReplace',
33
+ 'split_part': 'splitPart',
34
+ 'now': 'now',
35
+ 'current_timestamp': 'currentTimestamp',
36
+ 'current_date': 'currentDate',
37
+ 'current_time': 'currentTime',
38
+ 'date_trunc': 'dateTrunc',
39
+ 'date_part': 'datePart',
40
+ 'extract': 'extract',
41
+ 'age': 'age',
42
+ 'gen_random_uuid': 'genRandomUuid',
43
+ 'uuid_generate_v4': 'uuidGenerateV4',
44
+ 'jsonb_build_object': 'jsonbBuildObject',
45
+ 'jsonb_agg': 'jsonbAgg',
46
+ 'json_agg': 'jsonAgg',
47
+ 'array_agg': 'arrayAgg',
48
+ 'string_agg': 'stringAgg',
49
+ 'count': 'count',
50
+ 'sum': 'sum',
51
+ 'avg': 'avg',
52
+ 'min': 'min',
53
+ 'max': 'max',
54
+ 'abs': 'abs',
55
+ 'ceil': 'ceil',
56
+ 'floor': 'floor',
57
+ 'round': 'round',
58
+ 'sqrt': 'sqrt',
59
+ 'power': 'power',
60
+ 'mod': 'mod',
61
+ 'random': 'random',
62
+ };
63
+ function mapFunctionToBuilder(funcName) {
64
+ return exports.KNOWN_BUILDER_FUNCTIONS[funcName] || null;
65
+ }
66
+ function astToBuilder(node, prefixOrOptions = 'g') {
67
+ const opts = typeof prefixOrOptions === 'string'
68
+ ? { prefix: prefixOrOptions }
69
+ : prefixOrOptions;
70
+ const { prefix = 'g', useCamelCase = false, useTableRef = false } = opts;
71
+ if (!node)
72
+ return "''";
73
+ if (node.FuncCall) {
74
+ const func = node.FuncCall;
75
+ const funcName = func.funcname?.map((n) => n.String?.sval).filter(Boolean).join('.') || '';
76
+ const args = (func.args || []).map((a) => astToBuilder(a, opts));
77
+ const builderMethod = mapFunctionToBuilder(funcName.toLowerCase());
78
+ if (builderMethod) {
79
+ return `${prefix}.${builderMethod}(${args.join(', ')})`;
80
+ }
81
+ throw new Error(`Unsupported function in generated expression: "${funcName}". Add this function to KNOWN_BUILDER_FUNCTIONS in astToBuilder.`);
82
+ }
83
+ if (node.CoalesceExpr) {
84
+ const args = (node.CoalesceExpr.args || []).map((a) => astToBuilder(a, opts));
85
+ return `${prefix}.coalesce(${args.join(', ')})`;
86
+ }
87
+ if (node.A_Expr) {
88
+ const expr = node.A_Expr;
89
+ const op = expr.name?.[0]?.String?.sval || '';
90
+ const left = astToBuilder(expr.lexpr, opts);
91
+ const right = astToBuilder(expr.rexpr, opts);
92
+ switch (op) {
93
+ case '||':
94
+ return `__CONCAT__[${left}, ${right}]`;
95
+ case '->>':
96
+ return `${prefix}.jsonbExtractText(${left}, ${right})`;
97
+ case '->':
98
+ return `${prefix}.jsonbExtract(${left}, ${right})`;
99
+ case '@@':
100
+ return `${prefix}.tsMatch(${left}, ${right})`;
101
+ case '+':
102
+ return `${prefix}.add(${left}, ${right})`;
103
+ case '-':
104
+ return `${prefix}.subtract(${left}, ${right})`;
105
+ case '*':
106
+ return `${prefix}.multiply(${left}, ${right})`;
107
+ case '/':
108
+ return `${prefix}.divide(${left}, ${right})`;
109
+ case '%':
110
+ return `${prefix}.mod(${left}, ${right})`;
111
+ case '=':
112
+ case '<>':
113
+ case '!=':
114
+ case '<':
115
+ case '>':
116
+ case '<=':
117
+ case '>=':
118
+ if (useTableRef) {
119
+ const methodMap = {
120
+ '=': 'eq', '<>': 'ne', '!=': 'neq',
121
+ '<': 'lt', '>': 'gt', '<=': 'lte', '>=': 'gte'
122
+ };
123
+ return `${left}.${methodMap[op]}(${right})`;
124
+ }
125
+ return `${prefix}.compare(${left}, '${op}', ${right})`;
126
+ case '~':
127
+ case '~*':
128
+ case '!~':
129
+ case '!~*':
130
+ return `${prefix}.regex(${left}, '${op}', ${right})`;
131
+ default:
132
+ throw new Error(`Unsupported operator in generated expression: "${op}". Add explicit handling for this operator in astToBuilder.`);
133
+ }
134
+ }
135
+ if (node.ColumnRef) {
136
+ const fields = (node.ColumnRef.fields || [])
137
+ .map((f) => f.String?.sval)
138
+ .filter(Boolean);
139
+ if (fields.length === 1) {
140
+ const colName = useCamelCase ? (0, utils_1.toCamelCase)(fields[0]) : fields[0];
141
+ return `table.${colName}`;
142
+ }
143
+ else if (fields.length === 2) {
144
+ const colName = useCamelCase ? (0, utils_1.toCamelCase)(fields[1]) : fields[1];
145
+ return `table.${colName}`;
146
+ }
147
+ const colName = useCamelCase ? (0, utils_1.toCamelCase)(fields[fields.length - 1]) : fields[fields.length - 1];
148
+ return `table.${colName}`;
149
+ }
150
+ if (node.A_Const) {
151
+ const val = node.A_Const;
152
+ if (val.sval !== undefined) {
153
+ const s = val.sval?.sval ?? val.sval;
154
+ if (s === '' || s === ' ') {
155
+ return s === '' ? `${prefix}.asText()` : `${prefix}.asText(' ')`;
156
+ }
157
+ return `'${(0, utils_1.escapeString)(String(s))}'`;
158
+ }
159
+ else if (val.ival !== undefined) {
160
+ const i = val.ival?.ival ?? val.ival;
161
+ return String(i);
162
+ }
163
+ else if (val.fval !== undefined) {
164
+ const f = val.fval?.fval ?? val.fval;
165
+ return String(f);
166
+ }
167
+ else if (val.boolval !== undefined) {
168
+ return val.boolval?.boolval ? 'true' : 'false';
169
+ }
170
+ return `${prefix}.asText()`;
171
+ }
172
+ if (node.TypeCast) {
173
+ const arg = astToBuilder(node.TypeCast.arg, opts);
174
+ const typeName = node.TypeCast.typeName?.names
175
+ ?.map((n) => n.String?.sval)
176
+ .filter(Boolean)
177
+ .join('.') || '';
178
+ if (typeName === 'regconfig' || typeName === 'pg_catalog.regconfig') {
179
+ return arg;
180
+ }
181
+ if (typeName === 'text' || typeName === 'pg_catalog.text') {
182
+ return `${prefix}.asText(${arg})`;
183
+ }
184
+ if (typeName === 'varchar' || typeName === 'pg_catalog.varchar' || typeName.includes('character varying')) {
185
+ return arg;
186
+ }
187
+ if (typeName === 'char' || typeName === 'bpchar' || typeName === 'pg_catalog.bpchar') {
188
+ return arg;
189
+ }
190
+ if (typeName === 'numeric' || typeName === 'pg_catalog.numeric') {
191
+ return arg;
192
+ }
193
+ if (typeName === 'int4' || typeName === 'integer' || typeName === 'pg_catalog.int4') {
194
+ return arg;
195
+ }
196
+ return `${prefix}.cast(${arg}, '${typeName}')`;
197
+ }
198
+ if (node.NullTest) {
199
+ const arg = astToBuilder(node.NullTest.arg, opts);
200
+ const isNull = node.NullTest.nulltesttype === 'IS_NULL';
201
+ return isNull ? `${prefix}.isNull(${arg})` : `${prefix}.isNotNull(${arg})`;
202
+ }
203
+ if (node.BoolExpr) {
204
+ const boolexpr = node.BoolExpr;
205
+ const args = (boolexpr.args || []).map((a) => astToBuilder(a, opts));
206
+ if (useTableRef && args.length >= 2) {
207
+ switch (boolexpr.boolop) {
208
+ case 'AND_EXPR': return args.reduce((acc, arg) => `${acc}.and(${arg})`);
209
+ case 'OR_EXPR': return args.reduce((acc, arg) => `${acc}.or(${arg})`);
210
+ case 'NOT_EXPR': return `${args[0]}.not()`;
211
+ }
212
+ }
213
+ switch (boolexpr.boolop) {
214
+ case 'AND_EXPR': return `${prefix}.and(${args.join(', ')})`;
215
+ case 'OR_EXPR': return `${prefix}.or(${args.join(', ')})`;
216
+ case 'NOT_EXPR': return `${prefix}.not(${args[0]})`;
217
+ }
218
+ }
219
+ if (node.CaseExpr) {
220
+ const caseExpr = node.CaseExpr;
221
+ let chain = `${prefix}.case()`;
222
+ for (const w of (caseExpr.args || [])) {
223
+ if (w.CaseWhen) {
224
+ const condition = astToBuilder(w.CaseWhen.expr, opts);
225
+ const result = astToBuilder(w.CaseWhen.result, opts);
226
+ chain += `.when(${condition}, ${result})`;
227
+ }
228
+ }
229
+ if (caseExpr.defresult) {
230
+ const defResult = astToBuilder(caseExpr.defresult, opts);
231
+ chain += `.else(${defResult})`;
232
+ }
233
+ else {
234
+ chain += '.end()';
235
+ }
236
+ return chain;
237
+ }
238
+ const nodeType = Object.keys(node)[0];
239
+ throw new Error(`Unsupported AST node type in generated expression: "${nodeType}". Add explicit handling for this node type in astToBuilder.`);
240
+ }
241
+ function extractConcatItems(expr) {
242
+ if (expr.startsWith('__CONCAT__[')) {
243
+ const inner = expr.slice('__CONCAT__['.length, -1);
244
+ const items = [];
245
+ let depth = 0;
246
+ let current = '';
247
+ let inQuote = false;
248
+ for (let i = 0; i < inner.length; i++) {
249
+ const c = inner[i];
250
+ if (c === "'" && inner[i - 1] !== '\\') {
251
+ inQuote = !inQuote;
252
+ }
253
+ if (!inQuote) {
254
+ if (c === '[' || c === '(')
255
+ depth++;
256
+ if (c === ']' || c === ')')
257
+ depth--;
258
+ if (c === ',' && depth === 0) {
259
+ items.push(current.trim());
260
+ current = '';
261
+ continue;
262
+ }
263
+ }
264
+ current += c;
265
+ }
266
+ if (current.trim())
267
+ items.push(current.trim());
268
+ return items.flatMap(item => extractConcatItems(item));
269
+ }
270
+ return [expr];
271
+ }
272
+ function cleanAllConcats(expr) {
273
+ let result = expr;
274
+ let iterations = 0;
275
+ const maxIterations = 50;
276
+ while (result.includes('__CONCAT__[') && iterations < maxIterations) {
277
+ const match = result.match(/__CONCAT__\[([^\[\]]*)\]/);
278
+ if (match) {
279
+ const replacement = `F.concat(${match[1]})`;
280
+ result = result.replace(match[0], replacement);
281
+ }
282
+ else {
283
+ break;
284
+ }
285
+ iterations++;
286
+ }
287
+ return result;
288
+ }
289
+ function formatGeneratedExpression(expr) {
290
+ if (expr.startsWith('__CONCAT__[')) {
291
+ const items = extractConcatItems(expr);
292
+ const cleanedItems = items.map(item => cleanAllConcats(item));
293
+ return { isArray: true, items: cleanedItems };
294
+ }
295
+ const cleaned = cleanAllConcats(expr);
296
+ return { isArray: false, items: [cleaned] };
297
+ }