@dbsp/adapter-pgsql 1.6.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 +8 -3
- package/dist/index.js +536 -167
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -663,9 +663,6 @@ function funcCall(name, args = [], options = {}) {
|
|
|
663
663
|
function coalesceExpr(args) {
|
|
664
664
|
return { CoalesceExpr: { args } };
|
|
665
665
|
}
|
|
666
|
-
function coalesce(...args) {
|
|
667
|
-
return funcCall("coalesce", args);
|
|
668
|
-
}
|
|
669
666
|
function sortBy(expr, direction = "DEFAULT", nulls = "DEFAULT") {
|
|
670
667
|
const sb = {
|
|
671
668
|
node: expr,
|
|
@@ -4486,6 +4483,11 @@ var init_relation_filter = __esm({
|
|
|
4486
4483
|
|
|
4487
4484
|
// src/subquery-emission.ts
|
|
4488
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
|
+
}
|
|
4489
4491
|
function assertNoDroppedDecisionModifiers(decision, use) {
|
|
4490
4492
|
const d = decision;
|
|
4491
4493
|
const unsupported = [];
|
|
@@ -4604,8 +4606,9 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
|
|
|
4604
4606
|
],
|
|
4605
4607
|
...whereClause && { whereClause }
|
|
4606
4608
|
};
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
+
const orderBy = isColumnOrderBy(decision.orderBy) ? decision.orderBy : void 0;
|
|
4610
|
+
if (orderBy && orderBy.length > 0) {
|
|
4611
|
+
stmt.sortClause = orderBy.map(
|
|
4609
4612
|
(o) => sortBy(
|
|
4610
4613
|
columnRef(o.column, targetAlias, void 0, ctx.naming),
|
|
4611
4614
|
o.direction ?? "ASC",
|
|
@@ -5098,19 +5101,19 @@ var init_shared = __esm({
|
|
|
5098
5101
|
});
|
|
5099
5102
|
|
|
5100
5103
|
// src/handlers/include/json-agg.ts
|
|
5101
|
-
import {
|
|
5104
|
+
import { resolveJsonAggOrderKey } from "@dbsp/types";
|
|
5105
|
+
function isJsonAggOrderBy(orderBy) {
|
|
5106
|
+
return Array.isArray(orderBy) && orderBy.every((item) => typeof item === "string");
|
|
5107
|
+
}
|
|
5102
5108
|
function resolveJsonAggOrderBy(decision, targetTable, ctx) {
|
|
5103
5109
|
const table = ctx.model?.getTable(targetTable);
|
|
5104
5110
|
if (table) {
|
|
5105
|
-
const
|
|
5106
|
-
|
|
5107
|
-
columns: table.columns.map((col) => col.name),
|
|
5108
|
-
fallback: true
|
|
5109
|
-
};
|
|
5110
|
-
return orderIntent.columns.length > 0 ? orderIntent : void 0;
|
|
5111
|
+
const orderKey = resolveJsonAggOrderKey(table);
|
|
5112
|
+
return orderKey.columns.length > 0 ? orderKey : void 0;
|
|
5111
5113
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
+
const decisionOrderBy = isJsonAggOrderBy(decision.orderBy) ? decision.orderBy : void 0;
|
|
5115
|
+
return decisionOrderBy && decisionOrderBy.length > 0 ? {
|
|
5116
|
+
columns: decisionOrderBy,
|
|
5114
5117
|
fallback: decision.orderByFallback === true
|
|
5115
5118
|
} : void 0;
|
|
5116
5119
|
}
|
|
@@ -5210,7 +5213,7 @@ var init_json_agg = __esm({
|
|
|
5210
5213
|
});
|
|
5211
5214
|
|
|
5212
5215
|
// src/handlers/include/lateral.ts
|
|
5213
|
-
import { toColumnList as
|
|
5216
|
+
import { toColumnList as toColumnList6 } from "@dbsp/types";
|
|
5214
5217
|
function buildLateralTargets(columns, alias, ctx) {
|
|
5215
5218
|
if (columns && columns.length > 0 && !(columns.length === 1 && columns[0] === "*")) {
|
|
5216
5219
|
return columns.map((col) => ({
|
|
@@ -5312,7 +5315,7 @@ var init_lateral = __esm({
|
|
|
5312
5315
|
lateralIncludeHandler = {
|
|
5313
5316
|
strategy: "lateral",
|
|
5314
5317
|
compile(decision, ctx, state) {
|
|
5315
|
-
const sourceColumn =
|
|
5318
|
+
const sourceColumn = toColumnList6(decision.sourceColumn);
|
|
5316
5319
|
if (sourceColumn.length === 0) {
|
|
5317
5320
|
throw new Error(
|
|
5318
5321
|
"Missing required column 'sourceColumn' in lateral include"
|
|
@@ -6267,6 +6270,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6267
6270
|
pkColumn,
|
|
6268
6271
|
fkColumn,
|
|
6269
6272
|
outerAlias,
|
|
6273
|
+
outerSeedColumn,
|
|
6270
6274
|
isAncestors,
|
|
6271
6275
|
maxDepth,
|
|
6272
6276
|
selectColumn,
|
|
@@ -6277,6 +6281,9 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6277
6281
|
const dbPk = naming.toDatabase(pkColumn);
|
|
6278
6282
|
const dbFk = naming.toDatabase(fkColumn);
|
|
6279
6283
|
const dbOuter = naming.toDatabase(outerAlias);
|
|
6284
|
+
const dbOuterSeed = naming.toDatabase(
|
|
6285
|
+
outerSeedColumn ?? (isAncestors ? fkColumn : pkColumn)
|
|
6286
|
+
);
|
|
6280
6287
|
const innerAlias = "__n";
|
|
6281
6288
|
const anchorSelect = {
|
|
6282
6289
|
targetList: [
|
|
@@ -6345,7 +6352,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6345
6352
|
ColumnRef: {
|
|
6346
6353
|
fields: [
|
|
6347
6354
|
{ String: { sval: dbOuter } },
|
|
6348
|
-
{ String: { sval:
|
|
6355
|
+
{ String: { sval: dbOuterSeed } }
|
|
6349
6356
|
]
|
|
6350
6357
|
}
|
|
6351
6358
|
}
|
|
@@ -6365,7 +6372,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6365
6372
|
ColumnRef: {
|
|
6366
6373
|
fields: [
|
|
6367
6374
|
{ String: { sval: dbOuter } },
|
|
6368
|
-
{ String: { sval:
|
|
6375
|
+
{ String: { sval: dbOuterSeed } }
|
|
6369
6376
|
]
|
|
6370
6377
|
}
|
|
6371
6378
|
}
|
|
@@ -6429,15 +6436,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6429
6436
|
}
|
|
6430
6437
|
],
|
|
6431
6438
|
fromClause: [
|
|
6432
|
-
// FROM __rc
|
|
6433
|
-
{
|
|
6434
|
-
RangeVar: {
|
|
6435
|
-
relname: cteAlias,
|
|
6436
|
-
inh: true,
|
|
6437
|
-
relpersistence: "p"
|
|
6438
|
-
}
|
|
6439
|
-
},
|
|
6440
|
-
// INNER JOIN table AS __n
|
|
6439
|
+
// FROM __rc INNER JOIN table AS __n
|
|
6441
6440
|
{
|
|
6442
6441
|
JoinExpr: {
|
|
6443
6442
|
jointype: "JOIN_INNER",
|
|
@@ -6520,7 +6519,7 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6520
6519
|
},
|
|
6521
6520
|
integerNode(maxDepth)
|
|
6522
6521
|
),
|
|
6523
|
-
// pk <> ALL(__visited)
|
|
6522
|
+
// pk <> ALL(__visited) - cycle detection
|
|
6524
6523
|
{
|
|
6525
6524
|
A_Expr: {
|
|
6526
6525
|
kind: "AEXPR_OP_ALL",
|
|
@@ -6565,20 +6564,35 @@ function buildRecursiveScalarSubquery(config) {
|
|
|
6565
6564
|
targetList: [
|
|
6566
6565
|
{
|
|
6567
6566
|
ResTarget: {
|
|
6568
|
-
val:
|
|
6569
|
-
funcCall(
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6567
|
+
val: coalesceExpr([
|
|
6568
|
+
funcCall(
|
|
6569
|
+
"json_agg",
|
|
6570
|
+
[
|
|
6571
|
+
{
|
|
6572
|
+
ColumnRef: {
|
|
6573
|
+
fields: [
|
|
6574
|
+
{ String: { sval: cteAlias } },
|
|
6575
|
+
{ String: { sval: dbSelectCol } }
|
|
6576
|
+
]
|
|
6577
|
+
}
|
|
6576
6578
|
}
|
|
6579
|
+
],
|
|
6580
|
+
{
|
|
6581
|
+
orderBy: [
|
|
6582
|
+
sortBy({
|
|
6583
|
+
ColumnRef: {
|
|
6584
|
+
fields: [
|
|
6585
|
+
{ String: { sval: cteAlias } },
|
|
6586
|
+
{ String: { sval: "__depth" } }
|
|
6587
|
+
]
|
|
6588
|
+
}
|
|
6589
|
+
})
|
|
6590
|
+
]
|
|
6577
6591
|
}
|
|
6578
|
-
|
|
6592
|
+
),
|
|
6579
6593
|
// Empty array fallback: '[]'::json
|
|
6580
|
-
typeCast(
|
|
6581
|
-
)
|
|
6594
|
+
typeCast({ A_Const: { sval: { sval: "[]" } } }, "json")
|
|
6595
|
+
])
|
|
6582
6596
|
}
|
|
6583
6597
|
}
|
|
6584
6598
|
],
|
|
@@ -7037,6 +7051,11 @@ var init_relation = __esm({
|
|
|
7037
7051
|
});
|
|
7038
7052
|
|
|
7039
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
|
+
}
|
|
7040
7059
|
function buildSortBy(column, direction, ctx) {
|
|
7041
7060
|
const tableAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
7042
7061
|
const colRef = columnRef(column, tableAlias, void 0, ctx.naming);
|
|
@@ -7049,7 +7068,7 @@ function buildSortBy(column, direction, ctx) {
|
|
|
7049
7068
|
}
|
|
7050
7069
|
function buildWindowDef(decision, ctx) {
|
|
7051
7070
|
const partition = decision.partition;
|
|
7052
|
-
const orderBy = decision.orderBy;
|
|
7071
|
+
const orderBy = isWindowOrderBy(decision.orderBy) ? decision.orderBy : void 0;
|
|
7053
7072
|
const frame = decision.frame;
|
|
7054
7073
|
const tableAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
7055
7074
|
const windowDef = { frameOptions: WINDOW_FRAME_DEFAULT };
|
|
@@ -7313,7 +7332,7 @@ import {
|
|
|
7313
7332
|
isParamIntent as isParamIntent6,
|
|
7314
7333
|
NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
|
|
7315
7334
|
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST,
|
|
7316
|
-
toColumnList as
|
|
7335
|
+
toColumnList as toColumnList7
|
|
7317
7336
|
} from "@dbsp/types";
|
|
7318
7337
|
import {
|
|
7319
7338
|
getNqlBindingRefName,
|
|
@@ -8379,6 +8398,7 @@ init_case_value();
|
|
|
8379
8398
|
init_custom();
|
|
8380
8399
|
init_expression();
|
|
8381
8400
|
init_param_value();
|
|
8401
|
+
init_pseudo();
|
|
8382
8402
|
init_window();
|
|
8383
8403
|
init_include();
|
|
8384
8404
|
init_shared();
|
|
@@ -8425,7 +8445,7 @@ function trustedRelationHasMultipleHops(relation) {
|
|
|
8425
8445
|
if (typeof relation !== "string") return relation.length > 1;
|
|
8426
8446
|
return relation.split(".").length > 1;
|
|
8427
8447
|
}
|
|
8428
|
-
function
|
|
8448
|
+
function isJsonAggOrderBy2(orderBy) {
|
|
8429
8449
|
return Array.isArray(orderBy) && orderBy.every((item) => typeof item === "string");
|
|
8430
8450
|
}
|
|
8431
8451
|
function isExpressionOrderBy(orderBy) {
|
|
@@ -8434,8 +8454,12 @@ function isExpressionOrderBy(orderBy) {
|
|
|
8434
8454
|
);
|
|
8435
8455
|
}
|
|
8436
8456
|
function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
8437
|
-
const jsonAggOrderBy =
|
|
8457
|
+
const jsonAggOrderBy = isJsonAggOrderBy2(pd.orderBy) ? pd.orderBy : void 0;
|
|
8438
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
|
+
}));
|
|
8439
8463
|
const derivedFkColumns = deriveFkColumns(
|
|
8440
8464
|
pd,
|
|
8441
8465
|
pd.sourceTable ?? rootTable,
|
|
@@ -8470,7 +8494,6 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8470
8494
|
relationType: pd.relationType,
|
|
8471
8495
|
foreignKey: pd.foreignKey,
|
|
8472
8496
|
parentKey: pd.parentKey,
|
|
8473
|
-
targetPrimaryKey: pd.targetPrimaryKey ?? jsonAggOrderBy,
|
|
8474
8497
|
orderByFallback: pd.orderByFallback,
|
|
8475
8498
|
dataType: pd.dataType,
|
|
8476
8499
|
traversal: pd.traversal,
|
|
@@ -8486,10 +8509,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8486
8509
|
include: pd.include?.map(
|
|
8487
8510
|
(c) => mapToHandlerDecision(c, rootTable, defaultPk, deriveFk)
|
|
8488
8511
|
),
|
|
8489
|
-
orderBy:
|
|
8490
|
-
column: o.field,
|
|
8491
|
-
direction: o.direction?.toUpperCase() ?? "ASC"
|
|
8492
|
-
})),
|
|
8512
|
+
orderBy: handlerOrderBy,
|
|
8493
8513
|
partition: pd.partitionBy,
|
|
8494
8514
|
jsonPath: pd.jsonPath,
|
|
8495
8515
|
jsonMode: pd.jsonMode,
|
|
@@ -9048,8 +9068,11 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9048
9068
|
isNqlBindingRoot(plan) {
|
|
9049
9069
|
return hasBindingName(this.bindingNames, plan.rootTable, this.naming);
|
|
9050
9070
|
}
|
|
9071
|
+
allocateBindingRelationAlias() {
|
|
9072
|
+
return `rc_${this.bindingRelationColumnSubqueryIndex++}`;
|
|
9073
|
+
}
|
|
9051
9074
|
buildCorrelatedRelationRefs(fields, plan) {
|
|
9052
|
-
const relatedAlias =
|
|
9075
|
+
const relatedAlias = this.allocateBindingRelationAlias();
|
|
9053
9076
|
const relatedTable = rangeVar(
|
|
9054
9077
|
fields.targetTable,
|
|
9055
9078
|
relatedAlias,
|
|
@@ -9068,12 +9091,93 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9068
9091
|
relatedColumn
|
|
9069
9092
|
};
|
|
9070
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"
|
|
9130
|
+
);
|
|
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
|
+
}
|
|
9071
9151
|
compileBindingRelationColumnSubquery(fields, plan, dialectCapabilities) {
|
|
9072
9152
|
if (fields.selectedColumn === void 0) {
|
|
9073
9153
|
throw new Error(
|
|
9074
9154
|
`NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
|
|
9075
9155
|
);
|
|
9076
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
|
+
);
|
|
9176
|
+
}
|
|
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
|
+
}
|
|
9077
9181
|
if (fields.cardinality === "one") {
|
|
9078
9182
|
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9079
9183
|
if (fields.hops.length === 0 && trustedRelationHasMultipleHops(fields.relation)) {
|
|
@@ -9152,9 +9256,14 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9152
9256
|
};
|
|
9153
9257
|
}
|
|
9154
9258
|
if (fields.cardinality === "many") {
|
|
9155
|
-
if (fields.relationType !== "hasMany") {
|
|
9259
|
+
if (fields.relationType !== "hasMany" && fields.relationType !== "manyToMany" && fields.relationType !== "belongsToMany") {
|
|
9156
9260
|
throw new Error(
|
|
9157
|
-
`NQL binding relation-column proof for '${plan.rootTable}' has cardinality 'many' but relationType '${fields.relationType ?? "unknown"}' is not supported; only hasMany can be aggregated (ref-#192).`
|
|
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).`
|
|
9158
9267
|
);
|
|
9159
9268
|
}
|
|
9160
9269
|
if (dialectCapabilities?.supportsJsonAgg === false) {
|
|
@@ -9163,6 +9272,37 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9163
9272
|
);
|
|
9164
9273
|
}
|
|
9165
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
|
+
);
|
|
9166
9306
|
return {
|
|
9167
9307
|
SubLink: {
|
|
9168
9308
|
subLinkType: "EXPR_SUBLINK",
|
|
@@ -9185,14 +9325,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9185
9325
|
}
|
|
9186
9326
|
}
|
|
9187
9327
|
],
|
|
9188
|
-
from: [
|
|
9189
|
-
where:
|
|
9190
|
-
relatedAlias,
|
|
9191
|
-
fields.targetColumn,
|
|
9192
|
-
plan.rootTable,
|
|
9193
|
-
fields.sourceColumn,
|
|
9194
|
-
this.createHandlerContext(plan)
|
|
9195
|
-
)
|
|
9328
|
+
from: [fromNode],
|
|
9329
|
+
where: whereNode
|
|
9196
9330
|
})
|
|
9197
9331
|
}
|
|
9198
9332
|
};
|
|
@@ -10357,8 +10491,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10357
10491
|
this.schemaForRangeVar(plan, decision.targetTable ?? ""),
|
|
10358
10492
|
this.naming
|
|
10359
10493
|
);
|
|
10360
|
-
const sourceColumn =
|
|
10361
|
-
const targetColumn =
|
|
10494
|
+
const sourceColumn = toColumnList7(decision.sourceColumn);
|
|
10495
|
+
const targetColumn = toColumnList7(decision.targetColumn);
|
|
10362
10496
|
if (sourceColumn.length === 0) {
|
|
10363
10497
|
throw new Error("Missing required column 'sourceColumn' in compileJoin");
|
|
10364
10498
|
}
|
|
@@ -10797,6 +10931,12 @@ function buildSequenceClause(verb, seqName, seq, includeCycleNoCycle = false) {
|
|
|
10797
10931
|
}
|
|
10798
10932
|
return `${parts.join(" ")};`;
|
|
10799
10933
|
}
|
|
10934
|
+
function failSafeUnknownDownChange(kind) {
|
|
10935
|
+
return {
|
|
10936
|
+
sql: `-- WARNING: Cannot reverse unsupported SchemaChange kind "${kind}"`,
|
|
10937
|
+
destructive: true
|
|
10938
|
+
};
|
|
10939
|
+
}
|
|
10800
10940
|
function isChangeSupported(kind, caps) {
|
|
10801
10941
|
switch (kind) {
|
|
10802
10942
|
case "create_enum":
|
|
@@ -11249,188 +11389,381 @@ function changeToUpSQL(change, schemaName) {
|
|
|
11249
11389
|
function changeToDownSQL(change, schemaName) {
|
|
11250
11390
|
switch (change.kind) {
|
|
11251
11391
|
case "create_table":
|
|
11252
|
-
return
|
|
11392
|
+
return {
|
|
11393
|
+
sql: `DROP TABLE IF EXISTS ${qualifyTable(change.table, schemaName)} CASCADE;`,
|
|
11394
|
+
destructive: true
|
|
11395
|
+
};
|
|
11253
11396
|
case "drop_table":
|
|
11254
|
-
return
|
|
11397
|
+
return {
|
|
11398
|
+
sql: `-- WARNING: Cannot reverse drop_table "${change.table}" -- table data was lost`,
|
|
11399
|
+
destructive: true
|
|
11400
|
+
};
|
|
11255
11401
|
case "add_column":
|
|
11256
|
-
return
|
|
11402
|
+
return {
|
|
11403
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP COLUMN ${quoteIdent2(change.column, "alias")} CASCADE;`,
|
|
11404
|
+
destructive: true
|
|
11405
|
+
};
|
|
11257
11406
|
case "drop_column":
|
|
11258
|
-
return
|
|
11407
|
+
return {
|
|
11408
|
+
sql: `-- WARNING: Cannot reverse drop_column "${change.table}"."${change.column}" -- column data was lost`,
|
|
11409
|
+
destructive: true
|
|
11410
|
+
};
|
|
11259
11411
|
case "alter_column_type": {
|
|
11260
11412
|
const fromType = change.meta?.fromType;
|
|
11261
11413
|
if (!fromType) {
|
|
11262
|
-
return
|
|
11414
|
+
return {
|
|
11415
|
+
sql: `-- WARNING: Cannot reverse alter_column_type "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
11416
|
+
destructive: true
|
|
11417
|
+
};
|
|
11263
11418
|
}
|
|
11264
|
-
return
|
|
11419
|
+
return {
|
|
11420
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} ALTER COLUMN ${quoteIdent2(change.column, "alias")} TYPE ${fromType};`,
|
|
11421
|
+
destructive: true
|
|
11422
|
+
};
|
|
11265
11423
|
}
|
|
11266
11424
|
case "alter_column_nullable": {
|
|
11267
11425
|
const oldNullable = change.meta?.oldNullable;
|
|
11268
11426
|
if (oldNullable === void 0) {
|
|
11269
|
-
return
|
|
11427
|
+
return {
|
|
11428
|
+
sql: `-- WARNING: Cannot reverse alter_column_nullable "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
11429
|
+
destructive: true
|
|
11430
|
+
};
|
|
11270
11431
|
}
|
|
11271
11432
|
const action = oldNullable ? "DROP NOT NULL" : "SET NOT NULL";
|
|
11272
|
-
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
|
+
};
|
|
11273
11438
|
}
|
|
11274
11439
|
case "alter_column_default": {
|
|
11275
11440
|
const oldDefault = change.meta?.oldDefault;
|
|
11276
11441
|
if (oldDefault === void 0) {
|
|
11277
|
-
return
|
|
11442
|
+
return {
|
|
11443
|
+
sql: `-- WARNING: Cannot reverse alter_column_default "${change.table}"."${change.column}" -- missing migration metadata`,
|
|
11444
|
+
destructive: true
|
|
11445
|
+
};
|
|
11278
11446
|
}
|
|
11279
11447
|
if (oldDefault === null) {
|
|
11280
|
-
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
|
+
};
|
|
11281
11453
|
}
|
|
11282
|
-
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
|
+
};
|
|
11283
11459
|
}
|
|
11284
11460
|
case "add_primary_key": {
|
|
11285
|
-
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
|
+
};
|
|
11286
11480
|
}
|
|
11287
|
-
case "drop_primary_key":
|
|
11288
|
-
return `-- WARNING: Cannot reverse drop_primary_key "${change.table}" -- columns unknown`;
|
|
11289
11481
|
case "add_foreign_key": {
|
|
11290
11482
|
const fk = change.meta?.fk;
|
|
11291
|
-
if (!fk) return void 0;
|
|
11483
|
+
if (!fk) return { sql: void 0, destructive: true };
|
|
11292
11484
|
const constraintName = quoteIdent2(
|
|
11293
11485
|
fkName(change.table, fk.columns),
|
|
11294
11486
|
"alias"
|
|
11295
11487
|
);
|
|
11296
|
-
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
|
+
};
|
|
11297
11506
|
}
|
|
11298
|
-
case "drop_foreign_key":
|
|
11299
|
-
return `-- WARNING: Cannot reverse drop_foreign_key "${change.table}" -- FK definition was lost`;
|
|
11300
11507
|
case "alter_foreign_key": {
|
|
11301
11508
|
const oldFk = change.meta?.oldFk;
|
|
11302
|
-
if (!oldFk) {
|
|
11303
|
-
return `-- WARNING: Cannot reverse alter_foreign_key "${change.table}" -- missing migration metadata`;
|
|
11304
|
-
}
|
|
11305
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
|
+
}
|
|
11306
11516
|
const constraintName = quoteIdent2(
|
|
11307
11517
|
fkName(change.table, fk.columns),
|
|
11308
11518
|
"alias"
|
|
11309
11519
|
);
|
|
11310
11520
|
const drop = `ALTER TABLE ${qualifyTable(change.table, schemaName)} DROP CONSTRAINT IF EXISTS ${constraintName};`;
|
|
11311
11521
|
const add = generateAddFKSQL(change.table, oldFk, schemaName);
|
|
11312
|
-
return `${drop}
|
|
11313
|
-
${add}
|
|
11522
|
+
return { sql: `${drop}
|
|
11523
|
+
${add}`, destructive: false };
|
|
11314
11524
|
}
|
|
11315
11525
|
case "create_index": {
|
|
11316
11526
|
const idx = change.meta?.index;
|
|
11317
|
-
if (!idx) return void 0;
|
|
11527
|
+
if (!idx) return { sql: void 0, destructive: true };
|
|
11318
11528
|
const indexName = quoteIdent2(
|
|
11319
11529
|
idxName(change.table, idx.columns, idx.name),
|
|
11320
11530
|
"alias"
|
|
11321
11531
|
);
|
|
11322
11532
|
const schemaPrefix = schemaName ? `${quoteIdent2(schemaName, "alias")}.` : "";
|
|
11323
|
-
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
|
+
};
|
|
11324
11551
|
}
|
|
11325
|
-
case "drop_index":
|
|
11326
|
-
return `-- WARNING: Cannot reverse drop_index "${change.table}" -- index definition was lost`;
|
|
11327
11552
|
case "add_check_constraint": {
|
|
11328
11553
|
const check = change.meta?.check;
|
|
11329
|
-
if (!check) return void 0;
|
|
11330
|
-
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
|
+
};
|
|
11331
11559
|
}
|
|
11332
11560
|
case "drop_check_constraint": {
|
|
11333
11561
|
const check = change.meta?.check;
|
|
11334
|
-
if (!check) return void 0;
|
|
11562
|
+
if (!check) return { sql: void 0, destructive: true };
|
|
11335
11563
|
validateSqlExpression(
|
|
11336
11564
|
check.expression,
|
|
11337
11565
|
"migration check constraint (down)"
|
|
11338
11566
|
);
|
|
11339
|
-
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
|
+
};
|
|
11340
11572
|
}
|
|
11341
11573
|
case "create_enum": {
|
|
11342
11574
|
const enumDef = change.meta?.enum;
|
|
11343
|
-
if (!enumDef) return void 0;
|
|
11575
|
+
if (!enumDef) return { sql: void 0, destructive: true };
|
|
11344
11576
|
const enumName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(enumDef.name, "alias")}` : quoteIdent2(enumDef.name, "alias");
|
|
11345
|
-
return
|
|
11577
|
+
return {
|
|
11578
|
+
sql: `DROP TYPE IF EXISTS ${enumName} CASCADE;`,
|
|
11579
|
+
destructive: true
|
|
11580
|
+
};
|
|
11346
11581
|
}
|
|
11347
11582
|
case "drop_enum": {
|
|
11348
11583
|
const enumDef = change.meta?.enum;
|
|
11349
|
-
if (!enumDef) return void 0;
|
|
11584
|
+
if (!enumDef) return { sql: void 0, destructive: true };
|
|
11350
11585
|
const enumName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(enumDef.name, "alias")}` : quoteIdent2(enumDef.name, "alias");
|
|
11351
11586
|
const values = enumDef.values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
11352
|
-
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
|
+
};
|
|
11353
11592
|
}
|
|
11354
11593
|
case "alter_enum_add_value":
|
|
11355
|
-
return
|
|
11594
|
+
return {
|
|
11595
|
+
sql: `-- ALTER TYPE ADD VALUE cannot be reversed in PostgreSQL`,
|
|
11596
|
+
destructive: true
|
|
11597
|
+
};
|
|
11356
11598
|
case "alter_column_collation": {
|
|
11357
|
-
return
|
|
11599
|
+
return {
|
|
11600
|
+
sql: `-- WARNING: Cannot reverse alter_column_collation "${change.table}"."${change.column}" -- previous collation unknown`,
|
|
11601
|
+
destructive: true
|
|
11602
|
+
};
|
|
11358
11603
|
}
|
|
11359
11604
|
case "alter_column_identity": {
|
|
11360
11605
|
const col = change.meta?.column;
|
|
11361
11606
|
const prevIdentity = change.meta?.previousIdentity;
|
|
11362
|
-
if (!col) return void 0;
|
|
11607
|
+
if (!col) return { sql: void 0, destructive: true };
|
|
11363
11608
|
const table = qualifyTable(change.table, schemaName);
|
|
11364
11609
|
const column = quoteIdent2(change.column, "alias");
|
|
11365
11610
|
if (prevIdentity && !col.identity) {
|
|
11366
11611
|
const gen2 = prevIdentity === "always" ? "ALWAYS" : "BY DEFAULT";
|
|
11367
|
-
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
|
+
};
|
|
11368
11617
|
}
|
|
11369
11618
|
if (!prevIdentity && col.identity) {
|
|
11370
|
-
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
|
+
};
|
|
11371
11629
|
}
|
|
11372
11630
|
const gen = prevIdentity === "always" ? "ALWAYS" : "BY DEFAULT";
|
|
11373
|
-
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
|
+
};
|
|
11374
11636
|
}
|
|
11375
11637
|
case "add_comment": {
|
|
11376
11638
|
const target = change.meta?.target;
|
|
11377
11639
|
if (target === "table") {
|
|
11378
|
-
return
|
|
11640
|
+
return {
|
|
11641
|
+
sql: `COMMENT ON TABLE ${qualifyTable(change.table, schemaName)} IS NULL;`,
|
|
11642
|
+
destructive: true
|
|
11643
|
+
};
|
|
11379
11644
|
}
|
|
11380
|
-
return
|
|
11645
|
+
return {
|
|
11646
|
+
sql: `COMMENT ON COLUMN ${qualifyTable(change.table, schemaName)}.${quoteIdent2(change.column, "alias")} IS NULL;`,
|
|
11647
|
+
destructive: true
|
|
11648
|
+
};
|
|
11381
11649
|
}
|
|
11382
11650
|
case "drop_comment": {
|
|
11383
|
-
|
|
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
|
+
};
|
|
11384
11674
|
}
|
|
11385
11675
|
case "create_extension": {
|
|
11386
11676
|
const ext = change.meta?.extension;
|
|
11387
|
-
if (ext == null) return void 0;
|
|
11388
|
-
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
|
+
};
|
|
11389
11682
|
}
|
|
11390
11683
|
case "drop_extension": {
|
|
11391
11684
|
const ext = change.meta?.extension;
|
|
11392
|
-
if (ext == null) return void 0;
|
|
11393
|
-
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
|
+
};
|
|
11394
11691
|
}
|
|
11395
11692
|
case "create_sequence": {
|
|
11396
11693
|
const seq = change.meta?.sequence;
|
|
11397
|
-
if (!seq) return void 0;
|
|
11694
|
+
if (!seq) return { sql: void 0, destructive: true };
|
|
11398
11695
|
const seqName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(seq.name, "alias")}` : quoteIdent2(seq.name, "alias");
|
|
11399
|
-
return
|
|
11696
|
+
return {
|
|
11697
|
+
sql: `DROP SEQUENCE IF EXISTS ${seqName} CASCADE;`,
|
|
11698
|
+
destructive: true
|
|
11699
|
+
};
|
|
11400
11700
|
}
|
|
11401
11701
|
case "alter_sequence": {
|
|
11402
11702
|
const prevSeq = change.meta?.previousSequence;
|
|
11403
11703
|
if (!prevSeq) {
|
|
11404
|
-
return
|
|
11704
|
+
return {
|
|
11705
|
+
sql: `-- WARNING: Cannot reverse alter_sequence "${change.table}" -- missing migration metadata`,
|
|
11706
|
+
destructive: true
|
|
11707
|
+
};
|
|
11405
11708
|
}
|
|
11406
11709
|
const seqName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(prevSeq.name, "alias")}` : quoteIdent2(prevSeq.name, "alias");
|
|
11407
|
-
return
|
|
11710
|
+
return {
|
|
11711
|
+
sql: buildSequenceClause("ALTER SEQUENCE", seqName, prevSeq, true),
|
|
11712
|
+
// Allowlisted: restores the recorded prior sequence metadata.
|
|
11713
|
+
destructive: false
|
|
11714
|
+
};
|
|
11408
11715
|
}
|
|
11409
11716
|
case "drop_sequence": {
|
|
11410
11717
|
const seq = change.meta?.sequence;
|
|
11411
|
-
if (!seq) return void 0;
|
|
11718
|
+
if (!seq) return { sql: void 0, destructive: true };
|
|
11412
11719
|
const seqName = schemaName ? `${quoteIdent2(schemaName, "alias")}.${quoteIdent2(seq.name, "alias")}` : quoteIdent2(seq.name, "alias");
|
|
11413
|
-
return
|
|
11720
|
+
return {
|
|
11721
|
+
sql: buildSequenceClause("CREATE SEQUENCE", seqName, seq),
|
|
11722
|
+
// Allowlisted: re-creates the dropped sequence from metadata.
|
|
11723
|
+
destructive: false
|
|
11724
|
+
};
|
|
11414
11725
|
}
|
|
11415
11726
|
case "validate_constraint":
|
|
11416
|
-
return
|
|
11727
|
+
return {
|
|
11728
|
+
sql: `-- VALIDATE CONSTRAINT cannot be reversed in PostgreSQL (table: "${change.table}")`,
|
|
11729
|
+
destructive: true
|
|
11730
|
+
};
|
|
11417
11731
|
case "enable_rls":
|
|
11418
|
-
return
|
|
11732
|
+
return {
|
|
11733
|
+
sql: `ALTER TABLE ${qualifyTable(change.table, schemaName)} DISABLE ROW LEVEL SECURITY;`,
|
|
11734
|
+
destructive: true
|
|
11735
|
+
};
|
|
11419
11736
|
case "disable_rls":
|
|
11420
|
-
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
|
+
};
|
|
11421
11742
|
case "create_policy": {
|
|
11422
11743
|
const policy = change.meta?.policy;
|
|
11423
|
-
if (!policy) return void 0;
|
|
11424
|
-
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
|
+
};
|
|
11425
11749
|
}
|
|
11426
11750
|
case "drop_policy": {
|
|
11427
11751
|
const policy = change.meta?.policy;
|
|
11428
|
-
if (!policy) return void 0;
|
|
11429
|
-
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
|
+
};
|
|
11430
11758
|
}
|
|
11759
|
+
default:
|
|
11760
|
+
return failSafeUnknownDownChange(change.kind);
|
|
11431
11761
|
}
|
|
11432
11762
|
}
|
|
11433
11763
|
function generateDownSQL(diff, options) {
|
|
11764
|
+
return generateDownMigrationSQL(diff, options).statements;
|
|
11765
|
+
}
|
|
11766
|
+
function generateDownMigrationSQL(diff, options) {
|
|
11434
11767
|
const schemaName = options?.schemaName;
|
|
11435
11768
|
const includeDestructive = options?.includeDestructive ?? true;
|
|
11436
11769
|
const changes = includeDestructive ? diff.changes : diff.changes.filter((c) => !c.destructive);
|
|
@@ -11479,13 +11812,15 @@ function generateDownSQL(diff, options) {
|
|
|
11479
11812
|
phases[phase].push(change);
|
|
11480
11813
|
}
|
|
11481
11814
|
const statements = [];
|
|
11815
|
+
let destructive = false;
|
|
11482
11816
|
for (let i = phases.length - 1; i >= 0; i--) {
|
|
11483
11817
|
for (const change of phases[i]) {
|
|
11484
|
-
const
|
|
11485
|
-
|
|
11818
|
+
const down = changeToDownSQL(change, schemaName);
|
|
11819
|
+
destructive = down.destructive || destructive;
|
|
11820
|
+
if (down.sql) statements.push(down.sql);
|
|
11486
11821
|
}
|
|
11487
11822
|
}
|
|
11488
|
-
return statements;
|
|
11823
|
+
return { statements, destructive };
|
|
11489
11824
|
}
|
|
11490
11825
|
function generateCreateTableSQL(table, schemaName) {
|
|
11491
11826
|
const qualTable = qualifyTable(table.name, schemaName);
|
|
@@ -11778,29 +12113,36 @@ function generateCreatePolicy(tableName, policy, schemaName, naming) {
|
|
|
11778
12113
|
// src/ddl/migration-file.ts
|
|
11779
12114
|
function generateMigrationFile(diff, options) {
|
|
11780
12115
|
const upStatements = generateMigrationSQL(diff, options);
|
|
11781
|
-
const
|
|
12116
|
+
const downMigration = generateDownMigrationSQL(diff, options);
|
|
12117
|
+
const downStatements = downMigration.statements;
|
|
12118
|
+
const destructiveHeader = `-- dbsp:destructive: ${downMigration.destructive ? "true" : "false"}`;
|
|
11782
12119
|
const header = options?.name ? `-- Migration: ${options.name}
|
|
11783
12120
|
-- Generated by: dbsp migrate dev
|
|
11784
12121
|
-- Date: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
12122
|
+
${destructiveHeader}
|
|
11785
12123
|
|
|
11786
|
-
` :
|
|
12124
|
+
` : `${destructiveHeader}
|
|
12125
|
+
|
|
12126
|
+
`;
|
|
11787
12127
|
const upSection = upStatements.length > 0 ? `${upStatements.join(";\n\n")};
|
|
11788
12128
|
` : "";
|
|
11789
12129
|
const downSection = downStatements.length > 0 ? `${downStatements.join(";\n\n")};
|
|
11790
12130
|
` : "";
|
|
11791
|
-
|
|
11792
|
-
-- DOWN
|
|
12131
|
+
const separatorPrefix = upSection.length > 0 ? "\n" : "";
|
|
12132
|
+
return `${header}${upSection}${separatorPrefix}-- DOWN
|
|
11793
12133
|
|
|
11794
12134
|
${downSection}`;
|
|
11795
12135
|
}
|
|
11796
12136
|
function parseMigrationFile(content) {
|
|
12137
|
+
const destructive = parseDestructiveHeader(content);
|
|
11797
12138
|
const separatorRegex = /^\s*-- DOWN\s*$/m;
|
|
11798
12139
|
const match = separatorRegex.exec(content);
|
|
11799
12140
|
if (!match) {
|
|
11800
12141
|
return {
|
|
11801
12142
|
upStatements: splitStatements(content),
|
|
11802
12143
|
downStatements: [],
|
|
11803
|
-
hasDown: false
|
|
12144
|
+
hasDown: false,
|
|
12145
|
+
destructive
|
|
11804
12146
|
};
|
|
11805
12147
|
}
|
|
11806
12148
|
const upContent = content.slice(0, match.index);
|
|
@@ -11808,9 +12150,36 @@ function parseMigrationFile(content) {
|
|
|
11808
12150
|
return {
|
|
11809
12151
|
upStatements: splitStatements(upContent),
|
|
11810
12152
|
downStatements: splitStatements(downContent),
|
|
11811
|
-
hasDown: true
|
|
12153
|
+
hasDown: true,
|
|
12154
|
+
destructive
|
|
11812
12155
|
};
|
|
11813
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
|
+
}
|
|
11814
12183
|
function isDestructiveDown(downStatements) {
|
|
11815
12184
|
const destructivePatterns = [
|
|
11816
12185
|
/DROP\s+TABLE/i,
|
|
@@ -12933,6 +13302,7 @@ function vectorDims(column) {
|
|
|
12933
13302
|
// src/introspection.ts
|
|
12934
13303
|
init_assert_field();
|
|
12935
13304
|
import { ModelIRImpl } from "@dbsp/core";
|
|
13305
|
+
import { buildRelationKeyFields } from "@dbsp/types";
|
|
12936
13306
|
function parseExpressionsList(raw) {
|
|
12937
13307
|
const results = [];
|
|
12938
13308
|
let depth = 0;
|
|
@@ -13488,9 +13858,10 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13488
13858
|
for (const [, fk] of fksByConstraint) {
|
|
13489
13859
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
13490
13860
|
const belongsToName = deriveRelationName(fk.cols[0], fk.target);
|
|
13491
|
-
const
|
|
13492
|
-
|
|
13493
|
-
|
|
13861
|
+
const relationFk = {
|
|
13862
|
+
columns: fk.cols,
|
|
13863
|
+
references: { table: fk.target, columns: fk.refs }
|
|
13864
|
+
};
|
|
13494
13865
|
const belongsToKey = `${fk.source}.${belongsToName}`;
|
|
13495
13866
|
if (!relations.has(belongsToKey)) {
|
|
13496
13867
|
relations.set(belongsToKey, {
|
|
@@ -13498,8 +13869,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13498
13869
|
type: "belongsTo",
|
|
13499
13870
|
source: fk.source,
|
|
13500
13871
|
target: fk.target,
|
|
13501
|
-
|
|
13502
|
-
...keyOverride !== void 0 && { targetKey: keyOverride },
|
|
13872
|
+
...buildRelationKeyFields(relationFk, "belongsTo"),
|
|
13503
13873
|
cardinality: "one",
|
|
13504
13874
|
optionality: "optional",
|
|
13505
13875
|
includeStrategy: "auto",
|
|
@@ -13515,8 +13885,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13515
13885
|
type: "hasMany",
|
|
13516
13886
|
source: fk.target,
|
|
13517
13887
|
target: fk.source,
|
|
13518
|
-
|
|
13519
|
-
...keyOverride !== void 0 && { sourceKey: keyOverride },
|
|
13888
|
+
...buildRelationKeyFields(relationFk, "inverse"),
|
|
13520
13889
|
cardinality: "many",
|
|
13521
13890
|
optionality: "optional",
|
|
13522
13891
|
includeStrategy: "auto",
|
|
@@ -14419,7 +14788,7 @@ import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNql
|
|
|
14419
14788
|
|
|
14420
14789
|
// src/adapter-compiler-includes.ts
|
|
14421
14790
|
init_ast_helpers();
|
|
14422
|
-
import { toColumnList as
|
|
14791
|
+
import { toColumnList as toColumnList8 } from "@dbsp/types";
|
|
14423
14792
|
init_handlers();
|
|
14424
14793
|
function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
14425
14794
|
const schemaName = deps.schemaName;
|
|
@@ -14431,7 +14800,7 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14431
14800
|
parameters: []
|
|
14432
14801
|
};
|
|
14433
14802
|
}
|
|
14434
|
-
const fkColumns =
|
|
14803
|
+
const fkColumns = toColumnList8(info.foreignKey);
|
|
14435
14804
|
if (fkColumns.length === 0) {
|
|
14436
14805
|
throw new Error(
|
|
14437
14806
|
"Subquery include requires at least one foreignKey column."
|
|
@@ -14529,7 +14898,7 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
|
|
|
14529
14898
|
const throughTable = info.through;
|
|
14530
14899
|
const throughSourceKey = info.throughSourceKey;
|
|
14531
14900
|
const throughTargetKey = info.throughTargetKey;
|
|
14532
|
-
const targetPkColumns =
|
|
14901
|
+
const targetPkColumns = toColumnList8(info.sourceKey);
|
|
14533
14902
|
if (targetPkColumns.length !== 1) {
|
|
14534
14903
|
throw new Error(
|
|
14535
14904
|
`Many-to-many subquery include requires a single-column target key; got ${JSON.stringify(targetPkColumns)}.`
|
|
@@ -14624,7 +14993,7 @@ import {
|
|
|
14624
14993
|
POSTGRESQL_CAPABILITIES,
|
|
14625
14994
|
plan as planFn
|
|
14626
14995
|
} from "@dbsp/core";
|
|
14627
|
-
import { toColumnList as
|
|
14996
|
+
import { toColumnList as toColumnList11 } from "@dbsp/types";
|
|
14628
14997
|
|
|
14629
14998
|
// src/adapter-compiler-select.ts
|
|
14630
14999
|
init_assert_field();
|
|
@@ -14632,7 +15001,7 @@ init_ast_helpers();
|
|
|
14632
15001
|
init_binding_registry();
|
|
14633
15002
|
init_compile_where();
|
|
14634
15003
|
import { countDistinctRelationPathsByName } from "@dbsp/core";
|
|
14635
|
-
import { toColumnList as
|
|
15004
|
+
import { toColumnList as toColumnList10 } from "@dbsp/types";
|
|
14636
15005
|
init_compiler_utils();
|
|
14637
15006
|
init_types();
|
|
14638
15007
|
init_intent_to_decisions();
|
|
@@ -14642,7 +15011,7 @@ init_param_ref();
|
|
|
14642
15011
|
init_assert_field();
|
|
14643
15012
|
init_intent_to_decisions();
|
|
14644
15013
|
import { deriveRelationPathFromIntentPath } from "@dbsp/core";
|
|
14645
|
-
import { toColumnList as
|
|
15014
|
+
import { toColumnList as toColumnList9 } from "@dbsp/types";
|
|
14646
15015
|
function findExistsIntents(where) {
|
|
14647
15016
|
if (!where || typeof where !== "object") return [];
|
|
14648
15017
|
const w = where;
|
|
@@ -14663,7 +15032,7 @@ function findExistsIntents(where) {
|
|
|
14663
15032
|
function resolveRelation(model, sourceTable, relationName) {
|
|
14664
15033
|
const rel = model.getRelation(`${sourceTable}.${relationName}`);
|
|
14665
15034
|
if (!rel) return void 0;
|
|
14666
|
-
const foreignKeyColumns =
|
|
15035
|
+
const foreignKeyColumns = toColumnList9(rel.foreignKey);
|
|
14667
15036
|
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14668
15037
|
const relationType = rel.type;
|
|
14669
15038
|
return { target: rel.target, foreignKey, relationType };
|
|
@@ -14887,7 +15256,7 @@ function buildMultiHopExistsChain(hops, rootTable, outerOperator, innerCondition
|
|
|
14887
15256
|
const hopRelations = [];
|
|
14888
15257
|
for (const hop of hops) {
|
|
14889
15258
|
const rel = model.getRelation(`${currentSource}.${hop}`);
|
|
14890
|
-
const foreignKeyColumns = rel ?
|
|
15259
|
+
const foreignKeyColumns = rel ? toColumnList9(rel.foreignKey) : [];
|
|
14891
15260
|
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14892
15261
|
const relationType = rel?.type === "belongsTo" ? "belongsTo" : rel?.type === "hasMany" || rel?.type === "hasOne" ? rel.type : void 0;
|
|
14893
15262
|
const parentKey = relationType === "belongsTo" ? rel?.targetKey : rel?.sourceKey;
|
|
@@ -14998,7 +15367,7 @@ function enrichExistsStubsInConditions(conditions, sourceTable, model) {
|
|
|
14998
15367
|
`exists('${relation}'): no relation '${relation}' is declared on table '${sourceTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
14999
15368
|
);
|
|
15000
15369
|
}
|
|
15001
|
-
const foreignKeyColumns =
|
|
15370
|
+
const foreignKeyColumns = toColumnList9(rel.foreignKey);
|
|
15002
15371
|
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
15003
15372
|
const relationType = rel.type === "belongsTo" ? "belongsTo" : rel.type === "hasMany" || rel.type === "hasOne" ? rel.type : void 0;
|
|
15004
15373
|
const parentKey = relationType === "belongsTo" ? rel.targetKey : rel.sourceKey;
|
|
@@ -15058,7 +15427,7 @@ function buildEnrichedExistsDecision(d, matchingIntent, rootTable, model) {
|
|
|
15058
15427
|
const relIR = model && context.relation ? model.getRelation(
|
|
15059
15428
|
`${sourceTableForRelation}.${context.relation}`
|
|
15060
15429
|
) : void 0;
|
|
15061
|
-
const foreignKeyColumns = relIR ?
|
|
15430
|
+
const foreignKeyColumns = relIR ? toColumnList9(relIR.foreignKey) : [];
|
|
15062
15431
|
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
15063
15432
|
const relationType = relIR?.type === "belongsTo" ? "belongsTo" : relIR?.type === "hasMany" || relIR?.type === "hasOne" ? relIR.type : void 0;
|
|
15064
15433
|
const parentKey = relationType === "belongsTo" ? relIR?.targetKey : relIR?.sourceKey;
|
|
@@ -15395,10 +15764,7 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
15395
15764
|
...relationType && { relationType },
|
|
15396
15765
|
foreignKey,
|
|
15397
15766
|
parentKey,
|
|
15398
|
-
...context.
|
|
15399
|
-
targetPrimaryKey: context.targetPrimaryKey,
|
|
15400
|
-
orderBy: context.targetPrimaryKey
|
|
15401
|
-
} : {},
|
|
15767
|
+
...context.targetOrderKey && context.targetOrderKey.length > 0 ? { orderBy: context.targetOrderKey } : {},
|
|
15402
15768
|
...context.orderByFallback ? { orderByFallback: true } : {},
|
|
15403
15769
|
...context.intentPath && { intentPath: context.intentPath },
|
|
15404
15770
|
...limit != null && { limit }
|
|
@@ -15470,7 +15836,7 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15470
15836
|
(r) => r.name === alias || snakeToCamel(r.name) === alias
|
|
15471
15837
|
);
|
|
15472
15838
|
if (!rel) continue;
|
|
15473
|
-
const rawFkColumns =
|
|
15839
|
+
const rawFkColumns = toColumnList9(rel.foreignKey);
|
|
15474
15840
|
const rawFk = rawFkColumns.length > 0 ? rawFkColumns : [
|
|
15475
15841
|
deriveFk(
|
|
15476
15842
|
rel.type === "belongsTo" ? rel.target : sourceTable,
|
|
@@ -15498,7 +15864,7 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15498
15864
|
relationType: rel.type
|
|
15499
15865
|
},
|
|
15500
15866
|
foreignKey: rawFk,
|
|
15501
|
-
parentKey: rel.type === "belongsTo" ?
|
|
15867
|
+
parentKey: rel.type === "belongsTo" ? toColumnList9(rel.targetKey).length > 0 ? toColumnList9(rel.targetKey) : [defaultPk] : toColumnList9(rel.sourceKey).length > 0 ? toColumnList9(rel.sourceKey) : [defaultPk],
|
|
15502
15868
|
columns,
|
|
15503
15869
|
joinType: inc.join,
|
|
15504
15870
|
...conditions && { conditions }
|
|
@@ -15655,10 +16021,10 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
|
15655
16021
|
);
|
|
15656
16022
|
}
|
|
15657
16023
|
const isBelongsTo = rel.type === "belongsTo";
|
|
15658
|
-
const rawFk =
|
|
16024
|
+
const rawFk = toColumnList10(rel.foreignKey);
|
|
15659
16025
|
const fkColumns = rawFk.length > 0 ? rawFk : [deriveFk(isBelongsTo ? rootTable : rel.target, defaultPk)];
|
|
15660
|
-
const sourceKey =
|
|
15661
|
-
const targetKey =
|
|
16026
|
+
const sourceKey = toColumnList10(rel.sourceKey);
|
|
16027
|
+
const targetKey = toColumnList10(rel.targetKey);
|
|
15662
16028
|
const sourceColumn = isBelongsTo ? fkColumns : sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
15663
16029
|
const targetColumn = isBelongsTo ? targetKey.length > 0 ? targetKey : [defaultPk] : fkColumns;
|
|
15664
16030
|
const alias = intent.alias ?? intent.relation;
|
|
@@ -16023,8 +16389,8 @@ function compileWithIncludes(plan, options, deps) {
|
|
|
16023
16389
|
const relationName = ctx.includeAlias ?? ctx.relation;
|
|
16024
16390
|
if (!relationName) continue;
|
|
16025
16391
|
const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
|
|
16026
|
-
const fk =
|
|
16027
|
-
const parentKey =
|
|
16392
|
+
const fk = toColumnList10(rawFk);
|
|
16393
|
+
const parentKey = toColumnList10(ctx.parentKey);
|
|
16028
16394
|
const isBelongsTo = ctx.relationType === "belongsTo";
|
|
16029
16395
|
const sourceKey = isBelongsTo ? fk : parentKey.length > 0 ? parentKey : [deps.defaultPk];
|
|
16030
16396
|
const targetFk = isBelongsTo ? parentKey.length > 0 ? parentKey : [deps.defaultPk] : fk;
|
|
@@ -16090,7 +16456,7 @@ function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
|
|
|
16090
16456
|
};
|
|
16091
16457
|
}
|
|
16092
16458
|
function resolveMutationExistsForeignKey(foreignKey, relationName) {
|
|
16093
|
-
const columns =
|
|
16459
|
+
const columns = toColumnList11(foreignKey);
|
|
16094
16460
|
if (columns.length === 0) return void 0;
|
|
16095
16461
|
if (columns.some((column) => column.length === 0)) {
|
|
16096
16462
|
throw new Error(
|
|
@@ -16110,13 +16476,13 @@ function resolveExistsRelation(sourceTable, relation, model) {
|
|
|
16110
16476
|
return {
|
|
16111
16477
|
targetTable,
|
|
16112
16478
|
...fk2 !== void 0 && { sourceColumn: fk2 },
|
|
16113
|
-
targetColumn:
|
|
16479
|
+
targetColumn: toColumnList11(rel.targetKey).length > 0 ? toColumnList11(rel.targetKey) : ["id"]
|
|
16114
16480
|
};
|
|
16115
16481
|
}
|
|
16116
16482
|
const fk = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
|
|
16117
16483
|
return {
|
|
16118
16484
|
targetTable,
|
|
16119
|
-
sourceColumn:
|
|
16485
|
+
sourceColumn: toColumnList11(rel.sourceKey).length > 0 ? toColumnList11(rel.sourceKey) : ["id"],
|
|
16120
16486
|
...fk !== void 0 && { targetColumn: fk }
|
|
16121
16487
|
};
|
|
16122
16488
|
}
|
|
@@ -18038,6 +18404,9 @@ function orderedNqlBindingNames(bundle) {
|
|
|
18038
18404
|
}
|
|
18039
18405
|
return names;
|
|
18040
18406
|
}
|
|
18407
|
+
function runtimeBindingSourceTable(bundle, name) {
|
|
18408
|
+
return bundle.mutationBindings?.get(name)?.table ?? bundle.bindings?.get(name)?.from;
|
|
18409
|
+
}
|
|
18041
18410
|
function mapRuntimeBindingColumnType(type) {
|
|
18042
18411
|
switch (type) {
|
|
18043
18412
|
case "string":
|
|
@@ -18090,13 +18459,13 @@ function resolveRuntimeBindingColumnType(bindingName, sourceTable, columnName) {
|
|
|
18090
18459
|
);
|
|
18091
18460
|
if (column === void 0) {
|
|
18092
18461
|
throw new Error(
|
|
18093
|
-
`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}'.`
|
|
18094
18463
|
);
|
|
18095
18464
|
}
|
|
18096
18465
|
const dbType = column.originalDbType?.trim() || mapRuntimeBindingColumnType(column.type);
|
|
18097
18466
|
if (dbType === void 0 || dbType.trim() === "") {
|
|
18098
18467
|
throw new Error(
|
|
18099
|
-
`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}'.`
|
|
18100
18469
|
);
|
|
18101
18470
|
}
|
|
18102
18471
|
const typeName = dbType.trim();
|
|
@@ -18119,7 +18488,7 @@ function resolveRuntimeBindingColumnTypes(name, binding, model, sourceTableName)
|
|
|
18119
18488
|
const sourceTable = findRuntimeBindingSourceTable(model, sourceTableName);
|
|
18120
18489
|
if (sourceTable === void 0) {
|
|
18121
18490
|
throw new Error(
|
|
18122
|
-
`NQL runtime binding '${name}' cannot resolve source
|
|
18491
|
+
`NQL runtime binding '${name}' cannot resolve source table '${sourceTableName}' in the model.`
|
|
18123
18492
|
);
|
|
18124
18493
|
}
|
|
18125
18494
|
return binding.columns.map(
|
|
@@ -18146,7 +18515,7 @@ function compileNqlRuntimeBindingCte(name, binding, naming, parameterOffset, sou
|
|
|
18146
18515
|
const columnSql = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
|
|
18147
18516
|
if (sourceTable === void 0) {
|
|
18148
18517
|
throw new Error(
|
|
18149
|
-
`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.`
|
|
18150
18519
|
);
|
|
18151
18520
|
}
|
|
18152
18521
|
const projectedColumns = binding.columns.map((column) => quoteIdent2(naming.toDatabase(column), "column")).join(", ");
|
|
@@ -18421,7 +18790,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18421
18790
|
runtimeBinding,
|
|
18422
18791
|
naming,
|
|
18423
18792
|
parameters.length,
|
|
18424
|
-
bundle
|
|
18793
|
+
runtimeBindingSourceTable(bundle, name),
|
|
18425
18794
|
deps.schemaName,
|
|
18426
18795
|
deps.model
|
|
18427
18796
|
);
|