@dbsp/core 1.0.2 → 1.0.4
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 +30 -9
- package/dist/index.js +262 -115
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2146,6 +2146,7 @@ function plan(intent, model, options = {}) {
|
|
|
2146
2146
|
throw new Error(`Unknown table: ${intent.from}`);
|
|
2147
2147
|
}
|
|
2148
2148
|
const optimizedWhere = intent.where ? optimizeInToExists(intent.where, intent.from, model) : void 0;
|
|
2149
|
+
const plannedIntent = optimizedWhere !== void 0 && optimizedWhere !== intent.where ? { ...intent, where: optimizedWhere } : intent;
|
|
2149
2150
|
if (optimizedWhere) {
|
|
2150
2151
|
processWhere(optimizedWhere, intent.from, model, state, opts, "where");
|
|
2151
2152
|
}
|
|
@@ -2188,7 +2189,13 @@ function plan(intent, model, options = {}) {
|
|
|
2188
2189
|
decisions: Object.freeze(state.decisions.slice()),
|
|
2189
2190
|
warnings: Object.freeze(state.warnings.slice()),
|
|
2190
2191
|
ctes: Object.freeze(state.ctes.slice()),
|
|
2192
|
+
// intent is ALWAYS the original submitted intent (contract: observable via dump()).
|
|
2193
|
+
// executableIntent carries the optimized WHERE (e.g. IN→EXISTS) when the
|
|
2194
|
+
// optimizer rewrote it, so the adapter compiles the correct SQL from that field.
|
|
2195
|
+
// When no optimization applies, executableIntent is left undefined and the
|
|
2196
|
+
// adapter falls back to intent.
|
|
2191
2197
|
intent,
|
|
2198
|
+
...plannedIntent !== intent && { executableIntent: plannedIntent },
|
|
2192
2199
|
metadata
|
|
2193
2200
|
};
|
|
2194
2201
|
return Object.freeze(report);
|
|
@@ -2326,11 +2333,15 @@ function isSubquerySelectedColumnNonNullable(existsIntent, sourceTable, model) {
|
|
|
2326
2333
|
if (!column) return false;
|
|
2327
2334
|
return !column.nullable;
|
|
2328
2335
|
}
|
|
2329
|
-
function optimizeInToExists(where, sourceTable, model) {
|
|
2336
|
+
function optimizeInToExists(where, sourceTable, model, negated = false) {
|
|
2330
2337
|
switch (where.kind) {
|
|
2331
2338
|
case "in": {
|
|
2332
2339
|
const inWhere = where;
|
|
2333
2340
|
if (!inWhere.subquery) return where;
|
|
2341
|
+
const sq = inWhere.subquery;
|
|
2342
|
+
if (sq.limit != null || sq.orderBy?.length || sq.offset != null || sq.groupBy?.length || sq.having != null || sq.distinct || sq.distinctOn?.length || sq.joins?.length || sq.include?.length || sq.batchValuesSource != null || sq.existsWrap || sq.lock != null || sq.select != null && sq.select.type !== "fields") {
|
|
2343
|
+
return where;
|
|
2344
|
+
}
|
|
2334
2345
|
const subSelect = inWhere.subquery.select;
|
|
2335
2346
|
if (!subSelect || subSelect.type !== "fields") return where;
|
|
2336
2347
|
const fields = "fields" in subSelect ? subSelect.fields : void 0;
|
|
@@ -2350,18 +2361,24 @@ function optimizeInToExists(where, sourceTable, model) {
|
|
|
2350
2361
|
}
|
|
2351
2362
|
}
|
|
2352
2363
|
if (!matchedRelation) return where;
|
|
2353
|
-
const
|
|
2354
|
-
|
|
2364
|
+
const effectiveNegated = negated !== Boolean(inWhere.not);
|
|
2365
|
+
if (effectiveNegated) {
|
|
2366
|
+
const existsIntent = { relation: matchedRelation };
|
|
2367
|
+
if (!isSubquerySelectedColumnNonNullable(existsIntent, sourceTable, model)) {
|
|
2368
|
+
return where;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
const targetKind = inWhere.not ? "notExists" : "exists";
|
|
2372
|
+
return {
|
|
2373
|
+
kind: targetKind,
|
|
2355
2374
|
relation: matchedRelation,
|
|
2356
|
-
// Forward the subquery's inner WHERE conditions
|
|
2357
2375
|
...inWhere.subquery.where && { where: inWhere.subquery.where }
|
|
2358
2376
|
};
|
|
2359
|
-
return existsWhere;
|
|
2360
2377
|
}
|
|
2361
2378
|
case "and": {
|
|
2362
2379
|
const andWhere = where;
|
|
2363
2380
|
const optimized = andWhere.conditions.map(
|
|
2364
|
-
(c) => optimizeInToExists(c, sourceTable, model)
|
|
2381
|
+
(c) => optimizeInToExists(c, sourceTable, model, negated)
|
|
2365
2382
|
);
|
|
2366
2383
|
if (optimized.every((c, i) => c === andWhere.conditions[i])) return where;
|
|
2367
2384
|
return { kind: "and", conditions: optimized };
|
|
@@ -2369,7 +2386,7 @@ function optimizeInToExists(where, sourceTable, model) {
|
|
|
2369
2386
|
case "or": {
|
|
2370
2387
|
const orWhere = where;
|
|
2371
2388
|
const optimized = orWhere.conditions.map(
|
|
2372
|
-
(c) => optimizeInToExists(c, sourceTable, model)
|
|
2389
|
+
(c) => optimizeInToExists(c, sourceTable, model, negated)
|
|
2373
2390
|
);
|
|
2374
2391
|
if (optimized.every((c, i) => c === orWhere.conditions[i])) return where;
|
|
2375
2392
|
return { kind: "or", conditions: optimized };
|
|
@@ -2379,22 +2396,10 @@ function optimizeInToExists(where, sourceTable, model) {
|
|
|
2379
2396
|
const optimized = optimizeInToExists(
|
|
2380
2397
|
notWhere.condition,
|
|
2381
2398
|
sourceTable,
|
|
2382
|
-
model
|
|
2399
|
+
model,
|
|
2400
|
+
!negated
|
|
2383
2401
|
);
|
|
2384
2402
|
if (optimized === notWhere.condition) return where;
|
|
2385
|
-
if (optimized.kind === "exists") {
|
|
2386
|
-
if (!isSubquerySelectedColumnNonNullable(
|
|
2387
|
-
optimized,
|
|
2388
|
-
sourceTable,
|
|
2389
|
-
model
|
|
2390
|
-
)) {
|
|
2391
|
-
return where;
|
|
2392
|
-
}
|
|
2393
|
-
return {
|
|
2394
|
-
...optimized,
|
|
2395
|
-
kind: "notExists"
|
|
2396
|
-
};
|
|
2397
|
-
}
|
|
2398
2403
|
return { kind: "not", condition: optimized };
|
|
2399
2404
|
}
|
|
2400
2405
|
default:
|
|
@@ -2476,6 +2481,26 @@ function processWhere(where, sourceTable, model, state, opts, intentPath) {
|
|
|
2476
2481
|
break;
|
|
2477
2482
|
case "expression":
|
|
2478
2483
|
break;
|
|
2484
|
+
// Custom expression — no relation analysis, pass through
|
|
2485
|
+
// Adapter-only kinds: planner records no decisions; the adapter compiles
|
|
2486
|
+
// them directly from the intent. Explicit cases here prevent silent
|
|
2487
|
+
// fallthrough and keep the switch exhaustive.
|
|
2488
|
+
case "rawExists":
|
|
2489
|
+
case "rawNotExists":
|
|
2490
|
+
break;
|
|
2491
|
+
case "subquery":
|
|
2492
|
+
break;
|
|
2493
|
+
case "range":
|
|
2494
|
+
break;
|
|
2495
|
+
case "jsonContains":
|
|
2496
|
+
case "jsonExists":
|
|
2497
|
+
break;
|
|
2498
|
+
default: {
|
|
2499
|
+
const _exhaustive = where;
|
|
2500
|
+
throw new Error(
|
|
2501
|
+
`processWhere: unhandled WhereIntent kind '${_exhaustive.kind}'`
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2479
2504
|
}
|
|
2480
2505
|
}
|
|
2481
2506
|
function processRelationFilter(relationPath, sourceTable, model, state, opts, intentPath, nestedWhere, mode) {
|
|
@@ -3012,6 +3037,60 @@ function assertCapability(adapter, capability, operation) {
|
|
|
3012
3037
|
}
|
|
3013
3038
|
|
|
3014
3039
|
// src/dx/batch-values.ts
|
|
3040
|
+
var MULTIWORD_BASE_TYPES = [
|
|
3041
|
+
"timestamp with time zone",
|
|
3042
|
+
"timestamp without time zone",
|
|
3043
|
+
"time with time zone",
|
|
3044
|
+
"time without time zone",
|
|
3045
|
+
"double precision",
|
|
3046
|
+
"character varying",
|
|
3047
|
+
"bit varying"
|
|
3048
|
+
];
|
|
3049
|
+
var IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3050
|
+
function validateTypeName(typeName) {
|
|
3051
|
+
const raw2 = typeName.trim();
|
|
3052
|
+
if (raw2.length === 0) {
|
|
3053
|
+
throw new Error(
|
|
3054
|
+
`batchValues: invalid type name '${typeName}'. Type names must not be empty.`
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
3057
|
+
let rest = raw2;
|
|
3058
|
+
if (rest.endsWith("[]")) {
|
|
3059
|
+
rest = rest.slice(0, -2);
|
|
3060
|
+
if (rest.endsWith("[]")) {
|
|
3061
|
+
throw new Error(
|
|
3062
|
+
`batchValues: invalid type name '${typeName}'. At most one array suffix "[]" is allowed as a raw type-name input. Use "int4[]" not "int4[][]".`
|
|
3063
|
+
);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
const modifierMatch = rest.match(/\(([^)]*)\)$/);
|
|
3067
|
+
if (modifierMatch) {
|
|
3068
|
+
const inner = modifierMatch[1] ?? "";
|
|
3069
|
+
if (!/^\d+(?:,\d+)?$/.test(inner)) {
|
|
3070
|
+
throw new Error(
|
|
3071
|
+
`batchValues: invalid type name '${typeName}'. Type modifier must be "(N)" or "(N,M)" with digits only; got "(${inner})".`
|
|
3072
|
+
);
|
|
3073
|
+
}
|
|
3074
|
+
rest = rest.slice(0, rest.length - modifierMatch[0].length).trimEnd();
|
|
3075
|
+
}
|
|
3076
|
+
const baseLower = rest.toLowerCase();
|
|
3077
|
+
if (MULTIWORD_BASE_TYPES.includes(baseLower)) {
|
|
3078
|
+
return;
|
|
3079
|
+
}
|
|
3080
|
+
const parts = rest.split(".");
|
|
3081
|
+
if (parts.length > 2) {
|
|
3082
|
+
throw new Error(
|
|
3083
|
+
`batchValues: invalid type name '${typeName}'. Schema-qualified types allow at most one dot (schema.type).`
|
|
3084
|
+
);
|
|
3085
|
+
}
|
|
3086
|
+
for (const part of parts) {
|
|
3087
|
+
if (!IDENT_RE.test(part)) {
|
|
3088
|
+
throw new Error(
|
|
3089
|
+
`batchValues: invalid type name '${typeName}'. Base type "${part}" is not a valid SQL identifier ([A-Za-z_][A-Za-z0-9_]*) and is not in the multi-word type allowlist.`
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3015
3094
|
function isBatchValuesRef(value) {
|
|
3016
3095
|
return typeof value === "object" && value !== null && "__kind" in value && value.__kind === "batchValues";
|
|
3017
3096
|
}
|
|
@@ -3024,20 +3103,28 @@ function batchValues(data, columns, types, opts) {
|
|
|
3024
3103
|
if (columns.length === 0) {
|
|
3025
3104
|
throw new Error("batchValues: at least one column is required");
|
|
3026
3105
|
}
|
|
3027
|
-
const
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
`batchValues: invalid type name '${invalidType}'. Type names must contain only letters, digits, and underscores.`
|
|
3031
|
-
);
|
|
3106
|
+
const normalizedTypes = types.map((t) => t.trim());
|
|
3107
|
+
for (const t of normalizedTypes) {
|
|
3108
|
+
validateTypeName(t);
|
|
3032
3109
|
}
|
|
3033
|
-
|
|
3110
|
+
const alias = opts?.alias ?? "batch";
|
|
3111
|
+
validateIdentifier(alias, "alias");
|
|
3112
|
+
for (const col2 of columns) {
|
|
3113
|
+
validateIdentifier(col2, "column");
|
|
3114
|
+
}
|
|
3115
|
+
const frozenData = Object.freeze(
|
|
3116
|
+
data.map((row) => Object.freeze([...row]))
|
|
3117
|
+
);
|
|
3118
|
+
const frozenColumns = Object.freeze([...columns]);
|
|
3119
|
+
const frozenTypes = Object.freeze([...normalizedTypes]);
|
|
3120
|
+
return Object.freeze({
|
|
3034
3121
|
__kind: "batchValues",
|
|
3035
|
-
data,
|
|
3036
|
-
columns,
|
|
3037
|
-
types,
|
|
3038
|
-
alias
|
|
3122
|
+
data: frozenData,
|
|
3123
|
+
columns: frozenColumns,
|
|
3124
|
+
types: frozenTypes,
|
|
3125
|
+
alias,
|
|
3039
3126
|
ordinality: opts?.ordinality ?? false
|
|
3040
|
-
};
|
|
3127
|
+
});
|
|
3041
3128
|
}
|
|
3042
3129
|
|
|
3043
3130
|
// src/dx/expressions.ts
|
|
@@ -3662,6 +3749,23 @@ var DEFAULT_FEATURE_CHECKERS = Object.freeze([
|
|
|
3662
3749
|
}
|
|
3663
3750
|
return usages;
|
|
3664
3751
|
}
|
|
3752
|
+
},
|
|
3753
|
+
// -----------------------------------------------------------------------
|
|
3754
|
+
// Row-Level Security
|
|
3755
|
+
// -----------------------------------------------------------------------
|
|
3756
|
+
{
|
|
3757
|
+
capability: "supportsDDLRowLevelSecurity",
|
|
3758
|
+
feature: "rowLevelSecurity",
|
|
3759
|
+
detectUsage(model) {
|
|
3760
|
+
if (!model.tables) return [];
|
|
3761
|
+
const usages = [];
|
|
3762
|
+
for (const [tableName, table] of model.tables) {
|
|
3763
|
+
if (table.rlsEnabled || table.policies?.length) {
|
|
3764
|
+
usages.push({ table: tableName, detail: tableName });
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
return usages;
|
|
3768
|
+
}
|
|
3665
3769
|
}
|
|
3666
3770
|
]);
|
|
3667
3771
|
|
|
@@ -3772,11 +3876,12 @@ var WindowBuilder = class _WindowBuilder {
|
|
|
3772
3876
|
};
|
|
3773
3877
|
}
|
|
3774
3878
|
/**
|
|
3775
|
-
* Convert builder state to WindowIntent
|
|
3879
|
+
* Convert builder state to WindowIntent — produces the correct discriminated branch.
|
|
3880
|
+
* - ranking → RankingWindowIntent (no field)
|
|
3881
|
+
* - aggregate → AggregateWindowIntent (field required; COUNT uses '*' when omitted)
|
|
3882
|
+
* - offset → OffsetWindowIntent (field required)
|
|
3776
3883
|
*/
|
|
3777
3884
|
toWindowIntent(alias) {
|
|
3778
|
-
const fn2 = this.fnKind.fn;
|
|
3779
|
-
const field = this.fnKind.type === "aggregate" || this.fnKind.type === "offset" ? this.fnKind.field : void 0;
|
|
3780
3885
|
const over = {};
|
|
3781
3886
|
if (this.partitions.length > 0) {
|
|
3782
3887
|
over.partitionBy = this.partitions;
|
|
@@ -3784,16 +3889,31 @@ var WindowBuilder = class _WindowBuilder {
|
|
|
3784
3889
|
if (this.orders.length > 0) {
|
|
3785
3890
|
over.orderBy = this.orders;
|
|
3786
3891
|
}
|
|
3787
|
-
|
|
3892
|
+
if (this.fnKind.type === "ranking") {
|
|
3893
|
+
return {
|
|
3894
|
+
kind: "window",
|
|
3895
|
+
function: this.fnKind.fn,
|
|
3896
|
+
alias,
|
|
3897
|
+
over
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
if (this.fnKind.type === "offset") {
|
|
3901
|
+
return {
|
|
3902
|
+
kind: "window",
|
|
3903
|
+
function: this.fnKind.fn,
|
|
3904
|
+
field: this.fnKind.field,
|
|
3905
|
+
alias,
|
|
3906
|
+
over
|
|
3907
|
+
};
|
|
3908
|
+
}
|
|
3909
|
+
const { field } = this.fnKind;
|
|
3910
|
+
return {
|
|
3788
3911
|
kind: "window",
|
|
3789
|
-
function:
|
|
3912
|
+
function: this.fnKind.fn,
|
|
3913
|
+
...field !== void 0 && { field },
|
|
3790
3914
|
alias,
|
|
3791
3915
|
over
|
|
3792
3916
|
};
|
|
3793
|
-
if (field !== void 0) {
|
|
3794
|
-
return { ...intent, field };
|
|
3795
|
-
}
|
|
3796
|
-
return intent;
|
|
3797
3917
|
}
|
|
3798
3918
|
};
|
|
3799
3919
|
function rowNumber() {
|
|
@@ -3871,7 +3991,6 @@ function inSubquery(field, query) {
|
|
|
3871
3991
|
return {
|
|
3872
3992
|
kind: "in",
|
|
3873
3993
|
field: getColumnName(field),
|
|
3874
|
-
values: [],
|
|
3875
3994
|
subquery: expr.toIntent()
|
|
3876
3995
|
};
|
|
3877
3996
|
}
|
|
@@ -3986,36 +4105,31 @@ function coalesce(fields, as) {
|
|
|
3986
4105
|
};
|
|
3987
4106
|
}
|
|
3988
4107
|
function raw(sqlFragment, as) {
|
|
3989
|
-
|
|
3990
|
-
throw new Error("raw() requires a non-empty alias");
|
|
3991
|
-
}
|
|
4108
|
+
validateIdentifier(as, "column");
|
|
3992
4109
|
return {
|
|
3993
4110
|
__expr: true,
|
|
3994
4111
|
intent: { kind: "raw", sql: sqlFragment, as }
|
|
3995
4112
|
};
|
|
3996
4113
|
}
|
|
3997
4114
|
function col(column, alias) {
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
}
|
|
4001
|
-
if (!alias || alias.trim() === "") {
|
|
4002
|
-
throw new Error("col() requires a non-empty alias");
|
|
4003
|
-
}
|
|
4115
|
+
validateIdentifier(column, "column");
|
|
4116
|
+
validateIdentifier(alias, "column");
|
|
4004
4117
|
return {
|
|
4005
4118
|
__expr: true,
|
|
4006
4119
|
intent: { kind: "columnAlias", column, alias }
|
|
4007
4120
|
};
|
|
4008
4121
|
}
|
|
4009
4122
|
function relationColumn(relation, column, as) {
|
|
4010
|
-
if (!relation
|
|
4123
|
+
if (!relation) {
|
|
4011
4124
|
throw new Error("relationColumn() requires a non-empty relation path");
|
|
4012
4125
|
}
|
|
4013
|
-
|
|
4014
|
-
|
|
4126
|
+
for (const segment of relation.split(".")) {
|
|
4127
|
+
validateIdentifier(segment, "relation");
|
|
4015
4128
|
}
|
|
4016
|
-
if (
|
|
4017
|
-
|
|
4129
|
+
if (column !== "*") {
|
|
4130
|
+
validateIdentifier(column, "column");
|
|
4018
4131
|
}
|
|
4132
|
+
validateIdentifier(as, "column");
|
|
4019
4133
|
return {
|
|
4020
4134
|
__expr: true,
|
|
4021
4135
|
intent: { kind: "relationColumn", relation, column, as }
|
|
@@ -4374,7 +4488,7 @@ async function runAfterMutationHooks(hooks, ctx, result, onHookError) {
|
|
|
4374
4488
|
error,
|
|
4375
4489
|
hook.name || "anonymous",
|
|
4376
4490
|
frozen,
|
|
4377
|
-
"
|
|
4491
|
+
"afterMutation"
|
|
4378
4492
|
);
|
|
4379
4493
|
if (action === "continue") continue;
|
|
4380
4494
|
}
|
|
@@ -4587,7 +4701,8 @@ function subquery(table) {
|
|
|
4587
4701
|
function outerRef(column) {
|
|
4588
4702
|
return {
|
|
4589
4703
|
kind: "ref",
|
|
4590
|
-
column
|
|
4704
|
+
column,
|
|
4705
|
+
outer: true
|
|
4591
4706
|
};
|
|
4592
4707
|
}
|
|
4593
4708
|
function isSubqueryExpression(value) {
|
|
@@ -4901,17 +5016,24 @@ var IntentBuilder = class _IntentBuilder {
|
|
|
4901
5016
|
* DX-034: Now supports distinct flag for COUNT(DISTINCT field), etc.
|
|
4902
5017
|
*/
|
|
4903
5018
|
addAggregate(func, field, options) {
|
|
4904
|
-
const
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
5019
|
+
const resolvedField = field ?? options?.field;
|
|
5020
|
+
const as = options?.as;
|
|
5021
|
+
const distinct2 = options?.distinct ? true : void 0;
|
|
5022
|
+
let agg;
|
|
5023
|
+
if (func === "count") {
|
|
5024
|
+
agg = {
|
|
5025
|
+
function: "count",
|
|
5026
|
+
...resolvedField !== void 0 && { field: resolvedField },
|
|
5027
|
+
...as !== void 0 && { as },
|
|
5028
|
+
...distinct2 !== void 0 && { distinct: distinct2 }
|
|
5029
|
+
};
|
|
5030
|
+
} else {
|
|
5031
|
+
agg = {
|
|
5032
|
+
function: func,
|
|
5033
|
+
field: resolvedField ?? "",
|
|
5034
|
+
...as !== void 0 && { as },
|
|
5035
|
+
...distinct2 !== void 0 && { distinct: distinct2 }
|
|
5036
|
+
};
|
|
4915
5037
|
}
|
|
4916
5038
|
this.state.aggregates.push(agg);
|
|
4917
5039
|
}
|
|
@@ -5956,7 +6078,20 @@ var UpsertBuilder = class _UpsertBuilder extends MutationBuilderBase {
|
|
|
5956
6078
|
};
|
|
5957
6079
|
|
|
5958
6080
|
// src/dx/negotiate-features.ts
|
|
5959
|
-
|
|
6081
|
+
var UnsupportedFeatureError = class extends Error {
|
|
6082
|
+
constructor(feature, adapter, element) {
|
|
6083
|
+
super(
|
|
6084
|
+
`Unsupported feature "${feature}" on adapter "${adapter}" for "${element}"`
|
|
6085
|
+
);
|
|
6086
|
+
this.feature = feature;
|
|
6087
|
+
this.adapter = adapter;
|
|
6088
|
+
this.element = element;
|
|
6089
|
+
this.name = "UnsupportedFeatureError";
|
|
6090
|
+
}
|
|
6091
|
+
feature;
|
|
6092
|
+
adapter;
|
|
6093
|
+
element;
|
|
6094
|
+
};
|
|
5960
6095
|
function resolveBehavior(feature, config) {
|
|
5961
6096
|
if (typeof config === "string") return config;
|
|
5962
6097
|
return config.overrides?.[feature] ?? config.default;
|
|
@@ -7004,10 +7139,9 @@ function stream(builder, options) {
|
|
|
7004
7139
|
};
|
|
7005
7140
|
let hookIntent = rawIntent;
|
|
7006
7141
|
try {
|
|
7007
|
-
const afterHookCtx = await
|
|
7008
|
-
hookStore
|
|
7009
|
-
ctx,
|
|
7010
|
-
onHookError
|
|
7142
|
+
const afterHookCtx = await withReentrancyGuard(
|
|
7143
|
+
hookStore,
|
|
7144
|
+
(s) => runBeforeQueryHooks(s.beforeQuery, ctx, onHookError)
|
|
7011
7145
|
);
|
|
7012
7146
|
hookIntent = afterHookCtx.intent;
|
|
7013
7147
|
} catch (error) {
|
|
@@ -7210,27 +7344,25 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7210
7344
|
}
|
|
7211
7345
|
count(fieldOrOptions, as) {
|
|
7212
7346
|
const builder = this.clone();
|
|
7213
|
-
|
|
7347
|
+
let field;
|
|
7348
|
+
let alias = as;
|
|
7349
|
+
let distinct2;
|
|
7214
7350
|
if (fieldOrOptions === void 0) {
|
|
7215
7351
|
} else if (typeof fieldOrOptions === "string") {
|
|
7216
|
-
|
|
7217
|
-
if (as !== void 0) {
|
|
7218
|
-
agg.as = as;
|
|
7219
|
-
}
|
|
7352
|
+
field = fieldOrOptions;
|
|
7220
7353
|
} else if (isDistinctField(fieldOrOptions)) {
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
if (as !== void 0) {
|
|
7224
|
-
agg.as = as;
|
|
7225
|
-
}
|
|
7354
|
+
field = fieldOrOptions.field;
|
|
7355
|
+
distinct2 = true;
|
|
7226
7356
|
} else {
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
}
|
|
7233
|
-
|
|
7357
|
+
field = fieldOrOptions.field;
|
|
7358
|
+
alias = fieldOrOptions.as ?? as;
|
|
7359
|
+
}
|
|
7360
|
+
const agg = {
|
|
7361
|
+
function: "count",
|
|
7362
|
+
...field !== void 0 && { field },
|
|
7363
|
+
...alias !== void 0 && { as: alias },
|
|
7364
|
+
...distinct2 !== void 0 && { distinct: distinct2 }
|
|
7365
|
+
};
|
|
7234
7366
|
builder.aggregates.push(agg);
|
|
7235
7367
|
return builder;
|
|
7236
7368
|
}
|
|
@@ -7238,13 +7370,12 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7238
7370
|
const builder = this.clone();
|
|
7239
7371
|
const isDistinct = isDistinctField(field);
|
|
7240
7372
|
const fieldName = isDistinct ? field.field : field;
|
|
7241
|
-
const agg = {
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
}
|
|
7373
|
+
const agg = {
|
|
7374
|
+
function: "sum",
|
|
7375
|
+
field: fieldName,
|
|
7376
|
+
...isDistinct && { distinct: true },
|
|
7377
|
+
...as !== void 0 && { as }
|
|
7378
|
+
};
|
|
7248
7379
|
builder.aggregates.push(agg);
|
|
7249
7380
|
return builder;
|
|
7250
7381
|
}
|
|
@@ -7252,31 +7383,32 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7252
7383
|
const builder = this.clone();
|
|
7253
7384
|
const isDistinct = isDistinctField(field);
|
|
7254
7385
|
const fieldName = isDistinct ? field.field : field;
|
|
7255
|
-
const agg = {
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
}
|
|
7386
|
+
const agg = {
|
|
7387
|
+
function: "avg",
|
|
7388
|
+
field: fieldName,
|
|
7389
|
+
...isDistinct && { distinct: true },
|
|
7390
|
+
...as !== void 0 && { as }
|
|
7391
|
+
};
|
|
7262
7392
|
builder.aggregates.push(agg);
|
|
7263
7393
|
return builder;
|
|
7264
7394
|
}
|
|
7265
7395
|
min(field, as) {
|
|
7266
7396
|
const builder = this.clone();
|
|
7267
|
-
const agg = {
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7397
|
+
const agg = {
|
|
7398
|
+
function: "min",
|
|
7399
|
+
field,
|
|
7400
|
+
...as !== void 0 && { as }
|
|
7401
|
+
};
|
|
7271
7402
|
builder.aggregates.push(agg);
|
|
7272
7403
|
return builder;
|
|
7273
7404
|
}
|
|
7274
7405
|
max(field, as) {
|
|
7275
7406
|
const builder = this.clone();
|
|
7276
|
-
const agg = {
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7407
|
+
const agg = {
|
|
7408
|
+
function: "max",
|
|
7409
|
+
field,
|
|
7410
|
+
...as !== void 0 && { as }
|
|
7411
|
+
};
|
|
7280
7412
|
builder.aggregates.push(agg);
|
|
7281
7413
|
return builder;
|
|
7282
7414
|
}
|
|
@@ -7465,6 +7597,13 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7465
7597
|
"join(batchValuesRef): an `on` condition is required for BatchValues joins"
|
|
7466
7598
|
);
|
|
7467
7599
|
}
|
|
7600
|
+
validateIdentifier(bv.alias, "alias");
|
|
7601
|
+
for (const col2 of bv.columns) {
|
|
7602
|
+
validateIdentifier(col2, "column");
|
|
7603
|
+
}
|
|
7604
|
+
if (opts.as !== void 0) {
|
|
7605
|
+
validateIdentifier(opts.as, "alias");
|
|
7606
|
+
}
|
|
7468
7607
|
const joinIntent = {
|
|
7469
7608
|
batchValues: {
|
|
7470
7609
|
data: bv.data,
|
|
@@ -7480,6 +7619,9 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
7480
7619
|
builder.joinIntents.push(joinIntent);
|
|
7481
7620
|
} else {
|
|
7482
7621
|
validateIdentifier(relationOrTableOrBatch, "table");
|
|
7622
|
+
if (opts?.as !== void 0) {
|
|
7623
|
+
validateIdentifier(opts.as, "alias");
|
|
7624
|
+
}
|
|
7483
7625
|
const joinIntent = opts?.on ? {
|
|
7484
7626
|
table: relationOrTableOrBatch,
|
|
7485
7627
|
on: opts.on,
|
|
@@ -8281,7 +8423,9 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
|
|
|
8281
8423
|
const builder = new _QueryBuilderImpl(
|
|
8282
8424
|
{ ...this.ctx, ...ctxOverride },
|
|
8283
8425
|
this.from,
|
|
8284
|
-
{
|
|
8426
|
+
{
|
|
8427
|
+
...this.relationHints
|
|
8428
|
+
}
|
|
8285
8429
|
);
|
|
8286
8430
|
builder.includes.push(...this.includes);
|
|
8287
8431
|
builder.recursiveIncludes.push(...this.recursiveIncludes);
|
|
@@ -9255,6 +9399,7 @@ function rangeOverlaps(fieldOrColumn, valueOrRange, rangeType = "daterange") {
|
|
|
9255
9399
|
kind: "range",
|
|
9256
9400
|
field: fieldOrColumn,
|
|
9257
9401
|
operator: "overlaps",
|
|
9402
|
+
// Cast: non-array branch is RangeOperand (tuple path already returned above)
|
|
9258
9403
|
value: valueOrRange
|
|
9259
9404
|
};
|
|
9260
9405
|
}
|
|
@@ -10064,7 +10209,8 @@ var FEATURE_TO_FLAG = {
|
|
|
10064
10209
|
indexOpclass: "supportsDDLIndexOpclass",
|
|
10065
10210
|
indexInclude: "supportsDDLIndexInclude",
|
|
10066
10211
|
partialIndex: "supportsDDLPartialIndexes",
|
|
10067
|
-
expressionIndex: "supportsDDLExpressionIndexes"
|
|
10212
|
+
expressionIndex: "supportsDDLExpressionIndexes",
|
|
10213
|
+
rowLevelSecurity: "supportsDDLRowLevelSecurity"
|
|
10068
10214
|
};
|
|
10069
10215
|
function createDialectCapabilities(overrides, options) {
|
|
10070
10216
|
const result = {
|
|
@@ -11278,6 +11424,7 @@ export {
|
|
|
11278
11424
|
UnknownDialectError,
|
|
11279
11425
|
UnsafeOperationError,
|
|
11280
11426
|
UnsupportedCapabilityError,
|
|
11427
|
+
UnsupportedFeatureError,
|
|
11281
11428
|
UnsupportedStrategyError,
|
|
11282
11429
|
UpdateBuilder,
|
|
11283
11430
|
UpsertBuilder,
|