turbine-orm 0.90.2 → 0.91.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/README.md +3 -3
- package/dist/cjs/dialect.d.ts +26 -1
- package/dist/cjs/dialect.js +4 -0
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/mssql.js +2 -0
- package/dist/cjs/mysql.js +3 -0
- package/dist/cjs/powdb.js +2 -0
- package/dist/cjs/powql.d.ts +5 -2
- package/dist/cjs/powql.js +49 -28
- package/dist/cjs/prisma-compat.d.ts +2 -2
- package/dist/cjs/prisma-compat.js +8 -5
- package/dist/cjs/query/aggregates.js +74 -4
- package/dist/cjs/query/builder.d.ts +20 -7
- package/dist/cjs/query/builder.js +31 -22
- package/dist/cjs/query/expr.d.ts +47 -0
- package/dist/cjs/query/expr.js +129 -3
- package/dist/cjs/query/index.d.ts +1 -1
- package/dist/cjs/query/option-surface.js +3 -0
- package/dist/cjs/query/types.d.ts +107 -7
- package/dist/cjs/query/writes.d.ts +14 -2
- package/dist/cjs/query/writes.js +65 -18
- package/dist/cjs/sqlite.js +6 -0
- package/dist/dialect.d.ts +26 -1
- package/dist/dialect.js +4 -0
- package/dist/index.d.ts +2 -2
- package/dist/mssql.js +2 -0
- package/dist/mysql.js +3 -0
- package/dist/powdb.js +2 -0
- package/dist/powql.d.ts +5 -2
- package/dist/powql.js +49 -28
- package/dist/prisma-compat.d.ts +2 -2
- package/dist/prisma-compat.js +8 -5
- package/dist/query/aggregates.js +74 -4
- package/dist/query/builder.d.ts +20 -7
- package/dist/query/builder.js +31 -22
- package/dist/query/expr.d.ts +47 -0
- package/dist/query/expr.js +128 -3
- package/dist/query/index.d.ts +1 -1
- package/dist/query/option-surface.js +3 -0
- package/dist/query/types.d.ts +107 -7
- package/dist/query/writes.d.ts +14 -2
- package/dist/query/writes.js +63 -18
- package/dist/sqlite.js +6 -0
- package/package.json +1 -1
- package/skills/turbine-orm/SKILL.md +8 -0
package/dist/query/aggregates.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { UnsupportedFeatureError, ValidationError } from '../errors.js';
|
|
12
12
|
import { snakeToCamel } from '../schema.js';
|
|
13
|
+
import { compileReadExpr } from './expr.js';
|
|
13
14
|
import { assertEqualityOperand, dedupeColumnList, isJsonPathOrderBy, isUnmatchedPlainObject, isVectorOrderBy, isWhereOperator, normalizeOrderBy, orderByEntries, setOwn, } from './filters.js';
|
|
14
15
|
import { assertOrderDirection, resolveSkipGlobalFilters, resolveUnsafeFlag } from './types.js';
|
|
15
16
|
import { isTemporalInfinity, ownLookup, parseDbDate, resolveColumnName, suggestKey, warnRedundantSortTerm, } from './utils.js';
|
|
@@ -281,7 +282,7 @@ export function buildGroupBy(qi, args) {
|
|
|
281
282
|
buildAggregates('_avg', 'AVG', args._avg);
|
|
282
283
|
buildAggregates('_min', 'MIN', args._min);
|
|
283
284
|
buildAggregates('_max', 'MAX', args._max);
|
|
284
|
-
const percentiles = planPercentiles(qi, args._percentile, params, includePii, 'groupBy', claimResultKey);
|
|
285
|
+
const percentiles = planPercentiles(qi, args._percentile, params, includePii, 'groupBy', claimResultKey, (key) => key === '_count' || usedResultKeys.has(key));
|
|
285
286
|
selectExprs.push(...percentiles.map((plan) => plan.expr));
|
|
286
287
|
let sql = `SELECT ${selectExprs.join(', ')} FROM ${fromSql} GROUP BY ${groupExprs.join(', ')}`;
|
|
287
288
|
// HAVING, filter whole groups by their aggregate values.
|
|
@@ -1026,7 +1027,7 @@ const PERCENTILE_HINT = {
|
|
|
1026
1027
|
mssql: 'Percentile aggregates are not available on SQL Server in this release: PERCENTILE_CONT is a window ' +
|
|
1027
1028
|
'function there, not an aggregate.',
|
|
1028
1029
|
};
|
|
1029
|
-
const PERCENTILE_SPEC_KEYS = ['p', 'method'];
|
|
1030
|
+
const PERCENTILE_SPEC_KEYS = ['p', 'method', 'of'];
|
|
1030
1031
|
const PERCENTILE_METHODS = ['cont', 'disc'];
|
|
1031
1032
|
const PERCENTILE_NOT_ORDERABLE = 'ordering or filtering groups by _percentile is not supported yet.';
|
|
1032
1033
|
/**
|
|
@@ -1041,8 +1042,14 @@ const PERCENTILE_NOT_ORDERABLE = 'ordering or filtering groups by _percentile is
|
|
|
1041
1042
|
* form), a JSON-path target, and two keys naming one column. Both methods need
|
|
1042
1043
|
* `includePii: UNSAFE` on a PII column: `disc` returns a stored cell, and so
|
|
1043
1044
|
* does `cont` at p = 0 or 1 or over a one-row group.
|
|
1045
|
+
*
|
|
1046
|
+
* A key with `of` names an expression's result (see `PercentileExprSpec`): it
|
|
1047
|
+
* must not name a field, the expression compiles through `compileReadExpr`,
|
|
1048
|
+
* it is ordered as `float8`, and its SQL alias is `_percentile_of_<n>`, so the
|
|
1049
|
+
* caller's name never reaches the SQL. Every PII column it reads as a value
|
|
1050
|
+
* needs `includePii: UNSAFE`, as a column percentile does.
|
|
1044
1051
|
*/
|
|
1045
|
-
function planPercentiles(qi, spec, params, includePii, operation, claimResultKey) {
|
|
1052
|
+
function planPercentiles(qi, spec, params, includePii, operation, claimResultKey, resultKeyTaken) {
|
|
1046
1053
|
if (spec === undefined)
|
|
1047
1054
|
return [];
|
|
1048
1055
|
const where = `${operation} _percentile on table "${qi.table}"`;
|
|
@@ -1058,6 +1065,16 @@ function planPercentiles(qi, spec, params, includePii, operation, claimResultKey
|
|
|
1058
1065
|
const meta = qi.schema.tables[qi.table];
|
|
1059
1066
|
const plans = [];
|
|
1060
1067
|
const aliases = new Set();
|
|
1068
|
+
// Column aliases are `_percentile_<column>`; an expression's is the first
|
|
1069
|
+
// free `_percentile_of_<n>`, chosen after every column alias is known so a
|
|
1070
|
+
// column named `of_0` keeps its own.
|
|
1071
|
+
const columnAliases = new Set();
|
|
1072
|
+
for (const [key] of entries) {
|
|
1073
|
+
const column = resolveColumnName(qi.tableMeta, key);
|
|
1074
|
+
if (column !== undefined)
|
|
1075
|
+
columnAliases.add(`_percentile_${column}`);
|
|
1076
|
+
}
|
|
1077
|
+
let exprCount = 0;
|
|
1061
1078
|
for (const [key, entry] of entries) {
|
|
1062
1079
|
const label = `${where}, field "${key}"`;
|
|
1063
1080
|
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
|
@@ -1072,7 +1089,7 @@ function planPercentiles(qi, spec, params, includePii, operation, claimResultKey
|
|
|
1072
1089
|
throw new ValidationError(`${label}: unknown key "${k}".${hint ? ` Did you mean "${hint}"?` : ''} Expected { p, method? }.`);
|
|
1073
1090
|
}
|
|
1074
1091
|
}
|
|
1075
|
-
const { p, method = 'cont' } = entry;
|
|
1092
|
+
const { p, method = 'cont', of } = entry;
|
|
1076
1093
|
if (!PERCENTILE_METHODS.includes(method)) {
|
|
1077
1094
|
throw new ValidationError(`${label}: method must be 'cont' or 'disc', received ${describeValue(method)}.`);
|
|
1078
1095
|
}
|
|
@@ -1085,6 +1102,10 @@ function planPercentiles(qi, spec, params, includePii, operation, claimResultKey
|
|
|
1085
1102
|
else {
|
|
1086
1103
|
assertFraction(p, label);
|
|
1087
1104
|
}
|
|
1105
|
+
if (of !== undefined) {
|
|
1106
|
+
plans.push(planExpressionPercentile(qi, key, of, p, method, params, includePii, operation, label, aliases, columnAliases, exprCount++, claimResultKey, resultKeyTaken));
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1088
1109
|
const col = qi.toColumn(key);
|
|
1089
1110
|
if (method === 'cont' && qi.tableMeta.dateColumns.has(col)) {
|
|
1090
1111
|
throw new ValidationError(`${label}: method 'cont' interpolates and needs a numeric or interval column, and "${col}" is a ` +
|
|
@@ -1114,6 +1135,51 @@ function planPercentiles(qi, spec, params, includePii, operation, claimResultKey
|
|
|
1114
1135
|
}
|
|
1115
1136
|
return plans;
|
|
1116
1137
|
}
|
|
1138
|
+
/** One `_percentile` entry over an expression (`of`); see {@link planPercentiles}. */
|
|
1139
|
+
function planExpressionPercentile(qi, key, of, p, method, params, includePii, operation, label, aliases, columnAliases, index, claimResultKey, resultKeyTaken) {
|
|
1140
|
+
const column = resolveColumnName(qi.tableMeta, key);
|
|
1141
|
+
if (column !== undefined) {
|
|
1142
|
+
throw new ValidationError(`${label}: "${key}" is a field of the table, and \`of\` names an expression. Give the expression a name ` +
|
|
1143
|
+
`that is not a field (the name is only its result's key), or drop \`of\` for the percentile of "${key}".`);
|
|
1144
|
+
}
|
|
1145
|
+
const compiled = compileReadExpr({
|
|
1146
|
+
qi,
|
|
1147
|
+
name: key,
|
|
1148
|
+
operation: `${operation} _percentile`,
|
|
1149
|
+
bind: (value, cls) => {
|
|
1150
|
+
params.push(value);
|
|
1151
|
+
return `${qi.p(params.length)}::${cls === 'int' ? 'int8' : 'numeric'}`;
|
|
1152
|
+
},
|
|
1153
|
+
predicate: (when) => {
|
|
1154
|
+
const sql = whereMod.buildWhereClause(qi, when, params);
|
|
1155
|
+
if (sql === null)
|
|
1156
|
+
throw new ValidationError(`${label}: an expr.case() arm's when compiles to no predicate.`);
|
|
1157
|
+
return sql;
|
|
1158
|
+
},
|
|
1159
|
+
}, of);
|
|
1160
|
+
const meta = qi.schema.tables[qi.table];
|
|
1161
|
+
for (const col of compiled.values) {
|
|
1162
|
+
const field = qi.tableMeta.reverseColumnMap[col] ?? snakeToCamel(col);
|
|
1163
|
+
assertAggregatePiiOptIn(qi.table, meta, field, col, `${operation} _percentile "${key}"`, includePii);
|
|
1164
|
+
}
|
|
1165
|
+
// The caller's key never names the alias, so it skips every name already
|
|
1166
|
+
// taken, a groupBy key included, rather than collide with one.
|
|
1167
|
+
let n = index;
|
|
1168
|
+
const taken = (alias) => aliases.has(alias) || columnAliases.has(alias) || resultKeyTaken?.(alias) === true;
|
|
1169
|
+
while (taken(`_percentile_of_${n}`))
|
|
1170
|
+
n++;
|
|
1171
|
+
const alias = `_percentile_of_${n}`;
|
|
1172
|
+
aliases.add(alias);
|
|
1173
|
+
claimResultKey?.(alias, `_percentile "${key}"`);
|
|
1174
|
+
params.push(Array.isArray(p) ? [...p] : p);
|
|
1175
|
+
const fractions = `${qi.p(params.length)}::float8${Array.isArray(p) ? '[]' : ''}`;
|
|
1176
|
+
return {
|
|
1177
|
+
expr: `percentile_${method}(${fractions}) WITHIN GROUP (ORDER BY (${compiled.sql})::float8) AS ${qi.q(alias)}`,
|
|
1178
|
+
alias,
|
|
1179
|
+
field: key,
|
|
1180
|
+
col: null,
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1117
1183
|
function assertFraction(value, label) {
|
|
1118
1184
|
if (typeof value === 'number' && Number.isFinite(value) && value >= 0 && value <= 1)
|
|
1119
1185
|
return;
|
|
@@ -1155,6 +1221,10 @@ function readPercentiles(qi, plans, row) {
|
|
|
1155
1221
|
function percentileElement(qi, col, value) {
|
|
1156
1222
|
if (value === null || value === undefined)
|
|
1157
1223
|
return null;
|
|
1224
|
+
// An expression is ordered as float8, which the driver reads as a number
|
|
1225
|
+
// (a supplied pool's own parser may hand back text).
|
|
1226
|
+
if (col === null)
|
|
1227
|
+
return typeof value === 'string' ? Number(value) : value;
|
|
1158
1228
|
if (typeof value === 'string' && isInt8Type(whereMod.getColumnPgType(qi, col))) {
|
|
1159
1229
|
const n = Number(value);
|
|
1160
1230
|
return Number.isSafeInteger(n) ? n : value;
|
package/dist/query/builder.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { PgCompatPool } from '../pg-types.js';
|
|
14
14
|
import type { SchemaMetadata } from '../schema.js';
|
|
15
|
-
import type { AggregateArgs, AggregateResultOf, CountArgs, CreateArgs, CreateManyArgs, DefaultInputTypes, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, GroupByResult, QueryResult, TableInputTypes, TypedWithClause, UpdateArgs, UpdateManyArgs, UpsertArgs, WithClause } from './types.js';
|
|
15
|
+
import type { AggregateArgs, AggregateResultOf, CountArgs, CreateArgs, CreateManyArgs, CreateManyResult, DefaultInputTypes, DeleteArgs, DeleteManyArgs, FieldPercentiles, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GroupByArgs, GroupByResult, PercentileKeysChecked, QueryResult, TableInputTypes, TypedWithClause, UpdateArgs, UpdateManyArgs, UpsertArgs, WithClause } from './types.js';
|
|
16
16
|
/**
|
|
17
17
|
* Marginal cost of keeping a to-one relation on the JOIN plan, per parent row.
|
|
18
18
|
*
|
|
@@ -1180,7 +1180,12 @@ export declare class QueryInterface<T extends object, R extends object = {}, M e
|
|
|
1180
1180
|
* writes.ts because relations.ts imports writes.ts.
|
|
1181
1181
|
*/
|
|
1182
1182
|
private resolveWriteProjection;
|
|
1183
|
-
buildCreateMany<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: CreateManyArgs<T, M['createMany'], S, O>): DeferredQuery<FieldResult<T, S, O
|
|
1183
|
+
buildCreateMany<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined, C extends boolean | undefined = undefined>(args: CreateManyArgs<T, M['createMany'], S, O, C>): DeferredQuery<CreateManyResult<FieldResult<T, S, O>, C>>;
|
|
1184
|
+
/**
|
|
1185
|
+
* `createMany`'s projection: the caller's `select` / `omit`, or
|
|
1186
|
+
* {@link writesMod.COUNT_ONLY} for `returning: false`, which takes neither.
|
|
1187
|
+
*/
|
|
1188
|
+
private createManyProjection;
|
|
1184
1189
|
buildUpdate<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: UpdateArgs<T, R, S, O, M['update']>): DeferredQuery<FieldResult<T, S, O>>;
|
|
1185
1190
|
buildDelete<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: DeleteArgs<T, R, S, O>): DeferredQuery<FieldResult<T, S, O>>;
|
|
1186
1191
|
buildUpsert<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: UpsertArgs<T, R, S, O, M['whereUnique'], M['createMany'], M['updateMany']>): DeferredQuery<FieldResult<T, S, O>>;
|
|
@@ -1416,7 +1421,7 @@ export declare class QueryInterface<T extends object, R extends object = {}, M e
|
|
|
1416
1421
|
findUniqueOrThrow<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: FindUniqueArgs<T, R, W, S, O, M['whereUnique']>): Promise<QueryResult<T, R, W, S, O>>;
|
|
1417
1422
|
buildFindUniqueOrThrow<W extends TypedWithClause<R> = {}, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: FindUniqueArgs<T, R, W, S, O, M['whereUnique']>): DeferredQuery<QueryResult<T, R, W, S, O>>;
|
|
1418
1423
|
create<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: CreateArgs<T, R, S, O, M['create']>): Promise<FieldResult<T, S, O>>;
|
|
1419
|
-
createMany<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: CreateManyArgs<T, M['createMany'], S, O>): Promise<FieldResult<T, S, O
|
|
1424
|
+
createMany<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined, C extends boolean | undefined = undefined>(args: CreateManyArgs<T, M['createMany'], S, O, C>): Promise<CreateManyResult<FieldResult<T, S, O>, C>>;
|
|
1420
1425
|
update<S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined>(args: UpdateArgs<T, R, S, O, M['update']>): Promise<FieldResult<T, S, O>>;
|
|
1421
1426
|
private nestedCreate;
|
|
1422
1427
|
private nestedUpdate;
|
|
@@ -1477,10 +1482,18 @@ export declare class QueryInterface<T extends object, R extends object = {}, M e
|
|
|
1477
1482
|
* {@link GroupByResult}). Grouping by a JSON-path key yields a runtime alias
|
|
1478
1483
|
* that cannot be typed, so those columns are not projected onto the row type.
|
|
1479
1484
|
*/
|
|
1480
|
-
groupBy<A extends GroupByArgs<T, R>>(args: A): Promise<GroupByResult<T, A>[]>;
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1485
|
+
groupBy<A extends GroupByArgs<T, R>>(args: A & PercentileKeysChecked<T, A>): Promise<GroupByResult<T, A>[]>;
|
|
1486
|
+
/** Args of a generic type: field percentiles only (see {@link FieldPercentiles}). */
|
|
1487
|
+
groupBy<A extends Omit<GroupByArgs<T, R>, '_percentile'> & FieldPercentiles<T>>(args: A): Promise<GroupByResult<T, A>[]>;
|
|
1488
|
+
buildGroupBy<A extends GroupByArgs<T, R>>(args: A & PercentileKeysChecked<T, A>): DeferredQuery<GroupByResult<T, A>[]>;
|
|
1489
|
+
/** Args of a generic type: field percentiles only (see {@link FieldPercentiles}). */
|
|
1490
|
+
buildGroupBy<A extends Omit<GroupByArgs<T, R>, '_percentile'> & FieldPercentiles<T>>(args: A): DeferredQuery<GroupByResult<T, A>[]>;
|
|
1491
|
+
buildAggregate<A extends AggregateArgs<T, R>>(args: A & PercentileKeysChecked<T, A>): DeferredQuery<AggregateResultOf<T, A>>;
|
|
1492
|
+
/** Args of a generic type: field percentiles only (see {@link FieldPercentiles}). */
|
|
1493
|
+
buildAggregate<A extends Omit<AggregateArgs<T, R>, '_percentile'> & FieldPercentiles<T>>(args: A): DeferredQuery<AggregateResultOf<T, A>>;
|
|
1494
|
+
aggregate<A extends AggregateArgs<T, R>>(args: A & PercentileKeysChecked<T, A>): Promise<AggregateResultOf<T, A>>;
|
|
1495
|
+
/** Args of a generic type: field percentiles only (see {@link FieldPercentiles}). */
|
|
1496
|
+
aggregate<A extends Omit<AggregateArgs<T, R>, '_percentile'> & FieldPercentiles<T>>(args: A): Promise<AggregateResultOf<T, A>>;
|
|
1484
1497
|
private resolveColumns;
|
|
1485
1498
|
withFingerprint(withClause: WithClause | undefined, table?: string, depth?: number): string;
|
|
1486
1499
|
private collectWithParams;
|
package/dist/query/builder.js
CHANGED
|
@@ -3010,7 +3010,24 @@ export class QueryInterface {
|
|
|
3010
3010
|
}
|
|
3011
3011
|
buildCreateMany(args) {
|
|
3012
3012
|
args = this.normalizeArgs(args);
|
|
3013
|
-
return writesMod.buildCreateMany(this.ctx, args, 'refuse', this.
|
|
3013
|
+
return writesMod.buildCreateMany(this.ctx, args, 'refuse', this.createManyProjection(args));
|
|
3014
|
+
}
|
|
3015
|
+
/**
|
|
3016
|
+
* `createMany`'s projection: the caller's `select` / `omit`, or
|
|
3017
|
+
* {@link writesMod.COUNT_ONLY} for `returning: false`, which takes neither.
|
|
3018
|
+
*/
|
|
3019
|
+
createManyProjection(args) {
|
|
3020
|
+
const { returning } = args;
|
|
3021
|
+
if (returning === undefined || returning === true)
|
|
3022
|
+
return this.resolveWriteProjection(args);
|
|
3023
|
+
if (returning !== false) {
|
|
3024
|
+
throw new ValidationError(`createMany on "${this.table}": returning must be true or false, received ${typeof returning}.`);
|
|
3025
|
+
}
|
|
3026
|
+
if (args.select !== undefined || args.omit !== undefined) {
|
|
3027
|
+
throw new ValidationError(`createMany on "${this.table}": returning: false returns { count } and no rows, so it takes no ` +
|
|
3028
|
+
`\`${args.select !== undefined ? 'select' : 'omit'}\`. Drop one of them.`);
|
|
3029
|
+
}
|
|
3030
|
+
return writesMod.COUNT_ONLY;
|
|
3014
3031
|
}
|
|
3015
3032
|
buildUpdate(args) {
|
|
3016
3033
|
args = this.normalizeArgs(args);
|
|
@@ -4403,13 +4420,17 @@ export class QueryInterface {
|
|
|
4403
4420
|
async createMany(args) {
|
|
4404
4421
|
args = this.normalizeArgs(args);
|
|
4405
4422
|
return this.executeWithMiddleware('createMany', args, async () => {
|
|
4406
|
-
const projection = this.
|
|
4423
|
+
const projection = this.createManyProjection(args);
|
|
4424
|
+
const countOnly = writesMod.isCountOnly(projection);
|
|
4407
4425
|
const base = args;
|
|
4408
4426
|
// One statement when the rows fit the engine's per-statement limits,
|
|
4409
4427
|
// else chunks that do, all in one transaction so the rows land together
|
|
4410
4428
|
// or not at all. (`buildCreateMany`, one statement for a pipeline,
|
|
4411
4429
|
// refuses instead.)
|
|
4412
|
-
|
|
4430
|
+
// Splitting a call that fits one statement needs a transaction, which a
|
|
4431
|
+
// pool without connect() cannot open outside one.
|
|
4432
|
+
const canSplit = this.txScoped || typeof this.pool.connect === 'function';
|
|
4433
|
+
const { chunks, rowsPerChunk } = writesMod.planCreateMany(this.ctx, base, projection, canSplit);
|
|
4413
4434
|
const run = async () => {
|
|
4414
4435
|
// An engine that applies two rows whose onConflict keys are equal only
|
|
4415
4436
|
// under the column's collation (SQLite's NOCASE) as an insert and an
|
|
@@ -4425,13 +4446,18 @@ export class QueryInterface {
|
|
|
4425
4446
|
const reselects = this.dialect.resultStrategy === 'reselect';
|
|
4426
4447
|
return this.inOneTransaction(async (exec) => {
|
|
4427
4448
|
const rows = [];
|
|
4449
|
+
let count = 0;
|
|
4428
4450
|
for (const chunk of chunks) {
|
|
4429
4451
|
const result = reselects && chunk.reselect
|
|
4430
4452
|
? await chunk.reselect(exec)
|
|
4431
4453
|
: await exec(chunk.sql, chunk.params, chunk.preparedName);
|
|
4432
|
-
|
|
4454
|
+
const out = chunk.transform(result);
|
|
4455
|
+
if (countOnly)
|
|
4456
|
+
count += out.count;
|
|
4457
|
+
else
|
|
4458
|
+
rows.push(...out);
|
|
4433
4459
|
}
|
|
4434
|
-
return rows;
|
|
4460
|
+
return (countOnly ? { count } : rows);
|
|
4435
4461
|
}, args.timeout, `createMany of ${base.data.length} rows`, `Insert at most ${rowsPerChunk} rows per call, which fits one statement, or pass a pool that can check ` +
|
|
4436
4462
|
'out a connection.');
|
|
4437
4463
|
};
|
|
@@ -4721,17 +4747,6 @@ export class QueryInterface {
|
|
|
4721
4747
|
preparedName: entry.name,
|
|
4722
4748
|
};
|
|
4723
4749
|
}
|
|
4724
|
-
// -------------------------------------------------------------------------
|
|
4725
|
-
// groupBy (with aggregate functions)
|
|
4726
|
-
// -------------------------------------------------------------------------
|
|
4727
|
-
/**
|
|
4728
|
-
* Group rows and compute per-group aggregates (Prisma-style). The result row
|
|
4729
|
-
* type is INFERRED from the args: each `by` field carries its entity field
|
|
4730
|
-
* type, `_count` is always a number, and each requested `_sum` / `_avg` /
|
|
4731
|
-
* `_min` / `_max` block maps its fields to properly typed values (see
|
|
4732
|
-
* {@link GroupByResult}). Grouping by a JSON-path key yields a runtime alias
|
|
4733
|
-
* that cannot be typed, so those columns are not projected onto the row type.
|
|
4734
|
-
*/
|
|
4735
4750
|
async groupBy(args) {
|
|
4736
4751
|
args = this.normalizeArgs(args);
|
|
4737
4752
|
return this.executeWithMiddleware('groupBy', args, async () => {
|
|
@@ -4740,9 +4755,6 @@ export class QueryInterface {
|
|
|
4740
4755
|
return deferred.transform(result);
|
|
4741
4756
|
});
|
|
4742
4757
|
}
|
|
4743
|
-
// ---------------------------------------------------------------------------
|
|
4744
|
-
// Aggregate / groupBy compilation (extracted to aggregates.ts).
|
|
4745
|
-
// ---------------------------------------------------------------------------
|
|
4746
4758
|
buildGroupBy(args) {
|
|
4747
4759
|
const normalized = this.normalizeArgs(args);
|
|
4748
4760
|
return this.withPreparedName(() => aggMod.buildGroupBy(this.ctx, normalized));
|
|
@@ -4751,9 +4763,6 @@ export class QueryInterface {
|
|
|
4751
4763
|
const normalized = this.normalizeArgs(args);
|
|
4752
4764
|
return this.withPreparedName(() => aggMod.buildAggregate(this.ctx, normalized));
|
|
4753
4765
|
}
|
|
4754
|
-
// -------------------------------------------------------------------------
|
|
4755
|
-
// aggregate, standalone aggregation without groupBy
|
|
4756
|
-
// -------------------------------------------------------------------------
|
|
4757
4766
|
async aggregate(args) {
|
|
4758
4767
|
args = this.normalizeArgs(args);
|
|
4759
4768
|
return this.executeWithMiddleware('aggregate', args, async () => {
|
package/dist/query/expr.d.ts
CHANGED
|
@@ -124,6 +124,19 @@ export declare const expr: Readonly<{
|
|
|
124
124
|
* value). Refused anywhere else.
|
|
125
125
|
*/
|
|
126
126
|
excluded<F extends string>(field: F): ExcludedRef<F>;
|
|
127
|
+
/**
|
|
128
|
+
* The seconds from `from` to `to`, two timestamp columns of the row:
|
|
129
|
+
* `to - from`, fractional, negative when `to` is earlier, NULL when either is.
|
|
130
|
+
* Both columns must be timestamptz, or both zone-less timestamp. Accepted in
|
|
131
|
+
* `_percentile`'s `of` (PostgreSQL); refused in update data in this release.
|
|
132
|
+
*
|
|
133
|
+
* ```ts
|
|
134
|
+
* await db.jobs.aggregate({
|
|
135
|
+
* _percentile: { wait: { of: expr.secondsBetween(col('requestedAt'), col('readyAt')), p: [0.5, 0.95] } },
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
secondsBetween(from: ColumnRef, to: ColumnRef): SqlExpr<number>;
|
|
127
140
|
}>;
|
|
128
141
|
/** True only for a node built by {@link expr} (any loaded copy of the package). */
|
|
129
142
|
export declare function isSqlExpr(value: unknown): value is SqlExpr;
|
|
@@ -167,6 +180,12 @@ export interface ExprCompileEnv {
|
|
|
167
180
|
readonly insertColumns?: ReadonlySet<string> | undefined;
|
|
168
181
|
/** Compile a `case` arm's predicate (already pruned and validated) against the target row. */
|
|
169
182
|
predicate(when: Record<string, unknown>): string;
|
|
183
|
+
/**
|
|
184
|
+
* The expression is READ by a query (`_percentile`'s `of`), not assigned:
|
|
185
|
+
* there is no target column, `targetKey` is the result's name, and
|
|
186
|
+
* `expr.secondsBetween` is accepted while `expr.now` is not.
|
|
187
|
+
*/
|
|
188
|
+
readonly read?: true | undefined;
|
|
170
189
|
}
|
|
171
190
|
/** One compiled expression. */
|
|
172
191
|
export interface CompiledSetExpr {
|
|
@@ -197,6 +216,34 @@ export interface CompiledSetExpr {
|
|
|
197
216
|
* cache key does not capture.
|
|
198
217
|
*/
|
|
199
218
|
export declare function compileSetExpr(env: ExprCompileEnv, value: SqlExpr | ColumnRef): CompiledSetExpr;
|
|
219
|
+
/** What {@link compileReadExpr} needs from its caller (the aggregate module). */
|
|
220
|
+
export interface ReadExprEnv {
|
|
221
|
+
readonly qi: BuilderCtx;
|
|
222
|
+
/** The result's name, for messages. */
|
|
223
|
+
readonly name: string;
|
|
224
|
+
/** For messages: `aggregate _percentile`, `groupBy _percentile`. */
|
|
225
|
+
readonly operation: string;
|
|
226
|
+
/** Bind one number, bigint or null and return its placeholder, cast to its class's type. */
|
|
227
|
+
bind(value: number | bigint | null, cls: 'int' | 'dec'): string;
|
|
228
|
+
/** Compile a `case` arm's predicate (already pruned and validated) against the row. */
|
|
229
|
+
predicate(when: Record<string, unknown>): string;
|
|
230
|
+
}
|
|
231
|
+
/** One compiled read expression. */
|
|
232
|
+
export interface CompiledReadExpr {
|
|
233
|
+
readonly sql: string;
|
|
234
|
+
/** Value-free shape, as {@link CompiledSetExpr.fingerprint}. */
|
|
235
|
+
readonly fingerprint: string;
|
|
236
|
+
/** Columns the expression reads as a value (columns only a case arm's `when` reads are not). */
|
|
237
|
+
readonly values: ReadonlySet<string>;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Compile a numeric expression a query READS (`_percentile`'s `of`): the walk
|
|
241
|
+
* {@link compileSetExpr} runs, with no target column. Bound values must be
|
|
242
|
+
* numbers or bigints and are cast by their own class, the result must be
|
|
243
|
+
* numeric, and `col()` alone is refused because a field key already says it.
|
|
244
|
+
* Not gated on `updateExpressions`: the caller's own engine check applies.
|
|
245
|
+
*/
|
|
246
|
+
export declare function compileReadExpr(read: ReadExprEnv, value: unknown): CompiledReadExpr;
|
|
200
247
|
/**
|
|
201
248
|
* Rewrite every `expr.excluded(f)` in a single-row upsert's `update` to the
|
|
202
249
|
* value `create` supplies for `f`. The incoming row of a one-row upsert IS
|
package/dist/query/expr.js
CHANGED
|
@@ -44,6 +44,7 @@ const EXPR_KINDS = new Set([
|
|
|
44
44
|
'case',
|
|
45
45
|
'now',
|
|
46
46
|
'excluded',
|
|
47
|
+
'secondsBetween',
|
|
47
48
|
]);
|
|
48
49
|
/** Operand count bounds for coalesce / greatest / least. */
|
|
49
50
|
const MIN_VARIADIC = 2;
|
|
@@ -161,6 +162,20 @@ function caseNode(arms, otherwise) {
|
|
|
161
162
|
assertOperand('case', arms.length, otherwise);
|
|
162
163
|
return makeNode('case', frozen, otherwise === undefined ? undefined : { else: otherwise });
|
|
163
164
|
}
|
|
165
|
+
function secondsBetweenNode(from, to) {
|
|
166
|
+
[from, to].forEach((operand, i) => {
|
|
167
|
+
if (isColumnRef(operand))
|
|
168
|
+
return;
|
|
169
|
+
const at = `expr.secondsBetween(): operand ${i + 1}`;
|
|
170
|
+
if (isPlainColumnRefShape(operand)) {
|
|
171
|
+
const name = JSON.stringify(operand.col);
|
|
172
|
+
throw new ValidationError(`${at} is a plain { col: ${name} } object, not a column reference. Build one with col(${name}).`);
|
|
173
|
+
}
|
|
174
|
+
throw new ValidationError(`${at} is ${operand === undefined ? 'undefined' : describeOperand(operand)}. Both operands are col() of a ` +
|
|
175
|
+
'timestamp column of the row.');
|
|
176
|
+
});
|
|
177
|
+
return makeNode('secondsBetween', [from, to]);
|
|
178
|
+
}
|
|
164
179
|
/**
|
|
165
180
|
* Builders for SQL expressions in update data. Each returns a frozen, branded
|
|
166
181
|
* node; the SQL is generated per engine when the update compiles.
|
|
@@ -246,6 +261,21 @@ export const expr = Object.freeze({
|
|
|
246
261
|
Object.defineProperty(node, EXCLUDED_REF, { value: true, enumerable: false });
|
|
247
262
|
return Object.freeze(node);
|
|
248
263
|
},
|
|
264
|
+
/**
|
|
265
|
+
* The seconds from `from` to `to`, two timestamp columns of the row:
|
|
266
|
+
* `to - from`, fractional, negative when `to` is earlier, NULL when either is.
|
|
267
|
+
* Both columns must be timestamptz, or both zone-less timestamp. Accepted in
|
|
268
|
+
* `_percentile`'s `of` (PostgreSQL); refused in update data in this release.
|
|
269
|
+
*
|
|
270
|
+
* ```ts
|
|
271
|
+
* await db.jobs.aggregate({
|
|
272
|
+
* _percentile: { wait: { of: expr.secondsBetween(col('requestedAt'), col('readyAt')), p: [0.5, 0.95] } },
|
|
273
|
+
* });
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
secondsBetween(from, to) {
|
|
277
|
+
return secondsBetweenNode(from, to);
|
|
278
|
+
},
|
|
249
279
|
});
|
|
250
280
|
/** True only for a node built by {@link expr} (any loaded copy of the package). */
|
|
251
281
|
export function isSqlExpr(value) {
|
|
@@ -437,6 +467,12 @@ function isBareStructuredCopy(qi, column, value) {
|
|
|
437
467
|
const type = columnType(qi, column);
|
|
438
468
|
return source !== undefined && type !== undefined && columnType(qi, source) === type;
|
|
439
469
|
}
|
|
470
|
+
/** Where an expression sits, for messages. */
|
|
471
|
+
function site(env) {
|
|
472
|
+
return env.read
|
|
473
|
+
? `${env.operation} "${env.targetKey}" on "${env.qi.table}"`
|
|
474
|
+
: `${env.operation} data on "${env.qi.table}.${env.targetKey}"`;
|
|
475
|
+
}
|
|
440
476
|
/**
|
|
441
477
|
* Compile one expression (or `col()` reference) assigned to `env.targetKey`.
|
|
442
478
|
* The single traversal: SQL, params (through `env.bind`, in emission order)
|
|
@@ -457,7 +493,7 @@ export function compileSetExpr(env, value) {
|
|
|
457
493
|
const { qi } = env;
|
|
458
494
|
assertEngine(qi);
|
|
459
495
|
const column = assertExprTarget(qi, env.targetKey, value, env.operation);
|
|
460
|
-
const state = { env, targetType: columnType(qi, column), reads: new Set() };
|
|
496
|
+
const state = { env, targetType: columnType(qi, column), reads: new Set(), values: new Set() };
|
|
461
497
|
const part = walkOperand(state, value, false, 0);
|
|
462
498
|
if (part.cls === 'dec' && numClassOfType(state.targetType) === 'int') {
|
|
463
499
|
const what = isColumnRef(value) ? `col(${JSON.stringify(value.col)})` : `expr.${value.kind}()`;
|
|
@@ -469,6 +505,48 @@ export function compileSetExpr(env, value) {
|
|
|
469
505
|
}
|
|
470
506
|
return { sql: part.sql, fingerprint: part.fp, reads: state.reads };
|
|
471
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Compile a numeric expression a query READS (`_percentile`'s `of`): the walk
|
|
510
|
+
* {@link compileSetExpr} runs, with no target column. Bound values must be
|
|
511
|
+
* numbers or bigints and are cast by their own class, the result must be
|
|
512
|
+
* numeric, and `col()` alone is refused because a field key already says it.
|
|
513
|
+
* Not gated on `updateExpressions`: the caller's own engine check applies.
|
|
514
|
+
*/
|
|
515
|
+
export function compileReadExpr(read, value) {
|
|
516
|
+
const { qi, name, operation } = read;
|
|
517
|
+
const at = `${operation} "${name}" on "${qi.table}"`;
|
|
518
|
+
if (isColumnRef(value)) {
|
|
519
|
+
throw new ValidationError(`${at}: \`of\` is col(${JSON.stringify(value.col)}) alone. Use the field as the key instead: ` +
|
|
520
|
+
`{ ${JSON.stringify(value.col)}: { p } }.`);
|
|
521
|
+
}
|
|
522
|
+
if (!isSqlExpr(value)) {
|
|
523
|
+
throw new ValidationError(`${at}: \`of\` is ${value === undefined ? 'undefined' : describeOperand(value)}. Build it with expr ` +
|
|
524
|
+
"(import { col, expr } from 'turbine-orm'); a plain object is never an expression.");
|
|
525
|
+
}
|
|
526
|
+
const env = {
|
|
527
|
+
qi,
|
|
528
|
+
targetKey: name,
|
|
529
|
+
operation,
|
|
530
|
+
read: true,
|
|
531
|
+
bind: (leaf, arithmetic) => {
|
|
532
|
+
if (leaf !== null && typeof leaf !== 'number' && typeof leaf !== 'bigint') {
|
|
533
|
+
throw new ValidationError(`${at}: a bound value is ${leaf instanceof Date ? 'a Date' : leaf instanceof Uint8Array ? 'a Uint8Array' : `a ${typeof leaf}`}. ` +
|
|
534
|
+
'An expression a query reads binds numbers and bigints only.');
|
|
535
|
+
}
|
|
536
|
+
const integer = (arithmetic ?? numClassOfValue(leaf)) !== 'dec' && (typeof leaf !== 'number' || Number.isSafeInteger(leaf));
|
|
537
|
+
return read.bind(leaf, integer ? 'int' : 'dec');
|
|
538
|
+
},
|
|
539
|
+
column: (column) => qi.q(column),
|
|
540
|
+
predicate: (when) => read.predicate(when),
|
|
541
|
+
};
|
|
542
|
+
const state = { env, targetType: undefined, reads: new Set(), values: new Set() };
|
|
543
|
+
const part = walkOperand(state, value, false, 0);
|
|
544
|
+
if (part.cls === 'other' || part.cls === 'null') {
|
|
545
|
+
throw new ValidationError(`${at}: \`of\` is ${part.cls === 'null' ? 'NULL for every row' : 'not numeric (it reads a non-numeric column)'}. ` +
|
|
546
|
+
'A percentile orders numbers: numeric columns, bound numbers, and expr.secondsBetween() of two timestamps.');
|
|
547
|
+
}
|
|
548
|
+
return { sql: part.sql, fingerprint: part.fp, values: state.values };
|
|
549
|
+
}
|
|
472
550
|
function assertEngine(qi) {
|
|
473
551
|
if (qi.dialect.capabilities.updateExpressions)
|
|
474
552
|
return;
|
|
@@ -481,6 +559,7 @@ function walkOperand(state, value, inArithmetic, depth) {
|
|
|
481
559
|
if (isColumnRef(value)) {
|
|
482
560
|
const column = resolveOperandColumn(state, value.col, 'col', inArithmetic);
|
|
483
561
|
state.reads.add(column);
|
|
562
|
+
state.values.add(column);
|
|
484
563
|
return { sql: state.env.column(column), fp: `c:${column}`, cls: numClassOfType(columnType(state.env.qi, column)) };
|
|
485
564
|
}
|
|
486
565
|
if (isSqlExpr(value))
|
|
@@ -492,7 +571,7 @@ function walkOperand(state, value, inArithmetic, depth) {
|
|
|
492
571
|
// greatest, least or a case arm (`expr.add(expr.coalesce('x', 1), 2)`).
|
|
493
572
|
if (inArithmetic &&
|
|
494
573
|
(typeof value === 'string' || typeof value === 'boolean' || value instanceof Date || value instanceof Uint8Array)) {
|
|
495
|
-
throw new ValidationError(`An arithmetic operand in ${state.env
|
|
574
|
+
throw new ValidationError(`An arithmetic operand in ${site(state.env)} is a ` +
|
|
496
575
|
`${value instanceof Date ? 'Date' : value instanceof Uint8Array ? 'Uint8Array' : typeof value}. ` +
|
|
497
576
|
'Arithmetic takes numbers, bigints, col() of a numeric column, or numeric expressions.');
|
|
498
577
|
}
|
|
@@ -509,7 +588,7 @@ function resolveOperandColumn(state, field, how, inArithmetic) {
|
|
|
509
588
|
const call = how === 'col' ? `col(${JSON.stringify(field)})` : `expr.excluded(${JSON.stringify(field)})`;
|
|
510
589
|
const column = resolveColumnName(qi.tableMeta, field);
|
|
511
590
|
if (column === undefined) {
|
|
512
|
-
throw new ValidationError(`${call} in ${state.env
|
|
591
|
+
throw new ValidationError(`${call} in ${site(state.env)} names no column. ` +
|
|
513
592
|
`Known fields: ${Object.keys(qi.tableMeta.columnMap).join(', ') || '(none)'}.`);
|
|
514
593
|
}
|
|
515
594
|
const cls = columnClass(columnType(qi, column));
|
|
@@ -569,6 +648,9 @@ function walkNode(state, node, inArithmetic, depth) {
|
|
|
569
648
|
case 'case':
|
|
570
649
|
return walkCase(state, node, inArithmetic, depth);
|
|
571
650
|
case 'now': {
|
|
651
|
+
if (env.read) {
|
|
652
|
+
throw new ValidationError(`expr.now() in ${site(env)}: an expression a query reads takes the row's columns and bound numbers only.`);
|
|
653
|
+
}
|
|
572
654
|
if (inArithmetic) {
|
|
573
655
|
throw new ValidationError('expr.now() cannot be an arithmetic operand: date arithmetic is not supported.');
|
|
574
656
|
}
|
|
@@ -592,6 +674,8 @@ function walkNode(state, node, inArithmetic, depth) {
|
|
|
592
674
|
}
|
|
593
675
|
return { sql: env.excluded(column), fp: `x:${column}`, cls: numClassOfType(columnType(qi, column)) };
|
|
594
676
|
}
|
|
677
|
+
case 'secondsBetween':
|
|
678
|
+
return walkSecondsBetween(state, node);
|
|
595
679
|
default:
|
|
596
680
|
throw new ValidationError(`Unknown expression kind "${String(node.kind)}".`);
|
|
597
681
|
}
|
|
@@ -623,6 +707,47 @@ function walkCase(state, node, inArithmetic, depth) {
|
|
|
623
707
|
cls: combineClasses(classes),
|
|
624
708
|
};
|
|
625
709
|
}
|
|
710
|
+
/**
|
|
711
|
+
* `expr.secondsBetween(from, to)`: `EXTRACT(EPOCH FROM (to - from))`, the
|
|
712
|
+
* PostgreSQL spelling. It is read only by `_percentile`, which only an engine
|
|
713
|
+
* with `supportsPercentileAggregates` runs, and that SQL (`percentile_cont ...
|
|
714
|
+
* WITHIN GROUP`) is PostgreSQL's too. Both columns must have one temporal
|
|
715
|
+
* kind: subtracting a timestamptz from a zone-less timestamp converts through
|
|
716
|
+
* the session time zone.
|
|
717
|
+
*/
|
|
718
|
+
function walkSecondsBetween(state, node) {
|
|
719
|
+
const { env } = state;
|
|
720
|
+
const { qi } = env;
|
|
721
|
+
if (!env.read) {
|
|
722
|
+
throw new ValidationError(`expr.secondsBetween() in ${site(env)}: it is accepted in _percentile's \`of\` in this release, not in ` +
|
|
723
|
+
'update data. Compute the value in JavaScript and write it as a plain value.');
|
|
724
|
+
}
|
|
725
|
+
const kinds = [];
|
|
726
|
+
const columns = node.args.map((arg) => {
|
|
727
|
+
const field = arg.col;
|
|
728
|
+
const column = resolveOperandColumn(state, field, 'col', false);
|
|
729
|
+
const type = columnType(qi, column);
|
|
730
|
+
const kind = temporalKind(type);
|
|
731
|
+
if (kind !== 'tz' && kind !== 'timestamp') {
|
|
732
|
+
throw new ValidationError(`expr.secondsBetween() in ${site(env)}: col(${JSON.stringify(field)}) is ${type === undefined ? 'a column of unknown type' : `a ${type} column`}. ` +
|
|
733
|
+
'Both operands must be timestamp (or both timestamptz) columns.');
|
|
734
|
+
}
|
|
735
|
+
kinds.push(kind);
|
|
736
|
+
state.reads.add(column);
|
|
737
|
+
state.values.add(column);
|
|
738
|
+
return column;
|
|
739
|
+
});
|
|
740
|
+
if (kinds[0] !== kinds[1]) {
|
|
741
|
+
throw new ValidationError(`expr.secondsBetween() in ${site(env)} mixes a timestamptz column with a zone-less timestamp column, ` +
|
|
742
|
+
'whose difference depends on the session time zone. Use two columns of one kind.');
|
|
743
|
+
}
|
|
744
|
+
const [from, to] = columns;
|
|
745
|
+
return {
|
|
746
|
+
sql: `EXTRACT(EPOCH FROM (${env.column(to)} - ${env.column(from)}))`,
|
|
747
|
+
fp: `secs(${from},${to})`,
|
|
748
|
+
cls: 'dec',
|
|
749
|
+
};
|
|
750
|
+
}
|
|
626
751
|
/**
|
|
627
752
|
* Prune and validate one arm's `when`: `undefined` keys drop out (the C1 rule
|
|
628
753
|
* for every `where` position), an arm left with no predicate is refused, and
|
package/dist/query/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `import { … } from './query/index.js'` is a drop-in replacement for the
|
|
6
6
|
* former monolithic `import { … } from './query.js'`.
|
|
7
7
|
*/
|
|
8
|
-
export type { AggregateArgs, AggregateResult, ArrayFilter, ColumnRef, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByAggregateSpec, GroupByArgs, GroupByDistinctOn, GroupByResult, HavingClause, JsonEncoding, JsonFilter, JsonPathAggregateTarget, JsonPathGroupKey, JsonPathOrderBy, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderByObject, OrderBySpec, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, RelationOrderBy, RelationOrderByChain, RelationPickBy, RelationPickOrderBy, ResolvedSkipGlobalFilters, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithOrderByObject, WithResult, } from './types.js';
|
|
8
|
+
export type { AggregateArgs, AggregateResult, ArrayFilter, ColumnRef, ConnectOrCreateOp, CountArgs, CreateArgs, CreateDataInput, CreateManyArgs, CreateManyCount, CreateManyResult, DeleteArgs, DeleteManyArgs, FieldResult, FindManyArgs, FindManyStreamArgs, FindUniqueArgs, GlobalFilters, GroupByAggregateSpec, GroupByArgs, GroupByDistinctOn, GroupByResult, HavingClause, JsonEncoding, JsonFilter, JsonPathAggregateTarget, JsonPathGroupKey, JsonPathOrderBy, NestedCreateOp, NestedUpdateOp, NestedUpdateOpItem, NestedUpsertOpItem, OmitResult, OrderByClause, OrderByObject, OrderBySpec, OrderDirection, QueryResult, RelationDescriptor, RelationFilter, RelationLoadStrategy, RelationOrderBy, RelationOrderByChain, RelationPickBy, RelationPickOrderBy, ResolvedSkipGlobalFilters, SelectResult, SkipGlobalFilters, TextSearchFilter, TypedWithClause, UpdateArgs, UpdateDataInput, UpdateInput, UpdateManyArgs, UpdateOperatorInput, UpsertArgs, VectorDistanceFilter, VectorFilter, VectorMetric, VectorOrderBy, VectorOrderByDistance, WhereClause, WhereOperator, WhereValue, WithClause, WithOptions, WithOrderByObject, WithResult, } from './types.js';
|
|
9
9
|
export type { BuiltStatement, BulkInsertStatementInput, ColumnDefinitionInput, ColumnTypeInput, CreateIndexStatementInput, CreateTableStatementInput, Dialect, InsertStatementInput, UpsertStatementInput, } from '../dialect.js';
|
|
10
10
|
export { postgresDialect } from '../dialect.js';
|
|
11
11
|
export { col } from './column-ref.js';
|
|
@@ -136,6 +136,9 @@ export const CREATE_MANY_OPTIONS = {
|
|
|
136
136
|
onConflict: 'internal',
|
|
137
137
|
select: 'internal',
|
|
138
138
|
omit: 'internal',
|
|
139
|
+
// Compat's createMany already resolves to Prisma's { count } and sets this
|
|
140
|
+
// itself; a caller-supplied value would change the shape it relies on.
|
|
141
|
+
returning: 'internal',
|
|
139
142
|
timeout: 'native',
|
|
140
143
|
skipGlobalFilters: 'native',
|
|
141
144
|
};
|