@dbsp/adapter-pgsql 1.4.0 → 1.5.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 +4 -0
- package/dist/index.js +141 -8
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -289,6 +289,8 @@ declare class PlanCompiler {
|
|
|
289
289
|
private rawJoins;
|
|
290
290
|
/** CTE nodes from include handlers (e.g., CTE strategy) */
|
|
291
291
|
private pendingCtes;
|
|
292
|
+
/** Local aliases for binding relation-column scalar subqueries. */
|
|
293
|
+
private bindingRelationColumnSubqueryIndex;
|
|
292
294
|
/**
|
|
293
295
|
* Maps relation-dotted include path → JOIN alias for multi-hop FK resolution.
|
|
294
296
|
* Populated as join decisions are compiled so later hops can find the
|
|
@@ -347,6 +349,8 @@ declare class PlanCompiler {
|
|
|
347
349
|
/** Build a fresh HandlerCompilerState sharing the current parameter array. */
|
|
348
350
|
private createHandlerState;
|
|
349
351
|
private schemaForRangeVar;
|
|
352
|
+
private isNqlBindingRoot;
|
|
353
|
+
private compileBindingRelationColumnSubquery;
|
|
350
354
|
private compileExpressionSubquery;
|
|
351
355
|
private compileGenericNqlFunction;
|
|
352
356
|
private compileNqlFunctionArg;
|
package/dist/index.js
CHANGED
|
@@ -1457,6 +1457,10 @@ var init_custom = __esm({
|
|
|
1457
1457
|
});
|
|
1458
1458
|
|
|
1459
1459
|
// src/select-expression-handlers.ts
|
|
1460
|
+
import {
|
|
1461
|
+
getTrustedNqlRelationFilterFields,
|
|
1462
|
+
markNqlTrustedRelationFilter
|
|
1463
|
+
} from "@dbsp/types/internal";
|
|
1460
1464
|
function handleColumnExpression(expr, rootTable, decisions) {
|
|
1461
1465
|
const decision = {
|
|
1462
1466
|
type: "select",
|
|
@@ -1585,14 +1589,18 @@ function handleCaseExpression(expr, rootTable, decisions, _applyFilter, convertC
|
|
|
1585
1589
|
decisions.push(decision);
|
|
1586
1590
|
}
|
|
1587
1591
|
function handleRelationColumnExpression(expr, rootTable, decisions) {
|
|
1592
|
+
const trusted = getTrustedNqlRelationFilterFields(expr);
|
|
1593
|
+
const trustedRelation = typeof trusted?.relation === "string" ? trusted.relation : trusted?.relation.join(".");
|
|
1588
1594
|
const decision = {
|
|
1589
1595
|
type: "selectRelationColumn",
|
|
1590
|
-
relation: expr.relation,
|
|
1591
|
-
column: expr.column ?? "*",
|
|
1596
|
+
relation: trustedRelation ?? expr.relation,
|
|
1597
|
+
column: trusted?.selectedColumn ?? (expr.column ?? "*"),
|
|
1592
1598
|
table: rootTable
|
|
1593
1599
|
};
|
|
1594
1600
|
if (expr.as) decision.alias = expr.as;
|
|
1595
|
-
decisions.push(
|
|
1601
|
+
decisions.push(
|
|
1602
|
+
trusted?.selectedColumn !== void 0 ? markNqlTrustedRelationFilter(decision, trusted) : decision
|
|
1603
|
+
);
|
|
1596
1604
|
}
|
|
1597
1605
|
function handleArithmeticExpression(expr, rootTable, decisions) {
|
|
1598
1606
|
const decision = {
|
|
@@ -1690,6 +1698,9 @@ var init_select_expression_handlers = __esm({
|
|
|
1690
1698
|
|
|
1691
1699
|
// src/intent-to-decisions.ts
|
|
1692
1700
|
import { isParamIntent } from "@dbsp/types";
|
|
1701
|
+
import {
|
|
1702
|
+
getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields2
|
|
1703
|
+
} from "@dbsp/types/internal";
|
|
1693
1704
|
function intentToDecisions(intent, rootTable) {
|
|
1694
1705
|
const decisions = [];
|
|
1695
1706
|
if (intent.select) {
|
|
@@ -2075,10 +2086,29 @@ function convertNot(cond, rootTable) {
|
|
|
2075
2086
|
if (!subDecision) return null;
|
|
2076
2087
|
return { type: "whereNot", conditions: [subDecision] };
|
|
2077
2088
|
}
|
|
2089
|
+
function formatRelationName(relation) {
|
|
2090
|
+
return typeof relation === "string" ? relation : relation.join(".");
|
|
2091
|
+
}
|
|
2078
2092
|
function convertExistsLike(cond, operator) {
|
|
2079
|
-
const
|
|
2093
|
+
const rawRelation = cond.relation;
|
|
2094
|
+
const preResolved = getTrustedNqlRelationFilterFields2(cond);
|
|
2095
|
+
const trustedRelation = preResolved?.relation;
|
|
2096
|
+
const relationName = trustedRelation !== void 0 ? formatRelationName(trustedRelation) : formatRelationName(rawRelation);
|
|
2097
|
+
const targetTable = preResolved?.targetTable ?? rawRelation;
|
|
2080
2098
|
const subDecisions = cond.where ? convertWhere(cond.where, targetTable) : [];
|
|
2081
|
-
const
|
|
2099
|
+
const isPreResolved = preResolved !== void 0;
|
|
2100
|
+
const base = {
|
|
2101
|
+
type: "where",
|
|
2102
|
+
operator,
|
|
2103
|
+
targetTable,
|
|
2104
|
+
...preResolved !== void 0 && {
|
|
2105
|
+
sourceColumn: preResolved.sourceColumn
|
|
2106
|
+
},
|
|
2107
|
+
...preResolved !== void 0 && {
|
|
2108
|
+
targetColumn: preResolved.targetColumn
|
|
2109
|
+
},
|
|
2110
|
+
...isPreResolved && { relationName }
|
|
2111
|
+
};
|
|
2082
2112
|
return subDecisions.length > 0 ? { ...base, conditions: subDecisions } : base;
|
|
2083
2113
|
}
|
|
2084
2114
|
function convertSubquery(cond) {
|
|
@@ -3744,6 +3774,7 @@ var init_range = __esm({
|
|
|
3744
3774
|
});
|
|
3745
3775
|
|
|
3746
3776
|
// src/compile-where.ts
|
|
3777
|
+
import { getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields3 } from "@dbsp/types/internal";
|
|
3747
3778
|
function toHandlerContext(ctx) {
|
|
3748
3779
|
return {
|
|
3749
3780
|
naming: ctx.naming,
|
|
@@ -3937,11 +3968,16 @@ function handleSubqueryIntent(intent, ctx, _handlerCtx) {
|
|
|
3937
3968
|
}
|
|
3938
3969
|
function handleRelationFilterIntent(intent, ctx) {
|
|
3939
3970
|
const rf = intent;
|
|
3940
|
-
const
|
|
3971
|
+
const preResolved = getTrustedNqlRelationFilterFields3(rf);
|
|
3972
|
+
const relationPath = preResolved?.relation ?? rf.relation;
|
|
3973
|
+
const hops = Array.isArray(relationPath) ? [...relationPath] : [relationPath];
|
|
3941
3974
|
const existsKind = rf.mode === "none" || rf.mode === "every" ? "notExists" : "exists";
|
|
3942
3975
|
const rfWhere = rf.where;
|
|
3943
3976
|
const isVacuousEvery = rf.mode === "every" && (!rfWhere || typeof rfWhere === "object" && rfWhere.kind === "and" && Array.isArray(rfWhere.conditions) && rfWhere.conditions.length === 0);
|
|
3944
3977
|
if (isVacuousEvery) {
|
|
3978
|
+
if (preResolved) {
|
|
3979
|
+
return { A_Const: { boolval: { boolval: true } } };
|
|
3980
|
+
}
|
|
3945
3981
|
if (hops.length <= 1) {
|
|
3946
3982
|
const relation = hops[0] ?? rf.relation;
|
|
3947
3983
|
if (!ctx.model) {
|
|
@@ -3977,6 +4013,19 @@ function handleRelationFilterIntent(intent, ctx) {
|
|
|
3977
4013
|
const innermostWhere = rf.mode === "every" ? { kind: "not", condition: rf.where } : rf.where;
|
|
3978
4014
|
if (hops.length <= 1) {
|
|
3979
4015
|
const relation = hops[0] ?? rf.relation;
|
|
4016
|
+
if (preResolved) {
|
|
4017
|
+
return compileWhereIntent(
|
|
4018
|
+
{
|
|
4019
|
+
kind: existsKind,
|
|
4020
|
+
relation,
|
|
4021
|
+
targetTable: preResolved.targetTable,
|
|
4022
|
+
sourceColumn: preResolved.sourceColumn,
|
|
4023
|
+
targetColumn: preResolved.targetColumn,
|
|
4024
|
+
where: innermostWhere
|
|
4025
|
+
},
|
|
4026
|
+
ctx
|
|
4027
|
+
);
|
|
4028
|
+
}
|
|
3980
4029
|
const resolvedRelation = ctx.model?.getRelation(
|
|
3981
4030
|
`${ctx.rootTable}.${relation}`
|
|
3982
4031
|
);
|
|
@@ -7182,7 +7231,11 @@ import {
|
|
|
7182
7231
|
NQL_SELECT_SCALAR_FUNCTION_ALLOWLIST,
|
|
7183
7232
|
NQL_SELECT_WINDOW_FUNCTION_ALLOWLIST
|
|
7184
7233
|
} from "@dbsp/types";
|
|
7185
|
-
import {
|
|
7234
|
+
import {
|
|
7235
|
+
getNqlBindingRefName,
|
|
7236
|
+
getTrustedNqlRelationFilterFields as getTrustedNqlRelationFilterFields4,
|
|
7237
|
+
isNqlBindingRef
|
|
7238
|
+
} from "@dbsp/types/internal";
|
|
7186
7239
|
|
|
7187
7240
|
// src/pgsql-deparser.ts
|
|
7188
7241
|
function deparse(node) {
|
|
@@ -8284,6 +8337,12 @@ function assertNqlSelectWindowFunctionAllowed(functionName) {
|
|
|
8284
8337
|
}
|
|
8285
8338
|
}
|
|
8286
8339
|
function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
8340
|
+
const derivedFkColumns = deriveFkColumns(
|
|
8341
|
+
pd,
|
|
8342
|
+
pd.sourceTable ?? rootTable,
|
|
8343
|
+
defaultPk,
|
|
8344
|
+
deriveFk
|
|
8345
|
+
);
|
|
8287
8346
|
return {
|
|
8288
8347
|
type: pd.type,
|
|
8289
8348
|
table: pd.table,
|
|
@@ -8294,7 +8353,8 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
8294
8353
|
paramIndex: pd.paramIndex,
|
|
8295
8354
|
direction: pd.direction,
|
|
8296
8355
|
joinType: pd.joinType,
|
|
8297
|
-
|
|
8356
|
+
sourceColumn: pd.sourceColumn ?? derivedFkColumns.sourceColumn,
|
|
8357
|
+
targetColumn: pd.targetColumn ?? derivedFkColumns.targetColumn,
|
|
8298
8358
|
targetTable: pd.targetTable,
|
|
8299
8359
|
function: pd.function,
|
|
8300
8360
|
args: pd.args,
|
|
@@ -8488,6 +8548,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8488
8548
|
rawJoins = [];
|
|
8489
8549
|
/** CTE nodes from include handlers (e.g., CTE strategy) */
|
|
8490
8550
|
pendingCtes = [];
|
|
8551
|
+
/** Local aliases for binding relation-column scalar subqueries. */
|
|
8552
|
+
bindingRelationColumnSubqueryIndex = 0;
|
|
8491
8553
|
/**
|
|
8492
8554
|
* Maps relation-dotted include path → JOIN alias for multi-hop FK resolution.
|
|
8493
8555
|
* Populated as join decisions are compiled so later hops can find the
|
|
@@ -8875,6 +8937,56 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
8875
8937
|
this.naming
|
|
8876
8938
|
);
|
|
8877
8939
|
}
|
|
8940
|
+
isNqlBindingRoot(plan) {
|
|
8941
|
+
return hasBindingName(this.bindingNames, plan.rootTable, this.naming);
|
|
8942
|
+
}
|
|
8943
|
+
compileBindingRelationColumnSubquery(fields, plan) {
|
|
8944
|
+
if (fields.selectedColumn === void 0) {
|
|
8945
|
+
throw new Error(
|
|
8946
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is missing selectedColumn.`
|
|
8947
|
+
);
|
|
8948
|
+
}
|
|
8949
|
+
if (fields.cardinality !== "one") {
|
|
8950
|
+
throw new Error(
|
|
8951
|
+
`NQL binding relation-column proof for '${plan.rootTable}' is not scalar (cardinality: ${fields.cardinality ?? "unknown"}).`
|
|
8952
|
+
);
|
|
8953
|
+
}
|
|
8954
|
+
const relatedAlias = `rc_${this.bindingRelationColumnSubqueryIndex++}`;
|
|
8955
|
+
const relatedTable = rangeVar(
|
|
8956
|
+
fields.targetTable,
|
|
8957
|
+
relatedAlias,
|
|
8958
|
+
this.schemaForRangeVar(plan, fields.targetTable),
|
|
8959
|
+
this.naming
|
|
8960
|
+
);
|
|
8961
|
+
const relatedColumn = columnRef(
|
|
8962
|
+
fields.selectedColumn,
|
|
8963
|
+
relatedAlias,
|
|
8964
|
+
void 0,
|
|
8965
|
+
this.naming
|
|
8966
|
+
);
|
|
8967
|
+
const relatedJoinColumn = columnRef(
|
|
8968
|
+
fields.targetColumn,
|
|
8969
|
+
relatedAlias,
|
|
8970
|
+
void 0,
|
|
8971
|
+
this.naming
|
|
8972
|
+
);
|
|
8973
|
+
const bindingJoinColumn = columnRef(
|
|
8974
|
+
fields.sourceColumn,
|
|
8975
|
+
plan.rootTable,
|
|
8976
|
+
void 0,
|
|
8977
|
+
this.naming
|
|
8978
|
+
);
|
|
8979
|
+
return {
|
|
8980
|
+
SubLink: {
|
|
8981
|
+
subLinkType: "EXPR_SUBLINK",
|
|
8982
|
+
subselect: selectStmt({
|
|
8983
|
+
targetList: [{ ResTarget: { val: relatedColumn } }],
|
|
8984
|
+
from: [relatedTable],
|
|
8985
|
+
where: eqExpr(relatedJoinColumn, bindingJoinColumn)
|
|
8986
|
+
})
|
|
8987
|
+
}
|
|
8988
|
+
};
|
|
8989
|
+
}
|
|
8878
8990
|
compileExpressionSubquery(query, paramOffset) {
|
|
8879
8991
|
const innerCompiler = new _PlanCompiler(this.childCompilerOptions());
|
|
8880
8992
|
const innerPlan = {
|
|
@@ -9252,6 +9364,27 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
9252
9364
|
case "selectRelationColumn":
|
|
9253
9365
|
case "selectPseudoColumn":
|
|
9254
9366
|
case "selectArithmetic": {
|
|
9367
|
+
if (decision.type === "selectRelationColumn") {
|
|
9368
|
+
const trusted = getTrustedNqlRelationFilterFields4(decision);
|
|
9369
|
+
if (trusted?.selectedColumn !== void 0) {
|
|
9370
|
+
const node2 = this.compileBindingRelationColumnSubquery(
|
|
9371
|
+
trusted,
|
|
9372
|
+
plan
|
|
9373
|
+
);
|
|
9374
|
+
targetList.push({
|
|
9375
|
+
ResTarget: {
|
|
9376
|
+
val: node2,
|
|
9377
|
+
...decision.alias ? { name: this.naming.toDatabase(decision.alias) } : {}
|
|
9378
|
+
}
|
|
9379
|
+
});
|
|
9380
|
+
break;
|
|
9381
|
+
}
|
|
9382
|
+
if (this.isNqlBindingRoot(plan)) {
|
|
9383
|
+
throw new Error(
|
|
9384
|
+
`NQL binding-final query '${plan.rootTable}' cannot select relation column '${decision.relation ?? "unknown"}.${decision.column ?? "unknown"}' without a trusted compiler proof.`
|
|
9385
|
+
);
|
|
9386
|
+
}
|
|
9387
|
+
}
|
|
9255
9388
|
ensureExpressionHandlersRegistered();
|
|
9256
9389
|
const exprType = decision.type === "selectRelationColumn" ? "relationColumn" : decision.type === "selectPseudoColumn" ? "pseudoColumn" : "arithmetic";
|
|
9257
9390
|
const handler = getExpressionHandler(exprType);
|