@dbsp/adapter-pgsql 1.2.1 → 1.4.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.
- package/README.md +4 -0
- package/dist/index.d.ts +45 -1
- package/dist/index.js +2304 -1534
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -949,6 +949,42 @@ var init_ast_helpers = __esm({
|
|
|
949
949
|
}
|
|
950
950
|
});
|
|
951
951
|
|
|
952
|
+
// src/binding-registry.ts
|
|
953
|
+
function emittedBindName(name, naming) {
|
|
954
|
+
return naming.toDatabase(name);
|
|
955
|
+
}
|
|
956
|
+
function hasBindingName(bindingNames, name, naming) {
|
|
957
|
+
return bindingNames?.has(emittedBindName(name, naming)) ?? false;
|
|
958
|
+
}
|
|
959
|
+
function schemaForFromName(schemaName, fromName, bindingNames, naming) {
|
|
960
|
+
return hasBindingName(bindingNames, fromName, naming) ? void 0 : schemaName;
|
|
961
|
+
}
|
|
962
|
+
function withBindingName(bindingNames, name, naming) {
|
|
963
|
+
const next = new Set(bindingNames ?? []);
|
|
964
|
+
next.add(emittedBindName(name, naming));
|
|
965
|
+
return next;
|
|
966
|
+
}
|
|
967
|
+
var init_binding_registry = __esm({
|
|
968
|
+
"src/binding-registry.ts"() {
|
|
969
|
+
"use strict";
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
// src/dialect-capabilities.ts
|
|
974
|
+
function supportsDialectCapability(capabilities, flag) {
|
|
975
|
+
return capabilities?.[flag] !== false;
|
|
976
|
+
}
|
|
977
|
+
function assertDialectCapability(capabilities, flag, feature) {
|
|
978
|
+
if (!supportsDialectCapability(capabilities, flag)) {
|
|
979
|
+
throw new Error(`${feature} not supported by this adapter`);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
var init_dialect_capabilities = __esm({
|
|
983
|
+
"src/dialect-capabilities.ts"() {
|
|
984
|
+
"use strict";
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
|
|
952
988
|
// src/param-ref.ts
|
|
953
989
|
function validateParamRef(paramRef) {
|
|
954
990
|
const errors = [];
|
|
@@ -2813,6 +2849,7 @@ var init_param_intent = __esm({
|
|
|
2813
2849
|
});
|
|
2814
2850
|
|
|
2815
2851
|
// src/handlers/where/utils.ts
|
|
2852
|
+
import { validateTypeName as validateTypeName3 } from "@dbsp/core";
|
|
2816
2853
|
import { isFieldRef, isParamIntent as isParamIntent3 } from "@dbsp/types";
|
|
2817
2854
|
function buildColumnRef(column, ctx) {
|
|
2818
2855
|
if (column.includes(".")) {
|
|
@@ -2874,7 +2911,11 @@ function resolveColumnPgType(columnName, ctx) {
|
|
|
2874
2911
|
if (!table) return void 0;
|
|
2875
2912
|
const column = table.columns.find((c) => c.name === columnName);
|
|
2876
2913
|
if (!column) return void 0;
|
|
2877
|
-
if (column.originalDbType)
|
|
2914
|
+
if (column.originalDbType) {
|
|
2915
|
+
const typeName = column.originalDbType.trim();
|
|
2916
|
+
validateTypeName3(typeName);
|
|
2917
|
+
return typeName;
|
|
2918
|
+
}
|
|
2878
2919
|
return void 0;
|
|
2879
2920
|
}
|
|
2880
2921
|
var init_utils = __esm({
|
|
@@ -2883,7 +2924,6 @@ var init_utils = __esm({
|
|
|
2883
2924
|
init_ast_helpers();
|
|
2884
2925
|
init_param_intent();
|
|
2885
2926
|
init_param_ref();
|
|
2886
|
-
init_validate();
|
|
2887
2927
|
init_types();
|
|
2888
2928
|
init_param_intent();
|
|
2889
2929
|
}
|
|
@@ -2915,6 +2955,7 @@ var init_any = __esm({
|
|
|
2915
2955
|
"src/handlers/where/any.ts"() {
|
|
2916
2956
|
"use strict";
|
|
2917
2957
|
init_compiler_utils();
|
|
2958
|
+
init_dialect_capabilities();
|
|
2918
2959
|
init_param_intent();
|
|
2919
2960
|
init_param_ref();
|
|
2920
2961
|
init_types();
|
|
@@ -2922,6 +2963,11 @@ var init_any = __esm({
|
|
|
2922
2963
|
anyHandler = {
|
|
2923
2964
|
operators: [COLLECTION_OPERATORS.ANY],
|
|
2924
2965
|
compile(decision, ctx, state) {
|
|
2966
|
+
assertDialectCapability(
|
|
2967
|
+
ctx.dialectCapabilities,
|
|
2968
|
+
"supportsArrayType",
|
|
2969
|
+
"ANY array operator is"
|
|
2970
|
+
);
|
|
2925
2971
|
const column = decision.column;
|
|
2926
2972
|
if (!column) {
|
|
2927
2973
|
throw new Error("ANY handler requires a column");
|
|
@@ -3117,6 +3163,9 @@ function buildCorrelation(sourceAlias, sourceColumn, targetAlias, targetColumn,
|
|
|
3117
3163
|
const right = columnRef(targetColumn, targetAlias, void 0, ctx.naming);
|
|
3118
3164
|
return eqExpr(left, right);
|
|
3119
3165
|
}
|
|
3166
|
+
function schemaForExistsFromName(ctx, fromName) {
|
|
3167
|
+
return schemaForFromName(ctx.schema, fromName, ctx.bindingNames, ctx.naming);
|
|
3168
|
+
}
|
|
3120
3169
|
function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
3121
3170
|
const relation = decision.relation;
|
|
3122
3171
|
const targetTable = decision.targetTable ?? relation;
|
|
@@ -3160,7 +3209,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3160
3209
|
let fromNode = rangeVar(
|
|
3161
3210
|
targetTable,
|
|
3162
3211
|
targetAlias,
|
|
3163
|
-
ctx
|
|
3212
|
+
schemaForExistsFromName(ctx, targetTable),
|
|
3164
3213
|
ctx.naming
|
|
3165
3214
|
);
|
|
3166
3215
|
const includeDecisions = decision.include;
|
|
@@ -3219,7 +3268,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3219
3268
|
const joinRangeVar = rangeVar(
|
|
3220
3269
|
joinTargetTable,
|
|
3221
3270
|
joinAlias,
|
|
3222
|
-
ctx
|
|
3271
|
+
schemaForExistsFromName(ctx, joinTargetTable),
|
|
3223
3272
|
ctx.naming
|
|
3224
3273
|
);
|
|
3225
3274
|
fromNode = joinExpr(joinType, fromNode, joinRangeVar, joinQuals);
|
|
@@ -3245,6 +3294,7 @@ var init_exists = __esm({
|
|
|
3245
3294
|
"use strict";
|
|
3246
3295
|
init_assert_field();
|
|
3247
3296
|
init_ast_helpers();
|
|
3297
|
+
init_binding_registry();
|
|
3248
3298
|
existsHandler = {
|
|
3249
3299
|
operators: ["exists", "some"],
|
|
3250
3300
|
compile(decision, ctx, state, dispatch) {
|
|
@@ -3315,6 +3365,21 @@ function createNotInExpr(columnNode, state, values, columnType) {
|
|
|
3315
3365
|
}
|
|
3316
3366
|
};
|
|
3317
3367
|
}
|
|
3368
|
+
function createLiteralInListExpr(columnNode, state, values, negated) {
|
|
3369
|
+
const paramRefs = values.map((value) => {
|
|
3370
|
+
state.paramIndex++;
|
|
3371
|
+
state.parameters.push(value);
|
|
3372
|
+
return createParamRef(state.paramIndex);
|
|
3373
|
+
});
|
|
3374
|
+
return {
|
|
3375
|
+
A_Expr: {
|
|
3376
|
+
kind: "AEXPR_IN",
|
|
3377
|
+
name: [{ String: { sval: negated ? "<>" : "=" } }],
|
|
3378
|
+
lexpr: columnNode,
|
|
3379
|
+
rexpr: { List: { items: paramRefs } }
|
|
3380
|
+
}
|
|
3381
|
+
};
|
|
3382
|
+
}
|
|
3318
3383
|
function isWholeArrayParamList(value) {
|
|
3319
3384
|
return value.length === 1 && isParamIntent4(value[0]) && Array.isArray(value[0].value);
|
|
3320
3385
|
}
|
|
@@ -3330,6 +3395,7 @@ var init_in = __esm({
|
|
|
3330
3395
|
"src/handlers/where/in.ts"() {
|
|
3331
3396
|
"use strict";
|
|
3332
3397
|
init_ast_helpers();
|
|
3398
|
+
init_dialect_capabilities();
|
|
3333
3399
|
init_param_intent();
|
|
3334
3400
|
init_param_ref();
|
|
3335
3401
|
init_types();
|
|
@@ -3358,7 +3424,11 @@ var init_in = __esm({
|
|
|
3358
3424
|
}
|
|
3359
3425
|
const columnNode = buildColumnRef(column, ctx);
|
|
3360
3426
|
const columnType = resolveColumnPgType(column, ctx);
|
|
3361
|
-
|
|
3427
|
+
const isNotIn = operator === COLLECTION_OPERATORS.NOT_IN || operator === "notIn";
|
|
3428
|
+
if (!supportsDialectCapability(ctx.dialectCapabilities, "supportsArrayType")) {
|
|
3429
|
+
return createLiteralInListExpr(columnNode, state, values, isNotIn);
|
|
3430
|
+
}
|
|
3431
|
+
if (isNotIn) {
|
|
3362
3432
|
return createNotInExpr(columnNode, state, values, columnType);
|
|
3363
3433
|
}
|
|
3364
3434
|
return createInExpr(columnNode, state, values, columnType);
|
|
@@ -3372,10 +3442,16 @@ var jsonContainsHandler, jsonExistsHandler, jsonComparisonHandler;
|
|
|
3372
3442
|
var init_json = __esm({
|
|
3373
3443
|
"src/handlers/where/json.ts"() {
|
|
3374
3444
|
"use strict";
|
|
3445
|
+
init_dialect_capabilities();
|
|
3375
3446
|
init_utils();
|
|
3376
3447
|
jsonContainsHandler = {
|
|
3377
3448
|
operators: ["jsonContains", "jsonContainedBy"],
|
|
3378
3449
|
compile(decision, ctx, state) {
|
|
3450
|
+
assertDialectCapability(
|
|
3451
|
+
ctx.dialectCapabilities,
|
|
3452
|
+
"supportsJsonOperators",
|
|
3453
|
+
"JSON operators are"
|
|
3454
|
+
);
|
|
3379
3455
|
const column = decision.column;
|
|
3380
3456
|
if (!column) {
|
|
3381
3457
|
throw new Error("JSON contains handler requires a column");
|
|
@@ -3396,6 +3472,11 @@ var init_json = __esm({
|
|
|
3396
3472
|
jsonExistsHandler = {
|
|
3397
3473
|
operators: ["jsonExists"],
|
|
3398
3474
|
compile(decision, ctx, state) {
|
|
3475
|
+
assertDialectCapability(
|
|
3476
|
+
ctx.dialectCapabilities,
|
|
3477
|
+
"supportsJsonOperators",
|
|
3478
|
+
"JSON operators are"
|
|
3479
|
+
);
|
|
3399
3480
|
const column = decision.column;
|
|
3400
3481
|
if (!column) {
|
|
3401
3482
|
throw new Error("JSON exists handler requires a column");
|
|
@@ -3415,6 +3496,11 @@ var init_json = __esm({
|
|
|
3415
3496
|
jsonComparisonHandler = {
|
|
3416
3497
|
operators: ["jsonComparison"],
|
|
3417
3498
|
compile(decision, ctx, state) {
|
|
3499
|
+
assertDialectCapability(
|
|
3500
|
+
ctx.dialectCapabilities,
|
|
3501
|
+
"supportsJsonOperators",
|
|
3502
|
+
"JSON operators are"
|
|
3503
|
+
);
|
|
3418
3504
|
const column = decision.column;
|
|
3419
3505
|
if (!column) {
|
|
3420
3506
|
throw new Error("JSON comparison handler requires a column");
|
|
@@ -3592,6 +3678,7 @@ var RANGE_OP_MAP, rangeHandler;
|
|
|
3592
3678
|
var init_range = __esm({
|
|
3593
3679
|
"src/handlers/where/range.ts"() {
|
|
3594
3680
|
"use strict";
|
|
3681
|
+
init_dialect_capabilities();
|
|
3595
3682
|
init_param_intent();
|
|
3596
3683
|
init_param_ref();
|
|
3597
3684
|
init_types();
|
|
@@ -3604,6 +3691,11 @@ var init_range = __esm({
|
|
|
3604
3691
|
rangeHandler = {
|
|
3605
3692
|
operators: ["contains", "containedBy", "overlaps"],
|
|
3606
3693
|
compile(decision, ctx, state) {
|
|
3694
|
+
assertDialectCapability(
|
|
3695
|
+
ctx.dialectCapabilities,
|
|
3696
|
+
"supportsRangeTypes",
|
|
3697
|
+
"Range operators are"
|
|
3698
|
+
);
|
|
3607
3699
|
const column = decision.column;
|
|
3608
3700
|
if (!column) {
|
|
3609
3701
|
throw new Error("Range handler requires a column");
|
|
@@ -3659,11 +3751,15 @@ function toHandlerContext(ctx) {
|
|
|
3659
3751
|
currentAlias: ctx.currentAlias ?? ctx.rootTable,
|
|
3660
3752
|
maxRecursiveDepth: 100,
|
|
3661
3753
|
...ctx.schemaName !== void 0 && { schema: ctx.schemaName },
|
|
3754
|
+
...ctx.dialectCapabilities !== void 0 && {
|
|
3755
|
+
dialectCapabilities: ctx.dialectCapabilities
|
|
3756
|
+
},
|
|
3757
|
+
...ctx.bindingNames !== void 0 && { bindingNames: ctx.bindingNames },
|
|
3662
3758
|
...ctx.model !== void 0 && { model: ctx.model },
|
|
3663
3759
|
...ctx.outerTable !== void 0 && { outerAlias: ctx.outerTable }
|
|
3664
3760
|
};
|
|
3665
3761
|
}
|
|
3666
|
-
function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, schemaName, use = "rawExists") {
|
|
3762
|
+
function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, schemaName, use = "rawExists", bindingNames, dialectCapabilities) {
|
|
3667
3763
|
assertNoUnsupportedSubqueryModifiers(intent, use);
|
|
3668
3764
|
if (intent.where && containsOuterRef(intent.where)) {
|
|
3669
3765
|
throw new Error(
|
|
@@ -3708,9 +3804,14 @@ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, s
|
|
|
3708
3804
|
}
|
|
3709
3805
|
const stmt = {
|
|
3710
3806
|
targetList,
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3807
|
+
fromClause: [
|
|
3808
|
+
rangeVar(
|
|
3809
|
+
targetTable,
|
|
3810
|
+
innerAlias,
|
|
3811
|
+
schemaForFromName(schemaName, targetTable, bindingNames, naming),
|
|
3812
|
+
naming
|
|
3813
|
+
)
|
|
3814
|
+
]
|
|
3714
3815
|
};
|
|
3715
3816
|
let paramCount = 0;
|
|
3716
3817
|
let innerParameters = [];
|
|
@@ -3724,6 +3825,9 @@ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, s
|
|
|
3724
3825
|
aliases: /* @__PURE__ */ new Map(),
|
|
3725
3826
|
paramState: innerState,
|
|
3726
3827
|
naming,
|
|
3828
|
+
...schemaName !== void 0 && { schemaName },
|
|
3829
|
+
...dialectCapabilities !== void 0 && { dialectCapabilities },
|
|
3830
|
+
...bindingNames !== void 0 && { bindingNames },
|
|
3727
3831
|
compileSubquery: (_nestedIntent, _nestedOffset) => {
|
|
3728
3832
|
throw new Error(
|
|
3729
3833
|
"buildSubqueryFromIntent: nested subquery not supported"
|
|
@@ -4101,6 +4205,7 @@ var init_compile_where = __esm({
|
|
|
4101
4205
|
init_custom();
|
|
4102
4206
|
init_handlers();
|
|
4103
4207
|
init_assert_field();
|
|
4208
|
+
init_binding_registry();
|
|
4104
4209
|
init_types();
|
|
4105
4210
|
init_utils();
|
|
4106
4211
|
init_intent_to_decisions();
|
|
@@ -4145,7 +4250,9 @@ var init_raw_exists = __esm({
|
|
|
4145
4250
|
state.paramIndex,
|
|
4146
4251
|
ctx.naming,
|
|
4147
4252
|
ctx.schema,
|
|
4148
|
-
"rawExists"
|
|
4253
|
+
"rawExists",
|
|
4254
|
+
ctx.bindingNames,
|
|
4255
|
+
ctx.dialectCapabilities
|
|
4149
4256
|
);
|
|
4150
4257
|
if (innerParams) {
|
|
4151
4258
|
for (const p of innerParams) {
|
|
@@ -4381,7 +4488,19 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
|
|
|
4381
4488
|
}
|
|
4382
4489
|
const stmt = {
|
|
4383
4490
|
targetList: [{ ResTarget: { val: targetVal } }],
|
|
4384
|
-
fromClause: [
|
|
4491
|
+
fromClause: [
|
|
4492
|
+
rangeVar(
|
|
4493
|
+
targetTable,
|
|
4494
|
+
targetAlias,
|
|
4495
|
+
schemaForFromName(
|
|
4496
|
+
ctx.schema,
|
|
4497
|
+
targetTable,
|
|
4498
|
+
ctx.bindingNames,
|
|
4499
|
+
ctx.naming
|
|
4500
|
+
),
|
|
4501
|
+
ctx.naming
|
|
4502
|
+
)
|
|
4503
|
+
],
|
|
4385
4504
|
...whereClause && { whereClause }
|
|
4386
4505
|
};
|
|
4387
4506
|
if (decision.orderBy && decision.orderBy.length > 0) {
|
|
@@ -4418,6 +4537,7 @@ var init_subquery_emission = __esm({
|
|
|
4418
4537
|
"src/subquery-emission.ts"() {
|
|
4419
4538
|
"use strict";
|
|
4420
4539
|
init_ast_helpers();
|
|
4540
|
+
init_binding_registry();
|
|
4421
4541
|
init_intent_to_decisions();
|
|
4422
4542
|
init_param_intent();
|
|
4423
4543
|
}
|
|
@@ -5941,11 +6061,17 @@ var init_json2 = __esm({
|
|
|
5941
6061
|
"src/handlers/expression/json.ts"() {
|
|
5942
6062
|
"use strict";
|
|
5943
6063
|
init_ast_helpers();
|
|
6064
|
+
init_dialect_capabilities();
|
|
5944
6065
|
init_param_intent();
|
|
5945
6066
|
init_utils();
|
|
5946
6067
|
jsonExtractHandler = {
|
|
5947
6068
|
types: ["jsonExtract"],
|
|
5948
6069
|
compile(decision, ctx, state) {
|
|
6070
|
+
assertDialectCapability(
|
|
6071
|
+
ctx.dialectCapabilities,
|
|
6072
|
+
"supportsJsonOperators",
|
|
6073
|
+
"JSON operators are"
|
|
6074
|
+
);
|
|
5949
6075
|
const column = decision.column;
|
|
5950
6076
|
if (!column) {
|
|
5951
6077
|
throw new Error("JSON extract handler requires a column");
|
|
@@ -5973,6 +6099,11 @@ var init_json2 = __esm({
|
|
|
5973
6099
|
jsonPathExtractHandler = {
|
|
5974
6100
|
types: ["jsonPathExtract"],
|
|
5975
6101
|
compile(decision, ctx, state) {
|
|
6102
|
+
assertDialectCapability(
|
|
6103
|
+
ctx.dialectCapabilities,
|
|
6104
|
+
"supportsJsonOperators",
|
|
6105
|
+
"JSON operators are"
|
|
6106
|
+
);
|
|
5976
6107
|
const column = decision.column;
|
|
5977
6108
|
if (!column) {
|
|
5978
6109
|
throw new Error("JSON path extract handler requires a column");
|
|
@@ -7044,12 +7175,14 @@ init_ast_helpers();
|
|
|
7044
7175
|
// src/compiler.ts
|
|
7045
7176
|
init_assert_field();
|
|
7046
7177
|
init_ast_helpers();
|
|
7178
|
+
init_binding_registry();
|
|
7047
7179
|
import { InvalidOperationError as InvalidOperationError2 } from "@dbsp/core";
|
|
7048
7180
|
import {
|
|
7049
7181
|
isParamIntent as isParamIntent6,
|
|
7050
7182
|
NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
|
|
7051
7183
|
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
|
|
7052
7184
|
} from "@dbsp/types";
|
|
7185
|
+
import { getNqlBindingRefName, isNqlBindingRef } from "@dbsp/types/internal";
|
|
7053
7186
|
|
|
7054
7187
|
// src/pgsql-deparser.ts
|
|
7055
7188
|
function deparse(node) {
|
|
@@ -8104,6 +8237,7 @@ function deparseQuoted(ast) {
|
|
|
8104
8237
|
}
|
|
8105
8238
|
|
|
8106
8239
|
// src/compiler.ts
|
|
8240
|
+
init_dialect_capabilities();
|
|
8107
8241
|
init_case_value();
|
|
8108
8242
|
init_custom();
|
|
8109
8243
|
init_expression();
|
|
@@ -8336,6 +8470,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8336
8470
|
defaultPk;
|
|
8337
8471
|
deriveFk;
|
|
8338
8472
|
model;
|
|
8473
|
+
dialectCapabilities;
|
|
8474
|
+
bindingNames;
|
|
8339
8475
|
/** Mutable state shared with extracted condition/value compilation functions */
|
|
8340
8476
|
state = {
|
|
8341
8477
|
parameters: [],
|
|
@@ -8370,6 +8506,24 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8370
8506
|
this.defaultPk = options.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
|
|
8371
8507
|
this.deriveFk = options.deriveFkColumnName ?? defaultFkDerivation;
|
|
8372
8508
|
this.model = options.model ?? void 0;
|
|
8509
|
+
this.dialectCapabilities = options.dialectCapabilities;
|
|
8510
|
+
this.bindingNames = options.bindingNames;
|
|
8511
|
+
}
|
|
8512
|
+
childCompilerOptions(overrides = {}) {
|
|
8513
|
+
return {
|
|
8514
|
+
naming: this.naming,
|
|
8515
|
+
...this.schema !== void 0 && { schema: this.schema },
|
|
8516
|
+
defaultPkColumnName: this.defaultPk,
|
|
8517
|
+
deriveFkColumnName: this.deriveFk,
|
|
8518
|
+
...this.model !== void 0 && { model: this.model },
|
|
8519
|
+
...this.dialectCapabilities !== void 0 && {
|
|
8520
|
+
dialectCapabilities: this.dialectCapabilities
|
|
8521
|
+
},
|
|
8522
|
+
...this.bindingNames !== void 0 && {
|
|
8523
|
+
bindingNames: this.bindingNames
|
|
8524
|
+
},
|
|
8525
|
+
...overrides
|
|
8526
|
+
};
|
|
8373
8527
|
}
|
|
8374
8528
|
/** Build immutable context for handler-based WHERE compilation */
|
|
8375
8529
|
handlerCtx() {
|
|
@@ -8381,6 +8535,10 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8381
8535
|
defaultPkColumnName: this.defaultPk,
|
|
8382
8536
|
deriveFkColumnName: this.deriveFk,
|
|
8383
8537
|
...this.schema != null && { schema: this.schema },
|
|
8538
|
+
...this.dialectCapabilities != null && {
|
|
8539
|
+
dialectCapabilities: this.dialectCapabilities
|
|
8540
|
+
},
|
|
8541
|
+
...this.bindingNames != null && { bindingNames: this.bindingNames },
|
|
8384
8542
|
...this.model != null && { model: this.model }
|
|
8385
8543
|
};
|
|
8386
8544
|
}
|
|
@@ -8690,6 +8848,10 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8690
8848
|
defaultPkColumnName: this.defaultPk,
|
|
8691
8849
|
deriveFkColumnName: this.deriveFk,
|
|
8692
8850
|
...plan.schema ?? this.schema ? { schema: plan.schema ?? this.schema } : {},
|
|
8851
|
+
...this.dialectCapabilities != null && {
|
|
8852
|
+
dialectCapabilities: this.dialectCapabilities
|
|
8853
|
+
},
|
|
8854
|
+
...this.bindingNames != null && { bindingNames: this.bindingNames },
|
|
8693
8855
|
...this.model != null && { model: this.model },
|
|
8694
8856
|
compileSubquery: (query, paramOffset) => this.compileExpressionSubquery(query, paramOffset),
|
|
8695
8857
|
compileNqlSelectExpression: (value, handlerCtx, state) => this.compileNqlFunctionArg(value, handlerCtx, state)
|
|
@@ -8705,15 +8867,16 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8705
8867
|
joins: []
|
|
8706
8868
|
};
|
|
8707
8869
|
}
|
|
8870
|
+
schemaForRangeVar(plan, table) {
|
|
8871
|
+
return schemaForFromName(
|
|
8872
|
+
plan.schema ?? this.schema,
|
|
8873
|
+
table,
|
|
8874
|
+
this.bindingNames,
|
|
8875
|
+
this.naming
|
|
8876
|
+
);
|
|
8877
|
+
}
|
|
8708
8878
|
compileExpressionSubquery(query, paramOffset) {
|
|
8709
|
-
const innerCompiler = new _PlanCompiler(
|
|
8710
|
-
naming: this.naming,
|
|
8711
|
-
...this.schema !== void 0 && {
|
|
8712
|
-
schema: this.schema
|
|
8713
|
-
},
|
|
8714
|
-
defaultPkColumnName: this.defaultPk,
|
|
8715
|
-
deriveFkColumnName: this.deriveFk
|
|
8716
|
-
});
|
|
8879
|
+
const innerCompiler = new _PlanCompiler(this.childCompilerOptions());
|
|
8717
8880
|
const innerPlan = {
|
|
8718
8881
|
rootTable: query.from,
|
|
8719
8882
|
decisions: intentToDecisions(query, query.from)
|
|
@@ -8731,14 +8894,14 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8731
8894
|
return funcCall(functionName, argNodes);
|
|
8732
8895
|
}
|
|
8733
8896
|
compileNqlFunctionArg(arg, ctx, state) {
|
|
8897
|
+
if (isNqlBindingRef(arg)) {
|
|
8898
|
+
return buildColumnRef(getNqlBindingRefName(arg), ctx);
|
|
8899
|
+
}
|
|
8734
8900
|
if (typeof arg === "string") {
|
|
8735
8901
|
return buildColumnRef(arg, ctx);
|
|
8736
8902
|
}
|
|
8737
8903
|
if (typeof arg === "object" && arg !== null) {
|
|
8738
8904
|
const record = arg;
|
|
8739
|
-
if (typeof record.$ref === "string") {
|
|
8740
|
-
return buildColumnRef(record.$ref, ctx);
|
|
8741
|
-
}
|
|
8742
8905
|
if (typeof record.kind !== "string") {
|
|
8743
8906
|
const legacyKind = typeof record.$op === "string" ? `$op:${record.$op}` : typeof record.$fn === "string" ? `$fn:${record.$fn}` : "object";
|
|
8744
8907
|
throw new UnhandledNqlSelectExpressionKindError(legacyKind);
|
|
@@ -9053,24 +9216,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9053
9216
|
const ctx = {
|
|
9054
9217
|
...this.createHandlerContext(plan, plan.rootTable),
|
|
9055
9218
|
compileSubquery(query, paramOffset) {
|
|
9056
|
-
|
|
9057
|
-
naming: outerThis.naming,
|
|
9058
|
-
...outerThis.schema !== void 0 && {
|
|
9059
|
-
schema: outerThis.schema
|
|
9060
|
-
},
|
|
9061
|
-
defaultPkColumnName: outerThis.defaultPk,
|
|
9062
|
-
deriveFkColumnName: outerThis.deriveFk
|
|
9063
|
-
});
|
|
9064
|
-
const innerPlan = {
|
|
9065
|
-
rootTable: query.from,
|
|
9066
|
-
decisions: intentToDecisions(query, query.from)
|
|
9067
|
-
};
|
|
9068
|
-
const innerResult = innerCompiler.compile(innerPlan);
|
|
9069
|
-
const renumbered = renumberParamRefsInAst(
|
|
9070
|
-
innerResult.ast,
|
|
9071
|
-
paramOffset
|
|
9072
|
-
);
|
|
9073
|
-
return { ast: renumbered, parameters: innerResult.parameters };
|
|
9219
|
+
return outerThis.compileExpressionSubquery(query, paramOffset);
|
|
9074
9220
|
}
|
|
9075
9221
|
};
|
|
9076
9222
|
const state = this.createHandlerState();
|
|
@@ -9256,13 +9402,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9256
9402
|
const targetRV = rangeVar(
|
|
9257
9403
|
pj.table,
|
|
9258
9404
|
pj.alias,
|
|
9259
|
-
plan
|
|
9405
|
+
this.schemaForRangeVar(plan, pj.table),
|
|
9260
9406
|
this.naming
|
|
9261
9407
|
);
|
|
9262
9408
|
const base = from.length > 0 ? from[0] : rangeVar(
|
|
9263
9409
|
plan.rootTable,
|
|
9264
9410
|
void 0,
|
|
9265
|
-
plan
|
|
9411
|
+
this.schemaForRangeVar(plan, plan.rootTable),
|
|
9266
9412
|
this.naming
|
|
9267
9413
|
);
|
|
9268
9414
|
from[0] = pj.type === "LEFT JOIN" ? leftJoin(base, targetRV, pj.on) : innerJoin(base, targetRV, pj.on);
|
|
@@ -9271,7 +9417,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9271
9417
|
const base = from.length > 0 ? from[0] : rangeVar(
|
|
9272
9418
|
plan.rootTable,
|
|
9273
9419
|
void 0,
|
|
9274
|
-
plan
|
|
9420
|
+
this.schemaForRangeVar(plan, plan.rootTable),
|
|
9275
9421
|
this.naming
|
|
9276
9422
|
);
|
|
9277
9423
|
const joinExpr2 = rawJoin;
|
|
@@ -9296,7 +9442,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9296
9442
|
plan.batchValuesFromNode ? plan.batchValuesFromNode : rangeVar(
|
|
9297
9443
|
plan.rootTable,
|
|
9298
9444
|
void 0,
|
|
9299
|
-
plan
|
|
9445
|
+
this.schemaForRangeVar(plan, plan.rootTable),
|
|
9300
9446
|
this.naming
|
|
9301
9447
|
)
|
|
9302
9448
|
];
|
|
@@ -9390,19 +9536,35 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9390
9536
|
}
|
|
9391
9537
|
return void 0;
|
|
9392
9538
|
}
|
|
9539
|
+
/**
|
|
9540
|
+
* Compile a column reference that may use dotted 'relation.column' notation.
|
|
9541
|
+
*/
|
|
9542
|
+
compileRelationAwareColumnRef(column, table) {
|
|
9543
|
+
const dot = column.lastIndexOf(".");
|
|
9544
|
+
if (dot !== -1) {
|
|
9545
|
+
const relation = column.slice(0, dot);
|
|
9546
|
+
const alias = this.resolvedJoinAliases().get(relation) ?? relation;
|
|
9547
|
+
return columnRef(column.slice(dot + 1), alias, void 0, this.naming);
|
|
9548
|
+
}
|
|
9549
|
+
return columnRef(column, table, void 0, this.naming);
|
|
9550
|
+
}
|
|
9393
9551
|
/**
|
|
9394
9552
|
* Compile a single groupBy decision into a ColumnRef AST node.
|
|
9395
9553
|
* Supports dotted 'relation.column' notation for joined tables.
|
|
9396
9554
|
*/
|
|
9397
9555
|
compileGroupByDecision(decision) {
|
|
9398
|
-
|
|
9399
|
-
|
|
9400
|
-
|
|
9401
|
-
|
|
9402
|
-
|
|
9403
|
-
|
|
9404
|
-
|
|
9405
|
-
|
|
9556
|
+
return this.compileRelationAwareColumnRef(
|
|
9557
|
+
decision.column,
|
|
9558
|
+
decision.table
|
|
9559
|
+
);
|
|
9560
|
+
}
|
|
9561
|
+
/**
|
|
9562
|
+
* Compile a DISTINCT ON column into a ColumnRef AST node.
|
|
9563
|
+
* Mirrors GROUP BY relation-alias resolution while preserving unqualified
|
|
9564
|
+
* DISTINCT ON columns as unqualified references.
|
|
9565
|
+
*/
|
|
9566
|
+
compileDistinctOnColumn(column) {
|
|
9567
|
+
return this.compileRelationAwareColumnRef(column, void 0);
|
|
9406
9568
|
}
|
|
9407
9569
|
/**
|
|
9408
9570
|
* Assemble the final SelectStmt from all accumulated clause nodes.
|
|
@@ -9427,6 +9589,18 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9427
9589
|
options.withClause = { ctes: this.pendingCtes, recursive: false };
|
|
9428
9590
|
}
|
|
9429
9591
|
if (plan.lock) {
|
|
9592
|
+
assertDialectCapability(
|
|
9593
|
+
this.dialectCapabilities,
|
|
9594
|
+
"supportsRowLevelLocks",
|
|
9595
|
+
"Row-level locks are"
|
|
9596
|
+
);
|
|
9597
|
+
if (plan.lock.waitPolicy !== "block") {
|
|
9598
|
+
assertDialectCapability(
|
|
9599
|
+
this.dialectCapabilities,
|
|
9600
|
+
"supportsLockWaitPolicies",
|
|
9601
|
+
"Lock wait policies are"
|
|
9602
|
+
);
|
|
9603
|
+
}
|
|
9430
9604
|
const mapped = mapLockToAst(plan.lock);
|
|
9431
9605
|
const hasJoins = this.rawJoins.length > 0;
|
|
9432
9606
|
options.lockingClause = {
|
|
@@ -9436,7 +9610,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9436
9610
|
rangeVar(
|
|
9437
9611
|
plan.rootTable,
|
|
9438
9612
|
void 0,
|
|
9439
|
-
plan
|
|
9613
|
+
this.schemaForRangeVar(plan, plan.rootTable),
|
|
9440
9614
|
this.naming
|
|
9441
9615
|
)
|
|
9442
9616
|
]
|
|
@@ -9539,7 +9713,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9539
9713
|
case "distinctOn":
|
|
9540
9714
|
if (decision.columns && decision.columns.length > 0) {
|
|
9541
9715
|
distinct = decision.columns.map(
|
|
9542
|
-
(col) =>
|
|
9716
|
+
(col) => this.compileDistinctOnColumn(col)
|
|
9543
9717
|
);
|
|
9544
9718
|
}
|
|
9545
9719
|
break;
|
|
@@ -9816,13 +9990,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9816
9990
|
const baseTable = larg ?? rangeVar(
|
|
9817
9991
|
plan.rootTable,
|
|
9818
9992
|
void 0,
|
|
9819
|
-
plan
|
|
9993
|
+
this.schemaForRangeVar(plan, plan.rootTable),
|
|
9820
9994
|
this.naming
|
|
9821
9995
|
);
|
|
9822
9996
|
const targetTable = rangeVar(
|
|
9823
9997
|
decision.targetTable ?? "",
|
|
9824
9998
|
decision.alias,
|
|
9825
|
-
plan.
|
|
9999
|
+
this.schemaForRangeVar(plan, decision.targetTable ?? ""),
|
|
9826
10000
|
this.naming
|
|
9827
10001
|
);
|
|
9828
10002
|
const onCondition = eqExpr(
|
|
@@ -13139,6 +13313,7 @@ function matchGlob(pattern, value) {
|
|
|
13139
13313
|
|
|
13140
13314
|
// src/mutations/mutation-compiler.ts
|
|
13141
13315
|
init_ast_helpers();
|
|
13316
|
+
init_binding_registry();
|
|
13142
13317
|
init_compiler_utils();
|
|
13143
13318
|
init_handlers();
|
|
13144
13319
|
init_param_intent();
|
|
@@ -13364,12 +13539,18 @@ function compileInsertFrom(config, ctx, state) {
|
|
|
13364
13539
|
const _dbTargetTable = naming.toDatabase(config.targetTable);
|
|
13365
13540
|
const dbSourceTable = naming.toDatabase(config.sourceTable);
|
|
13366
13541
|
const sourceAlias = config.sourceTable;
|
|
13542
|
+
const sourceSchema = schemaForFromName(
|
|
13543
|
+
ctx.schema,
|
|
13544
|
+
config.sourceTable,
|
|
13545
|
+
ctx.bindingNames,
|
|
13546
|
+
naming
|
|
13547
|
+
);
|
|
13367
13548
|
const dbColumns = config.columns?.map((c) => naming.toDatabase(c));
|
|
13368
13549
|
let targetList;
|
|
13369
13550
|
if (config.columns && config.columns.length > 0) {
|
|
13370
13551
|
targetList = config.columns.map(
|
|
13371
13552
|
(col) => resTarget(
|
|
13372
|
-
columnRef(col, sourceAlias,
|
|
13553
|
+
columnRef(col, sourceAlias, sourceSchema, naming),
|
|
13373
13554
|
naming.toDatabase(col)
|
|
13374
13555
|
)
|
|
13375
13556
|
);
|
|
@@ -13409,8 +13590,8 @@ function compileInsertFrom(config, ctx, state) {
|
|
|
13409
13590
|
inh: true,
|
|
13410
13591
|
relpersistence: "p"
|
|
13411
13592
|
};
|
|
13412
|
-
if (
|
|
13413
|
-
sourceRelation.schemaname = naming.toDatabase(
|
|
13593
|
+
if (sourceSchema) {
|
|
13594
|
+
sourceRelation.schemaname = naming.toDatabase(sourceSchema);
|
|
13414
13595
|
}
|
|
13415
13596
|
const selectQuery = {
|
|
13416
13597
|
SelectStmt: {
|
|
@@ -13439,12 +13620,18 @@ function compileUpsertFrom(config, ctx, state) {
|
|
|
13439
13620
|
const naming = ctx.naming;
|
|
13440
13621
|
const dbSourceTable = naming.toDatabase(config.sourceTable);
|
|
13441
13622
|
const sourceAlias = config.sourceTable;
|
|
13623
|
+
const sourceSchema = schemaForFromName(
|
|
13624
|
+
ctx.schema,
|
|
13625
|
+
config.sourceTable,
|
|
13626
|
+
ctx.bindingNames,
|
|
13627
|
+
naming
|
|
13628
|
+
);
|
|
13442
13629
|
const dbColumns = config.columns?.map((c) => naming.toDatabase(c));
|
|
13443
13630
|
let targetList;
|
|
13444
13631
|
if (config.columns && config.columns.length > 0) {
|
|
13445
13632
|
targetList = config.columns.map(
|
|
13446
13633
|
(col) => resTarget(
|
|
13447
|
-
columnRef(col, sourceAlias,
|
|
13634
|
+
columnRef(col, sourceAlias, sourceSchema, naming),
|
|
13448
13635
|
naming.toDatabase(col)
|
|
13449
13636
|
)
|
|
13450
13637
|
);
|
|
@@ -13484,8 +13671,8 @@ function compileUpsertFrom(config, ctx, state) {
|
|
|
13484
13671
|
inh: true,
|
|
13485
13672
|
relpersistence: "p"
|
|
13486
13673
|
};
|
|
13487
|
-
if (
|
|
13488
|
-
sourceRelation.schemaname = naming.toDatabase(
|
|
13674
|
+
if (sourceSchema) {
|
|
13675
|
+
sourceRelation.schemaname = naming.toDatabase(sourceSchema);
|
|
13489
13676
|
}
|
|
13490
13677
|
const selectQuery = {
|
|
13491
13678
|
SelectStmt: {
|
|
@@ -13630,6 +13817,28 @@ init_compiler_utils();
|
|
|
13630
13817
|
init_handlers();
|
|
13631
13818
|
init_param_intent();
|
|
13632
13819
|
init_param_ref();
|
|
13820
|
+
function buildWhereClause(conditions, ctx, state) {
|
|
13821
|
+
if (!conditions || conditions.length === 0) return void 0;
|
|
13822
|
+
const dispatch = createWhereDispatcher();
|
|
13823
|
+
if (conditions.length === 1) {
|
|
13824
|
+
return dispatch(conditions[0], ctx, state);
|
|
13825
|
+
}
|
|
13826
|
+
const nodes = conditions.map((condition) => dispatch(condition, ctx, state));
|
|
13827
|
+
return {
|
|
13828
|
+
BoolExpr: { boolop: "AND_EXPR", args: nodes }
|
|
13829
|
+
};
|
|
13830
|
+
}
|
|
13831
|
+
function buildActionWhereClause(config, ctx, state) {
|
|
13832
|
+
if (config.actionWhereIntent) {
|
|
13833
|
+
if (!config.compileActionWhere) {
|
|
13834
|
+
throw new Error(
|
|
13835
|
+
"Upsert actionWhereIntent requires compileActionWhere callback"
|
|
13836
|
+
);
|
|
13837
|
+
}
|
|
13838
|
+
return config.compileActionWhere(config.actionWhereIntent, state);
|
|
13839
|
+
}
|
|
13840
|
+
return buildWhereClause(config.actionWhere, ctx, state);
|
|
13841
|
+
}
|
|
13633
13842
|
function buildOnConflictClause(config, ctx, state) {
|
|
13634
13843
|
const naming = ctx.naming;
|
|
13635
13844
|
const action = config.conflictAction;
|
|
@@ -13643,18 +13852,12 @@ function buildOnConflictClause(config, ctx, state) {
|
|
|
13643
13852
|
}))
|
|
13644
13853
|
};
|
|
13645
13854
|
if (config.conflictTarget.where && config.conflictTarget.where.length > 0) {
|
|
13646
|
-
const
|
|
13647
|
-
|
|
13648
|
-
|
|
13649
|
-
|
|
13650
|
-
|
|
13651
|
-
|
|
13652
|
-
const nodes = conditions.map((c) => dispatch(c, ctx, state));
|
|
13653
|
-
whereClause = {
|
|
13654
|
-
BoolExpr: { boolop: "AND_EXPR", args: nodes }
|
|
13655
|
-
};
|
|
13656
|
-
}
|
|
13657
|
-
infer.whereClause = whereClause;
|
|
13855
|
+
const whereClause2 = buildWhereClause(
|
|
13856
|
+
config.conflictTarget.where,
|
|
13857
|
+
ctx,
|
|
13858
|
+
state
|
|
13859
|
+
);
|
|
13860
|
+
if (whereClause2) infer.whereClause = whereClause2;
|
|
13658
13861
|
}
|
|
13659
13862
|
} else if (config.conflictTarget.constraint) {
|
|
13660
13863
|
infer = {
|
|
@@ -13700,10 +13903,12 @@ function buildOnConflictClause(config, ctx, state) {
|
|
|
13700
13903
|
}
|
|
13701
13904
|
};
|
|
13702
13905
|
});
|
|
13906
|
+
const whereClause = buildActionWhereClause(config, ctx, state);
|
|
13703
13907
|
return {
|
|
13704
13908
|
action: "ONCONFLICT_UPDATE",
|
|
13705
13909
|
...infer && { infer },
|
|
13706
|
-
targetList
|
|
13910
|
+
targetList,
|
|
13911
|
+
...whereClause && { whereClause }
|
|
13707
13912
|
};
|
|
13708
13913
|
}
|
|
13709
13914
|
function compileUpsert(config, ctx, state) {
|
|
@@ -13841,7 +14046,12 @@ init_naming_plugin();
|
|
|
13841
14046
|
init_param_ref();
|
|
13842
14047
|
|
|
13843
14048
|
// src/pgsql-adapter.ts
|
|
13844
|
-
import {
|
|
14049
|
+
import {
|
|
14050
|
+
POSTGRESQL_CAPABILITIES as POSTGRESQL_CAPABILITIES2,
|
|
14051
|
+
plan as planFn2,
|
|
14052
|
+
validateTypeName as validateTypeName4
|
|
14053
|
+
} from "@dbsp/core";
|
|
14054
|
+
import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNqlBindingRef2 } from "@dbsp/types/internal";
|
|
13845
14055
|
|
|
13846
14056
|
// src/adapter-compiler-includes.ts
|
|
13847
14057
|
init_ast_helpers();
|
|
@@ -14027,426 +14237,75 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
|
|
|
14027
14237
|
}
|
|
14028
14238
|
|
|
14029
14239
|
// src/adapter-compiler-mutations.ts
|
|
14240
|
+
import {
|
|
14241
|
+
InvalidOperationError as InvalidOperationError3,
|
|
14242
|
+
isSqlRaw as isSqlRaw2,
|
|
14243
|
+
POSTGRESQL_CAPABILITIES,
|
|
14244
|
+
plan as planFn
|
|
14245
|
+
} from "@dbsp/core";
|
|
14246
|
+
|
|
14247
|
+
// src/adapter-compiler-select.ts
|
|
14248
|
+
init_assert_field();
|
|
14249
|
+
init_ast_helpers();
|
|
14250
|
+
init_binding_registry();
|
|
14030
14251
|
init_compile_where();
|
|
14252
|
+
import { countDistinctRelationPathsByName } from "@dbsp/core";
|
|
14031
14253
|
init_compiler_utils();
|
|
14032
|
-
|
|
14033
|
-
|
|
14034
|
-
|
|
14035
|
-
|
|
14036
|
-
|
|
14037
|
-
|
|
14038
|
-
|
|
14039
|
-
|
|
14040
|
-
|
|
14041
|
-
|
|
14042
|
-
if (rel.type === "belongsTo") {
|
|
14043
|
-
const fk = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
|
|
14044
|
-
return {
|
|
14045
|
-
targetTable,
|
|
14046
|
-
...fk !== void 0 && { sourceColumn: fk },
|
|
14047
|
-
targetColumn: "id"
|
|
14048
|
-
};
|
|
14049
|
-
}
|
|
14050
|
-
return { targetTable };
|
|
14051
|
-
}
|
|
14052
|
-
function resolveExistsIntent(where, sourceTable, deps) {
|
|
14254
|
+
init_types();
|
|
14255
|
+
init_intent_to_decisions();
|
|
14256
|
+
init_param_ref();
|
|
14257
|
+
|
|
14258
|
+
// src/plan-decision-extractor.ts
|
|
14259
|
+
init_assert_field();
|
|
14260
|
+
init_intent_to_decisions();
|
|
14261
|
+
import { deriveRelationPathFromIntentPath } from "@dbsp/core";
|
|
14262
|
+
function findExistsIntents(where) {
|
|
14263
|
+
if (!where || typeof where !== "object") return [];
|
|
14053
14264
|
const w = where;
|
|
14054
|
-
|
|
14055
|
-
|
|
14056
|
-
const conditions = w.conditions;
|
|
14057
|
-
if (!conditions) return where;
|
|
14058
|
-
const enriched = conditions.map(
|
|
14059
|
-
(c) => resolveExistsIntent(c, sourceTable, deps)
|
|
14060
|
-
);
|
|
14061
|
-
const changed = enriched.some((c, i) => c !== conditions[i]);
|
|
14062
|
-
return changed ? { ...w, conditions: enriched } : where;
|
|
14063
|
-
}
|
|
14064
|
-
if (kind === "not") {
|
|
14065
|
-
const condition = w.condition;
|
|
14066
|
-
if (!condition) return where;
|
|
14067
|
-
const enriched = resolveExistsIntent(condition, sourceTable, deps);
|
|
14068
|
-
return enriched !== condition ? { ...w, condition: enriched } : where;
|
|
14265
|
+
if (w.kind === "exists" || w.kind === "notExists" || w.kind === "relationFilter") {
|
|
14266
|
+
return [w];
|
|
14069
14267
|
}
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
return {
|
|
14075
|
-
...w,
|
|
14076
|
-
targetTable: resolved.targetTable,
|
|
14077
|
-
...resolved.sourceColumn !== void 0 && {
|
|
14078
|
-
sourceColumn: resolved.sourceColumn
|
|
14079
|
-
},
|
|
14080
|
-
...resolved.targetColumn !== void 0 && {
|
|
14081
|
-
targetColumn: resolved.targetColumn
|
|
14268
|
+
const results = [];
|
|
14269
|
+
if (w.conditions && Array.isArray(w.conditions)) {
|
|
14270
|
+
for (const c of w.conditions) {
|
|
14271
|
+
results.push(...findExistsIntents(c));
|
|
14082
14272
|
}
|
|
14083
|
-
}
|
|
14273
|
+
}
|
|
14274
|
+
if (w.condition) {
|
|
14275
|
+
results.push(...findExistsIntents(w.condition));
|
|
14276
|
+
}
|
|
14277
|
+
return results;
|
|
14084
14278
|
}
|
|
14085
|
-
function
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14279
|
+
function resolveRelation(model, sourceTable, relationName) {
|
|
14280
|
+
const rel = model.getRelation(`${sourceTable}.${relationName}`);
|
|
14281
|
+
if (!rel) return void 0;
|
|
14282
|
+
const foreignKey = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
|
|
14283
|
+
const relationType = rel.type;
|
|
14284
|
+
return { target: rel.target, foreignKey, relationType };
|
|
14285
|
+
}
|
|
14286
|
+
function resolveIncludeAlias(context) {
|
|
14287
|
+
return context.relation ?? context.includeAlias;
|
|
14288
|
+
}
|
|
14289
|
+
function resolveIncludeByPath(includes, intentPath, relationName) {
|
|
14290
|
+
if (!includes) return void 0;
|
|
14291
|
+
if (intentPath) {
|
|
14292
|
+
const indexPattern = /include\[(\d+)\]/g;
|
|
14293
|
+
let current = includes;
|
|
14294
|
+
let resolved;
|
|
14295
|
+
let execResult = indexPattern.exec(intentPath);
|
|
14296
|
+
while (execResult !== null) {
|
|
14297
|
+
const idx = parseInt(execResult[1], 10);
|
|
14298
|
+
const item = current[idx];
|
|
14299
|
+
if (!item) break;
|
|
14300
|
+
resolved = item;
|
|
14301
|
+
current = item.include ?? [];
|
|
14302
|
+
execResult = indexPattern.exec(intentPath);
|
|
14095
14303
|
}
|
|
14304
|
+
if (resolved) return resolved;
|
|
14096
14305
|
}
|
|
14097
|
-
return
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
const schemaName = deps.schemaName;
|
|
14101
|
-
const ctx = {
|
|
14102
|
-
naming: deps.naming,
|
|
14103
|
-
rootTable: intent.table,
|
|
14104
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14105
|
-
maxRecursiveDepth: 100
|
|
14106
|
-
};
|
|
14107
|
-
const state = createCompilerState();
|
|
14108
|
-
const firstRow = intent.values?.[0] ?? {};
|
|
14109
|
-
const columns = Object.keys(firstRow);
|
|
14110
|
-
const rows = intent.values ?? [];
|
|
14111
|
-
const values = rows.map((row) => columns.map((col) => row[col]));
|
|
14112
|
-
const columnTypes = getColumnTypes(intent.table, columns, deps);
|
|
14113
|
-
const config = {
|
|
14114
|
-
table: intent.table,
|
|
14115
|
-
columns,
|
|
14116
|
-
values,
|
|
14117
|
-
...intent.returning && { returning: [...intent.returning] },
|
|
14118
|
-
...columnTypes && { columnTypes }
|
|
14119
|
-
};
|
|
14120
|
-
const maxBatchSize = options?.maxBatchSize;
|
|
14121
|
-
if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
|
|
14122
|
-
throw new InvalidOperationError3(
|
|
14123
|
-
"insert",
|
|
14124
|
-
`Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
|
|
14125
|
-
);
|
|
14126
|
-
}
|
|
14127
|
-
const batchThreshold = options?.batchThreshold ?? 50;
|
|
14128
|
-
const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
|
|
14129
|
-
const ast = useUnnest ? compileUnnestInsert(config, ctx, state) : compileInsert(config, ctx, state);
|
|
14130
|
-
const sql = deparseQuoted(ast);
|
|
14131
|
-
return {
|
|
14132
|
-
sql,
|
|
14133
|
-
parameters: state.parameters
|
|
14134
|
-
};
|
|
14135
|
-
}
|
|
14136
|
-
function compileInsertFrom2(intent, _options, deps) {
|
|
14137
|
-
const schemaName = deps.schemaName;
|
|
14138
|
-
const ctx = {
|
|
14139
|
-
naming: deps.naming,
|
|
14140
|
-
rootTable: intent.source,
|
|
14141
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14142
|
-
maxRecursiveDepth: 100
|
|
14143
|
-
};
|
|
14144
|
-
const state = createCompilerState();
|
|
14145
|
-
const config = {
|
|
14146
|
-
targetTable: intent.table,
|
|
14147
|
-
sourceTable: intent.source,
|
|
14148
|
-
...intent.columns && { columns: [...intent.columns] },
|
|
14149
|
-
...intent.where && { where: [whereIntentAsDecision(intent.where)] },
|
|
14150
|
-
...intent.limit !== void 0 && { limit: intent.limit },
|
|
14151
|
-
...intent.returning && { returning: [...intent.returning] }
|
|
14152
|
-
};
|
|
14153
|
-
const ast = compileInsertFrom(config, ctx, state);
|
|
14154
|
-
const sql = deparseQuoted(ast);
|
|
14155
|
-
return {
|
|
14156
|
-
sql,
|
|
14157
|
-
parameters: state.parameters
|
|
14158
|
-
};
|
|
14159
|
-
}
|
|
14160
|
-
function compileUpdate2(intent, _options, deps) {
|
|
14161
|
-
const schemaName = deps.schemaName;
|
|
14162
|
-
const ctx = {
|
|
14163
|
-
naming: deps.naming,
|
|
14164
|
-
rootTable: intent.table,
|
|
14165
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14166
|
-
maxRecursiveDepth: 100
|
|
14167
|
-
};
|
|
14168
|
-
const state = createCompilerState();
|
|
14169
|
-
const setColumns = Object.keys(intent.set ?? {});
|
|
14170
|
-
const columnTypes = getColumnTypes(intent.table, setColumns, deps);
|
|
14171
|
-
const config = {
|
|
14172
|
-
table: intent.table,
|
|
14173
|
-
set: Object.entries(intent.set ?? {}).map(([column, value]) => ({
|
|
14174
|
-
column,
|
|
14175
|
-
value
|
|
14176
|
-
})),
|
|
14177
|
-
...intent.where && { where: [whereIntentAsDecision(intent.where)] },
|
|
14178
|
-
...intent.returning && { returning: [...intent.returning] },
|
|
14179
|
-
...columnTypes && { columnTypes }
|
|
14180
|
-
};
|
|
14181
|
-
const ast = compileUpdate(config, ctx, state);
|
|
14182
|
-
const sql = deparseQuoted(ast);
|
|
14183
|
-
return {
|
|
14184
|
-
sql,
|
|
14185
|
-
parameters: state.parameters
|
|
14186
|
-
};
|
|
14187
|
-
}
|
|
14188
|
-
function compileBatchUpdate(intent, _options, deps) {
|
|
14189
|
-
const schemaName = deps.schemaName;
|
|
14190
|
-
const ctx = {
|
|
14191
|
-
naming: deps.naming,
|
|
14192
|
-
rootTable: intent.table,
|
|
14193
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14194
|
-
maxRecursiveDepth: 100
|
|
14195
|
-
};
|
|
14196
|
-
const state = createCompilerState();
|
|
14197
|
-
if (intent.updates.length === 0) {
|
|
14198
|
-
throw new InvalidOperationError3(
|
|
14199
|
-
"update",
|
|
14200
|
-
"batchSet requires at least one row"
|
|
14201
|
-
);
|
|
14202
|
-
}
|
|
14203
|
-
const allColumns = Object.keys(intent.updates[0]);
|
|
14204
|
-
const matchColumns = [...intent.matchColumns];
|
|
14205
|
-
for (const mc of matchColumns) {
|
|
14206
|
-
if (!allColumns.includes(mc)) {
|
|
14207
|
-
throw new InvalidOperationError3(
|
|
14208
|
-
"update",
|
|
14209
|
-
`Match column "${mc}" not found in update data. Each row must include the match column(s).`
|
|
14210
|
-
);
|
|
14211
|
-
}
|
|
14212
|
-
}
|
|
14213
|
-
const values = intent.updates.map((row) => allColumns.map((col) => row[col]));
|
|
14214
|
-
validateBatchCardinality(allColumns, values);
|
|
14215
|
-
const columnArrays = transposeToColumnArrays(allColumns, values);
|
|
14216
|
-
const columnTypes = getColumnTypes(intent.table, allColumns, deps);
|
|
14217
|
-
const scalarSet = intent.scalarSet ? Object.entries(intent.scalarSet).map(([column, value]) => ({
|
|
14218
|
-
column,
|
|
14219
|
-
value
|
|
14220
|
-
})) : void 0;
|
|
14221
|
-
let whereGuard;
|
|
14222
|
-
if (intent.where) {
|
|
14223
|
-
const whereCtx = {
|
|
14224
|
-
rootTable: intent.table,
|
|
14225
|
-
aliases: /* @__PURE__ */ new Map(),
|
|
14226
|
-
paramState: state,
|
|
14227
|
-
naming: deps.naming,
|
|
14228
|
-
...schemaName !== void 0 && { schemaName },
|
|
14229
|
-
...deps.model !== void 0 && { model: deps.model },
|
|
14230
|
-
compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
|
|
14231
|
-
sqIntent,
|
|
14232
|
-
paramOffset,
|
|
14233
|
-
deps.naming,
|
|
14234
|
-
schemaName,
|
|
14235
|
-
"rawExists"
|
|
14236
|
-
)
|
|
14237
|
-
};
|
|
14238
|
-
whereGuard = compileWhereIntent(intent.where, whereCtx);
|
|
14239
|
-
}
|
|
14240
|
-
const config = {
|
|
14241
|
-
table: intent.table,
|
|
14242
|
-
matchColumns,
|
|
14243
|
-
allColumns,
|
|
14244
|
-
columnArrays,
|
|
14245
|
-
...scalarSet && { scalarSet },
|
|
14246
|
-
...intent.returning && { returning: [...intent.returning] },
|
|
14247
|
-
...columnTypes && { columnTypes },
|
|
14248
|
-
...whereGuard !== void 0 && { whereGuard }
|
|
14249
|
-
};
|
|
14250
|
-
const ast = compileUnnestUpdate(config, ctx, state);
|
|
14251
|
-
const sql = deparseQuoted(ast);
|
|
14252
|
-
return {
|
|
14253
|
-
sql,
|
|
14254
|
-
parameters: state.parameters
|
|
14255
|
-
};
|
|
14256
|
-
}
|
|
14257
|
-
function compileDelete2(intent, options, deps) {
|
|
14258
|
-
const schemaName = deps.schemaName;
|
|
14259
|
-
const resolvedModel = options?.model ?? deps.model;
|
|
14260
|
-
const ctx = {
|
|
14261
|
-
naming: deps.naming,
|
|
14262
|
-
rootTable: intent.table,
|
|
14263
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14264
|
-
maxRecursiveDepth: 100,
|
|
14265
|
-
...resolvedModel !== void 0 && { model: resolvedModel }
|
|
14266
|
-
};
|
|
14267
|
-
const state = createCompilerState();
|
|
14268
|
-
const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.table, deps) : void 0;
|
|
14269
|
-
const config = {
|
|
14270
|
-
table: intent.table,
|
|
14271
|
-
...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
|
|
14272
|
-
...intent.returning && { returning: [...intent.returning] }
|
|
14273
|
-
};
|
|
14274
|
-
const ast = compileDelete(config, ctx, state);
|
|
14275
|
-
const sql = deparseQuoted(ast);
|
|
14276
|
-
return {
|
|
14277
|
-
sql,
|
|
14278
|
-
parameters: state.parameters
|
|
14279
|
-
};
|
|
14280
|
-
}
|
|
14281
|
-
function compileUpsert2(intent, options, deps) {
|
|
14282
|
-
const schemaName = deps.schemaName;
|
|
14283
|
-
const ctx = {
|
|
14284
|
-
naming: deps.naming,
|
|
14285
|
-
rootTable: intent.table,
|
|
14286
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14287
|
-
maxRecursiveDepth: 100
|
|
14288
|
-
};
|
|
14289
|
-
const state = createCompilerState();
|
|
14290
|
-
const firstRow = intent.values?.[0] ?? {};
|
|
14291
|
-
const rawExprs = {};
|
|
14292
|
-
const scalarSet = {};
|
|
14293
|
-
if (intent.action.type === "doUpdate" && intent.action.set) {
|
|
14294
|
-
for (const [key, val] of Object.entries(intent.action.set)) {
|
|
14295
|
-
if (isSqlRaw2(val)) {
|
|
14296
|
-
rawExprs[key] = val.sql;
|
|
14297
|
-
} else {
|
|
14298
|
-
scalarSet[key] = val;
|
|
14299
|
-
}
|
|
14300
|
-
}
|
|
14301
|
-
}
|
|
14302
|
-
const hasScalarSet = Object.keys(scalarSet).length > 0;
|
|
14303
|
-
const mergedFirstRow = hasScalarSet ? { ...firstRow, ...scalarSet } : firstRow;
|
|
14304
|
-
const columns = Object.keys(mergedFirstRow);
|
|
14305
|
-
const values = (intent.values ?? []).map((row) => {
|
|
14306
|
-
const mergedRow = hasScalarSet ? { ...row, ...scalarSet } : row;
|
|
14307
|
-
return columns.map((col) => mergedRow[col]);
|
|
14308
|
-
});
|
|
14309
|
-
const conflictTarget = {};
|
|
14310
|
-
if ("columns" in intent.onConflict) {
|
|
14311
|
-
conflictTarget.columns = [...intent.onConflict.columns];
|
|
14312
|
-
} else if ("constraint" in intent.onConflict) {
|
|
14313
|
-
conflictTarget.constraint = intent.onConflict.constraint;
|
|
14314
|
-
}
|
|
14315
|
-
const conflictAction = intent.action.type === "doNothing" ? "nothing" : "update";
|
|
14316
|
-
let updateColumns;
|
|
14317
|
-
if (intent.action.type === "doUpdate") {
|
|
14318
|
-
if (intent.action.set) {
|
|
14319
|
-
updateColumns = Object.keys(intent.action.set);
|
|
14320
|
-
} else {
|
|
14321
|
-
const conflictCols = "columns" in intent.onConflict ? intent.onConflict.columns : [];
|
|
14322
|
-
updateColumns = columns.filter((col) => !conflictCols.includes(col));
|
|
14323
|
-
}
|
|
14324
|
-
}
|
|
14325
|
-
const columnTypes = getColumnTypes(intent.table, columns, deps);
|
|
14326
|
-
const hasRawExprs = Object.keys(rawExprs).length > 0;
|
|
14327
|
-
const config = {
|
|
14328
|
-
table: intent.table,
|
|
14329
|
-
columns,
|
|
14330
|
-
values,
|
|
14331
|
-
conflictTarget,
|
|
14332
|
-
conflictAction,
|
|
14333
|
-
...updateColumns && { updateColumns },
|
|
14334
|
-
...intent.returning && { returning: [...intent.returning] },
|
|
14335
|
-
...columnTypes && { columnTypes },
|
|
14336
|
-
...hasRawExprs && { updateExpressions: rawExprs }
|
|
14337
|
-
};
|
|
14338
|
-
const maxBatchSize = options?.maxBatchSize;
|
|
14339
|
-
if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
|
|
14340
|
-
throw new InvalidOperationError3(
|
|
14341
|
-
"upsert",
|
|
14342
|
-
`Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
|
|
14343
|
-
);
|
|
14344
|
-
}
|
|
14345
|
-
const batchThreshold = options?.batchThreshold ?? 50;
|
|
14346
|
-
const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
|
|
14347
|
-
const ast = useUnnest ? compileUnnestUpsert(config, ctx, state) : compileUpsert(config, ctx, state);
|
|
14348
|
-
const sql = deparseQuoted(ast);
|
|
14349
|
-
return {
|
|
14350
|
-
sql,
|
|
14351
|
-
parameters: state.parameters
|
|
14352
|
-
};
|
|
14353
|
-
}
|
|
14354
|
-
function compileUpsertFrom2(intent, options, deps) {
|
|
14355
|
-
const schemaName = deps.schemaName;
|
|
14356
|
-
const ctx = {
|
|
14357
|
-
naming: deps.naming,
|
|
14358
|
-
rootTable: intent.source,
|
|
14359
|
-
...schemaName !== void 0 && { schema: schemaName },
|
|
14360
|
-
maxRecursiveDepth: 100
|
|
14361
|
-
};
|
|
14362
|
-
const state = createCompilerState();
|
|
14363
|
-
let columns;
|
|
14364
|
-
if (intent.columns) {
|
|
14365
|
-
columns = [...intent.columns];
|
|
14366
|
-
} else if (options?.model) {
|
|
14367
|
-
const targetTable = options.model.getTable(intent.table);
|
|
14368
|
-
if (targetTable) {
|
|
14369
|
-
columns = targetTable.columns.map((c) => c.name);
|
|
14370
|
-
}
|
|
14371
|
-
}
|
|
14372
|
-
const config = {
|
|
14373
|
-
targetTable: intent.table,
|
|
14374
|
-
sourceTable: intent.source,
|
|
14375
|
-
conflictColumns: [...intent.conflictColumns],
|
|
14376
|
-
...columns && { columns },
|
|
14377
|
-
...intent.where && { where: [whereIntentAsDecision(intent.where)] },
|
|
14378
|
-
...intent.limit !== void 0 && { limit: intent.limit },
|
|
14379
|
-
...intent.returning && { returning: [...intent.returning] }
|
|
14380
|
-
};
|
|
14381
|
-
const ast = compileUpsertFrom(config, ctx, state);
|
|
14382
|
-
const sql = deparseQuoted(ast);
|
|
14383
|
-
return {
|
|
14384
|
-
sql,
|
|
14385
|
-
parameters: state.parameters
|
|
14386
|
-
};
|
|
14387
|
-
}
|
|
14388
|
-
|
|
14389
|
-
// src/adapter-compiler-select.ts
|
|
14390
|
-
init_assert_field();
|
|
14391
|
-
init_ast_helpers();
|
|
14392
|
-
init_compile_where();
|
|
14393
|
-
import { countDistinctRelationPathsByName } from "@dbsp/core";
|
|
14394
|
-
init_compiler_utils();
|
|
14395
|
-
init_types();
|
|
14396
|
-
init_intent_to_decisions();
|
|
14397
|
-
init_param_ref();
|
|
14398
|
-
|
|
14399
|
-
// src/plan-decision-extractor.ts
|
|
14400
|
-
init_assert_field();
|
|
14401
|
-
init_intent_to_decisions();
|
|
14402
|
-
import { deriveRelationPathFromIntentPath } from "@dbsp/core";
|
|
14403
|
-
function findExistsIntents(where) {
|
|
14404
|
-
if (!where || typeof where !== "object") return [];
|
|
14405
|
-
const w = where;
|
|
14406
|
-
if (w.kind === "exists" || w.kind === "notExists" || w.kind === "relationFilter") {
|
|
14407
|
-
return [w];
|
|
14408
|
-
}
|
|
14409
|
-
const results = [];
|
|
14410
|
-
if (w.conditions && Array.isArray(w.conditions)) {
|
|
14411
|
-
for (const c of w.conditions) {
|
|
14412
|
-
results.push(...findExistsIntents(c));
|
|
14413
|
-
}
|
|
14414
|
-
}
|
|
14415
|
-
if (w.condition) {
|
|
14416
|
-
results.push(...findExistsIntents(w.condition));
|
|
14417
|
-
}
|
|
14418
|
-
return results;
|
|
14419
|
-
}
|
|
14420
|
-
function resolveRelation(model, sourceTable, relationName) {
|
|
14421
|
-
const rel = model.getRelation(`${sourceTable}.${relationName}`);
|
|
14422
|
-
if (!rel) return void 0;
|
|
14423
|
-
const foreignKey = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
|
|
14424
|
-
const relationType = rel.type;
|
|
14425
|
-
return { target: rel.target, foreignKey, relationType };
|
|
14426
|
-
}
|
|
14427
|
-
function resolveIncludeAlias(context) {
|
|
14428
|
-
return context.relation ?? context.includeAlias;
|
|
14429
|
-
}
|
|
14430
|
-
function resolveIncludeByPath(includes, intentPath, relationName) {
|
|
14431
|
-
if (!includes) return void 0;
|
|
14432
|
-
if (intentPath) {
|
|
14433
|
-
const indexPattern = /include\[(\d+)\]/g;
|
|
14434
|
-
let current = includes;
|
|
14435
|
-
let resolved;
|
|
14436
|
-
let execResult = indexPattern.exec(intentPath);
|
|
14437
|
-
while (execResult !== null) {
|
|
14438
|
-
const idx = parseInt(execResult[1], 10);
|
|
14439
|
-
const item = current[idx];
|
|
14440
|
-
if (!item) break;
|
|
14441
|
-
resolved = item;
|
|
14442
|
-
current = item.include ?? [];
|
|
14443
|
-
execResult = indexPattern.exec(intentPath);
|
|
14444
|
-
}
|
|
14445
|
-
if (resolved) return resolved;
|
|
14446
|
-
}
|
|
14447
|
-
return includes.find(
|
|
14448
|
-
(i) => i.relation === relationName || i.via === relationName
|
|
14449
|
-
);
|
|
14306
|
+
return includes.find(
|
|
14307
|
+
(i) => i.relation === relationName || i.via === relationName
|
|
14308
|
+
);
|
|
14450
14309
|
}
|
|
14451
14310
|
function deriveForeignKey(context, deriveFk = defaultFkDerivation, defaultPk = DEFAULT_PK_COLUMN) {
|
|
14452
14311
|
const fk = context.foreignKey ?? context.sourceFK;
|
|
@@ -14707,1070 +14566,1599 @@ function enrichExistsStubsInConditions(conditions, sourceTable, model) {
|
|
|
14707
14566
|
}
|
|
14708
14567
|
hopCheckSource = hopRel.target;
|
|
14709
14568
|
}
|
|
14710
|
-
const mode = d.operator === "notExists" ? "none" : d.operator === "every" ? "every" : "some";
|
|
14711
|
-
const isEvery = mode === "every";
|
|
14712
|
-
const outerOperator = d.operator === "notExists" || mode === "none" || isEvery ? "notExists" : "exists";
|
|
14713
|
-
const finalTarget = hopCheckSource;
|
|
14714
|
-
let rawInnerConditions;
|
|
14715
|
-
if (rawWhere) {
|
|
14716
|
-
const c = convertWhereToDecisions(rawWhere, finalTarget);
|
|
14569
|
+
const mode = d.operator === "notExists" ? "none" : d.operator === "every" ? "every" : "some";
|
|
14570
|
+
const isEvery = mode === "every";
|
|
14571
|
+
const outerOperator = d.operator === "notExists" || mode === "none" || isEvery ? "notExists" : "exists";
|
|
14572
|
+
const finalTarget = hopCheckSource;
|
|
14573
|
+
let rawInnerConditions;
|
|
14574
|
+
if (rawWhere) {
|
|
14575
|
+
const c = convertWhereToDecisions(rawWhere, finalTarget);
|
|
14576
|
+
if (c.length > 0) {
|
|
14577
|
+
enrichExistsStubsInConditions(c, finalTarget, model);
|
|
14578
|
+
rawInnerConditions = c;
|
|
14579
|
+
}
|
|
14580
|
+
}
|
|
14581
|
+
if (isEvery && !rawInnerConditions) {
|
|
14582
|
+
const firstHop = hops[0];
|
|
14583
|
+
const firstRel = model.getRelation(`${sourceTable}.${firstHop}`);
|
|
14584
|
+
conditions[i] = {
|
|
14585
|
+
type: "where",
|
|
14586
|
+
operator: "every",
|
|
14587
|
+
targetTable: firstRel?.target ?? firstHop,
|
|
14588
|
+
sourceTable
|
|
14589
|
+
};
|
|
14590
|
+
continue;
|
|
14591
|
+
}
|
|
14592
|
+
const innerConditions = isEvery && rawInnerConditions ? [
|
|
14593
|
+
{
|
|
14594
|
+
type: "logical",
|
|
14595
|
+
operator: "not",
|
|
14596
|
+
conditions: rawInnerConditions
|
|
14597
|
+
}
|
|
14598
|
+
] : rawInnerConditions;
|
|
14599
|
+
const enriched = buildMultiHopExistsChain(
|
|
14600
|
+
hops,
|
|
14601
|
+
sourceTable,
|
|
14602
|
+
outerOperator,
|
|
14603
|
+
innerConditions,
|
|
14604
|
+
model
|
|
14605
|
+
);
|
|
14606
|
+
conditions[i] = enriched;
|
|
14607
|
+
} else {
|
|
14608
|
+
const relation = hops[0];
|
|
14609
|
+
const rel = model.getRelation(`${sourceTable}.${relation}`);
|
|
14610
|
+
if (!rel) {
|
|
14611
|
+
throw new Error(
|
|
14612
|
+
`exists('${relation}'): no relation '${relation}' is declared on table '${sourceTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
14613
|
+
);
|
|
14614
|
+
}
|
|
14615
|
+
const foreignKey = typeof rel.foreignKey === "string" ? rel.foreignKey : rel.foreignKey?.[0];
|
|
14616
|
+
const relationType = rel.type === "belongsTo" ? "belongsTo" : rel.type === "hasMany" || rel.type === "hasOne" ? rel.type : void 0;
|
|
14617
|
+
const parentKey = relationType === "belongsTo" ? rel.targetKey : rel.sourceKey;
|
|
14618
|
+
const targetTable = rel.target;
|
|
14619
|
+
let innerConditions;
|
|
14620
|
+
if (rawWhere) {
|
|
14621
|
+
const c = convertWhereToDecisions(rawWhere, targetTable);
|
|
14622
|
+
if (c.length > 0) innerConditions = c;
|
|
14623
|
+
}
|
|
14624
|
+
const includeDecisions = rawInclude ? Object.entries(rawInclude).map(([rel2, opts]) => ({
|
|
14625
|
+
type: "existsInclude",
|
|
14626
|
+
relation: rel2,
|
|
14627
|
+
joinType: opts.join ?? "inner"
|
|
14628
|
+
})) : void 0;
|
|
14629
|
+
const enriched = {
|
|
14630
|
+
type: "where",
|
|
14631
|
+
operator: d.operator,
|
|
14632
|
+
targetTable,
|
|
14633
|
+
sourceTable,
|
|
14634
|
+
...foreignKey ? { foreignKey } : {},
|
|
14635
|
+
...relationType ? { relationType } : {},
|
|
14636
|
+
...parentKey ? { parentKey } : {},
|
|
14637
|
+
...innerConditions ? { conditions: innerConditions } : {},
|
|
14638
|
+
...includeDecisions ? { include: includeDecisions } : {}
|
|
14639
|
+
};
|
|
14640
|
+
conditions[i] = enriched;
|
|
14641
|
+
if (innerConditions && innerConditions.length > 0) {
|
|
14642
|
+
enrichExistsStubsInConditions(innerConditions, targetTable, model);
|
|
14643
|
+
}
|
|
14644
|
+
}
|
|
14645
|
+
} else if ((d.type === "whereAnd" || d.type === "whereOr" || d.type === "whereNot") && d.conditions) {
|
|
14646
|
+
enrichExistsStubsInConditions(
|
|
14647
|
+
d.conditions,
|
|
14648
|
+
sourceTable,
|
|
14649
|
+
model
|
|
14650
|
+
);
|
|
14651
|
+
}
|
|
14652
|
+
}
|
|
14653
|
+
}
|
|
14654
|
+
function buildEnrichedExistsDecision(d, matchingIntent, rootTable, model) {
|
|
14655
|
+
const context = d.context;
|
|
14656
|
+
const targetTable = context.target;
|
|
14657
|
+
let conditions;
|
|
14658
|
+
if (matchingIntent?.where) {
|
|
14659
|
+
const nestedDecisions = convertWhereToDecisions(
|
|
14660
|
+
matchingIntent.where,
|
|
14661
|
+
targetTable
|
|
14662
|
+
);
|
|
14663
|
+
if (nestedDecisions.length > 0) {
|
|
14664
|
+
conditions = nestedDecisions;
|
|
14665
|
+
if (model) {
|
|
14666
|
+
enrichExistsStubsInConditions(conditions, targetTable, model);
|
|
14667
|
+
}
|
|
14668
|
+
}
|
|
14669
|
+
}
|
|
14670
|
+
const sourceTableForRelation = context.sourceTable || rootTable;
|
|
14671
|
+
const relIR = model && context.relation ? model.getRelation(
|
|
14672
|
+
`${sourceTableForRelation}.${context.relation}`
|
|
14673
|
+
) : void 0;
|
|
14674
|
+
const foreignKey = relIR ? typeof relIR.foreignKey === "string" ? relIR.foreignKey : relIR.foreignKey?.[0] : void 0;
|
|
14675
|
+
const relationType = relIR?.type === "belongsTo" ? "belongsTo" : relIR?.type === "hasMany" || relIR?.type === "hasOne" ? relIR.type : void 0;
|
|
14676
|
+
const parentKey = relationType === "belongsTo" ? relIR?.targetKey : relIR?.sourceKey;
|
|
14677
|
+
let operator = "exists";
|
|
14678
|
+
if (matchingIntent?.kind === "notExists") {
|
|
14679
|
+
operator = "notExists";
|
|
14680
|
+
} else if (matchingIntent?.kind === "relationFilter") {
|
|
14681
|
+
const mode = matchingIntent.mode ?? "some";
|
|
14682
|
+
if (mode === "none") operator = "notExists";
|
|
14683
|
+
else if (mode === "every") operator = "every";
|
|
14684
|
+
}
|
|
14685
|
+
const includeIntent = matchingIntent?.include;
|
|
14686
|
+
const includeDecisions = includeIntent ? Object.entries(includeIntent).map(([rel, opts]) => ({
|
|
14687
|
+
type: "existsInclude",
|
|
14688
|
+
relation: rel,
|
|
14689
|
+
joinType: opts.join ?? "inner"
|
|
14690
|
+
})) : void 0;
|
|
14691
|
+
const relationName = context.relation;
|
|
14692
|
+
return {
|
|
14693
|
+
type: "where",
|
|
14694
|
+
operator,
|
|
14695
|
+
targetTable,
|
|
14696
|
+
// sourceTable: the table this EXISTS is resolved FROM (the relation's source).
|
|
14697
|
+
// Required by propagateExistsConditions's (sourceTable, relationName) identity
|
|
14698
|
+
// guard — without it the guard is a no-op and cross-source propagation can occur
|
|
14699
|
+
// when two relations share a name but originate from different tables.
|
|
14700
|
+
sourceTable: sourceTableForRelation,
|
|
14701
|
+
...foreignKey ? { foreignKey } : {},
|
|
14702
|
+
// relationType is required so deriveFkColumns (called by mapToHandlerDecision) can
|
|
14703
|
+
// set sourceColumn/targetColumn in the correct direction for the EXISTS correlation.
|
|
14704
|
+
...relationType ? { relationType } : {},
|
|
14705
|
+
// parentKey provides the explicit PK override for non-default PK columns.
|
|
14706
|
+
...parentKey ? { parentKey } : {},
|
|
14707
|
+
...conditions ? { conditions } : {},
|
|
14708
|
+
...d.choice === "join" ? { choice: "join" } : {},
|
|
14709
|
+
...relationName ? { relationName } : {},
|
|
14710
|
+
...includeDecisions ? { include: includeDecisions } : {}
|
|
14711
|
+
};
|
|
14712
|
+
}
|
|
14713
|
+
function collectNestedExistsTargets(where, insideExists, sourceTable, model, out) {
|
|
14714
|
+
if (!where || typeof where !== "object") return;
|
|
14715
|
+
const w = where;
|
|
14716
|
+
if (w.kind === "exists" || w.kind === "notExists" || w.kind === "relationFilter") {
|
|
14717
|
+
if (insideExists) {
|
|
14718
|
+
const rawRelation = w.relation;
|
|
14719
|
+
const hops = Array.isArray(rawRelation) ? rawRelation : typeof rawRelation === "string" ? [rawRelation] : [];
|
|
14720
|
+
if (hops.length === 1) {
|
|
14721
|
+
const singleHop = hops[0] ?? "";
|
|
14722
|
+
const rel = model.getRelation(`${sourceTable}.${singleHop}`);
|
|
14723
|
+
const resolvedTarget = rel ? rel.target : singleHop;
|
|
14724
|
+
out.add(`${sourceTable}:${resolvedTarget}`);
|
|
14725
|
+
} else if (hops.length > 1) {
|
|
14726
|
+
let penultimate = sourceTable;
|
|
14727
|
+
for (let hi = 0; hi < hops.length - 1; hi++) {
|
|
14728
|
+
const hr = model.getRelation(`${penultimate}.${hops[hi] ?? ""}`);
|
|
14729
|
+
penultimate = hr ? hr.target : hops[hi] ?? penultimate;
|
|
14730
|
+
}
|
|
14731
|
+
out.add(`${penultimate}:${hops.join(".")}`);
|
|
14732
|
+
const lastHop = hops[hops.length - 1] ?? "";
|
|
14733
|
+
const lastRel = model.getRelation(`${penultimate}.${lastHop}`);
|
|
14734
|
+
const finalTarget = lastRel ? lastRel.target : lastHop;
|
|
14735
|
+
out.add(`${penultimate}:${finalTarget}`);
|
|
14736
|
+
}
|
|
14737
|
+
let nextSource = sourceTable;
|
|
14738
|
+
if (hops.length >= 1) {
|
|
14739
|
+
let cur = sourceTable;
|
|
14740
|
+
for (const hop of hops) {
|
|
14741
|
+
const rel = model.getRelation(`${cur}.${hop}`);
|
|
14742
|
+
cur = rel ? rel.target : hop;
|
|
14743
|
+
}
|
|
14744
|
+
nextSource = cur;
|
|
14745
|
+
}
|
|
14746
|
+
if (w.where) {
|
|
14747
|
+
collectNestedExistsTargets(w.where, true, nextSource, model, out);
|
|
14748
|
+
}
|
|
14749
|
+
} else {
|
|
14750
|
+
const rawRelation = w.relation;
|
|
14751
|
+
const hops = Array.isArray(rawRelation) ? rawRelation : typeof rawRelation === "string" ? [rawRelation] : [];
|
|
14752
|
+
let nextSource = sourceTable;
|
|
14753
|
+
if (hops.length >= 1) {
|
|
14754
|
+
let cur = sourceTable;
|
|
14755
|
+
for (const hop of hops) {
|
|
14756
|
+
const rel = model.getRelation(`${cur}.${hop}`);
|
|
14757
|
+
cur = rel ? rel.target : hop;
|
|
14758
|
+
}
|
|
14759
|
+
nextSource = cur;
|
|
14760
|
+
}
|
|
14761
|
+
if (w.where) {
|
|
14762
|
+
collectNestedExistsTargets(w.where, true, nextSource, model, out);
|
|
14763
|
+
}
|
|
14764
|
+
}
|
|
14765
|
+
return;
|
|
14766
|
+
}
|
|
14767
|
+
if (w.conditions && Array.isArray(w.conditions)) {
|
|
14768
|
+
for (const c of w.conditions) {
|
|
14769
|
+
collectNestedExistsTargets(c, insideExists, sourceTable, model, out);
|
|
14770
|
+
}
|
|
14771
|
+
}
|
|
14772
|
+
if (w.condition) {
|
|
14773
|
+
collectNestedExistsTargets(
|
|
14774
|
+
w.condition,
|
|
14775
|
+
insideExists,
|
|
14776
|
+
sourceTable,
|
|
14777
|
+
model,
|
|
14778
|
+
out
|
|
14779
|
+
);
|
|
14780
|
+
}
|
|
14781
|
+
}
|
|
14782
|
+
function collectExistsStubs(decisions, out) {
|
|
14783
|
+
decisions.forEach((d, i) => {
|
|
14784
|
+
if (!d) return;
|
|
14785
|
+
if (d.type === "where" && (d.operator === "exists" || d.operator === "notExists" || d.operator === "every")) {
|
|
14786
|
+
if (d.relationName) return;
|
|
14787
|
+
out.push({ decision: d, container: decisions, index: i });
|
|
14788
|
+
} else if ((d.type === "whereAnd" || d.type === "whereOr" || d.type === "whereNot") && d.conditions) {
|
|
14789
|
+
collectExistsStubs(d.conditions, out);
|
|
14790
|
+
}
|
|
14791
|
+
});
|
|
14792
|
+
}
|
|
14793
|
+
function normalizeStubRelation(targetTable) {
|
|
14794
|
+
if (Array.isArray(targetTable) && targetTable.length > 0)
|
|
14795
|
+
return targetTable[targetTable.length - 1];
|
|
14796
|
+
if (typeof targetTable === "string") return targetTable;
|
|
14797
|
+
return void 0;
|
|
14798
|
+
}
|
|
14799
|
+
function normalizeStubRelationPath(targetTable) {
|
|
14800
|
+
if (Array.isArray(targetTable) && targetTable.length > 0)
|
|
14801
|
+
return targetTable.join(".");
|
|
14802
|
+
if (typeof targetTable === "string") return targetTable;
|
|
14803
|
+
return void 0;
|
|
14804
|
+
}
|
|
14805
|
+
function enrichExistsDecisionsInPlace(decisions, plan, model) {
|
|
14806
|
+
const stubs = [];
|
|
14807
|
+
collectExistsStubs(decisions, stubs);
|
|
14808
|
+
const nestedExistsTargets = /* @__PURE__ */ new Set();
|
|
14809
|
+
if (plan.intent?.where && model) {
|
|
14810
|
+
collectNestedExistsTargets(
|
|
14811
|
+
plan.intent.where,
|
|
14812
|
+
false,
|
|
14813
|
+
plan.rootTable,
|
|
14814
|
+
model,
|
|
14815
|
+
nestedExistsTargets
|
|
14816
|
+
);
|
|
14817
|
+
}
|
|
14818
|
+
const filterDecisions = plan.decisions.filter(
|
|
14819
|
+
(d) => d.type === "filter-strategy" && (d.choice === "exists" || d.choice === "notExists" || d.choice === "join")
|
|
14820
|
+
);
|
|
14821
|
+
const placedDecisions = [];
|
|
14822
|
+
const consumedStubIndices = /* @__PURE__ */ new Set();
|
|
14823
|
+
if (filterDecisions.length > 0) {
|
|
14824
|
+
const existsIntents = plan.intent?.where ? findExistsIntents(plan.intent.where) : [];
|
|
14825
|
+
for (const d of filterDecisions) {
|
|
14826
|
+
const context = d.context;
|
|
14827
|
+
if (!context.target) continue;
|
|
14828
|
+
const contextRelationPath = context.relationPath;
|
|
14829
|
+
const contextSourceTableForIntent = context.sourceTable;
|
|
14830
|
+
const isNestedRelation = contextSourceTableForIntent !== void 0 && contextSourceTableForIntent !== plan.rootTable && !contextRelationPath;
|
|
14831
|
+
const matchIdx = isNestedRelation ? -1 : existsIntents.findIndex((i) => {
|
|
14832
|
+
if (contextRelationPath) {
|
|
14833
|
+
const intentPath = Array.isArray(i.relation) ? i.relation.join(".") : typeof i.relation === "string" ? i.relation : void 0;
|
|
14834
|
+
return intentPath === contextRelationPath;
|
|
14835
|
+
}
|
|
14836
|
+
const rel = Array.isArray(i.relation) ? i.relation.length > 0 ? i.relation[i.relation.length - 1] : void 0 : typeof i.relation === "string" ? i.relation : void 0;
|
|
14837
|
+
return rel === context.relation || rel === context.target || rel === context.includeAlias;
|
|
14838
|
+
});
|
|
14839
|
+
const matchingIntent = matchIdx >= 0 ? existsIntents[matchIdx] : void 0;
|
|
14840
|
+
if (matchIdx >= 0) existsIntents.splice(matchIdx, 1);
|
|
14841
|
+
const isMultiHop = matchingIntent && Array.isArray(matchingIntent.relation) && matchingIntent.relation.length > 1 && model;
|
|
14842
|
+
let enriched;
|
|
14843
|
+
if (isMultiHop && matchingIntent && model) {
|
|
14844
|
+
const mode = matchingIntent.mode ?? "some";
|
|
14845
|
+
const isEvery = matchingIntent.kind !== "notExists" && mode === "every";
|
|
14846
|
+
const outerOperator = matchingIntent.kind === "notExists" || mode === "none" || isEvery ? "notExists" : "exists";
|
|
14847
|
+
const lastHopTarget = context.target;
|
|
14848
|
+
const rawInnerConditions = matchingIntent.where ? (() => {
|
|
14849
|
+
const c = convertWhereToDecisions(
|
|
14850
|
+
matchingIntent.where,
|
|
14851
|
+
lastHopTarget
|
|
14852
|
+
);
|
|
14717
14853
|
if (c.length > 0) {
|
|
14718
|
-
enrichExistsStubsInConditions(c,
|
|
14719
|
-
rawInnerConditions = c;
|
|
14854
|
+
enrichExistsStubsInConditions(c, lastHopTarget, model);
|
|
14720
14855
|
}
|
|
14721
|
-
|
|
14856
|
+
return c.length > 0 ? c : void 0;
|
|
14857
|
+
})() : void 0;
|
|
14722
14858
|
if (isEvery && !rawInnerConditions) {
|
|
14723
|
-
|
|
14724
|
-
|
|
14725
|
-
|
|
14726
|
-
|
|
14727
|
-
|
|
14728
|
-
|
|
14729
|
-
|
|
14730
|
-
|
|
14731
|
-
|
|
14859
|
+
enriched = buildEnrichedExistsDecision(
|
|
14860
|
+
d,
|
|
14861
|
+
matchingIntent,
|
|
14862
|
+
plan.rootTable,
|
|
14863
|
+
model
|
|
14864
|
+
);
|
|
14865
|
+
} else {
|
|
14866
|
+
const innerConditions = isEvery && rawInnerConditions ? [
|
|
14867
|
+
{
|
|
14868
|
+
type: "logical",
|
|
14869
|
+
operator: "not",
|
|
14870
|
+
conditions: rawInnerConditions
|
|
14871
|
+
}
|
|
14872
|
+
] : rawInnerConditions;
|
|
14873
|
+
enriched = buildMultiHopExistsChain(
|
|
14874
|
+
matchingIntent.relation,
|
|
14875
|
+
plan.rootTable,
|
|
14876
|
+
outerOperator,
|
|
14877
|
+
innerConditions,
|
|
14878
|
+
model
|
|
14879
|
+
);
|
|
14732
14880
|
}
|
|
14733
|
-
|
|
14734
|
-
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
}
|
|
14739
|
-
] : rawInnerConditions;
|
|
14740
|
-
const enriched = buildMultiHopExistsChain(
|
|
14741
|
-
hops,
|
|
14742
|
-
sourceTable,
|
|
14743
|
-
outerOperator,
|
|
14744
|
-
innerConditions,
|
|
14881
|
+
} else {
|
|
14882
|
+
enriched = buildEnrichedExistsDecision(
|
|
14883
|
+
d,
|
|
14884
|
+
matchingIntent,
|
|
14885
|
+
plan.rootTable,
|
|
14745
14886
|
model
|
|
14746
14887
|
);
|
|
14747
|
-
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
if (
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
);
|
|
14888
|
+
}
|
|
14889
|
+
const contextSourceTable = context.sourceTable;
|
|
14890
|
+
const stubIdx = stubs.findIndex((s, idx) => {
|
|
14891
|
+
if (consumedStubIndices.has(idx)) return false;
|
|
14892
|
+
if (contextRelationPath) {
|
|
14893
|
+
const stubPath = normalizeStubRelationPath(s.decision.targetTable);
|
|
14894
|
+
return stubPath === contextRelationPath;
|
|
14755
14895
|
}
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
const parentKey = relationType === "belongsTo" ? rel.targetKey : rel.sourceKey;
|
|
14759
|
-
const targetTable = rel.target;
|
|
14760
|
-
let innerConditions;
|
|
14761
|
-
if (rawWhere) {
|
|
14762
|
-
const c = convertWhereToDecisions(rawWhere, targetTable);
|
|
14763
|
-
if (c.length > 0) innerConditions = c;
|
|
14896
|
+
if (contextSourceTable !== void 0 && contextSourceTable !== plan.rootTable) {
|
|
14897
|
+
return false;
|
|
14764
14898
|
}
|
|
14765
|
-
const
|
|
14766
|
-
|
|
14767
|
-
|
|
14768
|
-
|
|
14769
|
-
|
|
14770
|
-
|
|
14771
|
-
|
|
14772
|
-
|
|
14773
|
-
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
14777
|
-
|
|
14778
|
-
|
|
14779
|
-
|
|
14780
|
-
};
|
|
14781
|
-
|
|
14782
|
-
|
|
14783
|
-
enrichExistsStubsInConditions(innerConditions, targetTable, model);
|
|
14899
|
+
const stubRelation = normalizeStubRelation(s.decision.targetTable);
|
|
14900
|
+
return stubRelation === context.relation || stubRelation === context.target || stubRelation === context.includeAlias;
|
|
14901
|
+
});
|
|
14902
|
+
if (stubIdx >= 0) {
|
|
14903
|
+
const stub = stubs[stubIdx];
|
|
14904
|
+
if (stub) {
|
|
14905
|
+
stub.container[stub.index] = enriched;
|
|
14906
|
+
consumedStubIndices.add(stubIdx);
|
|
14907
|
+
placedDecisions.push(enriched);
|
|
14908
|
+
}
|
|
14909
|
+
} else {
|
|
14910
|
+
const srcForKey = context.sourceTable ?? "";
|
|
14911
|
+
const targetName = context.target;
|
|
14912
|
+
const relationPath = contextRelationPath;
|
|
14913
|
+
const compoundTarget = targetName ? `${srcForKey}:${targetName}` : "";
|
|
14914
|
+
const compoundPath = relationPath ? `${srcForKey}:${relationPath}` : "";
|
|
14915
|
+
if (compoundTarget && nestedExistsTargets.has(compoundTarget) || compoundPath && nestedExistsTargets.has(compoundPath)) {
|
|
14916
|
+
continue;
|
|
14784
14917
|
}
|
|
14918
|
+
decisions.push(enriched);
|
|
14919
|
+
placedDecisions.push(enriched);
|
|
14785
14920
|
}
|
|
14786
|
-
}
|
|
14787
|
-
|
|
14788
|
-
|
|
14789
|
-
|
|
14790
|
-
|
|
14921
|
+
}
|
|
14922
|
+
}
|
|
14923
|
+
for (let i = 0; i < stubs.length; i++) {
|
|
14924
|
+
if (consumedStubIndices.has(i)) continue;
|
|
14925
|
+
const stub = stubs[i];
|
|
14926
|
+
if (!stub) continue;
|
|
14927
|
+
const rawTargetTable = stub.decision.targetTable;
|
|
14928
|
+
const relationPath = Array.isArray(rawTargetTable) ? rawTargetTable : [rawTargetTable];
|
|
14929
|
+
const displayName = Array.isArray(rawTargetTable) ? rawTargetTable.join(".") : String(rawTargetTable ?? "");
|
|
14930
|
+
if (!displayName) continue;
|
|
14931
|
+
if (!model) {
|
|
14932
|
+
throw new Error(
|
|
14933
|
+
`exists('${displayName}'): cannot resolve relation '${displayName}' on table '${plan.rootTable}' \u2014 no model is configured. Use rawExists(subquery(...)) for an EXISTS over an uncorrelated or undeclared subquery.`
|
|
14934
|
+
);
|
|
14935
|
+
}
|
|
14936
|
+
let currentSource = plan.rootTable;
|
|
14937
|
+
let allHopsDeclared = true;
|
|
14938
|
+
for (const hop of relationPath) {
|
|
14939
|
+
if (!hop) {
|
|
14940
|
+
allHopsDeclared = false;
|
|
14941
|
+
break;
|
|
14942
|
+
}
|
|
14943
|
+
const rel = model.getRelation(`${currentSource}.${hop}`);
|
|
14944
|
+
if (!rel) {
|
|
14945
|
+
allHopsDeclared = false;
|
|
14946
|
+
break;
|
|
14947
|
+
}
|
|
14948
|
+
currentSource = rel.target;
|
|
14949
|
+
}
|
|
14950
|
+
if (!allHopsDeclared) {
|
|
14951
|
+
throw new Error(
|
|
14952
|
+
`exists('${displayName}'): no relation '${displayName}' is declared on table '${plan.rootTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
14791
14953
|
);
|
|
14792
14954
|
}
|
|
14793
14955
|
}
|
|
14956
|
+
return placedDecisions;
|
|
14957
|
+
}
|
|
14958
|
+
function extractAllIncludeDecisions(plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk = defaultFkDerivation) {
|
|
14959
|
+
const includeDecisions = plan.decisions.filter(
|
|
14960
|
+
(d) => d.type === "include-strategy"
|
|
14961
|
+
);
|
|
14962
|
+
if (includeDecisions.length === 0) return [];
|
|
14963
|
+
const treeStrategies = /* @__PURE__ */ new Set(["json_agg", "subquery", "lateral"]);
|
|
14964
|
+
const treeDecisions = [];
|
|
14965
|
+
const flatDecisions = [];
|
|
14966
|
+
for (const d of includeDecisions) {
|
|
14967
|
+
const choice = d.choice;
|
|
14968
|
+
if (treeStrategies.has(choice)) {
|
|
14969
|
+
const converted = toIncludeDecision(d, choice, plan, defaultPk, deriveFk);
|
|
14970
|
+
if (converted) treeDecisions.push(converted);
|
|
14971
|
+
} else if (choice === "join") {
|
|
14972
|
+
const converted = toJoinIncludeDecision(d, plan, defaultPk, deriveFk);
|
|
14973
|
+
if (converted) flatDecisions.push(converted);
|
|
14974
|
+
} else if (choice === "cte") {
|
|
14975
|
+
const converted = toIncludeDecision(d, choice, plan, defaultPk, deriveFk);
|
|
14976
|
+
if (converted) flatDecisions.push(converted);
|
|
14977
|
+
}
|
|
14978
|
+
}
|
|
14979
|
+
const builtTree = buildIncludeTree(treeDecisions);
|
|
14980
|
+
return [...builtTree, ...flatDecisions];
|
|
14981
|
+
}
|
|
14982
|
+
function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk = defaultFkDerivation) {
|
|
14983
|
+
const context = d.context;
|
|
14984
|
+
const relationName = resolveIncludeAlias(context);
|
|
14985
|
+
if (!context.target || !relationName) return void 0;
|
|
14986
|
+
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
14987
|
+
const relationType = context.relationType;
|
|
14988
|
+
const effectiveChoice = choice === "subquery" ? "json_agg" : choice;
|
|
14989
|
+
const includeIntent = resolveIncludeByPath(
|
|
14990
|
+
plan.intent?.include,
|
|
14991
|
+
context.intentPath,
|
|
14992
|
+
relationName
|
|
14993
|
+
);
|
|
14994
|
+
const limit = includeIntent?.limit;
|
|
14995
|
+
return {
|
|
14996
|
+
type: "includeStrategy",
|
|
14997
|
+
choice: effectiveChoice,
|
|
14998
|
+
relationName,
|
|
14999
|
+
relationPath: deriveRelationPathFromIntentPath(
|
|
15000
|
+
Array.isArray(plan.intent?.include) ? plan.intent.include : void 0,
|
|
15001
|
+
context.intentPath,
|
|
15002
|
+
relationName
|
|
15003
|
+
) ?? relationName,
|
|
15004
|
+
targetTable: context.target,
|
|
15005
|
+
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15006
|
+
...relationType && { relationType },
|
|
15007
|
+
foreignKey: Array.isArray(foreignKey) ? foreignKey[0] : foreignKey,
|
|
15008
|
+
parentKey: defaultPk,
|
|
15009
|
+
...context.intentPath && { intentPath: context.intentPath },
|
|
15010
|
+
...limit != null && { limit }
|
|
15011
|
+
};
|
|
14794
15012
|
}
|
|
14795
|
-
function
|
|
15013
|
+
function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk = defaultFkDerivation) {
|
|
14796
15014
|
const context = d.context;
|
|
14797
|
-
const
|
|
15015
|
+
const relationName = context.relation ?? context.includeAlias;
|
|
15016
|
+
if (!context.target || !relationName) return void 0;
|
|
15017
|
+
const intentPath = context?.intentPath;
|
|
15018
|
+
const relationPath = deriveRelationPathFromIntentPath(
|
|
15019
|
+
Array.isArray(plan.intent?.include) ? plan.intent.include : void 0,
|
|
15020
|
+
intentPath,
|
|
15021
|
+
relationName
|
|
15022
|
+
) ?? relationName;
|
|
15023
|
+
const includeIntent = resolveIncludeByPath(
|
|
15024
|
+
plan.intent?.include,
|
|
15025
|
+
intentPath,
|
|
15026
|
+
relationName
|
|
15027
|
+
);
|
|
15028
|
+
let columns = [defaultPk];
|
|
15029
|
+
if (includeIntent?.select?.type === "fields" && includeIntent.select.fields) {
|
|
15030
|
+
const fields = includeIntent.select.fields.filter((f) => f !== defaultPk);
|
|
15031
|
+
columns = [defaultPk, ...fields];
|
|
15032
|
+
}
|
|
15033
|
+
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15034
|
+
const relationType = context.relationType;
|
|
14798
15035
|
let conditions;
|
|
14799
|
-
if (
|
|
14800
|
-
const
|
|
14801
|
-
|
|
14802
|
-
|
|
15036
|
+
if (includeIntent?.where) {
|
|
15037
|
+
const converted = convertWhereToDecisions(
|
|
15038
|
+
includeIntent.where,
|
|
15039
|
+
relationName
|
|
14803
15040
|
);
|
|
14804
|
-
if (
|
|
14805
|
-
conditions =
|
|
14806
|
-
if (model) {
|
|
14807
|
-
enrichExistsStubsInConditions(conditions, targetTable, model);
|
|
14808
|
-
}
|
|
15041
|
+
if (converted.length > 0) {
|
|
15042
|
+
conditions = converted;
|
|
14809
15043
|
}
|
|
14810
15044
|
}
|
|
14811
|
-
const
|
|
14812
|
-
const relIR = model && context.relation ? model.getRelation(
|
|
14813
|
-
`${sourceTableForRelation}.${context.relation}`
|
|
14814
|
-
) : void 0;
|
|
14815
|
-
const foreignKey = relIR ? typeof relIR.foreignKey === "string" ? relIR.foreignKey : relIR.foreignKey?.[0] : void 0;
|
|
14816
|
-
const relationType = relIR?.type === "belongsTo" ? "belongsTo" : relIR?.type === "hasMany" || relIR?.type === "hasOne" ? relIR.type : void 0;
|
|
14817
|
-
const parentKey = relationType === "belongsTo" ? relIR?.targetKey : relIR?.sourceKey;
|
|
14818
|
-
let operator = "exists";
|
|
14819
|
-
if (matchingIntent?.kind === "notExists") {
|
|
14820
|
-
operator = "notExists";
|
|
14821
|
-
} else if (matchingIntent?.kind === "relationFilter") {
|
|
14822
|
-
const mode = matchingIntent.mode ?? "some";
|
|
14823
|
-
if (mode === "none") operator = "notExists";
|
|
14824
|
-
else if (mode === "every") operator = "every";
|
|
14825
|
-
}
|
|
14826
|
-
const includeIntent = matchingIntent?.include;
|
|
14827
|
-
const includeDecisions = includeIntent ? Object.entries(includeIntent).map(([rel, opts]) => ({
|
|
14828
|
-
type: "existsInclude",
|
|
14829
|
-
relation: rel,
|
|
14830
|
-
joinType: opts.join ?? "inner"
|
|
14831
|
-
})) : void 0;
|
|
14832
|
-
const relationName = context.relation;
|
|
15045
|
+
const joinType = d.joinType;
|
|
14833
15046
|
return {
|
|
14834
|
-
type: "
|
|
14835
|
-
|
|
14836
|
-
|
|
14837
|
-
|
|
14838
|
-
|
|
14839
|
-
|
|
14840
|
-
|
|
14841
|
-
|
|
14842
|
-
|
|
14843
|
-
|
|
14844
|
-
|
|
14845
|
-
...
|
|
14846
|
-
// parentKey provides the explicit PK override for non-default PK columns.
|
|
14847
|
-
...parentKey ? { parentKey } : {},
|
|
14848
|
-
...conditions ? { conditions } : {},
|
|
14849
|
-
...d.choice === "join" ? { choice: "join" } : {},
|
|
14850
|
-
...relationName ? { relationName } : {},
|
|
14851
|
-
...includeDecisions ? { include: includeDecisions } : {}
|
|
15047
|
+
type: "includeStrategy",
|
|
15048
|
+
choice: "join",
|
|
15049
|
+
relationName,
|
|
15050
|
+
relationPath,
|
|
15051
|
+
targetTable: context.target,
|
|
15052
|
+
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15053
|
+
...relationType && { relationType },
|
|
15054
|
+
foreignKey: Array.isArray(foreignKey) ? foreignKey[0] : foreignKey,
|
|
15055
|
+
parentKey: defaultPk,
|
|
15056
|
+
columns,
|
|
15057
|
+
...joinType && { joinType },
|
|
15058
|
+
...conditions && { conditions }
|
|
14852
15059
|
};
|
|
14853
15060
|
}
|
|
14854
|
-
function
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
|
|
14861
|
-
|
|
14862
|
-
|
|
14863
|
-
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
15061
|
+
function snakeToCamel(s) {
|
|
15062
|
+
return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
15063
|
+
}
|
|
15064
|
+
function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk = DEFAULT_PK_COLUMN, deriveFk = defaultFkDerivation) {
|
|
15065
|
+
const includes = plan.intent?.include;
|
|
15066
|
+
if (!includes || includes.length === 0) return [];
|
|
15067
|
+
const sourceTable = plan.rootTable;
|
|
15068
|
+
const relationsFromSource = model.getRelationsFrom(sourceTable);
|
|
15069
|
+
const synthesized = [];
|
|
15070
|
+
for (const inc of includes) {
|
|
15071
|
+
const alias = inc.relation;
|
|
15072
|
+
if (inc.join !== "inner" && inc.join !== "left") continue;
|
|
15073
|
+
if (coveredRelations.has(alias)) continue;
|
|
15074
|
+
const rel = relationsFromSource.find(
|
|
15075
|
+
(r) => r.name === alias || snakeToCamel(r.name) === alias
|
|
15076
|
+
);
|
|
15077
|
+
if (!rel) continue;
|
|
15078
|
+
const rawFk = rel.foreignKey ? Array.isArray(rel.foreignKey) ? rel.foreignKey[0] : rel.foreignKey : deriveFk(
|
|
15079
|
+
rel.type === "belongsTo" ? rel.target : sourceTable,
|
|
15080
|
+
defaultPk
|
|
15081
|
+
);
|
|
15082
|
+
let columns = [defaultPk];
|
|
15083
|
+
if (inc.select?.type === "fields" && inc.select.fields) {
|
|
15084
|
+
const extraFields = inc.select.fields.filter((f) => f !== defaultPk);
|
|
15085
|
+
columns = [defaultPk, ...extraFields];
|
|
15086
|
+
}
|
|
15087
|
+
let conditions;
|
|
15088
|
+
if (inc.where) {
|
|
15089
|
+
const converted = convertWhereToDecisions(inc.where, alias);
|
|
15090
|
+
if (converted.length > 0) conditions = converted;
|
|
15091
|
+
}
|
|
15092
|
+
synthesized.push({
|
|
15093
|
+
type: "includeStrategy",
|
|
15094
|
+
choice: "join",
|
|
15095
|
+
relationName: alias,
|
|
15096
|
+
relationPath: alias,
|
|
15097
|
+
targetTable: rel.target,
|
|
15098
|
+
sourceTable,
|
|
15099
|
+
...rel.type && {
|
|
15100
|
+
relationType: rel.type
|
|
15101
|
+
},
|
|
15102
|
+
foreignKey: Array.isArray(rawFk) ? rawFk[0] : rawFk,
|
|
15103
|
+
parentKey: defaultPk,
|
|
15104
|
+
columns,
|
|
15105
|
+
joinType: inc.join,
|
|
15106
|
+
...conditions && { conditions }
|
|
15107
|
+
});
|
|
15108
|
+
}
|
|
15109
|
+
return synthesized;
|
|
15110
|
+
}
|
|
15111
|
+
function buildIncludeTree(allDecisions) {
|
|
15112
|
+
const decisionsByPath = /* @__PURE__ */ new Map();
|
|
15113
|
+
for (const d of allDecisions) {
|
|
15114
|
+
if (d.intentPath) decisionsByPath.set(d.intentPath, d);
|
|
15115
|
+
}
|
|
15116
|
+
function parentPath(path) {
|
|
15117
|
+
const lastDot = path.lastIndexOf(".include[");
|
|
15118
|
+
return lastDot > 0 ? path.substring(0, lastDot) : void 0;
|
|
15119
|
+
}
|
|
15120
|
+
const childrenMap = /* @__PURE__ */ new Map();
|
|
15121
|
+
const rootDecisions = [];
|
|
15122
|
+
for (const d of allDecisions) {
|
|
15123
|
+
const pp = d.intentPath ? parentPath(d.intentPath) : void 0;
|
|
15124
|
+
if (pp && decisionsByPath.has(pp)) {
|
|
15125
|
+
const siblings = childrenMap.get(pp) ?? [];
|
|
15126
|
+
siblings.push(d);
|
|
15127
|
+
childrenMap.set(pp, siblings);
|
|
14890
15128
|
} else {
|
|
14891
|
-
|
|
14892
|
-
|
|
14893
|
-
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
15129
|
+
rootDecisions.push(d);
|
|
15130
|
+
}
|
|
15131
|
+
}
|
|
15132
|
+
function attachChildren(decision) {
|
|
15133
|
+
const path = decision.intentPath;
|
|
15134
|
+
const children = path ? childrenMap.get(path) : void 0;
|
|
15135
|
+
if (!children || children.length === 0) return decision;
|
|
15136
|
+
return {
|
|
15137
|
+
...decision,
|
|
15138
|
+
children: children.map(attachChildren)
|
|
15139
|
+
};
|
|
15140
|
+
}
|
|
15141
|
+
return rootDecisions.map(attachChildren);
|
|
15142
|
+
}
|
|
15143
|
+
|
|
15144
|
+
// src/adapter-compiler-select.ts
|
|
15145
|
+
var ADAPTER_MULTIWORD_BASE_TYPES = [
|
|
15146
|
+
"timestamp with time zone",
|
|
15147
|
+
"timestamp without time zone",
|
|
15148
|
+
"time with time zone",
|
|
15149
|
+
"time without time zone",
|
|
15150
|
+
"double precision",
|
|
15151
|
+
"character varying",
|
|
15152
|
+
"bit varying"
|
|
15153
|
+
];
|
|
15154
|
+
var ADAPTER_IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
15155
|
+
function assertSafeTypeName(typeName, colIndex) {
|
|
15156
|
+
const raw = typeName.trim();
|
|
15157
|
+
if (raw.length === 0) {
|
|
15158
|
+
throw new Error(
|
|
15159
|
+
`BatchValues compile error: type name at column index ${colIndex} must not be empty.`
|
|
15160
|
+
);
|
|
15161
|
+
}
|
|
15162
|
+
let rest = raw;
|
|
15163
|
+
if (rest.endsWith("[]")) {
|
|
15164
|
+
rest = rest.slice(0, -2);
|
|
15165
|
+
if (rest.endsWith("[]")) {
|
|
15166
|
+
throw new Error(
|
|
15167
|
+
`BatchValues compile error: unsafe type name '${typeName}' at column index ${colIndex}. At most one array suffix "[]" is allowed as a raw type-name input.`
|
|
15168
|
+
);
|
|
15169
|
+
}
|
|
15170
|
+
}
|
|
15171
|
+
const modifierMatch = rest.match(/\(([^)]*)\)$/);
|
|
15172
|
+
if (modifierMatch) {
|
|
15173
|
+
const inner = modifierMatch[1] ?? "";
|
|
15174
|
+
if (!/^\d+(?:,\d+)?$/.test(inner)) {
|
|
15175
|
+
throw new Error(
|
|
15176
|
+
`BatchValues compile error: unsafe type name '${typeName}' at column index ${colIndex}. Type modifier must be "(N)" or "(N,M)" with digits only; got "(${inner})".`
|
|
15177
|
+
);
|
|
14905
15178
|
}
|
|
14906
|
-
|
|
15179
|
+
rest = rest.slice(0, rest.length - modifierMatch[0].length).trimEnd();
|
|
14907
15180
|
}
|
|
14908
|
-
|
|
14909
|
-
|
|
14910
|
-
|
|
14911
|
-
}
|
|
15181
|
+
const baseLower = rest.toLowerCase();
|
|
15182
|
+
if (ADAPTER_MULTIWORD_BASE_TYPES.includes(baseLower)) {
|
|
15183
|
+
return;
|
|
14912
15184
|
}
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
sourceTable,
|
|
14918
|
-
model,
|
|
14919
|
-
out
|
|
15185
|
+
const parts = rest.split(".");
|
|
15186
|
+
if (parts.length > 2) {
|
|
15187
|
+
throw new Error(
|
|
15188
|
+
`BatchValues compile error: unsafe type name '${typeName}' at column index ${colIndex}. Schema-qualified types allow at most one dot (schema.type).`
|
|
14920
15189
|
);
|
|
14921
15190
|
}
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
if (d.relationName) return;
|
|
14928
|
-
out.push({ decision: d, container: decisions, index: i });
|
|
14929
|
-
} else if ((d.type === "whereAnd" || d.type === "whereOr" || d.type === "whereNot") && d.conditions) {
|
|
14930
|
-
collectExistsStubs(d.conditions, out);
|
|
15191
|
+
for (const part of parts) {
|
|
15192
|
+
if (!ADAPTER_IDENT_RE.test(part)) {
|
|
15193
|
+
throw new Error(
|
|
15194
|
+
`BatchValues compile error: unsafe type name '${typeName}' at column index ${colIndex}. Base type "${part}" is not a valid SQL identifier and is not in the multi-word type allowlist.`
|
|
15195
|
+
);
|
|
14931
15196
|
}
|
|
14932
|
-
}
|
|
14933
|
-
}
|
|
14934
|
-
function normalizeStubRelation(targetTable) {
|
|
14935
|
-
if (Array.isArray(targetTable) && targetTable.length > 0)
|
|
14936
|
-
return targetTable[targetTable.length - 1];
|
|
14937
|
-
if (typeof targetTable === "string") return targetTable;
|
|
14938
|
-
return void 0;
|
|
15197
|
+
}
|
|
14939
15198
|
}
|
|
14940
|
-
function
|
|
14941
|
-
|
|
14942
|
-
return targetTable.join(".");
|
|
14943
|
-
if (typeof targetTable === "string") return targetTable;
|
|
14944
|
-
return void 0;
|
|
15199
|
+
function bridgeLegacyDecisions(decisions) {
|
|
15200
|
+
return decisions;
|
|
14945
15201
|
}
|
|
14946
|
-
function
|
|
14947
|
-
const
|
|
14948
|
-
|
|
14949
|
-
const
|
|
14950
|
-
|
|
14951
|
-
|
|
14952
|
-
|
|
14953
|
-
false,
|
|
14954
|
-
plan.rootTable,
|
|
14955
|
-
model,
|
|
14956
|
-
nestedExistsTargets
|
|
14957
|
-
);
|
|
15202
|
+
function buildBatchValuesRangeFn(bv, startParamIndex, aliasOverride) {
|
|
15203
|
+
const params = [];
|
|
15204
|
+
let paramIdx = startParamIndex - 1;
|
|
15205
|
+
const effectiveAlias = aliasOverride ?? bv.alias;
|
|
15206
|
+
for (let ci = 0; ci < bv.columns.length; ci++) {
|
|
15207
|
+
const rawType = bv.types[ci];
|
|
15208
|
+
if (rawType) assertSafeTypeName(rawType, ci);
|
|
14958
15209
|
}
|
|
14959
|
-
const
|
|
14960
|
-
|
|
15210
|
+
const unnestArgs = bv.columns.map((col, i) => {
|
|
15211
|
+
const colArray = bv.data[i] ?? [];
|
|
15212
|
+
let pgBaseType;
|
|
15213
|
+
if (bv.types[i]) {
|
|
15214
|
+
const rawType = bv.types[i];
|
|
15215
|
+
pgBaseType = rawType.endsWith("[]") ? rawType.slice(0, -2) : rawType;
|
|
15216
|
+
} else {
|
|
15217
|
+
const sampleValue = colArray.find((v) => v !== null && v !== void 0);
|
|
15218
|
+
const pgArrayType = inferPgArrayType(col, {}, sampleValue);
|
|
15219
|
+
pgBaseType = stripArraySuffix(pgArrayType);
|
|
15220
|
+
}
|
|
15221
|
+
params.push(colArray);
|
|
15222
|
+
paramIdx++;
|
|
15223
|
+
return createTypeCastParamRef(paramIdx, pgBaseType, true);
|
|
15224
|
+
});
|
|
15225
|
+
const unnestCall = funcCall("unnest", unnestArgs);
|
|
15226
|
+
const colnames = [...bv.columns, ...bv.ordinality ? ["ord"] : []].map(
|
|
15227
|
+
(c) => ({ String: { sval: c } })
|
|
14961
15228
|
);
|
|
14962
|
-
const
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
|
|
15229
|
+
const rangeFunction = {
|
|
15230
|
+
RangeFunction: {
|
|
15231
|
+
functions: [{ List: { items: [unnestCall] } }],
|
|
15232
|
+
ordinality: bv.ordinality,
|
|
15233
|
+
alias: { aliasname: effectiveAlias, colnames }
|
|
15234
|
+
}
|
|
15235
|
+
};
|
|
15236
|
+
return { rangeFunction, params };
|
|
15237
|
+
}
|
|
15238
|
+
function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
15239
|
+
if (joins.length === 0) return [];
|
|
15240
|
+
const model = deps.model;
|
|
15241
|
+
const naming = deps.naming;
|
|
15242
|
+
const deriveFk = deps.deriveFk ?? defaultFkDerivation;
|
|
15243
|
+
const defaultPk = deps.defaultPk;
|
|
15244
|
+
const results = [];
|
|
15245
|
+
for (const intent of joins) {
|
|
15246
|
+
if (intent.relation !== void 0) {
|
|
15247
|
+
if (!model) {
|
|
15248
|
+
throw new Error(
|
|
15249
|
+
`join('${intent.relation}'): relation-mode join requires a model for FK resolution.`
|
|
15250
|
+
);
|
|
15251
|
+
}
|
|
15252
|
+
const relationsFromRoot = model.getRelationsFrom(rootTable);
|
|
15253
|
+
const rel = relationsFromRoot.find((r) => r.name === intent.relation);
|
|
15254
|
+
if (!rel) {
|
|
15255
|
+
throw new Error(
|
|
15256
|
+
`join('${intent.relation}'): relation not found on table '${rootTable}'. Available: ${relationsFromRoot.map((r) => r.name).join(", ")}`
|
|
15257
|
+
);
|
|
15258
|
+
}
|
|
15259
|
+
const isBelongsTo = rel.type === "belongsTo";
|
|
15260
|
+
const rawFk = rel.foreignKey ? Array.isArray(rel.foreignKey) ? rel.foreignKey[0] : rel.foreignKey : deriveFk(isBelongsTo ? rootTable : rel.target, defaultPk);
|
|
15261
|
+
const sourceColumn = isBelongsTo ? rawFk : defaultPk;
|
|
15262
|
+
const targetColumn = isBelongsTo ? defaultPk : rawFk;
|
|
15263
|
+
const alias = intent.alias ?? intent.relation;
|
|
15264
|
+
results.push({
|
|
15265
|
+
type: "join",
|
|
15266
|
+
targetTable: rel.target,
|
|
15267
|
+
alias,
|
|
15268
|
+
sourceColumn,
|
|
15269
|
+
targetColumn,
|
|
15270
|
+
joinType: intent.type
|
|
14979
15271
|
});
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
const
|
|
14983
|
-
|
|
14984
|
-
|
|
14985
|
-
|
|
14986
|
-
|
|
14987
|
-
|
|
14988
|
-
|
|
14989
|
-
|
|
14990
|
-
|
|
14991
|
-
|
|
14992
|
-
|
|
14993
|
-
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
const innerConditions = isEvery && rawInnerConditions ? [
|
|
15008
|
-
{
|
|
15009
|
-
type: "logical",
|
|
15010
|
-
operator: "not",
|
|
15011
|
-
conditions: rawInnerConditions
|
|
15012
|
-
}
|
|
15013
|
-
] : rawInnerConditions;
|
|
15014
|
-
enriched = buildMultiHopExistsChain(
|
|
15015
|
-
matchingIntent.relation,
|
|
15016
|
-
plan.rootTable,
|
|
15017
|
-
outerOperator,
|
|
15018
|
-
innerConditions,
|
|
15019
|
-
model
|
|
15272
|
+
} else if (intent.batchValues !== void 0) {
|
|
15273
|
+
const bv = intent.batchValues;
|
|
15274
|
+
const alias = intent.alias ?? bv.alias;
|
|
15275
|
+
const { rangeFunction, params: bvParams } = buildBatchValuesRangeFn(
|
|
15276
|
+
bv,
|
|
15277
|
+
1,
|
|
15278
|
+
alias
|
|
15279
|
+
);
|
|
15280
|
+
const bvOnParamState = createCompilerState();
|
|
15281
|
+
bvOnParamState.paramIndex = bvParams.length;
|
|
15282
|
+
const bvCtx = {
|
|
15283
|
+
rootTable,
|
|
15284
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
15285
|
+
paramState: bvOnParamState,
|
|
15286
|
+
naming,
|
|
15287
|
+
outerTable: alias,
|
|
15288
|
+
...schemaName !== void 0 && { schemaName },
|
|
15289
|
+
...deps.bindingNames !== void 0 && {
|
|
15290
|
+
bindingNames: deps.bindingNames
|
|
15291
|
+
},
|
|
15292
|
+
...model !== void 0 && { model },
|
|
15293
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15294
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15295
|
+
},
|
|
15296
|
+
compileSubquery: () => {
|
|
15297
|
+
throw new Error(
|
|
15298
|
+
"Subquery in BatchValues JOIN ON condition is not supported."
|
|
15020
15299
|
);
|
|
15021
15300
|
}
|
|
15022
|
-
}
|
|
15023
|
-
|
|
15024
|
-
|
|
15025
|
-
|
|
15026
|
-
|
|
15027
|
-
|
|
15028
|
-
|
|
15301
|
+
};
|
|
15302
|
+
const onNode = compileWhereIntent(intent.on, bvCtx);
|
|
15303
|
+
const allBvParams = [
|
|
15304
|
+
...bvParams,
|
|
15305
|
+
...bvOnParamState.parameters
|
|
15306
|
+
];
|
|
15307
|
+
results.push({
|
|
15308
|
+
type: "join",
|
|
15309
|
+
targetTable: alias,
|
|
15310
|
+
alias,
|
|
15311
|
+
joinType: intent.type,
|
|
15312
|
+
joinRarg: rangeFunction,
|
|
15313
|
+
joinOnNode: onNode,
|
|
15314
|
+
// batchValuesParams are spliced into this.state.parameters BEFORE
|
|
15315
|
+
// other params in compiler.ts, so $1/$2/... refs align correctly.
|
|
15316
|
+
batchValuesParams: allBvParams
|
|
15317
|
+
});
|
|
15318
|
+
} else {
|
|
15319
|
+
const paramState = createCompilerState();
|
|
15320
|
+
const tableAlias = intent.alias ?? intent.table;
|
|
15321
|
+
const tableAliasMap = /* @__PURE__ */ new Map();
|
|
15322
|
+
tableAliasMap.set(rootTable, rootTable);
|
|
15323
|
+
if (tableAlias !== rootTable) {
|
|
15324
|
+
tableAliasMap.set(tableAlias, intent.table);
|
|
15029
15325
|
}
|
|
15030
|
-
const
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15326
|
+
const ctx = {
|
|
15327
|
+
rootTable,
|
|
15328
|
+
aliases: tableAliasMap,
|
|
15329
|
+
paramState,
|
|
15330
|
+
naming,
|
|
15331
|
+
// outerTable = tableAlias so FieldRef(scope:'outer') resolves to the
|
|
15332
|
+
// joined alias (e.g. 'e2' in self-join ON conditions).
|
|
15333
|
+
outerTable: tableAlias,
|
|
15334
|
+
...schemaName !== void 0 && { schemaName },
|
|
15335
|
+
...deps.bindingNames !== void 0 && {
|
|
15336
|
+
bindingNames: deps.bindingNames
|
|
15337
|
+
},
|
|
15338
|
+
...model !== void 0 && { model },
|
|
15339
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15340
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15341
|
+
},
|
|
15342
|
+
compileSubquery: () => {
|
|
15343
|
+
throw new Error("Subquery in JOIN ON condition is not supported.");
|
|
15039
15344
|
}
|
|
15040
|
-
|
|
15041
|
-
|
|
15345
|
+
};
|
|
15346
|
+
const onNode = compileWhereIntent(intent.on, ctx);
|
|
15347
|
+
const joinedRangeVar = rangeVar(
|
|
15348
|
+
intent.table,
|
|
15349
|
+
tableAlias,
|
|
15350
|
+
schemaForFromName(schemaName, intent.table, deps.bindingNames, naming),
|
|
15351
|
+
naming
|
|
15352
|
+
);
|
|
15353
|
+
results.push({
|
|
15354
|
+
type: "join",
|
|
15355
|
+
targetTable: intent.table,
|
|
15356
|
+
alias: tableAlias,
|
|
15357
|
+
joinType: intent.type,
|
|
15358
|
+
joinRarg: joinedRangeVar,
|
|
15359
|
+
joinOnNode: onNode
|
|
15042
15360
|
});
|
|
15043
|
-
if (stubIdx >= 0) {
|
|
15044
|
-
const stub = stubs[stubIdx];
|
|
15045
|
-
if (stub) {
|
|
15046
|
-
stub.container[stub.index] = enriched;
|
|
15047
|
-
consumedStubIndices.add(stubIdx);
|
|
15048
|
-
placedDecisions.push(enriched);
|
|
15049
|
-
}
|
|
15050
|
-
} else {
|
|
15051
|
-
const srcForKey = context.sourceTable ?? "";
|
|
15052
|
-
const targetName = context.target;
|
|
15053
|
-
const relationPath = contextRelationPath;
|
|
15054
|
-
const compoundTarget = targetName ? `${srcForKey}:${targetName}` : "";
|
|
15055
|
-
const compoundPath = relationPath ? `${srcForKey}:${relationPath}` : "";
|
|
15056
|
-
if (compoundTarget && nestedExistsTargets.has(compoundTarget) || compoundPath && nestedExistsTargets.has(compoundPath)) {
|
|
15057
|
-
continue;
|
|
15058
|
-
}
|
|
15059
|
-
decisions.push(enriched);
|
|
15060
|
-
placedDecisions.push(enriched);
|
|
15061
|
-
}
|
|
15062
15361
|
}
|
|
15063
15362
|
}
|
|
15064
|
-
|
|
15065
|
-
|
|
15066
|
-
|
|
15067
|
-
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
|
|
15071
|
-
|
|
15072
|
-
|
|
15073
|
-
|
|
15074
|
-
|
|
15075
|
-
);
|
|
15076
|
-
}
|
|
15077
|
-
let currentSource = plan.rootTable;
|
|
15078
|
-
let allHopsDeclared = true;
|
|
15079
|
-
for (const hop of relationPath) {
|
|
15080
|
-
if (!hop) {
|
|
15081
|
-
allHopsDeclared = false;
|
|
15082
|
-
break;
|
|
15083
|
-
}
|
|
15084
|
-
const rel = model.getRelation(`${currentSource}.${hop}`);
|
|
15085
|
-
if (!rel) {
|
|
15086
|
-
allHopsDeclared = false;
|
|
15087
|
-
break;
|
|
15363
|
+
return results;
|
|
15364
|
+
}
|
|
15365
|
+
function stripJoinColumnsForAggregation(decisions, intent) {
|
|
15366
|
+
const isAggregateOnly = intent.select && "type" in intent.select && intent.select.type === "aggregate" && !("fields" in intent.select && intent.select.fields);
|
|
15367
|
+
const isDistinct = intent.distinct === true;
|
|
15368
|
+
const hasGroupBy = intent.groupBy && intent.groupBy.length > 0;
|
|
15369
|
+
const hasExplicitColumns = intent.select && "type" in intent.select && intent.select.type === "expressions";
|
|
15370
|
+
if (isAggregateOnly || isDistinct || hasGroupBy || hasExplicitColumns) {
|
|
15371
|
+
for (const d of decisions) {
|
|
15372
|
+
if (d.type === "includeStrategy" && d.choice === "join") {
|
|
15373
|
+
d.columns = [];
|
|
15088
15374
|
}
|
|
15089
|
-
currentSource = rel.target;
|
|
15090
|
-
}
|
|
15091
|
-
if (!allHopsDeclared) {
|
|
15092
|
-
throw new Error(
|
|
15093
|
-
`exists('${displayName}'): no relation '${displayName}' is declared on table '${plan.rootTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
15094
|
-
);
|
|
15095
15375
|
}
|
|
15096
15376
|
}
|
|
15097
|
-
return placedDecisions;
|
|
15098
15377
|
}
|
|
15099
|
-
function
|
|
15100
|
-
const
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
const converted = toJoinIncludeDecision(d, plan, defaultPk, deriveFk);
|
|
15114
|
-
if (converted) flatDecisions.push(converted);
|
|
15115
|
-
} else if (choice === "cte") {
|
|
15116
|
-
const converted = toIncludeDecision(d, choice, plan, defaultPk, deriveFk);
|
|
15117
|
-
if (converted) flatDecisions.push(converted);
|
|
15378
|
+
function buildRelationColumnsMap(decisions, includedRelations) {
|
|
15379
|
+
const map = /* @__PURE__ */ new Map();
|
|
15380
|
+
for (const d of decisions) {
|
|
15381
|
+
if (!(d.type === "selectRelationColumn" && d.relation && d.column))
|
|
15382
|
+
continue;
|
|
15383
|
+
const col = d.column;
|
|
15384
|
+
const alias = d.alias;
|
|
15385
|
+
const fullRelation = d.relation;
|
|
15386
|
+
const rootRelation = fullRelation.split(".")[0] ?? "";
|
|
15387
|
+
if (!includedRelations.has(rootRelation)) continue;
|
|
15388
|
+
const mapKey = fullRelation;
|
|
15389
|
+
if (col === "*") {
|
|
15390
|
+
map.set(mapKey, [{ col: "*" }]);
|
|
15391
|
+
continue;
|
|
15118
15392
|
}
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
|
|
15124
|
-
|
|
15125
|
-
|
|
15126
|
-
|
|
15127
|
-
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15128
|
-
const relationType = context.relationType;
|
|
15129
|
-
const effectiveChoice = choice === "subquery" ? "json_agg" : choice;
|
|
15130
|
-
const includeIntent = resolveIncludeByPath(
|
|
15131
|
-
plan.intent?.include,
|
|
15132
|
-
context.intentPath,
|
|
15133
|
-
relationName
|
|
15134
|
-
);
|
|
15135
|
-
const limit = includeIntent?.limit;
|
|
15136
|
-
return {
|
|
15137
|
-
type: "includeStrategy",
|
|
15138
|
-
choice: effectiveChoice,
|
|
15139
|
-
relationName,
|
|
15140
|
-
relationPath: deriveRelationPathFromIntentPath(
|
|
15141
|
-
Array.isArray(plan.intent?.include) ? plan.intent.include : void 0,
|
|
15142
|
-
context.intentPath,
|
|
15143
|
-
relationName
|
|
15144
|
-
) ?? relationName,
|
|
15145
|
-
targetTable: context.target,
|
|
15146
|
-
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15147
|
-
...relationType && { relationType },
|
|
15148
|
-
foreignKey: Array.isArray(foreignKey) ? foreignKey[0] : foreignKey,
|
|
15149
|
-
parentKey: defaultPk,
|
|
15150
|
-
...context.intentPath && { intentPath: context.intentPath },
|
|
15151
|
-
...limit != null && { limit }
|
|
15152
|
-
};
|
|
15153
|
-
}
|
|
15154
|
-
function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk = defaultFkDerivation) {
|
|
15155
|
-
const context = d.context;
|
|
15156
|
-
const relationName = context.relation ?? context.includeAlias;
|
|
15157
|
-
if (!context.target || !relationName) return void 0;
|
|
15158
|
-
const intentPath = context?.intentPath;
|
|
15159
|
-
const relationPath = deriveRelationPathFromIntentPath(
|
|
15160
|
-
Array.isArray(plan.intent?.include) ? plan.intent.include : void 0,
|
|
15161
|
-
intentPath,
|
|
15162
|
-
relationName
|
|
15163
|
-
) ?? relationName;
|
|
15164
|
-
const includeIntent = resolveIncludeByPath(
|
|
15165
|
-
plan.intent?.include,
|
|
15166
|
-
intentPath,
|
|
15167
|
-
relationName
|
|
15168
|
-
);
|
|
15169
|
-
let columns = [defaultPk];
|
|
15170
|
-
if (includeIntent?.select?.type === "fields" && includeIntent.select.fields) {
|
|
15171
|
-
const fields = includeIntent.select.fields.filter((f) => f !== defaultPk);
|
|
15172
|
-
columns = [defaultPk, ...fields];
|
|
15173
|
-
}
|
|
15174
|
-
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15175
|
-
const relationType = context.relationType;
|
|
15176
|
-
let conditions;
|
|
15177
|
-
if (includeIntent?.where) {
|
|
15178
|
-
const converted = convertWhereToDecisions(
|
|
15179
|
-
includeIntent.where,
|
|
15180
|
-
relationName
|
|
15181
|
-
);
|
|
15182
|
-
if (converted.length > 0) {
|
|
15183
|
-
conditions = converted;
|
|
15393
|
+
const existing = map.get(mapKey);
|
|
15394
|
+
if (existing) {
|
|
15395
|
+
if (existing.length === 1 && existing[0]?.col === "*") continue;
|
|
15396
|
+
if (!existing.some((e) => e.col === col)) {
|
|
15397
|
+
existing.push({ col, ...alias !== void 0 && { alias } });
|
|
15398
|
+
}
|
|
15399
|
+
} else {
|
|
15400
|
+
map.set(mapKey, [{ col, ...alias !== void 0 && { alias } }]);
|
|
15184
15401
|
}
|
|
15185
15402
|
}
|
|
15186
|
-
|
|
15187
|
-
return {
|
|
15188
|
-
type: "includeStrategy",
|
|
15189
|
-
choice: "join",
|
|
15190
|
-
relationName,
|
|
15191
|
-
relationPath,
|
|
15192
|
-
targetTable: context.target,
|
|
15193
|
-
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15194
|
-
...relationType && { relationType },
|
|
15195
|
-
foreignKey: Array.isArray(foreignKey) ? foreignKey[0] : foreignKey,
|
|
15196
|
-
parentKey: defaultPk,
|
|
15197
|
-
columns,
|
|
15198
|
-
...joinType && { joinType },
|
|
15199
|
-
...conditions && { conditions }
|
|
15200
|
-
};
|
|
15201
|
-
}
|
|
15202
|
-
function snakeToCamel(s) {
|
|
15203
|
-
return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
15403
|
+
return map;
|
|
15204
15404
|
}
|
|
15205
|
-
function
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15405
|
+
function injectAndValidateRelationColumns(enrichedUnifiedDecisions, relationColumnsMap, model) {
|
|
15406
|
+
if (relationColumnsMap.size === 0) return;
|
|
15407
|
+
for (const d of enrichedUnifiedDecisions) {
|
|
15408
|
+
if (d.type === "includeStrategy" && d.relationName) {
|
|
15409
|
+
const mapKey = d.relationPath ?? d.relationName;
|
|
15410
|
+
const entries = mapKey ? relationColumnsMap.get(mapKey) : void 0;
|
|
15411
|
+
if (entries) {
|
|
15412
|
+
const mut = d;
|
|
15413
|
+
mut.columns = entries.map((e) => e.col);
|
|
15414
|
+
const aliasMap = {};
|
|
15415
|
+
for (const { col, alias } of entries) {
|
|
15416
|
+
if (alias) aliasMap[col] = alias;
|
|
15417
|
+
}
|
|
15418
|
+
if (Object.keys(aliasMap).length > 0) {
|
|
15419
|
+
mut.columnAliases = aliasMap;
|
|
15420
|
+
}
|
|
15421
|
+
}
|
|
15422
|
+
}
|
|
15423
|
+
}
|
|
15424
|
+
if (!model) return;
|
|
15425
|
+
for (const d of enrichedUnifiedDecisions) {
|
|
15426
|
+
if (d.type === "includeStrategy" && d.columns && d.targetTable && !(d.columns.length === 1 && d.columns[0] === "*")) {
|
|
15427
|
+
const targetTable = model.getTable(d.targetTable);
|
|
15428
|
+
if (targetTable) {
|
|
15429
|
+
const validColumnNames = new Set(
|
|
15430
|
+
targetTable.columns.map((c) => c.name)
|
|
15431
|
+
);
|
|
15432
|
+
const invalid = d.columns.filter(
|
|
15433
|
+
(c) => !validColumnNames.has(c)
|
|
15434
|
+
);
|
|
15435
|
+
if (invalid.length > 0) {
|
|
15436
|
+
throw new Error(
|
|
15437
|
+
`Unknown column(s) ${invalid.map((c) => `'${c}'`).join(", ")} in relation '${d.relationName}' (table '${d.targetTable}'). Available: ${[...validColumnNames].join(", ")}`
|
|
15438
|
+
);
|
|
15439
|
+
}
|
|
15440
|
+
}
|
|
15232
15441
|
}
|
|
15233
|
-
synthesized.push({
|
|
15234
|
-
type: "includeStrategy",
|
|
15235
|
-
choice: "join",
|
|
15236
|
-
relationName: alias,
|
|
15237
|
-
relationPath: alias,
|
|
15238
|
-
targetTable: rel.target,
|
|
15239
|
-
sourceTable,
|
|
15240
|
-
...rel.type && {
|
|
15241
|
-
relationType: rel.type
|
|
15242
|
-
},
|
|
15243
|
-
foreignKey: Array.isArray(rawFk) ? rawFk[0] : rawFk,
|
|
15244
|
-
parentKey: defaultPk,
|
|
15245
|
-
columns,
|
|
15246
|
-
joinType: inc.join,
|
|
15247
|
-
...conditions && { conditions }
|
|
15248
|
-
});
|
|
15249
15442
|
}
|
|
15250
|
-
return synthesized;
|
|
15251
15443
|
}
|
|
15252
|
-
function
|
|
15253
|
-
const
|
|
15254
|
-
for (const d of
|
|
15255
|
-
if (d.
|
|
15444
|
+
function applyJoinHydrationPrefixes(decisions) {
|
|
15445
|
+
const usages = [];
|
|
15446
|
+
for (const d of decisions) {
|
|
15447
|
+
if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
|
|
15448
|
+
continue;
|
|
15449
|
+
}
|
|
15450
|
+
const relationName = d.relationName;
|
|
15451
|
+
const relationPath = d.relationPath ?? relationName;
|
|
15452
|
+
usages.push({ relationName, relationPath });
|
|
15256
15453
|
}
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15454
|
+
const pathCountsByRelation = countDistinctRelationPathsByName(usages);
|
|
15455
|
+
for (const d of decisions) {
|
|
15456
|
+
if (d.type !== "includeStrategy" || d.choice !== "join" || !d.relationName) {
|
|
15457
|
+
continue;
|
|
15458
|
+
}
|
|
15459
|
+
const relationName = d.relationName;
|
|
15460
|
+
const relationPath = d.relationPath ?? relationName;
|
|
15461
|
+
const usesFullPath = (pathCountsByRelation.get(relationName) ?? 0) > 1;
|
|
15462
|
+
d.hydrationPrefix = usesFullPath ? relationPath : relationName;
|
|
15260
15463
|
}
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15464
|
+
}
|
|
15465
|
+
function enrichRangeDecisions(allDecisions, model, rootTable) {
|
|
15466
|
+
if (!model) return;
|
|
15467
|
+
for (let i = 0; i < allDecisions.length; i++) {
|
|
15468
|
+
const d = allDecisions[i];
|
|
15469
|
+
if (d && d.type === "where" && (d.operator === "contains" || d.operator === "containedBy" || d.operator === "overlaps")) {
|
|
15470
|
+
const tableName = d.table || rootTable;
|
|
15471
|
+
const table = model.getTable(tableName);
|
|
15472
|
+
if (table) {
|
|
15473
|
+
const col = table.columns.find((c) => c.name === d.column);
|
|
15474
|
+
if (col?.type.endsWith("range")) {
|
|
15475
|
+
allDecisions[i] = { ...d, dataType: col.type };
|
|
15476
|
+
}
|
|
15477
|
+
}
|
|
15271
15478
|
}
|
|
15272
15479
|
}
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
|
|
15276
|
-
|
|
15480
|
+
}
|
|
15481
|
+
function buildSimplifiedPlanReport(plan, allDecisions, schemaName) {
|
|
15482
|
+
const bvFromSource = plan.intent?.batchValuesSource;
|
|
15483
|
+
const batchValuesFromFields = bvFromSource ? (() => {
|
|
15484
|
+
const { rangeFunction, params } = buildBatchValuesRangeFn(
|
|
15485
|
+
bvFromSource,
|
|
15486
|
+
1
|
|
15487
|
+
);
|
|
15277
15488
|
return {
|
|
15278
|
-
|
|
15279
|
-
|
|
15489
|
+
batchValuesFromNode: rangeFunction,
|
|
15490
|
+
batchValuesFromParams: params
|
|
15280
15491
|
};
|
|
15281
|
-
}
|
|
15282
|
-
return
|
|
15492
|
+
})() : {};
|
|
15493
|
+
return {
|
|
15494
|
+
rootTable: plan.rootTable,
|
|
15495
|
+
decisions: allDecisions,
|
|
15496
|
+
...schemaName ? { schema: schemaName } : {},
|
|
15497
|
+
...plan.intent?.existsWrap ? { existsWrap: true } : {},
|
|
15498
|
+
...plan.intent?.lock ? { lock: plan.intent.lock } : {},
|
|
15499
|
+
...batchValuesFromFields
|
|
15500
|
+
};
|
|
15283
15501
|
}
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
|
|
15300
|
-
|
|
15502
|
+
function compileSelect(plan, options, deps) {
|
|
15503
|
+
const schemaName = deps.schemaName;
|
|
15504
|
+
const resolvedModelForCompiler = options?.model ?? deps.model;
|
|
15505
|
+
const compilerOptions = {
|
|
15506
|
+
naming: deps.naming,
|
|
15507
|
+
...schemaName && { schema: schemaName },
|
|
15508
|
+
defaultPkColumnName: deps.defaultPk,
|
|
15509
|
+
deriveFkColumnName: deps.deriveFk,
|
|
15510
|
+
...deps.bindingNames !== void 0 && {
|
|
15511
|
+
bindingNames: deps.bindingNames
|
|
15512
|
+
},
|
|
15513
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15514
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15515
|
+
},
|
|
15516
|
+
...resolvedModelForCompiler != null && {
|
|
15517
|
+
model: resolvedModelForCompiler
|
|
15518
|
+
}
|
|
15519
|
+
};
|
|
15520
|
+
const execIntent = plan.executableIntent ?? plan.intent;
|
|
15521
|
+
const planForCompilation = plan.executableIntent !== void 0 ? { ...plan, intent: plan.executableIntent } : plan;
|
|
15522
|
+
let simplifiedPlan;
|
|
15523
|
+
if (execIntent) {
|
|
15524
|
+
let decisions = intentToDecisions(execIntent, plan.rootTable);
|
|
15525
|
+
const resolvedModel = options?.model ?? deps.model;
|
|
15526
|
+
if (resolvedModel) {
|
|
15527
|
+
decisions = convertDottedFieldsToExists(
|
|
15528
|
+
decisions,
|
|
15529
|
+
plan.rootTable,
|
|
15530
|
+
resolvedModel
|
|
15531
|
+
);
|
|
15532
|
+
}
|
|
15533
|
+
enrichExistsDecisionsInPlace(
|
|
15534
|
+
decisions,
|
|
15535
|
+
planForCompilation,
|
|
15536
|
+
options?.model ?? deps.model
|
|
15301
15537
|
);
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
|
|
15308
|
-
|
|
15538
|
+
const unifiedIncludeDecisions = extractAllIncludeDecisions(
|
|
15539
|
+
planForCompilation,
|
|
15540
|
+
deps.defaultPk,
|
|
15541
|
+
deps.deriveFk
|
|
15542
|
+
);
|
|
15543
|
+
const coveredByPlanner = new Set(
|
|
15544
|
+
unifiedIncludeDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
|
|
15545
|
+
);
|
|
15546
|
+
const synthesizedModel = options?.model ?? deps.model;
|
|
15547
|
+
const synthesizedJoins = synthesizedModel ? synthesizeMissingJoinDecisions(
|
|
15548
|
+
planForCompilation,
|
|
15549
|
+
coveredByPlanner,
|
|
15550
|
+
synthesizedModel,
|
|
15551
|
+
deps.defaultPk,
|
|
15552
|
+
deps.deriveFk
|
|
15553
|
+
) : [];
|
|
15554
|
+
const allUnifiedIncludeDecisions = synthesizedJoins.length > 0 ? [...unifiedIncludeDecisions, ...synthesizedJoins] : unifiedIncludeDecisions;
|
|
15555
|
+
const enrichedUnifiedDecisions = [
|
|
15556
|
+
...allUnifiedIncludeDecisions
|
|
15557
|
+
];
|
|
15558
|
+
stripJoinColumnsForAggregation(enrichedUnifiedDecisions, execIntent);
|
|
15559
|
+
applyJoinHydrationPrefixes(enrichedUnifiedDecisions);
|
|
15560
|
+
const includedRelations = new Set(
|
|
15561
|
+
enrichedUnifiedDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
|
|
15562
|
+
);
|
|
15563
|
+
if (includedRelations.size > 0) {
|
|
15564
|
+
const relationColumnsMap = buildRelationColumnsMap(
|
|
15565
|
+
decisions,
|
|
15566
|
+
includedRelations
|
|
15567
|
+
);
|
|
15568
|
+
injectAndValidateRelationColumns(
|
|
15569
|
+
enrichedUnifiedDecisions,
|
|
15570
|
+
relationColumnsMap,
|
|
15571
|
+
options?.model ?? deps.model
|
|
15309
15572
|
);
|
|
15310
15573
|
}
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15574
|
+
const deduplicatedDecisions = includedRelations.size > 0 ? decisions.filter((d) => {
|
|
15575
|
+
if (d.type === "selectRelationColumn" && d.relation) {
|
|
15576
|
+
const rel = d.relation;
|
|
15577
|
+
const rootRelation = rel.split(".")[0] ?? rel;
|
|
15578
|
+
if (includedRelations.has(rootRelation)) {
|
|
15579
|
+
return false;
|
|
15580
|
+
}
|
|
15581
|
+
}
|
|
15582
|
+
return true;
|
|
15583
|
+
}) : decisions;
|
|
15584
|
+
const joinIntentDecisions = execIntent?.joins && execIntent.joins.length > 0 ? compileJoinIntents(
|
|
15585
|
+
execIntent.joins,
|
|
15586
|
+
plan.rootTable,
|
|
15587
|
+
schemaName,
|
|
15588
|
+
deps
|
|
15589
|
+
) : [];
|
|
15590
|
+
const allDecisions = [
|
|
15591
|
+
...deduplicatedDecisions,
|
|
15592
|
+
...enrichedUnifiedDecisions,
|
|
15593
|
+
...joinIntentDecisions
|
|
15594
|
+
];
|
|
15595
|
+
const rangeModel = options?.model ?? deps.model;
|
|
15596
|
+
enrichRangeDecisions(allDecisions, rangeModel, plan.rootTable);
|
|
15597
|
+
simplifiedPlan = buildSimplifiedPlanReport(
|
|
15598
|
+
planForCompilation,
|
|
15599
|
+
allDecisions,
|
|
15600
|
+
schemaName
|
|
15601
|
+
);
|
|
15602
|
+
} else {
|
|
15603
|
+
simplifiedPlan = {
|
|
15604
|
+
rootTable: plan.rootTable,
|
|
15605
|
+
decisions: bridgeLegacyDecisions(plan.decisions),
|
|
15606
|
+
...schemaName ? { schema: schemaName } : {}
|
|
15607
|
+
};
|
|
15608
|
+
}
|
|
15609
|
+
const result = compilePlan(simplifiedPlan, compilerOptions);
|
|
15610
|
+
return {
|
|
15611
|
+
sql: result.sql,
|
|
15612
|
+
parameters: result.parameters
|
|
15613
|
+
};
|
|
15614
|
+
}
|
|
15615
|
+
function compileWithIncludes(plan, options, deps) {
|
|
15616
|
+
const main = compileSelect(plan, options, deps);
|
|
15617
|
+
const subqueryIncludes = [];
|
|
15618
|
+
for (const d of plan.decisions) {
|
|
15619
|
+
if (d.type !== "include-strategy" || d.choice !== "subquery") continue;
|
|
15620
|
+
const ctx = d.context;
|
|
15621
|
+
if (!ctx.target) continue;
|
|
15622
|
+
const relationName = ctx.includeAlias ?? ctx.relation;
|
|
15623
|
+
if (!relationName) continue;
|
|
15624
|
+
const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
|
|
15625
|
+
const fk = Array.isArray(rawFk) ? rawFk[0] : rawFk;
|
|
15626
|
+
const isBelongsTo = ctx.relationType === "belongsTo";
|
|
15627
|
+
const sourceKey = isBelongsTo ? fk : "id";
|
|
15628
|
+
const targetFk = isBelongsTo ? "id" : fk;
|
|
15629
|
+
const includeIntent = plan.intent?.include?.find(
|
|
15630
|
+
(i) => i.relation === relationName || i.relation === ctx.includeAlias
|
|
15631
|
+
);
|
|
15632
|
+
const entry = {
|
|
15633
|
+
relationName,
|
|
15634
|
+
targetTable: ctx.target,
|
|
15635
|
+
foreignKey: targetFk,
|
|
15636
|
+
sourceKey,
|
|
15637
|
+
sourceTable: ctx.sourceTable ?? plan.rootTable
|
|
15638
|
+
};
|
|
15639
|
+
if (typeof ctx.relationType === "string") {
|
|
15640
|
+
entry.relationType = ctx.relationType;
|
|
15641
|
+
}
|
|
15642
|
+
if (includeIntent?.select != null) {
|
|
15643
|
+
entry.select = includeIntent.select;
|
|
15319
15644
|
}
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
const baseLower = rest.toLowerCase();
|
|
15323
|
-
if (ADAPTER_MULTIWORD_BASE_TYPES.includes(baseLower)) {
|
|
15324
|
-
return;
|
|
15325
|
-
}
|
|
15326
|
-
const parts = rest.split(".");
|
|
15327
|
-
if (parts.length > 2) {
|
|
15328
|
-
throw new Error(
|
|
15329
|
-
`BatchValues compile error: unsafe type name '${typeName}' at column index ${colIndex}. Schema-qualified types allow at most one dot (schema.type).`
|
|
15330
|
-
);
|
|
15331
|
-
}
|
|
15332
|
-
for (const part of parts) {
|
|
15333
|
-
if (!ADAPTER_IDENT_RE.test(part)) {
|
|
15334
|
-
throw new Error(
|
|
15335
|
-
`BatchValues compile error: unsafe type name '${typeName}' at column index ${colIndex}. Base type "${part}" is not a valid SQL identifier and is not in the multi-word type allowlist.`
|
|
15336
|
-
);
|
|
15645
|
+
if (includeIntent?.where != null) {
|
|
15646
|
+
entry.where = includeIntent.where;
|
|
15337
15647
|
}
|
|
15648
|
+
subqueryIncludes.push(entry);
|
|
15338
15649
|
}
|
|
15650
|
+
return { main, subqueryIncludes };
|
|
15339
15651
|
}
|
|
15340
|
-
|
|
15341
|
-
|
|
15652
|
+
|
|
15653
|
+
// src/adapter-compiler-mutations.ts
|
|
15654
|
+
init_binding_registry();
|
|
15655
|
+
init_compile_where();
|
|
15656
|
+
init_compiler_utils();
|
|
15657
|
+
init_handlers();
|
|
15658
|
+
function whereIntentAsDecision(where) {
|
|
15659
|
+
return where;
|
|
15342
15660
|
}
|
|
15343
|
-
function
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
for (let ci = 0; ci < bv.columns.length; ci++) {
|
|
15348
|
-
const rawType = bv.types[ci];
|
|
15349
|
-
if (rawType) assertSafeTypeName(rawType, ci);
|
|
15350
|
-
}
|
|
15351
|
-
const unnestArgs = bv.columns.map((col, i) => {
|
|
15352
|
-
const colArray = bv.data[i] ?? [];
|
|
15353
|
-
let pgBaseType;
|
|
15354
|
-
if (bv.types[i]) {
|
|
15355
|
-
const rawType = bv.types[i];
|
|
15356
|
-
pgBaseType = rawType.endsWith("[]") ? rawType.slice(0, -2) : rawType;
|
|
15357
|
-
} else {
|
|
15358
|
-
const sampleValue = colArray.find((v) => v !== null && v !== void 0);
|
|
15359
|
-
const pgArrayType = inferPgArrayType(col, {}, sampleValue);
|
|
15360
|
-
pgBaseType = stripArraySuffix(pgArrayType);
|
|
15361
|
-
}
|
|
15362
|
-
params.push(colArray);
|
|
15363
|
-
paramIdx++;
|
|
15364
|
-
return createTypeCastParamRef(paramIdx, pgBaseType, true);
|
|
15661
|
+
function renumberSqlParams(sql, offset) {
|
|
15662
|
+
if (offset === 0) return sql;
|
|
15663
|
+
return sql.replace(/\$(\d+)/g, (_match, num) => {
|
|
15664
|
+
return `$${Number.parseInt(num, 10) + offset}`;
|
|
15365
15665
|
});
|
|
15366
|
-
const unnestCall = funcCall("unnest", unnestArgs);
|
|
15367
|
-
const colnames = [...bv.columns, ...bv.ordinality ? ["ord"] : []].map(
|
|
15368
|
-
(c) => ({ String: { sval: c } })
|
|
15369
|
-
);
|
|
15370
|
-
const rangeFunction = {
|
|
15371
|
-
RangeFunction: {
|
|
15372
|
-
functions: [{ List: { items: [unnestCall] } }],
|
|
15373
|
-
ordinality: bv.ordinality,
|
|
15374
|
-
alias: { aliasname: effectiveAlias, colnames }
|
|
15375
|
-
}
|
|
15376
|
-
};
|
|
15377
|
-
return { rangeFunction, params };
|
|
15378
15666
|
}
|
|
15379
|
-
function
|
|
15380
|
-
if (joins.length === 0) return [];
|
|
15667
|
+
function compileSourceQueryCte(operation, sourceName, sourceQuery, options, deps) {
|
|
15381
15668
|
const model = deps.model;
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
for (const intent of joins) {
|
|
15387
|
-
if (intent.relation !== void 0) {
|
|
15388
|
-
if (!model) {
|
|
15389
|
-
throw new Error(
|
|
15390
|
-
`join('${intent.relation}'): relation-mode join requires a model for FK resolution.`
|
|
15391
|
-
);
|
|
15392
|
-
}
|
|
15393
|
-
const relationsFromRoot = model.getRelationsFrom(rootTable);
|
|
15394
|
-
const rel = relationsFromRoot.find((r) => r.name === intent.relation);
|
|
15395
|
-
if (!rel) {
|
|
15396
|
-
throw new Error(
|
|
15397
|
-
`join('${intent.relation}'): relation not found on table '${rootTable}'. Available: ${relationsFromRoot.map((r) => r.name).join(", ")}`
|
|
15398
|
-
);
|
|
15399
|
-
}
|
|
15400
|
-
const isBelongsTo = rel.type === "belongsTo";
|
|
15401
|
-
const rawFk = rel.foreignKey ? Array.isArray(rel.foreignKey) ? rel.foreignKey[0] : rel.foreignKey : deriveFk(isBelongsTo ? rootTable : rel.target, defaultPk);
|
|
15402
|
-
const sourceColumn = isBelongsTo ? rawFk : defaultPk;
|
|
15403
|
-
const targetColumn = isBelongsTo ? defaultPk : rawFk;
|
|
15404
|
-
const alias = intent.alias ?? intent.relation;
|
|
15405
|
-
results.push({
|
|
15406
|
-
type: "join",
|
|
15407
|
-
targetTable: rel.target,
|
|
15408
|
-
alias,
|
|
15409
|
-
sourceColumn,
|
|
15410
|
-
targetColumn,
|
|
15411
|
-
joinType: intent.type
|
|
15412
|
-
});
|
|
15413
|
-
} else if (intent.batchValues !== void 0) {
|
|
15414
|
-
const bv = intent.batchValues;
|
|
15415
|
-
const alias = intent.alias ?? bv.alias;
|
|
15416
|
-
const { rangeFunction, params: bvParams } = buildBatchValuesRangeFn(
|
|
15417
|
-
bv,
|
|
15418
|
-
1,
|
|
15419
|
-
alias
|
|
15420
|
-
);
|
|
15421
|
-
const bvOnParamState = createCompilerState();
|
|
15422
|
-
bvOnParamState.paramIndex = bvParams.length;
|
|
15423
|
-
const bvCtx = {
|
|
15424
|
-
rootTable,
|
|
15425
|
-
aliases: /* @__PURE__ */ new Map(),
|
|
15426
|
-
paramState: bvOnParamState,
|
|
15427
|
-
naming,
|
|
15428
|
-
outerTable: alias,
|
|
15429
|
-
...schemaName !== void 0 && { schemaName },
|
|
15430
|
-
...model !== void 0 && { model },
|
|
15431
|
-
compileSubquery: () => {
|
|
15432
|
-
throw new Error(
|
|
15433
|
-
"Subquery in BatchValues JOIN ON condition is not supported."
|
|
15434
|
-
);
|
|
15435
|
-
}
|
|
15436
|
-
};
|
|
15437
|
-
const onNode = compileWhereIntent(intent.on, bvCtx);
|
|
15438
|
-
const allBvParams = [
|
|
15439
|
-
...bvParams,
|
|
15440
|
-
...bvOnParamState.parameters
|
|
15441
|
-
];
|
|
15442
|
-
results.push({
|
|
15443
|
-
type: "join",
|
|
15444
|
-
targetTable: alias,
|
|
15445
|
-
alias,
|
|
15446
|
-
joinType: intent.type,
|
|
15447
|
-
joinRarg: rangeFunction,
|
|
15448
|
-
joinOnNode: onNode,
|
|
15449
|
-
// batchValuesParams are spliced into this.state.parameters BEFORE
|
|
15450
|
-
// other params in compiler.ts, so $1/$2/... refs align correctly.
|
|
15451
|
-
batchValuesParams: allBvParams
|
|
15452
|
-
});
|
|
15453
|
-
} else {
|
|
15454
|
-
const paramState = createCompilerState();
|
|
15455
|
-
const tableAlias = intent.alias ?? intent.table;
|
|
15456
|
-
const tableAliasMap = /* @__PURE__ */ new Map();
|
|
15457
|
-
tableAliasMap.set(rootTable, rootTable);
|
|
15458
|
-
if (tableAlias !== rootTable) {
|
|
15459
|
-
tableAliasMap.set(tableAlias, intent.table);
|
|
15460
|
-
}
|
|
15461
|
-
const ctx = {
|
|
15462
|
-
rootTable,
|
|
15463
|
-
aliases: tableAliasMap,
|
|
15464
|
-
paramState,
|
|
15465
|
-
naming,
|
|
15466
|
-
// outerTable = tableAlias so FieldRef(scope:'outer') resolves to the
|
|
15467
|
-
// joined alias (e.g. 'e2' in self-join ON conditions).
|
|
15468
|
-
outerTable: tableAlias,
|
|
15469
|
-
...schemaName !== void 0 && { schemaName },
|
|
15470
|
-
...model !== void 0 && { model },
|
|
15471
|
-
compileSubquery: () => {
|
|
15472
|
-
throw new Error("Subquery in JOIN ON condition is not supported.");
|
|
15473
|
-
}
|
|
15474
|
-
};
|
|
15475
|
-
const onNode = compileWhereIntent(intent.on, ctx);
|
|
15476
|
-
const joinedRangeVar = rangeVar(
|
|
15477
|
-
intent.table,
|
|
15478
|
-
tableAlias,
|
|
15479
|
-
schemaName,
|
|
15480
|
-
naming
|
|
15481
|
-
);
|
|
15482
|
-
results.push({
|
|
15483
|
-
type: "join",
|
|
15484
|
-
targetTable: intent.table,
|
|
15485
|
-
alias: tableAlias,
|
|
15486
|
-
joinType: intent.type,
|
|
15487
|
-
joinRarg: joinedRangeVar,
|
|
15488
|
-
joinOnNode: onNode
|
|
15489
|
-
});
|
|
15490
|
-
}
|
|
15669
|
+
if (model === void 0) {
|
|
15670
|
+
throw new Error(
|
|
15671
|
+
`${operation} with sourceQuery requires a model to emit the source CTE for '${sourceName}'.`
|
|
15672
|
+
);
|
|
15491
15673
|
}
|
|
15492
|
-
|
|
15674
|
+
const sourcePlan = planFn(sourceQuery, model, {
|
|
15675
|
+
dialectCapabilities: deps.dialectCapabilities ?? POSTGRESQL_CAPABILITIES
|
|
15676
|
+
});
|
|
15677
|
+
return compileSelect(sourcePlan, options, deps);
|
|
15493
15678
|
}
|
|
15494
|
-
function
|
|
15495
|
-
|
|
15496
|
-
|
|
15497
|
-
const hasGroupBy = intent.groupBy && intent.groupBy.length > 0;
|
|
15498
|
-
const hasExplicitColumns = intent.select && "type" in intent.select && intent.select.type === "expressions";
|
|
15499
|
-
if (isAggregateOnly || isDistinct || hasGroupBy || hasExplicitColumns) {
|
|
15500
|
-
for (const d of decisions) {
|
|
15501
|
-
if (d.type === "includeStrategy" && d.choice === "join") {
|
|
15502
|
-
d.columns = [];
|
|
15503
|
-
}
|
|
15504
|
-
}
|
|
15679
|
+
function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
|
|
15680
|
+
if (sourceCte === void 0) {
|
|
15681
|
+
return { sql, parameters };
|
|
15505
15682
|
}
|
|
15683
|
+
const cteParamCount = sourceCte.parameters.length;
|
|
15684
|
+
const sourceCteName = emittedBindName(sourceName, naming);
|
|
15685
|
+
return {
|
|
15686
|
+
sql: `WITH ${quoteIdent2(sourceCteName, "alias")} as (${sourceCte.sql}) ${renumberSqlParams(sql, cteParamCount)}`,
|
|
15687
|
+
parameters: [...sourceCte.parameters, ...parameters]
|
|
15688
|
+
};
|
|
15506
15689
|
}
|
|
15507
|
-
function
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
}
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
}
|
|
15690
|
+
function resolveMutationExistsForeignKey(foreignKey, relationName) {
|
|
15691
|
+
if (typeof foreignKey === "string") return foreignKey;
|
|
15692
|
+
if (!Array.isArray(foreignKey)) return void 0;
|
|
15693
|
+
if (foreignKey.length > 1) {
|
|
15694
|
+
throw new Error(
|
|
15695
|
+
`Mutation exists()/notExists() guards do not support composite foreignKey relations yet: relation '${relationName}' has foreignKey ${JSON.stringify(foreignKey)}. Composite FK correlation is tracked by #179.`
|
|
15696
|
+
);
|
|
15697
|
+
}
|
|
15698
|
+
return foreignKey[0];
|
|
15699
|
+
}
|
|
15700
|
+
function resolveExistsRelation(sourceTable, relation, model) {
|
|
15701
|
+
if (!model) return { targetTable: relation };
|
|
15702
|
+
const relationName = `${sourceTable}.${relation}`;
|
|
15703
|
+
const rel = model.getRelation(relationName);
|
|
15704
|
+
if (!rel) return { targetTable: relation };
|
|
15705
|
+
const targetTable = rel.target;
|
|
15706
|
+
if (rel.type === "belongsTo") {
|
|
15707
|
+
const fk2 = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
|
|
15708
|
+
return {
|
|
15709
|
+
targetTable,
|
|
15710
|
+
...fk2 !== void 0 && { sourceColumn: fk2 },
|
|
15711
|
+
targetColumn: "id"
|
|
15712
|
+
};
|
|
15531
15713
|
}
|
|
15532
|
-
|
|
15714
|
+
const fk = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
|
|
15715
|
+
return {
|
|
15716
|
+
targetTable,
|
|
15717
|
+
sourceColumn: rel.sourceKey ?? "id",
|
|
15718
|
+
...fk !== void 0 && { targetColumn: fk }
|
|
15719
|
+
};
|
|
15533
15720
|
}
|
|
15534
|
-
function
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
if (alias) aliasMap[col] = alias;
|
|
15546
|
-
}
|
|
15547
|
-
if (Object.keys(aliasMap).length > 0) {
|
|
15548
|
-
mut.columnAliases = aliasMap;
|
|
15549
|
-
}
|
|
15550
|
-
}
|
|
15551
|
-
}
|
|
15721
|
+
function resolveExistsIntent(where, sourceTable, deps) {
|
|
15722
|
+
const w = where;
|
|
15723
|
+
const kind = w.kind;
|
|
15724
|
+
if (kind === "and" || kind === "or") {
|
|
15725
|
+
const conditions = w.conditions;
|
|
15726
|
+
if (!conditions) return where;
|
|
15727
|
+
const enriched = conditions.map(
|
|
15728
|
+
(c) => resolveExistsIntent(c, sourceTable, deps)
|
|
15729
|
+
);
|
|
15730
|
+
const changed = enriched.some((c, i) => c !== conditions[i]);
|
|
15731
|
+
return changed ? { ...w, conditions: enriched } : where;
|
|
15552
15732
|
}
|
|
15553
|
-
if (
|
|
15554
|
-
|
|
15555
|
-
if (
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
`Unknown column(s) ${invalid.map((c) => `'${c}'`).join(", ")} in relation '${d.relationName}' (table '${d.targetTable}'). Available: ${[...validColumnNames].join(", ")}`
|
|
15567
|
-
);
|
|
15568
|
-
}
|
|
15569
|
-
}
|
|
15570
|
-
}
|
|
15733
|
+
if (kind === "not") {
|
|
15734
|
+
const condition = w.condition;
|
|
15735
|
+
if (!condition) return where;
|
|
15736
|
+
const enriched = resolveExistsIntent(condition, sourceTable, deps);
|
|
15737
|
+
return enriched !== condition ? { ...w, condition: enriched } : where;
|
|
15738
|
+
}
|
|
15739
|
+
if (kind !== "exists" && kind !== "notExists") return where;
|
|
15740
|
+
const relation = w.relation;
|
|
15741
|
+
const resolved = resolveExistsRelation(sourceTable, relation, deps.model);
|
|
15742
|
+
const nestedWhere = w.where;
|
|
15743
|
+
const enrichedNestedWhere = nestedWhere !== void 0 ? resolveExistsIntent(nestedWhere, resolved.targetTable, deps) : void 0;
|
|
15744
|
+
if (resolved.targetTable === relation && !resolved.sourceColumn && !resolved.targetColumn && enrichedNestedWhere === nestedWhere) {
|
|
15745
|
+
return where;
|
|
15571
15746
|
}
|
|
15747
|
+
return {
|
|
15748
|
+
...w,
|
|
15749
|
+
targetTable: resolved.targetTable,
|
|
15750
|
+
...resolved.sourceColumn !== void 0 && {
|
|
15751
|
+
sourceColumn: resolved.sourceColumn
|
|
15752
|
+
},
|
|
15753
|
+
...resolved.targetColumn !== void 0 && {
|
|
15754
|
+
targetColumn: resolved.targetColumn
|
|
15755
|
+
},
|
|
15756
|
+
...enrichedNestedWhere !== void 0 && { where: enrichedNestedWhere }
|
|
15757
|
+
};
|
|
15572
15758
|
}
|
|
15573
|
-
function
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15759
|
+
function getColumnTypes(tableName, columns, deps) {
|
|
15760
|
+
if (!deps.model) return void 0;
|
|
15761
|
+
const table = deps.model.getTable(tableName);
|
|
15762
|
+
if (!table) return void 0;
|
|
15763
|
+
let result;
|
|
15764
|
+
for (const col of columns) {
|
|
15765
|
+
const columnIR = table.columns.find((c) => c.name === col);
|
|
15766
|
+
if (columnIR) {
|
|
15767
|
+
result ??= {};
|
|
15768
|
+
result[col] = columnIR.originalDbType ?? columnIR.type;
|
|
15578
15769
|
}
|
|
15579
|
-
const relationName = d.relationName;
|
|
15580
|
-
const relationPath = d.relationPath ?? relationName;
|
|
15581
|
-
usages.push({ relationName, relationPath });
|
|
15582
15770
|
}
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15771
|
+
return result;
|
|
15772
|
+
}
|
|
15773
|
+
function compileUpsertActionWhere(where, table, state, deps, schemaName) {
|
|
15774
|
+
const whereCtx = {
|
|
15775
|
+
rootTable: table,
|
|
15776
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
15777
|
+
paramState: state,
|
|
15778
|
+
naming: deps.naming,
|
|
15779
|
+
...schemaName !== void 0 && { schemaName },
|
|
15780
|
+
...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
|
|
15781
|
+
...deps.model !== void 0 && { model: deps.model },
|
|
15782
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15783
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15784
|
+
},
|
|
15785
|
+
compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
|
|
15786
|
+
sqIntent,
|
|
15787
|
+
paramOffset,
|
|
15788
|
+
deps.naming,
|
|
15789
|
+
schemaName,
|
|
15790
|
+
"rawExists",
|
|
15791
|
+
deps.bindingNames,
|
|
15792
|
+
deps.dialectCapabilities
|
|
15793
|
+
)
|
|
15794
|
+
};
|
|
15795
|
+
return compileWhereIntent(where, whereCtx);
|
|
15796
|
+
}
|
|
15797
|
+
function compileInsert2(intent, options, deps) {
|
|
15798
|
+
const schemaName = deps.schemaName;
|
|
15799
|
+
const ctx = {
|
|
15800
|
+
naming: deps.naming,
|
|
15801
|
+
rootTable: intent.table,
|
|
15802
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
15803
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15804
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15805
|
+
},
|
|
15806
|
+
...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
|
|
15807
|
+
maxRecursiveDepth: 100
|
|
15808
|
+
};
|
|
15809
|
+
const state = createCompilerState();
|
|
15810
|
+
const firstRow = intent.values?.[0] ?? {};
|
|
15811
|
+
const columns = Object.keys(firstRow);
|
|
15812
|
+
const rows = intent.values ?? [];
|
|
15813
|
+
const values = rows.map((row) => columns.map((col) => row[col]));
|
|
15814
|
+
const columnTypes = getColumnTypes(intent.table, columns, deps);
|
|
15815
|
+
const config = {
|
|
15816
|
+
table: intent.table,
|
|
15817
|
+
columns,
|
|
15818
|
+
values,
|
|
15819
|
+
...intent.returning && { returning: [...intent.returning] },
|
|
15820
|
+
...columnTypes && { columnTypes }
|
|
15821
|
+
};
|
|
15822
|
+
const maxBatchSize = options?.maxBatchSize;
|
|
15823
|
+
if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
|
|
15824
|
+
throw new InvalidOperationError3(
|
|
15825
|
+
"insert",
|
|
15826
|
+
`Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
|
|
15827
|
+
);
|
|
15592
15828
|
}
|
|
15829
|
+
const batchThreshold = options?.batchThreshold ?? 50;
|
|
15830
|
+
const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
|
|
15831
|
+
const ast = useUnnest ? compileUnnestInsert(config, ctx, state) : compileInsert(config, ctx, state);
|
|
15832
|
+
const sql = deparseQuoted(ast);
|
|
15833
|
+
return {
|
|
15834
|
+
sql,
|
|
15835
|
+
parameters: state.parameters
|
|
15836
|
+
};
|
|
15593
15837
|
}
|
|
15594
|
-
function
|
|
15595
|
-
|
|
15596
|
-
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15838
|
+
function compileInsertFrom2(intent, options, deps) {
|
|
15839
|
+
const schemaName = deps.schemaName;
|
|
15840
|
+
const sourceCte = intent.sourceQuery !== void 0 && !hasBindingName(deps.bindingNames, intent.source, deps.naming) ? compileSourceQueryCte(
|
|
15841
|
+
"compileInsertFrom",
|
|
15842
|
+
intent.source,
|
|
15843
|
+
intent.sourceQuery,
|
|
15844
|
+
options,
|
|
15845
|
+
deps
|
|
15846
|
+
) : void 0;
|
|
15847
|
+
const bindingNames = intent.sourceQuery !== void 0 ? withBindingName(deps.bindingNames, intent.source, deps.naming) : deps.bindingNames;
|
|
15848
|
+
const ctx = {
|
|
15849
|
+
naming: deps.naming,
|
|
15850
|
+
rootTable: intent.source,
|
|
15851
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
15852
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15853
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15854
|
+
},
|
|
15855
|
+
...bindingNames !== void 0 && { bindingNames },
|
|
15856
|
+
maxRecursiveDepth: 100
|
|
15857
|
+
};
|
|
15858
|
+
const state = createCompilerState();
|
|
15859
|
+
const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.source, deps) : void 0;
|
|
15860
|
+
const config = {
|
|
15861
|
+
targetTable: intent.table,
|
|
15862
|
+
sourceTable: intent.source,
|
|
15863
|
+
...intent.columns && { columns: [...intent.columns] },
|
|
15864
|
+
...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
|
|
15865
|
+
...intent.limit !== void 0 && { limit: intent.limit },
|
|
15866
|
+
...intent.returning && { returning: [...intent.returning] }
|
|
15867
|
+
};
|
|
15868
|
+
const ast = compileInsertFrom(config, ctx, state);
|
|
15869
|
+
const sql = deparseQuoted(ast);
|
|
15870
|
+
return prependSourceCte(
|
|
15871
|
+
sql,
|
|
15872
|
+
state.parameters,
|
|
15873
|
+
intent.source,
|
|
15874
|
+
sourceCte,
|
|
15875
|
+
deps.naming
|
|
15876
|
+
);
|
|
15877
|
+
}
|
|
15878
|
+
function compileUpdate2(intent, options, deps) {
|
|
15879
|
+
const schemaName = deps.schemaName;
|
|
15880
|
+
const resolvedModel = options?.model ?? deps.model;
|
|
15881
|
+
const ctx = {
|
|
15882
|
+
naming: deps.naming,
|
|
15883
|
+
rootTable: intent.table,
|
|
15884
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
15885
|
+
...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
|
|
15886
|
+
...resolvedModel !== void 0 && { model: resolvedModel },
|
|
15887
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15888
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15889
|
+
},
|
|
15890
|
+
maxRecursiveDepth: 100
|
|
15891
|
+
};
|
|
15892
|
+
const state = createCompilerState();
|
|
15893
|
+
const setColumns = Object.keys(intent.set ?? {});
|
|
15894
|
+
const columnTypes = getColumnTypes(intent.table, setColumns, deps);
|
|
15895
|
+
const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.table, deps) : void 0;
|
|
15896
|
+
const config = {
|
|
15897
|
+
table: intent.table,
|
|
15898
|
+
set: Object.entries(intent.set ?? {}).map(([column, value]) => ({
|
|
15899
|
+
column,
|
|
15900
|
+
value
|
|
15901
|
+
})),
|
|
15902
|
+
...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
|
|
15903
|
+
...intent.returning && { returning: [...intent.returning] },
|
|
15904
|
+
...columnTypes && { columnTypes }
|
|
15905
|
+
};
|
|
15906
|
+
const ast = compileUpdate(config, ctx, state);
|
|
15907
|
+
const sql = deparseQuoted(ast);
|
|
15908
|
+
return {
|
|
15909
|
+
sql,
|
|
15910
|
+
parameters: state.parameters
|
|
15911
|
+
};
|
|
15912
|
+
}
|
|
15913
|
+
function compileBatchUpdate(intent, _options, deps) {
|
|
15914
|
+
const schemaName = deps.schemaName;
|
|
15915
|
+
const ctx = {
|
|
15916
|
+
naming: deps.naming,
|
|
15917
|
+
rootTable: intent.table,
|
|
15918
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
15919
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15920
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15921
|
+
},
|
|
15922
|
+
...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
|
|
15923
|
+
maxRecursiveDepth: 100
|
|
15924
|
+
};
|
|
15925
|
+
const state = createCompilerState();
|
|
15926
|
+
if (intent.updates.length === 0) {
|
|
15927
|
+
throw new InvalidOperationError3(
|
|
15928
|
+
"update",
|
|
15929
|
+
"batchSet requires at least one row"
|
|
15930
|
+
);
|
|
15931
|
+
}
|
|
15932
|
+
const allColumns = Object.keys(intent.updates[0]);
|
|
15933
|
+
const matchColumns = [...intent.matchColumns];
|
|
15934
|
+
for (const mc of matchColumns) {
|
|
15935
|
+
if (!allColumns.includes(mc)) {
|
|
15936
|
+
throw new InvalidOperationError3(
|
|
15937
|
+
"update",
|
|
15938
|
+
`Match column "${mc}" not found in update data. Each row must include the match column(s).`
|
|
15939
|
+
);
|
|
15607
15940
|
}
|
|
15608
15941
|
}
|
|
15942
|
+
const values = intent.updates.map((row) => allColumns.map((col) => row[col]));
|
|
15943
|
+
validateBatchCardinality(allColumns, values);
|
|
15944
|
+
const columnArrays = transposeToColumnArrays(allColumns, values);
|
|
15945
|
+
const columnTypes = getColumnTypes(intent.table, allColumns, deps);
|
|
15946
|
+
const scalarSet = intent.scalarSet ? Object.entries(intent.scalarSet).map(([column, value]) => ({
|
|
15947
|
+
column,
|
|
15948
|
+
value
|
|
15949
|
+
})) : void 0;
|
|
15950
|
+
let whereGuard;
|
|
15951
|
+
if (intent.where) {
|
|
15952
|
+
const resolvedWhere = resolveExistsIntent(intent.where, intent.table, deps);
|
|
15953
|
+
const whereCtx = {
|
|
15954
|
+
rootTable: intent.table,
|
|
15955
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
15956
|
+
paramState: state,
|
|
15957
|
+
naming: deps.naming,
|
|
15958
|
+
...schemaName !== void 0 && { schemaName },
|
|
15959
|
+
...deps.bindingNames !== void 0 && {
|
|
15960
|
+
bindingNames: deps.bindingNames
|
|
15961
|
+
},
|
|
15962
|
+
...deps.model !== void 0 && { model: deps.model },
|
|
15963
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
15964
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
15965
|
+
},
|
|
15966
|
+
compileSubquery: (sqIntent, paramOffset) => buildSubqueryFromIntent(
|
|
15967
|
+
sqIntent,
|
|
15968
|
+
paramOffset,
|
|
15969
|
+
deps.naming,
|
|
15970
|
+
schemaName,
|
|
15971
|
+
"rawExists",
|
|
15972
|
+
deps.bindingNames,
|
|
15973
|
+
deps.dialectCapabilities
|
|
15974
|
+
)
|
|
15975
|
+
};
|
|
15976
|
+
whereGuard = compileWhereIntent(resolvedWhere, whereCtx);
|
|
15977
|
+
}
|
|
15978
|
+
const config = {
|
|
15979
|
+
table: intent.table,
|
|
15980
|
+
matchColumns,
|
|
15981
|
+
allColumns,
|
|
15982
|
+
columnArrays,
|
|
15983
|
+
...scalarSet && { scalarSet },
|
|
15984
|
+
...intent.returning && { returning: [...intent.returning] },
|
|
15985
|
+
...columnTypes && { columnTypes },
|
|
15986
|
+
...whereGuard !== void 0 && { whereGuard }
|
|
15987
|
+
};
|
|
15988
|
+
const ast = compileUnnestUpdate(config, ctx, state);
|
|
15989
|
+
const sql = deparseQuoted(ast);
|
|
15990
|
+
return {
|
|
15991
|
+
sql,
|
|
15992
|
+
parameters: state.parameters
|
|
15993
|
+
};
|
|
15609
15994
|
}
|
|
15610
|
-
function
|
|
15611
|
-
const
|
|
15612
|
-
const
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
}
|
|
15621
|
-
|
|
15995
|
+
function compileDelete2(intent, options, deps) {
|
|
15996
|
+
const schemaName = deps.schemaName;
|
|
15997
|
+
const resolvedModel = options?.model ?? deps.model;
|
|
15998
|
+
const ctx = {
|
|
15999
|
+
naming: deps.naming,
|
|
16000
|
+
rootTable: intent.table,
|
|
16001
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
16002
|
+
...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
|
|
16003
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
16004
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
16005
|
+
},
|
|
16006
|
+
maxRecursiveDepth: 100,
|
|
16007
|
+
...resolvedModel !== void 0 && { model: resolvedModel }
|
|
16008
|
+
};
|
|
16009
|
+
const state = createCompilerState();
|
|
16010
|
+
const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.table, deps) : void 0;
|
|
16011
|
+
const config = {
|
|
16012
|
+
table: intent.table,
|
|
16013
|
+
...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
|
|
16014
|
+
...intent.returning && { returning: [...intent.returning] }
|
|
16015
|
+
};
|
|
16016
|
+
const ast = compileDelete(config, ctx, state);
|
|
16017
|
+
const sql = deparseQuoted(ast);
|
|
15622
16018
|
return {
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
...schemaName ? { schema: schemaName } : {},
|
|
15626
|
-
...plan.intent?.existsWrap ? { existsWrap: true } : {},
|
|
15627
|
-
...plan.intent?.lock ? { lock: plan.intent.lock } : {},
|
|
15628
|
-
...batchValuesFromFields
|
|
16019
|
+
sql,
|
|
16020
|
+
parameters: state.parameters
|
|
15629
16021
|
};
|
|
15630
16022
|
}
|
|
15631
|
-
function
|
|
16023
|
+
function compileUpsert2(intent, options, deps) {
|
|
15632
16024
|
const schemaName = deps.schemaName;
|
|
15633
|
-
const
|
|
15634
|
-
const compilerOptions = {
|
|
16025
|
+
const ctx = {
|
|
15635
16026
|
naming: deps.naming,
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
...
|
|
15640
|
-
|
|
15641
|
-
|
|
16027
|
+
rootTable: intent.table,
|
|
16028
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
16029
|
+
...deps.bindingNames !== void 0 && { bindingNames: deps.bindingNames },
|
|
16030
|
+
...deps.model !== void 0 && { model: deps.model },
|
|
16031
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
16032
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
16033
|
+
},
|
|
16034
|
+
maxRecursiveDepth: 100
|
|
15642
16035
|
};
|
|
15643
|
-
const
|
|
15644
|
-
const
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
const
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
);
|
|
16036
|
+
const state = createCompilerState();
|
|
16037
|
+
const firstRow = intent.values?.[0] ?? {};
|
|
16038
|
+
const rawExprs = {};
|
|
16039
|
+
const scalarSet = {};
|
|
16040
|
+
if (intent.action.type === "doUpdate" && intent.action.set) {
|
|
16041
|
+
for (const [key, val] of Object.entries(intent.action.set)) {
|
|
16042
|
+
if (isSqlRaw2(val)) {
|
|
16043
|
+
rawExprs[key] = val.sql;
|
|
16044
|
+
} else {
|
|
16045
|
+
scalarSet[key] = val;
|
|
16046
|
+
}
|
|
15655
16047
|
}
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
const
|
|
15662
|
-
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
const enrichedUnifiedDecisions = [
|
|
15679
|
-
...allUnifiedIncludeDecisions
|
|
15680
|
-
];
|
|
15681
|
-
stripJoinColumnsForAggregation(enrichedUnifiedDecisions, execIntent);
|
|
15682
|
-
applyJoinHydrationPrefixes(enrichedUnifiedDecisions);
|
|
15683
|
-
const includedRelations = new Set(
|
|
15684
|
-
enrichedUnifiedDecisions.filter((d) => d.type === "includeStrategy").map((d) => d.relationName).filter(Boolean)
|
|
15685
|
-
);
|
|
15686
|
-
if (includedRelations.size > 0) {
|
|
15687
|
-
const relationColumnsMap = buildRelationColumnsMap(
|
|
15688
|
-
decisions,
|
|
15689
|
-
includedRelations
|
|
15690
|
-
);
|
|
15691
|
-
injectAndValidateRelationColumns(
|
|
15692
|
-
enrichedUnifiedDecisions,
|
|
15693
|
-
relationColumnsMap,
|
|
15694
|
-
options?.model ?? deps.model
|
|
15695
|
-
);
|
|
16048
|
+
}
|
|
16049
|
+
const hasScalarSet = Object.keys(scalarSet).length > 0;
|
|
16050
|
+
const mergedFirstRow = hasScalarSet ? { ...firstRow, ...scalarSet } : firstRow;
|
|
16051
|
+
const columns = Object.keys(mergedFirstRow);
|
|
16052
|
+
const values = (intent.values ?? []).map((row) => {
|
|
16053
|
+
const mergedRow = hasScalarSet ? { ...row, ...scalarSet } : row;
|
|
16054
|
+
return columns.map((col) => mergedRow[col]);
|
|
16055
|
+
});
|
|
16056
|
+
const conflictTarget = {};
|
|
16057
|
+
if ("columns" in intent.onConflict) {
|
|
16058
|
+
conflictTarget.columns = [...intent.onConflict.columns];
|
|
16059
|
+
} else if ("constraint" in intent.onConflict) {
|
|
16060
|
+
conflictTarget.constraint = intent.onConflict.constraint;
|
|
16061
|
+
}
|
|
16062
|
+
const conflictAction = intent.action.type === "doNothing" ? "nothing" : "update";
|
|
16063
|
+
let updateColumns;
|
|
16064
|
+
if (intent.action.type === "doUpdate") {
|
|
16065
|
+
if (intent.action.set) {
|
|
16066
|
+
updateColumns = Object.keys(intent.action.set);
|
|
16067
|
+
} else {
|
|
16068
|
+
const conflictCols = "columns" in intent.onConflict ? intent.onConflict.columns : [];
|
|
16069
|
+
updateColumns = columns.filter((col) => !conflictCols.includes(col));
|
|
15696
16070
|
}
|
|
15697
|
-
const deduplicatedDecisions = includedRelations.size > 0 ? decisions.filter((d) => {
|
|
15698
|
-
if (d.type === "selectRelationColumn" && d.relation) {
|
|
15699
|
-
const rel = d.relation;
|
|
15700
|
-
const rootRelation = rel.split(".")[0] ?? rel;
|
|
15701
|
-
if (includedRelations.has(rootRelation)) {
|
|
15702
|
-
return false;
|
|
15703
|
-
}
|
|
15704
|
-
}
|
|
15705
|
-
return true;
|
|
15706
|
-
}) : decisions;
|
|
15707
|
-
const joinIntentDecisions = execIntent?.joins && execIntent.joins.length > 0 ? compileJoinIntents(
|
|
15708
|
-
execIntent.joins,
|
|
15709
|
-
plan.rootTable,
|
|
15710
|
-
schemaName,
|
|
15711
|
-
deps
|
|
15712
|
-
) : [];
|
|
15713
|
-
const allDecisions = [
|
|
15714
|
-
...deduplicatedDecisions,
|
|
15715
|
-
...enrichedUnifiedDecisions,
|
|
15716
|
-
...joinIntentDecisions
|
|
15717
|
-
];
|
|
15718
|
-
const rangeModel = options?.model ?? deps.model;
|
|
15719
|
-
enrichRangeDecisions(allDecisions, rangeModel, plan.rootTable);
|
|
15720
|
-
simplifiedPlan = buildSimplifiedPlanReport(
|
|
15721
|
-
planForCompilation,
|
|
15722
|
-
allDecisions,
|
|
15723
|
-
schemaName
|
|
15724
|
-
);
|
|
15725
|
-
} else {
|
|
15726
|
-
simplifiedPlan = {
|
|
15727
|
-
rootTable: plan.rootTable,
|
|
15728
|
-
decisions: bridgeLegacyDecisions(plan.decisions),
|
|
15729
|
-
...schemaName ? { schema: schemaName } : {}
|
|
15730
|
-
};
|
|
15731
16071
|
}
|
|
15732
|
-
const
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
16072
|
+
const columnTypes = getColumnTypes(intent.table, columns, deps);
|
|
16073
|
+
const hasRawExprs = Object.keys(rawExprs).length > 0;
|
|
16074
|
+
const actionWhere = intent.action.type === "doUpdate" && intent.action.where ? intent.action.where : void 0;
|
|
16075
|
+
const resolvedActionWhere = actionWhere ? resolveExistsIntent(actionWhere, intent.table, deps) : void 0;
|
|
16076
|
+
const config = {
|
|
16077
|
+
table: intent.table,
|
|
16078
|
+
columns,
|
|
16079
|
+
values,
|
|
16080
|
+
conflictTarget,
|
|
16081
|
+
conflictAction,
|
|
16082
|
+
...updateColumns && { updateColumns },
|
|
16083
|
+
...intent.returning && { returning: [...intent.returning] },
|
|
16084
|
+
...columnTypes && { columnTypes },
|
|
16085
|
+
...hasRawExprs && { updateExpressions: rawExprs },
|
|
16086
|
+
...resolvedActionWhere && {
|
|
16087
|
+
actionWhereIntent: resolvedActionWhere,
|
|
16088
|
+
compileActionWhere: (where, paramState) => compileUpsertActionWhere(
|
|
16089
|
+
where,
|
|
16090
|
+
intent.table,
|
|
16091
|
+
paramState,
|
|
16092
|
+
deps,
|
|
16093
|
+
schemaName
|
|
16094
|
+
)
|
|
16095
|
+
}
|
|
15736
16096
|
};
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
15742
|
-
if (d.type !== "include-strategy" || d.choice !== "subquery") continue;
|
|
15743
|
-
const ctx = d.context;
|
|
15744
|
-
if (!ctx.target) continue;
|
|
15745
|
-
const relationName = ctx.includeAlias ?? ctx.relation;
|
|
15746
|
-
if (!relationName) continue;
|
|
15747
|
-
const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
|
|
15748
|
-
const fk = Array.isArray(rawFk) ? rawFk[0] : rawFk;
|
|
15749
|
-
const isBelongsTo = ctx.relationType === "belongsTo";
|
|
15750
|
-
const sourceKey = isBelongsTo ? fk : "id";
|
|
15751
|
-
const targetFk = isBelongsTo ? "id" : fk;
|
|
15752
|
-
const includeIntent = plan.intent?.include?.find(
|
|
15753
|
-
(i) => i.relation === relationName || i.relation === ctx.includeAlias
|
|
16097
|
+
const maxBatchSize = options?.maxBatchSize;
|
|
16098
|
+
if (maxBatchSize !== void 0 && values.length > maxBatchSize) {
|
|
16099
|
+
throw new InvalidOperationError3(
|
|
16100
|
+
"upsert",
|
|
16101
|
+
`Batch size ${values.length} exceeds maxBatchSize ${maxBatchSize}`
|
|
15754
16102
|
);
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
|
|
15758
|
-
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
15764
|
-
|
|
15765
|
-
|
|
15766
|
-
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
16103
|
+
}
|
|
16104
|
+
const batchThreshold = options?.batchThreshold ?? 50;
|
|
16105
|
+
const useUnnest = values.length > 0 && (batchThreshold === 0 || values.length > batchThreshold);
|
|
16106
|
+
const ast = useUnnest ? compileUnnestUpsert(config, ctx, state) : compileUpsert(config, ctx, state);
|
|
16107
|
+
const sql = deparseQuoted(ast);
|
|
16108
|
+
return {
|
|
16109
|
+
sql,
|
|
16110
|
+
parameters: state.parameters
|
|
16111
|
+
};
|
|
16112
|
+
}
|
|
16113
|
+
function compileUpsertFrom2(intent, options, deps) {
|
|
16114
|
+
const schemaName = deps.schemaName;
|
|
16115
|
+
const sourceCte = intent.sourceQuery !== void 0 && !hasBindingName(deps.bindingNames, intent.source, deps.naming) ? compileSourceQueryCte(
|
|
16116
|
+
"compileUpsertFrom",
|
|
16117
|
+
intent.source,
|
|
16118
|
+
intent.sourceQuery,
|
|
16119
|
+
options,
|
|
16120
|
+
deps
|
|
16121
|
+
) : void 0;
|
|
16122
|
+
const bindingNames = intent.sourceQuery !== void 0 ? withBindingName(deps.bindingNames, intent.source, deps.naming) : deps.bindingNames;
|
|
16123
|
+
const ctx = {
|
|
16124
|
+
naming: deps.naming,
|
|
16125
|
+
rootTable: intent.source,
|
|
16126
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
16127
|
+
...deps.dialectCapabilities !== void 0 && {
|
|
16128
|
+
dialectCapabilities: deps.dialectCapabilities
|
|
16129
|
+
},
|
|
16130
|
+
...bindingNames !== void 0 && { bindingNames },
|
|
16131
|
+
maxRecursiveDepth: 100
|
|
16132
|
+
};
|
|
16133
|
+
const state = createCompilerState();
|
|
16134
|
+
const resolvedWhere = intent.where ? resolveExistsIntent(intent.where, intent.source, deps) : void 0;
|
|
16135
|
+
let columns;
|
|
16136
|
+
if (intent.columns) {
|
|
16137
|
+
columns = [...intent.columns];
|
|
16138
|
+
} else if (options?.model) {
|
|
16139
|
+
const targetTable = options.model.getTable(intent.table);
|
|
16140
|
+
if (targetTable) {
|
|
16141
|
+
columns = targetTable.columns.map((c) => c.name);
|
|
15770
16142
|
}
|
|
15771
|
-
subqueryIncludes.push(entry);
|
|
15772
16143
|
}
|
|
15773
|
-
|
|
16144
|
+
const config = {
|
|
16145
|
+
targetTable: intent.table,
|
|
16146
|
+
sourceTable: intent.source,
|
|
16147
|
+
conflictColumns: [...intent.conflictColumns],
|
|
16148
|
+
...columns && { columns },
|
|
16149
|
+
...resolvedWhere && { where: [whereIntentAsDecision(resolvedWhere)] },
|
|
16150
|
+
...intent.limit !== void 0 && { limit: intent.limit },
|
|
16151
|
+
...intent.returning && { returning: [...intent.returning] }
|
|
16152
|
+
};
|
|
16153
|
+
const ast = compileUpsertFrom(config, ctx, state);
|
|
16154
|
+
const sql = deparseQuoted(ast);
|
|
16155
|
+
return prependSourceCte(
|
|
16156
|
+
sql,
|
|
16157
|
+
state.parameters,
|
|
16158
|
+
intent.source,
|
|
16159
|
+
sourceCte,
|
|
16160
|
+
deps.naming
|
|
16161
|
+
);
|
|
15774
16162
|
}
|
|
15775
16163
|
|
|
15776
16164
|
// src/adapter-compiler-recursive.ts
|
|
@@ -16859,6 +17247,7 @@ function buildRecursiveAnchorWhere(where, tableAlias, deps, state) {
|
|
|
16859
17247
|
// src/pgsql-adapter.ts
|
|
16860
17248
|
init_assert_field();
|
|
16861
17249
|
init_ast_helpers();
|
|
17250
|
+
init_binding_registry();
|
|
16862
17251
|
|
|
16863
17252
|
// src/ddl/index-operations.ts
|
|
16864
17253
|
init_validate();
|
|
@@ -17025,9 +17414,9 @@ function compileLeafOrBranch(intent, compileFn) {
|
|
|
17025
17414
|
const result = compileFn(intent);
|
|
17026
17415
|
return { sql: result.sql, parameters: result.parameters };
|
|
17027
17416
|
}
|
|
17028
|
-
function createLeafCompileFn(adapter, model,
|
|
17417
|
+
function createLeafCompileFn(adapter, model, planFn3, options) {
|
|
17029
17418
|
return (query) => {
|
|
17030
|
-
const planReport =
|
|
17419
|
+
const planReport = planFn3(query, model, {
|
|
17031
17420
|
dialectCapabilities: adapter.dialectCapabilities
|
|
17032
17421
|
});
|
|
17033
17422
|
return adapter.compile(planReport, { ...options, model });
|
|
@@ -17149,14 +17538,257 @@ function mapFetchDirection(direction) {
|
|
|
17149
17538
|
|
|
17150
17539
|
// src/pgsql-adapter.ts
|
|
17151
17540
|
init_validate();
|
|
17152
|
-
|
|
17541
|
+
var MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS = 32e3;
|
|
17542
|
+
function renumberSqlParams2(sql, offset) {
|
|
17153
17543
|
if (offset === 0) return sql;
|
|
17154
17544
|
return sql.replace(/\$(\d+)/g, (_match, num) => {
|
|
17155
17545
|
return `$${Number.parseInt(num, 10) + offset}`;
|
|
17156
17546
|
});
|
|
17157
17547
|
}
|
|
17158
17548
|
function isCompiledNqlQuery(input) {
|
|
17159
|
-
return !("intent" in input) && ("query" in input || "cteQuery" in input || "mutation" in input || "setOperation" in input || "bindings" in input || "mutationBindings" in input);
|
|
17549
|
+
return !("intent" in input) && ("query" in input || "cteQuery" in input || "mutation" in input || "setOperation" in input || "bindings" in input || "mutationBindings" in input || "runtimeBindings" in input);
|
|
17550
|
+
}
|
|
17551
|
+
function formatGuardPathSegment(key) {
|
|
17552
|
+
return /^[A-Za-z_$][\w$]*$/.test(key) ? `.${key}` : `[${JSON.stringify(key)}]`;
|
|
17553
|
+
}
|
|
17554
|
+
function findNqlBindingRefMarker(value, path, seen = /* @__PURE__ */ new WeakSet()) {
|
|
17555
|
+
if (value === null || typeof value !== "object") {
|
|
17556
|
+
return void 0;
|
|
17557
|
+
}
|
|
17558
|
+
if (isNqlBindingRef2(value)) {
|
|
17559
|
+
return {
|
|
17560
|
+
ref: getNqlBindingRefName2(value),
|
|
17561
|
+
path
|
|
17562
|
+
};
|
|
17563
|
+
}
|
|
17564
|
+
if (seen.has(value)) {
|
|
17565
|
+
return void 0;
|
|
17566
|
+
}
|
|
17567
|
+
seen.add(value);
|
|
17568
|
+
if (Array.isArray(value)) {
|
|
17569
|
+
for (let i = 0; i < value.length; i++) {
|
|
17570
|
+
const found = findNqlBindingRefMarker(value[i], `${path}[${i}]`, seen);
|
|
17571
|
+
if (found) return found;
|
|
17572
|
+
}
|
|
17573
|
+
return void 0;
|
|
17574
|
+
}
|
|
17575
|
+
const record = value;
|
|
17576
|
+
for (const key of Object.keys(record)) {
|
|
17577
|
+
const found = findNqlBindingRefMarker(
|
|
17578
|
+
record[key],
|
|
17579
|
+
`${path}${formatGuardPathSegment(key)}`,
|
|
17580
|
+
seen
|
|
17581
|
+
);
|
|
17582
|
+
if (found) return found;
|
|
17583
|
+
}
|
|
17584
|
+
return void 0;
|
|
17585
|
+
}
|
|
17586
|
+
function guardCompiledQuery(query, context) {
|
|
17587
|
+
const found = findNqlBindingRefMarker(query.parameters, "parameters");
|
|
17588
|
+
if (found) {
|
|
17589
|
+
throw new Error(
|
|
17590
|
+
`NQL binding reference marker '${found.ref}' survived into emitted SQL parameters at ${found.path} while compiling ${context}. Binding references must resolve to CTE subqueries before SQL emission.`
|
|
17591
|
+
);
|
|
17592
|
+
}
|
|
17593
|
+
return query;
|
|
17594
|
+
}
|
|
17595
|
+
function guardCompileResultWithIncludes(result, context) {
|
|
17596
|
+
return {
|
|
17597
|
+
...result,
|
|
17598
|
+
main: guardCompiledQuery(result.main, context)
|
|
17599
|
+
};
|
|
17600
|
+
}
|
|
17601
|
+
function findPhysicalTableNameCollision(model, bindingName, naming) {
|
|
17602
|
+
for (const [modelTableName, table] of model.tables) {
|
|
17603
|
+
if (bindingName === table.name) return table.name;
|
|
17604
|
+
if (bindingName === modelTableName) return table.name;
|
|
17605
|
+
const emittedTableName = naming.toDatabase(table.name);
|
|
17606
|
+
if (bindingName === emittedTableName) return emittedTableName;
|
|
17607
|
+
const emittedModelTableName = naming.toDatabase(modelTableName);
|
|
17608
|
+
if (bindingName === emittedModelTableName) return emittedModelTableName;
|
|
17609
|
+
}
|
|
17610
|
+
return void 0;
|
|
17611
|
+
}
|
|
17612
|
+
function findDuplicateEmittedNqlBindingName(bindingNames, naming) {
|
|
17613
|
+
const seen = /* @__PURE__ */ new Map();
|
|
17614
|
+
for (const bindingName of bindingNames) {
|
|
17615
|
+
const emittedName = emittedBindName(bindingName, naming);
|
|
17616
|
+
const originalName = seen.get(emittedName);
|
|
17617
|
+
if (originalName !== void 0 && originalName !== bindingName) {
|
|
17618
|
+
return { originalName, duplicateName: bindingName, emittedName };
|
|
17619
|
+
}
|
|
17620
|
+
seen.set(emittedName, bindingName);
|
|
17621
|
+
}
|
|
17622
|
+
return void 0;
|
|
17623
|
+
}
|
|
17624
|
+
function orderedNqlBindingNames(bundle) {
|
|
17625
|
+
const names = [];
|
|
17626
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17627
|
+
for (const name of bundle.bindings?.keys() ?? []) {
|
|
17628
|
+
names.push(name);
|
|
17629
|
+
seen.add(name);
|
|
17630
|
+
}
|
|
17631
|
+
for (const name of bundle.runtimeBindings?.keys() ?? []) {
|
|
17632
|
+
if (!seen.has(name)) {
|
|
17633
|
+
names.push(name);
|
|
17634
|
+
seen.add(name);
|
|
17635
|
+
}
|
|
17636
|
+
}
|
|
17637
|
+
return names;
|
|
17638
|
+
}
|
|
17639
|
+
function mapRuntimeBindingColumnType(type) {
|
|
17640
|
+
switch (type) {
|
|
17641
|
+
case "string":
|
|
17642
|
+
return "text";
|
|
17643
|
+
case "text":
|
|
17644
|
+
return "text";
|
|
17645
|
+
case "number":
|
|
17646
|
+
case "integer":
|
|
17647
|
+
return "integer";
|
|
17648
|
+
case "bigint":
|
|
17649
|
+
return "bigint";
|
|
17650
|
+
case "decimal":
|
|
17651
|
+
return "numeric";
|
|
17652
|
+
case "boolean":
|
|
17653
|
+
return "boolean";
|
|
17654
|
+
case "date":
|
|
17655
|
+
return "date";
|
|
17656
|
+
case "time":
|
|
17657
|
+
return "time";
|
|
17658
|
+
case "datetime":
|
|
17659
|
+
case "timestamp":
|
|
17660
|
+
return "timestamptz";
|
|
17661
|
+
case "json":
|
|
17662
|
+
case "jsonb":
|
|
17663
|
+
return "jsonb";
|
|
17664
|
+
case "uuid":
|
|
17665
|
+
return "uuid";
|
|
17666
|
+
case "daterange":
|
|
17667
|
+
return "daterange";
|
|
17668
|
+
case "tsrange":
|
|
17669
|
+
return "tsrange";
|
|
17670
|
+
case "tstzrange":
|
|
17671
|
+
return "tstzrange";
|
|
17672
|
+
case "int4range":
|
|
17673
|
+
return "int4range";
|
|
17674
|
+
case "int8range":
|
|
17675
|
+
return "int8range";
|
|
17676
|
+
case "numrange":
|
|
17677
|
+
return "numrange";
|
|
17678
|
+
default:
|
|
17679
|
+
return void 0;
|
|
17680
|
+
}
|
|
17681
|
+
}
|
|
17682
|
+
function findRuntimeBindingSourceTable(model, sourceTable) {
|
|
17683
|
+
return model.getTable(sourceTable) ?? [...model.tables.values()].find((table) => table.name === sourceTable);
|
|
17684
|
+
}
|
|
17685
|
+
function resolveRuntimeBindingColumnType(bindingName, sourceTable, columnName) {
|
|
17686
|
+
const column = sourceTable.columns.find(
|
|
17687
|
+
(candidate) => candidate.name === columnName
|
|
17688
|
+
);
|
|
17689
|
+
if (column === void 0) {
|
|
17690
|
+
throw new Error(
|
|
17691
|
+
`NQL runtime binding '${bindingName}' cannot resolve projected column '${columnName}' on source mutation table '${sourceTable.name}'.`
|
|
17692
|
+
);
|
|
17693
|
+
}
|
|
17694
|
+
const dbType = column.originalDbType?.trim() || mapRuntimeBindingColumnType(column.type);
|
|
17695
|
+
if (dbType === void 0 || dbType.trim() === "") {
|
|
17696
|
+
throw new Error(
|
|
17697
|
+
`NQL runtime binding '${bindingName}' cannot resolve a PostgreSQL type for projected column '${columnName}' on source mutation table '${sourceTable.name}'.`
|
|
17698
|
+
);
|
|
17699
|
+
}
|
|
17700
|
+
const typeName = dbType.trim();
|
|
17701
|
+
try {
|
|
17702
|
+
validateTypeName4(typeName);
|
|
17703
|
+
} catch (error) {
|
|
17704
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
17705
|
+
throw new Error(
|
|
17706
|
+
`NQL runtime binding '${bindingName}' cannot use PostgreSQL cast type for projected column '${columnName}': ${reason}`
|
|
17707
|
+
);
|
|
17708
|
+
}
|
|
17709
|
+
return typeName;
|
|
17710
|
+
}
|
|
17711
|
+
function resolveRuntimeBindingColumnTypes(name, binding, model, sourceTableName) {
|
|
17712
|
+
if (model === void 0) {
|
|
17713
|
+
throw new Error(
|
|
17714
|
+
`NQL runtime binding '${name}' cannot materialize non-empty rows because no model is available for source-table column type resolution.`
|
|
17715
|
+
);
|
|
17716
|
+
}
|
|
17717
|
+
const sourceTable = findRuntimeBindingSourceTable(model, sourceTableName);
|
|
17718
|
+
if (sourceTable === void 0) {
|
|
17719
|
+
throw new Error(
|
|
17720
|
+
`NQL runtime binding '${name}' cannot resolve source mutation table '${sourceTableName}' in the model.`
|
|
17721
|
+
);
|
|
17722
|
+
}
|
|
17723
|
+
return binding.columns.map(
|
|
17724
|
+
(column) => resolveRuntimeBindingColumnType(name, sourceTable, column)
|
|
17725
|
+
);
|
|
17726
|
+
}
|
|
17727
|
+
function assertRuntimeBindingValuesParameterCount(name, binding, parameterOffset) {
|
|
17728
|
+
const valueParameterCount = binding.rows.length * binding.columns.length;
|
|
17729
|
+
if (valueParameterCount <= MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS && parameterOffset + valueParameterCount <= MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS) {
|
|
17730
|
+
return;
|
|
17731
|
+
}
|
|
17732
|
+
const totalParameterCount = parameterOffset + valueParameterCount;
|
|
17733
|
+
throw new Error(
|
|
17734
|
+
`NQL runtime binding '${name}' would materialize ${valueParameterCount} VALUES parameters; limit is ${MAX_NQL_RUNTIME_BINDING_VALUES_PARAMETERS}. Current parameter offset is ${parameterOffset}, which would make ${totalParameterCount} total parameters before compiling the final statement.`
|
|
17735
|
+
);
|
|
17736
|
+
}
|
|
17737
|
+
function compileNqlRuntimeBindingCte(name, binding, naming, parameterOffset, sourceTable, schemaName, model) {
|
|
17738
|
+
if (binding.columns.length === 0) {
|
|
17739
|
+
throw new Error(
|
|
17740
|
+
`NQL runtime binding '${name}' cannot be materialized without projected columns.`
|
|
17741
|
+
);
|
|
17742
|
+
}
|
|
17743
|
+
const cteName = quoteIdent2(emittedBindName(name, naming), "alias");
|
|
17744
|
+
const columnSql = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
|
|
17745
|
+
if (sourceTable === void 0) {
|
|
17746
|
+
throw new Error(
|
|
17747
|
+
`NQL runtime binding '${name}' cannot materialize a typed relation because its source mutation table is unavailable.`
|
|
17748
|
+
);
|
|
17749
|
+
}
|
|
17750
|
+
const projectedColumns = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
|
|
17751
|
+
const sourceAnchorSql = `SELECT ${projectedColumns} FROM ${qualifyTableIdent(sourceTable, schemaName, naming)} WHERE false`;
|
|
17752
|
+
if (binding.rows.length === 0) {
|
|
17753
|
+
return {
|
|
17754
|
+
cte: `${cteName} (${columnSql}) as (${sourceAnchorSql})`,
|
|
17755
|
+
parameters: []
|
|
17756
|
+
};
|
|
17757
|
+
}
|
|
17758
|
+
assertRuntimeBindingValuesParameterCount(name, binding, parameterOffset);
|
|
17759
|
+
const columnTypes = resolveRuntimeBindingColumnTypes(
|
|
17760
|
+
name,
|
|
17761
|
+
binding,
|
|
17762
|
+
model,
|
|
17763
|
+
sourceTable
|
|
17764
|
+
);
|
|
17765
|
+
const parameters = [];
|
|
17766
|
+
let nextParam = parameterOffset + 1;
|
|
17767
|
+
const valuesSql = binding.rows.map((row) => {
|
|
17768
|
+
const placeholders = binding.columns.map((column, columnIndex) => {
|
|
17769
|
+
parameters.push(row[column]);
|
|
17770
|
+
return `$${nextParam++}::${columnTypes[columnIndex]}`;
|
|
17771
|
+
});
|
|
17772
|
+
return `(${placeholders.join(", ")})`;
|
|
17773
|
+
}).join(", ");
|
|
17774
|
+
return {
|
|
17775
|
+
cte: `${cteName} (${columnSql}) as (${sourceAnchorSql} UNION ALL VALUES ${valuesSql})`,
|
|
17776
|
+
parameters
|
|
17777
|
+
};
|
|
17778
|
+
}
|
|
17779
|
+
function createNqlBindingSelectPlan(query) {
|
|
17780
|
+
return {
|
|
17781
|
+
rootTable: query.from,
|
|
17782
|
+
decisions: [],
|
|
17783
|
+
warnings: [],
|
|
17784
|
+
ctes: [],
|
|
17785
|
+
intent: query,
|
|
17786
|
+
metadata: {
|
|
17787
|
+
planningTimeMs: 0,
|
|
17788
|
+
relationsAnalyzed: 0,
|
|
17789
|
+
isAmbiguous: false
|
|
17790
|
+
}
|
|
17791
|
+
};
|
|
17160
17792
|
}
|
|
17161
17793
|
var PgsqlAdapter = class _PgsqlAdapter {
|
|
17162
17794
|
pool;
|
|
@@ -17224,17 +17856,20 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17224
17856
|
...overrides
|
|
17225
17857
|
};
|
|
17226
17858
|
}
|
|
17227
|
-
buildCompileDeps(options) {
|
|
17859
|
+
buildCompileDeps(options, bindingNames) {
|
|
17228
17860
|
if (options?.schemaName) {
|
|
17229
17861
|
validateIdentifier(options.schemaName, "schema");
|
|
17230
17862
|
}
|
|
17863
|
+
const naming = options?.naming ?? this.naming;
|
|
17231
17864
|
return {
|
|
17232
|
-
naming
|
|
17865
|
+
naming,
|
|
17233
17866
|
// `||` (not `??`): empty string is treated as "no override" and falls back to this.schemaName (which may be a configured schema or undefined)
|
|
17234
17867
|
schemaName: options?.schemaName || this.schemaName,
|
|
17235
17868
|
model: options?.model ?? this.model,
|
|
17869
|
+
dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities,
|
|
17236
17870
|
defaultPk: this.defaultPk,
|
|
17237
|
-
deriveFk: this.deriveFk
|
|
17871
|
+
deriveFk: this.deriveFk,
|
|
17872
|
+
...bindingNames !== void 0 && { bindingNames }
|
|
17238
17873
|
};
|
|
17239
17874
|
}
|
|
17240
17875
|
requireNqlCompileModel(options) {
|
|
@@ -17246,7 +17881,24 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17246
17881
|
}
|
|
17247
17882
|
return model;
|
|
17248
17883
|
}
|
|
17249
|
-
|
|
17884
|
+
assertNqlBindingNamesDisjointFromTables(bindingNames, options) {
|
|
17885
|
+
if (bindingNames === void 0 || bindingNames.size === 0) return;
|
|
17886
|
+
const model = this.requireNqlCompileModel(options);
|
|
17887
|
+
const naming = this.buildCompileDeps(options, bindingNames).naming;
|
|
17888
|
+
for (const bindingName of bindingNames) {
|
|
17889
|
+
const physicalTableName = findPhysicalTableNameCollision(
|
|
17890
|
+
model,
|
|
17891
|
+
bindingName,
|
|
17892
|
+
naming
|
|
17893
|
+
);
|
|
17894
|
+
if (physicalTableName !== void 0) {
|
|
17895
|
+
throw new Error(
|
|
17896
|
+
`NQL binding '${bindingName}' collides with physical table name '${physicalTableName}'. NQL binding names must be disjoint from model table names.`
|
|
17897
|
+
);
|
|
17898
|
+
}
|
|
17899
|
+
}
|
|
17900
|
+
}
|
|
17901
|
+
compileNqlMutation(bundle, options, bindingNames) {
|
|
17250
17902
|
const mutation = bundle.mutation;
|
|
17251
17903
|
if (mutation === void 0) {
|
|
17252
17904
|
throw new Error("NQL bundle did not contain a mutation intent.");
|
|
@@ -17256,84 +17908,140 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17256
17908
|
return compileInsert2(
|
|
17257
17909
|
mutation,
|
|
17258
17910
|
options,
|
|
17259
|
-
this.buildCompileDeps(options)
|
|
17911
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17260
17912
|
);
|
|
17261
17913
|
case "insert_from":
|
|
17262
17914
|
return compileInsertFrom2(
|
|
17263
17915
|
mutation,
|
|
17264
17916
|
options,
|
|
17265
|
-
this.buildCompileDeps(options)
|
|
17917
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17266
17918
|
);
|
|
17267
17919
|
case "update":
|
|
17268
17920
|
return compileUpdate2(
|
|
17269
17921
|
mutation,
|
|
17270
17922
|
options,
|
|
17271
|
-
this.buildCompileDeps(options)
|
|
17923
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17272
17924
|
);
|
|
17273
17925
|
case "delete":
|
|
17274
17926
|
return compileDelete2(
|
|
17275
17927
|
mutation,
|
|
17276
17928
|
options,
|
|
17277
|
-
this.buildCompileDeps(options)
|
|
17929
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17278
17930
|
);
|
|
17279
17931
|
case "upsert":
|
|
17280
17932
|
return compileUpsert2(
|
|
17281
17933
|
mutation,
|
|
17282
17934
|
options,
|
|
17283
|
-
this.buildCompileDeps(options)
|
|
17935
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17284
17936
|
);
|
|
17285
17937
|
case "upsert_from":
|
|
17286
17938
|
return compileUpsertFrom2(
|
|
17287
17939
|
mutation,
|
|
17288
17940
|
options,
|
|
17289
|
-
this.buildCompileDeps(options)
|
|
17941
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17290
17942
|
);
|
|
17291
17943
|
}
|
|
17292
17944
|
throw new Error(
|
|
17293
17945
|
`Unsupported NQL mutation type: ${mutation.type}`
|
|
17294
17946
|
);
|
|
17295
17947
|
}
|
|
17296
|
-
compileNqlBundleLeaf(bundle, options) {
|
|
17948
|
+
compileNqlBundleLeaf(bundle, options, bindingNames) {
|
|
17297
17949
|
if (bundle.query !== void 0) {
|
|
17298
|
-
const
|
|
17299
|
-
const
|
|
17300
|
-
|
|
17950
|
+
const deps = this.buildCompileDeps(options, bindingNames);
|
|
17951
|
+
const queryFromBinding = hasBindingName(
|
|
17952
|
+
bindingNames,
|
|
17953
|
+
bundle.query.from,
|
|
17954
|
+
deps.naming
|
|
17955
|
+
);
|
|
17956
|
+
const planReport = queryFromBinding ? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
17957
|
+
dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
|
|
17301
17958
|
});
|
|
17302
|
-
return
|
|
17303
|
-
planReport,
|
|
17304
|
-
|
|
17305
|
-
this.buildCompileDeps(options)
|
|
17959
|
+
return guardCompiledQuery(
|
|
17960
|
+
compileSelect(planReport, options, deps),
|
|
17961
|
+
"NQL query"
|
|
17306
17962
|
);
|
|
17307
17963
|
}
|
|
17308
17964
|
if (bundle.cteQuery !== void 0) {
|
|
17309
|
-
return
|
|
17310
|
-
|
|
17311
|
-
|
|
17312
|
-
|
|
17965
|
+
return guardCompiledQuery(
|
|
17966
|
+
compileCteQuery(
|
|
17967
|
+
bundle.cteQuery,
|
|
17968
|
+
options,
|
|
17969
|
+
this.buildCompileDeps(options, bindingNames)
|
|
17970
|
+
),
|
|
17971
|
+
"NQL CTE query"
|
|
17313
17972
|
);
|
|
17314
17973
|
}
|
|
17315
17974
|
if (bundle.setOperation !== void 0) {
|
|
17316
17975
|
const model = this.requireNqlCompileModel(options);
|
|
17317
|
-
return
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17976
|
+
return guardCompiledQuery(
|
|
17977
|
+
this.compileSetOperationWithBindings(
|
|
17978
|
+
bundle.setOperation,
|
|
17979
|
+
model,
|
|
17980
|
+
options,
|
|
17981
|
+
bindingNames
|
|
17982
|
+
),
|
|
17983
|
+
"NQL set operation"
|
|
17321
17984
|
);
|
|
17322
17985
|
}
|
|
17323
17986
|
if (bundle.mutation !== void 0) {
|
|
17324
|
-
return
|
|
17987
|
+
return guardCompiledQuery(
|
|
17988
|
+
this.compileNqlMutation(
|
|
17989
|
+
bundle,
|
|
17990
|
+
options,
|
|
17991
|
+
bindingNames
|
|
17992
|
+
),
|
|
17993
|
+
"NQL mutation"
|
|
17994
|
+
);
|
|
17325
17995
|
}
|
|
17326
17996
|
throw new Error("NQL bundle did not contain a compilable intent.");
|
|
17327
17997
|
}
|
|
17328
17998
|
compileNqlBundle(bundle, options) {
|
|
17329
17999
|
const ctes = [];
|
|
17330
18000
|
const parameters = [];
|
|
17331
|
-
|
|
17332
|
-
|
|
18001
|
+
const deps = this.buildCompileDeps(options);
|
|
18002
|
+
const { naming } = deps;
|
|
18003
|
+
const bindingNamesInOrder = orderedNqlBindingNames(bundle);
|
|
18004
|
+
const duplicateEmittedBinding = bindingNamesInOrder.length > 0 ? findDuplicateEmittedNqlBindingName(bindingNamesInOrder, naming) : void 0;
|
|
18005
|
+
if (duplicateEmittedBinding !== void 0) {
|
|
18006
|
+
throw new Error(
|
|
18007
|
+
`NQL bindings '${duplicateEmittedBinding.originalName}' and '${duplicateEmittedBinding.duplicateName}' emit to duplicate CTE name '${duplicateEmittedBinding.emittedName}'. NQL binding names must be unique after database naming.`
|
|
18008
|
+
);
|
|
18009
|
+
}
|
|
18010
|
+
const bindingNames = bindingNamesInOrder.length > 0 ? new Set(
|
|
18011
|
+
bindingNamesInOrder.map((name) => emittedBindName(name, naming))
|
|
18012
|
+
) : void 0;
|
|
18013
|
+
this.assertNqlBindingNamesDisjointFromTables(bindingNames, options);
|
|
18014
|
+
for (const name of bindingNamesInOrder) {
|
|
18015
|
+
const runtimeBinding = bundle.runtimeBindings?.get(name);
|
|
18016
|
+
if (runtimeBinding !== void 0) {
|
|
18017
|
+
const compiledRuntimeBinding = compileNqlRuntimeBindingCte(
|
|
18018
|
+
name,
|
|
18019
|
+
runtimeBinding,
|
|
18020
|
+
naming,
|
|
18021
|
+
parameters.length,
|
|
18022
|
+
bundle.mutationBindings?.get(name)?.table,
|
|
18023
|
+
deps.schemaName,
|
|
18024
|
+
deps.model
|
|
18025
|
+
);
|
|
18026
|
+
ctes.push(compiledRuntimeBinding.cte);
|
|
18027
|
+
parameters.push(...compiledRuntimeBinding.parameters);
|
|
18028
|
+
continue;
|
|
18029
|
+
}
|
|
18030
|
+
const queryIntent = bundle.bindings?.get(name);
|
|
18031
|
+
if (queryIntent === void 0) {
|
|
18032
|
+
throw new Error(
|
|
18033
|
+
`NQL binding '${name}' has no query intent or runtime rows to materialize.`
|
|
18034
|
+
);
|
|
18035
|
+
}
|
|
18036
|
+
const cteName = quoteIdent2(emittedBindName(name, naming), "alias");
|
|
17333
18037
|
const bindingBundle = bundle.mutationBindings?.has(name) ? { mutation: bundle.mutationBindings.get(name) } : { query: queryIntent };
|
|
17334
|
-
const compiled2 = this.compileNqlBundleLeaf(
|
|
18038
|
+
const compiled2 = this.compileNqlBundleLeaf(
|
|
18039
|
+
bindingBundle,
|
|
18040
|
+
options,
|
|
18041
|
+
bindingNames
|
|
18042
|
+
);
|
|
17335
18043
|
ctes.push(
|
|
17336
|
-
`${cteName} as (${
|
|
18044
|
+
`${cteName} as (${renumberSqlParams2(compiled2.sql, parameters.length)})`
|
|
17337
18045
|
);
|
|
17338
18046
|
parameters.push(...compiled2.parameters);
|
|
17339
18047
|
}
|
|
@@ -17346,14 +18054,21 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17346
18054
|
setOperation: bundle.setOperation
|
|
17347
18055
|
}
|
|
17348
18056
|
};
|
|
17349
|
-
const compiled = this.compileNqlBundleLeaf(
|
|
18057
|
+
const compiled = this.compileNqlBundleLeaf(
|
|
18058
|
+
leafBundle,
|
|
18059
|
+
options,
|
|
18060
|
+
bindingNames
|
|
18061
|
+
);
|
|
17350
18062
|
if (ctes.length === 0) {
|
|
17351
|
-
return compiled;
|
|
18063
|
+
return guardCompiledQuery(compiled, "NQL bundle");
|
|
17352
18064
|
}
|
|
17353
|
-
return
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
|
|
18065
|
+
return guardCompiledQuery(
|
|
18066
|
+
{
|
|
18067
|
+
sql: `WITH ${ctes.join(", ")} ${renumberSqlParams2(compiled.sql, parameters.length)}`,
|
|
18068
|
+
parameters: [...parameters, ...compiled.parameters]
|
|
18069
|
+
},
|
|
18070
|
+
"NQL bundle"
|
|
18071
|
+
);
|
|
17357
18072
|
}
|
|
17358
18073
|
/**
|
|
17359
18074
|
* Returns the pool/client executor, or throws if in compile-only mode.
|
|
@@ -17373,7 +18088,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17373
18088
|
}
|
|
17374
18089
|
/** PostgreSQL dialect capabilities for planner strategy selection */
|
|
17375
18090
|
get dialectCapabilities() {
|
|
17376
|
-
return
|
|
18091
|
+
return POSTGRESQL_CAPABILITIES2;
|
|
17377
18092
|
}
|
|
17378
18093
|
/**
|
|
17379
18094
|
* DB column casing convention used by this adapter.
|
|
@@ -17392,21 +18107,28 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17392
18107
|
// =========================================================================
|
|
17393
18108
|
/**
|
|
17394
18109
|
* Compile a plan to executable SQL.
|
|
18110
|
+
*
|
|
18111
|
+
* When passed a direct `CompiledNqlQuery`, the adapter trusts that the bundle
|
|
18112
|
+
* has already been semantically validated by the NQL compiler. This method
|
|
18113
|
+
* still performs adapter-owned SQL safety checks for emitted binding names
|
|
18114
|
+
* before CTE emission.
|
|
17395
18115
|
*/
|
|
17396
18116
|
compile(plan, options) {
|
|
17397
18117
|
if (isCompiledNqlQuery(plan)) {
|
|
17398
18118
|
return this.compileNqlBundle(plan, options);
|
|
17399
18119
|
}
|
|
17400
|
-
return
|
|
18120
|
+
return guardCompiledQuery(
|
|
18121
|
+
compileSelect(plan, options, this.buildCompileDeps(options)),
|
|
18122
|
+
"select plan"
|
|
18123
|
+
);
|
|
17401
18124
|
}
|
|
17402
18125
|
/**
|
|
17403
18126
|
* Compile a plan with includes, returning subquery include metadata (DX-033).
|
|
17404
18127
|
*/
|
|
17405
18128
|
compileWithIncludes(plan, options) {
|
|
17406
|
-
return
|
|
17407
|
-
plan,
|
|
17408
|
-
|
|
17409
|
-
this.buildCompileDeps(options)
|
|
18129
|
+
return guardCompileResultWithIncludes(
|
|
18130
|
+
compileWithIncludes(plan, options, this.buildCompileDeps(options)),
|
|
18131
|
+
"select plan with includes"
|
|
17410
18132
|
);
|
|
17411
18133
|
}
|
|
17412
18134
|
/**
|
|
@@ -17419,11 +18141,14 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17419
18141
|
* @returns Compiled query for fetching related records
|
|
17420
18142
|
*/
|
|
17421
18143
|
compileSubqueryInclude(info, parentIds, options) {
|
|
17422
|
-
return
|
|
17423
|
-
|
|
17424
|
-
|
|
17425
|
-
|
|
17426
|
-
|
|
18144
|
+
return guardCompiledQuery(
|
|
18145
|
+
compileSubqueryInclude(
|
|
18146
|
+
info,
|
|
18147
|
+
parentIds,
|
|
18148
|
+
options,
|
|
18149
|
+
this.buildCompileDeps(options)
|
|
18150
|
+
),
|
|
18151
|
+
"subquery include"
|
|
17427
18152
|
);
|
|
17428
18153
|
}
|
|
17429
18154
|
/**
|
|
@@ -17438,10 +18163,12 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17438
18163
|
compileSelectExpression(expr) {
|
|
17439
18164
|
const naming = this.naming;
|
|
17440
18165
|
const schemaName = this.schemaName;
|
|
18166
|
+
const dialectCapabilities = this.dialectCapabilities;
|
|
17441
18167
|
const state = createCompilerState();
|
|
17442
18168
|
const ctx = {
|
|
17443
18169
|
naming,
|
|
17444
18170
|
...schemaName !== void 0 && { schema: schemaName },
|
|
18171
|
+
dialectCapabilities,
|
|
17445
18172
|
rootTable: "",
|
|
17446
18173
|
maxRecursiveDepth: 100,
|
|
17447
18174
|
// Wire compileSubquery so that SubqueryExpressionIntent nested inside
|
|
@@ -17451,7 +18178,8 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17451
18178
|
compileSubquery(query, paramOffset) {
|
|
17452
18179
|
const innerCompiler = new PlanCompiler({
|
|
17453
18180
|
naming,
|
|
17454
|
-
...schemaName !== void 0 && { schema: schemaName }
|
|
18181
|
+
...schemaName !== void 0 && { schema: schemaName },
|
|
18182
|
+
dialectCapabilities
|
|
17455
18183
|
});
|
|
17456
18184
|
const innerPlan = {
|
|
17457
18185
|
rootTable: query.from,
|
|
@@ -17467,7 +18195,10 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17467
18195
|
targetList: [{ ResTarget: { val: node } }]
|
|
17468
18196
|
});
|
|
17469
18197
|
const sql = deparseQuoted(ast);
|
|
17470
|
-
return
|
|
18198
|
+
return guardCompiledQuery(
|
|
18199
|
+
{ sql, parameters: state.parameters },
|
|
18200
|
+
"select expression"
|
|
18201
|
+
);
|
|
17471
18202
|
}
|
|
17472
18203
|
/**
|
|
17473
18204
|
* Compile an insert intent to executable SQL.
|
|
@@ -17477,24 +18208,29 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17477
18208
|
* - rows > batchThreshold OR batchThreshold === 0: SELECT unnest($1::type[]),...
|
|
17478
18209
|
*/
|
|
17479
18210
|
compileInsert(intent, options) {
|
|
17480
|
-
return
|
|
18211
|
+
return guardCompiledQuery(
|
|
18212
|
+
compileInsert2(intent, options, this.buildCompileDeps(options)),
|
|
18213
|
+
"insert"
|
|
18214
|
+
);
|
|
17481
18215
|
}
|
|
17482
18216
|
/**
|
|
17483
18217
|
* Compile an insert-from intent to executable SQL (NQL-ALIGN).
|
|
17484
18218
|
* INSERT INTO target (cols) SELECT cols FROM source WHERE ... LIMIT ... RETURNING ...
|
|
17485
18219
|
*/
|
|
17486
18220
|
compileInsertFrom(intent, options) {
|
|
17487
|
-
return
|
|
17488
|
-
intent,
|
|
17489
|
-
|
|
17490
|
-
this.buildCompileDeps(options)
|
|
18221
|
+
return guardCompiledQuery(
|
|
18222
|
+
compileInsertFrom2(intent, options, this.buildCompileDeps(options)),
|
|
18223
|
+
"insert from"
|
|
17491
18224
|
);
|
|
17492
18225
|
}
|
|
17493
18226
|
/**
|
|
17494
18227
|
* Compile an update intent to executable SQL.
|
|
17495
18228
|
*/
|
|
17496
18229
|
compileUpdate(intent, options) {
|
|
17497
|
-
return
|
|
18230
|
+
return guardCompiledQuery(
|
|
18231
|
+
compileUpdate2(intent, options, this.buildCompileDeps(options)),
|
|
18232
|
+
"update"
|
|
18233
|
+
);
|
|
17498
18234
|
}
|
|
17499
18235
|
/**
|
|
17500
18236
|
* Compile a batch update intent to executable SQL using unnest FROM strategy (BATCH-001).
|
|
@@ -17506,33 +18242,37 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17506
18242
|
* [RETURNING ...]
|
|
17507
18243
|
*/
|
|
17508
18244
|
compileBatchUpdate(intent, options) {
|
|
17509
|
-
return
|
|
17510
|
-
intent,
|
|
17511
|
-
|
|
17512
|
-
this.buildCompileDeps(options)
|
|
18245
|
+
return guardCompiledQuery(
|
|
18246
|
+
compileBatchUpdate(intent, options, this.buildCompileDeps(options)),
|
|
18247
|
+
"batch update"
|
|
17513
18248
|
);
|
|
17514
18249
|
}
|
|
17515
18250
|
/**
|
|
17516
18251
|
* Compile a delete intent to executable SQL.
|
|
17517
18252
|
*/
|
|
17518
18253
|
compileDelete(intent, options) {
|
|
17519
|
-
return
|
|
18254
|
+
return guardCompiledQuery(
|
|
18255
|
+
compileDelete2(intent, options, this.buildCompileDeps(options)),
|
|
18256
|
+
"delete"
|
|
18257
|
+
);
|
|
17520
18258
|
}
|
|
17521
18259
|
/**
|
|
17522
18260
|
* Compile an upsert intent to executable SQL (DX-026).
|
|
17523
18261
|
*/
|
|
17524
18262
|
compileUpsert(intent, options) {
|
|
17525
|
-
return
|
|
18263
|
+
return guardCompiledQuery(
|
|
18264
|
+
compileUpsert2(intent, options, this.buildCompileDeps(options)),
|
|
18265
|
+
"upsert"
|
|
18266
|
+
);
|
|
17526
18267
|
}
|
|
17527
18268
|
/**
|
|
17528
18269
|
* Compile an upsert-from intent to executable SQL (NQL-BIND).
|
|
17529
18270
|
* INSERT INTO target SELECT ... FROM source ON CONFLICT (cols) DO UPDATE SET ...
|
|
17530
18271
|
*/
|
|
17531
18272
|
compileUpsertFrom(intent, options) {
|
|
17532
|
-
return
|
|
17533
|
-
intent,
|
|
17534
|
-
|
|
17535
|
-
this.buildCompileDeps(options)
|
|
18273
|
+
return guardCompiledQuery(
|
|
18274
|
+
compileUpsertFrom2(intent, options, this.buildCompileDeps(options)),
|
|
18275
|
+
"upsert from"
|
|
17536
18276
|
);
|
|
17537
18277
|
}
|
|
17538
18278
|
/**
|
|
@@ -17540,11 +18280,14 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17540
18280
|
* Supports adjacency-list and edge-table traversal modes.
|
|
17541
18281
|
*/
|
|
17542
18282
|
compileRecursive(report, model, options) {
|
|
17543
|
-
return
|
|
17544
|
-
|
|
17545
|
-
|
|
17546
|
-
|
|
17547
|
-
|
|
18283
|
+
return guardCompiledQuery(
|
|
18284
|
+
compileRecursive(
|
|
18285
|
+
report,
|
|
18286
|
+
model,
|
|
18287
|
+
options,
|
|
18288
|
+
this.buildCompileDeps(options)
|
|
18289
|
+
),
|
|
18290
|
+
"recursive query"
|
|
17548
18291
|
);
|
|
17549
18292
|
}
|
|
17550
18293
|
/**
|
|
@@ -17555,18 +18298,45 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17555
18298
|
* to start after CTE params and prepend WITH clause.
|
|
17556
18299
|
*/
|
|
17557
18300
|
compileCteQuery(intent, options) {
|
|
17558
|
-
return
|
|
18301
|
+
return guardCompiledQuery(
|
|
18302
|
+
compileCteQuery(intent, options, this.buildCompileDeps(options)),
|
|
18303
|
+
"CTE query"
|
|
18304
|
+
);
|
|
17559
18305
|
}
|
|
17560
18306
|
/**
|
|
17561
18307
|
* Compile a set operation (UNION / INTERSECT / EXCEPT) to SQL.
|
|
17562
18308
|
*/
|
|
17563
18309
|
compileSetOperation(intent, model, options) {
|
|
17564
|
-
|
|
17565
|
-
|
|
17566
|
-
|
|
17567
|
-
|
|
17568
|
-
|
|
18310
|
+
return guardCompiledQuery(
|
|
18311
|
+
this.compileSetOperationWithBindings(intent, model, options),
|
|
18312
|
+
"set operation"
|
|
18313
|
+
);
|
|
18314
|
+
}
|
|
18315
|
+
compileSetOperationWithBindings(intent, model, options, bindingNames) {
|
|
18316
|
+
const compileFn = (query) => {
|
|
18317
|
+
const leafOptions = {
|
|
18318
|
+
...options,
|
|
18319
|
+
model
|
|
18320
|
+
};
|
|
18321
|
+
const deps = this.buildCompileDeps(leafOptions, bindingNames);
|
|
18322
|
+
const queryFromBinding = hasBindingName(
|
|
18323
|
+
bindingNames,
|
|
18324
|
+
query.from,
|
|
18325
|
+
deps.naming
|
|
18326
|
+
);
|
|
18327
|
+
const planReport = queryFromBinding ? createNqlBindingSelectPlan(query) : planFn2(query, model, {
|
|
18328
|
+
dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
|
|
18329
|
+
});
|
|
18330
|
+
return compileSelect(planReport, leafOptions, deps);
|
|
17569
18331
|
};
|
|
18332
|
+
const result = compileSetOperation(intent, compileFn);
|
|
18333
|
+
return guardCompiledQuery(
|
|
18334
|
+
{
|
|
18335
|
+
sql: result.sql,
|
|
18336
|
+
parameters: result.parameters
|
|
18337
|
+
},
|
|
18338
|
+
"set operation"
|
|
18339
|
+
);
|
|
17570
18340
|
}
|
|
17571
18341
|
/**
|
|
17572
18342
|
* Create a dump for observability.
|