@dbsp/adapter-pgsql 1.5.0 → 1.7.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 +25 -14
- package/dist/index.js +935 -296
- 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
|
}
|
|
@@ -669,9 +663,6 @@ function funcCall(name, args = [], options = {}) {
|
|
|
669
663
|
function coalesceExpr(args) {
|
|
670
664
|
return { CoalesceExpr: { args } };
|
|
671
665
|
}
|
|
672
|
-
function coalesce(...args) {
|
|
673
|
-
return funcCall("coalesce", args);
|
|
674
|
-
}
|
|
675
666
|
function sortBy(expr, direction = "DEFAULT", nulls = "DEFAULT") {
|
|
676
667
|
const sb = {
|
|
677
668
|
node: expr,
|
|
@@ -885,10 +876,23 @@ function jsonAggSubquery(targetTable, whereExpr, alias, schemaName, naming = ide
|
|
|
885
876
|
}
|
|
886
877
|
};
|
|
887
878
|
}
|
|
879
|
+
const aggOrder = options?.orderBy && options.orderBy.length > 0 ? options.orderBy.map(
|
|
880
|
+
(col) => sortBy(
|
|
881
|
+
jsonAggOrderByExpression(
|
|
882
|
+
col,
|
|
883
|
+
targetAlias,
|
|
884
|
+
naming,
|
|
885
|
+
options.orderByFallback === true
|
|
886
|
+
),
|
|
887
|
+
"ASC",
|
|
888
|
+
"LAST"
|
|
889
|
+
)
|
|
890
|
+
) : void 0;
|
|
888
891
|
const jsonAggCall = {
|
|
889
892
|
FuncCall: {
|
|
890
893
|
funcname: [stringNode("json_agg")],
|
|
891
|
-
args: [toJsonbCall]
|
|
894
|
+
args: [toJsonbCall],
|
|
895
|
+
...aggOrder && { agg_order: aggOrder }
|
|
892
896
|
}
|
|
893
897
|
};
|
|
894
898
|
const fromTable = rangeVar(targetTable, targetAlias, schemaName, naming);
|
|
@@ -923,11 +927,9 @@ function jsonAggSubquery(targetTable, whereExpr, alias, schemaName, naming = ide
|
|
|
923
927
|
}
|
|
924
928
|
};
|
|
925
929
|
}
|
|
926
|
-
function
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
columnRef(parentColumn, parentAlias, void 0, naming)
|
|
930
|
-
);
|
|
930
|
+
function jsonAggOrderByExpression(entry, targetAlias, naming, castToText) {
|
|
931
|
+
const ref = columnRef(entry, targetAlias, void 0, naming);
|
|
932
|
+
return castToText ? typeCast(ref, "text") : ref;
|
|
931
933
|
}
|
|
932
934
|
var STRENGTH_MAP, POLICY_MAP;
|
|
933
935
|
var init_ast_helpers = __esm({
|
|
@@ -3172,6 +3174,7 @@ var init_custom_expression = __esm({
|
|
|
3172
3174
|
});
|
|
3173
3175
|
|
|
3174
3176
|
// src/handlers/where/exists.ts
|
|
3177
|
+
import { toColumnList } from "@dbsp/types";
|
|
3175
3178
|
function createSubLinkExists(subquery, negated) {
|
|
3176
3179
|
const subLink = {
|
|
3177
3180
|
subLinkType: "EXISTS_SUBLINK",
|
|
@@ -3188,10 +3191,27 @@ function createSubLinkExists(subquery, negated) {
|
|
|
3188
3191
|
}
|
|
3189
3192
|
return existsNode;
|
|
3190
3193
|
}
|
|
3191
|
-
function
|
|
3192
|
-
const
|
|
3193
|
-
const
|
|
3194
|
-
|
|
3194
|
+
function buildKeyCorrelation(sourceAlias, sourceCols, targetAlias, targetCols, ctx) {
|
|
3195
|
+
const normalizedSourceCols = toColumnList(sourceCols);
|
|
3196
|
+
const normalizedTargetCols = toColumnList(targetCols);
|
|
3197
|
+
if (normalizedSourceCols.length === 0 || normalizedTargetCols.length === 0 || normalizedSourceCols.length !== normalizedTargetCols.length || normalizedSourceCols.some((column) => column.length === 0) || normalizedTargetCols.some((column) => column.length === 0)) {
|
|
3198
|
+
throw new Error(
|
|
3199
|
+
`Invalid relation correlation: source columns (${normalizedSourceCols.length}) must match target columns (${normalizedTargetCols.length}) and both must be non-empty.`
|
|
3200
|
+
);
|
|
3201
|
+
}
|
|
3202
|
+
const comparisons = normalizedSourceCols.map(
|
|
3203
|
+
(sourceColumn, index) => eqExpr(
|
|
3204
|
+
columnRef(sourceColumn, sourceAlias, void 0, ctx.naming),
|
|
3205
|
+
columnRef(
|
|
3206
|
+
normalizedTargetCols[index],
|
|
3207
|
+
targetAlias,
|
|
3208
|
+
void 0,
|
|
3209
|
+
ctx.naming
|
|
3210
|
+
)
|
|
3211
|
+
)
|
|
3212
|
+
);
|
|
3213
|
+
if (comparisons.length === 1) return comparisons[0];
|
|
3214
|
+
return andExpr(...comparisons);
|
|
3195
3215
|
}
|
|
3196
3216
|
function schemaForExistsFromName(ctx, fromName) {
|
|
3197
3217
|
return schemaForFromName(ctx.schema, fromName, ctx.bindingNames, ctx.naming);
|
|
@@ -3199,11 +3219,15 @@ function schemaForExistsFromName(ctx, fromName) {
|
|
|
3199
3219
|
function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
3200
3220
|
const relation = decision.relation;
|
|
3201
3221
|
const targetTable = decision.targetTable ?? relation;
|
|
3202
|
-
const sourceColumn = decision.sourceColumn ??
|
|
3203
|
-
const targetColumn = decision.targetColumn ?? (ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3204
|
-
ctx.rootTable,
|
|
3222
|
+
const sourceColumn = decision.sourceColumn ?? [
|
|
3205
3223
|
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3206
|
-
|
|
3224
|
+
];
|
|
3225
|
+
const targetColumn = decision.targetColumn ?? [
|
|
3226
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3227
|
+
ctx.rootTable,
|
|
3228
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3229
|
+
)
|
|
3230
|
+
];
|
|
3207
3231
|
if (!targetTable) {
|
|
3208
3232
|
throw new Error("EXISTS handler requires targetTable or relation");
|
|
3209
3233
|
}
|
|
@@ -3211,7 +3235,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3211
3235
|
const targetAlias = `${targetTable}_exists_${existingAliases}`;
|
|
3212
3236
|
state.aliases.set(`exists_${targetTable}`, targetAlias);
|
|
3213
3237
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
3214
|
-
const correlation =
|
|
3238
|
+
const correlation = buildKeyCorrelation(
|
|
3215
3239
|
sourceAlias,
|
|
3216
3240
|
sourceColumn,
|
|
3217
3241
|
targetAlias,
|
|
@@ -3249,8 +3273,8 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3249
3273
|
const joinRelation = inc.relation;
|
|
3250
3274
|
if (!joinRelation) continue;
|
|
3251
3275
|
let joinTargetTable = joinRelation;
|
|
3252
|
-
let
|
|
3253
|
-
let
|
|
3276
|
+
let joinSourceCols;
|
|
3277
|
+
let joinTargetCols;
|
|
3254
3278
|
let sourceAliasForJoin = targetAlias;
|
|
3255
3279
|
const model = ctx.model;
|
|
3256
3280
|
if (model) {
|
|
@@ -3268,31 +3292,41 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3268
3292
|
if (rel) {
|
|
3269
3293
|
joinTargetTable = rel.target;
|
|
3270
3294
|
if (rel.type === "belongsTo") {
|
|
3271
|
-
const fk =
|
|
3272
|
-
|
|
3273
|
-
|
|
3295
|
+
const fk = toColumnList(rel.foreignKey);
|
|
3296
|
+
joinSourceCols = fk.length > 0 ? fk : void 0;
|
|
3297
|
+
const targetKey = toColumnList(rel.targetKey);
|
|
3298
|
+
joinTargetCols = targetKey.length > 0 ? targetKey : [
|
|
3299
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3300
|
+
];
|
|
3274
3301
|
} else {
|
|
3275
|
-
const fk =
|
|
3276
|
-
|
|
3277
|
-
|
|
3302
|
+
const fk = toColumnList(rel.foreignKey);
|
|
3303
|
+
const sourceKey = toColumnList(rel.sourceKey);
|
|
3304
|
+
joinSourceCols = sourceKey.length > 0 ? sourceKey : [
|
|
3305
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3306
|
+
];
|
|
3307
|
+
joinTargetCols = fk.length > 0 ? fk : void 0;
|
|
3278
3308
|
}
|
|
3279
3309
|
}
|
|
3280
3310
|
}
|
|
3281
|
-
if (!
|
|
3282
|
-
|
|
3283
|
-
|
|
3311
|
+
if (!joinSourceCols || joinSourceCols.length === 0) {
|
|
3312
|
+
joinSourceCols = [
|
|
3313
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3314
|
+
joinRelation,
|
|
3315
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3316
|
+
)
|
|
3317
|
+
];
|
|
3318
|
+
joinTargetCols = [
|
|
3284
3319
|
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3285
|
-
|
|
3286
|
-
joinTargetCol = ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
|
|
3320
|
+
];
|
|
3287
3321
|
}
|
|
3288
3322
|
const joinAlias = joinRelation;
|
|
3289
|
-
const joinQuals =
|
|
3290
|
-
joinSourceCol,
|
|
3323
|
+
const joinQuals = buildKeyCorrelation(
|
|
3291
3324
|
sourceAliasForJoin,
|
|
3292
3325
|
// resolved source alias (root or intermediate)
|
|
3293
|
-
|
|
3326
|
+
joinSourceCols,
|
|
3294
3327
|
joinAlias,
|
|
3295
|
-
|
|
3328
|
+
joinTargetCols,
|
|
3329
|
+
ctx
|
|
3296
3330
|
);
|
|
3297
3331
|
const joinType = inc.joinType === "left" ? "JOIN_LEFT" : "JOIN_INNER";
|
|
3298
3332
|
const joinRangeVar = rangeVar(
|
|
@@ -3774,6 +3808,7 @@ var init_range = __esm({
|
|
|
3774
3808
|
});
|
|
3775
3809
|
|
|
3776
3810
|
// src/compile-where.ts
|
|
3811
|
+
import { toColumnList as toColumnList2 } from "@dbsp/types";
|
|
3777
3812
|
import { getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields3 } from "@dbsp/types/internal";
|
|
3778
3813
|
function toHandlerContext(ctx) {
|
|
3779
3814
|
return {
|
|
@@ -4039,14 +4074,16 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
4039
4074
|
let singleHopTargetColumn;
|
|
4040
4075
|
if (resolvedRelation) {
|
|
4041
4076
|
const rel = resolvedRelation;
|
|
4042
|
-
const fk =
|
|
4077
|
+
const fk = toColumnList2(rel.foreignKey);
|
|
4043
4078
|
const defaultPk = DEFAULT_PK_COLUMN;
|
|
4044
4079
|
if (rel.type === "belongsTo") {
|
|
4045
|
-
singleHopSourceColumn = fk
|
|
4046
|
-
|
|
4080
|
+
singleHopSourceColumn = fk.length > 0 ? fk : [defaultFkDerivation(rel.target, defaultPk)];
|
|
4081
|
+
const targetKey = toColumnList2(rel.targetKey);
|
|
4082
|
+
singleHopTargetColumn = targetKey.length > 0 ? targetKey : [defaultPk];
|
|
4047
4083
|
} else {
|
|
4048
|
-
|
|
4049
|
-
|
|
4084
|
+
const sourceKey = toColumnList2(rel.sourceKey);
|
|
4085
|
+
singleHopSourceColumn = sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
4086
|
+
singleHopTargetColumn = fk.length > 0 ? fk : [defaultFkDerivation(ctx.rootTable, defaultPk)];
|
|
4050
4087
|
}
|
|
4051
4088
|
}
|
|
4052
4089
|
return compileWhereIntent(
|
|
@@ -4083,15 +4120,19 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
4083
4120
|
);
|
|
4084
4121
|
}
|
|
4085
4122
|
hopTargets.push(rel.target);
|
|
4086
|
-
const fk =
|
|
4123
|
+
const fk = toColumnList2(rel.foreignKey);
|
|
4087
4124
|
const defaultPk = DEFAULT_PK_COLUMN;
|
|
4088
4125
|
if (rel.type === "belongsTo") {
|
|
4089
|
-
hopSourceColumns.push(
|
|
4090
|
-
|
|
4126
|
+
hopSourceColumns.push(
|
|
4127
|
+
fk.length > 0 ? fk : [defaultFkDerivation(rel.target, defaultPk)]
|
|
4128
|
+
);
|
|
4129
|
+
const targetKey = toColumnList2(rel.targetKey);
|
|
4130
|
+
hopTargetColumns.push(targetKey.length > 0 ? targetKey : [defaultPk]);
|
|
4091
4131
|
} else {
|
|
4092
|
-
|
|
4132
|
+
const sourceKey = toColumnList2(rel.sourceKey);
|
|
4133
|
+
hopSourceColumns.push(sourceKey.length > 0 ? sourceKey : [defaultPk]);
|
|
4093
4134
|
hopTargetColumns.push(
|
|
4094
|
-
fk
|
|
4135
|
+
fk.length > 0 ? fk : [defaultFkDerivation(currentSource, defaultPk)]
|
|
4095
4136
|
);
|
|
4096
4137
|
}
|
|
4097
4138
|
currentSource = rel.target;
|
|
@@ -4319,6 +4360,7 @@ var init_raw_exists = __esm({
|
|
|
4319
4360
|
});
|
|
4320
4361
|
|
|
4321
4362
|
// src/handlers/where/relation-filter.ts
|
|
4363
|
+
import { toColumnList as toColumnList3 } from "@dbsp/types";
|
|
4322
4364
|
function getFilterMode(decision) {
|
|
4323
4365
|
const operator = decision.operator;
|
|
4324
4366
|
if (operator === "some" || operator === "exists") return "some";
|
|
@@ -4330,15 +4372,18 @@ function getFilterMode(decision) {
|
|
|
4330
4372
|
function buildJoinFilter(decision, ctx, state, dispatch) {
|
|
4331
4373
|
const relation = decision.relation;
|
|
4332
4374
|
const targetTable = decision.targetTable ?? relation;
|
|
4333
|
-
const sourceColumn =
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
ctx.
|
|
4341
|
-
|
|
4375
|
+
const sourceColumn = toColumnList3(decision.sourceColumn);
|
|
4376
|
+
if (sourceColumn.length === 0) {
|
|
4377
|
+
throw new Error(
|
|
4378
|
+
"Missing required column 'sourceColumn' in relation filter"
|
|
4379
|
+
);
|
|
4380
|
+
}
|
|
4381
|
+
const targetColumn = decision.targetColumn ?? [
|
|
4382
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
4383
|
+
ctx.rootTable,
|
|
4384
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
4385
|
+
)
|
|
4386
|
+
];
|
|
4342
4387
|
if (!targetTable) {
|
|
4343
4388
|
throw new Error("Relation filter requires targetTable");
|
|
4344
4389
|
}
|
|
@@ -4346,9 +4391,12 @@ function buildJoinFilter(decision, ctx, state, dispatch) {
|
|
|
4346
4391
|
const targetAlias = `${targetTable}_rel_${existingAliases}`;
|
|
4347
4392
|
state.aliases.set(`rel_${targetTable}`, targetAlias);
|
|
4348
4393
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
4349
|
-
const joinCondition =
|
|
4350
|
-
|
|
4351
|
-
|
|
4394
|
+
const joinCondition = buildKeyCorrelation(
|
|
4395
|
+
sourceAlias,
|
|
4396
|
+
sourceColumn,
|
|
4397
|
+
targetAlias,
|
|
4398
|
+
targetColumn,
|
|
4399
|
+
ctx
|
|
4352
4400
|
);
|
|
4353
4401
|
const joinExpr2 = {
|
|
4354
4402
|
jointype: "JOIN_INNER",
|
|
@@ -4383,6 +4431,7 @@ var init_relation_filter = __esm({
|
|
|
4383
4431
|
"use strict";
|
|
4384
4432
|
init_assert_field();
|
|
4385
4433
|
init_ast_helpers();
|
|
4434
|
+
init_exists();
|
|
4386
4435
|
relationFilterHandler = {
|
|
4387
4436
|
operators: ["relationFilter", "relation", "is", "isNot"],
|
|
4388
4437
|
compile(decision, ctx, state, dispatch) {
|
|
@@ -4434,6 +4483,11 @@ var init_relation_filter = __esm({
|
|
|
4434
4483
|
|
|
4435
4484
|
// src/subquery-emission.ts
|
|
4436
4485
|
import { isParamIntent as isParamIntent5 } from "@dbsp/types";
|
|
4486
|
+
function isColumnOrderBy(orderBy) {
|
|
4487
|
+
return Array.isArray(orderBy) && orderBy.every(
|
|
4488
|
+
(item) => typeof item === "object" && item !== null && "column" in item && typeof item.column === "string"
|
|
4489
|
+
);
|
|
4490
|
+
}
|
|
4437
4491
|
function assertNoDroppedDecisionModifiers(decision, use) {
|
|
4438
4492
|
const d = decision;
|
|
4439
4493
|
const unsupported = [];
|
|
@@ -4552,8 +4606,9 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
|
|
|
4552
4606
|
],
|
|
4553
4607
|
...whereClause && { whereClause }
|
|
4554
4608
|
};
|
|
4555
|
-
|
|
4556
|
-
|
|
4609
|
+
const orderBy = isColumnOrderBy(decision.orderBy) ? decision.orderBy : void 0;
|
|
4610
|
+
if (orderBy && orderBy.length > 0) {
|
|
4611
|
+
stmt.sortClause = orderBy.map(
|
|
4557
4612
|
(o) => sortBy(
|
|
4558
4613
|
columnRef(o.column, targetAlias, void 0, ctx.naming),
|
|
4559
4614
|
o.direction ?? "ASC",
|
|
@@ -4812,6 +4867,7 @@ var init_where = __esm({
|
|
|
4812
4867
|
});
|
|
4813
4868
|
|
|
4814
4869
|
// src/handlers/include/cte.ts
|
|
4870
|
+
import { toColumnList as toColumnList4 } from "@dbsp/types";
|
|
4815
4871
|
function buildCteTargets(columns, alias, ctx) {
|
|
4816
4872
|
if (columns && columns.length > 0 && !columns.every((c) => c === "*")) {
|
|
4817
4873
|
return columns.filter((col) => col !== "*").map((col) => ({
|
|
@@ -4862,12 +4918,12 @@ function buildCTE(cteName, cteSelect, ctx) {
|
|
|
4862
4918
|
return { CommonTableExpr: cte };
|
|
4863
4919
|
}
|
|
4864
4920
|
function buildCteJoin(cteName, cteAlias, sourceAlias, sourceColumn, targetColumn, ctx) {
|
|
4865
|
-
const joinCondition =
|
|
4866
|
-
sourceColumn,
|
|
4921
|
+
const joinCondition = buildKeyCorrelation(
|
|
4867
4922
|
sourceAlias,
|
|
4868
|
-
|
|
4923
|
+
sourceColumn,
|
|
4869
4924
|
cteAlias,
|
|
4870
|
-
|
|
4925
|
+
targetColumn,
|
|
4926
|
+
ctx
|
|
4871
4927
|
);
|
|
4872
4928
|
const cteRef = {
|
|
4873
4929
|
RangeVar: {
|
|
@@ -4891,20 +4947,22 @@ var init_cte = __esm({
|
|
|
4891
4947
|
init_assert_field();
|
|
4892
4948
|
init_ast_helpers();
|
|
4893
4949
|
init_handlers();
|
|
4950
|
+
init_exists();
|
|
4894
4951
|
cteIncludeHandler = {
|
|
4895
4952
|
strategy: "cte",
|
|
4896
4953
|
compile(decision, ctx, state) {
|
|
4897
4954
|
const relation = decision.relation;
|
|
4898
4955
|
const targetTable = decision.targetTable ?? relation;
|
|
4899
|
-
const sourceColumn =
|
|
4900
|
-
|
|
4901
|
-
"sourceColumn"
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4956
|
+
const sourceColumn = toColumnList4(decision.sourceColumn);
|
|
4957
|
+
if (sourceColumn.length === 0 || sourceColumn.some((col) => col === "")) {
|
|
4958
|
+
throw new Error("Missing required column 'sourceColumn' in CTE include");
|
|
4959
|
+
}
|
|
4960
|
+
const targetColumn = decision.targetColumn ?? [
|
|
4961
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
4962
|
+
ctx.rootTable,
|
|
4963
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
4964
|
+
)
|
|
4965
|
+
];
|
|
4908
4966
|
const columns = decision.columns;
|
|
4909
4967
|
const conditions = decision.conditions;
|
|
4910
4968
|
if (!targetTable) {
|
|
@@ -4947,13 +5005,14 @@ var init_cte = __esm({
|
|
|
4947
5005
|
});
|
|
4948
5006
|
|
|
4949
5007
|
// src/handlers/include/join.ts
|
|
5008
|
+
import { toColumnList as toColumnList5 } from "@dbsp/types";
|
|
4950
5009
|
function buildJoin(targetTable, targetAlias, sourceAlias, sourceColumn, targetColumn, ctx, joinType = "left") {
|
|
4951
|
-
const joinCondition =
|
|
4952
|
-
sourceColumn,
|
|
5010
|
+
const joinCondition = buildKeyCorrelation(
|
|
4953
5011
|
sourceAlias,
|
|
4954
|
-
|
|
5012
|
+
sourceColumn,
|
|
4955
5013
|
targetAlias,
|
|
4956
|
-
|
|
5014
|
+
targetColumn,
|
|
5015
|
+
ctx
|
|
4957
5016
|
);
|
|
4958
5017
|
const joinExpr2 = {
|
|
4959
5018
|
jointype: joinType === "inner" ? "JOIN_INNER" : "JOIN_LEFT",
|
|
@@ -4968,6 +5027,7 @@ var init_join = __esm({
|
|
|
4968
5027
|
"use strict";
|
|
4969
5028
|
init_assert_field();
|
|
4970
5029
|
init_ast_helpers();
|
|
5030
|
+
init_exists();
|
|
4971
5031
|
joinIncludeHandler = {
|
|
4972
5032
|
strategy: "join",
|
|
4973
5033
|
compile(decision, ctx, _state) {
|
|
@@ -4978,15 +5038,16 @@ var init_join = __esm({
|
|
|
4978
5038
|
}
|
|
4979
5039
|
const targetAlias = relation ?? targetTable;
|
|
4980
5040
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
4981
|
-
const sourceColumn =
|
|
4982
|
-
|
|
4983
|
-
"sourceColumn"
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
5041
|
+
const sourceColumn = toColumnList5(decision.sourceColumn);
|
|
5042
|
+
if (sourceColumn.length === 0) {
|
|
5043
|
+
throw new Error("Missing required column 'sourceColumn' in JOIN include");
|
|
5044
|
+
}
|
|
5045
|
+
const targetColumn = decision.targetColumn ?? [
|
|
5046
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
5047
|
+
ctx.rootTable,
|
|
5048
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
5049
|
+
)
|
|
5050
|
+
];
|
|
4990
5051
|
const join = buildJoin(
|
|
4991
5052
|
targetTable,
|
|
4992
5053
|
targetAlias,
|
|
@@ -5040,6 +5101,22 @@ var init_shared = __esm({
|
|
|
5040
5101
|
});
|
|
5041
5102
|
|
|
5042
5103
|
// src/handlers/include/json-agg.ts
|
|
5104
|
+
import { resolveJsonAggOrderKey } from "@dbsp/types";
|
|
5105
|
+
function isJsonAggOrderBy(orderBy) {
|
|
5106
|
+
return Array.isArray(orderBy) && orderBy.every((item) => typeof item === "string");
|
|
5107
|
+
}
|
|
5108
|
+
function resolveJsonAggOrderBy(decision, targetTable, ctx) {
|
|
5109
|
+
const table = ctx.model?.getTable(targetTable);
|
|
5110
|
+
if (table) {
|
|
5111
|
+
const orderKey = resolveJsonAggOrderKey(table);
|
|
5112
|
+
return orderKey.columns.length > 0 ? orderKey : void 0;
|
|
5113
|
+
}
|
|
5114
|
+
const decisionOrderBy = isJsonAggOrderBy(decision.orderBy) ? decision.orderBy : void 0;
|
|
5115
|
+
return decisionOrderBy && decisionOrderBy.length > 0 ? {
|
|
5116
|
+
columns: decisionOrderBy,
|
|
5117
|
+
fallback: decision.orderByFallback === true
|
|
5118
|
+
} : void 0;
|
|
5119
|
+
}
|
|
5043
5120
|
function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
5044
5121
|
const innerAlias = depth === 0 ? "__t__" : `__t${depth}__`;
|
|
5045
5122
|
const relation = decision.relation ?? decision.relationName;
|
|
@@ -5056,12 +5133,12 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5056
5133
|
ctx.defaultPkColumnName,
|
|
5057
5134
|
ctx.deriveFkColumnName
|
|
5058
5135
|
);
|
|
5059
|
-
let whereExpr =
|
|
5060
|
-
parentAlias,
|
|
5061
|
-
sourceColumn,
|
|
5136
|
+
let whereExpr = buildKeyCorrelation(
|
|
5062
5137
|
innerAlias,
|
|
5063
5138
|
targetColumn,
|
|
5064
|
-
|
|
5139
|
+
parentAlias,
|
|
5140
|
+
sourceColumn,
|
|
5141
|
+
ctx
|
|
5065
5142
|
);
|
|
5066
5143
|
const compiledFilter = decision._compiledFilterWhere;
|
|
5067
5144
|
if (compiledFilter) {
|
|
@@ -5092,6 +5169,7 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5092
5169
|
if (childNodes.length === 0) childNodes = void 0;
|
|
5093
5170
|
}
|
|
5094
5171
|
const limit = typeof decision.limit === "number" ? decision.limit : void 0;
|
|
5172
|
+
const orderBy = resolveJsonAggOrderBy(decision, targetTable, ctx);
|
|
5095
5173
|
return jsonAggSubquery(
|
|
5096
5174
|
targetTable,
|
|
5097
5175
|
whereExpr,
|
|
@@ -5102,7 +5180,9 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5102
5180
|
...childNodes && { childNodes },
|
|
5103
5181
|
innerAlias,
|
|
5104
5182
|
...limit !== void 0 && { limit },
|
|
5105
|
-
...decision.columns && { columns: decision.columns }
|
|
5183
|
+
...decision.columns && { columns: decision.columns },
|
|
5184
|
+
...orderBy && { orderBy: orderBy.columns },
|
|
5185
|
+
...orderBy?.fallback && { orderByFallback: true }
|
|
5106
5186
|
}
|
|
5107
5187
|
);
|
|
5108
5188
|
}
|
|
@@ -5111,6 +5191,7 @@ var init_json_agg = __esm({
|
|
|
5111
5191
|
"src/handlers/include/json-agg.ts"() {
|
|
5112
5192
|
"use strict";
|
|
5113
5193
|
init_ast_helpers();
|
|
5194
|
+
init_exists();
|
|
5114
5195
|
init_shared();
|
|
5115
5196
|
jsonAggIncludeHandler = {
|
|
5116
5197
|
strategy: "json_agg",
|
|
@@ -5132,6 +5213,7 @@ var init_json_agg = __esm({
|
|
|
5132
5213
|
});
|
|
5133
5214
|
|
|
5134
5215
|
// src/handlers/include/lateral.ts
|
|
5216
|
+
import { toColumnList as toColumnList6 } from "@dbsp/types";
|
|
5135
5217
|
function buildLateralTargets(columns, alias, ctx) {
|
|
5136
5218
|
if (columns && columns.length > 0 && !(columns.length === 1 && columns[0] === "*")) {
|
|
5137
5219
|
return columns.map((col) => ({
|
|
@@ -5143,12 +5225,12 @@ function buildLateralTargets(columns, alias, ctx) {
|
|
|
5143
5225
|
return [starTarget(alias, ctx.naming)];
|
|
5144
5226
|
}
|
|
5145
5227
|
function buildLateralSubquery(targetTable, innerAlias, outerAlias, sourceColumn, targetColumn, columns, limit, ctx) {
|
|
5146
|
-
const whereClause =
|
|
5147
|
-
targetColumn,
|
|
5228
|
+
const whereClause = buildKeyCorrelation(
|
|
5148
5229
|
innerAlias,
|
|
5149
|
-
|
|
5230
|
+
targetColumn,
|
|
5150
5231
|
outerAlias,
|
|
5151
|
-
|
|
5232
|
+
sourceColumn,
|
|
5233
|
+
ctx
|
|
5152
5234
|
);
|
|
5153
5235
|
const targetList = buildLateralTargets(columns, innerAlias, ctx);
|
|
5154
5236
|
const stmt = {
|
|
@@ -5228,19 +5310,23 @@ var init_lateral = __esm({
|
|
|
5228
5310
|
"use strict";
|
|
5229
5311
|
init_assert_field();
|
|
5230
5312
|
init_ast_helpers();
|
|
5313
|
+
init_exists();
|
|
5231
5314
|
init_shared();
|
|
5232
5315
|
lateralIncludeHandler = {
|
|
5233
5316
|
strategy: "lateral",
|
|
5234
5317
|
compile(decision, ctx, state) {
|
|
5235
|
-
const sourceColumn =
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
ctx.
|
|
5243
|
-
|
|
5318
|
+
const sourceColumn = toColumnList6(decision.sourceColumn);
|
|
5319
|
+
if (sourceColumn.length === 0) {
|
|
5320
|
+
throw new Error(
|
|
5321
|
+
"Missing required column 'sourceColumn' in lateral include"
|
|
5322
|
+
);
|
|
5323
|
+
}
|
|
5324
|
+
const targetColumn = decision.targetColumn ?? [
|
|
5325
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
5326
|
+
ctx.rootTable,
|
|
5327
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
5328
|
+
)
|
|
5329
|
+
];
|
|
5244
5330
|
const outerAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
5245
5331
|
const { joins, targets } = compileLateralCascade(
|
|
5246
5332
|
decision,
|
|
@@ -6184,6 +6270,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6184
6270
|
pkColumn,
|
|
6185
6271
|
fkColumn,
|
|
6186
6272
|
outerAlias,
|
|
6273
|
+
outerSeedColumn,
|
|
6187
6274
|
isAncestors,
|
|
6188
6275
|
maxDepth,
|
|
6189
6276
|
selectColumn,
|
|
@@ -6194,6 +6281,9 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6194
6281
|
const dbPk = naming.toDatabase(pkColumn);
|
|
6195
6282
|
const dbFk = naming.toDatabase(fkColumn);
|
|
6196
6283
|
const dbOuter = naming.toDatabase(outerAlias);
|
|
6284
|
+
const dbOuterSeed = naming.toDatabase(
|
|
6285
|
+
outerSeedColumn ?? (isAncestors ? fkColumn : pkColumn)
|
|
6286
|
+
);
|
|
6197
6287
|
const innerAlias = "__n";
|
|
6198
6288
|
const anchorSelect = {
|
|
6199
6289
|
targetList: [
|
|
@@ -6262,7 +6352,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6262
6352
|
ColumnRef: {
|
|
6263
6353
|
fields: [
|
|
6264
6354
|
{ String: { sval: dbOuter } },
|
|
6265
|
-
{ String: { sval:
|
|
6355
|
+
{ String: { sval: dbOuterSeed } }
|
|
6266
6356
|
]
|
|
6267
6357
|
}
|
|
6268
6358
|
}
|
|
@@ -6282,7 +6372,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6282
6372
|
ColumnRef: {
|
|
6283
6373
|
fields: [
|
|
6284
6374
|
{ String: { sval: dbOuter } },
|
|
6285
|
-
{ String: { sval:
|
|
6375
|
+
{ String: { sval: dbOuterSeed } }
|
|
6286
6376
|
]
|
|
6287
6377
|
}
|
|
6288
6378
|
}
|
|
@@ -6346,15 +6436,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6346
6436
|
}
|
|
6347
6437
|
],
|
|
6348
6438
|
fromClause: [
|
|
6349
|
-
// FROM __rc
|
|
6350
|
-
{
|
|
6351
|
-
RangeVar: {
|
|
6352
|
-
relname: cteAlias,
|
|
6353
|
-
inh: true,
|
|
6354
|
-
relpersistence: "p"
|
|
6355
|
-
}
|
|
6356
|
-
},
|
|
6357
|
-
// INNER JOIN table AS __n
|
|
6439
|
+
// FROM __rc INNER JOIN table AS __n
|
|
6358
6440
|
{
|
|
6359
6441
|
JoinExpr: {
|
|
6360
6442
|
jointype: "JOIN_INNER",
|
|
@@ -6437,7 +6519,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6437
6519
|
},
|
|
6438
6520
|
integerNode(maxDepth)
|
|
6439
6521
|
),
|
|
6440
|
-
// pk <> ALL(__visited)
|
|
6522
|
+
// pk <> ALL(__visited) - cycle detection
|
|
6441
6523
|
{
|
|
6442
6524
|
A_Expr: {
|
|
6443
6525
|
kind: "AEXPR_OP_ALL",
|
|
@@ -6482,20 +6564,35 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6482
6564
|
targetList: [
|
|
6483
6565
|
{
|
|
6484
6566
|
ResTarget: {
|
|
6485
|
-
val:
|
|
6486
|
-
funcCall(
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6567
|
+
val: coalesceExpr([
|
|
6568
|
+
funcCall(
|
|
6569
|
+
"json_agg",
|
|
6570
|
+
[
|
|
6571
|
+
{
|
|
6572
|
+
ColumnRef: {
|
|
6573
|
+
fields: [
|
|
6574
|
+
{ String: { sval: cteAlias } },
|
|
6575
|
+
{ String: { sval: dbSelectCol } }
|
|
6576
|
+
]
|
|
6577
|
+
}
|
|
6493
6578
|
}
|
|
6579
|
+
],
|
|
6580
|
+
{
|
|
6581
|
+
orderBy: [
|
|
6582
|
+
sortBy({
|
|
6583
|
+
ColumnRef: {
|
|
6584
|
+
fields: [
|
|
6585
|
+
{ String: { sval: cteAlias } },
|
|
6586
|
+
{ String: { sval: "__depth" } }
|
|
6587
|
+
]
|
|
6588
|
+
}
|
|
6589
|
+
})
|
|
6590
|
+
]
|
|
6494
6591
|
}
|
|
6495
|
-
|
|
6592
|
+
),
|
|
6496
6593
|
// Empty array fallback: '[]'::json
|
|
6497
|
-
typeCast(
|
|
6498
|
-
)
|
|
6594
|
+
typeCast({ A_Const: { sval: { sval: "[]" } } }, "json")
|
|
6595
|
+
])
|
|
6499
6596
|
}
|
|
6500
6597
|
}
|
|
6501
6598
|
],
|
|
@@ -6954,6 +7051,11 @@ var init_relation = __esm({
|
|
|
6954
7051
|
});
|
|
6955
7052
|
|
|
6956
7053
|
// src/handlers/expression/window.ts
|
|
7054
|
+
function isWindowOrderBy(orderBy) {
|
|
7055
|
+
return Array.isArray(orderBy) && orderBy.every(
|
|
7056
|
+
(item) => typeof item === "object" && item !== null && "column" in item && typeof item.column === "string"
|
|
7057
|
+
);
|
|
7058
|
+
}
|
|
6957
7059
|
function buildSortBy(column, direction, ctx) {
|
|
6958
7060
|
const tableAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
6959
7061
|
const colRef = columnRef(column, tableAlias, void 0, ctx.naming);
|
|
@@ -6966,7 +7068,7 @@ function buildSortBy(column, direction, ctx) {
|
|
|
6966
7068
|
}
|
|
6967
7069
|
function buildWindowDef(decision, ctx) {
|
|
6968
7070
|
const partition = decision.partition;
|
|
6969
|
-
const orderBy = decision.orderBy;
|
|
7071
|
+
const orderBy = isWindowOrderBy(decision.orderBy) ? decision.orderBy : void 0;
|
|
6970
7072
|
const frame = decision.frame;
|
|
6971
7073
|
const tableAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
6972
7074
|
const windowDef = { frameOptions: WINDOW_FRAME_DEFAULT };
|
|
@@ -7229,7 +7331,8 @@ import { InvalidOperationError as InvalidOperationError2 } from "@dbsp/core";
|
|
|
7229
7331
|
import {
|
|
7230
7332
|
isParamIntent as isParamIntent6,
|
|
7231
7333
|
NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
|
|
7232
|
-
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
|
|
7334
|
+
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST,
|
|
7335
|
+
toColumnList as toColumnList7
|
|
7233
7336
|
} from "@dbsp/types";
|
|
7234
7337
|
import {
|
|
7235
7338
|
getNqlBindingRefName,
|
|
@@ -8295,10 +8398,12 @@ init_case_value();
|
|
|
8295
8398
|
init_custom();
|
|
8296
8399
|
init_expression();
|
|
8297
8400
|
init_param_value();
|
|
8401
|
+
init_pseudo();
|
|
8298
8402
|
init_window();
|
|
8299
8403
|
init_include();
|
|
8300
8404
|
init_shared();
|
|
8301
8405
|
init_handlers();
|
|
8406
|
+
init_exists();
|
|
8302
8407
|
init_types();
|
|
8303
8408
|
init_utils();
|
|
8304
8409
|
init_intent_to_decisions();
|
|
@@ -8336,7 +8441,25 @@ function assertNqlSelectWindowFunctionAllowed(functionName) {
|
|
|
8336
8441
|
throw new UnsupportedNqlSelectFunctionError(functionName);
|
|
8337
8442
|
}
|
|
8338
8443
|
}
|
|
8444
|
+
function trustedRelationHasMultipleHops(relation) {
|
|
8445
|
+
if (typeof relation !== "string") return relation.length > 1;
|
|
8446
|
+
return relation.split(".").length > 1;
|
|
8447
|
+
}
|
|
8448
|
+
function isJsonAggOrderBy2(orderBy) {
|
|
8449
|
+
return Array.isArray(orderBy) && orderBy.every((item) => typeof item === "string");
|
|
8450
|
+
}
|
|
8451
|
+
function isExpressionOrderBy(orderBy) {
|
|
8452
|
+
return Array.isArray(orderBy) && orderBy.every(
|
|
8453
|
+
(item) => typeof item === "object" && item !== null && "field" in item && typeof item.field === "string"
|
|
8454
|
+
);
|
|
8455
|
+
}
|
|
8339
8456
|
function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
8457
|
+
const jsonAggOrderBy = isJsonAggOrderBy2(pd.orderBy) ? pd.orderBy : void 0;
|
|
8458
|
+
const expressionOrderBy = isExpressionOrderBy(pd.orderBy) ? pd.orderBy : void 0;
|
|
8459
|
+
const handlerOrderBy = jsonAggOrderBy ?? expressionOrderBy?.map((o) => ({
|
|
8460
|
+
column: o.field,
|
|
8461
|
+
direction: o.direction?.toUpperCase() ?? "ASC"
|
|
8462
|
+
}));
|
|
8340
8463
|
const derivedFkColumns = deriveFkColumns(
|
|
8341
8464
|
pd,
|
|
8342
8465
|
pd.sourceTable ?? rootTable,
|
|
@@ -8371,6 +8494,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8371
8494
|
relationType: pd.relationType,
|
|
8372
8495
|
foreignKey: pd.foreignKey,
|
|
8373
8496
|
parentKey: pd.parentKey,
|
|
8497
|
+
orderByFallback: pd.orderByFallback,
|
|
8374
8498
|
dataType: pd.dataType,
|
|
8375
8499
|
traversal: pd.traversal,
|
|
8376
8500
|
pkColumn: pd.pkColumn,
|
|
@@ -8385,10 +8509,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8385
8509
|
include: pd.include?.map(
|
|
8386
8510
|
(c) => mapToHandlerDecision(c, rootTable, defaultPk, deriveFk)
|
|
8387
8511
|
),
|
|
8388
|
-
orderBy:
|
|
8389
|
-
column: o.field,
|
|
8390
|
-
direction: o.direction?.toUpperCase() ?? "ASC"
|
|
8391
|
-
})),
|
|
8512
|
+
orderBy: handlerOrderBy,
|
|
8392
8513
|
partition: pd.partitionBy,
|
|
8393
8514
|
jsonPath: pd.jsonPath,
|
|
8394
8515
|
jsonMode: pd.jsonMode,
|
|
@@ -8767,6 +8888,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8767
8888
|
this.defaultPk,
|
|
8768
8889
|
this.deriveFk
|
|
8769
8890
|
);
|
|
8891
|
+
if (handlerDecision.strategy === "json_agg") {
|
|
8892
|
+
assertDialectCapability(
|
|
8893
|
+
this.dialectCapabilities,
|
|
8894
|
+
"supportsJsonAgg",
|
|
8895
|
+
"JSON aggregation for relation includes"
|
|
8896
|
+
);
|
|
8897
|
+
}
|
|
8770
8898
|
const handler = getIncludeHandler(
|
|
8771
8899
|
handlerDecision.strategy
|
|
8772
8900
|
);
|
|
@@ -8940,18 +9068,11 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8940
9068
|
isNqlBindingRoot(plan) {
|
|
8941
9069
|
return hasBindingName(this.bindingNames, plan.rootTable, this.naming);
|
|
8942
9070
|
}
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
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
|
-
}
|
|
8954
|
-
const relatedAlias = `rc_${this.bindingRelationColumnSubqueryIndex++}`;
|
|
9071
|
+
allocateBindingRelationAlias() {
|
|
9072
|
+
return `rc_${this.bindingRelationColumnSubqueryIndex++}`;
|
|
9073
|
+
}
|
|
9074
|
+
buildCorrelatedRelationRefs(fields, plan) {
|
|
9075
|
+
const relatedAlias = this.allocateBindingRelationAlias();
|
|
8955
9076
|
const relatedTable = rangeVar(
|
|
8956
9077
|
fields.targetTable,
|
|
8957
9078
|
relatedAlias,
|
|
@@ -8964,28 +9085,255 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8964
9085
|
void 0,
|
|
8965
9086
|
this.naming
|
|
8966
9087
|
);
|
|
8967
|
-
|
|
8968
|
-
fields.targetColumn,
|
|
9088
|
+
return {
|
|
8969
9089
|
relatedAlias,
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
9090
|
+
relatedTable,
|
|
9091
|
+
relatedColumn
|
|
9092
|
+
};
|
|
9093
|
+
}
|
|
9094
|
+
bindingRelationHasCompleteManyToManyProof(fields) {
|
|
9095
|
+
return (fields.relationType === "manyToMany" || fields.relationType === "belongsToMany") && fields.through !== void 0 && fields.throughSourceColumn !== void 0 && fields.throughTargetColumn !== void 0;
|
|
9096
|
+
}
|
|
9097
|
+
bindingRelationName(fields) {
|
|
9098
|
+
return typeof fields.relation === "string" ? fields.relation : fields.relation.join(".");
|
|
9099
|
+
}
|
|
9100
|
+
bindingRelationNameLooksRecursive(fields) {
|
|
9101
|
+
const relationName = this.bindingRelationName(fields).toLowerCase();
|
|
9102
|
+
return relationName === "ascendant" || relationName === "descendant";
|
|
9103
|
+
}
|
|
9104
|
+
compileBindingRecursiveRelationColumnSubquery(fields, plan, dialectCapabilities) {
|
|
9105
|
+
const recursive = fields.recursive;
|
|
9106
|
+
if (recursive === void 0) {
|
|
9107
|
+
throw new Error(
|
|
9108
|
+
`NQL binding recursive relation-column proof for '${plan.rootTable}' is missing recursive metadata.`
|
|
9109
|
+
);
|
|
9110
|
+
}
|
|
9111
|
+
if (fields.cardinality !== "many") {
|
|
9112
|
+
throw new Error(
|
|
9113
|
+
`NQL binding recursive relation-column proof for '${plan.rootTable}' must use cardinality 'many'.`
|
|
9114
|
+
);
|
|
9115
|
+
}
|
|
9116
|
+
if (fields.sourceColumn.length !== 1) {
|
|
9117
|
+
throw new Error(
|
|
9118
|
+
`NQL binding recursive relation-column proof for '${plan.rootTable}' must carry exactly one projected seed column.`
|
|
9119
|
+
);
|
|
9120
|
+
}
|
|
9121
|
+
if (dialectCapabilities?.supportsRecursiveCTE === false) {
|
|
9122
|
+
throw new Error(
|
|
9123
|
+
`Recursive NQL binding relation columns require a dialect with supportsRecursiveCTE; current dialect (${dialectCapabilities.name}) declared it unsupported.`
|
|
9124
|
+
);
|
|
9125
|
+
}
|
|
9126
|
+
assertDialectCapability(
|
|
9127
|
+
dialectCapabilities,
|
|
9128
|
+
"supportsJsonAgg",
|
|
9129
|
+
"JSON aggregation for NQL binding relation columns is"
|
|
8978
9130
|
);
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
9131
|
+
const seedColumn = fields.sourceColumn[0];
|
|
9132
|
+
if (!seedColumn) {
|
|
9133
|
+
throw new Error(
|
|
9134
|
+
`NQL binding recursive relation-column proof for '${plan.rootTable}' is missing its projected seed column.`
|
|
9135
|
+
);
|
|
9136
|
+
}
|
|
9137
|
+
const relatedAlias = this.allocateBindingRelationAlias();
|
|
9138
|
+
return buildRecursiveScalarSubquery({
|
|
9139
|
+
cteAlias: `__${relatedAlias}`,
|
|
9140
|
+
table: fields.targetTable,
|
|
9141
|
+
pkColumn: recursive.targetKeyColumn,
|
|
9142
|
+
fkColumn: recursive.selfRefColumn,
|
|
9143
|
+
outerAlias: plan.rootTable,
|
|
9144
|
+
outerSeedColumn: seedColumn,
|
|
9145
|
+
isAncestors: recursive.direction === "up",
|
|
9146
|
+
maxDepth: recursive.maxDepth,
|
|
9147
|
+
selectColumn: fields.selectedColumn,
|
|
9148
|
+
ctx: this.createHandlerContext(plan, plan.rootTable)
|
|
9149
|
+
});
|
|
9150
|
+
}
|
|
9151
|
+
compileBindingRelationColumnSubquery(fields, plan, dialectCapabilities) {
|
|
9152
|
+
if (fields.selectedColumn === void 0) {
|
|
9153
|
+
throw new Error(
|
|
9154
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
|
|
9155
|
+
);
|
|
9156
|
+
}
|
|
9157
|
+
if (fields.recursive !== void 0) {
|
|
9158
|
+
return this.compileBindingRecursiveRelationColumnSubquery(
|
|
9159
|
+
fields,
|
|
9160
|
+
plan,
|
|
9161
|
+
dialectCapabilities
|
|
9162
|
+
);
|
|
9163
|
+
}
|
|
9164
|
+
if (this.bindingRelationNameLooksRecursive(fields)) {
|
|
9165
|
+
throw new Error(
|
|
9166
|
+
`NQL binding recursive relation-column proof for '${plan.rootTable}' is missing recursive metadata.`
|
|
9167
|
+
);
|
|
9168
|
+
}
|
|
9169
|
+
const isManyToManyRelation = fields.relationType === "manyToMany" || fields.relationType === "belongsToMany";
|
|
9170
|
+
const hasCompleteManyToManyProof = this.bindingRelationHasCompleteManyToManyProof(fields);
|
|
9171
|
+
if (isManyToManyRelation && (fields.cardinality !== "many" || !hasCompleteManyToManyProof)) {
|
|
9172
|
+
if (fields.cardinality !== "many") {
|
|
9173
|
+
throw new Error(
|
|
9174
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is manyToMany but must use cardinality 'many' with complete junction proof (through, throughSourceColumn, throughTargetColumn) (ref-#192).`
|
|
9175
|
+
);
|
|
8987
9176
|
}
|
|
8988
|
-
|
|
9177
|
+
throw new Error(
|
|
9178
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is manyToMany but missing complete junction proof (through, throughSourceColumn, throughTargetColumn) (ref-#192).`
|
|
9179
|
+
);
|
|
9180
|
+
}
|
|
9181
|
+
if (fields.cardinality === "one") {
|
|
9182
|
+
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9183
|
+
if (fields.hops.length === 0 && trustedRelationHasMultipleHops(fields.relation)) {
|
|
9184
|
+
throw new Error(
|
|
9185
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is dotted but missing resolved hops.`
|
|
9186
|
+
);
|
|
9187
|
+
}
|
|
9188
|
+
if (fields.hops.length > 0) {
|
|
9189
|
+
let fromNode = relatedTable;
|
|
9190
|
+
let previousAlias = relatedAlias;
|
|
9191
|
+
for (let i = 0; i < fields.hops.length; i++) {
|
|
9192
|
+
const hop = fields.hops[i];
|
|
9193
|
+
const hopAlias = `${relatedAlias}_h${i + 1}`;
|
|
9194
|
+
const hopTable = rangeVar(
|
|
9195
|
+
hop.target,
|
|
9196
|
+
hopAlias,
|
|
9197
|
+
this.schemaForRangeVar(plan, hop.target),
|
|
9198
|
+
this.naming
|
|
9199
|
+
);
|
|
9200
|
+
fromNode = innerJoin(
|
|
9201
|
+
fromNode,
|
|
9202
|
+
hopTable,
|
|
9203
|
+
buildKeyCorrelation(
|
|
9204
|
+
hopAlias,
|
|
9205
|
+
hop.joinColumn,
|
|
9206
|
+
previousAlias,
|
|
9207
|
+
hop.fkColumn,
|
|
9208
|
+
this.createHandlerContext(plan)
|
|
9209
|
+
)
|
|
9210
|
+
);
|
|
9211
|
+
previousAlias = hopAlias;
|
|
9212
|
+
}
|
|
9213
|
+
return {
|
|
9214
|
+
SubLink: {
|
|
9215
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9216
|
+
subselect: selectStmt({
|
|
9217
|
+
targetList: [
|
|
9218
|
+
{
|
|
9219
|
+
ResTarget: {
|
|
9220
|
+
val: columnRef(
|
|
9221
|
+
fields.selectedColumn,
|
|
9222
|
+
previousAlias,
|
|
9223
|
+
void 0,
|
|
9224
|
+
this.naming
|
|
9225
|
+
)
|
|
9226
|
+
}
|
|
9227
|
+
}
|
|
9228
|
+
],
|
|
9229
|
+
from: [fromNode],
|
|
9230
|
+
where: buildKeyCorrelation(
|
|
9231
|
+
relatedAlias,
|
|
9232
|
+
fields.targetColumn,
|
|
9233
|
+
plan.rootTable,
|
|
9234
|
+
fields.sourceColumn,
|
|
9235
|
+
this.createHandlerContext(plan)
|
|
9236
|
+
)
|
|
9237
|
+
})
|
|
9238
|
+
}
|
|
9239
|
+
};
|
|
9240
|
+
}
|
|
9241
|
+
return {
|
|
9242
|
+
SubLink: {
|
|
9243
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9244
|
+
subselect: selectStmt({
|
|
9245
|
+
targetList: [{ ResTarget: { val: relatedColumn } }],
|
|
9246
|
+
from: [relatedTable],
|
|
9247
|
+
where: buildKeyCorrelation(
|
|
9248
|
+
relatedAlias,
|
|
9249
|
+
fields.targetColumn,
|
|
9250
|
+
plan.rootTable,
|
|
9251
|
+
fields.sourceColumn,
|
|
9252
|
+
this.createHandlerContext(plan)
|
|
9253
|
+
)
|
|
9254
|
+
})
|
|
9255
|
+
}
|
|
9256
|
+
};
|
|
9257
|
+
}
|
|
9258
|
+
if (fields.cardinality === "many") {
|
|
9259
|
+
if (fields.relationType !== "hasMany" && fields.relationType !== "manyToMany" && fields.relationType !== "belongsToMany") {
|
|
9260
|
+
throw new Error(
|
|
9261
|
+
`NQL binding relation-column proof for '${plan.rootTable}' has cardinality 'many' but relationType '${fields.relationType ?? "unknown"}' is not supported; only hasMany or manyToMany can be aggregated (ref-#192).`
|
|
9262
|
+
);
|
|
9263
|
+
}
|
|
9264
|
+
if ((fields.relationType === "manyToMany" || fields.relationType === "belongsToMany") && !hasCompleteManyToManyProof) {
|
|
9265
|
+
throw new Error(
|
|
9266
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is manyToMany but missing complete junction proof (through, throughSourceColumn, throughTargetColumn) (ref-#192).`
|
|
9267
|
+
);
|
|
9268
|
+
}
|
|
9269
|
+
if (dialectCapabilities?.supportsJsonAgg === false) {
|
|
9270
|
+
throw new Error(
|
|
9271
|
+
"JSON aggregation for NQL binding relation columns is not supported by this adapter"
|
|
9272
|
+
);
|
|
9273
|
+
}
|
|
9274
|
+
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9275
|
+
const junctionAlias = hasCompleteManyToManyProof ? this.allocateBindingRelationAlias() : void 0;
|
|
9276
|
+
const handlerContext = this.createHandlerContext(plan);
|
|
9277
|
+
const fromNode = hasCompleteManyToManyProof ? innerJoin(
|
|
9278
|
+
relatedTable,
|
|
9279
|
+
rangeVar(
|
|
9280
|
+
fields.through,
|
|
9281
|
+
junctionAlias,
|
|
9282
|
+
this.schemaForRangeVar(plan, fields.through),
|
|
9283
|
+
this.naming
|
|
9284
|
+
),
|
|
9285
|
+
buildKeyCorrelation(
|
|
9286
|
+
relatedAlias,
|
|
9287
|
+
fields.targetColumn,
|
|
9288
|
+
junctionAlias,
|
|
9289
|
+
[fields.throughTargetColumn],
|
|
9290
|
+
handlerContext
|
|
9291
|
+
)
|
|
9292
|
+
) : relatedTable;
|
|
9293
|
+
const whereNode = hasCompleteManyToManyProof ? buildKeyCorrelation(
|
|
9294
|
+
junctionAlias,
|
|
9295
|
+
[fields.throughSourceColumn],
|
|
9296
|
+
plan.rootTable,
|
|
9297
|
+
fields.sourceColumn,
|
|
9298
|
+
handlerContext
|
|
9299
|
+
) : buildKeyCorrelation(
|
|
9300
|
+
relatedAlias,
|
|
9301
|
+
fields.targetColumn,
|
|
9302
|
+
plan.rootTable,
|
|
9303
|
+
fields.sourceColumn,
|
|
9304
|
+
handlerContext
|
|
9305
|
+
);
|
|
9306
|
+
return {
|
|
9307
|
+
SubLink: {
|
|
9308
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9309
|
+
subselect: selectStmt({
|
|
9310
|
+
targetList: [
|
|
9311
|
+
{
|
|
9312
|
+
ResTarget: {
|
|
9313
|
+
val: coalesceExpr([
|
|
9314
|
+
funcCall("json_agg", [relatedColumn], {
|
|
9315
|
+
orderBy: [
|
|
9316
|
+
sortBy(
|
|
9317
|
+
typeCast(relatedColumn, "text"),
|
|
9318
|
+
"DEFAULT",
|
|
9319
|
+
"LAST"
|
|
9320
|
+
)
|
|
9321
|
+
]
|
|
9322
|
+
}),
|
|
9323
|
+
typeCast({ A_Const: { sval: { sval: "[]" } } }, "json")
|
|
9324
|
+
])
|
|
9325
|
+
}
|
|
9326
|
+
}
|
|
9327
|
+
],
|
|
9328
|
+
from: [fromNode],
|
|
9329
|
+
where: whereNode
|
|
9330
|
+
})
|
|
9331
|
+
}
|
|
9332
|
+
};
|
|
9333
|
+
}
|
|
9334
|
+
throw new Error(
|
|
9335
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is not scalar (cardinality: ${fields.cardinality ?? "unknown"}).`
|
|
9336
|
+
);
|
|
8989
9337
|
}
|
|
8990
9338
|
compileExpressionSubquery(query, paramOffset) {
|
|
8991
9339
|
const innerCompiler = new _PlanCompiler(this.childCompilerOptions());
|
|
@@ -9369,7 +9717,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9369
9717
|
if (trusted?.selectedColumn !== void 0) {
|
|
9370
9718
|
const node2 = this.compileBindingRelationColumnSubquery(
|
|
9371
9719
|
trusted,
|
|
9372
|
-
plan
|
|
9720
|
+
plan,
|
|
9721
|
+
this.dialectCapabilities
|
|
9373
9722
|
);
|
|
9374
9723
|
targetList.push({
|
|
9375
9724
|
ResTarget: {
|
|
@@ -10106,12 +10455,22 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10106
10455
|
registerJoinFilter(decision) {
|
|
10107
10456
|
const targetTable = decision.targetTable;
|
|
10108
10457
|
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
10458
|
const alias = targetTable === sourceTable ? decision.relationName ?? `${targetTable}_join` : void 0;
|
|
10459
|
+
const targetAlias = alias ?? targetTable;
|
|
10460
|
+
const fkColumn = decision.foreignKey ?? [
|
|
10461
|
+
this.deriveFk(targetTable, this.defaultPk)
|
|
10462
|
+
];
|
|
10463
|
+
const targetKey = decision.parentKey ?? [this.defaultPk];
|
|
10464
|
+
const onCondition = buildKeyCorrelation(
|
|
10465
|
+
targetAlias,
|
|
10466
|
+
targetKey,
|
|
10467
|
+
sourceTable,
|
|
10468
|
+
fkColumn,
|
|
10469
|
+
this.createHandlerContext({
|
|
10470
|
+
rootTable: sourceTable,
|
|
10471
|
+
decisions: []
|
|
10472
|
+
})
|
|
10473
|
+
);
|
|
10115
10474
|
this.pendingJoins.push({
|
|
10116
10475
|
type: "JOIN",
|
|
10117
10476
|
table: targetTable,
|
|
@@ -10132,19 +10491,21 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10132
10491
|
this.schemaForRangeVar(plan, decision.targetTable ?? ""),
|
|
10133
10492
|
this.naming
|
|
10134
10493
|
);
|
|
10135
|
-
const
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
)
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10494
|
+
const sourceColumn = toColumnList7(decision.sourceColumn);
|
|
10495
|
+
const targetColumn = toColumnList7(decision.targetColumn);
|
|
10496
|
+
if (sourceColumn.length === 0) {
|
|
10497
|
+
throw new Error("Missing required column 'sourceColumn' in compileJoin");
|
|
10498
|
+
}
|
|
10499
|
+
if (targetColumn.length === 0) {
|
|
10500
|
+
throw new Error("Missing required column 'targetColumn' in compileJoin");
|
|
10501
|
+
}
|
|
10502
|
+
const sourceAlias = sourceColumn.length > 1 ? plan.rootTable : "";
|
|
10503
|
+
const onCondition = buildKeyCorrelation(
|
|
10504
|
+
sourceAlias,
|
|
10505
|
+
sourceColumn,
|
|
10506
|
+
decision.alias ?? decision.targetTable ?? "",
|
|
10507
|
+
targetColumn,
|
|
10508
|
+
this.createHandlerContext(plan)
|
|
10148
10509
|
);
|
|
10149
10510
|
if (decision.joinType === "left") {
|
|
10150
10511
|
return leftJoin(baseTable, targetTable, onCondition, decision.alias);
|
|
@@ -10570,6 +10931,12 @@ function buildSequenceClause(verb, seqName, seq, includeCycleNoCycle = false) {
|
|
|
10570
10931
|
}
|
|
10571
10932
|
return `${parts.join(" ")};`;
|
|
10572
10933
|
}
|
|
10934
|
+
function failSafeUnknownDownChange(kind) {
|
|
10935
|
+
return {
|
|
10936
|
+
sql: `-- WARNING: Cannot reverse unsupported SchemaChange kind "${kind}"`,
|
|
10937
|
+
destructive: true
|
|
10938
|
+
};
|
|
10939
|
+
}
|
|
10573
10940
|
function isChangeSupported(kind, caps) {
|
|
10574
10941
|
switch (kind) {
|
|
10575
10942
|
case "create_enum":
|
|
@@ -11022,188 +11389,381 @@ function changeToUpSQL(change, schemaName) {
|
|
|
11022
11389
|
function changeToDownSQL(change, schemaName) {
|
|
11023
11390
|
switch (change.kind) {
|
|
11024
11391
|
case "create_table":
|
|
11025
|
-
return
|
|
11392
|
+
return {
|
|
11393
|
+
sql: `DROP TABLE IF EXISTS ${qualifyTable(change.table, schemaName)} CASCADE;`,
|
|
11394
|
+
destructive: true
|
|
11395
|
+
};
|
|
11026
11396
|
case "drop_table":
|
|
11027
|
-
return
|
|
11397
|
+
return {
|
|
11398
|
+
sql: `-- WARNING: Cannot reverse drop_table "${change.table}" -- table data was lost`,
|
|
11399
|
+
destructive: true
|
|
11400
|
+
};
|
|
11028
11401
|
case "add_column":
|
|
11029
|
-
return
|
|
11402
|
+
return {
|
|
11403
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP COLUMN ${quoteIdent2(change.column, "alias")} CASCADE;`,
|
|
11404
|
+
destructive: true
|
|
11405
|
+
};
|
|
11030
11406
|
case "drop_column":
|
|
11031
|
-
return
|
|
11407
|
+
return {
|
|
11408
|
+
sql: `-- WARNING: Cannot reverse drop_column "${change.table}"."${change.column}" -- column data was lost`,
|
|
11409
|
+
destructive: true
|
|
11410
|
+
};
|
|
11032
11411
|
case "alter_column_type": {
|
|
11033
11412
|
const fromType = change.meta?.fromType;
|
|
11034
11413
|
if (!fromType) {
|
|
11035
|
-
return
|
|
11414
|
+
return {
|
|
11415
|
+
sql: `-- WARNING: Cannot reverse alter_column_type "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
11416
|
+
destructive: true
|
|
11417
|
+
};
|
|
11036
11418
|
}
|
|
11037
|
-
return
|
|
11419
|
+
return {
|
|
11420
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} TYPE ${fromType};`,
|
|
11421
|
+
destructive: true
|
|
11422
|
+
};
|
|
11038
11423
|
}
|
|
11039
11424
|
case "alter_column_nullable": {
|
|
11040
11425
|
const oldNullable = change.meta?.oldNullable;
|
|
11041
11426
|
if (oldNullable === void 0) {
|
|
11042
|
-
return
|
|
11427
|
+
return {
|
|
11428
|
+
sql: `-- WARNING: Cannot reverse alter_column_nullable "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
11429
|
+
destructive: true
|
|
11430
|
+
};
|
|
11043
11431
|
}
|
|
11044
11432
|
const action = oldNullable ? "DROP NOT NULL" : "SET NOT NULL";
|
|
11045
|
-
return
|
|
11433
|
+
return {
|
|
11434
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} ${action};`,
|
|
11435
|
+
// Allowlisted: restores the recorded prior nullability metadata.
|
|
11436
|
+
destructive: false
|
|
11437
|
+
};
|
|
11046
11438
|
}
|
|
11047
11439
|
case "alter_column_default": {
|
|
11048
11440
|
const oldDefault = change.meta?.oldDefault;
|
|
11049
11441
|
if (oldDefault === void 0) {
|
|
11050
|
-
return
|
|
11442
|
+
return {
|
|
11443
|
+
sql: `-- WARNING: Cannot reverse alter_column_default "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
11444
|
+
destructive: true
|
|
11445
|
+
};
|
|
11051
11446
|
}
|
|
11052
11447
|
if (oldDefault === null) {
|
|
11053
|
-
return
|
|
11448
|
+
return {
|
|
11449
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} DROP DEFAULT;`,
|
|
11450
|
+
// Allowlisted: restores the recorded prior state of "no default".
|
|
11451
|
+
destructive: false
|
|
11452
|
+
};
|
|
11054
11453
|
}
|
|
11055
|
-
return
|
|
11454
|
+
return {
|
|
11455
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} SET DEFAULT ${formatDefault(oldDefault)};`,
|
|
11456
|
+
// Allowlisted: restores the recorded prior default value.
|
|
11457
|
+
destructive: false
|
|
11458
|
+
};
|
|
11056
11459
|
}
|
|
11057
11460
|
case "add_primary_key": {
|
|
11058
|
-
return
|
|
11461
|
+
return {
|
|
11462
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${quoteIdent2(pkName(change.table), "alias")} CASCADE;`,
|
|
11463
|
+
destructive: true
|
|
11464
|
+
};
|
|
11465
|
+
}
|
|
11466
|
+
case "drop_primary_key": {
|
|
11467
|
+
const columns = change.meta?.columns;
|
|
11468
|
+
if (Array.isArray(columns) && columns.length > 0) {
|
|
11469
|
+
const pkCols = columns.map((n) => quoteIdent2(n, "alias")).join(", ");
|
|
11470
|
+
return {
|
|
11471
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ADD CONSTRAINT ${quoteIdent2(pkName(change.table), "alias")} PRIMARY KEY (${pkCols});`,
|
|
11472
|
+
// Allowlisted: re-adds the dropped primary-key constraint from metadata.
|
|
11473
|
+
destructive: false
|
|
11474
|
+
};
|
|
11475
|
+
}
|
|
11476
|
+
return {
|
|
11477
|
+
sql: `-- WARNING: Cannot reverse drop_primary_key "${change.table}" -- columns unknown`,
|
|
11478
|
+
destructive: true
|
|
11479
|
+
};
|
|
11059
11480
|
}
|
|
11060
|
-
case "drop_primary_key":
|
|
11061
|
-
return `-- WARNING: Cannot reverse drop_primary_key "${change.table}" -- columns unknown`;
|
|
11062
11481
|
case "add_foreign_key": {
|
|
11063
11482
|
const fk = change.meta?.fk;
|
|
11064
|
-
if (!fk) return void 0;
|
|
11483
|
+
if (!fk) return { sql: void 0, destructive: true };
|
|
11065
11484
|
const constraintName = quoteIdent2(
|
|
11066
11485
|
fkName(change.table, fk.columns),
|
|
11067
11486
|
"alias"
|
|
11068
11487
|
);
|
|
11069
|
-
return
|
|
11488
|
+
return {
|
|
11489
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${constraintName} CASCADE;`,
|
|
11490
|
+
destructive: true
|
|
11491
|
+
};
|
|
11492
|
+
}
|
|
11493
|
+
case "drop_foreign_key": {
|
|
11494
|
+
const fk = change.meta?.fk;
|
|
11495
|
+
if (!fk) {
|
|
11496
|
+
return {
|
|
11497
|
+
sql: `-- WARNING: Cannot reverse drop_foreign_key "${change.table}" -- FK definition was lost`,
|
|
11498
|
+
destructive: true
|
|
11499
|
+
};
|
|
11500
|
+
}
|
|
11501
|
+
return {
|
|
11502
|
+
sql: generateAddFKSQL(change.table, fk, schemaName),
|
|
11503
|
+
// Allowlisted: re-adds the dropped foreign-key constraint from metadata.
|
|
11504
|
+
destructive: false
|
|
11505
|
+
};
|
|
11070
11506
|
}
|
|
11071
|
-
case "drop_foreign_key":
|
|
11072
|
-
return `-- WARNING: Cannot reverse drop_foreign_key "${change.table}" -- FK definition was lost`;
|
|
11073
11507
|
case "alter_foreign_key": {
|
|
11074
11508
|
const oldFk = change.meta?.oldFk;
|
|
11075
|
-
if (!oldFk) {
|
|
11076
|
-
return `-- WARNING: Cannot reverse alter_foreign_key "${change.table}" -- missing migration metadata`;
|
|
11077
|
-
}
|
|
11078
11509
|
const fk = change.meta?.fk;
|
|
11510
|
+
if (!oldFk || !fk) {
|
|
11511
|
+
return {
|
|
11512
|
+
sql: `-- WARNING: Cannot reverse alter_foreign_key "${change.table}" -- missing migration metadata`,
|
|
11513
|
+
destructive: true
|
|
11514
|
+
};
|
|
11515
|
+
}
|
|
11079
11516
|
const constraintName = quoteIdent2(
|
|
11080
11517
|
fkName(change.table, fk.columns),
|
|
11081
11518
|
"alias"
|
|
11082
11519
|
);
|
|
11083
11520
|
const drop = `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${constraintName};`;
|
|
11084
11521
|
const add = generateAddFKSQL(change.table, oldFk, schemaName);
|
|
11085
|
-
return `${drop}
|
|
11086
|
-
${add}
|
|
11522
|
+
return { sql: `${drop}
|
|
11523
|
+
${add}`, destructive: false };
|
|
11087
11524
|
}
|
|
11088
11525
|
case "create_index": {
|
|
11089
11526
|
const idx = change.meta?.index;
|
|
11090
|
-
if (!idx) return void 0;
|
|
11527
|
+
if (!idx) return { sql: void 0, destructive: true };
|
|
11091
11528
|
const indexName = quoteIdent2(
|
|
11092
11529
|
idxName(change.table, idx.columns, idx.name),
|
|
11093
11530
|
"alias"
|
|
11094
11531
|
);
|
|
11095
11532
|
const schemaPrefix = schemaName ? `${quoteIdent2(schemaName, "alias")}.` : "";
|
|
11096
|
-
return
|
|
11533
|
+
return {
|
|
11534
|
+
sql: `DROP INDEX IF EXISTS ${schemaPrefix}${indexName};`,
|
|
11535
|
+
destructive: true
|
|
11536
|
+
};
|
|
11537
|
+
}
|
|
11538
|
+
case "drop_index": {
|
|
11539
|
+
const idx = change.meta?.index;
|
|
11540
|
+
if (!idx) {
|
|
11541
|
+
return {
|
|
11542
|
+
sql: `-- WARNING: Cannot reverse drop_index "${change.table}" -- index definition was lost`,
|
|
11543
|
+
destructive: true
|
|
11544
|
+
};
|
|
11545
|
+
}
|
|
11546
|
+
return {
|
|
11547
|
+
sql: upCreateIndex(change, schemaName),
|
|
11548
|
+
// Allowlisted: re-creates the dropped index from metadata.
|
|
11549
|
+
destructive: false
|
|
11550
|
+
};
|
|
11097
11551
|
}
|
|
11098
|
-
case "drop_index":
|
|
11099
|
-
return `-- WARNING: Cannot reverse drop_index "${change.table}" -- index definition was lost`;
|
|
11100
11552
|
case "add_check_constraint": {
|
|
11101
11553
|
const check = change.meta?.check;
|
|
11102
|
-
if (!check) return void 0;
|
|
11103
|
-
return
|
|
11554
|
+
if (!check) return { sql: void 0, destructive: true };
|
|
11555
|
+
return {
|
|
11556
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${quoteIdent2(check.name, "alias")};`,
|
|
11557
|
+
destructive: true
|
|
11558
|
+
};
|
|
11104
11559
|
}
|
|
11105
11560
|
case "drop_check_constraint": {
|
|
11106
11561
|
const check = change.meta?.check;
|
|
11107
|
-
if (!check) return void 0;
|
|
11562
|
+
if (!check) return { sql: void 0, destructive: true };
|
|
11108
11563
|
validateSqlExpression(
|
|
11109
11564
|
check.expression,
|
|
11110
11565
|
"migration check constraint (down)"
|
|
11111
11566
|
);
|
|
11112
|
-
return
|
|
11567
|
+
return {
|
|
11568
|
+
sql: "DO $$ BEGIN ALTER TABLE " + qualifyTable(change.table, schemaName) + " ADD CONSTRAINT " + quoteIdent2(check.name, "alias") + " " + check.expression + "; EXCEPTION WHEN duplicate_object THEN NULL; END $$;",
|
|
11569
|
+
// Allowlisted: re-adds the dropped CHECK constraint from metadata.
|
|
11570
|
+
destructive: false
|
|
11571
|
+
};
|
|
11113
11572
|
}
|
|
11114
11573
|
case "create_enum": {
|
|
11115
11574
|
const enumDef = change.meta?.enum;
|
|
11116
|
-
if (!enumDef) return void 0;
|
|
11575
|
+
if (!enumDef) return { sql: void 0, destructive: true };
|
|
11117
11576
|
const enumName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(enumDef.name, "alias")}` : quoteIdent2(enumDef.name, "alias");
|
|
11118
|
-
return
|
|
11577
|
+
return {
|
|
11578
|
+
sql: `DROP TYPE IF EXISTS ${enumName} CASCADE;`,
|
|
11579
|
+
destructive: true
|
|
11580
|
+
};
|
|
11119
11581
|
}
|
|
11120
11582
|
case "drop_enum": {
|
|
11121
11583
|
const enumDef = change.meta?.enum;
|
|
11122
|
-
if (!enumDef) return void 0;
|
|
11584
|
+
if (!enumDef) return { sql: void 0, destructive: true };
|
|
11123
11585
|
const enumName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(enumDef.name, "alias")}` : quoteIdent2(enumDef.name, "alias");
|
|
11124
11586
|
const values = enumDef.values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
11125
|
-
return
|
|
11587
|
+
return {
|
|
11588
|
+
sql: `CREATE TYPE ${enumName} AS ENUM (${values});`,
|
|
11589
|
+
// Allowlisted: re-creates the dropped enum type from metadata.
|
|
11590
|
+
destructive: false
|
|
11591
|
+
};
|
|
11126
11592
|
}
|
|
11127
11593
|
case "alter_enum_add_value":
|
|
11128
|
-
return
|
|
11594
|
+
return {
|
|
11595
|
+
sql: `-- ALTER TYPE ADD VALUE cannot be reversed in PostgreSQL`,
|
|
11596
|
+
destructive: true
|
|
11597
|
+
};
|
|
11129
11598
|
case "alter_column_collation": {
|
|
11130
|
-
return
|
|
11599
|
+
return {
|
|
11600
|
+
sql: `-- WARNING: Cannot reverse alter_column_collation "${change.table}"."${change.column}" -- previous collation unknown`,
|
|
11601
|
+
destructive: true
|
|
11602
|
+
};
|
|
11131
11603
|
}
|
|
11132
11604
|
case "alter_column_identity": {
|
|
11133
11605
|
const col = change.meta?.column;
|
|
11134
11606
|
const prevIdentity = change.meta?.previousIdentity;
|
|
11135
|
-
if (!col) return void 0;
|
|
11607
|
+
if (!col) return { sql: void 0, destructive: true };
|
|
11136
11608
|
const table = qualifyTable(change.table, schemaName);
|
|
11137
11609
|
const column = quoteIdent2(change.column, "alias");
|
|
11138
11610
|
if (prevIdentity && !col.identity) {
|
|
11139
11611
|
const gen2 = prevIdentity === "always" ? "ALWAYS" : "BY DEFAULT";
|
|
11140
|
-
return
|
|
11612
|
+
return {
|
|
11613
|
+
sql: `ALTER TABLE ${table} ALTER COLUMN ${column} ADD GENERATED ${gen2} AS IDENTITY;`,
|
|
11614
|
+
// Allowlisted: re-adds the previously recorded identity metadata.
|
|
11615
|
+
destructive: false
|
|
11616
|
+
};
|
|
11141
11617
|
}
|
|
11142
11618
|
if (!prevIdentity && col.identity) {
|
|
11143
|
-
return
|
|
11619
|
+
return {
|
|
11620
|
+
sql: `ALTER TABLE ${table} ALTER COLUMN ${column} DROP IDENTITY IF EXISTS;`,
|
|
11621
|
+
destructive: true
|
|
11622
|
+
};
|
|
11623
|
+
}
|
|
11624
|
+
if (!prevIdentity && !col.identity) {
|
|
11625
|
+
return {
|
|
11626
|
+
sql: `-- WARNING: Cannot reverse alter_column_identity "${change.table}"."${change.column}" -- missing previous identity metadata`,
|
|
11627
|
+
destructive: true
|
|
11628
|
+
};
|
|
11144
11629
|
}
|
|
11145
11630
|
const gen = prevIdentity === "always" ? "ALWAYS" : "BY DEFAULT";
|
|
11146
|
-
return
|
|
11631
|
+
return {
|
|
11632
|
+
sql: `ALTER TABLE ${table} ALTER COLUMN ${column} SET GENERATED ${gen};`,
|
|
11633
|
+
// Allowlisted: restores the recorded prior identity generation mode.
|
|
11634
|
+
destructive: false
|
|
11635
|
+
};
|
|
11147
11636
|
}
|
|
11148
11637
|
case "add_comment": {
|
|
11149
11638
|
const target = change.meta?.target;
|
|
11150
11639
|
if (target === "table") {
|
|
11151
|
-
return
|
|
11640
|
+
return {
|
|
11641
|
+
sql: `COMMENT ON TABLE ${qualifyTable(change.table, schemaName)} IS NULL;`,
|
|
11642
|
+
destructive: true
|
|
11643
|
+
};
|
|
11152
11644
|
}
|
|
11153
|
-
return
|
|
11645
|
+
return {
|
|
11646
|
+
sql: `COMMENT ON COLUMN ${qualifyTable(change.table, schemaName)}.${quoteIdent2(change.column, "alias")} IS NULL;`,
|
|
11647
|
+
destructive: true
|
|
11648
|
+
};
|
|
11154
11649
|
}
|
|
11155
11650
|
case "drop_comment": {
|
|
11156
|
-
|
|
11651
|
+
const target = change.meta?.target;
|
|
11652
|
+
const comment = change.meta?.comment;
|
|
11653
|
+
if (typeof comment === "string") {
|
|
11654
|
+
const escaped = comment.replace(/'/g, "''");
|
|
11655
|
+
if (target === "table") {
|
|
11656
|
+
return {
|
|
11657
|
+
sql: `COMMENT ON TABLE ${qualifyTable(change.table, schemaName)} IS '${escaped}';`,
|
|
11658
|
+
// Allowlisted: re-adds the dropped table comment from metadata.
|
|
11659
|
+
destructive: false
|
|
11660
|
+
};
|
|
11661
|
+
}
|
|
11662
|
+
if (target === "column") {
|
|
11663
|
+
return {
|
|
11664
|
+
sql: `COMMENT ON COLUMN ${qualifyTable(change.table, schemaName)}.${quoteIdent2(change.column, "alias")} IS '${escaped}';`,
|
|
11665
|
+
// Allowlisted: re-adds the dropped column comment from metadata.
|
|
11666
|
+
destructive: false
|
|
11667
|
+
};
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
return {
|
|
11671
|
+
sql: `-- WARNING: Cannot reverse drop_comment "${change.table}"${change.column ? `."${change.column}"` : ""} -- comment text was lost`,
|
|
11672
|
+
destructive: true
|
|
11673
|
+
};
|
|
11157
11674
|
}
|
|
11158
11675
|
case "create_extension": {
|
|
11159
11676
|
const ext = change.meta?.extension;
|
|
11160
|
-
if (ext == null) return void 0;
|
|
11161
|
-
return
|
|
11677
|
+
if (ext == null) return { sql: void 0, destructive: true };
|
|
11678
|
+
return {
|
|
11679
|
+
sql: `DROP EXTENSION IF EXISTS ${quoteExtensionName(ext)} CASCADE;`,
|
|
11680
|
+
destructive: true
|
|
11681
|
+
};
|
|
11162
11682
|
}
|
|
11163
11683
|
case "drop_extension": {
|
|
11164
11684
|
const ext = change.meta?.extension;
|
|
11165
|
-
if (ext == null) return void 0;
|
|
11166
|
-
return
|
|
11685
|
+
if (ext == null) return { sql: void 0, destructive: true };
|
|
11686
|
+
return {
|
|
11687
|
+
sql: `CREATE EXTENSION IF NOT EXISTS ${quoteExtensionName(ext)};`,
|
|
11688
|
+
// Allowlisted: re-creates the dropped extension from metadata.
|
|
11689
|
+
destructive: false
|
|
11690
|
+
};
|
|
11167
11691
|
}
|
|
11168
11692
|
case "create_sequence": {
|
|
11169
11693
|
const seq = change.meta?.sequence;
|
|
11170
|
-
if (!seq) return void 0;
|
|
11694
|
+
if (!seq) return { sql: void 0, destructive: true };
|
|
11171
11695
|
const seqName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(seq.name, "alias")}` : quoteIdent2(seq.name, "alias");
|
|
11172
|
-
return
|
|
11696
|
+
return {
|
|
11697
|
+
sql: `DROP SEQUENCE IF EXISTS ${seqName} CASCADE;`,
|
|
11698
|
+
destructive: true
|
|
11699
|
+
};
|
|
11173
11700
|
}
|
|
11174
11701
|
case "alter_sequence": {
|
|
11175
11702
|
const prevSeq = change.meta?.previousSequence;
|
|
11176
11703
|
if (!prevSeq) {
|
|
11177
|
-
return
|
|
11704
|
+
return {
|
|
11705
|
+
sql: `-- WARNING: Cannot reverse alter_sequence "${change.table}" -- missing migration metadata`,
|
|
11706
|
+
destructive: true
|
|
11707
|
+
};
|
|
11178
11708
|
}
|
|
11179
11709
|
const seqName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(prevSeq.name, "alias")}` : quoteIdent2(prevSeq.name, "alias");
|
|
11180
|
-
return
|
|
11710
|
+
return {
|
|
11711
|
+
sql: buildSequenceClause("ALTER SEQUENCE", seqName, prevSeq, true),
|
|
11712
|
+
// Allowlisted: restores the recorded prior sequence metadata.
|
|
11713
|
+
destructive: false
|
|
11714
|
+
};
|
|
11181
11715
|
}
|
|
11182
11716
|
case "drop_sequence": {
|
|
11183
11717
|
const seq = change.meta?.sequence;
|
|
11184
|
-
if (!seq) return void 0;
|
|
11718
|
+
if (!seq) return { sql: void 0, destructive: true };
|
|
11185
11719
|
const seqName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(seq.name, "alias")}` : quoteIdent2(seq.name, "alias");
|
|
11186
|
-
return
|
|
11720
|
+
return {
|
|
11721
|
+
sql: buildSequenceClause("CREATE SEQUENCE", seqName, seq),
|
|
11722
|
+
// Allowlisted: re-creates the dropped sequence from metadata.
|
|
11723
|
+
destructive: false
|
|
11724
|
+
};
|
|
11187
11725
|
}
|
|
11188
11726
|
case "validate_constraint":
|
|
11189
|
-
return
|
|
11727
|
+
return {
|
|
11728
|
+
sql: `-- VALIDATE CONSTRAINT cannot be reversed in PostgreSQL (table: "${change.table}")`,
|
|
11729
|
+
destructive: true
|
|
11730
|
+
};
|
|
11190
11731
|
case "enable_rls":
|
|
11191
|
-
return
|
|
11732
|
+
return {
|
|
11733
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DISABLE ROW LEVEL SECURITY;`,
|
|
11734
|
+
destructive: true
|
|
11735
|
+
};
|
|
11192
11736
|
case "disable_rls":
|
|
11193
|
-
return
|
|
11737
|
+
return {
|
|
11738
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ENABLE ROW LEVEL SECURITY;`,
|
|
11739
|
+
// Allowlisted: re-enables the previously present RLS security control.
|
|
11740
|
+
destructive: false
|
|
11741
|
+
};
|
|
11194
11742
|
case "create_policy": {
|
|
11195
11743
|
const policy = change.meta?.policy;
|
|
11196
|
-
if (!policy) return void 0;
|
|
11197
|
-
return
|
|
11744
|
+
if (!policy) return { sql: void 0, destructive: true };
|
|
11745
|
+
return {
|
|
11746
|
+
sql: `DROP POLICY IF EXISTS ${quoteIdent2(policy.name, "alias")} ON ${qualifyTable(change.table, schemaName)};`,
|
|
11747
|
+
destructive: true
|
|
11748
|
+
};
|
|
11198
11749
|
}
|
|
11199
11750
|
case "drop_policy": {
|
|
11200
11751
|
const policy = change.meta?.policy;
|
|
11201
|
-
if (!policy) return void 0;
|
|
11202
|
-
return
|
|
11752
|
+
if (!policy) return { sql: void 0, destructive: true };
|
|
11753
|
+
return {
|
|
11754
|
+
sql: buildPolicySQL(change.table, policy, schemaName),
|
|
11755
|
+
// Allowlisted: re-creates the dropped RLS policy from metadata.
|
|
11756
|
+
destructive: false
|
|
11757
|
+
};
|
|
11203
11758
|
}
|
|
11759
|
+
default:
|
|
11760
|
+
return failSafeUnknownDownChange(change.kind);
|
|
11204
11761
|
}
|
|
11205
11762
|
}
|
|
11206
11763
|
function generateDownSQL(diff, options) {
|
|
11764
|
+
return generateDownMigrationSQL(diff, options).statements;
|
|
11765
|
+
}
|
|
11766
|
+
function generateDownMigrationSQL(diff, options) {
|
|
11207
11767
|
const schemaName = options?.schemaName;
|
|
11208
11768
|
const includeDestructive = options?.includeDestructive ?? true;
|
|
11209
11769
|
const changes = includeDestructive ? diff.changes : diff.changes.filter((c) => !c.destructive);
|
|
@@ -11252,13 +11812,15 @@ function generateDownSQL(diff, options) {
|
|
|
11252
11812
|
phases[phase].push(change);
|
|
11253
11813
|
}
|
|
11254
11814
|
const statements = [];
|
|
11815
|
+
let destructive = false;
|
|
11255
11816
|
for (let i = phases.length - 1; i >= 0; i--) {
|
|
11256
11817
|
for (const change of phases[i]) {
|
|
11257
|
-
const
|
|
11258
|
-
|
|
11818
|
+
const down = changeToDownSQL(change, schemaName);
|
|
11819
|
+
destructive = down.destructive || destructive;
|
|
11820
|
+
if (down.sql) statements.push(down.sql);
|
|
11259
11821
|
}
|
|
11260
11822
|
}
|
|
11261
|
-
return statements;
|
|
11823
|
+
return { statements, destructive };
|
|
11262
11824
|
}
|
|
11263
11825
|
function generateCreateTableSQL(table, schemaName) {
|
|
11264
11826
|
const qualTable = qualifyTable(table.name, schemaName);
|
|
@@ -11551,29 +12113,36 @@ function generateCreatePolicy(tableName, policy, schemaName, naming) {
|
|
|
11551
12113
|
// src/ddl/migration-file.ts
|
|
11552
12114
|
function generateMigrationFile(diff, options) {
|
|
11553
12115
|
const upStatements = generateMigrationSQL(diff, options);
|
|
11554
|
-
const
|
|
12116
|
+
const downMigration = generateDownMigrationSQL(diff, options);
|
|
12117
|
+
const downStatements = downMigration.statements;
|
|
12118
|
+
const destructiveHeader = `-- dbsp:destructive: ${downMigration.destructive ? "true" : "false"}`;
|
|
11555
12119
|
const header = options?.name ? `-- Migration: ${options.name}
|
|
11556
12120
|
-- Generated by: dbsp migrate dev
|
|
11557
12121
|
-- Date: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
12122
|
+
${destructiveHeader}
|
|
11558
12123
|
|
|
11559
|
-
` :
|
|
12124
|
+
` : `${destructiveHeader}
|
|
12125
|
+
|
|
12126
|
+
`;
|
|
11560
12127
|
const upSection = upStatements.length > 0 ? `${upStatements.join(";\n\n")};
|
|
11561
12128
|
` : "";
|
|
11562
12129
|
const downSection = downStatements.length > 0 ? `${downStatements.join(";\n\n")};
|
|
11563
12130
|
` : "";
|
|
11564
|
-
|
|
11565
|
-
-- DOWN
|
|
12131
|
+
const separatorPrefix = upSection.length > 0 ? "\n" : "";
|
|
12132
|
+
return `${header}${upSection}${separatorPrefix}-- DOWN
|
|
11566
12133
|
|
|
11567
12134
|
${downSection}`;
|
|
11568
12135
|
}
|
|
11569
12136
|
function parseMigrationFile(content) {
|
|
12137
|
+
const destructive = parseDestructiveHeader(content);
|
|
11570
12138
|
const separatorRegex = /^\s*-- DOWN\s*$/m;
|
|
11571
12139
|
const match = separatorRegex.exec(content);
|
|
11572
12140
|
if (!match) {
|
|
11573
12141
|
return {
|
|
11574
12142
|
upStatements: splitStatements(content),
|
|
11575
12143
|
downStatements: [],
|
|
11576
|
-
hasDown: false
|
|
12144
|
+
hasDown: false,
|
|
12145
|
+
destructive
|
|
11577
12146
|
};
|
|
11578
12147
|
}
|
|
11579
12148
|
const upContent = content.slice(0, match.index);
|
|
@@ -11581,9 +12150,36 @@ function parseMigrationFile(content) {
|
|
|
11581
12150
|
return {
|
|
11582
12151
|
upStatements: splitStatements(upContent),
|
|
11583
12152
|
downStatements: splitStatements(downContent),
|
|
11584
|
-
hasDown: true
|
|
12153
|
+
hasDown: true,
|
|
12154
|
+
destructive
|
|
11585
12155
|
};
|
|
11586
12156
|
}
|
|
12157
|
+
function parseDestructiveHeader(content) {
|
|
12158
|
+
let destructive;
|
|
12159
|
+
let found = false;
|
|
12160
|
+
for (const line of content.split(/\r?\n/)) {
|
|
12161
|
+
const trimmed = line.trim();
|
|
12162
|
+
if (trimmed.length === 0) {
|
|
12163
|
+
continue;
|
|
12164
|
+
}
|
|
12165
|
+
if (/^--\s*DOWN\s*$/i.test(trimmed)) {
|
|
12166
|
+
break;
|
|
12167
|
+
}
|
|
12168
|
+
if (!trimmed.startsWith("--")) {
|
|
12169
|
+
break;
|
|
12170
|
+
}
|
|
12171
|
+
const match = /^--\s*dbsp:destructive:\s*(true|false)\s*$/i.exec(trimmed);
|
|
12172
|
+
if (!match) {
|
|
12173
|
+
continue;
|
|
12174
|
+
}
|
|
12175
|
+
if (found) {
|
|
12176
|
+
return void 0;
|
|
12177
|
+
}
|
|
12178
|
+
found = true;
|
|
12179
|
+
destructive = match[1]?.toLowerCase() === "true";
|
|
12180
|
+
}
|
|
12181
|
+
return found ? destructive : void 0;
|
|
12182
|
+
}
|
|
11587
12183
|
function isDestructiveDown(downStatements) {
|
|
11588
12184
|
const destructivePatterns = [
|
|
11589
12185
|
/DROP\s+TABLE/i,
|
|
@@ -12706,6 +13302,7 @@ function vectorDims(column) {
|
|
|
12706
13302
|
// src/introspection.ts
|
|
12707
13303
|
init_assert_field();
|
|
12708
13304
|
import { ModelIRImpl } from "@dbsp/core";
|
|
13305
|
+
import { buildRelationKeyFields } from "@dbsp/types";
|
|
12709
13306
|
function parseExpressionsList(raw) {
|
|
12710
13307
|
const results = [];
|
|
12711
13308
|
let depth = 0;
|
|
@@ -13261,7 +13858,10 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13261
13858
|
for (const [, fk] of fksByConstraint) {
|
|
13262
13859
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
13263
13860
|
const belongsToName = deriveRelationName(fk.cols[0], fk.target);
|
|
13264
|
-
const
|
|
13861
|
+
const relationFk = {
|
|
13862
|
+
columns: fk.cols,
|
|
13863
|
+
references: { table: fk.target, columns: fk.refs }
|
|
13864
|
+
};
|
|
13265
13865
|
const belongsToKey = `${fk.source}.${belongsToName}`;
|
|
13266
13866
|
if (!relations.has(belongsToKey)) {
|
|
13267
13867
|
relations.set(belongsToKey, {
|
|
@@ -13269,7 +13869,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13269
13869
|
type: "belongsTo",
|
|
13270
13870
|
source: fk.source,
|
|
13271
13871
|
target: fk.target,
|
|
13272
|
-
|
|
13872
|
+
...buildRelationKeyFields(relationFk, "belongsTo"),
|
|
13273
13873
|
cardinality: "one",
|
|
13274
13874
|
optionality: "optional",
|
|
13275
13875
|
includeStrategy: "auto",
|
|
@@ -13285,7 +13885,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13285
13885
|
type: "hasMany",
|
|
13286
13886
|
source: fk.target,
|
|
13287
13887
|
target: fk.source,
|
|
13288
|
-
|
|
13888
|
+
...buildRelationKeyFields(relationFk, "inverse"),
|
|
13289
13889
|
cardinality: "many",
|
|
13290
13890
|
optionality: "optional",
|
|
13291
13891
|
includeStrategy: "auto",
|
|
@@ -14188,6 +14788,7 @@ import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNql
|
|
|
14188
14788
|
|
|
14189
14789
|
// src/adapter-compiler-includes.ts
|
|
14190
14790
|
init_ast_helpers();
|
|
14791
|
+
import { toColumnList as toColumnList8 } from "@dbsp/types";
|
|
14191
14792
|
init_handlers();
|
|
14192
14793
|
function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
14193
14794
|
const schemaName = deps.schemaName;
|
|
@@ -14199,7 +14800,12 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14199
14800
|
parameters: []
|
|
14200
14801
|
};
|
|
14201
14802
|
}
|
|
14202
|
-
const fkColumns =
|
|
14803
|
+
const fkColumns = toColumnList8(info.foreignKey);
|
|
14804
|
+
if (fkColumns.length === 0) {
|
|
14805
|
+
throw new Error(
|
|
14806
|
+
"Subquery include requires at least one foreignKey column."
|
|
14807
|
+
);
|
|
14808
|
+
}
|
|
14203
14809
|
if (info.through && info.throughSourceKey && info.throughTargetKey) {
|
|
14204
14810
|
return compileSubqueryIncludeManyToMany(
|
|
14205
14811
|
info,
|
|
@@ -14247,6 +14853,11 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14247
14853
|
};
|
|
14248
14854
|
} else {
|
|
14249
14855
|
const conditions = parentIds.map((id) => {
|
|
14856
|
+
if (!Array.isArray(id) || id.length !== fkColumns.length) {
|
|
14857
|
+
throw new Error(
|
|
14858
|
+
`Subquery include composite key parameter width (${Array.isArray(id) ? id.length : 1}) must match foreignKey width (${fkColumns.length}).`
|
|
14859
|
+
);
|
|
14860
|
+
}
|
|
14250
14861
|
const idValues = id;
|
|
14251
14862
|
const colConditions = fkColumns.map((col, idx) => {
|
|
14252
14863
|
state.parameters.push(idValues[idx]);
|
|
@@ -14287,7 +14898,13 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
|
|
|
14287
14898
|
const throughTable = info.through;
|
|
14288
14899
|
const throughSourceKey = info.throughSourceKey;
|
|
14289
14900
|
const throughTargetKey = info.throughTargetKey;
|
|
14290
|
-
const
|
|
14901
|
+
const targetPkColumns = toColumnList8(info.sourceKey);
|
|
14902
|
+
if (targetPkColumns.length !== 1) {
|
|
14903
|
+
throw new Error(
|
|
14904
|
+
`Many-to-many subquery include requires a single-column target key; got ${JSON.stringify(targetPkColumns)}.`
|
|
14905
|
+
);
|
|
14906
|
+
}
|
|
14907
|
+
const targetPk = targetPkColumns[0];
|
|
14291
14908
|
const paramRefs = parentIds.map((id) => {
|
|
14292
14909
|
state.parameters.push(id);
|
|
14293
14910
|
state.paramIndex++;
|
|
@@ -14376,6 +14993,7 @@ import {
|
|
|
14376
14993
|
POSTGRESQL_CAPABILITIES,
|
|
14377
14994
|
plan as planFn
|
|
14378
14995
|
} from "@dbsp/core";
|
|
14996
|
+
import { toColumnList as toColumnList11 } from "@dbsp/types";
|
|
14379
14997
|
|
|
14380
14998
|
// src/adapter-compiler-select.ts
|
|
14381
14999
|
init_assert_field();
|
|
@@ -14383,6 +15001,7 @@ init_ast_helpers();
|
|
|
14383
15001
|
init_binding_registry();
|
|
14384
15002
|
init_compile_where();
|
|
14385
15003
|
import { countDistinctRelationPathsByName } from "@dbsp/core";
|
|
15004
|
+
import { toColumnList as toColumnList10 } from "@dbsp/types";
|
|
14386
15005
|
init_compiler_utils();
|
|
14387
15006
|
init_types();
|
|
14388
15007
|
init_intent_to_decisions();
|
|
@@ -14392,6 +15011,7 @@ init_param_ref();
|
|
|
14392
15011
|
init_assert_field();
|
|
14393
15012
|
init_intent_to_decisions();
|
|
14394
15013
|
import { deriveRelationPathFromIntentPath } from "@dbsp/core";
|
|
15014
|
+
import { toColumnList as toColumnList9 } from "@dbsp/types";
|
|
14395
15015
|
function findExistsIntents(where) {
|
|
14396
15016
|
if (!where || typeof where !== "object") return [];
|
|
14397
15017
|
const w = where;
|
|
@@ -14412,7 +15032,8 @@ function findExistsIntents(where) {
|
|
|
14412
15032
|
function resolveRelation(model, sourceTable, relationName) {
|
|
14413
15033
|
const rel = model.getRelation(`${sourceTable}.${relationName}`);
|
|
14414
15034
|
if (!rel) return void 0;
|
|
14415
|
-
const
|
|
15035
|
+
const foreignKeyColumns = toColumnList9(rel.foreignKey);
|
|
15036
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14416
15037
|
const relationType = rel.type;
|
|
14417
15038
|
return { target: rel.target, foreignKey, relationType };
|
|
14418
15039
|
}
|
|
@@ -14635,7 +15256,8 @@ function buildMultiHopExistsChain(hops, rootTable, outerOperator, innerCondition
|
|
|
14635
15256
|
const hopRelations = [];
|
|
14636
15257
|
for (const hop of hops) {
|
|
14637
15258
|
const rel = model.getRelation(`${currentSource}.${hop}`);
|
|
14638
|
-
const
|
|
15259
|
+
const foreignKeyColumns = rel ? toColumnList9(rel.foreignKey) : [];
|
|
15260
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14639
15261
|
const relationType = rel?.type === "belongsTo" ? "belongsTo" : rel?.type === "hasMany" || rel?.type === "hasOne" ? rel.type : void 0;
|
|
14640
15262
|
const parentKey = relationType === "belongsTo" ? rel?.targetKey : rel?.sourceKey;
|
|
14641
15263
|
const target = rel ? rel.target : hop;
|
|
@@ -14745,7 +15367,8 @@ function enrichExistsStubsInConditions(conditions, sourceTable, model) {
|
|
|
14745
15367
|
`exists('${relation}'): no relation '${relation}' is declared on table '${sourceTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
14746
15368
|
);
|
|
14747
15369
|
}
|
|
14748
|
-
const
|
|
15370
|
+
const foreignKeyColumns = toColumnList9(rel.foreignKey);
|
|
15371
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14749
15372
|
const relationType = rel.type === "belongsTo" ? "belongsTo" : rel.type === "hasMany" || rel.type === "hasOne" ? rel.type : void 0;
|
|
14750
15373
|
const parentKey = relationType === "belongsTo" ? rel.targetKey : rel.sourceKey;
|
|
14751
15374
|
const targetTable = rel.target;
|
|
@@ -14804,7 +15427,8 @@ function buildEnrichedExistsDecision(d, matchingIntent, rootTable, model) {
|
|
|
14804
15427
|
const relIR = model && context.relation ? model.getRelation(
|
|
14805
15428
|
`${sourceTableForRelation}.${context.relation}`
|
|
14806
15429
|
) : void 0;
|
|
14807
|
-
const
|
|
15430
|
+
const foreignKeyColumns = relIR ? toColumnList9(relIR.foreignKey) : [];
|
|
15431
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14808
15432
|
const relationType = relIR?.type === "belongsTo" ? "belongsTo" : relIR?.type === "hasMany" || relIR?.type === "hasOne" ? relIR.type : void 0;
|
|
14809
15433
|
const parentKey = relationType === "belongsTo" ? relIR?.targetKey : relIR?.sourceKey;
|
|
14810
15434
|
let operator = "exists";
|
|
@@ -15117,6 +15741,7 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
15117
15741
|
const relationName = resolveIncludeAlias(context);
|
|
15118
15742
|
if (!context.target || !relationName) return void 0;
|
|
15119
15743
|
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15744
|
+
const parentKey = context.parentKey ?? defaultPk;
|
|
15120
15745
|
const relationType = context.relationType;
|
|
15121
15746
|
const effectiveChoice = choice === "subquery" ? "json_agg" : choice;
|
|
15122
15747
|
const includeIntent = resolveIncludeByPath(
|
|
@@ -15137,8 +15762,10 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
15137
15762
|
targetTable: context.target,
|
|
15138
15763
|
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15139
15764
|
...relationType && { relationType },
|
|
15140
|
-
foreignKey
|
|
15141
|
-
parentKey
|
|
15765
|
+
foreignKey,
|
|
15766
|
+
parentKey,
|
|
15767
|
+
...context.targetOrderKey && context.targetOrderKey.length > 0 ? { orderBy: context.targetOrderKey } : {},
|
|
15768
|
+
...context.orderByFallback ? { orderByFallback: true } : {},
|
|
15142
15769
|
...context.intentPath && { intentPath: context.intentPath },
|
|
15143
15770
|
...limit != null && { limit }
|
|
15144
15771
|
};
|
|
@@ -15164,6 +15791,7 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
|
|
|
15164
15791
|
columns = [defaultPk, ...fields];
|
|
15165
15792
|
}
|
|
15166
15793
|
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15794
|
+
const parentKey = context.parentKey ?? defaultPk;
|
|
15167
15795
|
const relationType = context.relationType;
|
|
15168
15796
|
let conditions;
|
|
15169
15797
|
if (includeIntent?.where) {
|
|
@@ -15184,8 +15812,8 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
|
|
|
15184
15812
|
targetTable: context.target,
|
|
15185
15813
|
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15186
15814
|
...relationType && { relationType },
|
|
15187
|
-
foreignKey
|
|
15188
|
-
parentKey
|
|
15815
|
+
foreignKey,
|
|
15816
|
+
parentKey,
|
|
15189
15817
|
columns,
|
|
15190
15818
|
...joinType && { joinType },
|
|
15191
15819
|
...conditions && { conditions }
|
|
@@ -15208,10 +15836,13 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15208
15836
|
(r) => r.name === alias || snakeToCamel(r.name) === alias
|
|
15209
15837
|
);
|
|
15210
15838
|
if (!rel) continue;
|
|
15211
|
-
const
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15839
|
+
const rawFkColumns = toColumnList9(rel.foreignKey);
|
|
15840
|
+
const rawFk = rawFkColumns.length > 0 ? rawFkColumns : [
|
|
15841
|
+
deriveFk(
|
|
15842
|
+
rel.type === "belongsTo" ? rel.target : sourceTable,
|
|
15843
|
+
defaultPk
|
|
15844
|
+
)
|
|
15845
|
+
];
|
|
15215
15846
|
let columns = [defaultPk];
|
|
15216
15847
|
if (inc.select?.type === "fields" && inc.select.fields) {
|
|
15217
15848
|
const extraFields = inc.select.fields.filter((f) => f !== defaultPk);
|
|
@@ -15232,8 +15863,8 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15232
15863
|
...rel.type && {
|
|
15233
15864
|
relationType: rel.type
|
|
15234
15865
|
},
|
|
15235
|
-
foreignKey:
|
|
15236
|
-
parentKey: defaultPk,
|
|
15866
|
+
foreignKey: rawFk,
|
|
15867
|
+
parentKey: rel.type === "belongsTo" ? toColumnList9(rel.targetKey).length > 0 ? toColumnList9(rel.targetKey) : [defaultPk] : toColumnList9(rel.sourceKey).length > 0 ? toColumnList9(rel.sourceKey) : [defaultPk],
|
|
15237
15868
|
columns,
|
|
15238
15869
|
joinType: inc.join,
|
|
15239
15870
|
...conditions && { conditions }
|
|
@@ -15390,9 +16021,12 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
|
15390
16021
|
);
|
|
15391
16022
|
}
|
|
15392
16023
|
const isBelongsTo = rel.type === "belongsTo";
|
|
15393
|
-
const rawFk =
|
|
15394
|
-
const
|
|
15395
|
-
const
|
|
16024
|
+
const rawFk = toColumnList10(rel.foreignKey);
|
|
16025
|
+
const fkColumns = rawFk.length > 0 ? rawFk : [deriveFk(isBelongsTo ? rootTable : rel.target, defaultPk)];
|
|
16026
|
+
const sourceKey = toColumnList10(rel.sourceKey);
|
|
16027
|
+
const targetKey = toColumnList10(rel.targetKey);
|
|
16028
|
+
const sourceColumn = isBelongsTo ? fkColumns : sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
16029
|
+
const targetColumn = isBelongsTo ? targetKey.length > 0 ? targetKey : [defaultPk] : fkColumns;
|
|
15396
16030
|
const alias = intent.alias ?? intent.relation;
|
|
15397
16031
|
results.push({
|
|
15398
16032
|
type: "join",
|
|
@@ -15755,10 +16389,11 @@ function compileWithIncludes(plan, options, deps) {
|
|
|
15755
16389
|
const relationName = ctx.includeAlias ?? ctx.relation;
|
|
15756
16390
|
if (!relationName) continue;
|
|
15757
16391
|
const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
|
|
15758
|
-
const fk =
|
|
16392
|
+
const fk = toColumnList10(rawFk);
|
|
16393
|
+
const parentKey = toColumnList10(ctx.parentKey);
|
|
15759
16394
|
const isBelongsTo = ctx.relationType === "belongsTo";
|
|
15760
|
-
const sourceKey = isBelongsTo ? fk :
|
|
15761
|
-
const targetFk = isBelongsTo ?
|
|
16395
|
+
const sourceKey = isBelongsTo ? fk : parentKey.length > 0 ? parentKey : [deps.defaultPk];
|
|
16396
|
+
const targetFk = isBelongsTo ? parentKey.length > 0 ? parentKey : [deps.defaultPk] : fk;
|
|
15762
16397
|
const includeIntent = plan.intent?.include?.find(
|
|
15763
16398
|
(i) => i.relation === relationName || i.relation === ctx.includeAlias
|
|
15764
16399
|
);
|
|
@@ -15821,14 +16456,14 @@ function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
|
|
|
15821
16456
|
};
|
|
15822
16457
|
}
|
|
15823
16458
|
function resolveMutationExistsForeignKey(foreignKey, relationName) {
|
|
15824
|
-
|
|
15825
|
-
if (
|
|
15826
|
-
if (
|
|
16459
|
+
const columns = toColumnList11(foreignKey);
|
|
16460
|
+
if (columns.length === 0) return void 0;
|
|
16461
|
+
if (columns.some((column) => column.length === 0)) {
|
|
15827
16462
|
throw new Error(
|
|
15828
|
-
`Mutation exists()/notExists()
|
|
16463
|
+
`Mutation exists()/notExists() guard relation '${relationName}' has an empty foreignKey column.`
|
|
15829
16464
|
);
|
|
15830
16465
|
}
|
|
15831
|
-
return
|
|
16466
|
+
return columns;
|
|
15832
16467
|
}
|
|
15833
16468
|
function resolveExistsRelation(sourceTable, relation, model) {
|
|
15834
16469
|
if (!model) return { targetTable: relation };
|
|
@@ -15841,13 +16476,13 @@ function resolveExistsRelation(sourceTable, relation, model) {
|
|
|
15841
16476
|
return {
|
|
15842
16477
|
targetTable,
|
|
15843
16478
|
...fk2 !== void 0 && { sourceColumn: fk2 },
|
|
15844
|
-
targetColumn: "id"
|
|
16479
|
+
targetColumn: toColumnList11(rel.targetKey).length > 0 ? toColumnList11(rel.targetKey) : ["id"]
|
|
15845
16480
|
};
|
|
15846
16481
|
}
|
|
15847
16482
|
const fk = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
|
|
15848
16483
|
return {
|
|
15849
16484
|
targetTable,
|
|
15850
|
-
sourceColumn: rel.sourceKey
|
|
16485
|
+
sourceColumn: toColumnList11(rel.sourceKey).length > 0 ? toColumnList11(rel.sourceKey) : ["id"],
|
|
15851
16486
|
...fk !== void 0 && { targetColumn: fk }
|
|
15852
16487
|
};
|
|
15853
16488
|
}
|
|
@@ -17769,6 +18404,9 @@ function orderedNqlBindingNames(bundle) {
|
|
|
17769
18404
|
}
|
|
17770
18405
|
return names;
|
|
17771
18406
|
}
|
|
18407
|
+
function runtimeBindingSourceTable(bundle, name) {
|
|
18408
|
+
return bundle.mutationBindings?.get(name)?.table ?? bundle.bindings?.get(name)?.from;
|
|
18409
|
+
}
|
|
17772
18410
|
function mapRuntimeBindingColumnType(type) {
|
|
17773
18411
|
switch (type) {
|
|
17774
18412
|
case "string":
|
|
@@ -17821,13 +18459,13 @@ function resolveRuntimeBindingColumnType(bindingName, sourceTable, columnName) {
|
|
|
17821
18459
|
);
|
|
17822
18460
|
if (column === void 0) {
|
|
17823
18461
|
throw new Error(
|
|
17824
|
-
`NQL runtime binding '${bindingName}' cannot resolve projected column '${columnName}' on source
|
|
18462
|
+
`NQL runtime binding '${bindingName}' cannot resolve projected column '${columnName}' on source table '${sourceTable.name}'.`
|
|
17825
18463
|
);
|
|
17826
18464
|
}
|
|
17827
18465
|
const dbType = column.originalDbType?.trim() || mapRuntimeBindingColumnType(column.type);
|
|
17828
18466
|
if (dbType === void 0 || dbType.trim() === "") {
|
|
17829
18467
|
throw new Error(
|
|
17830
|
-
`NQL runtime binding '${bindingName}' cannot resolve a PostgreSQL type for projected column '${columnName}' on source
|
|
18468
|
+
`NQL runtime binding '${bindingName}' cannot resolve a PostgreSQL type for projected column '${columnName}' on source table '${sourceTable.name}'.`
|
|
17831
18469
|
);
|
|
17832
18470
|
}
|
|
17833
18471
|
const typeName = dbType.trim();
|
|
@@ -17850,7 +18488,7 @@ function resolveRuntimeBindingColumnTypes(name, binding, model, sourceTableName)
|
|
|
17850
18488
|
const sourceTable = findRuntimeBindingSourceTable(model, sourceTableName);
|
|
17851
18489
|
if (sourceTable === void 0) {
|
|
17852
18490
|
throw new Error(
|
|
17853
|
-
`NQL runtime binding '${name}' cannot resolve source
|
|
18491
|
+
`NQL runtime binding '${name}' cannot resolve source table '${sourceTableName}' in the model.`
|
|
17854
18492
|
);
|
|
17855
18493
|
}
|
|
17856
18494
|
return binding.columns.map(
|
|
@@ -17877,7 +18515,7 @@ function compileNqlRuntimeBindingCte(name, binding, naming, parameterOffset, sou
|
|
|
17877
18515
|
const columnSql = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
|
|
17878
18516
|
if (sourceTable === void 0) {
|
|
17879
18517
|
throw new Error(
|
|
17880
|
-
`NQL runtime binding '${name}' cannot materialize a typed relation because its source
|
|
18518
|
+
`NQL runtime binding '${name}' cannot materialize a typed relation because its source table is unavailable.`
|
|
17881
18519
|
);
|
|
17882
18520
|
}
|
|
17883
18521
|
const projectedColumns = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
|
|
@@ -18086,7 +18724,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18086
18724
|
bundle.query.from,
|
|
18087
18725
|
deps.naming
|
|
18088
18726
|
);
|
|
18089
|
-
const planReport = queryFromBinding ? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
18727
|
+
const planReport = queryFromBinding ? bundle.plan ?? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
18090
18728
|
dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
|
|
18091
18729
|
});
|
|
18092
18730
|
return guardCompiledQuery(
|
|
@@ -18152,7 +18790,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18152
18790
|
runtimeBinding,
|
|
18153
18791
|
naming,
|
|
18154
18792
|
parameters.length,
|
|
18155
|
-
bundle
|
|
18793
|
+
runtimeBindingSourceTable(bundle, name),
|
|
18156
18794
|
deps.schemaName,
|
|
18157
18795
|
deps.model
|
|
18158
18796
|
);
|
|
@@ -18180,6 +18818,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18180
18818
|
}
|
|
18181
18819
|
const leafBundle = {
|
|
18182
18820
|
...bundle.query !== void 0 && { query: bundle.query },
|
|
18821
|
+
...bundle.plan !== void 0 && { plan: bundle.plan },
|
|
18183
18822
|
...bundle.cteQuery !== void 0 && { cteQuery: bundle.cteQuery },
|
|
18184
18823
|
...bundle.mutation !== void 0 && { mutation: bundle.mutation },
|
|
18185
18824
|
...bundle.returning !== void 0 && { returning: bundle.returning },
|