turbine-orm 0.90.2 → 0.91.0-next.a5cefb1
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/types.d.ts
CHANGED
|
@@ -1034,7 +1034,7 @@ export interface CreateArgs<T, R extends object = {}, S extends Record<string, b
|
|
|
1034
1034
|
/** Query timeout in milliseconds. Rejects with an error if exceeded. */
|
|
1035
1035
|
timeout?: number | undefined;
|
|
1036
1036
|
}
|
|
1037
|
-
export interface CreateManyArgs<T, D = OptionalInput<T>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined> {
|
|
1037
|
+
export interface CreateManyArgs<T, D = OptionalInput<T>, S extends Record<string, boolean> | undefined = undefined, O extends Record<string, boolean> | undefined = undefined, C extends boolean | undefined = true | undefined> {
|
|
1038
1038
|
/** The rows. A generated client passes its `XCreate` as `D`. */
|
|
1039
1039
|
data: D[];
|
|
1040
1040
|
/** Skip rows that conflict on ANY unique constraint (ON CONFLICT DO NOTHING). */
|
|
@@ -1052,6 +1052,22 @@ export interface CreateManyArgs<T, D = OptionalInput<T>, S extends Record<string
|
|
|
1052
1052
|
select?: FlagsArg<T, S>;
|
|
1053
1053
|
/** Return the written rows without these fields. Same rules as `select`. */
|
|
1054
1054
|
omit?: FlagsArg<T, O>;
|
|
1055
|
+
/**
|
|
1056
|
+
* `false` resolves to `{ count }`, the number of rows written, instead of the
|
|
1057
|
+
* rows. A plain insert then sends no `RETURNING` / `OUTPUT` at all; an
|
|
1058
|
+
* `onConflict` call returns only the key columns it needs to refuse two input
|
|
1059
|
+
* rows that write one table row. The count is the number of rows the call
|
|
1060
|
+
* would otherwise have returned: inserted plus updated, never a row
|
|
1061
|
+
* `skipDuplicates` or `ignore` skipped. Cannot be combined with `select` or
|
|
1062
|
+
* `omit` (E003). On MySQL, `skipDuplicates` with `false` is E017: a skipped
|
|
1063
|
+
* row there counts as written.
|
|
1064
|
+
*
|
|
1065
|
+
* Typed by `C`, which defaults to the rows form, so args typed ahead of the
|
|
1066
|
+
* call (`const args: CreateManyArgs<User> = ...`) still resolve to rows.
|
|
1067
|
+
* Pass `false` as `C` to type count-only args ahead of the call, or
|
|
1068
|
+
* `boolean` for a flag known only at run time (the result is then either).
|
|
1069
|
+
*/
|
|
1070
|
+
returning?: C;
|
|
1055
1071
|
/**
|
|
1056
1072
|
* Opt out of configured {@link GlobalFilters} on the conflict-UPDATE of
|
|
1057
1073
|
* `onConflict` (inserts are never filtered). See {@link SkipGlobalFilters}.
|
|
@@ -1060,6 +1076,16 @@ export interface CreateManyArgs<T, D = OptionalInput<T>, S extends Record<string
|
|
|
1060
1076
|
/** Query timeout in milliseconds. Rejects with an error if exceeded. */
|
|
1061
1077
|
timeout?: number | undefined;
|
|
1062
1078
|
}
|
|
1079
|
+
/** What `createMany({ returning: false })` resolves to. */
|
|
1080
|
+
export interface CreateManyCount {
|
|
1081
|
+
/** Rows written: inserted plus updated. */
|
|
1082
|
+
count: number;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* `createMany`'s result for a `returning` flag `C`: the rows for `true` or no
|
|
1086
|
+
* flag, `{ count }` for `false`, either for a `boolean` known only at run time.
|
|
1087
|
+
*/
|
|
1088
|
+
export type CreateManyResult<Row, C> = [C] extends [false] ? CreateManyCount : [C] extends [true | undefined] ? Row[] : Row[] | CreateManyCount;
|
|
1063
1089
|
/**
|
|
1064
1090
|
* The columns of the primary key, or of one non-partial unique constraint or
|
|
1065
1091
|
* index, in any order. A `createMany` conflict target.
|
|
@@ -1098,7 +1124,7 @@ export type ConflictUpdateData<T> = {
|
|
|
1098
1124
|
[K in keyof T]?: T[K] | UpdateOperatorInput<T[K]> | SqlExpr<T[K]> | ColumnRef | ExcludedRef | undefined;
|
|
1099
1125
|
};
|
|
1100
1126
|
/** The node kinds an update expression is built from (see {@link SqlExpr}). */
|
|
1101
|
-
export type SqlExprKind = 'add' | 'sub' | 'mul' | 'div' | 'coalesce' | 'greatest' | 'least' | 'case' | 'now' | 'excluded';
|
|
1127
|
+
export type SqlExprKind = 'add' | 'sub' | 'mul' | 'div' | 'coalesce' | 'greatest' | 'least' | 'case' | 'now' | 'excluded' | 'secondsBetween';
|
|
1102
1128
|
/** Carries the phantom result type of a {@link SqlExpr}. Type-level only. */
|
|
1103
1129
|
declare const SQL_EXPR_RESULT: unique symbol;
|
|
1104
1130
|
/**
|
|
@@ -1622,8 +1648,11 @@ export interface GroupByArgs<T, R extends object = {}> {
|
|
|
1622
1648
|
_min?: GroupByAggregateSpec<T> | undefined;
|
|
1623
1649
|
/** Maximum value of fields (or JSON paths) in each group */
|
|
1624
1650
|
_max?: GroupByAggregateSpec<T> | undefined;
|
|
1625
|
-
/**
|
|
1626
|
-
|
|
1651
|
+
/**
|
|
1652
|
+
* Percentiles of fields, or of expressions, in each group (PostgreSQL only).
|
|
1653
|
+
* See {@link PercentileSpec} and {@link PercentileExprSpec}.
|
|
1654
|
+
*/
|
|
1655
|
+
_percentile?: PercentileArgs<T> | undefined;
|
|
1627
1656
|
/** Filter whole groups by their aggregate values (SQL HAVING). JSON-path aggregates key by their alias. */
|
|
1628
1657
|
having?: HavingClause<T> | undefined;
|
|
1629
1658
|
/**
|
|
@@ -1769,9 +1798,19 @@ export interface AggregateArgs<T, R extends object = {}> {
|
|
|
1769
1798
|
* s._percentile.statusCode; // number | null
|
|
1770
1799
|
* ```
|
|
1771
1800
|
*
|
|
1772
|
-
*
|
|
1801
|
+
* A key that names no field computes the percentile of an expression,
|
|
1802
|
+
* given as `of`:
|
|
1803
|
+
*
|
|
1804
|
+
* ```ts
|
|
1805
|
+
* const s = await db.jobs.aggregate({
|
|
1806
|
+
* _percentile: { wait: { of: expr.secondsBetween(col('requestedAt'), col('readyAt')), p: 0.95 } },
|
|
1807
|
+
* });
|
|
1808
|
+
* s._percentile.wait; // number | null, in seconds
|
|
1809
|
+
* ```
|
|
1810
|
+
*
|
|
1811
|
+
* See {@link PercentileSpec} and {@link PercentileExprSpec}.
|
|
1773
1812
|
*/
|
|
1774
|
-
_percentile?:
|
|
1813
|
+
_percentile?: PercentileArgs<T> | undefined;
|
|
1775
1814
|
/**
|
|
1776
1815
|
* Opt in to aggregating PII-tagged (`defineSchema` `pii: true`) columns.
|
|
1777
1816
|
* `_min` / `_max` / `_percentile` return a STORED VALUE (or one that equals a
|
|
@@ -1836,11 +1875,72 @@ export type AggregateResultOf<T, A> = AggregateCountPart<A> & GroupBySumAvgPart<
|
|
|
1836
1875
|
export interface PercentileSpec {
|
|
1837
1876
|
p: number | readonly number[];
|
|
1838
1877
|
method?: PercentileMethod | undefined;
|
|
1878
|
+
/** Only on a key that names no field: see {@link PercentileExprSpec}. */
|
|
1879
|
+
of?: undefined;
|
|
1880
|
+
}
|
|
1881
|
+
/**
|
|
1882
|
+
* One `_percentile` entry over an expression, under a key that names no field
|
|
1883
|
+
* (the key is only the result's name and never reaches the SQL).
|
|
1884
|
+
*
|
|
1885
|
+
* - `of`: a numeric expression over the row's own columns, built with
|
|
1886
|
+
* {@link expr}: arithmetic (`expr.add` / `sub` / `mul` / `div`),
|
|
1887
|
+
* `expr.coalesce` / `greatest` / `least` / `case`, and
|
|
1888
|
+
* `expr.secondsBetween(col('a'), col('b'))` for the seconds from one
|
|
1889
|
+
* timestamp column to another. Bound values must be numbers or bigints.
|
|
1890
|
+
* `col('x')` alone is refused: use the field key `x` instead.
|
|
1891
|
+
* - `p` and `method` as in {@link PercentileSpec}. The expression is ordered as
|
|
1892
|
+
* double precision, so the result is a `number` for both methods (an integer
|
|
1893
|
+
* beyond 2^53 loses exactness).
|
|
1894
|
+
*
|
|
1895
|
+
* Every column the expression reads as a value needs `includePii: UNSAFE`
|
|
1896
|
+
* when it is PII-tagged; columns read only by an `expr.case` arm's `when` do
|
|
1897
|
+
* not. Over zero rows the result is `null`.
|
|
1898
|
+
*/
|
|
1899
|
+
export interface PercentileExprSpec {
|
|
1900
|
+
of: SqlExpr<number | bigint | string>;
|
|
1901
|
+
p: number | readonly number[];
|
|
1902
|
+
method?: PercentileMethod | undefined;
|
|
1839
1903
|
}
|
|
1904
|
+
/**
|
|
1905
|
+
* The `_percentile` argument: field keys take a {@link PercentileSpec}; any
|
|
1906
|
+
* other key names an expression's result and takes a {@link PercentileExprSpec}.
|
|
1907
|
+
* On an untyped table (`keyof T` is `string`) any key takes either: the
|
|
1908
|
+
* `string` key is dropped from the field half, which would otherwise narrow
|
|
1909
|
+
* every key to a field spec.
|
|
1910
|
+
*/
|
|
1911
|
+
export type PercentileArgs<T> = {
|
|
1912
|
+
[K in keyof T & string as string extends K ? never : K]?: PercentileSpec | undefined;
|
|
1913
|
+
} & {
|
|
1914
|
+
[name: string]: PercentileSpec | PercentileExprSpec | undefined;
|
|
1915
|
+
};
|
|
1916
|
+
/**
|
|
1917
|
+
* `_percentile` naming fields only. `aggregate` / `groupBy` take args of this
|
|
1918
|
+
* shape when their type is generic, where {@link PercentileKeysChecked} cannot
|
|
1919
|
+
* read the keys, so a wrapper generic over its args still compiles.
|
|
1920
|
+
*/
|
|
1921
|
+
export type FieldPercentiles<T> = {
|
|
1922
|
+
_percentile?: {
|
|
1923
|
+
[K in keyof T & string]?: PercentileSpec | undefined;
|
|
1924
|
+
} | undefined;
|
|
1925
|
+
};
|
|
1926
|
+
/**
|
|
1927
|
+
* Applied to `aggregate` / `groupBy` arguments: every `_percentile` key that
|
|
1928
|
+
* names no field must carry `of`, so a misspelled field name is a compile
|
|
1929
|
+
* error rather than an expression percentile with no expression.
|
|
1930
|
+
*/
|
|
1931
|
+
export type PercentileKeysChecked<T, A> = A extends {
|
|
1932
|
+
_percentile: infer S;
|
|
1933
|
+
} ? [S] extends [object] ? {
|
|
1934
|
+
_percentile: {
|
|
1935
|
+
[K in keyof S]: K extends keyof T ? S[K] : PercentileExprSpec | undefined;
|
|
1936
|
+
};
|
|
1937
|
+
} : unknown : unknown;
|
|
1840
1938
|
/** `'cont'` interpolates (`percentile_cont`); `'disc'` returns a stored value (`percentile_disc`). */
|
|
1841
1939
|
export type PercentileMethod = 'cont' | 'disc';
|
|
1842
1940
|
/** One percentile value: the column's own type for `disc`; for `cont` a number, or an interval as the column reads. */
|
|
1843
|
-
type PercentileScalar<T, K, Spec> =
|
|
1941
|
+
type PercentileScalar<T, K, Spec> = Spec extends {
|
|
1942
|
+
of: object;
|
|
1943
|
+
} ? number : K extends keyof T ? Spec extends {
|
|
1844
1944
|
method: 'disc';
|
|
1845
1945
|
} ? NonNullable<T[K]> : [NonNullable<T[K]>] extends [number] ? number : number | NonNullable<T[K]> : unknown;
|
|
1846
1946
|
/** A scalar `p` yields one value, an array `p` yields an array, `null` over zero rows. */
|
package/dist/query/writes.d.ts
CHANGED
|
@@ -91,7 +91,7 @@ export declare function buildCreateMany<T extends object>(qi: BuilderCtx, args:
|
|
|
91
91
|
* builder (`skipGlobalFilters`), which a concurrent call could change across
|
|
92
92
|
* an await.
|
|
93
93
|
*/
|
|
94
|
-
export declare function planCreateMany<T extends object>(qi: BuilderCtx, args: CreateManyArgs<T>, projection?: WriteProjection): {
|
|
94
|
+
export declare function planCreateMany<T extends object>(qi: BuilderCtx, args: CreateManyArgs<T>, projection?: WriteProjection, canSplit?: boolean): {
|
|
95
95
|
chunks: DeferredQuery<T[]>[];
|
|
96
96
|
rowsPerChunk: number;
|
|
97
97
|
};
|
|
@@ -192,9 +192,21 @@ export declare function piiFields(_qi: BuilderCtx, meta: TableMetadata): string[
|
|
|
192
192
|
* 34,500/s with `RETURNING *`.
|
|
193
193
|
*/
|
|
194
194
|
export interface WriteProjection {
|
|
195
|
-
/**
|
|
195
|
+
/**
|
|
196
|
+
* Unquoted column names, in table order. Empty only for {@link COUNT_ONLY}:
|
|
197
|
+
* a caller's projection always names at least one column.
|
|
198
|
+
*/
|
|
196
199
|
readonly columns: readonly string[];
|
|
197
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* The projection of `createMany({ returning: false })`: no column. A plain
|
|
203
|
+
* insert then sends no RETURNING / OUTPUT and reports its row count; an
|
|
204
|
+
* onConflict call returns only its identity columns (see
|
|
205
|
+
* {@link conflictIdentity}), which the count is taken from.
|
|
206
|
+
*/
|
|
207
|
+
export declare const COUNT_ONLY: WriteProjection;
|
|
208
|
+
/** True for {@link COUNT_ONLY} (or any projection naming no column). */
|
|
209
|
+
export declare function isCountOnly(projection: WriteProjection | undefined): boolean;
|
|
198
210
|
/**
|
|
199
211
|
* The write-SQL cache segment for a projection. EMPTY for the default shape, so
|
|
200
212
|
* every existing key is byte-identical; a projected statement differs only in
|
package/dist/query/writes.js
CHANGED
|
@@ -828,12 +828,17 @@ export function buildCreateMany(qi, args, overBindLimit = 'refuse', projection)
|
|
|
828
828
|
* builder (`skipGlobalFilters`), which a concurrent call could change across
|
|
829
829
|
* an await.
|
|
830
830
|
*/
|
|
831
|
-
export function planCreateMany(qi, args, projection) {
|
|
831
|
+
export function planCreateMany(qi, args, projection, canSplit = true) {
|
|
832
832
|
const prepared = prepareCreateMany(qi, args, projection);
|
|
833
833
|
if ('deferred' in prepared)
|
|
834
834
|
return { chunks: [prepared.deferred], rowsPerChunk: args.data.length };
|
|
835
835
|
const rows = prepared.rowValues;
|
|
836
|
-
const
|
|
836
|
+
const { most, chunk } = statementSizes(qi, prepared);
|
|
837
|
+
// Rows that cannot be told apart across statements (refused below past one),
|
|
838
|
+
// and a call whose executor cannot run several statements in one
|
|
839
|
+
// transaction (`canSplit`), take the fewest statements the engine allows;
|
|
840
|
+
// the rest are split at the size the engine runs fastest.
|
|
841
|
+
const size = !canSplit || (prepared.conflict && !prepared.identity) ? most : chunk;
|
|
837
842
|
if (rows.length <= size) {
|
|
838
843
|
return { chunks: [emitCreateMany(qi, prepared, rows, 'allow', true)], rowsPerChunk: size };
|
|
839
844
|
}
|
|
@@ -861,12 +866,19 @@ function prepareCreateMany(qi, args, projection) {
|
|
|
861
866
|
// The data-independent half of onConflict is checked before the empty-data
|
|
862
867
|
// shortcut, so a call is never valid with no rows and invalid with some.
|
|
863
868
|
const conflict = args.onConflict === undefined ? null : resolveConflictPlan(qi, args);
|
|
869
|
+
const countOnly = isCountOnly(projection);
|
|
870
|
+
// Before the empty-data shortcut too, so the refusal never depends on the rows.
|
|
871
|
+
if (countOnly && args.skipDuplicates && qi.dialect.capabilities.skipDuplicatesCountsSkipped) {
|
|
872
|
+
throw new UnsupportedFeatureError('createMany({ skipDuplicates: true, returning: false })', qi.dialect.name, 'It counts a skipped row as written, so the count would include the rows skipped. Without ' +
|
|
873
|
+
'returning: false the call writes the same rows and reports no count.');
|
|
874
|
+
}
|
|
864
875
|
if (args.data.length === 0) {
|
|
865
876
|
return {
|
|
866
877
|
deferred: {
|
|
867
|
-
|
|
878
|
+
// `1 = 0`, not `false`: T-SQL has no boolean literal in a condition.
|
|
879
|
+
sql: `SELECT * FROM ${qt} WHERE 1 = 0`,
|
|
868
880
|
params: [],
|
|
869
|
-
transform: () => [],
|
|
881
|
+
transform: () => (countOnly ? { count: 0 } : []),
|
|
870
882
|
tag: `${qi.table}.createMany`,
|
|
871
883
|
},
|
|
872
884
|
};
|
|
@@ -895,7 +907,9 @@ function prepareCreateMany(qi, args, projection) {
|
|
|
895
907
|
deferred: {
|
|
896
908
|
sql: buildDefaultValuesInsert(qi, args.data.length, args.skipDuplicates, projection),
|
|
897
909
|
params: [],
|
|
898
|
-
transform: (result) =>
|
|
910
|
+
transform: (result) => countOnly
|
|
911
|
+
? countOf(result)
|
|
912
|
+
: result.rows.map((row) => parseWriteRow(qi, row, projection)),
|
|
899
913
|
tag: `${qi.table}.createMany`,
|
|
900
914
|
},
|
|
901
915
|
};
|
|
@@ -973,32 +987,40 @@ function emitCreateMany(qi, prepared, rowValues, overBindLimit, first) {
|
|
|
973
987
|
return {
|
|
974
988
|
sql: built.sql,
|
|
975
989
|
params: built.params,
|
|
976
|
-
transform: (result) =>
|
|
990
|
+
transform: (result) => isCountOnly(projection)
|
|
991
|
+
? countOf(result)
|
|
992
|
+
: result.rows.map((row) => parseWriteRow(qi, row, projection)),
|
|
977
993
|
tag: `${qi.table}.createMany`,
|
|
978
994
|
};
|
|
979
995
|
}
|
|
980
996
|
/**
|
|
981
|
-
* Rows one `createMany` statement may carry
|
|
982
|
-
* parameters within the engine's limit and, on SQL Server, its rows
|
|
983
|
-
* the 1000 a plain `INSERT ... VALUES` accepts.
|
|
984
|
-
*
|
|
985
|
-
*
|
|
986
|
-
*
|
|
997
|
+
* Rows one `createMany` statement may carry. `most`: the most that keep its
|
|
998
|
+
* bind parameters within the engine's limit and, on SQL Server, its rows
|
|
999
|
+
* within the 1000 a plain `INSERT ... VALUES` accepts. `chunk`: the same under
|
|
1000
|
+
* the engine's `chunkBindParameters`, the size a call that runs as several
|
|
1001
|
+
* statements is split at. The parameter cost is measured from one-row and
|
|
1002
|
+
* two-row builds, which separates the per-row parameters from the ones bound
|
|
1003
|
+
* once per statement (an onConflict update's values); the PostgreSQL UNNEST
|
|
1004
|
+
* form binds one array per column, so its rows never run out.
|
|
987
1005
|
*/
|
|
988
|
-
function
|
|
1006
|
+
function statementSizes(qi, prepared) {
|
|
989
1007
|
// An onConflict statement (SQL Server's MERGE) reads its rows as a derived
|
|
990
1008
|
// table, which the plain INSERT ... VALUES row cap does not bind.
|
|
991
1009
|
const cap = prepared.conflict ? Infinity : qi.dialect.capabilities.maxInsertRows;
|
|
992
|
-
const limit = maxBindParameters(qi.dialect);
|
|
1010
|
+
const limit = maxBindParameters(qi.dialect) ?? Infinity;
|
|
1011
|
+
// A dialect written before the key existed has none, and splits at its limit.
|
|
1012
|
+
const preferred = qi.dialect.capabilities.chunkBindParameters;
|
|
1013
|
+
const chunkLimit = typeof preferred === 'number' && preferred < limit ? preferred : limit;
|
|
993
1014
|
const rows = prepared.rowValues;
|
|
994
|
-
if (
|
|
995
|
-
return cap;
|
|
1015
|
+
if (!Number.isFinite(chunkLimit) || rows.length < 2)
|
|
1016
|
+
return { most: cap, chunk: cap };
|
|
996
1017
|
const one = emitCreateMany(qi, prepared, rows.slice(0, 1), 'allow', true).params.length;
|
|
997
1018
|
const perRow = emitCreateMany(qi, prepared, rows.slice(0, 2), 'allow', true).params.length - one;
|
|
998
1019
|
if (perRow <= 0)
|
|
999
|
-
return cap;
|
|
1020
|
+
return { most: cap, chunk: cap };
|
|
1000
1021
|
const fixed = Math.max(0, one - perRow);
|
|
1001
|
-
|
|
1022
|
+
const fit = (bound) => Number.isFinite(bound) ? Math.max(1, Math.min(cap, Math.floor((bound - fixed) / perRow))) : cap;
|
|
1023
|
+
return { most: fit(limit), chunk: fit(chunkLimit) };
|
|
1002
1024
|
}
|
|
1003
1025
|
const ON_CONFLICT_KEYS = new Set(['target', 'update', 'where', 'ignore']);
|
|
1004
1026
|
/**
|
|
@@ -1359,6 +1381,11 @@ function buildBulkUpsert(qi, conflict, insert, columns, overBindLimit, projectio
|
|
|
1359
1381
|
transform: (result) => {
|
|
1360
1382
|
if (identity)
|
|
1361
1383
|
assertEachRowWrittenOnce(qi, conflict, identity, result.rows, first);
|
|
1384
|
+
// COUNT_ONLY: the rows back are the identity columns (or none at all when
|
|
1385
|
+
// the written rows cannot be identified), counted and never returned.
|
|
1386
|
+
if (isCountOnly(projection)) {
|
|
1387
|
+
return (identity ? { count: result.rows.length } : countOf(result));
|
|
1388
|
+
}
|
|
1362
1389
|
const rows = result.rows.map((row) => parseWriteRow(qi, row, projection));
|
|
1363
1390
|
if (identity && identity.added.length > 0) {
|
|
1364
1391
|
for (const row of rows)
|
|
@@ -1989,6 +2016,21 @@ export function piiFields(_qi, meta) {
|
|
|
1989
2016
|
}
|
|
1990
2017
|
return out;
|
|
1991
2018
|
}
|
|
2019
|
+
/**
|
|
2020
|
+
* The projection of `createMany({ returning: false })`: no column. A plain
|
|
2021
|
+
* insert then sends no RETURNING / OUTPUT and reports its row count; an
|
|
2022
|
+
* onConflict call returns only its identity columns (see
|
|
2023
|
+
* {@link conflictIdentity}), which the count is taken from.
|
|
2024
|
+
*/
|
|
2025
|
+
export const COUNT_ONLY = Object.freeze({ columns: Object.freeze([]) });
|
|
2026
|
+
/** True for {@link COUNT_ONLY} (or any projection naming no column). */
|
|
2027
|
+
export function isCountOnly(projection) {
|
|
2028
|
+
return projection !== undefined && projection.columns.length === 0;
|
|
2029
|
+
}
|
|
2030
|
+
/** `{ count }` from a write that returned no rows to count: the engine's row count. */
|
|
2031
|
+
function countOf(result) {
|
|
2032
|
+
return { count: typeof result.rowCount === 'number' ? result.rowCount : result.rows.length };
|
|
2033
|
+
}
|
|
1992
2034
|
/**
|
|
1993
2035
|
* The write-SQL cache segment for a projection. EMPTY for the default shape, so
|
|
1994
2036
|
* every existing key is byte-identical; a projected statement differs only in
|
|
@@ -2084,6 +2126,9 @@ export function writeReturningColumns(qi, projection) {
|
|
|
2084
2126
|
*/
|
|
2085
2127
|
export function writeReselectSelection(qi, projection) {
|
|
2086
2128
|
const cols = writeReturningColumns(qi, projection);
|
|
2129
|
+
// COUNT_ONLY: the re-read is counted, not returned, so it reads no column.
|
|
2130
|
+
if (cols !== '*' && cols.length === 0)
|
|
2131
|
+
return '1';
|
|
2087
2132
|
return cols === '*' ? '*' : cols.join(', ');
|
|
2088
2133
|
}
|
|
2089
2134
|
/**
|
package/dist/sqlite.js
CHANGED
|
@@ -905,12 +905,16 @@ export const sqliteDialect = {
|
|
|
905
905
|
// The default SQLITE_MAX_VARIABLE_NUMBER.
|
|
906
906
|
maxBindParameters: 32766,
|
|
907
907
|
maxInsertRows: Number.POSITIVE_INFINITY,
|
|
908
|
+
// Measured: 40,000 rows insert 70 to 140 times faster in statements of 250
|
|
909
|
+
// parameters than of 32,766 (named-parameter lookup is a linear search).
|
|
910
|
+
chunkBindParameters: 250,
|
|
908
911
|
upsertConflictTarget: 'explicit',
|
|
909
912
|
setClauseOrder: 'snapshot',
|
|
910
913
|
reselectLocksRows: false,
|
|
911
914
|
integerDivision: '/',
|
|
912
915
|
updateExpressions: true,
|
|
913
916
|
bulkUpsertAppliesRepeatedKeys: true,
|
|
917
|
+
skipDuplicatesCountsSkipped: false,
|
|
914
918
|
readOnlyTransactions: false,
|
|
915
919
|
connectionParameters: false,
|
|
916
920
|
backendCancel: false,
|
|
@@ -1121,6 +1125,8 @@ export const sqliteDialect = {
|
|
|
1121
1125
|
(boolean ? 'Write true or false.' : 'Write a number, or a string holding one, such as "12.5".'));
|
|
1122
1126
|
},
|
|
1123
1127
|
buildReturningClause(selection = '*') {
|
|
1128
|
+
if (selection !== '*' && selection.length === 0)
|
|
1129
|
+
return '';
|
|
1124
1130
|
return ` RETURNING ${selection === '*' ? '*' : selection.join(', ')}`;
|
|
1125
1131
|
},
|
|
1126
1132
|
buildInsertStatement(input) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turbine-orm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.91.0-next.a5cefb1",
|
|
4
4
|
"description": "Postgres-native TypeScript ORM, runs on Neon, Vercel Postgres, Cloudflare, Supabase. Streaming cursors, typed errors, single-query nested relations. One dependency, no WASM engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"//exports": "Each subpath declares its types PER CONDITION. A single shared top-level \"types\" resolves to the ESM declarations for `require` too, which is TS1479 (\"is an ES module ... cannot be require()d\") for any CJS consumer on moduleResolution node16/nodenext. The require condition points at dist/cjs, which ships its own {\"type\":\"commonjs\"} package.json, so those declarations are CJS declarations. Gated in CI by publint + @arethetypeswrong/cli + a real .cts consumer typecheck (see the package-types job in ci.yml).",
|
|
@@ -233,6 +233,12 @@ PostgreSQL only. `p` may be an array of fractions, and the result is then an
|
|
|
233
233
|
array in the same order. The default `method: 'cont'` interpolates and is
|
|
234
234
|
refused on a date column with `TURBINE_E003`; use `method: 'disc'` there.
|
|
235
235
|
|
|
236
|
+
A `_percentile` key that names no field takes `of`, an expression, and returns
|
|
237
|
+
a number under that key: `scoreGap: { of: expr.sub(col('rindScore'),
|
|
238
|
+
col('aromaScore')), p: 0.5 }`. `expr.secondsBetween(col('a'), col('b'))` is the
|
|
239
|
+
seconds between two timestamp columns, and a date column there is refused with
|
|
240
|
+
`TURBINE_E003`.
|
|
241
|
+
|
|
236
242
|
## groupBy and having
|
|
237
243
|
|
|
238
244
|
`by` lists the grouping columns. The `having` shape is **column first, aggregate
|
|
@@ -293,6 +299,8 @@ refused with `TURBINE_E003`, and so is an expression in `create` data.
|
|
|
293
299
|
target must be the primary key or one whole unique key; any other column list
|
|
294
300
|
is refused with `TURBINE_E003`.
|
|
295
301
|
|
|
302
|
+
`createMany` with `returning: false` returns `{ count }` instead of the rows.
|
|
303
|
+
|
|
296
304
|
## Locking rows
|
|
297
305
|
|
|
298
306
|
`lock: 'update'` on `findMany`, `findFirst` or `findUnique` locks the rows it
|