@dbsp/adapter-pgsql 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +23 -13
- package/dist/index.js +565 -162
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -588,12 +588,6 @@ function binaryExpr(operator, left, right, kind = "AEXPR_OP") {
|
|
|
588
588
|
function eqExpr(left, right) {
|
|
589
589
|
return binaryExpr("=", left, right);
|
|
590
590
|
}
|
|
591
|
-
function fkCorrelation(col1, alias1, col2, alias2, naming) {
|
|
592
|
-
return eqExpr(
|
|
593
|
-
columnRef(col1, alias1, void 0, naming),
|
|
594
|
-
columnRef(col2, alias2, void 0, naming)
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
591
|
function neExpr(left, right) {
|
|
598
592
|
return binaryExpr("<>", left, right);
|
|
599
593
|
}
|
|
@@ -885,10 +879,23 @@ function jsonAggSubquery(targetTable, whereExpr, alias, schemaName, naming = ide
|
|
|
885
879
|
}
|
|
886
880
|
};
|
|
887
881
|
}
|
|
882
|
+
const aggOrder = options?.orderBy && options.orderBy.length > 0 ? options.orderBy.map(
|
|
883
|
+
(col) => sortBy(
|
|
884
|
+
jsonAggOrderByExpression(
|
|
885
|
+
col,
|
|
886
|
+
targetAlias,
|
|
887
|
+
naming,
|
|
888
|
+
options.orderByFallback === true
|
|
889
|
+
),
|
|
890
|
+
"ASC",
|
|
891
|
+
"LAST"
|
|
892
|
+
)
|
|
893
|
+
) : void 0;
|
|
888
894
|
const jsonAggCall = {
|
|
889
895
|
FuncCall: {
|
|
890
896
|
funcname: [stringNode("json_agg")],
|
|
891
|
-
args: [toJsonbCall]
|
|
897
|
+
args: [toJsonbCall],
|
|
898
|
+
...aggOrder && { agg_order: aggOrder }
|
|
892
899
|
}
|
|
893
900
|
};
|
|
894
901
|
const fromTable = rangeVar(targetTable, targetAlias, schemaName, naming);
|
|
@@ -923,11 +930,9 @@ function jsonAggSubquery(targetTable, whereExpr, alias, schemaName, naming = ide
|
|
|
923
930
|
}
|
|
924
931
|
};
|
|
925
932
|
}
|
|
926
|
-
function
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
columnRef(parentColumn, parentAlias, void 0, naming)
|
|
930
|
-
);
|
|
933
|
+
function jsonAggOrderByExpression(entry, targetAlias, naming, castToText) {
|
|
934
|
+
const ref = columnRef(entry, targetAlias, void 0, naming);
|
|
935
|
+
return castToText ? typeCast(ref, "text") : ref;
|
|
931
936
|
}
|
|
932
937
|
var STRENGTH_MAP, POLICY_MAP;
|
|
933
938
|
var init_ast_helpers = __esm({
|
|
@@ -1457,6 +1462,10 @@ var init_custom = __esm({
|
|
|
1457
1462
|
});
|
|
1458
1463
|
|
|
1459
1464
|
// src/select-expression-handlers.ts
|
|
1465
|
+
import {
|
|
1466
|
+
getTrustedNqlRelationFilterFields,
|
|
1467
|
+
markNqlTrustedRelationFilter
|
|
1468
|
+
} from "@dbsp/types/internal";
|
|
1460
1469
|
function handleColumnExpression(expr, rootTable, decisions) {
|
|
1461
1470
|
const decision = {
|
|
1462
1471
|
type: "select",
|
|
@@ -1585,14 +1594,18 @@ function handleCaseExpression(expr, rootTable, decisions, _applyFilter, convertC
|
|
|
1585
1594
|
decisions.push(decision);
|
|
1586
1595
|
}
|
|
1587
1596
|
function handleRelationColumnExpression(expr, rootTable, decisions) {
|
|
1597
|
+
const trusted = getTrustedNqlRelationFilterFields(expr);
|
|
1598
|
+
const trustedRelation = typeof trusted?.relation === "string" ? trusted.relation : trusted?.relation.join(".");
|
|
1588
1599
|
const decision = {
|
|
1589
1600
|
type: "selectRelationColumn",
|
|
1590
|
-
relation: expr.relation,
|
|
1591
|
-
column: expr.column ?? "*",
|
|
1601
|
+
relation: trustedRelation ?? expr.relation,
|
|
1602
|
+
column: trusted?.selectedColumn ?? (expr.column ?? "*"),
|
|
1592
1603
|
table: rootTable
|
|
1593
1604
|
};
|
|
1594
1605
|
if (expr.as) decision.alias = expr.as;
|
|
1595
|
-
decisions.push(
|
|
1606
|
+
decisions.push(
|
|
1607
|
+
trusted?.selectedColumn !== void 0 ? markNqlTrustedRelationFilter(decision, trusted) : decision
|
|
1608
|
+
);
|
|
1596
1609
|
}
|
|
1597
1610
|
function handleArithmeticExpression(expr, rootTable, decisions) {
|
|
1598
1611
|
const decision = {
|
|
@@ -1690,6 +1703,9 @@ var init_select_expression_handlers = __esm({
|
|
|
1690
1703
|
|
|
1691
1704
|
// src/intent-to-decisions.ts
|
|
1692
1705
|
import { isParamIntent } from "@dbsp/types";
|
|
1706
|
+
import {
|
|
1707
|
+
getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields2
|
|
1708
|
+
} from "@dbsp/types/internal";
|
|
1693
1709
|
function intentToDecisions(intent, rootTable) {
|
|
1694
1710
|
const decisions = [];
|
|
1695
1711
|
if (intent.select) {
|
|
@@ -2075,10 +2091,29 @@ function convertNot(cond, rootTable) {
|
|
|
2075
2091
|
if (!subDecision) return null;
|
|
2076
2092
|
return { type: "whereNot", conditions: [subDecision] };
|
|
2077
2093
|
}
|
|
2094
|
+
function formatRelationName(relation) {
|
|
2095
|
+
return typeof relation === "string" ? relation : relation.join(".");
|
|
2096
|
+
}
|
|
2078
2097
|
function convertExistsLike(cond, operator) {
|
|
2079
|
-
const
|
|
2098
|
+
const rawRelation = cond.relation;
|
|
2099
|
+
const preResolved = getTrustedNqlRelationFilterFields2(cond);
|
|
2100
|
+
const trustedRelation = preResolved?.relation;
|
|
2101
|
+
const relationName = trustedRelation !== void 0 ? formatRelationName(trustedRelation) : formatRelationName(rawRelation);
|
|
2102
|
+
const targetTable = preResolved?.targetTable ?? rawRelation;
|
|
2080
2103
|
const subDecisions = cond.where ? convertWhere(cond.where, targetTable) : [];
|
|
2081
|
-
const
|
|
2104
|
+
const isPreResolved = preResolved !== void 0;
|
|
2105
|
+
const base = {
|
|
2106
|
+
type: "where",
|
|
2107
|
+
operator,
|
|
2108
|
+
targetTable,
|
|
2109
|
+
...preResolved !== void 0 && {
|
|
2110
|
+
sourceColumn: preResolved.sourceColumn
|
|
2111
|
+
},
|
|
2112
|
+
...preResolved !== void 0 && {
|
|
2113
|
+
targetColumn: preResolved.targetColumn
|
|
2114
|
+
},
|
|
2115
|
+
...isPreResolved && { relationName }
|
|
2116
|
+
};
|
|
2082
2117
|
return subDecisions.length > 0 ? { ...base, conditions: subDecisions } : base;
|
|
2083
2118
|
}
|
|
2084
2119
|
function convertSubquery(cond) {
|
|
@@ -3142,6 +3177,7 @@ var init_custom_expression = __esm({
|
|
|
3142
3177
|
});
|
|
3143
3178
|
|
|
3144
3179
|
// src/handlers/where/exists.ts
|
|
3180
|
+
import { toColumnList } from "@dbsp/types";
|
|
3145
3181
|
function createSubLinkExists(subquery, negated) {
|
|
3146
3182
|
const subLink = {
|
|
3147
3183
|
subLinkType: "EXISTS_SUBLINK",
|
|
@@ -3158,10 +3194,27 @@ function createSubLinkExists(subquery, negated) {
|
|
|
3158
3194
|
}
|
|
3159
3195
|
return existsNode;
|
|
3160
3196
|
}
|
|
3161
|
-
function
|
|
3162
|
-
const
|
|
3163
|
-
const
|
|
3164
|
-
|
|
3197
|
+
function buildKeyCorrelation(sourceAlias, sourceCols, targetAlias, targetCols, ctx) {
|
|
3198
|
+
const normalizedSourceCols = toColumnList(sourceCols);
|
|
3199
|
+
const normalizedTargetCols = toColumnList(targetCols);
|
|
3200
|
+
if (normalizedSourceCols.length === 0 || normalizedTargetCols.length === 0 || normalizedSourceCols.length !== normalizedTargetCols.length || normalizedSourceCols.some((column) => column.length === 0) || normalizedTargetCols.some((column) => column.length === 0)) {
|
|
3201
|
+
throw new Error(
|
|
3202
|
+
`Invalid relation correlation: source columns (${normalizedSourceCols.length}) must match target columns (${normalizedTargetCols.length}) and both must be non-empty.`
|
|
3203
|
+
);
|
|
3204
|
+
}
|
|
3205
|
+
const comparisons = normalizedSourceCols.map(
|
|
3206
|
+
(sourceColumn, index) => eqExpr(
|
|
3207
|
+
columnRef(sourceColumn, sourceAlias, void 0, ctx.naming),
|
|
3208
|
+
columnRef(
|
|
3209
|
+
normalizedTargetCols[index],
|
|
3210
|
+
targetAlias,
|
|
3211
|
+
void 0,
|
|
3212
|
+
ctx.naming
|
|
3213
|
+
)
|
|
3214
|
+
)
|
|
3215
|
+
);
|
|
3216
|
+
if (comparisons.length === 1) return comparisons[0];
|
|
3217
|
+
return andExpr(...comparisons);
|
|
3165
3218
|
}
|
|
3166
3219
|
function schemaForExistsFromName(ctx, fromName) {
|
|
3167
3220
|
return schemaForFromName(ctx.schema, fromName, ctx.bindingNames, ctx.naming);
|
|
@@ -3169,11 +3222,15 @@ function schemaForExistsFromName(ctx, fromName) {
|
|
|
3169
3222
|
function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
3170
3223
|
const relation = decision.relation;
|
|
3171
3224
|
const targetTable = decision.targetTable ?? relation;
|
|
3172
|
-
const sourceColumn = decision.sourceColumn ??
|
|
3173
|
-
const targetColumn = decision.targetColumn ?? (ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3174
|
-
ctx.rootTable,
|
|
3225
|
+
const sourceColumn = decision.sourceColumn ?? [
|
|
3175
3226
|
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3176
|
-
|
|
3227
|
+
];
|
|
3228
|
+
const targetColumn = decision.targetColumn ?? [
|
|
3229
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3230
|
+
ctx.rootTable,
|
|
3231
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3232
|
+
)
|
|
3233
|
+
];
|
|
3177
3234
|
if (!targetTable) {
|
|
3178
3235
|
throw new Error("EXISTS handler requires targetTable or relation");
|
|
3179
3236
|
}
|
|
@@ -3181,7 +3238,7 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3181
3238
|
const targetAlias = `${targetTable}_exists_${existingAliases}`;
|
|
3182
3239
|
state.aliases.set(`exists_${targetTable}`, targetAlias);
|
|
3183
3240
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
3184
|
-
const correlation =
|
|
3241
|
+
const correlation = buildKeyCorrelation(
|
|
3185
3242
|
sourceAlias,
|
|
3186
3243
|
sourceColumn,
|
|
3187
3244
|
targetAlias,
|
|
@@ -3219,8 +3276,8 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3219
3276
|
const joinRelation = inc.relation;
|
|
3220
3277
|
if (!joinRelation) continue;
|
|
3221
3278
|
let joinTargetTable = joinRelation;
|
|
3222
|
-
let
|
|
3223
|
-
let
|
|
3279
|
+
let joinSourceCols;
|
|
3280
|
+
let joinTargetCols;
|
|
3224
3281
|
let sourceAliasForJoin = targetAlias;
|
|
3225
3282
|
const model = ctx.model;
|
|
3226
3283
|
if (model) {
|
|
@@ -3238,31 +3295,41 @@ function buildExistsSubquery(decision, ctx, state, dispatch) {
|
|
|
3238
3295
|
if (rel) {
|
|
3239
3296
|
joinTargetTable = rel.target;
|
|
3240
3297
|
if (rel.type === "belongsTo") {
|
|
3241
|
-
const fk =
|
|
3242
|
-
|
|
3243
|
-
|
|
3298
|
+
const fk = toColumnList(rel.foreignKey);
|
|
3299
|
+
joinSourceCols = fk.length > 0 ? fk : void 0;
|
|
3300
|
+
const targetKey = toColumnList(rel.targetKey);
|
|
3301
|
+
joinTargetCols = targetKey.length > 0 ? targetKey : [
|
|
3302
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3303
|
+
];
|
|
3244
3304
|
} else {
|
|
3245
|
-
const fk =
|
|
3246
|
-
|
|
3247
|
-
|
|
3305
|
+
const fk = toColumnList(rel.foreignKey);
|
|
3306
|
+
const sourceKey = toColumnList(rel.sourceKey);
|
|
3307
|
+
joinSourceCols = sourceKey.length > 0 ? sourceKey : [
|
|
3308
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3309
|
+
];
|
|
3310
|
+
joinTargetCols = fk.length > 0 ? fk : void 0;
|
|
3248
3311
|
}
|
|
3249
3312
|
}
|
|
3250
3313
|
}
|
|
3251
|
-
if (!
|
|
3252
|
-
|
|
3253
|
-
|
|
3314
|
+
if (!joinSourceCols || joinSourceCols.length === 0) {
|
|
3315
|
+
joinSourceCols = [
|
|
3316
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
3317
|
+
joinRelation,
|
|
3318
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3319
|
+
)
|
|
3320
|
+
];
|
|
3321
|
+
joinTargetCols = [
|
|
3254
3322
|
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
3255
|
-
|
|
3256
|
-
joinTargetCol = ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN;
|
|
3323
|
+
];
|
|
3257
3324
|
}
|
|
3258
3325
|
const joinAlias = joinRelation;
|
|
3259
|
-
const joinQuals =
|
|
3260
|
-
joinSourceCol,
|
|
3326
|
+
const joinQuals = buildKeyCorrelation(
|
|
3261
3327
|
sourceAliasForJoin,
|
|
3262
3328
|
// resolved source alias (root or intermediate)
|
|
3263
|
-
|
|
3329
|
+
joinSourceCols,
|
|
3264
3330
|
joinAlias,
|
|
3265
|
-
|
|
3331
|
+
joinTargetCols,
|
|
3332
|
+
ctx
|
|
3266
3333
|
);
|
|
3267
3334
|
const joinType = inc.joinType === "left" ? "JOIN_LEFT" : "JOIN_INNER";
|
|
3268
3335
|
const joinRangeVar = rangeVar(
|
|
@@ -3744,6 +3811,8 @@ var init_range = __esm({
|
|
|
3744
3811
|
});
|
|
3745
3812
|
|
|
3746
3813
|
// src/compile-where.ts
|
|
3814
|
+
import { toColumnList as toColumnList2 } from "@dbsp/types";
|
|
3815
|
+
import { getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields3 } from "@dbsp/types/internal";
|
|
3747
3816
|
function toHandlerContext(ctx) {
|
|
3748
3817
|
return {
|
|
3749
3818
|
naming: ctx.naming,
|
|
@@ -3937,11 +4006,16 @@ function handleSubqueryIntent(intent, ctx, _handlerCtx) {
|
|
|
3937
4006
|
}
|
|
3938
4007
|
function handleRelationFilterIntent(intent, ctx) {
|
|
3939
4008
|
const rf = intent;
|
|
3940
|
-
const
|
|
4009
|
+
const preResolved = getTrustedNqlRelationFilterFields3(rf);
|
|
4010
|
+
const relationPath = preResolved?.relation ?? rf.relation;
|
|
4011
|
+
const hops = Array.isArray(relationPath) ? [...relationPath] : [relationPath];
|
|
3941
4012
|
const existsKind = rf.mode === "none" || rf.mode === "every" ? "notExists" : "exists";
|
|
3942
4013
|
const rfWhere = rf.where;
|
|
3943
4014
|
const isVacuousEvery = rf.mode === "every" && (!rfWhere || typeof rfWhere === "object" && rfWhere.kind === "and" && Array.isArray(rfWhere.conditions) && rfWhere.conditions.length === 0);
|
|
3944
4015
|
if (isVacuousEvery) {
|
|
4016
|
+
if (preResolved) {
|
|
4017
|
+
return { A_Const: { boolval: { boolval: true } } };
|
|
4018
|
+
}
|
|
3945
4019
|
if (hops.length <= 1) {
|
|
3946
4020
|
const relation = hops[0] ?? rf.relation;
|
|
3947
4021
|
if (!ctx.model) {
|
|
@@ -3977,6 +4051,19 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
3977
4051
|
const innermostWhere = rf.mode === "every" ? { kind: "not", condition: rf.where } : rf.where;
|
|
3978
4052
|
if (hops.length <= 1) {
|
|
3979
4053
|
const relation = hops[0] ?? rf.relation;
|
|
4054
|
+
if (preResolved) {
|
|
4055
|
+
return compileWhereIntent(
|
|
4056
|
+
{
|
|
4057
|
+
kind: existsKind,
|
|
4058
|
+
relation,
|
|
4059
|
+
targetTable: preResolved.targetTable,
|
|
4060
|
+
sourceColumn: preResolved.sourceColumn,
|
|
4061
|
+
targetColumn: preResolved.targetColumn,
|
|
4062
|
+
where: innermostWhere
|
|
4063
|
+
},
|
|
4064
|
+
ctx
|
|
4065
|
+
);
|
|
4066
|
+
}
|
|
3980
4067
|
const resolvedRelation = ctx.model?.getRelation(
|
|
3981
4068
|
`${ctx.rootTable}.${relation}`
|
|
3982
4069
|
);
|
|
@@ -3990,14 +4077,16 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
3990
4077
|
let singleHopTargetColumn;
|
|
3991
4078
|
if (resolvedRelation) {
|
|
3992
4079
|
const rel = resolvedRelation;
|
|
3993
|
-
const fk =
|
|
4080
|
+
const fk = toColumnList2(rel.foreignKey);
|
|
3994
4081
|
const defaultPk = DEFAULT_PK_COLUMN;
|
|
3995
4082
|
if (rel.type === "belongsTo") {
|
|
3996
|
-
singleHopSourceColumn = fk
|
|
3997
|
-
|
|
4083
|
+
singleHopSourceColumn = fk.length > 0 ? fk : [defaultFkDerivation(rel.target, defaultPk)];
|
|
4084
|
+
const targetKey = toColumnList2(rel.targetKey);
|
|
4085
|
+
singleHopTargetColumn = targetKey.length > 0 ? targetKey : [defaultPk];
|
|
3998
4086
|
} else {
|
|
3999
|
-
|
|
4000
|
-
|
|
4087
|
+
const sourceKey = toColumnList2(rel.sourceKey);
|
|
4088
|
+
singleHopSourceColumn = sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
4089
|
+
singleHopTargetColumn = fk.length > 0 ? fk : [defaultFkDerivation(ctx.rootTable, defaultPk)];
|
|
4001
4090
|
}
|
|
4002
4091
|
}
|
|
4003
4092
|
return compileWhereIntent(
|
|
@@ -4034,15 +4123,19 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
4034
4123
|
);
|
|
4035
4124
|
}
|
|
4036
4125
|
hopTargets.push(rel.target);
|
|
4037
|
-
const fk =
|
|
4126
|
+
const fk = toColumnList2(rel.foreignKey);
|
|
4038
4127
|
const defaultPk = DEFAULT_PK_COLUMN;
|
|
4039
4128
|
if (rel.type === "belongsTo") {
|
|
4040
|
-
hopSourceColumns.push(
|
|
4041
|
-
|
|
4129
|
+
hopSourceColumns.push(
|
|
4130
|
+
fk.length > 0 ? fk : [defaultFkDerivation(rel.target, defaultPk)]
|
|
4131
|
+
);
|
|
4132
|
+
const targetKey = toColumnList2(rel.targetKey);
|
|
4133
|
+
hopTargetColumns.push(targetKey.length > 0 ? targetKey : [defaultPk]);
|
|
4042
4134
|
} else {
|
|
4043
|
-
|
|
4135
|
+
const sourceKey = toColumnList2(rel.sourceKey);
|
|
4136
|
+
hopSourceColumns.push(sourceKey.length > 0 ? sourceKey : [defaultPk]);
|
|
4044
4137
|
hopTargetColumns.push(
|
|
4045
|
-
fk
|
|
4138
|
+
fk.length > 0 ? fk : [defaultFkDerivation(currentSource, defaultPk)]
|
|
4046
4139
|
);
|
|
4047
4140
|
}
|
|
4048
4141
|
currentSource = rel.target;
|
|
@@ -4270,6 +4363,7 @@ var init_raw_exists = __esm({
|
|
|
4270
4363
|
});
|
|
4271
4364
|
|
|
4272
4365
|
// src/handlers/where/relation-filter.ts
|
|
4366
|
+
import { toColumnList as toColumnList3 } from "@dbsp/types";
|
|
4273
4367
|
function getFilterMode(decision) {
|
|
4274
4368
|
const operator = decision.operator;
|
|
4275
4369
|
if (operator === "some" || operator === "exists") return "some";
|
|
@@ -4281,15 +4375,18 @@ function getFilterMode(decision) {
|
|
|
4281
4375
|
function buildJoinFilter(decision, ctx, state, dispatch) {
|
|
4282
4376
|
const relation = decision.relation;
|
|
4283
4377
|
const targetTable = decision.targetTable ?? relation;
|
|
4284
|
-
const sourceColumn =
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
ctx.
|
|
4292
|
-
|
|
4378
|
+
const sourceColumn = toColumnList3(decision.sourceColumn);
|
|
4379
|
+
if (sourceColumn.length === 0) {
|
|
4380
|
+
throw new Error(
|
|
4381
|
+
"Missing required column 'sourceColumn' in relation filter"
|
|
4382
|
+
);
|
|
4383
|
+
}
|
|
4384
|
+
const targetColumn = decision.targetColumn ?? [
|
|
4385
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
4386
|
+
ctx.rootTable,
|
|
4387
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
4388
|
+
)
|
|
4389
|
+
];
|
|
4293
4390
|
if (!targetTable) {
|
|
4294
4391
|
throw new Error("Relation filter requires targetTable");
|
|
4295
4392
|
}
|
|
@@ -4297,9 +4394,12 @@ function buildJoinFilter(decision, ctx, state, dispatch) {
|
|
|
4297
4394
|
const targetAlias = `${targetTable}_rel_${existingAliases}`;
|
|
4298
4395
|
state.aliases.set(`rel_${targetTable}`, targetAlias);
|
|
4299
4396
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
4300
|
-
const joinCondition =
|
|
4301
|
-
|
|
4302
|
-
|
|
4397
|
+
const joinCondition = buildKeyCorrelation(
|
|
4398
|
+
sourceAlias,
|
|
4399
|
+
sourceColumn,
|
|
4400
|
+
targetAlias,
|
|
4401
|
+
targetColumn,
|
|
4402
|
+
ctx
|
|
4303
4403
|
);
|
|
4304
4404
|
const joinExpr2 = {
|
|
4305
4405
|
jointype: "JOIN_INNER",
|
|
@@ -4334,6 +4434,7 @@ var init_relation_filter = __esm({
|
|
|
4334
4434
|
"use strict";
|
|
4335
4435
|
init_assert_field();
|
|
4336
4436
|
init_ast_helpers();
|
|
4437
|
+
init_exists();
|
|
4337
4438
|
relationFilterHandler = {
|
|
4338
4439
|
operators: ["relationFilter", "relation", "is", "isNot"],
|
|
4339
4440
|
compile(decision, ctx, state, dispatch) {
|
|
@@ -4763,6 +4864,7 @@ var init_where = __esm({
|
|
|
4763
4864
|
});
|
|
4764
4865
|
|
|
4765
4866
|
// src/handlers/include/cte.ts
|
|
4867
|
+
import { toColumnList as toColumnList4 } from "@dbsp/types";
|
|
4766
4868
|
function buildCteTargets(columns, alias, ctx) {
|
|
4767
4869
|
if (columns && columns.length > 0 && !columns.every((c) => c === "*")) {
|
|
4768
4870
|
return columns.filter((col) => col !== "*").map((col) => ({
|
|
@@ -4813,12 +4915,12 @@ function buildCTE(cteName, cteSelect, ctx) {
|
|
|
4813
4915
|
return { CommonTableExpr: cte };
|
|
4814
4916
|
}
|
|
4815
4917
|
function buildCteJoin(cteName, cteAlias, sourceAlias, sourceColumn, targetColumn, ctx) {
|
|
4816
|
-
const joinCondition =
|
|
4817
|
-
sourceColumn,
|
|
4918
|
+
const joinCondition = buildKeyCorrelation(
|
|
4818
4919
|
sourceAlias,
|
|
4819
|
-
|
|
4920
|
+
sourceColumn,
|
|
4820
4921
|
cteAlias,
|
|
4821
|
-
|
|
4922
|
+
targetColumn,
|
|
4923
|
+
ctx
|
|
4822
4924
|
);
|
|
4823
4925
|
const cteRef = {
|
|
4824
4926
|
RangeVar: {
|
|
@@ -4842,20 +4944,22 @@ var init_cte = __esm({
|
|
|
4842
4944
|
init_assert_field();
|
|
4843
4945
|
init_ast_helpers();
|
|
4844
4946
|
init_handlers();
|
|
4947
|
+
init_exists();
|
|
4845
4948
|
cteIncludeHandler = {
|
|
4846
4949
|
strategy: "cte",
|
|
4847
4950
|
compile(decision, ctx, state) {
|
|
4848
4951
|
const relation = decision.relation;
|
|
4849
4952
|
const targetTable = decision.targetTable ?? relation;
|
|
4850
|
-
const sourceColumn =
|
|
4851
|
-
|
|
4852
|
-
"sourceColumn"
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4953
|
+
const sourceColumn = toColumnList4(decision.sourceColumn);
|
|
4954
|
+
if (sourceColumn.length === 0 || sourceColumn.some((col) => col === "")) {
|
|
4955
|
+
throw new Error("Missing required column 'sourceColumn' in CTE include");
|
|
4956
|
+
}
|
|
4957
|
+
const targetColumn = decision.targetColumn ?? [
|
|
4958
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
4959
|
+
ctx.rootTable,
|
|
4960
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
4961
|
+
)
|
|
4962
|
+
];
|
|
4859
4963
|
const columns = decision.columns;
|
|
4860
4964
|
const conditions = decision.conditions;
|
|
4861
4965
|
if (!targetTable) {
|
|
@@ -4898,13 +5002,14 @@ var init_cte = __esm({
|
|
|
4898
5002
|
});
|
|
4899
5003
|
|
|
4900
5004
|
// src/handlers/include/join.ts
|
|
5005
|
+
import { toColumnList as toColumnList5 } from "@dbsp/types";
|
|
4901
5006
|
function buildJoin(targetTable, targetAlias, sourceAlias, sourceColumn, targetColumn, ctx, joinType = "left") {
|
|
4902
|
-
const joinCondition =
|
|
4903
|
-
sourceColumn,
|
|
5007
|
+
const joinCondition = buildKeyCorrelation(
|
|
4904
5008
|
sourceAlias,
|
|
4905
|
-
|
|
5009
|
+
sourceColumn,
|
|
4906
5010
|
targetAlias,
|
|
4907
|
-
|
|
5011
|
+
targetColumn,
|
|
5012
|
+
ctx
|
|
4908
5013
|
);
|
|
4909
5014
|
const joinExpr2 = {
|
|
4910
5015
|
jointype: joinType === "inner" ? "JOIN_INNER" : "JOIN_LEFT",
|
|
@@ -4919,6 +5024,7 @@ var init_join = __esm({
|
|
|
4919
5024
|
"use strict";
|
|
4920
5025
|
init_assert_field();
|
|
4921
5026
|
init_ast_helpers();
|
|
5027
|
+
init_exists();
|
|
4922
5028
|
joinIncludeHandler = {
|
|
4923
5029
|
strategy: "join",
|
|
4924
5030
|
compile(decision, ctx, _state) {
|
|
@@ -4929,15 +5035,16 @@ var init_join = __esm({
|
|
|
4929
5035
|
}
|
|
4930
5036
|
const targetAlias = relation ?? targetTable;
|
|
4931
5037
|
const sourceAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
4932
|
-
const sourceColumn =
|
|
4933
|
-
|
|
4934
|
-
"sourceColumn"
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
5038
|
+
const sourceColumn = toColumnList5(decision.sourceColumn);
|
|
5039
|
+
if (sourceColumn.length === 0) {
|
|
5040
|
+
throw new Error("Missing required column 'sourceColumn' in JOIN include");
|
|
5041
|
+
}
|
|
5042
|
+
const targetColumn = decision.targetColumn ?? [
|
|
5043
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
5044
|
+
ctx.rootTable,
|
|
5045
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
5046
|
+
)
|
|
5047
|
+
];
|
|
4941
5048
|
const join = buildJoin(
|
|
4942
5049
|
targetTable,
|
|
4943
5050
|
targetAlias,
|
|
@@ -4991,6 +5098,22 @@ var init_shared = __esm({
|
|
|
4991
5098
|
});
|
|
4992
5099
|
|
|
4993
5100
|
// src/handlers/include/json-agg.ts
|
|
5101
|
+
import { toColumnList as toColumnList6 } from "@dbsp/types";
|
|
5102
|
+
function resolveJsonAggOrderBy(decision, targetTable, ctx) {
|
|
5103
|
+
const table = ctx.model?.getTable(targetTable);
|
|
5104
|
+
if (table) {
|
|
5105
|
+
const pkColumns = toColumnList6(table.primaryKey);
|
|
5106
|
+
const orderIntent = pkColumns.length > 0 ? { columns: pkColumns, fallback: false } : {
|
|
5107
|
+
columns: table.columns.map((col) => col.name),
|
|
5108
|
+
fallback: true
|
|
5109
|
+
};
|
|
5110
|
+
return orderIntent.columns.length > 0 ? orderIntent : void 0;
|
|
5111
|
+
}
|
|
5112
|
+
return decision.targetPrimaryKey && decision.targetPrimaryKey.length > 0 ? {
|
|
5113
|
+
columns: decision.targetPrimaryKey,
|
|
5114
|
+
fallback: decision.orderByFallback === true
|
|
5115
|
+
} : void 0;
|
|
5116
|
+
}
|
|
4994
5117
|
function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
4995
5118
|
const innerAlias = depth === 0 ? "__t__" : `__t${depth}__`;
|
|
4996
5119
|
const relation = decision.relation ?? decision.relationName;
|
|
@@ -5007,12 +5130,12 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5007
5130
|
ctx.defaultPkColumnName,
|
|
5008
5131
|
ctx.deriveFkColumnName
|
|
5009
5132
|
);
|
|
5010
|
-
let whereExpr =
|
|
5011
|
-
parentAlias,
|
|
5012
|
-
sourceColumn,
|
|
5133
|
+
let whereExpr = buildKeyCorrelation(
|
|
5013
5134
|
innerAlias,
|
|
5014
5135
|
targetColumn,
|
|
5015
|
-
|
|
5136
|
+
parentAlias,
|
|
5137
|
+
sourceColumn,
|
|
5138
|
+
ctx
|
|
5016
5139
|
);
|
|
5017
5140
|
const compiledFilter = decision._compiledFilterWhere;
|
|
5018
5141
|
if (compiledFilter) {
|
|
@@ -5043,6 +5166,7 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5043
5166
|
if (childNodes.length === 0) childNodes = void 0;
|
|
5044
5167
|
}
|
|
5045
5168
|
const limit = typeof decision.limit === "number" ? decision.limit : void 0;
|
|
5169
|
+
const orderBy = resolveJsonAggOrderBy(decision, targetTable, ctx);
|
|
5046
5170
|
return jsonAggSubquery(
|
|
5047
5171
|
targetTable,
|
|
5048
5172
|
whereExpr,
|
|
@@ -5053,7 +5177,9 @@ function compileJsonAggRecursive(decision, parentAlias, depth, ctx, _state) {
|
|
|
5053
5177
|
...childNodes && { childNodes },
|
|
5054
5178
|
innerAlias,
|
|
5055
5179
|
...limit !== void 0 && { limit },
|
|
5056
|
-
...decision.columns && { columns: decision.columns }
|
|
5180
|
+
...decision.columns && { columns: decision.columns },
|
|
5181
|
+
...orderBy && { orderBy: orderBy.columns },
|
|
5182
|
+
...orderBy?.fallback && { orderByFallback: true }
|
|
5057
5183
|
}
|
|
5058
5184
|
);
|
|
5059
5185
|
}
|
|
@@ -5062,6 +5188,7 @@ var init_json_agg = __esm({
|
|
|
5062
5188
|
"src/handlers/include/json-agg.ts"() {
|
|
5063
5189
|
"use strict";
|
|
5064
5190
|
init_ast_helpers();
|
|
5191
|
+
init_exists();
|
|
5065
5192
|
init_shared();
|
|
5066
5193
|
jsonAggIncludeHandler = {
|
|
5067
5194
|
strategy: "json_agg",
|
|
@@ -5083,6 +5210,7 @@ var init_json_agg = __esm({
|
|
|
5083
5210
|
});
|
|
5084
5211
|
|
|
5085
5212
|
// src/handlers/include/lateral.ts
|
|
5213
|
+
import { toColumnList as toColumnList7 } from "@dbsp/types";
|
|
5086
5214
|
function buildLateralTargets(columns, alias, ctx) {
|
|
5087
5215
|
if (columns && columns.length > 0 && !(columns.length === 1 && columns[0] === "*")) {
|
|
5088
5216
|
return columns.map((col) => ({
|
|
@@ -5094,12 +5222,12 @@ function buildLateralTargets(columns, alias, ctx) {
|
|
|
5094
5222
|
return [starTarget(alias, ctx.naming)];
|
|
5095
5223
|
}
|
|
5096
5224
|
function buildLateralSubquery(targetTable, innerAlias, outerAlias, sourceColumn, targetColumn, columns, limit, ctx) {
|
|
5097
|
-
const whereClause =
|
|
5098
|
-
targetColumn,
|
|
5225
|
+
const whereClause = buildKeyCorrelation(
|
|
5099
5226
|
innerAlias,
|
|
5100
|
-
|
|
5227
|
+
targetColumn,
|
|
5101
5228
|
outerAlias,
|
|
5102
|
-
|
|
5229
|
+
sourceColumn,
|
|
5230
|
+
ctx
|
|
5103
5231
|
);
|
|
5104
5232
|
const targetList = buildLateralTargets(columns, innerAlias, ctx);
|
|
5105
5233
|
const stmt = {
|
|
@@ -5179,19 +5307,23 @@ var init_lateral = __esm({
|
|
|
5179
5307
|
"use strict";
|
|
5180
5308
|
init_assert_field();
|
|
5181
5309
|
init_ast_helpers();
|
|
5310
|
+
init_exists();
|
|
5182
5311
|
init_shared();
|
|
5183
5312
|
lateralIncludeHandler = {
|
|
5184
5313
|
strategy: "lateral",
|
|
5185
5314
|
compile(decision, ctx, state) {
|
|
5186
|
-
const sourceColumn =
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
ctx.
|
|
5194
|
-
|
|
5315
|
+
const sourceColumn = toColumnList7(decision.sourceColumn);
|
|
5316
|
+
if (sourceColumn.length === 0) {
|
|
5317
|
+
throw new Error(
|
|
5318
|
+
"Missing required column 'sourceColumn' in lateral include"
|
|
5319
|
+
);
|
|
5320
|
+
}
|
|
5321
|
+
const targetColumn = decision.targetColumn ?? [
|
|
5322
|
+
(ctx.deriveFkColumnName ?? defaultFkDerivation)(
|
|
5323
|
+
ctx.rootTable,
|
|
5324
|
+
ctx.defaultPkColumnName ?? DEFAULT_PK_COLUMN
|
|
5325
|
+
)
|
|
5326
|
+
];
|
|
5195
5327
|
const outerAlias = ctx.currentAlias ?? ctx.rootTable;
|
|
5196
5328
|
const { joins, targets } = compileLateralCascade(
|
|
5197
5329
|
decision,
|
|
@@ -7180,9 +7312,14 @@ import { InvalidOperationError as InvalidOperationError2 } from "@dbsp/core";
|
|
|
7180
7312
|
import {
|
|
7181
7313
|
isParamIntent as isParamIntent6,
|
|
7182
7314
|
NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
|
|
7183
|
-
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
|
|
7315
|
+
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST,
|
|
7316
|
+
toColumnList as toColumnList8
|
|
7184
7317
|
} from "@dbsp/types";
|
|
7185
|
-
import {
|
|
7318
|
+
import {
|
|
7319
|
+
getNqlBindingRefName,
|
|
7320
|
+
getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields4,
|
|
7321
|
+
isNqlBindingRef
|
|
7322
|
+
} from "@dbsp/types/internal";
|
|
7186
7323
|
|
|
7187
7324
|
// src/pgsql-deparser.ts
|
|
7188
7325
|
function deparse(node) {
|
|
@@ -8246,6 +8383,7 @@ init_window();
|
|
|
8246
8383
|
init_include();
|
|
8247
8384
|
init_shared();
|
|
8248
8385
|
init_handlers();
|
|
8386
|
+
init_exists();
|
|
8249
8387
|
init_types();
|
|
8250
8388
|
init_utils();
|
|
8251
8389
|
init_intent_to_decisions();
|
|
@@ -8283,7 +8421,27 @@ function assertNqlSelectWindowFunctionAllowed(functionName) {
|
|
|
8283
8421
|
throw new UnsupportedNqlSelectFunctionError(functionName);
|
|
8284
8422
|
}
|
|
8285
8423
|
}
|
|
8424
|
+
function trustedRelationHasMultipleHops(relation) {
|
|
8425
|
+
if (typeof relation !== "string") return relation.length > 1;
|
|
8426
|
+
return relation.split(".").length > 1;
|
|
8427
|
+
}
|
|
8428
|
+
function isJsonAggOrderBy(orderBy) {
|
|
8429
|
+
return Array.isArray(orderBy) && orderBy.every((item) => typeof item === "string");
|
|
8430
|
+
}
|
|
8431
|
+
function isExpressionOrderBy(orderBy) {
|
|
8432
|
+
return Array.isArray(orderBy) && orderBy.every(
|
|
8433
|
+
(item) => typeof item === "object" && item !== null && "field" in item && typeof item.field === "string"
|
|
8434
|
+
);
|
|
8435
|
+
}
|
|
8286
8436
|
function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
8437
|
+
const jsonAggOrderBy = isJsonAggOrderBy(pd.orderBy) ? pd.orderBy : void 0;
|
|
8438
|
+
const expressionOrderBy = isExpressionOrderBy(pd.orderBy) ? pd.orderBy : void 0;
|
|
8439
|
+
const derivedFkColumns = deriveFkColumns(
|
|
8440
|
+
pd,
|
|
8441
|
+
pd.sourceTable ?? rootTable,
|
|
8442
|
+
defaultPk,
|
|
8443
|
+
deriveFk
|
|
8444
|
+
);
|
|
8287
8445
|
return {
|
|
8288
8446
|
type: pd.type,
|
|
8289
8447
|
table: pd.table,
|
|
@@ -8294,7 +8452,8 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8294
8452
|
paramIndex: pd.paramIndex,
|
|
8295
8453
|
direction: pd.direction,
|
|
8296
8454
|
joinType: pd.joinType,
|
|
8297
|
-
|
|
8455
|
+
sourceColumn: pd.sourceColumn ?? derivedFkColumns.sourceColumn,
|
|
8456
|
+
targetColumn: pd.targetColumn ?? derivedFkColumns.targetColumn,
|
|
8298
8457
|
targetTable: pd.targetTable,
|
|
8299
8458
|
function: pd.function,
|
|
8300
8459
|
args: pd.args,
|
|
@@ -8311,6 +8470,8 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8311
8470
|
relationType: pd.relationType,
|
|
8312
8471
|
foreignKey: pd.foreignKey,
|
|
8313
8472
|
parentKey: pd.parentKey,
|
|
8473
|
+
targetPrimaryKey: pd.targetPrimaryKey ?? jsonAggOrderBy,
|
|
8474
|
+
orderByFallback: pd.orderByFallback,
|
|
8314
8475
|
dataType: pd.dataType,
|
|
8315
8476
|
traversal: pd.traversal,
|
|
8316
8477
|
pkColumn: pd.pkColumn,
|
|
@@ -8325,7 +8486,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8325
8486
|
include: pd.include?.map(
|
|
8326
8487
|
(c) => mapToHandlerDecision(c, rootTable, defaultPk, deriveFk)
|
|
8327
8488
|
),
|
|
8328
|
-
orderBy:
|
|
8489
|
+
orderBy: expressionOrderBy?.map((o) => ({
|
|
8329
8490
|
column: o.field,
|
|
8330
8491
|
direction: o.direction?.toUpperCase() ?? "ASC"
|
|
8331
8492
|
})),
|
|
@@ -8488,6 +8649,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8488
8649
|
rawJoins = [];
|
|
8489
8650
|
/** CTE nodes from include handlers (e.g., CTE strategy) */
|
|
8490
8651
|
pendingCtes = [];
|
|
8652
|
+
/** Local aliases for binding relation-column scalar subqueries. */
|
|
8653
|
+
bindingRelationColumnSubqueryIndex = 0;
|
|
8491
8654
|
/**
|
|
8492
8655
|
* Maps relation-dotted include path → JOIN alias for multi-hop FK resolution.
|
|
8493
8656
|
* Populated as join decisions are compiled so later hops can find the
|
|
@@ -8705,6 +8868,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8705
8868
|
this.defaultPk,
|
|
8706
8869
|
this.deriveFk
|
|
8707
8870
|
);
|
|
8871
|
+
if (handlerDecision.strategy === "json_agg") {
|
|
8872
|
+
assertDialectCapability(
|
|
8873
|
+
this.dialectCapabilities,
|
|
8874
|
+
"supportsJsonAgg",
|
|
8875
|
+
"JSON aggregation for relation includes"
|
|
8876
|
+
);
|
|
8877
|
+
}
|
|
8708
8878
|
const handler = getIncludeHandler(
|
|
8709
8879
|
handlerDecision.strategy
|
|
8710
8880
|
);
|
|
@@ -8875,6 +9045,162 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8875
9045
|
this.naming
|
|
8876
9046
|
);
|
|
8877
9047
|
}
|
|
9048
|
+
isNqlBindingRoot(plan) {
|
|
9049
|
+
return hasBindingName(this.bindingNames, plan.rootTable, this.naming);
|
|
9050
|
+
}
|
|
9051
|
+
buildCorrelatedRelationRefs(fields, plan) {
|
|
9052
|
+
const relatedAlias = `rc_${this.bindingRelationColumnSubqueryIndex++}`;
|
|
9053
|
+
const relatedTable = rangeVar(
|
|
9054
|
+
fields.targetTable,
|
|
9055
|
+
relatedAlias,
|
|
9056
|
+
this.schemaForRangeVar(plan, fields.targetTable),
|
|
9057
|
+
this.naming
|
|
9058
|
+
);
|
|
9059
|
+
const relatedColumn = columnRef(
|
|
9060
|
+
fields.selectedColumn,
|
|
9061
|
+
relatedAlias,
|
|
9062
|
+
void 0,
|
|
9063
|
+
this.naming
|
|
9064
|
+
);
|
|
9065
|
+
return {
|
|
9066
|
+
relatedAlias,
|
|
9067
|
+
relatedTable,
|
|
9068
|
+
relatedColumn
|
|
9069
|
+
};
|
|
9070
|
+
}
|
|
9071
|
+
compileBindingRelationColumnSubquery(fields, plan, dialectCapabilities) {
|
|
9072
|
+
if (fields.selectedColumn === void 0) {
|
|
9073
|
+
throw new Error(
|
|
9074
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
|
|
9075
|
+
);
|
|
9076
|
+
}
|
|
9077
|
+
if (fields.cardinality === "one") {
|
|
9078
|
+
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9079
|
+
if (fields.hops.length === 0 && trustedRelationHasMultipleHops(fields.relation)) {
|
|
9080
|
+
throw new Error(
|
|
9081
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is dotted but missing resolved hops.`
|
|
9082
|
+
);
|
|
9083
|
+
}
|
|
9084
|
+
if (fields.hops.length > 0) {
|
|
9085
|
+
let fromNode = relatedTable;
|
|
9086
|
+
let previousAlias = relatedAlias;
|
|
9087
|
+
for (let i = 0; i < fields.hops.length; i++) {
|
|
9088
|
+
const hop = fields.hops[i];
|
|
9089
|
+
const hopAlias = `${relatedAlias}_h${i + 1}`;
|
|
9090
|
+
const hopTable = rangeVar(
|
|
9091
|
+
hop.target,
|
|
9092
|
+
hopAlias,
|
|
9093
|
+
this.schemaForRangeVar(plan, hop.target),
|
|
9094
|
+
this.naming
|
|
9095
|
+
);
|
|
9096
|
+
fromNode = innerJoin(
|
|
9097
|
+
fromNode,
|
|
9098
|
+
hopTable,
|
|
9099
|
+
buildKeyCorrelation(
|
|
9100
|
+
hopAlias,
|
|
9101
|
+
hop.joinColumn,
|
|
9102
|
+
previousAlias,
|
|
9103
|
+
hop.fkColumn,
|
|
9104
|
+
this.createHandlerContext(plan)
|
|
9105
|
+
)
|
|
9106
|
+
);
|
|
9107
|
+
previousAlias = hopAlias;
|
|
9108
|
+
}
|
|
9109
|
+
return {
|
|
9110
|
+
SubLink: {
|
|
9111
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9112
|
+
subselect: selectStmt({
|
|
9113
|
+
targetList: [
|
|
9114
|
+
{
|
|
9115
|
+
ResTarget: {
|
|
9116
|
+
val: columnRef(
|
|
9117
|
+
fields.selectedColumn,
|
|
9118
|
+
previousAlias,
|
|
9119
|
+
void 0,
|
|
9120
|
+
this.naming
|
|
9121
|
+
)
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
],
|
|
9125
|
+
from: [fromNode],
|
|
9126
|
+
where: buildKeyCorrelation(
|
|
9127
|
+
relatedAlias,
|
|
9128
|
+
fields.targetColumn,
|
|
9129
|
+
plan.rootTable,
|
|
9130
|
+
fields.sourceColumn,
|
|
9131
|
+
this.createHandlerContext(plan)
|
|
9132
|
+
)
|
|
9133
|
+
})
|
|
9134
|
+
}
|
|
9135
|
+
};
|
|
9136
|
+
}
|
|
9137
|
+
return {
|
|
9138
|
+
SubLink: {
|
|
9139
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9140
|
+
subselect: selectStmt({
|
|
9141
|
+
targetList: [{ ResTarget: { val: relatedColumn } }],
|
|
9142
|
+
from: [relatedTable],
|
|
9143
|
+
where: buildKeyCorrelation(
|
|
9144
|
+
relatedAlias,
|
|
9145
|
+
fields.targetColumn,
|
|
9146
|
+
plan.rootTable,
|
|
9147
|
+
fields.sourceColumn,
|
|
9148
|
+
this.createHandlerContext(plan)
|
|
9149
|
+
)
|
|
9150
|
+
})
|
|
9151
|
+
}
|
|
9152
|
+
};
|
|
9153
|
+
}
|
|
9154
|
+
if (fields.cardinality === "many") {
|
|
9155
|
+
if (fields.relationType !== "hasMany") {
|
|
9156
|
+
throw new Error(
|
|
9157
|
+
`NQL binding relation-column proof for '${plan.rootTable}' has cardinality 'many' but relationType '${fields.relationType ?? "unknown"}' is not supported; only hasMany can be aggregated (ref-#192).`
|
|
9158
|
+
);
|
|
9159
|
+
}
|
|
9160
|
+
if (dialectCapabilities?.supportsJsonAgg === false) {
|
|
9161
|
+
throw new Error(
|
|
9162
|
+
"JSON aggregation for NQL binding relation columns is not supported by this adapter"
|
|
9163
|
+
);
|
|
9164
|
+
}
|
|
9165
|
+
const { relatedAlias, relatedTable, relatedColumn } = this.buildCorrelatedRelationRefs(fields, plan);
|
|
9166
|
+
return {
|
|
9167
|
+
SubLink: {
|
|
9168
|
+
subLinkType: "EXPR_SUBLINK",
|
|
9169
|
+
subselect: selectStmt({
|
|
9170
|
+
targetList: [
|
|
9171
|
+
{
|
|
9172
|
+
ResTarget: {
|
|
9173
|
+
val: coalesceExpr([
|
|
9174
|
+
funcCall("json_agg", [relatedColumn], {
|
|
9175
|
+
orderBy: [
|
|
9176
|
+
sortBy(
|
|
9177
|
+
typeCast(relatedColumn, "text"),
|
|
9178
|
+
"DEFAULT",
|
|
9179
|
+
"LAST"
|
|
9180
|
+
)
|
|
9181
|
+
]
|
|
9182
|
+
}),
|
|
9183
|
+
typeCast({ A_Const: { sval: { sval: "[]" } } }, "json")
|
|
9184
|
+
])
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
],
|
|
9188
|
+
from: [relatedTable],
|
|
9189
|
+
where: buildKeyCorrelation(
|
|
9190
|
+
relatedAlias,
|
|
9191
|
+
fields.targetColumn,
|
|
9192
|
+
plan.rootTable,
|
|
9193
|
+
fields.sourceColumn,
|
|
9194
|
+
this.createHandlerContext(plan)
|
|
9195
|
+
)
|
|
9196
|
+
})
|
|
9197
|
+
}
|
|
9198
|
+
};
|
|
9199
|
+
}
|
|
9200
|
+
throw new Error(
|
|
9201
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is not scalar (cardinality: ${fields.cardinality ?? "unknown"}).`
|
|
9202
|
+
);
|
|
9203
|
+
}
|
|
8878
9204
|
compileExpressionSubquery(query, paramOffset) {
|
|
8879
9205
|
const innerCompiler = new _PlanCompiler(this.childCompilerOptions());
|
|
8880
9206
|
const innerPlan = {
|
|
@@ -9252,6 +9578,28 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9252
9578
|
case "selectRelationColumn":
|
|
9253
9579
|
case "selectPseudoColumn":
|
|
9254
9580
|
case "selectArithmetic": {
|
|
9581
|
+
if (decision.type === "selectRelationColumn") {
|
|
9582
|
+
const trusted = getTrustedNqlRelationFilterFields4(decision);
|
|
9583
|
+
if (trusted?.selectedColumn !== void 0) {
|
|
9584
|
+
const node2 = this.compileBindingRelationColumnSubquery(
|
|
9585
|
+
trusted,
|
|
9586
|
+
plan,
|
|
9587
|
+
this.dialectCapabilities
|
|
9588
|
+
);
|
|
9589
|
+
targetList.push({
|
|
9590
|
+
ResTarget: {
|
|
9591
|
+
val: node2,
|
|
9592
|
+
...decision.alias ? { name: this.naming.toDatabase(decision.alias) } : {}
|
|
9593
|
+
}
|
|
9594
|
+
});
|
|
9595
|
+
break;
|
|
9596
|
+
}
|
|
9597
|
+
if (this.isNqlBindingRoot(plan)) {
|
|
9598
|
+
throw new Error(
|
|
9599
|
+
`NQL binding-final query '${plan.rootTable}' cannot select relation column '${decision.relation ?? "unknown"}.${decision.column ?? "unknown"}' without a trusted compiler proof.`
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9602
|
+
}
|
|
9255
9603
|
ensureExpressionHandlersRegistered();
|
|
9256
9604
|
const exprType = decision.type === "selectRelationColumn" ? "relationColumn" : decision.type === "selectPseudoColumn" ? "pseudoColumn" : "arithmetic";
|
|
9257
9605
|
const handler = getExpressionHandler(exprType);
|
|
@@ -9973,12 +10321,22 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9973
10321
|
registerJoinFilter(decision) {
|
|
9974
10322
|
const targetTable = decision.targetTable;
|
|
9975
10323
|
const sourceTable = this.currentRootTable;
|
|
9976
|
-
const fkColumn = decision.foreignKey ?? this.deriveFk(targetTable, this.defaultPk);
|
|
9977
|
-
const onCondition = eqExpr(
|
|
9978
|
-
columnRef(this.defaultPk, targetTable, void 0, this.naming),
|
|
9979
|
-
columnRef(fkColumn, sourceTable, void 0, this.naming)
|
|
9980
|
-
);
|
|
9981
10324
|
const alias = targetTable === sourceTable ? decision.relationName ?? `${targetTable}_join` : void 0;
|
|
10325
|
+
const targetAlias = alias ?? targetTable;
|
|
10326
|
+
const fkColumn = decision.foreignKey ?? [
|
|
10327
|
+
this.deriveFk(targetTable, this.defaultPk)
|
|
10328
|
+
];
|
|
10329
|
+
const targetKey = decision.parentKey ?? [this.defaultPk];
|
|
10330
|
+
const onCondition = buildKeyCorrelation(
|
|
10331
|
+
targetAlias,
|
|
10332
|
+
targetKey,
|
|
10333
|
+
sourceTable,
|
|
10334
|
+
fkColumn,
|
|
10335
|
+
this.createHandlerContext({
|
|
10336
|
+
rootTable: sourceTable,
|
|
10337
|
+
decisions: []
|
|
10338
|
+
})
|
|
10339
|
+
);
|
|
9982
10340
|
this.pendingJoins.push({
|
|
9983
10341
|
type: "JOIN",
|
|
9984
10342
|
table: targetTable,
|
|
@@ -9999,19 +10357,21 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9999
10357
|
this.schemaForRangeVar(plan, decision.targetTable ?? ""),
|
|
10000
10358
|
this.naming
|
|
10001
10359
|
);
|
|
10002
|
-
const
|
|
10003
|
-
|
|
10004
|
-
|
|
10005
|
-
|
|
10006
|
-
|
|
10007
|
-
|
|
10008
|
-
)
|
|
10009
|
-
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10360
|
+
const sourceColumn = toColumnList8(decision.sourceColumn);
|
|
10361
|
+
const targetColumn = toColumnList8(decision.targetColumn);
|
|
10362
|
+
if (sourceColumn.length === 0) {
|
|
10363
|
+
throw new Error("Missing required column 'sourceColumn' in compileJoin");
|
|
10364
|
+
}
|
|
10365
|
+
if (targetColumn.length === 0) {
|
|
10366
|
+
throw new Error("Missing required column 'targetColumn' in compileJoin");
|
|
10367
|
+
}
|
|
10368
|
+
const sourceAlias = sourceColumn.length > 1 ? plan.rootTable : "";
|
|
10369
|
+
const onCondition = buildKeyCorrelation(
|
|
10370
|
+
sourceAlias,
|
|
10371
|
+
sourceColumn,
|
|
10372
|
+
decision.alias ?? decision.targetTable ?? "",
|
|
10373
|
+
targetColumn,
|
|
10374
|
+
this.createHandlerContext(plan)
|
|
10015
10375
|
);
|
|
10016
10376
|
if (decision.joinType === "left") {
|
|
10017
10377
|
return leftJoin(baseTable, targetTable, onCondition, decision.alias);
|
|
@@ -13129,6 +13489,8 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13129
13489
|
if (!filteredSet.has(fk.source) || !filteredSet.has(fk.target)) continue;
|
|
13130
13490
|
const belongsToName = deriveRelationName(fk.cols[0], fk.target);
|
|
13131
13491
|
const fkCol = fk.cols.length === 1 ? fk.cols[0] : fk.cols;
|
|
13492
|
+
const refCol = fk.refs.length === 1 ? fk.refs[0] : fk.refs;
|
|
13493
|
+
const keyOverride = Array.isArray(refCol) || refCol !== DEFAULT_PK_COLUMN ? refCol : void 0;
|
|
13132
13494
|
const belongsToKey = `${fk.source}.${belongsToName}`;
|
|
13133
13495
|
if (!relations.has(belongsToKey)) {
|
|
13134
13496
|
relations.set(belongsToKey, {
|
|
@@ -13137,6 +13499,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13137
13499
|
source: fk.source,
|
|
13138
13500
|
target: fk.target,
|
|
13139
13501
|
foreignKey: fkCol,
|
|
13502
|
+
...keyOverride !== void 0 && { targetKey: keyOverride },
|
|
13140
13503
|
cardinality: "one",
|
|
13141
13504
|
optionality: "optional",
|
|
13142
13505
|
includeStrategy: "auto",
|
|
@@ -13153,6 +13516,7 @@ function inferRelations(fksByConstraint, filteredTables) {
|
|
|
13153
13516
|
source: fk.target,
|
|
13154
13517
|
target: fk.source,
|
|
13155
13518
|
foreignKey: fkCol,
|
|
13519
|
+
...keyOverride !== void 0 && { sourceKey: keyOverride },
|
|
13156
13520
|
cardinality: "many",
|
|
13157
13521
|
optionality: "optional",
|
|
13158
13522
|
includeStrategy: "auto",
|
|
@@ -14055,6 +14419,7 @@ import { getNqlBindingRefName as getNqlBindingRefName2, isNqlBindingRef as isNql
|
|
|
14055
14419
|
|
|
14056
14420
|
// src/adapter-compiler-includes.ts
|
|
14057
14421
|
init_ast_helpers();
|
|
14422
|
+
import { toColumnList as toColumnList9 } from "@dbsp/types";
|
|
14058
14423
|
init_handlers();
|
|
14059
14424
|
function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
14060
14425
|
const schemaName = deps.schemaName;
|
|
@@ -14066,7 +14431,12 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14066
14431
|
parameters: []
|
|
14067
14432
|
};
|
|
14068
14433
|
}
|
|
14069
|
-
const fkColumns =
|
|
14434
|
+
const fkColumns = toColumnList9(info.foreignKey);
|
|
14435
|
+
if (fkColumns.length === 0) {
|
|
14436
|
+
throw new Error(
|
|
14437
|
+
"Subquery include requires at least one foreignKey column."
|
|
14438
|
+
);
|
|
14439
|
+
}
|
|
14070
14440
|
if (info.through && info.throughSourceKey && info.throughTargetKey) {
|
|
14071
14441
|
return compileSubqueryIncludeManyToMany(
|
|
14072
14442
|
info,
|
|
@@ -14114,6 +14484,11 @@ function compileSubqueryInclude(info, parentIds, _options, deps) {
|
|
|
14114
14484
|
};
|
|
14115
14485
|
} else {
|
|
14116
14486
|
const conditions = parentIds.map((id) => {
|
|
14487
|
+
if (!Array.isArray(id) || id.length !== fkColumns.length) {
|
|
14488
|
+
throw new Error(
|
|
14489
|
+
`Subquery include composite key parameter width (${Array.isArray(id) ? id.length : 1}) must match foreignKey width (${fkColumns.length}).`
|
|
14490
|
+
);
|
|
14491
|
+
}
|
|
14117
14492
|
const idValues = id;
|
|
14118
14493
|
const colConditions = fkColumns.map((col, idx) => {
|
|
14119
14494
|
state.parameters.push(idValues[idx]);
|
|
@@ -14154,7 +14529,13 @@ function compileSubqueryIncludeManyToMany(info, parentIds, schemaName, state, de
|
|
|
14154
14529
|
const throughTable = info.through;
|
|
14155
14530
|
const throughSourceKey = info.throughSourceKey;
|
|
14156
14531
|
const throughTargetKey = info.throughTargetKey;
|
|
14157
|
-
const
|
|
14532
|
+
const targetPkColumns = toColumnList9(info.sourceKey);
|
|
14533
|
+
if (targetPkColumns.length !== 1) {
|
|
14534
|
+
throw new Error(
|
|
14535
|
+
`Many-to-many subquery include requires a single-column target key; got ${JSON.stringify(targetPkColumns)}.`
|
|
14536
|
+
);
|
|
14537
|
+
}
|
|
14538
|
+
const targetPk = targetPkColumns[0];
|
|
14158
14539
|
const paramRefs = parentIds.map((id) => {
|
|
14159
14540
|
state.parameters.push(id);
|
|
14160
14541
|
state.paramIndex++;
|
|
@@ -14243,6 +14624,7 @@ import {
|
|
|
14243
14624
|
POSTGRESQL_CAPABILITIES,
|
|
14244
14625
|
plan as planFn
|
|
14245
14626
|
} from "@dbsp/core";
|
|
14627
|
+
import { toColumnList as toColumnList12 } from "@dbsp/types";
|
|
14246
14628
|
|
|
14247
14629
|
// src/adapter-compiler-select.ts
|
|
14248
14630
|
init_assert_field();
|
|
@@ -14250,6 +14632,7 @@ init_ast_helpers();
|
|
|
14250
14632
|
init_binding_registry();
|
|
14251
14633
|
init_compile_where();
|
|
14252
14634
|
import { countDistinctRelationPathsByName } from "@dbsp/core";
|
|
14635
|
+
import { toColumnList as toColumnList11 } from "@dbsp/types";
|
|
14253
14636
|
init_compiler_utils();
|
|
14254
14637
|
init_types();
|
|
14255
14638
|
init_intent_to_decisions();
|
|
@@ -14259,6 +14642,7 @@ init_param_ref();
|
|
|
14259
14642
|
init_assert_field();
|
|
14260
14643
|
init_intent_to_decisions();
|
|
14261
14644
|
import { deriveRelationPathFromIntentPath } from "@dbsp/core";
|
|
14645
|
+
import { toColumnList as toColumnList10 } from "@dbsp/types";
|
|
14262
14646
|
function findExistsIntents(where) {
|
|
14263
14647
|
if (!where || typeof where !== "object") return [];
|
|
14264
14648
|
const w = where;
|
|
@@ -14279,7 +14663,8 @@ function findExistsIntents(where) {
|
|
|
14279
14663
|
function resolveRelation(model, sourceTable, relationName) {
|
|
14280
14664
|
const rel = model.getRelation(`${sourceTable}.${relationName}`);
|
|
14281
14665
|
if (!rel) return void 0;
|
|
14282
|
-
const
|
|
14666
|
+
const foreignKeyColumns = toColumnList10(rel.foreignKey);
|
|
14667
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14283
14668
|
const relationType = rel.type;
|
|
14284
14669
|
return { target: rel.target, foreignKey, relationType };
|
|
14285
14670
|
}
|
|
@@ -14502,7 +14887,8 @@ function buildMultiHopExistsChain(hops, rootTable, outerOperator, innerCondition
|
|
|
14502
14887
|
const hopRelations = [];
|
|
14503
14888
|
for (const hop of hops) {
|
|
14504
14889
|
const rel = model.getRelation(`${currentSource}.${hop}`);
|
|
14505
|
-
const
|
|
14890
|
+
const foreignKeyColumns = rel ? toColumnList10(rel.foreignKey) : [];
|
|
14891
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14506
14892
|
const relationType = rel?.type === "belongsTo" ? "belongsTo" : rel?.type === "hasMany" || rel?.type === "hasOne" ? rel.type : void 0;
|
|
14507
14893
|
const parentKey = relationType === "belongsTo" ? rel?.targetKey : rel?.sourceKey;
|
|
14508
14894
|
const target = rel ? rel.target : hop;
|
|
@@ -14612,7 +14998,8 @@ function enrichExistsStubsInConditions(conditions, sourceTable, model) {
|
|
|
14612
14998
|
`exists('${relation}'): no relation '${relation}' is declared on table '${sourceTable}'. Use rawExists(subquery(...)) for an EXISTS over an undeclared or uncorrelated subquery.`
|
|
14613
14999
|
);
|
|
14614
15000
|
}
|
|
14615
|
-
const
|
|
15001
|
+
const foreignKeyColumns = toColumnList10(rel.foreignKey);
|
|
15002
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14616
15003
|
const relationType = rel.type === "belongsTo" ? "belongsTo" : rel.type === "hasMany" || rel.type === "hasOne" ? rel.type : void 0;
|
|
14617
15004
|
const parentKey = relationType === "belongsTo" ? rel.targetKey : rel.sourceKey;
|
|
14618
15005
|
const targetTable = rel.target;
|
|
@@ -14671,7 +15058,8 @@ function buildEnrichedExistsDecision(d, matchingIntent, rootTable, model) {
|
|
|
14671
15058
|
const relIR = model && context.relation ? model.getRelation(
|
|
14672
15059
|
`${sourceTableForRelation}.${context.relation}`
|
|
14673
15060
|
) : void 0;
|
|
14674
|
-
const
|
|
15061
|
+
const foreignKeyColumns = relIR ? toColumnList10(relIR.foreignKey) : [];
|
|
15062
|
+
const foreignKey = foreignKeyColumns.length > 0 ? foreignKeyColumns : void 0;
|
|
14675
15063
|
const relationType = relIR?.type === "belongsTo" ? "belongsTo" : relIR?.type === "hasMany" || relIR?.type === "hasOne" ? relIR.type : void 0;
|
|
14676
15064
|
const parentKey = relationType === "belongsTo" ? relIR?.targetKey : relIR?.sourceKey;
|
|
14677
15065
|
let operator = "exists";
|
|
@@ -14984,6 +15372,7 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
14984
15372
|
const relationName = resolveIncludeAlias(context);
|
|
14985
15373
|
if (!context.target || !relationName) return void 0;
|
|
14986
15374
|
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15375
|
+
const parentKey = context.parentKey ?? defaultPk;
|
|
14987
15376
|
const relationType = context.relationType;
|
|
14988
15377
|
const effectiveChoice = choice === "subquery" ? "json_agg" : choice;
|
|
14989
15378
|
const includeIntent = resolveIncludeByPath(
|
|
@@ -15004,8 +15393,13 @@ function toIncludeDecision(d, choice, plan, defaultPk = DEFAULT_PK_COLUMN, deriv
|
|
|
15004
15393
|
targetTable: context.target,
|
|
15005
15394
|
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15006
15395
|
...relationType && { relationType },
|
|
15007
|
-
foreignKey
|
|
15008
|
-
parentKey
|
|
15396
|
+
foreignKey,
|
|
15397
|
+
parentKey,
|
|
15398
|
+
...context.targetPrimaryKey && context.targetPrimaryKey.length > 0 ? {
|
|
15399
|
+
targetPrimaryKey: context.targetPrimaryKey,
|
|
15400
|
+
orderBy: context.targetPrimaryKey
|
|
15401
|
+
} : {},
|
|
15402
|
+
...context.orderByFallback ? { orderByFallback: true } : {},
|
|
15009
15403
|
...context.intentPath && { intentPath: context.intentPath },
|
|
15010
15404
|
...limit != null && { limit }
|
|
15011
15405
|
};
|
|
@@ -15031,6 +15425,7 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
|
|
|
15031
15425
|
columns = [defaultPk, ...fields];
|
|
15032
15426
|
}
|
|
15033
15427
|
const foreignKey = deriveForeignKey(context, deriveFk, defaultPk) ?? defaultPk;
|
|
15428
|
+
const parentKey = context.parentKey ?? defaultPk;
|
|
15034
15429
|
const relationType = context.relationType;
|
|
15035
15430
|
let conditions;
|
|
15036
15431
|
if (includeIntent?.where) {
|
|
@@ -15051,8 +15446,8 @@ function toJoinIncludeDecision(d, plan, defaultPk = DEFAULT_PK_COLUMN, deriveFk
|
|
|
15051
15446
|
targetTable: context.target,
|
|
15052
15447
|
...context.sourceTable && { sourceTable: context.sourceTable },
|
|
15053
15448
|
...relationType && { relationType },
|
|
15054
|
-
foreignKey
|
|
15055
|
-
parentKey
|
|
15449
|
+
foreignKey,
|
|
15450
|
+
parentKey,
|
|
15056
15451
|
columns,
|
|
15057
15452
|
...joinType && { joinType },
|
|
15058
15453
|
...conditions && { conditions }
|
|
@@ -15075,10 +15470,13 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15075
15470
|
(r) => r.name === alias || snakeToCamel(r.name) === alias
|
|
15076
15471
|
);
|
|
15077
15472
|
if (!rel) continue;
|
|
15078
|
-
const
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15473
|
+
const rawFkColumns = toColumnList10(rel.foreignKey);
|
|
15474
|
+
const rawFk = rawFkColumns.length > 0 ? rawFkColumns : [
|
|
15475
|
+
deriveFk(
|
|
15476
|
+
rel.type === "belongsTo" ? rel.target : sourceTable,
|
|
15477
|
+
defaultPk
|
|
15478
|
+
)
|
|
15479
|
+
];
|
|
15082
15480
|
let columns = [defaultPk];
|
|
15083
15481
|
if (inc.select?.type === "fields" && inc.select.fields) {
|
|
15084
15482
|
const extraFields = inc.select.fields.filter((f) => f !== defaultPk);
|
|
@@ -15099,8 +15497,8 @@ function synthesizeMissingJoinDecisions(plan, coveredRelations, model, defaultPk
|
|
|
15099
15497
|
...rel.type && {
|
|
15100
15498
|
relationType: rel.type
|
|
15101
15499
|
},
|
|
15102
|
-
foreignKey:
|
|
15103
|
-
parentKey: defaultPk,
|
|
15500
|
+
foreignKey: rawFk,
|
|
15501
|
+
parentKey: rel.type === "belongsTo" ? toColumnList10(rel.targetKey).length > 0 ? toColumnList10(rel.targetKey) : [defaultPk] : toColumnList10(rel.sourceKey).length > 0 ? toColumnList10(rel.sourceKey) : [defaultPk],
|
|
15104
15502
|
columns,
|
|
15105
15503
|
joinType: inc.join,
|
|
15106
15504
|
...conditions && { conditions }
|
|
@@ -15257,9 +15655,12 @@ function compileJoinIntents(joins, rootTable, schemaName, deps) {
|
|
|
15257
15655
|
);
|
|
15258
15656
|
}
|
|
15259
15657
|
const isBelongsTo = rel.type === "belongsTo";
|
|
15260
|
-
const rawFk =
|
|
15261
|
-
const
|
|
15262
|
-
const
|
|
15658
|
+
const rawFk = toColumnList11(rel.foreignKey);
|
|
15659
|
+
const fkColumns = rawFk.length > 0 ? rawFk : [deriveFk(isBelongsTo ? rootTable : rel.target, defaultPk)];
|
|
15660
|
+
const sourceKey = toColumnList11(rel.sourceKey);
|
|
15661
|
+
const targetKey = toColumnList11(rel.targetKey);
|
|
15662
|
+
const sourceColumn = isBelongsTo ? fkColumns : sourceKey.length > 0 ? sourceKey : [defaultPk];
|
|
15663
|
+
const targetColumn = isBelongsTo ? targetKey.length > 0 ? targetKey : [defaultPk] : fkColumns;
|
|
15263
15664
|
const alias = intent.alias ?? intent.relation;
|
|
15264
15665
|
results.push({
|
|
15265
15666
|
type: "join",
|
|
@@ -15622,10 +16023,11 @@ function compileWithIncludes(plan, options, deps) {
|
|
|
15622
16023
|
const relationName = ctx.includeAlias ?? ctx.relation;
|
|
15623
16024
|
if (!relationName) continue;
|
|
15624
16025
|
const rawFk = deriveForeignKey(ctx, deps.deriveFk, deps.defaultPk) ?? deps.defaultPk;
|
|
15625
|
-
const fk =
|
|
16026
|
+
const fk = toColumnList11(rawFk);
|
|
16027
|
+
const parentKey = toColumnList11(ctx.parentKey);
|
|
15626
16028
|
const isBelongsTo = ctx.relationType === "belongsTo";
|
|
15627
|
-
const sourceKey = isBelongsTo ? fk :
|
|
15628
|
-
const targetFk = isBelongsTo ?
|
|
16029
|
+
const sourceKey = isBelongsTo ? fk : parentKey.length > 0 ? parentKey : [deps.defaultPk];
|
|
16030
|
+
const targetFk = isBelongsTo ? parentKey.length > 0 ? parentKey : [deps.defaultPk] : fk;
|
|
15629
16031
|
const includeIntent = plan.intent?.include?.find(
|
|
15630
16032
|
(i) => i.relation === relationName || i.relation === ctx.includeAlias
|
|
15631
16033
|
);
|
|
@@ -15688,14 +16090,14 @@ function prependSourceCte(sql, parameters, sourceName, sourceCte, naming) {
|
|
|
15688
16090
|
};
|
|
15689
16091
|
}
|
|
15690
16092
|
function resolveMutationExistsForeignKey(foreignKey, relationName) {
|
|
15691
|
-
|
|
15692
|
-
if (
|
|
15693
|
-
if (
|
|
16093
|
+
const columns = toColumnList12(foreignKey);
|
|
16094
|
+
if (columns.length === 0) return void 0;
|
|
16095
|
+
if (columns.some((column) => column.length === 0)) {
|
|
15694
16096
|
throw new Error(
|
|
15695
|
-
`Mutation exists()/notExists()
|
|
16097
|
+
`Mutation exists()/notExists() guard relation '${relationName}' has an empty foreignKey column.`
|
|
15696
16098
|
);
|
|
15697
16099
|
}
|
|
15698
|
-
return
|
|
16100
|
+
return columns;
|
|
15699
16101
|
}
|
|
15700
16102
|
function resolveExistsRelation(sourceTable, relation, model) {
|
|
15701
16103
|
if (!model) return { targetTable: relation };
|
|
@@ -15708,13 +16110,13 @@ function resolveExistsRelation(sourceTable, relation, model) {
|
|
|
15708
16110
|
return {
|
|
15709
16111
|
targetTable,
|
|
15710
16112
|
...fk2 !== void 0 && { sourceColumn: fk2 },
|
|
15711
|
-
targetColumn: "id"
|
|
16113
|
+
targetColumn: toColumnList12(rel.targetKey).length > 0 ? toColumnList12(rel.targetKey) : ["id"]
|
|
15712
16114
|
};
|
|
15713
16115
|
}
|
|
15714
16116
|
const fk = resolveMutationExistsForeignKey(rel.foreignKey, relationName);
|
|
15715
16117
|
return {
|
|
15716
16118
|
targetTable,
|
|
15717
|
-
sourceColumn: rel.sourceKey
|
|
16119
|
+
sourceColumn: toColumnList12(rel.sourceKey).length > 0 ? toColumnList12(rel.sourceKey) : ["id"],
|
|
15718
16120
|
...fk !== void 0 && { targetColumn: fk }
|
|
15719
16121
|
};
|
|
15720
16122
|
}
|
|
@@ -17953,7 +18355,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
17953
18355
|
bundle.query.from,
|
|
17954
18356
|
deps.naming
|
|
17955
18357
|
);
|
|
17956
|
-
const planReport = queryFromBinding ? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
18358
|
+
const planReport = queryFromBinding ? bundle.plan ?? createNqlBindingSelectPlan(bundle.query) : planFn2(bundle.query, this.requireNqlCompileModel(options), {
|
|
17957
18359
|
dialectCapabilities: options?.dialectCapabilities ?? this.dialectCapabilities
|
|
17958
18360
|
});
|
|
17959
18361
|
return guardCompiledQuery(
|
|
@@ -18047,6 +18449,7 @@ var PgsqlAdapter = class _PgsqlAdapter {
|
|
|
18047
18449
|
}
|
|
18048
18450
|
const leafBundle = {
|
|
18049
18451
|
...bundle.query !== void 0 && { query: bundle.query },
|
|
18452
|
+
...bundle.plan !== void 0 && { plan: bundle.plan },
|
|
18050
18453
|
...bundle.cteQuery !== void 0 && { cteQuery: bundle.cteQuery },
|
|
18051
18454
|
...bundle.mutation !== void 0 && { mutation: bundle.mutation },
|
|
18052
18455
|
...bundle.returning !== void 0 && { returning: bundle.returning },
|