@dbsp/adapter-pgsql 1.5.0 → 1.6.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/dist/index.d.ts +19 -13
- package/dist/index.js +456 -186
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -588,12 +588,6 @@ function binaryExpr(operator, left, right, kind = "AEXPR_OP") {
|
|
|
588
588
|
function eqExpr(left, right) {
|
|
589
589
|
return binaryExpr("=", left, right);
|
|
590
590
|
}
|
|
591
|
-
function fkCorrelation(col1, alias1, col2, alias2, naming) {
|
|
592
|
-
return eqExpr(
|
|
593
|
-
columnRef(col1, alias1, void 0, naming),
|
|
594
|
-
columnRef(col2, alias2, void 0, naming)
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
591
|
function neExpr(left, right) {
|
|
598
592
|
return binaryExpr("<>", left, right);
|
|
599
593
|
}
|
|
@@ -885,10 +879,23 @@ function jsonAggSubquery(targetTable, whereExpr, alias, schemaName, naming = ide
|
|
|
885
879
|
}
|
|
886
880
|
};
|
|
887
881
|
}
|
|
882
|
+
const aggOrder = options?.orderBy && options.orderBy.length > 0 ? options.orderBy.map(
|
|
883
|
+
(col) => sortBy(
|
|
884
|
+
jsonAggOrderByExpression(
|
|
885
|
+
col,
|
|
886
|
+
targetAlias,
|
|
887
|
+
naming,
|
|
888
|
+
options.orderByFallback === true
|
|
889
|
+
),
|
|
890
|
+
"ASC",
|
|
891
|
+
"LAST"
|
|
892
|
+
)
|
|
893
|
+
) : void 0;
|
|
888
894
|
const jsonAggCall = {
|
|
889
895
|
FuncCall: {
|
|
890
896
|
funcname: [stringNode("json_agg")],
|
|
891
|
-
args: [toJsonbCall]
|
|
897
|
+
args: [toJsonbCall],
|
|
898
|
+
...aggOrder && { agg_order: aggOrder }
|
|
892
899
|
}
|
|
893
900
|
};
|
|
894
901
|
const fromTable = rangeVar(targetTable, targetAlias, schemaName, naming);
|
|
@@ -923,11 +930,9 @@ function jsonAggSubquery(targetTable, whereExpr, alias, schemaName, naming = ide
|
|
|
923
930
|
}
|
|
924
931
|
};
|
|
925
932
|
}
|
|
926
|
-
function
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
columnRef(parentColumn, parentAlias, void 0, naming)
|
|
930
|
-
);
|
|
933
|
+
function jsonAggOrderByExpression(entry, targetAlias, naming, castToText) {
|
|
934
|
+
const ref = columnRef(entry, targetAlias, void 0, naming);
|
|
935
|
+
return castToText ? typeCast(ref, "text") : ref;
|
|
931
936
|
}
|
|
932
937
|
var STRENGTH_MAP, POLICY_MAP;
|
|
933
938
|
var init_ast_helpers = __esm({
|
|
@@ -3172,6 +3177,7 @@ var init_custom_expression = __esm({
|
|
|
3172
3177
|
});
|
|
3173
3178
|
|
|
3174
3179
|
// src/handlers/where/exists.ts
|
|
3180
|
+
import { toColumnList } from "@dbsp/types";
|
|
3175
3181
|
function createSubLinkExists(subquery, negated) {
|
|
3176
3182
|
const subLink = {
|
|
3177
3183
|
subLinkType: "EXISTS_SUBLINK",
|
|
@@ -3188,10 +3194,27 @@ function createSubLinkExists(subquery, negated) {
|
|
|
3188
3194
|
}
|
|
3189
3195
|
return existsNode;
|
|
3190
3196
|
}
|
|
3191
|
-
function
|
|
3192
|
-
const
|
|
3193
|
-
const
|
|
3194
|
-
|
|
3197
|
+
function buildKeyCorrelation(sourceAlias, sourceCols, targetAlias, targetCols, ctx) {
|
|
3198
|
+
const normalizedSourceCols = toColumnList(sourceCols);
|
|
3199
|
+
const normalizedTargetCols = toColumnList(targetCols);
|
|
3200
|
+
if (normalizedSourceCols.length === 0 || normalizedTargetCols.length === 0 || normalizedSourceCols.length !== normalizedTargetCols.length || normalizedSourceCols.some((column) => column.length === 0) || normalizedTargetCols.some((column) => column.length === 0)) {
|
|
3201
|
+
throw new Error(
|
|
3202
|
+
`Invalid relation correlation: source columns (${normalizedSourceCols.length}) must match target columns (${normalizedTargetCols.length}) and both must be non-empty.`
|
|
3203
|
+
);
|
|
3204
|
+
}
|
|
3205
|
+
const comparisons = normalizedSourceCols.map(
|
|
3206
|
+
(sourceColumn, index) => eqExpr(
|
|
3207
|
+
columnRef(sourceColumn, sourceAlias, void 0, ctx.naming),
|
|
3208
|
+
columnRef(
|
|
3209
|
+
normalizedTargetCols[index],
|
|
3210
|
+
targetAlias,
|
|
3211
|
+
void 0,
|
|
3212
|
+
ctx.naming
|
|
3213
|
+
)
|
|
3214
|
+
)
|
|
3215
|
+
);
|
|
3216
|
+
if (comparisons.length === 1) return comparisons[0];
|
|
3217
|
+
return andExpr(...comparisons);
|
|
3195
3218
|
}
|
|
3196
3219
|
function schemaForExistsFromName(ctx, fromName) {
|
|
3197
3220
|
return schemaForFromName(ctx.schema, fromName, ctx.bindingNames, ctx.naming);
|
|
@@ -3199,11 +3222,15 @@ function schemaForExistsFromName(ctx, fromName) {
|
|
|
3199
3222
|
function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
3200
3223
|
const relation = decision.relation;
|
|
3201
3224
|
const targetTable = decision.targetTable ?? relation;
|
|
3202
|
-
const sourceColumn = decision.sourceColumn ??
|
|
3203
|
-
const targetColumn = decision.targetColumn ?? (ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3204
|
-
ctx.rootTable,
|
|
3225
|
+
const sourceColumn = decision.sourceColumn ?? [
|
|
3205
3226
|
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3206
|
-
|
|
3227
|
+
];
|
|
3228
|
+
const targetColumn = decision.targetColumn ?? [
|
|
3229
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3230
|
+
ctx.rootTable,
|
|
3231
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3232
|
+
)
|
|
3233
|
+
];
|
|
3207
3234
|
if (!targetTable) {
|
|
3208
3235
|
throw new Error("EXISTS handler requires targetTable or relation");
|
|
3209
3236
|
}
|
|
@@ -3211,7 +3238,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3211
3238
|
const targetAlias = `${targetTable}_exists_${existingAliases}`;
|
|
3212
3239
|
state.aliases.set(`exists_${targetTable}`, targetAlias);
|
|
3213
3240
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
3214
|
-
const correlation =
|
|
3241
|
+
const correlation = buildKeyCorrelation(
|
|
3215
3242
|
sourceAlias,
|
|
3216
3243
|
sourceColumn,
|
|
3217
3244
|
targetAlias,
|
|
@@ -3249,8 +3276,8 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3249
3276
|
const joinRelation = inc.relation;
|
|
3250
3277
|
if (!joinRelation) continue;
|
|
3251
3278
|
let joinTargetTable = joinRelation;
|
|
3252
|
-
let
|
|
3253
|
-
let
|
|
3279
|
+
let joinSourceCols;
|
|
3280
|
+
let joinTargetCols;
|
|
3254
3281
|
let sourceAliasForJoin = targetAlias;
|
|
3255
3282
|
const model = ctx.model;
|
|
3256
3283
|
if (model) {
|
|
@@ -3268,31 +3295,41 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3268
3295
|
if (rel) {
|
|
3269
3296
|
joinTargetTable = rel.target;
|
|
3270
3297
|
if (rel.type === "belongsTo") {
|
|
3271
|
-
const fk =
|
|
3272
|
-
|
|
3273
|
-
|
|
3298
|
+
const fk = toColumnList(rel.foreignKey);
|
|
3299
|
+
joinSourceCols = fk.length > 0 ? fk : void 0;
|
|
3300
|
+
const targetKey = toColumnList(rel.targetKey);
|
|
3301
|
+
joinTargetCols = targetKey.length > 0 ? targetKey : [
|
|
3302
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3303
|
+
];
|
|
3274
3304
|
} else {
|
|
3275
|
-
const fk =
|
|
3276
|
-
|
|
3277
|
-
|
|
3305
|
+
const fk = toColumnList(rel.foreignKey);
|
|
3306
|
+
const sourceKey = toColumnList(rel.sourceKey);
|
|
3307
|
+
joinSourceCols = sourceKey.length > 0 ? sourceKey : [
|
|
3308
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3309
|
+
];
|
|
3310
|
+
joinTargetCols = fk.length > 0 ? fk : void 0;
|
|
3278
3311
|
}
|
|
3279
3312
|
}
|
|
3280
3313
|
}
|
|
3281
|
-
if (!
|
|
3282
|
-
|
|
3283
|
-
|
|
3314
|
+
if (!joinSourceCols || joinSourceCols.length === 0) {
|
|
3315
|
+
joinSourceCols = [
|
|
3316
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3317
|
+
joinRelation,
|
|
3318
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3319
|
+
)
|
|
3320
|
+
];
|
|
3321
|
+
joinTargetCols = [
|
|
3284
3322
|
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3285
|
-
|
|
3286
|
-
joinTargetCol = ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
|
|
3323
|
+
];
|
|
3287
3324
|
}
|
|
3288
3325
|
const joinAlias = joinRelation;
|
|
3289
|
-
const joinQuals =
|
|
3290
|
-
joinSourceCol,
|
|
3326
|
+
const joinQuals = buildKeyCorrelation(
|
|
3291
3327
|
sourceAliasForJoin,
|
|
3292
3328
|
// resolved source alias (root or intermediate)
|
|
3293
|
-
|
|
3329
|
+
joinSourceCols,
|
|
3294
3330
|
joinAlias,
|
|
3295
|
-
|
|
3331
|
+
joinTargetCols,
|
|
3332
|
+
ctx
|
|
3296
3333
|
);
|
|
3297
3334
|
const joinType = inc.joinType === "left" ? "JOIN_LEFT" : "JOIN_INNER";
|
|
3298
3335
|
const joinRangeVar = rangeVar(
|
|
@@ -3774,6 +3811,7 @@ var init_range = __esm({
|
|
|
3774
3811
|
});
|
|
3775
3812
|
|
|
3776
3813
|
// src/compile-where.ts
|
|
3814
|
+
import { toColumnList as toColumnList2 } from "@dbsp/types";
|
|
3777
3815
|
import { getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields3 } from "@dbsp/types/internal";
|
|
3778
3816
|
function toHandlerContext(ctx) {
|
|
3779
3817
|
return {
|
|
@@ -4039,14 +4077,16 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
4039
4077
|
let singleHopTargetColumn;
|
|
4040
4078
|
if (resolvedRelation) {
|
|
4041
4079
|
const rel = resolvedRelation;
|
|
4042
|
-
const fk =
|
|
4080
|
+
const fk = toColumnList2(rel.foreignKey);
|
|
4043
4081
|
const defaultPk = DEFAULT_PK_COLUMN;
|
|
4044
4082
|
if (rel.type === "belongsTo") {
|
|
4045
|
-
singleHopSourceColumn = fk
|
|
4046
|
-
|
|
4083
|
+
singleHopSourceColumn = fk.length > 0 ? fk : [defaultFkDerivation(rel.target, defaultPk)];
|
|
4084
|
+
const targetKey = toColumnList2(rel.targetKey);
|
|
4085
|
+
singleHopTargetColumn = targetKey.length > 0 ? targetKey : [defaultPk];
|
|
4047
4086
|
} else {
|
|
4048
|
-
|
|
4049
|
-
|
|
4087
|
+
const sourceKey = toColumnList2(rel.sourceKey);
|
|
4088
|
+
singleHopSourceColumn = sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
4089
|
+
singleHopTargetColumn = fk.length > 0 ? fk : [defaultFkDerivation(ctx.rootTable, defaultPk)];
|
|
4050
4090
|
}
|
|
4051
4091
|
}
|
|
4052
4092
|
return compileWhereIntent(
|
|
@@ -4083,15 +4123,19 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
4083
4123
|
);
|
|
4084
4124
|
}
|
|
4085
4125
|
hopTargets.push(rel.target);
|
|
4086
|
-
const fk =
|
|
4126
|
+
const fk = toColumnList2(rel.foreignKey);
|
|
4087
4127
|
const defaultPk = DEFAULT_PK_COLUMN;
|
|
4088
4128
|
if (rel.type === "belongsTo") {
|
|
4089
|
-
hopSourceColumns.push(
|
|
4090
|
-
|
|
4129
|
+
hopSourceColumns.push(
|
|
4130
|
+
fk.length > 0 ? fk : [defaultFkDerivation(rel.target, defaultPk)]
|
|
4131
|
+
);
|
|
4132
|
+
const targetKey = toColumnList2(rel.targetKey);
|
|
4133
|
+
hopTargetColumns.push(targetKey.length > 0 ? targetKey : [defaultPk]);
|
|
4091
4134
|
} else {
|
|
4092
|
-
|
|
4135
|
+
const sourceKey = toColumnList2(rel.sourceKey);
|
|
4136
|
+
hopSourceColumns.push(sourceKey.length > 0 ? sourceKey : [defaultPk]);
|
|
4093
4137
|
hopTargetColumns.push(
|
|
4094
|
-
fk
|
|
4138
|
+
fk.length > 0 ? fk : [defaultFkDerivation(currentSource, defaultPk)]
|
|
4095
4139
|
);
|
|
4096
4140
|
}
|
|
4097
4141
|
currentSource = rel.target;
|
|
@@ -4319,6 +4363,7 @@ var init_raw_exists = __esm({
|
|
|
4319
4363
|
});
|
|
4320
4364
|
|
|
4321
4365
|
// src/handlers/where/relation-filter.ts
|
|
4366
|
+
import { toColumnList as toColumnList3 } from "@dbsp/types";
|
|
4322
4367
|
function getFilterMode(decision) {
|
|
4323
4368
|
const operator = decision.operator;
|
|
4324
4369
|
if (operator === "some" || operator === "exists") return "some";
|
|
@@ -4330,15 +4375,18 @@ function getFilterMode(decision) {
|
|
|
4330
4375
|
function buildJoinFilter(decision, ctx, state, dispatch) {
|
|
4331
4376
|
const relation = decision.relation;
|
|
4332
4377
|
const targetTable = decision.targetTable ?? relation;
|
|
4333
|
-
const sourceColumn =
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
ctx.
|
|
4341
|
-
|
|
4378
|
+
const sourceColumn = toColumnList3(decision.sourceColumn);
|
|
4379
|
+
if (sourceColumn.length === 0) {
|
|
4380
|
+
throw new Error(
|
|
4381
|
+
"Missing required column 'sourceColumn' in relation filter"
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
const targetColumn = decision.targetColumn ?? [
|
|
4385
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
4386
|
+
ctx.rootTable,
|
|
4387
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
4388
|
+
)
|
|
4389
|
+
];
|
|
4342
4390
|
if (!targetTable) {
|
|
4343
4391
|
throw new Error("Relation filter requires targetTable");
|
|
4344
4392
|
}
|
|
@@ -4346,9 +4394,12 @@ function buildJoinFilter(decision, ctx, state, dispatch) {
|
|
|
4346
4394
|
const targetAlias = `${targetTable}_rel_${existingAliases}`;
|
|
4347
4395
|
state.aliases.set(`rel_${targetTable}`, targetAlias);
|
|
4348
4396
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
4349
|
-
const joinCondition =
|
|
4350
|
-
|
|
4351
|
-
|
|
4397
|
+
const joinCondition = buildKeyCorrelation(
|
|
4398
|
+
sourceAlias,
|
|
4399
|
+
sourceColumn,
|
|
4400
|
+
targetAlias,
|
|
4401
|
+
targetColumn,
|
|
4402
|
+
ctx
|
|
4352
4403
|
);
|
|
4353
4404
|
const joinExpr2 = {
|
|
4354
4405
|
jointype: "JOIN_INNER",
|
|
@@ -4383,6 +4434,7 @@ var init_relation_filter = __esm({
|
|
|
4383
4434
|
"use strict";
|
|
4384
4435
|
init_assert_field();
|
|
4385
4436
|
init_ast_helpers();
|
|
4437
|
+
init_exists();
|
|
4386
4438
|
relationFilterHandler = {
|
|
4387
4439
|
operators: ["relationFilter", "relation", "is", "isNot"],
|
|
4388
4440
|
compile(decision, ctx, state, dispatch) {
|
|
@@ -4812,6 +4864,7 @@ var init_where = __esm({
|
|
|
4812
4864
|
});
|
|
4813
4865
|
|
|
4814
4866
|
// src/handlers/include/cte.ts
|
|
4867
|
+
import { toColumnList as toColumnList4 } from "@dbsp/types";
|
|
4815
4868
|
function buildCteTargets(columns, alias, ctx) {
|
|
4816
4869
|
if (columns && columns.length > 0 && !columns.every((c) => c === "*")) {
|
|
4817
4870
|
return columns.filter((col) => col !== "*").map((col) => ({
|
|
@@ -4862,12 +4915,12 @@ function buildCTE(cteName, cteSelect, ctx) {
|
|
|
4862
4915
|
return { CommonTableExpr: cte };
|
|
4863
4916
|
}
|
|
4864
4917
|
function buildCteJoin(cteName, cteAlias, sourceAlias, sourceColumn, targetColumn, ctx) {
|
|
4865
|
-
const joinCondition =
|
|
4866
|
-
sourceColumn,
|
|
4918
|
+
const joinCondition = buildKeyCorrelation(
|
|
4867
4919
|
sourceAlias,
|
|
4868
|
-
|
|
4920
|
+
sourceColumn,
|
|
4869
4921
|
cteAlias,
|
|
4870
|
-
|
|
4922
|
+
targetColumn,
|
|
4923
|
+
ctx
|
|
4871
4924
|
);
|
|
4872
4925
|
const cteRef = {
|
|
4873
4926
|
RangeVar: {
|
|
@@ -4891,20 +4944,22 @@ var init_cte = __esm({
|
|
|
4891
4944
|
init_assert_field();
|
|
4892
4945
|
init_ast_helpers();
|
|
4893
4946
|
init_handlers();
|
|
4947
|
+
init_exists();
|
|
4894
4948
|
cteIncludeHandler = {
|
|
4895
4949
|
strategy: "cte",
|
|
4896
4950
|
compile(decision, ctx, state) {
|
|
4897
4951
|
const relation = decision.relation;
|
|
4898
4952
|
const targetTable = decision.targetTable ?? relation;
|
|
4899
|
-
const sourceColumn =
|
|
4900
|
-
|
|
4901
|
-
"sourceColumn"
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4953
|
+
const sourceColumn = toColumnList4(decision.sourceColumn);
|
|
4954
|
+
if (sourceColumn.length === 0 || sourceColumn.some((col) => col === "")) {
|
|
4955
|
+
throw new Error("Missing required column 'sourceColumn' in CTE include");
|
|
4956
|
+
}
|
|
4957
|
+
const targetColumn = decision.targetColumn ?? [
|
|
4958
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
4959
|
+
ctx.rootTable,
|
|
4960
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
4961
|
+
)
|
|
4962
|
+
];
|
|
4908
4963
|
const columns = decision.columns;
|
|
4909
4964
|
const conditions = decision.conditions;
|
|
4910
4965
|
if (!targetTable) {
|
|
@@ -4947,13 +5002,14 @@ var init_cte = __esm({
|
|
|
4947
5002
|
});
|
|
4948
5003
|
|
|
4949
5004
|
// src/handlers/include/join.ts
|
|
5005
|
+
import { toColumnList as toColumnList5 } from "@dbsp/types";
|
|
4950
5006
|
function buildJoin(targetTable, targetAlias, sourceAlias, sourceColumn, targetColumn, ctx, joinType = "left") {
|
|
4951
|
-
const joinCondition =
|
|
4952
|
-
sourceColumn,
|
|
5007
|
+
const joinCondition = buildKeyCorrelation(
|
|
4953
5008
|
sourceAlias,
|
|
4954
|
-
|
|
5009
|
+
sourceColumn,
|
|
4955
5010
|
targetAlias,
|
|
4956
|
-
|
|
5011
|
+
targetColumn,
|
|
5012
|
+
ctx
|
|
4957
5013
|
);
|
|
4958
5014
|
const joinExpr2 = {
|
|
4959
5015
|
jointype: joinType === "inner" ? "JOIN_INNER" : "JOIN_LEFT",
|
|
@@ -4968,6 +5024,7 @@ var init_join = __esm({
|
|
|
4968
5024
|
"use strict";
|
|
4969
5025
|
init_assert_field();
|
|
4970
5026
|
init_ast_helpers();
|
|
5027
|
+
init_exists();
|
|
4971
5028
|
joinIncludeHandler = {
|
|
4972
5029
|
strategy: "join",
|
|
4973
5030
|
compile(decision, ctx, _state) {
|
|
@@ -4978,15 +5035,16 @@ var init_join = __esm({
|
|
|
4978
5035
|
}
|
|
4979
5036
|
const targetAlias = relation ?? targetTable;
|
|
4980
5037
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
4981
|
-
const sourceColumn =
|
|
4982
|
-
|
|
4983
|
-
"sourceColumn"
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
5038
|
+
const sourceColumn = toColumnList5(decision.sourceColumn);
|
|
5039
|
+
if (sourceColumn.length === 0) {
|
|
5040
|
+
throw new Error("Missing required column 'sourceColumn' in JOIN include");
|
|
5041
|
+
}
|
|
5042
|
+
const targetColumn = decision.targetColumn ?? [
|
|
5043
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
5044
|
+
ctx.rootTable,
|
|
5045
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
5046
|
+
)
|
|
5047
|
+
];
|
|
4990
5048
|
const join = buildJoin(
|
|
4991
5049
|
targetTable,
|
|
4992
5050
|
targetAlias,
|
|
@@ -5040,6 +5098,22 @@ var init_shared = __esm({
|
|
|
5040
5098
|
});
|
|
5041
5099
|
|
|
5042
5100
|
// src/handlers/include/json-agg.ts
|
|
5101
|
+
import { toColumnList as toColumnList6 } from "@dbsp/types";
|
|
5102
|
+
function resolveJsonAggOrderBy(decision, targetTable, ctx) {
|
|
5103
|
+
const table = ctx.model?.getTable(targetTable);
|
|
5104
|
+
if (table) {
|
|
5105
|
+
const pkColumns = toColumnList6(table.primaryKey);
|
|
5106
|
+
const orderIntent = pkColumns.length > 0 ? { columns: pkColumns, fallback: false } : {
|
|
5107
|
+
columns: table.columns.map((col) => col.name),
|
|
5108
|
+
fallback: true
|
|
5109
|
+
};
|
|
5110
|
+
return orderIntent.columns.length > 0 ? orderIntent : void 0;
|
|
5111
|
+
}
|
|
5112
|
+
return decision.targetPrimaryKey && decision.targetPrimaryKey.length > 0 ? {
|
|
5113
|
+
columns: decision.targetPrimaryKey,
|
|
5114
|
+
fallback: decision.orderByFallback === true
|
|
5115
|
+
} : void 0;
|
|
5116
|
+
}
|
|
5043
5117
|
function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
5044
5118
|
const innerAlias = depth === 0 ? "__t__" : `__t${depth}__`;
|
|
5045
5119
|
const relation = decision.relation ?? decision.relationName;
|
|
@@ -5056,12 +5130,12 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5056
5130
|
ctx.defaultPkColumnName,
|
|
5057
5131
|
ctx.deriveFkColumnName
|
|
5058
5132
|
);
|
|
5059
|
-
let whereExpr =
|
|
5060
|
-
parentAlias,
|
|
5061
|
-
sourceColumn,
|
|
5133
|
+
let whereExpr = buildKeyCorrelation(
|
|
5062
5134
|
innerAlias,
|
|
5063
5135
|
targetColumn,
|
|
5064
|
-
|
|
5136
|
+
parentAlias,
|
|
5137
|
+
sourceColumn,
|
|
5138
|
+
ctx
|
|
5065
5139
|
);
|
|
5066
5140
|
const compiledFilter = decision._compiledFilterWhere;
|
|
5067
5141
|
if (compiledFilter) {
|
|
@@ -5092,6 +5166,7 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5092
5166
|
if (childNodes.length === 0) childNodes = void 0;
|
|
5093
5167
|
}
|
|
5094
5168
|
const limit = typeof decision.limit === "number" ? decision.limit : void 0;
|
|
5169
|
+
const orderBy = resolveJsonAggOrderBy(decision, targetTable, ctx);
|
|
5095
5170
|
return jsonAggSubquery(
|
|
5096
5171
|
targetTable,
|
|
5097
5172
|
whereExpr,
|
|
@@ -5102,7 +5177,9 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5102
5177
|
...childNodes && { childNodes },
|
|
5103
5178
|
innerAlias,
|
|
5104
5179
|
...limit !== void 0 && { limit },
|
|
5105
|
-
...decision.columns && { columns: decision.columns }
|
|
5180
|
+
...decision.columns && { columns: decision.columns },
|
|
5181
|
+
...orderBy && { orderBy: orderBy.columns },
|
|
5182
|
+
...orderBy?.fallback && { orderByFallback: true }
|
|
5106
5183
|
}
|
|
5107
5184
|
);
|
|
5108
5185
|
}
|
|
@@ -5111,6 +5188,7 @@ var init_json_agg = __esm({
|
|
|
5111
5188
|
"src/handlers/include/json-agg.ts"() {
|
|
5112
5189
|
"use strict";
|
|
5113
5190
|
init_ast_helpers();
|
|
5191
|
+
init_exists();
|
|
5114
5192
|
init_shared();
|
|
5115
5193
|
jsonAggIncludeHandler = {
|
|
5116
5194
|
strategy: "json_agg",
|
|
@@ -5132,6 +5210,7 @@ var init_json_agg = __esm({
|
|
|
5132
5210
|
});
|
|
5133
5211
|
|
|
5134
5212
|
// src/handlers/include/lateral.ts
|
|
5213
|
+
import { toColumnList as toColumnList7 } from "@dbsp/types";
|
|
5135
5214
|
function buildLateralTargets(columns, alias, ctx) {
|
|
5136
5215
|
if (columns && columns.length > 0 && !(columns.length === 1 && columns[0] === "*")) {
|
|
5137
5216
|
return columns.map((col) => ({
|
|
@@ -5143,12 +5222,12 @@ function buildLateralTargets(columns, alias, ctx) {
|
|
|
5143
5222
|
return [starTarget(alias, ctx.naming)];
|
|
5144
5223
|
}
|
|
5145
5224
|
function buildLateralSubquery(targetTable, innerAlias, outerAlias, sourceColumn, targetColumn, columns, limit, ctx) {
|
|
5146
|
-
const whereClause =
|
|
5147
|
-
targetColumn,
|
|
5225
|
+
const whereClause = buildKeyCorrelation(
|
|
5148
5226
|
innerAlias,
|
|
5149
|
-
|
|
5227
|
+
targetColumn,
|
|
5150
5228
|
outerAlias,
|
|
5151
|
-
|
|
5229
|
+
sourceColumn,
|
|
5230
|
+
ctx
|
|
5152
5231
|
);
|
|
5153
5232
|
const targetList = buildLateralTargets(columns, innerAlias, ctx);
|
|
5154
5233
|
const stmt = {
|
|
@@ -5228,19 +5307,23 @@ var init_lateral = __esm({
|
|
|
5228
5307
|
"use strict";
|
|
5229
5308
|
init_assert_field();
|
|
5230
5309
|
init_ast_helpers();
|
|
5310
|
+
init_exists();
|
|
5231
5311
|
init_shared();
|
|
5232
5312
|
lateralIncludeHandler = {
|
|
5233
5313
|
strategy: "lateral",
|
|
5234
5314
|
compile(decision, ctx, state) {
|
|
5235
|
-
const sourceColumn =
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
ctx.
|
|
5243
|
-
|
|
5315
|
+
const sourceColumn = toColumnList7(decision.sourceColumn);
|
|
5316
|
+
if (sourceColumn.length === 0) {
|
|
5317
|
+
throw new Error(
|
|
5318
|
+
"Missing required column 'sourceColumn' in lateral include"
|
|
5319
|
+
);
|
|
5320
|
+
}
|
|
5321
|
+
const targetColumn = decision.targetColumn ?? [
|
|
5322
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
5323
|
+
ctx.rootTable,
|
|
5324
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
5325
|
+
)
|
|
5326
|
+
];
|
|
5244
5327
|
const outerAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
5245
5328
|
const { joins, targets } = compileLateralCascade(
|
|
5246
5329
|
decision,
|
|
@@ -7229,7 +7312,8 @@ import { InvalidOperationError as InvalidOperationError2 } from "@dbsp/core";
|
|
|
7229
7312
|
import {
|
|
7230
7313
|
isParamIntent as isParamIntent6,
|
|
7231
7314
|
NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
|
|
7232
|
-
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
|
|
7315
|
+
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST,
|
|
7316
|
+
toColumnList as toColumnList8
|
|
7233
7317
|
} from "@dbsp/types";
|
|
7234
7318
|
import {
|
|
7235
7319
|
getNqlBindingRefName,
|
|
@@ -8299,6 +8383,7 @@ init_window();
|
|
|
8299
8383
|
init_include();
|
|
8300
8384
|
init_shared();
|
|
8301
8385
|
init_handlers();
|
|
8386
|
+
init_exists();
|
|
8302
8387
|
init_types();
|
|
8303
8388
|
init_utils();
|
|
8304
8389
|
init_intent_to_decisions();
|
|
@@ -8336,7 +8421,21 @@ function assertNqlSelectWindowFunctionAllowed(functionName) {
|
|
|
8336
8421
|
throw new UnsupportedNqlSelectFunctionError(functionName);
|
|
8337
8422
|
}
|
|
8338
8423
|
}
|
|
8424
|
+
function trustedRelationHasMultipleHops(relation) {
|
|
8425
|
+
if (typeof relation !== "string") return relation.length > 1;
|
|
8426
|
+
return relation.split(".").length > 1;
|
|
8427
|
+
}
|
|
8428
|
+
function isJsonAggOrderBy(orderBy) {
|
|
8429
|
+
return Array.isArray(orderBy) && orderBy.every((item) => typeof item === "string");
|
|
8430
|
+
}
|
|
8431
|
+
function isExpressionOrderBy(orderBy) {
|
|
8432
|
+
return Array.isArray(orderBy) && orderBy.every(
|
|
8433
|
+
(item) => typeof item === "object" && item !== null && "field" in item && typeof item.field === "string"
|
|
8434
|
+
);
|
|
8435
|
+
}
|
|
8339
8436
|
function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
8437
|
+
const jsonAggOrderBy = isJsonAggOrderBy(pd.orderBy) ? pd.orderBy : void 0;
|
|
8438
|
+
const expressionOrderBy = isExpressionOrderBy(pd.orderBy) ? pd.orderBy : void 0;
|
|
8340
8439
|
const derivedFkColumns = deriveFkColumns(
|
|
8341
8440
|
pd,
|
|
8342
8441
|
pd.sourceTable ?? rootTable,
|
|
@@ -8371,6 +8470,8 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8371
8470
|
relationType: pd.relationType,
|
|
8372
8471
|
foreignKey: pd.foreignKey,
|
|
8373
8472
|
parentKey: pd.parentKey,
|
|
8473
|
+
targetPrimaryKey: pd.targetPrimaryKey ?? jsonAggOrderBy,
|
|
8474
|
+
orderByFallback: pd.orderByFallback,
|
|
8374
8475
|
dataType: pd.dataType,
|
|
8375
8476
|
traversal: pd.traversal,
|
|
8376
8477
|
pkColumn: pd.pkColumn,
|
|
@@ -8385,7 +8486,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8385
8486
|
include: pd.include?.map(
|
|
8386
8487
|
(c) => mapToHandlerDecision(c, rootTable, defaultPk, deriveFk)
|
|
8387
8488
|
),
|
|
8388
|
-
orderBy:
|
|
8489
|
+
orderBy: expressionOrderBy?.map((o) => ({
|
|
8389
8490
|
column: o.field,
|
|
8390
8491
|
direction: o.direction?.toUpperCase() ?? "ASC"
|
|
8391
8492
|
})),
|
|
@@ -8767,6 +8868,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8767
8868
|
this.defaultPk,
|
|
8768
8869
|
this.deriveFk
|
|
8769
8870
|
);
|
|
8871
|
+
if (handlerDecision.strategy === "json_agg") {
|
|
8872
|
+
assertDialectCapability(
|
|
8873
|
+
this.dialectCapabilities,
|
|
8874
|
+
"supportsJsonAgg",
|
|
8875
|
+
"JSON aggregation for relation includes"
|
|
8876
|
+
);
|
|
8877
|
+
}
|
|
8770
8878
|
const handler = getIncludeHandler(
|
|
8771
8879
|
handlerDecision.strategy
|
|
8772
8880
|
);
|
|
@@ -8940,17 +9048,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8940
9048
|
isNqlBindingRoot(plan) {
|
|
8941
9049
|
return hasBindingName(this.bindingNames, plan.rootTable, this.naming);
|
|
8942
9050
|
}
|
|
8943
|
-
|
|
8944
|
-
if (fields.selectedColumn === void 0) {
|
|
8945
|
-
throw new Error(
|
|
8946
|
-
`NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
|
|
8947
|
-
);
|
|
8948
|
-
}
|
|
8949
|
-
if (fields.cardinality !== "one") {
|
|
8950
|
-
throw new Error(
|
|
8951
|
-
`NQL binding relation-column proof for '${plan.rootTable}' is not scalar (cardinality: ${fields.cardinality ?? "unknown"}).`
|
|
8952
|
-
);
|
|
8953
|
-
}
|
|
9051
|
+
buildCorrelatedRelationRefs(fields, plan) {
|
|
8954
9052
|
const relatedAlias = `rc_${this.bindingRelationColumnSubqueryIndex++}`;
|
|
8955
9053
|
const relatedTable = rangeVar(
|
|
8956
9054
|
fields.targetTable,
|
|
@@ -8964,29 +9062,145 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8964
9062
|
void 0,
|
|
8965
9063
|
this.naming
|
|
8966
9064
|
);
|
|
8967
|
-
const relatedJoinColumn = columnRef(
|
|
8968
|
-
fields.targetColumn,
|
|
8969
|
-
relatedAlias,
|
|
8970
|
-
void 0,
|
|
8971
|
-
this.naming
|
|
8972
|
-
);
|
|
8973
|
-
const bindingJoinColumn = columnRef(
|
|
8974
|
-
fields.sourceColumn,
|
|
8975
|
-
plan.rootTable,
|
|
8976
|
-
void 0,
|
|
8977
|
-
this.naming
|
|
8978
|
-
);
|
|
8979
9065
|
return {
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
targetList: [{ ResTarget: { val: relatedColumn } }],
|
|
8984
|
-
from: [relatedTable],
|
|
8985
|
-
where: eqExpr(relatedJoinColumn, bindingJoinColumn)
|
|
8986
|
-
})
|
|
8987
|
-
}
|
|
9066
|
+
relatedAlias,
|
|
9067
|
+
relatedTable,
|
|
9068
|
+
relatedColumn
|
|
8988
9069
|
};
|
|
8989
9070
|
}
|
|
9071
|
+
compileBindingRelationColumnSubquery(fields, plan, dialectCapabilities) {
|
|
9072
|
+
if (fields.selectedColumn === void 0) {
|
|
9073
|
+
throw new Error(
|
|
9074
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
|
|
9075
|
+
);
|
|
9076
|
+
}
|
|
9077
|
+
if (fields.cardinality === "one") {
|
|
9078
|
+
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9079
|
+
if (fields.hops.length === 0 && trustedRelationHasMultipleHops(fields.relation)) {
|
|
9080
|
+
throw new Error(
|
|
9081
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is dotted but missing resolved hops.`
|
|
9082
|
+
);
|
|
9083
|
+
}
|
|
9084
|
+
if (fields.hops.length > 0) {
|
|
9085
|
+
let fromNode = relatedTable;
|
|
9086
|
+
let previousAlias = relatedAlias;
|
|
9087
|
+
for (let i = 0; i < fields.hops.length; i++) {
|
|
9088
|
+
const hop = fields.hops[i];
|
|
9089
|
+
const hopAlias = `${relatedAlias}_h${i + 1}`;
|
|
9090
|
+
const hopTable = rangeVar(
|
|
9091
|
+
hop.target,
|
|
9092
|
+
hopAlias,
|
|
9093
|
+
this.schemaForRangeVar(plan, hop.target),
|
|
9094
|
+
this.naming
|
|
9095
|
+
);
|
|
9096
|
+
fromNode = innerJoin(
|
|
9097
|
+
fromNode,
|
|
9098
|
+
hopTable,
|
|
9099
|
+
buildKeyCorrelation(
|
|
9100
|
+
hopAlias,
|
|
9101
|
+
hop.joinColumn,
|
|
9102
|
+
previousAlias,
|
|
9103
|
+
hop.fkColumn,
|
|
9104
|
+
this.createHandlerContext(plan)
|
|
9105
|
+
)
|
|
9106
|
+
);
|
|
9107
|
+
previousAlias = hopAlias;
|
|
9108
|
+
}
|
|
9109
|
+
return {
|
|
9110
|
+
SubLink: {
|
|
9111
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9112
|
+
subselect: selectStmt({
|
|
9113
|
+
targetList: [
|
|
9114
|
+
{
|
|
9115
|
+
ResTarget: {
|
|
9116
|
+
val: columnRef(
|
|
9117
|
+
fields.selectedColumn,
|
|
9118
|
+
previousAlias,
|
|
9119
|
+
void 0,
|
|
9120
|
+
this.naming
|
|
9121
|
+
)
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
],
|
|
9125
|
+
from: [fromNode],
|
|
9126
|
+
where: buildKeyCorrelation(
|
|
9127
|
+
relatedAlias,
|
|
9128
|
+
fields.targetColumn,
|
|
9129
|
+
plan.rootTable,
|
|
9130
|
+
fields.sourceColumn,
|
|
9131
|
+
this.createHandlerContext(plan)
|
|
9132
|
+
)
|
|
9133
|
+
})
|
|
9134
|
+
}
|
|
9135
|
+
};
|
|
9136
|
+
}
|
|
9137
|
+
return {
|
|
9138
|
+
SubLink: {
|
|
9139
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9140
|
+
subselect: selectStmt({
|
|
9141
|
+
targetList: [{ ResTarget: { val: relatedColumn } }],
|
|
9142
|
+
from: [relatedTable],
|
|
9143
|
+
where: buildKeyCorrelation(
|
|
9144
|
+
relatedAlias,
|
|
9145
|
+
fields.targetColumn,
|
|
9146
|
+
plan.rootTable,
|
|
9147
|
+
fields.sourceColumn,
|
|
9148
|
+
this.createHandlerContext(plan)
|
|
9149
|
+
)
|
|
9150
|
+
})
|
|
9151
|
+
}
|
|
9152
|
+
};
|
|
9153
|
+
}
|
|
9154
|
+
if (fields.cardinality === "many") {
|
|
9155
|
+
if (fields.relationType !== "hasMany") {
|
|
9156
|
+
throw new Error(
|
|
9157
|
+
`NQL binding relation-column proof for '${plan.rootTable}' has cardinality 'many' but relationType '${fields.relationType ?? "unknown"}' is not supported; only hasMany can be aggregated (ref-#192).`
|
|
9158
|
+
);
|
|
9159
|
+
}
|
|
9160
|
+
if (dialectCapabilities?.supportsJsonAgg === false) {
|
|
9161
|
+
throw new Error(
|
|
9162
|
+
"JSON aggregation for NQL binding relation columns is not supported by this adapter"
|
|
9163
|
+
);
|
|
9164
|
+
}
|
|
9165
|
+
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9166
|
+
return {
|
|
9167
|
+
SubLink: {
|
|
9168
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9169
|
+
subselect: selectStmt({
|
|
9170
|
+
targetList: [
|
|
9171
|
+
{
|
|
9172
|
+
ResTarget: {
|
|
9173
|
+
val: coalesceExpr([
|
|
9174
|
+
funcCall("json_agg", [relatedColumn], {
|
|
9175
|
+
orderBy: [
|
|
9176
|
+
sortBy(
|
|
9177
|
+
typeCast(relatedColumn, "text"),
|
|
9178
|
+
"DEFAULT",
|
|
9179
|
+
"LAST"
|
|
9180
|
+
)
|
|
9181
|
+
]
|
|
9182
|
+
}),
|
|
9183
|
+
typeCast({ A_Const: { sval: { sval: "[]" } } }, "json")
|
|
9184
|
+
])
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
],
|
|
9188
|
+
from: [relatedTable],
|
|
9189
|
+
where: buildKeyCorrelation(
|
|
9190
|
+
relatedAlias,
|
|
9191
|
+
fields.targetColumn,
|
|
9192
|
+
plan.rootTable,
|
|
9193
|
+
fields.sourceColumn,
|
|
9194
|
+
this.createHandlerContext(plan)
|
|
9195
|
+
)
|
|
9196
|
+
})
|
|
9197
|
+
}
|
|
9198
|
+
};
|
|
9199
|
+
}
|
|
9200
|
+
throw new Error(
|
|
9201
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is not scalar (cardinality: ${fields.cardinality ?? "unknown"}).`
|
|
9202
|
+
);
|
|
9203
|
+
}
|
|
8990
9204
|
compileExpressionSubquery(query, paramOffset) {
|
|
8991
9205
|
const innerCompiler = new _PlanCompiler(this.childCompilerOptions());
|
|
8992
9206
|
const innerPlan = {
|
|
@@ -9369,7 +9583,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9369
9583
|
if (trusted?.selectedColumn !== void 0) {
|
|
9370
9584
|
const node2 = this.compileBindingRelationColumnSubquery(
|
|
9371
9585
|
trusted,
|
|
9372
|
-
plan
|
|
9586
|
+
plan,
|
|
9587
|
+
this.dialectCapabilities
|
|
9373
9588
|
);
|
|
9374
9589
|
targetList.push({
|
|
9375
9590
|
ResTarget: {
|
|
@@ -10106,12 +10321,22 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10106
10321
|
registerJoinFilter(decision) {
|
|
10107
10322
|
const targetTable = decision.targetTable;
|
|
10108
10323
|
const sourceTable = this.currentRootTable;
|
|
10109
|
-
const fkColumn = decision.foreignKey ?? this.deriveFk(targetTable, this.defaultPk);
|
|
10110
|
-
const onCondition = eqExpr(
|
|
10111
|
-
columnRef(this.defaultPk, targetTable, void 0, this.naming),
|
|
10112
|
-
columnRef(fkColumn, sourceTable, void 0, this.naming)
|
|
10113
|
-
);
|
|
10114
10324
|
const alias = targetTable === sourceTable ? decision.relationName ?? `${targetTable}_join` : void 0;
|
|
10325
|
+
const targetAlias = alias ?? targetTable;
|
|
10326
|
+
const fkColumn = decision.foreignKey ?? [
|
|
10327
|
+
this.deriveFk(targetTable, this.defaultPk)
|
|
10328
|
+
];
|
|
10329
|
+
const targetKey = decision.parentKey ?? [this.defaultPk];
|
|
10330
|
+
const onCondition = buildKeyCorrelation(
|
|
10331
|
+
targetAlias,
|
|
10332
|
+
targetKey,
|
|
10333
|
+
sourceTable,
|
|
10334
|
+
fkColumn,
|
|
10335
|
+
this.createHandlerContext({
|
|
10336
|
+
rootTable: sourceTable,
|
|
10337
|
+
decisions: []
|
|
10338
|
+
})
|
|
10339
|
+
);
|
|
10115
10340
|
this.pendingJoins.push({
|
|
10116
10341
|
type: "JOIN",
|
|
10117
10342
|
table: targetTable,
|
|
@@ -10132,19 +10357,21 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10132
10357
|
this.schemaForRangeVar(plan, decision.targetTable ?? ""),
|
|
10133
10358
|
this.naming
|
|
10134
10359
|
);
|
|
10135
|
-
const
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
)
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10360
|
+
const sourceColumn = toColumnList8(decision.sourceColumn);
|
|
10361
|
+
const targetColumn = toColumnList8(decision.targetColumn);
|
|
10362
|
+
if (sourceColumn.length === 0) {
|
|
10363
|
+
throw new Error("Missing required column 'sourceColumn' in compileJoin");
|
|
10364
|
+
}
|
|
10365
|
+
if (targetColumn.length === 0) {
|
|
10366
|
+
throw new Error("Missing required column 'targetColumn' in compileJoin");
|
|
10367
|
+
}
|
|
10368
|
+
const sourceAlias = sourceColumn.length > 1 ? plan.rootTable : "";
|
|
10369
|
+
const onCondition = buildKeyCorrelation(
|
|
10370
|
+
sourceAlias,
|
|
10371
|
+
sourceColumn,
|
|
10372
|
+
decision.alias ?? decision.targetTable ?? "",
|
|
10373
|
+
targetColumn,
|
|
10374
|
+
this.createHandlerContext(plan)
|
|
10148
10375
|
);
|
|
10149
10376
|
if (decision.joinType === "left") {
|
|
10150
10377
|
return leftJoin(baseTable, targetTable, onCondition, decision.alias);
|
|
@@ -13262,6 +13489,8 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13262
13489
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
13263
13490
|
const belongsToName = deriveRelationName(fk.cols[0], fk.target);
|
|
13264
13491
|
const fkCol = fk.cols.length === 1 ? fk.cols[0] : fk.cols;
|
|
13492
|
+
const refCol = fk.refs.length === 1 ? fk.refs[0] : fk.refs;
|
|
13493
|
+
const keyOverride = Array.isArray(refCol) || refCol !== DEFAULT_PK_COLUMN ? refCol : void 0;
|
|
13265
13494
|
const belongsToKey = `${fk.source}.${belongsToName}`;
|
|
13266
13495
|
if (!relations.has(belongsToKey)) {
|
|
13267
13496
|
relations.set(belongsToKey, {
|
|
@@ -13270,6 +13499,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13270
13499
|
source: fk.source,
|
|
13271
13500
|
target: fk.target,
|
|
13272
13501
|
foreignKey: fkCol,
|
|
13502
|
+
...keyOverride !== void 0 && { targetKey: keyOverride },
|
|
13273
13503
|
cardinality: "one",
|
|
13274
13504
|
optionality: "optional",
|
|
13275
13505
|
includeStrategy: "auto",
|
|
@@ -13286,6 +13516,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13286
13516
|
source: fk.target,
|
|
13287
13517
|
target: fk.source,
|
|
13288
13518
|
foreignKey: fkCol,
|
|
13519
|
+
...keyOverride !== void 0 && { sourceKey: keyOverride },
|
|
13289
13520
|
cardinality: "many",
|
|
13290
13521
|
optionality: "optional",
|
|
13291
13522
|
includeStrategy: "auto",
|
|
@@ -14188,6 +14419,7 @@ import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNql
|
|
|
14188
14419
|
|
|
14189
14420
|
// src/adapter-compiler-includes.ts
|
|
14190
14421
|
init_ast_helpers();
|
|
14422
|
+
import { toColumnList as toColumnList9 } from "@dbsp/types";
|
|
14191
14423
|
init_handlers();
|
|
14192
14424
|
function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
14193
14425
|
const schemaName = deps.schemaName;
|
|
@@ -14199,7 +14431,12 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14199
14431
|
parameters: []
|
|
14200
14432
|
};
|
|
14201
14433
|
}
|
|
14202
|
-
const fkColumns =
|
|
14434
|
+
const fkColumns = toColumnList9(info.foreignKey);
|
|
14435
|
+
if (fkColumns.length === 0) {
|
|
14436
|
+
throw new Error(
|
|
14437
|
+
"Subquery include requires at least one foreignKey column."
|
|
14438
|
+
);
|
|
14439
|
+
}
|
|
14203
14440
|
if (info.through && info.throughSourceKey && info.throughTargetKey) {
|
|
14204
14441
|
return compileSubqueryIncludeManyToMany(
|
|
14205
14442
|
info,
|
|
@@ -14247,6 +14484,11 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14247
14484
|
};
|
|
14248
14485
|
} else {
|
|
14249
14486
|
const conditions = parentIds.map((id) => {
|
|
14487
|
+
if (!Array.isArray(id) || id.length !== fkColumns.length) {
|
|
14488
|
+
throw new Error(
|
|
14489
|
+
`Subquery include composite key parameter width (${Array.isArray(id) ? id.length : 1}) must match foreignKey width (${fkColumns.length}).`
|
|
14490
|
+
);
|
|
14491
|
+
}
|
|
14250
14492
|
const idValues = id;
|
|
14251
14493
|
const colConditions = fkColumns.map((col, idx) => {
|
|
14252
14494
|
state.parameters.push(idValues[idx]);
|
|
@@ -14287,7 +14529,13 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
|
|
|
14287
14529
|
const throughTable = info.through;
|
|
14288
14530
|
const throughSourceKey = info.throughSourceKey;
|
|
14289
14531
|
const throughTargetKey = info.throughTargetKey;
|
|
14290
|
-
const
|
|
14532
|
+
const targetPkColumns = toColumnList9(info.sourceKey);
|
|
14533
|
+
if (targetPkColumns.length !== 1) {
|
|
14534
|
+
throw new Error(
|
|
14535
|
+
`Many-to-many subquery include requires a single-column target key; got ${JSON.stringify(targetPkColumns)}.`
|
|
14536
|
+
);
|
|
14537
|
+
}
|
|
14538
|
+
const targetPk = targetPkColumns[0];
|
|
14291
14539
|
const paramRefs = parentIds.map((id) => {
|
|
14292
14540
|
state.parameters.push(id);
|
|
14293
14541
|
state.paramIndex++;
|
|
@@ -14376,6 +14624,7 @@ import {
|
|
|
14376
14624
|
POSTGRESQL_CAPABILITIES,
|
|
14377
14625
|
plan as planFn
|
|
14378
14626
|
} from "@dbsp/core";
|
|
14627
|
+
import { toColumnList as toColumnList12 } from "@dbsp/types";
|
|
14379
14628
|
|
|
14380
14629
|
// src/adapter-compiler-select.ts
|
|
14381
14630
|
init_assert_field();
|
|
@@ -14383,6 +14632,7 @@ init_ast_helpers();
|
|
|
14383
14632
|
init_binding_registry();
|
|
14384
14633
|
init_compile_where();
|
|
14385
14634
|
import { countDistinctRelationPathsByName } from "@dbsp/core";
|
|
14635
|
+
import { toColumnList as toColumnList11 } from "@dbsp/types";
|
|
14386
14636
|
init_compiler_utils();
|
|
14387
14637
|
init_types();
|
|
14388
14638
|
init_intent_to_decisions();
|
|
@@ -14392,6 +14642,7 @@ init_param_ref();
|
|
|
14392
14642
|
init_assert_field();
|
|
14393
14643
|
init_intent_to_decisions();
|
|
14394
14644
|
import { deriveRelationPathFromIntentPath } from "@dbsp/core";
|
|
14645
|
+
import { toColumnList as toColumnList10 } from "@dbsp/types";
|
|
14395
14646
|
function findExistsIntents(where) {
|
|
14396
14647
|
if (!where || typeof where !== "object") return [];
|
|
14397
14648
|
const w = where;
|
|
@@ -14412,7 +14663,8 @@ function findExistsIntents(where) {
|
|
|
14412
14663
|
function resolveRelation(model, sourceTable, relationName) {
|
|
14413
14664
|
const rel = model.getRelation(`${sourceTable}.${relationName}`);
|
|
14414
14665
|
if (!rel) return void 0;
|
|
14415
|
-
const
|
|
14666
|
+
const foreignKeyColumns = toColumnList10(rel.foreignKey);
|
|
14667
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14416
14668
|
const relationType = rel.type;
|
|
14417
14669
|
return { target: rel.target, foreignKey, relationType };
|
|
14418
14670
|
}
|
|
@@ -14635,7 +14887,8 @@ function buildMultiHopExistsChain(hops, rootTable, outerOperator, innerCondition
|
|
|
14635
14887
|
const hopRelations = [];
|
|
14636
14888
|
for (const hop of hops) {
|
|
14637
14889
|
const rel = model.getRelation(`${currentSource}.${hop}`);
|
|
14638
|
-
const
|
|
14890
|
+
const foreignKeyColumns = rel ? toColumnList10(rel.foreignKey) : [];
|
|
14891
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14639
14892
|
const relationType = rel?.type === "belongsTo" ? "belongsTo" : rel?.type === "hasMany" || rel?.type === "hasOne" ? rel.type : void 0;
|
|
14640
14893
|
const parentKey = relationType === "belongsTo" ? rel?.targetKey : rel?.sourceKey;
|
|
14641
14894
|
const target = rel ? rel.target : hop;
|
|
@@ -14745,7 +14998,8 @@ function enrichExistsStubsInConditions(conditions, sourceTable, model) {
|
|
|
14745
14998
|
`exists('${relation}'): no relation '${relation}' is declared on table '${sourceTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
14746
14999
|
);
|
|
14747
15000
|
}
|
|
14748
|
-
const
|
|
15001
|
+
const foreignKeyColumns = toColumnList10(rel.foreignKey);
|
|
15002
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14749
15003
|
const relationType = rel.type === "belongsTo" ? "belongsTo" : rel.type === "hasMany" || rel.type === "hasOne" ? rel.type : void 0;
|
|
14750
15004
|
const parentKey = relationType === "belongsTo" ? rel.targetKey : rel.sourceKey;
|
|
14751
15005
|
const targetTable = rel.target;
|
|
@@ -14804,7 +15058,8 @@ function buildEnrichedExistsDecision(d, matchingIntent, rootTable, model) {
|
|
|
14804
15058
|
const relIR = model && context.relation ? model.getRelation(
|
|
14805
15059
|
`${sourceTableForRelation}.${context.relation}`
|
|
14806
15060
|
) : void 0;
|
|
14807
|
-
const
|
|
15061
|
+
const foreignKeyColumns = relIR ? toColumnList10(relIR.foreignKey) : [];
|
|
15062
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14808
15063
|
const relationType = relIR?.type === "belongsTo" ? "belongsTo" : relIR?.type === "hasMany" || relIR?.type === "hasOne" ? relIR.type : void 0;
|
|
14809
15064
|
const parentKey = relationType === "belongsTo" ? relIR?.targetKey : relIR?.sourceKey;
|
|
14810
15065
|
let operator = "exists";
|
|
@@ -15117,6 +15372,7 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
15117
15372
|
const relationName = resolveIncludeAlias(context);
|
|
15118
15373
|
if (!context.target || !relationName) return void 0;
|
|
15119
15374
|
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15375
|
+
const parentKey = context.parentKey ?? defaultPk;
|
|
15120
15376
|
const relationType = context.relationType;
|
|
15121
15377
|
const effectiveChoice = choice === "subquery" ? "json_agg" : choice;
|
|
15122
15378
|
const includeIntent = resolveIncludeByPath(
|
|
@@ -15137,8 +15393,13 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
15137
15393
|
targetTable: context.target,
|
|
15138
15394
|
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15139
15395
|
...relationType && { relationType },
|
|
15140
|
-
foreignKey
|
|
15141
|
-
parentKey
|
|
15396
|
+
foreignKey,
|
|
15397
|
+
parentKey,
|
|
15398
|
+
...context.targetPrimaryKey && context.targetPrimaryKey.length > 0 ? {
|
|
15399
|
+
targetPrimaryKey: context.targetPrimaryKey,
|
|
15400
|
+
orderBy: context.targetPrimaryKey
|
|
15401
|
+
} : {},
|
|
15402
|
+
...context.orderByFallback ? { orderByFallback: true } : {},
|
|
15142
15403
|
...context.intentPath && { intentPath: context.intentPath },
|
|
15143
15404
|
...limit != null && { limit }
|
|
15144
15405
|
};
|
|
@@ -15164,6 +15425,7 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
|
|
|
15164
15425
|
columns = [defaultPk, ...fields];
|
|
15165
15426
|
}
|
|
15166
15427
|
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15428
|
+
const parentKey = context.parentKey ?? defaultPk;
|
|
15167
15429
|
const relationType = context.relationType;
|
|
15168
15430
|
let conditions;
|
|
15169
15431
|
if (includeIntent?.where) {
|
|
@@ -15184,8 +15446,8 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
|
|
|
15184
15446
|
targetTable: context.target,
|
|
15185
15447
|
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15186
15448
|
...relationType && { relationType },
|
|
15187
|
-
foreignKey
|
|
15188
|
-
parentKey
|
|
15449
|
+
foreignKey,
|
|
15450
|
+
parentKey,
|
|
15189
15451
|
columns,
|
|
15190
15452
|
...joinType && { joinType },
|
|
15191
15453
|
...conditions && { conditions }
|
|
@@ -15208,10 +15470,13 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15208
15470
|
(r) => r.name === alias || snakeToCamel(r.name) === alias
|
|
15209
15471
|
);
|
|
15210
15472
|
if (!rel) continue;
|
|
15211
|
-
const
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15473
|
+
const rawFkColumns = toColumnList10(rel.foreignKey);
|
|
15474
|
+
const rawFk = rawFkColumns.length > 0 ? rawFkColumns : [
|
|
15475
|
+
deriveFk(
|
|
15476
|
+
rel.type === "belongsTo" ? rel.target : sourceTable,
|
|
15477
|
+
defaultPk
|
|
15478
|
+
)
|
|
15479
|
+
];
|
|
15215
15480
|
let columns = [defaultPk];
|
|
15216
15481
|
if (inc.select?.type === "fields" && inc.select.fields) {
|
|
15217
15482
|
const extraFields = inc.select.fields.filter((f) => f !== defaultPk);
|
|
@@ -15232,8 +15497,8 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15232
15497
|
...rel.type && {
|
|
15233
15498
|
relationType: rel.type
|
|
15234
15499
|
},
|
|
15235
|
-
foreignKey:
|
|
15236
|
-
parentKey: defaultPk,
|
|
15500
|
+
foreignKey: rawFk,
|
|
15501
|
+
parentKey: rel.type === "belongsTo" ? toColumnList10(rel.targetKey).length > 0 ? toColumnList10(rel.targetKey) : [defaultPk] : toColumnList10(rel.sourceKey).length > 0 ? toColumnList10(rel.sourceKey) : [defaultPk],
|
|
15237
15502
|
columns,
|
|
15238
15503
|
joinType: inc.join,
|
|
15239
15504
|
...conditions && { conditions }
|
|
@@ -15390,9 +15655,12 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
|
15390
15655
|
);
|
|
15391
15656
|
}
|
|
15392
15657
|
const isBelongsTo = rel.type === "belongsTo";
|
|
15393
|
-
const rawFk =
|
|
15394
|
-
const
|
|
15395
|
-
const
|
|
15658
|
+
const rawFk = toColumnList11(rel.foreignKey);
|
|
15659
|
+
const fkColumns = rawFk.length > 0 ? rawFk : [deriveFk(isBelongsTo ? rootTable : rel.target, defaultPk)];
|
|
15660
|
+
const sourceKey = toColumnList11(rel.sourceKey);
|
|
15661
|
+
const targetKey = toColumnList11(rel.targetKey);
|
|
15662
|
+
const sourceColumn = isBelongsTo ? fkColumns : sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
15663
|
+
const targetColumn = isBelongsTo ? targetKey.length > 0 ? targetKey : [defaultPk] : fkColumns;
|
|
15396
15664
|
const alias = intent.alias ?? intent.relation;
|
|
15397
15665
|
results.push({
|
|
15398
15666
|
type: "join",
|
|
@@ -15755,10 +16023,11 @@ function compileWithIncludes(plan, options, deps) {
|
|
|
15755
16023
|
const relationName = ctx.includeAlias ?? ctx.relation;
|
|
15756
16024
|
if (!relationName) continue;
|
|
15757
16025
|
const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
|
|
15758
|
-
const fk =
|
|
16026
|
+
const fk = toColumnList11(rawFk);
|
|
16027
|
+
const parentKey = toColumnList11(ctx.parentKey);
|
|
15759
16028
|
const isBelongsTo = ctx.relationType === "belongsTo";
|
|
15760
|
-
const sourceKey = isBelongsTo ? fk :
|
|
15761
|
-
const targetFk = isBelongsTo ?
|
|
16029
|
+
const sourceKey = isBelongsTo ? fk : parentKey.length > 0 ? parentKey : [deps.defaultPk];
|
|
16030
|
+
const targetFk = isBelongsTo ? parentKey.length > 0 ? parentKey : [deps.defaultPk] : fk;
|
|
15762
16031
|
const includeIntent = plan.intent?.include?.find(
|
|
15763
16032
|
(i) => i.relation === relationName || i.relation === ctx.includeAlias
|
|
15764
16033
|
);
|
|
@@ -15821,14 +16090,14 @@ function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
|
|
|
15821
16090
|
};
|
|
15822
16091
|
}
|
|
15823
16092
|
function resolveMutationExistsForeignKey(foreignKey, relationName) {
|
|
15824
|
-
|
|
15825
|
-
if (
|
|
15826
|
-
if (
|
|
16093
|
+
const columns = toColumnList12(foreignKey);
|
|
16094
|
+
if (columns.length === 0) return void 0;
|
|
16095
|
+
if (columns.some((column) => column.length === 0)) {
|
|
15827
16096
|
throw new Error(
|
|
15828
|
-
`Mutation exists()/notExists()
|
|
16097
|
+
`Mutation exists()/notExists() guard relation '${relationName}' has an empty foreignKey column.`
|
|
15829
16098
|
);
|
|
15830
16099
|
}
|
|
15831
|
-
return
|
|
16100
|
+
return columns;
|
|
15832
16101
|
}
|
|
15833
16102
|
function resolveExistsRelation(sourceTable, relation, model) {
|
|
15834
16103
|
if (!model) return { targetTable: relation };
|
|
@@ -15841,13 +16110,13 @@ function resolveExistsRelation(sourceTable, relation, model) {
|
|
|
15841
16110
|
return {
|
|
15842
16111
|
targetTable,
|
|
15843
16112
|
...fk2 !== void 0 && { sourceColumn: fk2 },
|
|
15844
|
-
targetColumn: "id"
|
|
16113
|
+
targetColumn: toColumnList12(rel.targetKey).length > 0 ? toColumnList12(rel.targetKey) : ["id"]
|
|
15845
16114
|
};
|
|
15846
16115
|
}
|
|
15847
16116
|
const fk = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
|
|
15848
16117
|
return {
|
|
15849
16118
|
targetTable,
|
|
15850
|
-
sourceColumn: rel.sourceKey
|
|
16119
|
+
sourceColumn: toColumnList12(rel.sourceKey).length > 0 ? toColumnList12(rel.sourceKey) : ["id"],
|
|
15851
16120
|
...fk !== void 0 && { targetColumn: fk }
|
|
15852
16121
|
};
|
|
15853
16122
|
}
|
|
@@ -18086,7 +18355,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18086
18355
|
bundle.query.from,
|
|
18087
18356
|
deps.naming
|
|
18088
18357
|
);
|
|
18089
|
-
const planReport = queryFromBinding ? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
18358
|
+
const planReport = queryFromBinding ? bundle.plan ?? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
18090
18359
|
dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
|
|
18091
18360
|
});
|
|
18092
18361
|
return guardCompiledQuery(
|
|
@@ -18180,6 +18449,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18180
18449
|
}
|
|
18181
18450
|
const leafBundle = {
|
|
18182
18451
|
...bundle.query !== void 0 && { query: bundle.query },
|
|
18452
|
+
...bundle.plan !== void 0 && { plan: bundle.plan },
|
|
18183
18453
|
...bundle.cteQuery !== void 0 && { cteQuery: bundle.cteQuery },
|
|
18184
18454
|
...bundle.mutation !== void 0 && { mutation: bundle.mutation },
|
|
18185
18455
|
...bundle.returning !== void 0 && { returning: bundle.returning },
|