linkgress-orm 0.4.40 → 0.4.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/database/bun-client.d.ts +31 -0
- package/dist/database/bun-client.d.ts.map +1 -1
- package/dist/database/bun-client.js +143 -23
- package/dist/database/bun-client.js.map +1 -1
- package/dist/database/database-client.interface.d.ts +12 -0
- package/dist/database/database-client.interface.d.ts.map +1 -1
- package/dist/database/database-client.interface.js +16 -0
- package/dist/database/database-client.interface.js.map +1 -1
- package/dist/database/index.d.ts +2 -1
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/index.js.map +1 -1
- package/dist/database/pg-client.d.ts +5 -2
- package/dist/database/pg-client.d.ts.map +1 -1
- package/dist/database/pg-client.js +21 -17
- package/dist/database/pg-client.js.map +1 -1
- package/dist/database/postgres-client.d.ts +9 -0
- package/dist/database/postgres-client.d.ts.map +1 -1
- package/dist/database/postgres-client.js +59 -15
- package/dist/database/postgres-client.js.map +1 -1
- package/dist/database/types.d.ts +9 -0
- package/dist/database/types.d.ts.map +1 -1
- package/dist/entity/db-context.d.ts +14 -2
- package/dist/entity/db-context.d.ts.map +1 -1
- package/dist/entity/db-context.js +55 -10
- package/dist/entity/db-context.js.map +1 -1
- package/dist/index.d.ts +42 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/query/collection-strategy.interface.d.ts +6 -0
- package/dist/query/collection-strategy.interface.d.ts.map +1 -1
- package/dist/query/conditions.d.ts +5 -0
- package/dist/query/conditions.d.ts.map +1 -1
- package/dist/query/conditions.js +13 -0
- package/dist/query/conditions.js.map +1 -1
- package/dist/query/cte-builder.d.ts +50 -2
- package/dist/query/cte-builder.d.ts.map +1 -1
- package/dist/query/cte-builder.js +68 -6
- package/dist/query/cte-builder.js.map +1 -1
- package/dist/query/grouped-query.d.ts +2 -0
- package/dist/query/grouped-query.d.ts.map +1 -1
- package/dist/query/grouped-query.js +4 -0
- package/dist/query/grouped-query.js.map +1 -1
- package/dist/query/query-builder.d.ts +15 -0
- package/dist/query/query-builder.d.ts.map +1 -1
- package/dist/query/query-builder.js +129 -8
- package/dist/query/query-builder.js.map +1 -1
- package/dist/query/strategies/cte-collection-strategy.js +98 -98
- package/dist/query/strategies/cte-collection-strategy.js.map +1 -1
- package/dist/query/strategies/lateral-collection-strategy.d.ts.map +1 -1
- package/dist/query/strategies/lateral-collection-strategy.js +62 -58
- package/dist/query/strategies/lateral-collection-strategy.js.map +1 -1
- package/dist/query/strategies/temptable-collection-strategy.d.ts +3 -14
- package/dist/query/strategies/temptable-collection-strategy.d.ts.map +1 -1
- package/dist/query/strategies/temptable-collection-strategy.js +106 -226
- package/dist/query/strategies/temptable-collection-strategy.js.map +1 -1
- package/dist/types/custom-types.d.ts +0 -4
- package/dist/types/custom-types.d.ts.map +1 -1
- package/dist/types/custom-types.js +29 -1
- package/dist/types/custom-types.js.map +1 -1
- package/package.json +4 -1
|
@@ -1005,6 +1005,7 @@ class SelectQueryBuilder {
|
|
|
1005
1005
|
const context = {
|
|
1006
1006
|
ctes: new Map(),
|
|
1007
1007
|
cteCounter: 0,
|
|
1008
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1008
1009
|
paramCounter: 1,
|
|
1009
1010
|
allParams: [],
|
|
1010
1011
|
executor: this.executor,
|
|
@@ -1036,6 +1037,7 @@ class SelectQueryBuilder {
|
|
|
1036
1037
|
const context = {
|
|
1037
1038
|
ctes: new Map(),
|
|
1038
1039
|
cteCounter: 0,
|
|
1040
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1039
1041
|
paramCounter: 1,
|
|
1040
1042
|
allParams: [],
|
|
1041
1043
|
executor: this.executor,
|
|
@@ -1067,6 +1069,7 @@ class SelectQueryBuilder {
|
|
|
1067
1069
|
const context = {
|
|
1068
1070
|
ctes: new Map(),
|
|
1069
1071
|
cteCounter: 0,
|
|
1072
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1070
1073
|
paramCounter: 1,
|
|
1071
1074
|
allParams: [],
|
|
1072
1075
|
executor: this.executor,
|
|
@@ -1098,6 +1101,7 @@ class SelectQueryBuilder {
|
|
|
1098
1101
|
const context = {
|
|
1099
1102
|
ctes: new Map(),
|
|
1100
1103
|
cteCounter: 0,
|
|
1104
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1101
1105
|
paramCounter: 1,
|
|
1102
1106
|
allParams: [],
|
|
1103
1107
|
executor: this.executor,
|
|
@@ -1121,6 +1125,7 @@ class SelectQueryBuilder {
|
|
|
1121
1125
|
const context = tracer.trace('createContext', () => ({
|
|
1122
1126
|
ctes: new Map(),
|
|
1123
1127
|
cteCounter: 0,
|
|
1128
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1124
1129
|
paramCounter: 1,
|
|
1125
1130
|
allParams: [],
|
|
1126
1131
|
collectionStrategy: this.collectionStrategy,
|
|
@@ -1167,6 +1172,7 @@ class SelectQueryBuilder {
|
|
|
1167
1172
|
const context = {
|
|
1168
1173
|
ctes: new Map(),
|
|
1169
1174
|
cteCounter: 0,
|
|
1175
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1170
1176
|
paramCounter: 1,
|
|
1171
1177
|
allParams: [],
|
|
1172
1178
|
executor: this.executor,
|
|
@@ -1224,6 +1230,7 @@ class SelectQueryBuilder {
|
|
|
1224
1230
|
const queryContext = {
|
|
1225
1231
|
ctes: new Map(),
|
|
1226
1232
|
cteCounter: 0,
|
|
1233
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1227
1234
|
paramCounter: context.paramCounter,
|
|
1228
1235
|
allParams: context.params,
|
|
1229
1236
|
collectionStrategy: this.collectionStrategy,
|
|
@@ -1291,6 +1298,7 @@ class SelectQueryBuilder {
|
|
|
1291
1298
|
const context = {
|
|
1292
1299
|
ctes: new Map(),
|
|
1293
1300
|
cteCounter: 0,
|
|
1301
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1294
1302
|
paramCounter: 1,
|
|
1295
1303
|
allParams: [],
|
|
1296
1304
|
collectionStrategy: this.collectionStrategy,
|
|
@@ -1335,6 +1343,7 @@ class SelectQueryBuilder {
|
|
|
1335
1343
|
const context = {
|
|
1336
1344
|
ctes: new Map(),
|
|
1337
1345
|
cteCounter: 0,
|
|
1346
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1338
1347
|
paramCounter: 1,
|
|
1339
1348
|
allParams: [],
|
|
1340
1349
|
collectionStrategy: this.collectionStrategy,
|
|
@@ -1376,6 +1385,7 @@ class SelectQueryBuilder {
|
|
|
1376
1385
|
const context = {
|
|
1377
1386
|
ctes: new Map(),
|
|
1378
1387
|
cteCounter: 0,
|
|
1388
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1379
1389
|
paramCounter: 1,
|
|
1380
1390
|
allParams: [],
|
|
1381
1391
|
executor: this.executor,
|
|
@@ -1395,6 +1405,7 @@ class SelectQueryBuilder {
|
|
|
1395
1405
|
const context = tracer.trace('createContext', () => ({
|
|
1396
1406
|
ctes: new Map(),
|
|
1397
1407
|
cteCounter: 0,
|
|
1408
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
1398
1409
|
paramCounter: 1,
|
|
1399
1410
|
allParams: [],
|
|
1400
1411
|
collectionStrategy: this.collectionStrategy,
|
|
@@ -1463,9 +1474,18 @@ class SelectQueryBuilder {
|
|
|
1463
1474
|
tracer.endPhase();
|
|
1464
1475
|
// Check if we can use fully optimized single-query approach
|
|
1465
1476
|
// Requirements: PostgresClient with querySimpleMulti support AND no parameters in base query
|
|
1477
|
+
// AND every collection expressible by the naive per-collection SQL that
|
|
1478
|
+
// executeFullyOptimized emits (plain flat list over a single-column `id`
|
|
1479
|
+
// correlation). Anything richer — collection .where(), limit/offset, scalar
|
|
1480
|
+
// aggregations, firstOrDefault, toNumberList/toStringList, DISTINCT,
|
|
1481
|
+
// constant-FK (`__LIT:`) predicates, navigation paths, selectMany joins,
|
|
1482
|
+
// nested/fragment/mapped selector fields — must go through the two-phase
|
|
1483
|
+
// path, whose strategy builders handle those features. The naive SQL used
|
|
1484
|
+
// to silently drop them (e.g. a `.where()` filter on the collection).
|
|
1466
1485
|
const canUseFullOptimization = this.client.supportsMultiStatementQueries() &&
|
|
1467
1486
|
baseParams.length === 0 &&
|
|
1468
|
-
collections.length > 0
|
|
1487
|
+
collections.length > 0 &&
|
|
1488
|
+
collections.every(c => this.isNaiveCollectionFastPathSafe(c.builder));
|
|
1469
1489
|
if (canUseFullOptimization) {
|
|
1470
1490
|
return this.executeFullyOptimized(baseSql, baseSelection, selectionResult, context, collections, tracer);
|
|
1471
1491
|
}
|
|
@@ -1584,6 +1604,102 @@ class SelectQueryBuilder {
|
|
|
1584
1604
|
* Execute using fully optimized single-query approach (PostgresClient only)
|
|
1585
1605
|
* Combines base query + all collections into ONE multi-statement query
|
|
1586
1606
|
*/
|
|
1607
|
+
/**
|
|
1608
|
+
* Whether a collection can be served by the naive per-collection SQL emitted
|
|
1609
|
+
* in {@link executeFullyOptimized} (`SELECT fk AS parent_id, <flat fields>
|
|
1610
|
+
* FROM target WHERE fk IN (SELECT __pk_id FROM tmp_base)`). That SQL knows
|
|
1611
|
+
* nothing about collection filters, pagination, aggregations, constant-FK
|
|
1612
|
+
* predicates, navigation correlation, or mapper transforms — so any of those
|
|
1613
|
+
* features disqualifies the fast path and the query falls back to the
|
|
1614
|
+
* two-phase execution whose strategy builders implement them correctly.
|
|
1615
|
+
*/
|
|
1616
|
+
isNaiveCollectionFastPathSafe(builder) {
|
|
1617
|
+
const b = builder;
|
|
1618
|
+
if (b.whereCond) {
|
|
1619
|
+
return false;
|
|
1620
|
+
}
|
|
1621
|
+
if (b.limitValue != null || b.offsetValue != null) {
|
|
1622
|
+
return false;
|
|
1623
|
+
}
|
|
1624
|
+
if (b.aggregationType) {
|
|
1625
|
+
return false;
|
|
1626
|
+
}
|
|
1627
|
+
if (b.flattenResultType) {
|
|
1628
|
+
return false;
|
|
1629
|
+
}
|
|
1630
|
+
if (b.isDistinct) {
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
if (typeof b.isSingleResult === 'function' && b.isSingleResult()) {
|
|
1634
|
+
return false;
|
|
1635
|
+
}
|
|
1636
|
+
if ((b.navigationPath?.length ?? 0) > 0 || (b.selectManyJoins?.length ?? 0) > 0 || b.foreignKeyTableAlias) {
|
|
1637
|
+
return false;
|
|
1638
|
+
}
|
|
1639
|
+
// Single-column `fk = parent.id` correlation only; composite keys and
|
|
1640
|
+
// `__LIT:` constant predicates (SCD2 is_current etc.) need the strategies.
|
|
1641
|
+
const hasLiteralMarker = (arr) => (arr ?? []).some(entry => typeof entry === 'string' && entry.startsWith('__LIT:'));
|
|
1642
|
+
if (hasLiteralMarker(b.foreignKeys) || hasLiteralMarker(b.matches)) {
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
if ((b.foreignKeys?.length ?? 0) > 1) {
|
|
1646
|
+
return false;
|
|
1647
|
+
}
|
|
1648
|
+
if ((b.matches?.length ?? 0) > 0 && !(b.matches.length === 1 && b.matches[0] === 'id')) {
|
|
1649
|
+
return false;
|
|
1650
|
+
}
|
|
1651
|
+
// Selector must project only direct, unmapped FieldRef columns — nested
|
|
1652
|
+
// objects, SQL fragments, and nested collection builders are silently
|
|
1653
|
+
// dropped by the naive field loop, and custom mappers would be skipped.
|
|
1654
|
+
// Column types must additionally be "jsonb-stable": the naive path returns
|
|
1655
|
+
// DRIVER-native values (numeric => string, timestamp => Date), while every
|
|
1656
|
+
// aggregating strategy returns json_agg semantics (numeric => number,
|
|
1657
|
+
// timestamp => ISO string). Only allow types where both representations
|
|
1658
|
+
// are identical, so all execution paths stay value-equal.
|
|
1659
|
+
if (!b.selector || typeof b.createMockItem !== 'function') {
|
|
1660
|
+
return false;
|
|
1661
|
+
}
|
|
1662
|
+
const JSONB_STABLE_TYPES = new Set([
|
|
1663
|
+
'smallint',
|
|
1664
|
+
'integer',
|
|
1665
|
+
'smallserial',
|
|
1666
|
+
'serial',
|
|
1667
|
+
'real',
|
|
1668
|
+
'double precision',
|
|
1669
|
+
'boolean',
|
|
1670
|
+
'varchar',
|
|
1671
|
+
'char',
|
|
1672
|
+
'text',
|
|
1673
|
+
'uuid',
|
|
1674
|
+
'json',
|
|
1675
|
+
'jsonb',
|
|
1676
|
+
]);
|
|
1677
|
+
try {
|
|
1678
|
+
const selected = b.selector(b.createMockItem());
|
|
1679
|
+
for (const value of Object.values(selected)) {
|
|
1680
|
+
if (!(value && typeof value === 'object' && '__dbColumnName' in value)) {
|
|
1681
|
+
return false;
|
|
1682
|
+
}
|
|
1683
|
+
if (value.__mapper) {
|
|
1684
|
+
return false;
|
|
1685
|
+
}
|
|
1686
|
+
const fieldName = value.__fieldName;
|
|
1687
|
+
const meta = b.targetTableSchema?.columnMetadataCache?.get?.(fieldName);
|
|
1688
|
+
if (meta?.hasMapper) {
|
|
1689
|
+
return false;
|
|
1690
|
+
}
|
|
1691
|
+
const columnBuilder = b.targetTableSchema?.columns?.[fieldName];
|
|
1692
|
+
const columnConfig = typeof columnBuilder?.build === 'function' ? columnBuilder.build() : null;
|
|
1693
|
+
if (!columnConfig || !JSONB_STABLE_TYPES.has(columnConfig.type)) {
|
|
1694
|
+
return false;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
catch {
|
|
1699
|
+
return false;
|
|
1700
|
+
}
|
|
1701
|
+
return true;
|
|
1702
|
+
}
|
|
1587
1703
|
async executeFullyOptimized(baseSql, baseSelection, selectionResult, context, collections, tracer) {
|
|
1588
1704
|
tracer.startPhase('queryBuild');
|
|
1589
1705
|
const baseTempTable = `tmp_base_${context.cteCounter++}`;
|
|
@@ -1999,6 +2115,7 @@ class SelectQueryBuilder {
|
|
|
1999
2115
|
const context = {
|
|
2000
2116
|
ctes: new Map(),
|
|
2001
2117
|
cteCounter: 0,
|
|
2118
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
2002
2119
|
paramCounter: 1,
|
|
2003
2120
|
allParams: [],
|
|
2004
2121
|
placeholders: new Map(),
|
|
@@ -2629,11 +2746,11 @@ class SelectQueryBuilder {
|
|
|
2629
2746
|
joinClauses.push(collection.joinClause);
|
|
2630
2747
|
}
|
|
2631
2748
|
// Build the final CTE query
|
|
2632
|
-
const sql = `WITH "__mutation__" AS (
|
|
2633
|
-
${mutationWithReturning}
|
|
2634
|
-
)
|
|
2635
|
-
SELECT ${selectParts.join(', ')}
|
|
2636
|
-
FROM "__mutation__"
|
|
2749
|
+
const sql = `WITH "__mutation__" AS (
|
|
2750
|
+
${mutationWithReturning}
|
|
2751
|
+
)
|
|
2752
|
+
SELECT ${selectParts.join(', ')}
|
|
2753
|
+
FROM "__mutation__"
|
|
2637
2754
|
${joinClauses.join('\n')}`;
|
|
2638
2755
|
return { sql, params: allParams, nestedPaths };
|
|
2639
2756
|
}
|
|
@@ -4846,6 +4963,7 @@ ${joinClauses.join('\n')}`;
|
|
|
4846
4963
|
const context = {
|
|
4847
4964
|
ctes: new Map(),
|
|
4848
4965
|
cteCounter: 0,
|
|
4966
|
+
useJsonArrayAggregation: !this.client.supportsBinaryArrayResults(),
|
|
4849
4967
|
paramCounter: outerContext.paramCounter,
|
|
4850
4968
|
allParams: outerContext.params,
|
|
4851
4969
|
placeholders: outerContext.placeholders, // Pass placeholders through for prepared statements
|
|
@@ -6066,8 +6184,10 @@ class CollectionQueryBuilder {
|
|
|
6066
6184
|
const firstField = selectedFieldConfigs[0];
|
|
6067
6185
|
arrayField = firstField.alias;
|
|
6068
6186
|
}
|
|
6069
|
-
// Use typed empty array literal (PostgreSQL will infer type from array_agg)
|
|
6070
|
-
|
|
6187
|
+
// Use typed empty array literal (PostgreSQL will infer type from array_agg).
|
|
6188
|
+
// Drivers that cannot decode native arrays aggregate via json_agg instead,
|
|
6189
|
+
// so the default must be a JSON empty array for those.
|
|
6190
|
+
defaultValue = context.useJsonArrayAggregation ? "'[]'::json" : "'{}'";
|
|
6071
6191
|
}
|
|
6072
6192
|
else {
|
|
6073
6193
|
// JSON aggregation (default) - use JSON instead of JSONB for better performance
|
|
@@ -6121,6 +6241,7 @@ class CollectionQueryBuilder {
|
|
|
6121
6241
|
aggregateExpression,
|
|
6122
6242
|
arrayField,
|
|
6123
6243
|
defaultValue,
|
|
6244
|
+
useJsonArrayAggregation: context.useJsonArrayAggregation,
|
|
6124
6245
|
// Use the reserved counter for LATERAL strategy, otherwise increment as before
|
|
6125
6246
|
counter: reservedCounter !== undefined ? reservedCounter : context.cteCounter++,
|
|
6126
6247
|
navigationJoins: allNavigationJoins.length > 0 ? allNavigationJoins : undefined,
|