@dbsp/adapter-pgsql 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +19 -0
- package/dist/index.js +56 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -123,6 +123,7 @@ interface PlanDecision {
|
|
|
123
123
|
readonly targetColumn?: ColumnListInput;
|
|
124
124
|
readonly targetTable?: string;
|
|
125
125
|
readonly function?: string;
|
|
126
|
+
readonly distinct?: boolean;
|
|
126
127
|
readonly args?: readonly unknown[];
|
|
127
128
|
readonly conditions?: readonly PlanDecision[];
|
|
128
129
|
readonly columns?: readonly string[];
|
|
@@ -173,6 +174,13 @@ interface PlanDecision {
|
|
|
173
174
|
readonly jsonMode?: 'json' | 'text';
|
|
174
175
|
readonly selectColumn?: string;
|
|
175
176
|
readonly aggregate?: string;
|
|
177
|
+
/**
|
|
178
|
+
* Apply DISTINCT to a scalar subquery's aggregate (e.g. AVG(DISTINCT price)).
|
|
179
|
+
* Deliberately NOT named `distinct` — that field means "this decision's own
|
|
180
|
+
* query-level DISTINCT modifier" and `assertNoDroppedDecisionModifiers`
|
|
181
|
+
* (subquery-emission.ts) rejects it as unsupported on subquery decisions.
|
|
182
|
+
*/
|
|
183
|
+
readonly aggregateDistinct?: boolean;
|
|
176
184
|
readonly subqueryOperator?: string;
|
|
177
185
|
readonly filterCondition?: PlanDecision;
|
|
178
186
|
readonly expressionIntent?: unknown;
|
|
@@ -1165,6 +1173,8 @@ interface Decision {
|
|
|
1165
1173
|
readonly targetColumn?: ColumnListInput;
|
|
1166
1174
|
readonly targetTable?: string;
|
|
1167
1175
|
readonly function?: string;
|
|
1176
|
+
/** Apply DISTINCT to a SELECT-list aggregate (e.g. COUNT(DISTINCT col)). */
|
|
1177
|
+
readonly distinct?: boolean;
|
|
1168
1178
|
readonly args?: readonly unknown[];
|
|
1169
1179
|
readonly conditions?: readonly Decision[];
|
|
1170
1180
|
readonly columns?: readonly string[];
|
|
@@ -1201,6 +1211,15 @@ interface Decision {
|
|
|
1201
1211
|
readonly cycleDetection?: boolean;
|
|
1202
1212
|
readonly selectColumn?: string;
|
|
1203
1213
|
readonly aggregate?: string;
|
|
1214
|
+
/**
|
|
1215
|
+
* Apply DISTINCT to a scalar subquery's aggregate (e.g. AVG(DISTINCT price)).
|
|
1216
|
+
* Deliberately NOT named `distinct` — `assertNoDroppedDecisionModifiers`
|
|
1217
|
+
* (subquery-emission.ts) treats a top-level `distinct === true` on ANY
|
|
1218
|
+
* subquery decision as an unsupported query-level DISTINCT modifier and
|
|
1219
|
+
* throws. This field is scoped to the aggregate projection only, so it
|
|
1220
|
+
* must not collide with that generic guard.
|
|
1221
|
+
*/
|
|
1222
|
+
readonly aggregateDistinct?: boolean;
|
|
1204
1223
|
readonly subqueryOperator?: string;
|
|
1205
1224
|
readonly traversal?: string;
|
|
1206
1225
|
readonly traversals?: readonly {
|
package/dist/index.js
CHANGED
|
@@ -1268,6 +1268,7 @@ function compileExpressionIntent(intent, ctx, state) {
|
|
|
1268
1268
|
return sortBy(colNode, ob.direction === "desc" ? "DESC" : "ASC");
|
|
1269
1269
|
}) : void 0;
|
|
1270
1270
|
return funcCall(nameParts, argNodes, {
|
|
1271
|
+
distinct: i.distinct === true,
|
|
1271
1272
|
...orderByNodes ? { orderBy: orderByNodes } : {}
|
|
1272
1273
|
});
|
|
1273
1274
|
}
|
|
@@ -1488,6 +1489,7 @@ function handleAggregateExpression(expr, rootTable, decisions, applyFilter) {
|
|
|
1488
1489
|
table: rootTable
|
|
1489
1490
|
};
|
|
1490
1491
|
if (aggAs) decision.alias = aggAs;
|
|
1492
|
+
if (aggDistinct === true) decision.distinct = true;
|
|
1491
1493
|
decisions.push(decision);
|
|
1492
1494
|
} else if (aggFunc === "count" && !aggField) {
|
|
1493
1495
|
const decision = {
|
|
@@ -1496,10 +1498,11 @@ function handleAggregateExpression(expr, rootTable, decisions, applyFilter) {
|
|
|
1496
1498
|
column: "*",
|
|
1497
1499
|
table: rootTable
|
|
1498
1500
|
};
|
|
1501
|
+
if (aggDistinct === true) decision.distinct = true;
|
|
1499
1502
|
if (aggAs) decision.alias = aggAs;
|
|
1500
1503
|
applyFilter(decision, aggFilter, rootTable);
|
|
1501
1504
|
decisions.push(decision);
|
|
1502
|
-
} else if (aggFunc === "count" && aggDistinct && aggField) {
|
|
1505
|
+
} else if (aggFunc === "count" && aggDistinct === true && aggField) {
|
|
1503
1506
|
const decision = {
|
|
1504
1507
|
type: "selectFunction",
|
|
1505
1508
|
function: "countDistinct",
|
|
@@ -1517,6 +1520,7 @@ function handleAggregateExpression(expr, rootTable, decisions, applyFilter) {
|
|
|
1517
1520
|
};
|
|
1518
1521
|
if (aggField) decision.column = aggField;
|
|
1519
1522
|
if (aggAs) decision.alias = aggAs;
|
|
1523
|
+
if (aggDistinct === true) decision.distinct = true;
|
|
1520
1524
|
applyFilter(decision, aggFilter, rootTable);
|
|
1521
1525
|
decisions.push(decision);
|
|
1522
1526
|
}
|
|
@@ -1797,6 +1801,9 @@ function convertSelect(select, rootTable) {
|
|
|
1797
1801
|
if (agg.as) {
|
|
1798
1802
|
aggDecision.alias = agg.as;
|
|
1799
1803
|
}
|
|
1804
|
+
if (agg.distinct === true) {
|
|
1805
|
+
aggDecision.distinct = true;
|
|
1806
|
+
}
|
|
1800
1807
|
applyFilterCondition(aggDecision, agg.filter, rootTable);
|
|
1801
1808
|
decisions.push(aggDecision);
|
|
1802
1809
|
}
|
|
@@ -2127,6 +2134,7 @@ function convertSubquery(cond) {
|
|
|
2127
2134
|
const targetTable = subquery.from;
|
|
2128
2135
|
let selectColumn = "*";
|
|
2129
2136
|
let aggregate;
|
|
2137
|
+
let aggregateDistinct;
|
|
2130
2138
|
const select = subquery.select;
|
|
2131
2139
|
if (select) {
|
|
2132
2140
|
if ("type" in select && select.type === "aggregate") {
|
|
@@ -2134,6 +2142,7 @@ function convertSubquery(cond) {
|
|
|
2134
2142
|
if (agg) {
|
|
2135
2143
|
aggregate = agg.function;
|
|
2136
2144
|
selectColumn = agg.field ?? "*";
|
|
2145
|
+
aggregateDistinct = agg.distinct === true ? true : void 0;
|
|
2137
2146
|
}
|
|
2138
2147
|
} else if ("fields" in select && select.fields?.length) {
|
|
2139
2148
|
selectColumn = select.fields[0];
|
|
@@ -2167,6 +2176,7 @@ function convertSubquery(cond) {
|
|
|
2167
2176
|
// Provenance: original QueryIntent for validation in buildPredicateSubquerySelect
|
|
2168
2177
|
subqueryIntent: subquery,
|
|
2169
2178
|
...aggregate && { aggregate },
|
|
2179
|
+
...aggregateDistinct && { aggregateDistinct },
|
|
2170
2180
|
...subConditions.length > 0 && { conditions: subConditions },
|
|
2171
2181
|
...rawLimit != null && { limit: rawLimit },
|
|
2172
2182
|
...rawOrderBy && rawOrderBy.length > 0 && {
|
|
@@ -4564,21 +4574,19 @@ function buildSubqueryFromIntent(intent, paramOffset, naming = identityNaming, s
|
|
|
4564
4574
|
if (select?.type === "aggregate" && select.aggregates && select.aggregates.length > 0) {
|
|
4565
4575
|
targetList = select.aggregates.map((agg) => {
|
|
4566
4576
|
let aggNode;
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
}
|
|
4577
|
+
const field = agg.field;
|
|
4578
|
+
if (!field || field === "*") {
|
|
4579
|
+
if (agg.distinct === true) {
|
|
4580
|
+
throw new Error(
|
|
4581
|
+
`${agg.function}(DISTINCT *) is not valid SQL \u2014 PostgreSQL does not support DISTINCT on a star aggregate; provide a specific column.`
|
|
4582
|
+
);
|
|
4583
|
+
}
|
|
4584
|
+
aggNode = funcCall(agg.function.toLowerCase(), [], { star: true });
|
|
4574
4585
|
} else {
|
|
4575
|
-
const aggArg = columnRef(
|
|
4576
|
-
aggNode = {
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
args: [aggArg]
|
|
4580
|
-
}
|
|
4581
|
-
};
|
|
4586
|
+
const aggArg = columnRef(field, innerAlias, void 0, naming);
|
|
4587
|
+
aggNode = funcCall(agg.function.toLowerCase(), [aggArg], {
|
|
4588
|
+
distinct: agg.distinct === true
|
|
4589
|
+
});
|
|
4582
4590
|
}
|
|
4583
4591
|
return { ResTarget: { val: aggNode } };
|
|
4584
4592
|
});
|
|
@@ -5222,6 +5230,8 @@ function assertNoDroppedDecisionModifiers(decision, use) {
|
|
|
5222
5230
|
if (d.having != null) unsupported.push("HAVING");
|
|
5223
5231
|
if (d.offset != null) unsupported.push("OFFSET");
|
|
5224
5232
|
if (d.distinct === true) unsupported.push("DISTINCT");
|
|
5233
|
+
if (d.aggregateDistinct === true && d.aggregate == null)
|
|
5234
|
+
unsupported.push("aggregate DISTINCT without an aggregate function");
|
|
5225
5235
|
if (Array.isArray(d.distinctOn) && d.distinctOn.length > 0)
|
|
5226
5236
|
unsupported.push("DISTINCT ON");
|
|
5227
5237
|
if (Array.isArray(d.include) && d.include.length > 0)
|
|
@@ -5271,6 +5281,11 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
|
|
|
5271
5281
|
let targetVal;
|
|
5272
5282
|
if (aggregate) {
|
|
5273
5283
|
if (selectColumn === "*") {
|
|
5284
|
+
if (decision.aggregateDistinct === true) {
|
|
5285
|
+
throw new Error(
|
|
5286
|
+
`${aggregate}(DISTINCT *) is not valid SQL \u2014 PostgreSQL does not support DISTINCT on a star aggregate; provide a specific column.`
|
|
5287
|
+
);
|
|
5288
|
+
}
|
|
5274
5289
|
targetVal = {
|
|
5275
5290
|
FuncCall: {
|
|
5276
5291
|
funcname: [{ String: { sval: aggregate.toLowerCase() } }],
|
|
@@ -5287,7 +5302,8 @@ function buildPredicateSubquerySelect(use, sourceIntent, decision, ctx, state, d
|
|
|
5287
5302
|
targetVal = {
|
|
5288
5303
|
FuncCall: {
|
|
5289
5304
|
funcname: [{ String: { sval: aggregate.toLowerCase() } }],
|
|
5290
|
-
args: [aggArg]
|
|
5305
|
+
args: [aggArg],
|
|
5306
|
+
...decision.aggregateDistinct === true && { agg_distinct: true }
|
|
5291
5307
|
}
|
|
5292
5308
|
};
|
|
5293
5309
|
}
|
|
@@ -6503,7 +6519,13 @@ var init_handlers = __esm({
|
|
|
6503
6519
|
|
|
6504
6520
|
// src/handlers/expression/aggregate.ts
|
|
6505
6521
|
function buildAggregate(funcName, column, distinct, ctx, filterNode) {
|
|
6506
|
-
const
|
|
6522
|
+
const isStarColumn = !column || column === "*";
|
|
6523
|
+
if (distinct && isStarColumn) {
|
|
6524
|
+
throw new Error(
|
|
6525
|
+
`${funcName}(DISTINCT *) is not valid SQL \u2014 PostgreSQL does not support DISTINCT on a star aggregate; provide a specific column.`
|
|
6526
|
+
);
|
|
6527
|
+
}
|
|
6528
|
+
const isCountStar = funcName === "count" && isStarColumn;
|
|
6507
6529
|
if (isCountStar) {
|
|
6508
6530
|
return funcCall(funcName, [], {
|
|
6509
6531
|
star: true,
|
|
@@ -6535,7 +6557,7 @@ function createSimpleAggregateHandler(funcName, types) {
|
|
|
6535
6557
|
return buildAggregate(
|
|
6536
6558
|
funcName,
|
|
6537
6559
|
decision.column,
|
|
6538
|
-
|
|
6560
|
+
decision.distinct === true,
|
|
6539
6561
|
ctx,
|
|
6540
6562
|
decision.filterWhere
|
|
6541
6563
|
);
|
|
@@ -6551,7 +6573,7 @@ var init_aggregate = __esm({
|
|
|
6551
6573
|
types: ["count", "COUNT"],
|
|
6552
6574
|
compile(decision, ctx, _state) {
|
|
6553
6575
|
const column = decision.column;
|
|
6554
|
-
const distinct = decision.
|
|
6576
|
+
const distinct = decision.distinct === true || decision.operator === "countDistinct";
|
|
6555
6577
|
return buildAggregate("count", column, distinct, ctx, decision.filterWhere);
|
|
6556
6578
|
}
|
|
6557
6579
|
};
|
|
@@ -6583,7 +6605,7 @@ var init_aggregate = __esm({
|
|
|
6583
6605
|
return buildAggregate(
|
|
6584
6606
|
funcName,
|
|
6585
6607
|
decision.column,
|
|
6586
|
-
|
|
6608
|
+
decision.distinct === true,
|
|
6587
6609
|
ctx,
|
|
6588
6610
|
decision.filterWhere
|
|
6589
6611
|
);
|
|
@@ -9189,6 +9211,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
9189
9211
|
targetColumn: pd.targetColumn ?? derivedFkColumns.targetColumn,
|
|
9190
9212
|
targetTable: pd.targetTable,
|
|
9191
9213
|
function: pd.function,
|
|
9214
|
+
distinct: pd.distinct,
|
|
9192
9215
|
args: pd.args,
|
|
9193
9216
|
columns: pd.columns,
|
|
9194
9217
|
values: pd.values,
|
|
@@ -9226,6 +9249,7 @@ function mapToHandlerDecision(pd, rootTable, defaultPk, deriveFk) {
|
|
|
9226
9249
|
subqueryOperator: pd.subqueryOperator,
|
|
9227
9250
|
selectColumn: pd.selectColumn,
|
|
9228
9251
|
aggregate: pd.aggregate,
|
|
9252
|
+
aggregateDistinct: pd.aggregateDistinct,
|
|
9229
9253
|
columnAliases: pd.columnAliases,
|
|
9230
9254
|
escape: pd.escape,
|
|
9231
9255
|
subqueryIntent: pd.subqueryIntent
|
|
@@ -10054,13 +10078,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10054
10078
|
const renumbered = renumberParamRefsInAst(innerResult.ast, paramOffset);
|
|
10055
10079
|
return { ast: renumbered, parameters: innerResult.parameters };
|
|
10056
10080
|
}
|
|
10057
|
-
compileGenericNqlFunction(functionName, args, ctx, state) {
|
|
10081
|
+
compileGenericNqlFunction(functionName, args, ctx, state, distinct = false) {
|
|
10058
10082
|
assertNqlSelectScalarFunctionAllowed(functionName);
|
|
10059
10083
|
validateIdentifier(functionName, "function");
|
|
10060
10084
|
const argNodes = args.map(
|
|
10061
10085
|
(arg) => this.compileNqlFunctionArg(arg, ctx, state)
|
|
10062
10086
|
);
|
|
10063
|
-
return funcCall(functionName, argNodes);
|
|
10087
|
+
return funcCall(functionName, argNodes, { distinct });
|
|
10064
10088
|
}
|
|
10065
10089
|
compileNqlFunctionArg(arg, ctx, state) {
|
|
10066
10090
|
if (isNqlBindingRef(arg)) {
|
|
@@ -10139,6 +10163,7 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10139
10163
|
"NQL aggregate expression requires a function name"
|
|
10140
10164
|
);
|
|
10141
10165
|
}
|
|
10166
|
+
const isDistinct = record.distinct === true;
|
|
10142
10167
|
const aggregateArgs = [];
|
|
10143
10168
|
if (record.field === "*") {
|
|
10144
10169
|
aggregateArgs.push({ kind: "star" });
|
|
@@ -10148,7 +10173,13 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10148
10173
|
if (Array.isArray(record.extraArgs)) {
|
|
10149
10174
|
aggregateArgs.push(...record.extraArgs);
|
|
10150
10175
|
}
|
|
10151
|
-
return this.compileGenericNqlFunction(
|
|
10176
|
+
return this.compileGenericNqlFunction(
|
|
10177
|
+
fn4,
|
|
10178
|
+
aggregateArgs,
|
|
10179
|
+
ctx,
|
|
10180
|
+
state,
|
|
10181
|
+
isDistinct
|
|
10182
|
+
);
|
|
10152
10183
|
}
|
|
10153
10184
|
case "arithmetic": {
|
|
10154
10185
|
const operator = record.operator;
|
|
@@ -10355,7 +10386,8 @@ var PlanCompiler = class _PlanCompiler {
|
|
|
10355
10386
|
funcType,
|
|
10356
10387
|
decision.args ?? [],
|
|
10357
10388
|
ctx,
|
|
10358
|
-
state
|
|
10389
|
+
state,
|
|
10390
|
+
decision.distinct === true
|
|
10359
10391
|
);
|
|
10360
10392
|
this.state.paramIndex = state.paramIndex;
|
|
10361
10393
|
targetList.push({
|