linkgress-orm 0.1.0 → 0.1.1

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.
@@ -1,12 +1,117 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CollectionQueryBuilder = exports.ReferenceQueryBuilder = exports.SelectQueryBuilder = exports.QueryBuilder = void 0;
4
+ exports.getColumnNameMapForSchema = getColumnNameMapForSchema;
5
+ exports.getRelationEntriesForSchema = getRelationEntriesForSchema;
6
+ exports.getTargetSchemaForRelation = getTargetSchemaForRelation;
7
+ exports.createNestedFieldRefProxy = createNestedFieldRefProxy;
4
8
  const conditions_1 = require("./conditions");
5
9
  const query_utils_1 = require("./query-utils");
6
10
  const subquery_1 = require("./subquery");
7
11
  const grouped_query_1 = require("./grouped-query");
8
12
  const cte_builder_1 = require("./cte-builder");
9
13
  const collection_strategy_factory_1 = require("./collection-strategy.factory");
14
+ /**
15
+ * Performance utility: Get column name map from schema, using cached version if available
16
+ */
17
+ function getColumnNameMapForSchema(schema) {
18
+ if (schema.columnNameMap) {
19
+ return schema.columnNameMap;
20
+ }
21
+ // Fallback: build the map (for schemas that weren't built with the new TableBuilder)
22
+ const map = new Map();
23
+ for (const [colName, colBuilder] of Object.entries(schema.columns)) {
24
+ map.set(colName, colBuilder.build().name);
25
+ }
26
+ return map;
27
+ }
28
+ /**
29
+ * Performance utility: Get relation entries array from schema, using cached version if available
30
+ */
31
+ function getRelationEntriesForSchema(schema) {
32
+ if (schema.relationEntries) {
33
+ return schema.relationEntries;
34
+ }
35
+ // Fallback: build the array (for schemas that weren't built with the new TableBuilder)
36
+ return Object.entries(schema.relations);
37
+ }
38
+ /**
39
+ * Performance utility: Get target schema for a relation, using cached version if available
40
+ */
41
+ function getTargetSchemaForRelation(schema, relName, relConfig) {
42
+ // Try cached version first
43
+ if (schema.relationSchemaCache) {
44
+ const cached = schema.relationSchemaCache.get(relName);
45
+ if (cached)
46
+ return cached;
47
+ }
48
+ // Fallback: build the schema
49
+ if (relConfig.targetTableBuilder) {
50
+ return relConfig.targetTableBuilder.build();
51
+ }
52
+ return undefined;
53
+ }
54
+ // Performance: Cache nested field ref proxies per table alias
55
+ const nestedFieldRefProxyCache = new Map();
56
+ /**
57
+ * Creates a nested proxy that supports accessing properties at any depth.
58
+ * This allows patterns like `p.product.priceMode` to work even without full schema information.
59
+ * Each property access returns an object that is both a FieldRef and can be further accessed.
60
+ *
61
+ * @param tableAlias The table alias to use for the FieldRef
62
+ * @returns A proxy that creates FieldRefs for any property access
63
+ */
64
+ function createNestedFieldRefProxy(tableAlias) {
65
+ // Return cached proxy if available
66
+ const cached = nestedFieldRefProxyCache.get(tableAlias);
67
+ if (cached)
68
+ return cached;
69
+ const handler = {
70
+ get: (_target, prop) => {
71
+ // Handle Symbol.toPrimitive for string conversion (used in template literals)
72
+ if (prop === Symbol.toPrimitive || prop === 'toString' || prop === 'valueOf') {
73
+ return () => `[NestedFieldRefProxy:${tableAlias}]`;
74
+ }
75
+ if (typeof prop === 'symbol')
76
+ return undefined;
77
+ // Return an object that is both a FieldRef AND a proxy for further nesting
78
+ const fieldRef = {
79
+ __fieldName: prop,
80
+ __dbColumnName: prop,
81
+ __tableAlias: tableAlias,
82
+ };
83
+ // Return a proxy that acts as both the FieldRef and allows further property access
84
+ return new Proxy(fieldRef, {
85
+ get: (fieldTarget, nestedProp) => {
86
+ // Handle Symbol.toPrimitive for string conversion (used in template literals)
87
+ if (nestedProp === Symbol.toPrimitive || nestedProp === 'toString' || nestedProp === 'valueOf') {
88
+ return () => fieldTarget.__dbColumnName;
89
+ }
90
+ if (typeof nestedProp === 'symbol')
91
+ return undefined;
92
+ // If accessing FieldRef properties, return them
93
+ if (nestedProp === '__fieldName' || nestedProp === '__dbColumnName' || nestedProp === '__tableAlias') {
94
+ return fieldTarget[nestedProp];
95
+ }
96
+ // Otherwise, treat as nested navigation and create a new nested proxy
97
+ // The nested table alias is the property name (e.g., 'product' for p.product)
98
+ return createNestedFieldRefProxy(prop)[nestedProp];
99
+ },
100
+ has: (_fieldTarget, _nestedProp) => true,
101
+ });
102
+ },
103
+ has: (_target, prop) => {
104
+ // The outer proxy doesn't have FieldRef properties - only field names
105
+ if (prop === '__fieldName' || prop === '__dbColumnName' || prop === '__tableAlias') {
106
+ return false;
107
+ }
108
+ return true;
109
+ },
110
+ };
111
+ const proxy = new Proxy({}, handler);
112
+ nestedFieldRefProxyCache.set(tableAlias, proxy);
113
+ return proxy;
114
+ }
10
115
  /**
11
116
  * Cached regex for numeric string detection
12
117
  * Used to convert PostgreSQL NUMERIC/BIGINT strings to numbers
@@ -77,14 +182,10 @@ class QueryBuilder {
77
182
  return this._cachedMockRow;
78
183
  }
79
184
  const mock = {};
80
- // Performance: Build column configs once and cache them
81
- const columnEntries = Object.entries(this.schema.columns);
82
- const columnConfigs = new Map();
83
- for (const [colName, colBuilder] of columnEntries) {
84
- columnConfigs.set(colName, colBuilder.build().name);
85
- }
185
+ // Performance: Use pre-computed column name map if available
186
+ const columnNameMap = getColumnNameMapForSchema(this.schema);
86
187
  // Add columns as FieldRef objects - type-safe with property name and database column name
87
- for (const [colName, dbColumnName] of columnConfigs) {
188
+ for (const [colName, dbColumnName] of columnNameMap) {
88
189
  Object.defineProperty(mock, colName, {
89
190
  get: () => ({
90
191
  __fieldName: colName,
@@ -95,17 +196,13 @@ class QueryBuilder {
95
196
  configurable: true,
96
197
  });
97
198
  }
98
- // Performance: Cache target schemas for relations to avoid repeated .build() calls
99
- const relationSchemas = new Map();
100
- for (const [relName, relConfig] of Object.entries(this.schema.relations)) {
101
- if (relConfig.targetTableBuilder) {
102
- relationSchemas.set(relName, relConfig.targetTableBuilder.build());
103
- }
104
- }
199
+ // Performance: Use pre-computed relation entries and cached schemas
200
+ const relationEntries = getRelationEntriesForSchema(this.schema);
105
201
  // Add relations (both collections and single references)
106
- for (const [relName, relConfig] of Object.entries(this.schema.relations)) {
202
+ for (const [relName, relConfig] of relationEntries) {
203
+ // Performance: Use cached target schema
204
+ const targetSchema = getTargetSchemaForRelation(this.schema, relName, relConfig);
107
205
  if (relConfig.type === 'many') {
108
- const targetSchema = relationSchemas.get(relName);
109
206
  Object.defineProperty(mock, relName, {
110
207
  get: () => {
111
208
  return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', this.schema.name, targetSchema);
@@ -116,7 +213,6 @@ class QueryBuilder {
116
213
  }
117
214
  else {
118
215
  // Single reference navigation (many-to-one, one-to-one)
119
- const targetSchema = relationSchemas.get(relName);
120
216
  Object.defineProperty(mock, relName, {
121
217
  get: () => {
122
218
  const refBuilder = new ReferenceQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKeys || [relConfig.foreignKey || ''], relConfig.matches || [], relConfig.isMandatory ?? false, targetSchema);
@@ -234,14 +330,10 @@ class QueryBuilder {
234
330
  */
235
331
  createMockRowForTable(schema, alias) {
236
332
  const mock = {};
237
- // Performance: Build column configs once and cache them
238
- const columnEntries = Object.entries(schema.columns);
239
- const columnConfigs = new Map();
240
- for (const [colName, colBuilder] of columnEntries) {
241
- columnConfigs.set(colName, colBuilder.build().name);
242
- }
333
+ // Performance: Use pre-computed column name map if available
334
+ const columnNameMap = getColumnNameMapForSchema(schema);
243
335
  // Add columns as FieldRef objects with table alias
244
- for (const [colName, dbColumnName] of columnConfigs) {
336
+ for (const [colName, dbColumnName] of columnNameMap) {
245
337
  Object.defineProperty(mock, colName, {
246
338
  get: () => ({
247
339
  __fieldName: colName,
@@ -252,18 +344,14 @@ class QueryBuilder {
252
344
  configurable: true,
253
345
  });
254
346
  }
255
- // Performance: Cache target schemas for relations
256
- const relationSchemas = new Map();
257
- for (const [relName, relConfig] of Object.entries(schema.relations)) {
258
- if (relConfig.targetTableBuilder) {
259
- relationSchemas.set(relName, relConfig.targetTableBuilder.build());
260
- }
261
- }
347
+ // Performance: Use pre-computed relation entries and cached schemas
348
+ const relationEntries = getRelationEntriesForSchema(schema);
262
349
  // Add navigation properties (single references and collections)
263
- for (const [relName, relConfig] of Object.entries(schema.relations)) {
350
+ for (const [relName, relConfig] of relationEntries) {
351
+ // Performance: Use cached target schema
352
+ const targetSchema = getTargetSchemaForRelation(schema, relName, relConfig);
264
353
  if (relConfig.type === 'many') {
265
354
  // Collection navigation
266
- const targetSchema = relationSchemas.get(relName);
267
355
  Object.defineProperty(mock, relName, {
268
356
  get: () => {
269
357
  return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', schema.name, targetSchema);
@@ -274,7 +362,6 @@ class QueryBuilder {
274
362
  }
275
363
  else {
276
364
  // Single reference navigation
277
- const targetSchema = relationSchemas.get(relName);
278
365
  Object.defineProperty(mock, relName, {
279
366
  get: () => {
280
367
  const refBuilder = new ReferenceQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKeys || [relConfig.foreignKey || ''], relConfig.matches || [], relConfig.isMandatory ?? false, targetSchema);
@@ -383,7 +470,12 @@ class SelectQueryBuilder {
383
470
  * .toList();
384
471
  */
385
472
  with(...ctes) {
386
- this.ctes.push(...ctes);
473
+ // Add CTEs, avoiding duplicates by name
474
+ for (const cte of ctes) {
475
+ if (!this.ctes.some(existing => existing.name === cte.name)) {
476
+ this.ctes.push(cte);
477
+ }
478
+ }
387
479
  return this;
388
480
  }
389
481
  /**
@@ -610,14 +702,10 @@ class SelectQueryBuilder {
610
702
  */
611
703
  createMockRowForTable(schema, alias) {
612
704
  const mock = {};
613
- // Performance: Build column configs once and cache them
614
- const columnEntries = Object.entries(schema.columns);
615
- const columnConfigs = new Map();
616
- for (const [colName, colBuilder] of columnEntries) {
617
- columnConfigs.set(colName, colBuilder.build().name);
618
- }
705
+ // Performance: Use pre-computed column name map if available
706
+ const columnNameMap = getColumnNameMapForSchema(schema);
619
707
  // Add columns as FieldRef objects with table alias
620
- for (const [colName, dbColumnName] of columnConfigs) {
708
+ for (const [colName, dbColumnName] of columnNameMap) {
621
709
  Object.defineProperty(mock, colName, {
622
710
  get: () => ({
623
711
  __fieldName: colName,
@@ -628,18 +716,14 @@ class SelectQueryBuilder {
628
716
  configurable: true,
629
717
  });
630
718
  }
631
- // Performance: Cache target schemas for relations
632
- const relationSchemas = new Map();
633
- for (const [relName, relConfig] of Object.entries(schema.relations)) {
634
- if (relConfig.targetTableBuilder) {
635
- relationSchemas.set(relName, relConfig.targetTableBuilder.build());
636
- }
637
- }
719
+ // Performance: Use pre-computed relation entries and cached schemas
720
+ const relationEntries = getRelationEntriesForSchema(schema);
638
721
  // Add navigation properties (single references and collections)
639
- for (const [relName, relConfig] of Object.entries(schema.relations)) {
722
+ for (const [relName, relConfig] of relationEntries) {
723
+ // Performance: Use cached target schema
724
+ const targetSchema = getTargetSchemaForRelation(schema, relName, relConfig);
640
725
  if (relConfig.type === 'many') {
641
726
  // Collection navigation
642
- const targetSchema = relationSchemas.get(relName);
643
727
  Object.defineProperty(mock, relName, {
644
728
  get: () => {
645
729
  return new CollectionQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKey || relConfig.foreignKeys?.[0] || '', schema.name, targetSchema);
@@ -650,7 +734,6 @@ class SelectQueryBuilder {
650
734
  }
651
735
  else {
652
736
  // Single reference navigation
653
- const targetSchema = relationSchemas.get(relName);
654
737
  Object.defineProperty(mock, relName, {
655
738
  get: () => {
656
739
  const refBuilder = new ReferenceQueryBuilder(relName, relConfig.targetTable, relConfig.foreignKeys || [relConfig.foreignKey || ''], relConfig.matches || [], relConfig.isMandatory ?? false, targetSchema);
@@ -1238,9 +1321,10 @@ class SelectQueryBuilder {
1238
1321
  */
1239
1322
  createMockRow() {
1240
1323
  const mock = {};
1324
+ // Performance: Use pre-computed column name map if available
1325
+ const columnNameMap = getColumnNameMapForSchema(this.schema);
1241
1326
  // Add columns as FieldRef objects - type-safe with property name and database column name
1242
- for (const [colName, colBuilder] of Object.entries(this.schema.columns)) {
1243
- const dbColumnName = colBuilder.build().name;
1327
+ for (const [colName, dbColumnName] of columnNameMap) {
1244
1328
  Object.defineProperty(mock, colName, {
1245
1329
  get: () => ({
1246
1330
  __fieldName: colName,
@@ -1257,13 +1341,12 @@ class SelectQueryBuilder {
1257
1341
  if (join.isSubquery || !join.schema) {
1258
1342
  continue;
1259
1343
  }
1260
- for (const [colName, colBuilder] of Object.entries(join.schema.columns)) {
1261
- const dbColumnName = colBuilder.build().name;
1262
- // Create a unique property name by prefixing with table alias or using the column name directly
1263
- // For now, we'll create nested objects for each joined table
1264
- if (!mock[join.alias]) {
1265
- mock[join.alias] = {};
1266
- }
1344
+ // Performance: Use pre-computed column name map for joined schema
1345
+ const joinColumnNameMap = getColumnNameMapForSchema(join.schema);
1346
+ if (!mock[join.alias]) {
1347
+ mock[join.alias] = {};
1348
+ }
1349
+ for (const [colName, dbColumnName] of joinColumnNameMap) {
1267
1350
  Object.defineProperty(mock[join.alias], colName, {
1268
1351
  get: () => ({
1269
1352
  __fieldName: colName,
@@ -1275,15 +1358,18 @@ class SelectQueryBuilder {
1275
1358
  });
1276
1359
  }
1277
1360
  }
1361
+ // Performance: Use pre-computed relation entries
1362
+ const relationEntries = getRelationEntriesForSchema(this.schema);
1278
1363
  // Add relations as CollectionQueryBuilder or ReferenceQueryBuilder
1279
- for (const [relName, relConfig] of Object.entries(this.schema.relations)) {
1280
- // Try to get target schema from registry (preferred, has full relations) or targetTableBuilder
1364
+ for (const [relName, relConfig] of relationEntries) {
1365
+ // Try to get target schema from registry (preferred, has full relations) or cached schema
1281
1366
  let targetSchema;
1282
1367
  if (this.schemaRegistry) {
1283
1368
  targetSchema = this.schemaRegistry.get(relConfig.targetTable);
1284
1369
  }
1285
- if (!targetSchema && relConfig.targetTableBuilder) {
1286
- targetSchema = relConfig.targetTableBuilder.build();
1370
+ if (!targetSchema) {
1371
+ // Performance: Use cached target schema
1372
+ targetSchema = getTargetSchemaForRelation(this.schema, relName, relConfig);
1287
1373
  }
1288
1374
  if (relConfig.type === 'many') {
1289
1375
  Object.defineProperty(mock, relName, {
@@ -1384,27 +1470,13 @@ class SelectQueryBuilder {
1384
1470
  for (const [key, value] of Object.entries(selection)) {
1385
1471
  if (value && typeof value === 'object' && '__tableAlias' in value && '__dbColumnName' in value) {
1386
1472
  // This is a FieldRef with a table alias - check if it's from a related table
1387
- const tableAlias = value.__tableAlias;
1388
- if (tableAlias !== this.schema.name && !joins.some(j => j.alias === tableAlias)) {
1389
- // This references a related table - find the relation and add a JOIN
1390
- const relation = this.schema.relations[tableAlias];
1391
- if (relation && relation.type === 'one') {
1392
- // Get target schema from targetTableBuilder if available
1393
- let targetSchema;
1394
- if (relation.targetTableBuilder) {
1395
- const targetTableSchema = relation.targetTableBuilder.build();
1396
- targetSchema = targetTableSchema.schema;
1397
- }
1398
- // Add a JOIN for this reference
1399
- joins.push({
1400
- alias: tableAlias,
1401
- targetTable: relation.targetTable,
1402
- targetSchema,
1403
- foreignKeys: relation.foreignKeys || [relation.foreignKey || ''],
1404
- matches: relation.matches || [],
1405
- isMandatory: relation.isMandatory ?? false,
1406
- });
1407
- }
1473
+ this.addJoinForFieldRef(value, joins);
1474
+ }
1475
+ else if (value instanceof conditions_1.SqlFragment) {
1476
+ // SqlFragment may contain navigation property references - extract them
1477
+ const fieldRefs = value.getFieldRefs();
1478
+ for (const fieldRef of fieldRefs) {
1479
+ this.addJoinForFieldRef(fieldRef, joins);
1408
1480
  }
1409
1481
  }
1410
1482
  else if (value && typeof value === 'object' && !Array.isArray(value)) {
@@ -1413,6 +1485,36 @@ class SelectQueryBuilder {
1413
1485
  }
1414
1486
  }
1415
1487
  }
1488
+ /**
1489
+ * Add a JOIN for a FieldRef if it references a related table
1490
+ */
1491
+ addJoinForFieldRef(fieldRef, joins) {
1492
+ if (!fieldRef || typeof fieldRef !== 'object' || !('__tableAlias' in fieldRef) || !('__dbColumnName' in fieldRef)) {
1493
+ return;
1494
+ }
1495
+ const tableAlias = fieldRef.__tableAlias;
1496
+ if (tableAlias && tableAlias !== this.schema.name && !joins.some(j => j.alias === tableAlias)) {
1497
+ // This references a related table - find the relation and add a JOIN
1498
+ const relation = this.schema.relations[tableAlias];
1499
+ if (relation && relation.type === 'one') {
1500
+ // Get target schema from targetTableBuilder if available
1501
+ let targetSchema;
1502
+ if (relation.targetTableBuilder) {
1503
+ const targetTableSchema = relation.targetTableBuilder.build();
1504
+ targetSchema = targetTableSchema.schema;
1505
+ }
1506
+ // Add a JOIN for this reference
1507
+ joins.push({
1508
+ alias: tableAlias,
1509
+ targetTable: relation.targetTable,
1510
+ targetSchema,
1511
+ foreignKeys: relation.foreignKeys || [relation.foreignKey || ''],
1512
+ matches: relation.matches || [],
1513
+ isMandatory: relation.isMandatory ?? false,
1514
+ });
1515
+ }
1516
+ }
1517
+ }
1416
1518
  /**
1417
1519
  * Detect navigation property references in a WHERE condition and add necessary JOINs
1418
1520
  */
@@ -1599,9 +1701,10 @@ class SelectQueryBuilder {
1599
1701
  // Select all columns from the target table and group them
1600
1702
  // We'll need to use JSON object building in SQL
1601
1703
  const fieldParts = [];
1602
- for (const [colKey, col] of Object.entries(targetSchema.columns)) {
1603
- const config = col.build();
1604
- fieldParts.push(`'${colKey}', "${alias}"."${config.name}"`);
1704
+ // Performance: Use cached column name map
1705
+ const targetColMap = getColumnNameMapForSchema(targetSchema);
1706
+ for (const [colKey, dbColName] of targetColMap) {
1707
+ fieldParts.push(`'${colKey}', "${alias}"."${dbColName}"`);
1605
1708
  }
1606
1709
  selectParts.push(`json_build_object(${fieldParts.join(', ')}) as "${key}"`);
1607
1710
  }
@@ -1631,11 +1734,8 @@ class SelectQueryBuilder {
1631
1734
  const relConfig = this.schema.relations[alias];
1632
1735
  if (relConfig && relConfig.type === 'one') {
1633
1736
  // This is a reference navigation - select all fields from the target table
1634
- // Find the target table schema
1635
- let targetSchema;
1636
- if (relConfig.targetTableBuilder) {
1637
- targetSchema = relConfig.targetTableBuilder.build();
1638
- }
1737
+ // Performance: Use cached target schema
1738
+ const targetSchema = getTargetSchemaForRelation(this.schema, alias, relConfig);
1639
1739
  if (targetSchema) {
1640
1740
  // Add JOIN if not already added
1641
1741
  if (!joins.find(j => j.alias === alias)) {
@@ -1654,9 +1754,10 @@ class SelectQueryBuilder {
1654
1754
  }
1655
1755
  // Select all columns from the target table and group them into a JSON object
1656
1756
  const fieldParts = [];
1657
- for (const [colKey, col] of Object.entries(targetSchema.columns)) {
1658
- const config = col.build();
1659
- fieldParts.push(`'${colKey}', "${alias}"."${config.name}"`);
1757
+ // Performance: Use cached column name map
1758
+ const targetColMap = getColumnNameMapForSchema(targetSchema);
1759
+ for (const [colKey, dbColName] of targetColMap) {
1760
+ fieldParts.push(`'${colKey}', "${alias}"."${dbColName}"`);
1660
1761
  }
1661
1762
  selectParts.push(`json_build_object(${fieldParts.join(', ')}) as "${key}"`);
1662
1763
  continue;
@@ -2301,9 +2402,9 @@ class ReferenceQueryBuilder {
2301
2402
  createMockTargetRow() {
2302
2403
  if (this.targetTableSchema) {
2303
2404
  const mock = {};
2304
- // Add columns
2305
- for (const [colName, colBuilder] of Object.entries(this.targetTableSchema.columns)) {
2306
- const dbColumnName = colBuilder.build().name;
2405
+ // Add columns - use pre-computed column name map if available
2406
+ const columnNameMap = getColumnNameMapForSchema(this.targetTableSchema);
2407
+ for (const [colName, dbColumnName] of columnNameMap) {
2307
2408
  Object.defineProperty(mock, colName, {
2308
2409
  get: () => ({
2309
2410
  __fieldName: colName,
@@ -2356,15 +2457,8 @@ class ReferenceQueryBuilder {
2356
2457
  return mock;
2357
2458
  }
2358
2459
  else {
2359
- // Fallback: generic proxy
2360
- const handler = {
2361
- get: (target, prop) => ({
2362
- __fieldName: prop,
2363
- __dbColumnName: prop,
2364
- __tableAlias: this.relationName,
2365
- }),
2366
- };
2367
- return new Proxy({}, handler);
2460
+ // Fallback: use the shared nested proxy that supports deep property access
2461
+ return createNestedFieldRefProxy(this.relationName);
2368
2462
  }
2369
2463
  }
2370
2464
  }
@@ -2444,14 +2538,10 @@ class CollectionQueryBuilder {
2444
2538
  if (this.targetTableSchema) {
2445
2539
  // If we have schema information, create a properly typed mock
2446
2540
  const mock = {};
2447
- // Performance: Build column configs once and cache them
2448
- const columnEntries = Object.entries(this.targetTableSchema.columns);
2449
- const columnConfigs = new Map();
2450
- for (const [colName, colBuilder] of columnEntries) {
2451
- columnConfigs.set(colName, colBuilder.build().name);
2452
- }
2541
+ // Performance: Use pre-computed column name map if available
2542
+ const columnNameMap = getColumnNameMapForSchema(this.targetTableSchema);
2453
2543
  // Add columns
2454
- for (const [colName, dbColumnName] of columnConfigs) {
2544
+ for (const [colName, dbColumnName] of columnNameMap) {
2455
2545
  Object.defineProperty(mock, colName, {
2456
2546
  get: () => ({
2457
2547
  __fieldName: colName,
@@ -2500,14 +2590,8 @@ class CollectionQueryBuilder {
2500
2590
  return mock;
2501
2591
  }
2502
2592
  else {
2503
- // Fallback: generic proxy (don't cache as it's dynamic)
2504
- const handler = {
2505
- get: (target, prop) => ({
2506
- __fieldName: prop,
2507
- __dbColumnName: prop,
2508
- }),
2509
- };
2510
- return new Proxy({}, handler);
2593
+ // Fallback: use the shared nested proxy that supports deep property access
2594
+ return createNestedFieldRefProxy(this.targetTable);
2511
2595
  }
2512
2596
  }
2513
2597
  /**
@@ -2724,9 +2808,9 @@ class CollectionQueryBuilder {
2724
2808
  else {
2725
2809
  // No selector - select all fields from the target table schema
2726
2810
  if (this.targetTableSchema && this.targetTableSchema.columns) {
2727
- for (const [colName, colBuilder] of Object.entries(this.targetTableSchema.columns)) {
2728
- const colConfig = colBuilder.build ? colBuilder.build() : colBuilder;
2729
- const dbColumnName = colConfig.name || colName;
2811
+ // Performance: Use cached column name map
2812
+ const colNameMap = getColumnNameMapForSchema(this.targetTableSchema);
2813
+ for (const [colName, dbColumnName] of colNameMap) {
2730
2814
  selectedFieldConfigs.push({
2731
2815
  alias: colName,
2732
2816
  expression: `"${dbColumnName}"`,
@@ -2756,13 +2840,11 @@ class CollectionQueryBuilder {
2756
2840
  // Step 3: Build ORDER BY clause SQL (without ORDER BY keyword)
2757
2841
  let orderByClause;
2758
2842
  if (this.orderByFields.length > 0) {
2843
+ // Performance: Pre-compute column name map for ORDER BY lookups
2844
+ const colNameMap = this.targetTableSchema ? getColumnNameMapForSchema(this.targetTableSchema) : null;
2759
2845
  const orderParts = this.orderByFields.map(({ field, direction }) => {
2760
- // Look up the database column name from the schema if available
2761
- let dbColumnName = field;
2762
- if (this.targetTableSchema && this.targetTableSchema.columns[field]) {
2763
- const colBuilder = this.targetTableSchema.columns[field];
2764
- dbColumnName = colBuilder.build().name;
2765
- }
2846
+ // Look up the database column name from the cached map if available
2847
+ const dbColumnName = colNameMap?.get(field) ?? field;
2766
2848
  return `"${dbColumnName}" ${direction}`;
2767
2849
  });
2768
2850
  orderByClause = orderParts.join(', ');