@nextlyhq/adapter-drizzle 0.0.2-alpha.64 → 0.0.2-alpha.66
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/{adapter-D4GM1VAZ.d.ts → adapter-CHlx_Taa.d.ts} +89 -3
- package/dist/{adapter-C6ENLDau.d.cts → adapter-Cs3Jl9Fb.d.cts} +89 -3
- package/dist/index.cjs +283 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +284 -21
- package/dist/index.mjs.map +1 -1
- package/dist/{migration-B6AmjCQ1.d.cts → migration-Ce0hkgr_.d.cts} +25 -3
- package/dist/{migration-R06fsTrz.d.ts → migration-CgoxgWOL.d.ts} +25 -3
- package/dist/migrations.d.cts +3 -3
- package/dist/migrations.d.ts +3 -3
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +3 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyRelations, SQL } from 'drizzle-orm';
|
|
2
|
-
import { T as TransactionContext, S as SelectOptions, W as WhereClause,
|
|
2
|
+
import { T as TransactionContext, S as SelectOptions, W as WhereClause, D as DeleteOptions, U as UpsertOptions, e as TransactionOptions, f as DatabaseCapabilities, P as PoolStats, C as CountOptions, I as InsertOptions, g as UpdateOptions, a as Migration, d as MigrationResult } from './migration-CgoxgWOL.js';
|
|
3
3
|
import { S as SupportedDialect, a as SqlParam, T as TableResolver } from './core-CVO7WYDj.js';
|
|
4
4
|
import { T as TableDefinition, C as CreateTableOptions, D as DropTableOptions, A as AlterTableOperation, a as AlterTableOptions } from './schema-BIQ0YQZ_.js';
|
|
5
5
|
import { D as DatabaseErrorKind, a as DatabaseError } from './error-BrdknH2s.js';
|
|
@@ -21,15 +21,20 @@ import { D as DatabaseErrorKind, a as DatabaseError } from './error-BrdknH2s.js'
|
|
|
21
21
|
interface TransactionCrudDelegator {
|
|
22
22
|
select<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T[]>;
|
|
23
23
|
selectOne<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T | null>;
|
|
24
|
-
update<T = unknown>(table: string, data: Record<string, unknown>, where: WhereClause, options?: UpdateOptions, executor?: unknown): Promise<T[]>;
|
|
25
24
|
updateCount(table: string, data: Record<string, unknown>, where: WhereClause, executor?: unknown): Promise<number>;
|
|
26
25
|
delete(table: string, where: WhereClause, options?: DeleteOptions, executor?: unknown): Promise<number>;
|
|
27
26
|
upsert<T = unknown>(table: string, data: Record<string, unknown>, options: UpsertOptions, executor?: unknown): Promise<T>;
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
30
29
|
* Transaction context CRUD methods provided by the forwarder.
|
|
30
|
+
*
|
|
31
|
+
* `update` is not among them. The pooled `update` goes through the Drizzle
|
|
32
|
+
* query builder, which writes the columns the runtime model declares; a
|
|
33
|
+
* transaction's update must reach the columns the physical table has, and
|
|
34
|
+
* each adapter binds the base class's `transactionUpdate` beside its
|
|
35
|
+
* transactional `insert`.
|
|
31
36
|
*/
|
|
32
|
-
type TransactionCrudForwarders = Pick<TransactionContext, "select" | "selectOne" | "
|
|
37
|
+
type TransactionCrudForwarders = Pick<TransactionContext, "select" | "selectOne" | "updateCount" | "delete" | "upsert" | "getDrizzle">;
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Base database adapter abstract class.
|
|
@@ -806,6 +811,87 @@ declare abstract class DrizzleAdapter {
|
|
|
806
811
|
* @protected
|
|
807
812
|
*/
|
|
808
813
|
protected createTransactionForwarders(txDb: () => unknown): TransactionCrudForwarders;
|
|
814
|
+
/**
|
|
815
|
+
* A transaction context's `update`, for each adapter to bind.
|
|
816
|
+
*
|
|
817
|
+
* Not forwarded to the pooled `update` the way `select` and `delete` are:
|
|
818
|
+
* that one goes through the Drizzle query builder, which writes the columns
|
|
819
|
+
* the runtime MODEL declares, and a transaction's update must reach the
|
|
820
|
+
* columns the physical TABLE has — the localization transition window
|
|
821
|
+
* writes a column the model has already moved to a companion table that
|
|
822
|
+
* does not exist yet. The INSERT half of every transaction context already
|
|
823
|
+
* builds its own statement for that reason; `update-statement.ts` is the
|
|
824
|
+
* UPDATE half, spelled once for the three adapters, and this runs it: on
|
|
825
|
+
* the transaction executor, classified with the operation and table named
|
|
826
|
+
* as the pooled `update` classifies its failures, and read back through
|
|
827
|
+
* `select` on the same executor when the caller asked for rows.
|
|
828
|
+
*
|
|
829
|
+
* That read is by the rows' IDENTITY where the dialect can report it: on
|
|
830
|
+
* PostgreSQL and SQLite the statement carries `RETURNING <primary key>`, and
|
|
831
|
+
* the read-back asks for exactly those rows — so a row another transaction
|
|
832
|
+
* adds under the same predicate meanwhile is not among them, and an update
|
|
833
|
+
* whose own write falsifies its predicate still reads its rows back. MySQL
|
|
834
|
+
* has no RETURNING, so there the read re-runs the predicate, as the pooled
|
|
835
|
+
* `update` always has on that dialect; a table with no primary key in its
|
|
836
|
+
* model reads back the same way.
|
|
837
|
+
*
|
|
838
|
+
* @param txDb - thunk returning the transaction-bound Drizzle instance
|
|
839
|
+
* @param run - how this dialect runs a statement on that instance, and the
|
|
840
|
+
* rows it returns when the statement carries RETURNING: better-sqlite3
|
|
841
|
+
* answers synchronously, the pooled drivers do not
|
|
842
|
+
* @param bindUnmodeled - how a value binds when the model declares no
|
|
843
|
+
* column for it; a declared column binds through its own encoder
|
|
844
|
+
* @returns the context's `update` method
|
|
845
|
+
*/
|
|
846
|
+
protected transactionUpdate(txDb: () => unknown, run: (statement: SQL, returnsRows: boolean) => Promise<Record<string, unknown>[] | undefined> | Record<string, unknown>[] | undefined, bindUnmodeled: (value: unknown) => unknown): TransactionContext["update"];
|
|
847
|
+
/**
|
|
848
|
+
* The columns that identify a row of this table, under both spellings: the
|
|
849
|
+
* SQL name RETURNING reports and the Drizzle property name the query
|
|
850
|
+
* builder's WHERE is addressed by. Columns flagged primary first; failing
|
|
851
|
+
* that, the table-level primary key the dialect's table config carries
|
|
852
|
+
* (`primaryKey({ columns })` leaves each column's flag false), which each
|
|
853
|
+
* adapter reads through its own dialect. Empty when the model declares
|
|
854
|
+
* neither, and the read-back falls back to the caller's predicate.
|
|
855
|
+
*/
|
|
856
|
+
protected identityColumns(tableObj: Record<string, unknown>): Array<{
|
|
857
|
+
jsName: string;
|
|
858
|
+
sqlName: string;
|
|
859
|
+
}>;
|
|
860
|
+
/**
|
|
861
|
+
* How a value binds to a column the model does not declare, on a driver
|
|
862
|
+
* that binds scalars, dates and binary natively: a structured value goes
|
|
863
|
+
* as JSON text, since what node-postgres and mysql2 make of a bare object
|
|
864
|
+
* or array is not JSON. For the adapters to hand `transactionUpdate`.
|
|
865
|
+
*/
|
|
866
|
+
protected bindUnmodeledStructuredAsJson(value: unknown): unknown;
|
|
867
|
+
/**
|
|
868
|
+
* The columns of a table-level `primaryKey({ columns })`, as the dialect's
|
|
869
|
+
* table config reports them. The base class cannot read that config without
|
|
870
|
+
* naming a dialect, so each adapter answers for its own; a dialect that does
|
|
871
|
+
* not answer reports none.
|
|
872
|
+
*/
|
|
873
|
+
protected compositePrimaryKey(_tableObj: Record<string, unknown>): object[];
|
|
874
|
+
/** The registered table object, or the error every CRUD method throws without one. */
|
|
875
|
+
private resolvedTableObject;
|
|
876
|
+
/**
|
|
877
|
+
* The statement `transactionUpdate` runs.
|
|
878
|
+
*
|
|
879
|
+
* @throws when nothing would be written — every key `undefined`, or none at
|
|
880
|
+
* all. The query builder refused that too ("No values to set"), and a
|
|
881
|
+
* patch that names nothing is a caller's mistake rather than a write of
|
|
882
|
+
* nothing.
|
|
883
|
+
*/
|
|
884
|
+
private buildTransactionUpdate;
|
|
885
|
+
/**
|
|
886
|
+
* Whether a transaction's `update` reads the rows it changed back: the
|
|
887
|
+
* caller named columns, or `*`. An empty list, like no option at all, asks
|
|
888
|
+
* for nothing and `update` answers `[]`, as it always has. The rows come
|
|
889
|
+
* from a read of the update's own WHERE on its own transaction, so they are
|
|
890
|
+
* the model's view of the row, decoded as every read is decoded — which is
|
|
891
|
+
* what the query-builder update returned for them, and why the named list
|
|
892
|
+
* selects nothing narrower.
|
|
893
|
+
*/
|
|
894
|
+
private updateReturnsRows;
|
|
809
895
|
/**
|
|
810
896
|
* Classify an error into a DatabaseError.
|
|
811
897
|
*
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AnyRelations, SQL } from 'drizzle-orm';
|
|
2
|
-
import { T as TransactionContext, S as SelectOptions, W as WhereClause,
|
|
2
|
+
import { T as TransactionContext, S as SelectOptions, W as WhereClause, D as DeleteOptions, U as UpsertOptions, e as TransactionOptions, f as DatabaseCapabilities, P as PoolStats, C as CountOptions, I as InsertOptions, g as UpdateOptions, a as Migration, d as MigrationResult } from './migration-Ce0hkgr_.cjs';
|
|
3
3
|
import { S as SupportedDialect, a as SqlParam, T as TableResolver } from './core-CVO7WYDj.cjs';
|
|
4
4
|
import { T as TableDefinition, C as CreateTableOptions, D as DropTableOptions, A as AlterTableOperation, a as AlterTableOptions } from './schema-BDn8WfSL.cjs';
|
|
5
5
|
import { D as DatabaseErrorKind, a as DatabaseError } from './error-BrdknH2s.cjs';
|
|
@@ -21,15 +21,20 @@ import { D as DatabaseErrorKind, a as DatabaseError } from './error-BrdknH2s.cjs
|
|
|
21
21
|
interface TransactionCrudDelegator {
|
|
22
22
|
select<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T[]>;
|
|
23
23
|
selectOne<T = unknown>(table: string, options?: SelectOptions, executor?: unknown): Promise<T | null>;
|
|
24
|
-
update<T = unknown>(table: string, data: Record<string, unknown>, where: WhereClause, options?: UpdateOptions, executor?: unknown): Promise<T[]>;
|
|
25
24
|
updateCount(table: string, data: Record<string, unknown>, where: WhereClause, executor?: unknown): Promise<number>;
|
|
26
25
|
delete(table: string, where: WhereClause, options?: DeleteOptions, executor?: unknown): Promise<number>;
|
|
27
26
|
upsert<T = unknown>(table: string, data: Record<string, unknown>, options: UpsertOptions, executor?: unknown): Promise<T>;
|
|
28
27
|
}
|
|
29
28
|
/**
|
|
30
29
|
* Transaction context CRUD methods provided by the forwarder.
|
|
30
|
+
*
|
|
31
|
+
* `update` is not among them. The pooled `update` goes through the Drizzle
|
|
32
|
+
* query builder, which writes the columns the runtime model declares; a
|
|
33
|
+
* transaction's update must reach the columns the physical table has, and
|
|
34
|
+
* each adapter binds the base class's `transactionUpdate` beside its
|
|
35
|
+
* transactional `insert`.
|
|
31
36
|
*/
|
|
32
|
-
type TransactionCrudForwarders = Pick<TransactionContext, "select" | "selectOne" | "
|
|
37
|
+
type TransactionCrudForwarders = Pick<TransactionContext, "select" | "selectOne" | "updateCount" | "delete" | "upsert" | "getDrizzle">;
|
|
33
38
|
|
|
34
39
|
/**
|
|
35
40
|
* Base database adapter abstract class.
|
|
@@ -806,6 +811,87 @@ declare abstract class DrizzleAdapter {
|
|
|
806
811
|
* @protected
|
|
807
812
|
*/
|
|
808
813
|
protected createTransactionForwarders(txDb: () => unknown): TransactionCrudForwarders;
|
|
814
|
+
/**
|
|
815
|
+
* A transaction context's `update`, for each adapter to bind.
|
|
816
|
+
*
|
|
817
|
+
* Not forwarded to the pooled `update` the way `select` and `delete` are:
|
|
818
|
+
* that one goes through the Drizzle query builder, which writes the columns
|
|
819
|
+
* the runtime MODEL declares, and a transaction's update must reach the
|
|
820
|
+
* columns the physical TABLE has — the localization transition window
|
|
821
|
+
* writes a column the model has already moved to a companion table that
|
|
822
|
+
* does not exist yet. The INSERT half of every transaction context already
|
|
823
|
+
* builds its own statement for that reason; `update-statement.ts` is the
|
|
824
|
+
* UPDATE half, spelled once for the three adapters, and this runs it: on
|
|
825
|
+
* the transaction executor, classified with the operation and table named
|
|
826
|
+
* as the pooled `update` classifies its failures, and read back through
|
|
827
|
+
* `select` on the same executor when the caller asked for rows.
|
|
828
|
+
*
|
|
829
|
+
* That read is by the rows' IDENTITY where the dialect can report it: on
|
|
830
|
+
* PostgreSQL and SQLite the statement carries `RETURNING <primary key>`, and
|
|
831
|
+
* the read-back asks for exactly those rows — so a row another transaction
|
|
832
|
+
* adds under the same predicate meanwhile is not among them, and an update
|
|
833
|
+
* whose own write falsifies its predicate still reads its rows back. MySQL
|
|
834
|
+
* has no RETURNING, so there the read re-runs the predicate, as the pooled
|
|
835
|
+
* `update` always has on that dialect; a table with no primary key in its
|
|
836
|
+
* model reads back the same way.
|
|
837
|
+
*
|
|
838
|
+
* @param txDb - thunk returning the transaction-bound Drizzle instance
|
|
839
|
+
* @param run - how this dialect runs a statement on that instance, and the
|
|
840
|
+
* rows it returns when the statement carries RETURNING: better-sqlite3
|
|
841
|
+
* answers synchronously, the pooled drivers do not
|
|
842
|
+
* @param bindUnmodeled - how a value binds when the model declares no
|
|
843
|
+
* column for it; a declared column binds through its own encoder
|
|
844
|
+
* @returns the context's `update` method
|
|
845
|
+
*/
|
|
846
|
+
protected transactionUpdate(txDb: () => unknown, run: (statement: SQL, returnsRows: boolean) => Promise<Record<string, unknown>[] | undefined> | Record<string, unknown>[] | undefined, bindUnmodeled: (value: unknown) => unknown): TransactionContext["update"];
|
|
847
|
+
/**
|
|
848
|
+
* The columns that identify a row of this table, under both spellings: the
|
|
849
|
+
* SQL name RETURNING reports and the Drizzle property name the query
|
|
850
|
+
* builder's WHERE is addressed by. Columns flagged primary first; failing
|
|
851
|
+
* that, the table-level primary key the dialect's table config carries
|
|
852
|
+
* (`primaryKey({ columns })` leaves each column's flag false), which each
|
|
853
|
+
* adapter reads through its own dialect. Empty when the model declares
|
|
854
|
+
* neither, and the read-back falls back to the caller's predicate.
|
|
855
|
+
*/
|
|
856
|
+
protected identityColumns(tableObj: Record<string, unknown>): Array<{
|
|
857
|
+
jsName: string;
|
|
858
|
+
sqlName: string;
|
|
859
|
+
}>;
|
|
860
|
+
/**
|
|
861
|
+
* How a value binds to a column the model does not declare, on a driver
|
|
862
|
+
* that binds scalars, dates and binary natively: a structured value goes
|
|
863
|
+
* as JSON text, since what node-postgres and mysql2 make of a bare object
|
|
864
|
+
* or array is not JSON. For the adapters to hand `transactionUpdate`.
|
|
865
|
+
*/
|
|
866
|
+
protected bindUnmodeledStructuredAsJson(value: unknown): unknown;
|
|
867
|
+
/**
|
|
868
|
+
* The columns of a table-level `primaryKey({ columns })`, as the dialect's
|
|
869
|
+
* table config reports them. The base class cannot read that config without
|
|
870
|
+
* naming a dialect, so each adapter answers for its own; a dialect that does
|
|
871
|
+
* not answer reports none.
|
|
872
|
+
*/
|
|
873
|
+
protected compositePrimaryKey(_tableObj: Record<string, unknown>): object[];
|
|
874
|
+
/** The registered table object, or the error every CRUD method throws without one. */
|
|
875
|
+
private resolvedTableObject;
|
|
876
|
+
/**
|
|
877
|
+
* The statement `transactionUpdate` runs.
|
|
878
|
+
*
|
|
879
|
+
* @throws when nothing would be written — every key `undefined`, or none at
|
|
880
|
+
* all. The query builder refused that too ("No values to set"), and a
|
|
881
|
+
* patch that names nothing is a caller's mistake rather than a write of
|
|
882
|
+
* nothing.
|
|
883
|
+
*/
|
|
884
|
+
private buildTransactionUpdate;
|
|
885
|
+
/**
|
|
886
|
+
* Whether a transaction's `update` reads the rows it changed back: the
|
|
887
|
+
* caller named columns, or `*`. An empty list, like no option at all, asks
|
|
888
|
+
* for nothing and `update` answers `[]`, as it always has. The rows come
|
|
889
|
+
* from a read of the update's own WHERE on its own transaction, so they are
|
|
890
|
+
* the model's view of the row, decoded as every read is decoded — which is
|
|
891
|
+
* what the query-builder update returned for them, and why the named list
|
|
892
|
+
* selects nothing narrower.
|
|
893
|
+
*/
|
|
894
|
+
private updateReturnsRows;
|
|
809
895
|
/**
|
|
810
896
|
* Classify an error into a DatabaseError.
|
|
811
897
|
*
|
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
var drizzleOrm = require('drizzle-orm');
|
|
4
4
|
|
|
5
5
|
// src/adapter.ts
|
|
6
|
+
|
|
7
|
+
// src/column-kinds.ts
|
|
8
|
+
function isJsonColumn(column) {
|
|
9
|
+
return column.dataType === "json" || column.columnType === "PgJsonb" || column.columnType === "PgJson" || column.columnType === "MySqlJson" || column.columnType === "SQLiteTextJson";
|
|
10
|
+
}
|
|
11
|
+
function isStructuredValue(value) {
|
|
12
|
+
if (value === null || typeof value !== "object") return false;
|
|
13
|
+
if (value instanceof Date) return false;
|
|
14
|
+
if (Buffer.isBuffer(value) || ArrayBuffer.isView(value)) return false;
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
function bindStructuredAsJson(value) {
|
|
18
|
+
return isStructuredValue(value) ? JSON.stringify(value) : value;
|
|
19
|
+
}
|
|
6
20
|
function buildDrizzleOrderBy(columns, orderBy) {
|
|
7
21
|
if (!orderBy?.length) return [];
|
|
8
22
|
return orderBy.flatMap((spec) => {
|
|
@@ -122,9 +136,6 @@ function createTransactionForwarders(delegator, txDb) {
|
|
|
122
136
|
selectOne: async (table, options) => {
|
|
123
137
|
return delegator.selectOne(table, options, txDb());
|
|
124
138
|
},
|
|
125
|
-
update: async (table, data, where, options) => {
|
|
126
|
-
return delegator.update(table, data, where, options, txDb());
|
|
127
|
-
},
|
|
128
139
|
updateCount: async (table, data, where) => {
|
|
129
140
|
return delegator.updateCount(table, data, where, txDb());
|
|
130
141
|
},
|
|
@@ -155,6 +166,74 @@ function createDatabaseError(options) {
|
|
|
155
166
|
if (options.cause !== void 0) error.cause = options.cause;
|
|
156
167
|
return error;
|
|
157
168
|
}
|
|
169
|
+
function isBindableColumn(value) {
|
|
170
|
+
return typeof value === "object" && value !== null && typeof value.name === "string" && typeof value.mapToDriverValue === "function";
|
|
171
|
+
}
|
|
172
|
+
function valueForJsonColumn(value) {
|
|
173
|
+
if (typeof value !== "string") return value;
|
|
174
|
+
try {
|
|
175
|
+
return JSON.parse(value);
|
|
176
|
+
} catch {
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function columnsOf(tableObj) {
|
|
181
|
+
const byName = /* @__PURE__ */ new Map();
|
|
182
|
+
const declared = [];
|
|
183
|
+
for (const [jsName, column] of Object.entries(
|
|
184
|
+
drizzleOrm.getColumns(tableObj)
|
|
185
|
+
)) {
|
|
186
|
+
if (!isBindableColumn(column)) continue;
|
|
187
|
+
byName.set(column.name, column);
|
|
188
|
+
byName.set(jsName, column);
|
|
189
|
+
declared.push(column);
|
|
190
|
+
}
|
|
191
|
+
return { byName, declared };
|
|
192
|
+
}
|
|
193
|
+
function boundValue(value, column, bindUnmodeled) {
|
|
194
|
+
if (drizzleOrm.is(value, drizzleOrm.SQL) || drizzleOrm.is(value, drizzleOrm.Column)) return drizzleOrm.sql`${value}`;
|
|
195
|
+
if (column) {
|
|
196
|
+
return drizzleOrm.sql`${drizzleOrm.sql.param(
|
|
197
|
+
isJsonColumn(column) ? valueForJsonColumn(value) : value,
|
|
198
|
+
column
|
|
199
|
+
)}`;
|
|
200
|
+
}
|
|
201
|
+
return drizzleOrm.sql`${drizzleOrm.sql.param(bindUnmodeled(value))}`;
|
|
202
|
+
}
|
|
203
|
+
function buildUpdateStatement(input) {
|
|
204
|
+
const { byName, declared } = columnsOf(input.tableObj);
|
|
205
|
+
const assignments = /* @__PURE__ */ new Map();
|
|
206
|
+
for (const [key, value] of Object.entries(input.data)) {
|
|
207
|
+
const column = byName.get(key);
|
|
208
|
+
const name = column?.name ?? key;
|
|
209
|
+
if (value === void 0) {
|
|
210
|
+
assignments.delete(name);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
assignments.set(
|
|
214
|
+
name,
|
|
215
|
+
drizzleOrm.sql`${drizzleOrm.sql.identifier(name)} = ${boundValue(value, column, input.bindUnmodeled)}`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (assignments.size === 0) return null;
|
|
219
|
+
for (const column of declared) {
|
|
220
|
+
if (assignments.has(column.name) || column.onUpdateFn === void 0) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const value = column.onUpdateFn();
|
|
224
|
+
assignments.set(
|
|
225
|
+
column.name,
|
|
226
|
+
drizzleOrm.sql`${drizzleOrm.sql.identifier(column.name)} = ${boundValue(value, column, input.bindUnmodeled)}`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const statement = drizzleOrm.sql`UPDATE ${input.tableObj} SET ${drizzleOrm.sql.join([...assignments.values()], drizzleOrm.sql`, `)}`;
|
|
230
|
+
const condition = buildDrizzleWhere(input.tableObj, input.where);
|
|
231
|
+
if (condition) statement.append(drizzleOrm.sql` WHERE ${condition}`);
|
|
232
|
+
if (input.returning && input.returning.length > 0) {
|
|
233
|
+
statement.append(drizzleOrm.sql` RETURNING ${drizzleOrm.sql.join(input.returning, drizzleOrm.sql`, `)}`);
|
|
234
|
+
}
|
|
235
|
+
return statement;
|
|
236
|
+
}
|
|
158
237
|
|
|
159
238
|
// src/adapter.ts
|
|
160
239
|
var DATE_DATA_TYPE = /(?:^|\s)date$/;
|
|
@@ -286,9 +365,7 @@ var DrizzleAdapter = class {
|
|
|
286
365
|
continue;
|
|
287
366
|
const sqlName = colDef.name;
|
|
288
367
|
sqlToJs.set(sqlName, jsName);
|
|
289
|
-
|
|
290
|
-
const columnType = colDef.columnType;
|
|
291
|
-
if (dataType === "json" || columnType === "PgJsonb" || columnType === "PgJson" || columnType === "MySqlJson" || columnType === "SQLiteTextJson") {
|
|
368
|
+
if (isJsonColumn(colDef)) {
|
|
292
369
|
jsonColumns.add(jsName);
|
|
293
370
|
}
|
|
294
371
|
}
|
|
@@ -1458,11 +1535,11 @@ var DrizzleAdapter = class {
|
|
|
1458
1535
|
*/
|
|
1459
1536
|
async tableExists(tableName, schema) {
|
|
1460
1537
|
try {
|
|
1461
|
-
let
|
|
1538
|
+
let sql5;
|
|
1462
1539
|
const params = [];
|
|
1463
1540
|
switch (this.dialect) {
|
|
1464
1541
|
case "postgresql":
|
|
1465
|
-
|
|
1542
|
+
sql5 = `
|
|
1466
1543
|
SELECT EXISTS (
|
|
1467
1544
|
SELECT FROM information_schema.tables
|
|
1468
1545
|
WHERE table_schema = COALESCE($1, (
|
|
@@ -1476,7 +1553,7 @@ var DrizzleAdapter = class {
|
|
|
1476
1553
|
params.push(schema ?? null, tableName);
|
|
1477
1554
|
break;
|
|
1478
1555
|
case "mysql":
|
|
1479
|
-
|
|
1556
|
+
sql5 = `
|
|
1480
1557
|
SELECT COUNT(*) as count
|
|
1481
1558
|
FROM information_schema.tables
|
|
1482
1559
|
WHERE table_schema = DATABASE()
|
|
@@ -1485,7 +1562,7 @@ var DrizzleAdapter = class {
|
|
|
1485
1562
|
params.push(tableName);
|
|
1486
1563
|
break;
|
|
1487
1564
|
case "sqlite":
|
|
1488
|
-
|
|
1565
|
+
sql5 = `
|
|
1489
1566
|
SELECT COUNT(*) as count
|
|
1490
1567
|
FROM sqlite_master
|
|
1491
1568
|
WHERE type = 'table'
|
|
@@ -1501,7 +1578,7 @@ var DrizzleAdapter = class {
|
|
|
1501
1578
|
);
|
|
1502
1579
|
}
|
|
1503
1580
|
const results = await this.executeQuery(
|
|
1504
|
-
|
|
1581
|
+
sql5,
|
|
1505
1582
|
params
|
|
1506
1583
|
);
|
|
1507
1584
|
if (results.length === 0) {
|
|
@@ -1533,11 +1610,11 @@ var DrizzleAdapter = class {
|
|
|
1533
1610
|
*/
|
|
1534
1611
|
async listTables(schema) {
|
|
1535
1612
|
try {
|
|
1536
|
-
let
|
|
1613
|
+
let sql5;
|
|
1537
1614
|
const params = [];
|
|
1538
1615
|
switch (this.dialect) {
|
|
1539
1616
|
case "postgresql":
|
|
1540
|
-
|
|
1617
|
+
sql5 = `
|
|
1541
1618
|
SELECT table_name
|
|
1542
1619
|
FROM information_schema.tables
|
|
1543
1620
|
WHERE table_schema = $1
|
|
@@ -1547,7 +1624,7 @@ var DrizzleAdapter = class {
|
|
|
1547
1624
|
params.push(schema ?? "public");
|
|
1548
1625
|
break;
|
|
1549
1626
|
case "mysql":
|
|
1550
|
-
|
|
1627
|
+
sql5 = `
|
|
1551
1628
|
SELECT table_name AS table_name
|
|
1552
1629
|
FROM information_schema.tables
|
|
1553
1630
|
WHERE table_schema = DATABASE()
|
|
@@ -1556,7 +1633,7 @@ var DrizzleAdapter = class {
|
|
|
1556
1633
|
`;
|
|
1557
1634
|
break;
|
|
1558
1635
|
case "sqlite":
|
|
1559
|
-
|
|
1636
|
+
sql5 = `
|
|
1560
1637
|
SELECT name as table_name
|
|
1561
1638
|
FROM sqlite_master
|
|
1562
1639
|
WHERE type = 'table'
|
|
@@ -1572,7 +1649,7 @@ var DrizzleAdapter = class {
|
|
|
1572
1649
|
);
|
|
1573
1650
|
}
|
|
1574
1651
|
const results = await this.executeQuery(
|
|
1575
|
-
|
|
1652
|
+
sql5,
|
|
1576
1653
|
params
|
|
1577
1654
|
);
|
|
1578
1655
|
return results.map((row) => row.table_name);
|
|
@@ -1638,6 +1715,163 @@ var DrizzleAdapter = class {
|
|
|
1638
1715
|
createTransactionForwarders(txDb) {
|
|
1639
1716
|
return createTransactionForwarders(this, txDb);
|
|
1640
1717
|
}
|
|
1718
|
+
/**
|
|
1719
|
+
* A transaction context's `update`, for each adapter to bind.
|
|
1720
|
+
*
|
|
1721
|
+
* Not forwarded to the pooled `update` the way `select` and `delete` are:
|
|
1722
|
+
* that one goes through the Drizzle query builder, which writes the columns
|
|
1723
|
+
* the runtime MODEL declares, and a transaction's update must reach the
|
|
1724
|
+
* columns the physical TABLE has — the localization transition window
|
|
1725
|
+
* writes a column the model has already moved to a companion table that
|
|
1726
|
+
* does not exist yet. The INSERT half of every transaction context already
|
|
1727
|
+
* builds its own statement for that reason; `update-statement.ts` is the
|
|
1728
|
+
* UPDATE half, spelled once for the three adapters, and this runs it: on
|
|
1729
|
+
* the transaction executor, classified with the operation and table named
|
|
1730
|
+
* as the pooled `update` classifies its failures, and read back through
|
|
1731
|
+
* `select` on the same executor when the caller asked for rows.
|
|
1732
|
+
*
|
|
1733
|
+
* That read is by the rows' IDENTITY where the dialect can report it: on
|
|
1734
|
+
* PostgreSQL and SQLite the statement carries `RETURNING <primary key>`, and
|
|
1735
|
+
* the read-back asks for exactly those rows — so a row another transaction
|
|
1736
|
+
* adds under the same predicate meanwhile is not among them, and an update
|
|
1737
|
+
* whose own write falsifies its predicate still reads its rows back. MySQL
|
|
1738
|
+
* has no RETURNING, so there the read re-runs the predicate, as the pooled
|
|
1739
|
+
* `update` always has on that dialect; a table with no primary key in its
|
|
1740
|
+
* model reads back the same way.
|
|
1741
|
+
*
|
|
1742
|
+
* @param txDb - thunk returning the transaction-bound Drizzle instance
|
|
1743
|
+
* @param run - how this dialect runs a statement on that instance, and the
|
|
1744
|
+
* rows it returns when the statement carries RETURNING: better-sqlite3
|
|
1745
|
+
* answers synchronously, the pooled drivers do not
|
|
1746
|
+
* @param bindUnmodeled - how a value binds when the model declares no
|
|
1747
|
+
* column for it; a declared column binds through its own encoder
|
|
1748
|
+
* @returns the context's `update` method
|
|
1749
|
+
*/
|
|
1750
|
+
transactionUpdate(txDb, run, bindUnmodeled) {
|
|
1751
|
+
return async (table, data, where, options) => {
|
|
1752
|
+
const wantsRows = this.updateReturnsRows(options?.returning);
|
|
1753
|
+
const tableObj = this.resolvedTableObject(table);
|
|
1754
|
+
const identity = wantsRows && this.getCapabilities().supportsReturning ? this.identityColumns(tableObj) : [];
|
|
1755
|
+
let reported;
|
|
1756
|
+
try {
|
|
1757
|
+
reported = await run(
|
|
1758
|
+
this.buildTransactionUpdate(tableObj, table, data, where, {
|
|
1759
|
+
bindUnmodeled,
|
|
1760
|
+
returning: identity.map((key) => drizzleOrm.sql`${drizzleOrm.sql.identifier(key.sqlName)}`)
|
|
1761
|
+
}),
|
|
1762
|
+
identity.length > 0
|
|
1763
|
+
);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
throw this.handleQueryError(error, "update", table);
|
|
1766
|
+
}
|
|
1767
|
+
if (!wantsRows) return [];
|
|
1768
|
+
if (identity.length === 0 || !Array.isArray(reported)) {
|
|
1769
|
+
return this.select(table, { where }, txDb());
|
|
1770
|
+
}
|
|
1771
|
+
const rows = [];
|
|
1772
|
+
for (let at = 0; at < reported.length; at += IDENTITY_READ_BACK_CHUNK) {
|
|
1773
|
+
rows.push(
|
|
1774
|
+
...await this.select(
|
|
1775
|
+
table,
|
|
1776
|
+
{
|
|
1777
|
+
where: rowsByIdentity(
|
|
1778
|
+
identity,
|
|
1779
|
+
reported.slice(at, at + IDENTITY_READ_BACK_CHUNK)
|
|
1780
|
+
)
|
|
1781
|
+
},
|
|
1782
|
+
txDb()
|
|
1783
|
+
)
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
return rows;
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* The columns that identify a row of this table, under both spellings: the
|
|
1791
|
+
* SQL name RETURNING reports and the Drizzle property name the query
|
|
1792
|
+
* builder's WHERE is addressed by. Columns flagged primary first; failing
|
|
1793
|
+
* that, the table-level primary key the dialect's table config carries
|
|
1794
|
+
* (`primaryKey({ columns })` leaves each column's flag false), which each
|
|
1795
|
+
* adapter reads through its own dialect. Empty when the model declares
|
|
1796
|
+
* neither, and the read-back falls back to the caller's predicate.
|
|
1797
|
+
*/
|
|
1798
|
+
identityColumns(tableObj) {
|
|
1799
|
+
const columns = Object.entries(drizzleOrm.getColumns(tableObj));
|
|
1800
|
+
const flagged = columns.filter(([, c]) => c.primary === true);
|
|
1801
|
+
const members = flagged.length > 0 ? flagged : columns.filter(
|
|
1802
|
+
([, c]) => this.compositePrimaryKey(tableObj).includes(c)
|
|
1803
|
+
);
|
|
1804
|
+
return members.flatMap(
|
|
1805
|
+
([jsName, c]) => typeof c.name === "string" ? [{ jsName, sqlName: c.name }] : []
|
|
1806
|
+
);
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* How a value binds to a column the model does not declare, on a driver
|
|
1810
|
+
* that binds scalars, dates and binary natively: a structured value goes
|
|
1811
|
+
* as JSON text, since what node-postgres and mysql2 make of a bare object
|
|
1812
|
+
* or array is not JSON. For the adapters to hand `transactionUpdate`.
|
|
1813
|
+
*/
|
|
1814
|
+
bindUnmodeledStructuredAsJson(value) {
|
|
1815
|
+
return bindStructuredAsJson(value);
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* The columns of a table-level `primaryKey({ columns })`, as the dialect's
|
|
1819
|
+
* table config reports them. The base class cannot read that config without
|
|
1820
|
+
* naming a dialect, so each adapter answers for its own; a dialect that does
|
|
1821
|
+
* not answer reports none.
|
|
1822
|
+
*/
|
|
1823
|
+
compositePrimaryKey(_tableObj) {
|
|
1824
|
+
return [];
|
|
1825
|
+
}
|
|
1826
|
+
/** The registered table object, or the error every CRUD method throws without one. */
|
|
1827
|
+
resolvedTableObject(table) {
|
|
1828
|
+
const tableObj = this.getTableObject(table);
|
|
1829
|
+
if (!tableObj || typeof tableObj !== "object") {
|
|
1830
|
+
throw this.createDatabaseError(
|
|
1831
|
+
"query",
|
|
1832
|
+
`Table "${table}" not found in schema registry. Ensure setTableResolver() has been called during boot.`,
|
|
1833
|
+
void 0
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
return tableObj;
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* The statement `transactionUpdate` runs.
|
|
1840
|
+
*
|
|
1841
|
+
* @throws when nothing would be written — every key `undefined`, or none at
|
|
1842
|
+
* all. The query builder refused that too ("No values to set"), and a
|
|
1843
|
+
* patch that names nothing is a caller's mistake rather than a write of
|
|
1844
|
+
* nothing.
|
|
1845
|
+
*/
|
|
1846
|
+
buildTransactionUpdate(tableObj, table, data, where, binding) {
|
|
1847
|
+
const statement = buildUpdateStatement({
|
|
1848
|
+
tableObj,
|
|
1849
|
+
data,
|
|
1850
|
+
where,
|
|
1851
|
+
bindUnmodeled: binding.bindUnmodeled,
|
|
1852
|
+
returning: binding.returning
|
|
1853
|
+
});
|
|
1854
|
+
if (!statement) {
|
|
1855
|
+
throw this.createDatabaseError(
|
|
1856
|
+
"query",
|
|
1857
|
+
`No values to set: the update of "${table}" names no column with a defined value.`,
|
|
1858
|
+
void 0
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
return statement;
|
|
1862
|
+
}
|
|
1863
|
+
/**
|
|
1864
|
+
* Whether a transaction's `update` reads the rows it changed back: the
|
|
1865
|
+
* caller named columns, or `*`. An empty list, like no option at all, asks
|
|
1866
|
+
* for nothing and `update` answers `[]`, as it always has. The rows come
|
|
1867
|
+
* from a read of the update's own WHERE on its own transaction, so they are
|
|
1868
|
+
* the model's view of the row, decoded as every read is decoded — which is
|
|
1869
|
+
* what the query-builder update returned for them, and why the named list
|
|
1870
|
+
* selects nothing narrower.
|
|
1871
|
+
*/
|
|
1872
|
+
updateReturnsRows(returning) {
|
|
1873
|
+
return returning !== void 0 && !(Array.isArray(returning) && returning.length === 0);
|
|
1874
|
+
}
|
|
1641
1875
|
/**
|
|
1642
1876
|
* Classify an error into a DatabaseError.
|
|
1643
1877
|
*
|
|
@@ -1650,14 +1884,14 @@ var DrizzleAdapter = class {
|
|
|
1650
1884
|
*
|
|
1651
1885
|
* @protected
|
|
1652
1886
|
*/
|
|
1653
|
-
classifyError(error,
|
|
1887
|
+
classifyError(error, sql5) {
|
|
1654
1888
|
if (isDatabaseError(error)) {
|
|
1655
1889
|
return error;
|
|
1656
1890
|
}
|
|
1657
1891
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1658
1892
|
return this.createDatabaseError(
|
|
1659
1893
|
"query",
|
|
1660
|
-
|
|
1894
|
+
sql5 ? `Query failed: ${errorMessage}` : errorMessage,
|
|
1661
1895
|
error instanceof Error ? error : void 0
|
|
1662
1896
|
);
|
|
1663
1897
|
}
|
|
@@ -1678,8 +1912,9 @@ var DrizzleAdapter = class {
|
|
|
1678
1912
|
*/
|
|
1679
1913
|
handleQueryError(error, operation, table) {
|
|
1680
1914
|
const dbError = this.classifyError(error);
|
|
1681
|
-
|
|
1682
|
-
|
|
1915
|
+
const context = `${operation} operation failed on table '${table}'`;
|
|
1916
|
+
if (!dbError.message.includes(context)) {
|
|
1917
|
+
dbError.message = `${context}: ${dbError.message}`;
|
|
1683
1918
|
}
|
|
1684
1919
|
if (!dbError.table) {
|
|
1685
1920
|
dbError.table = table;
|
|
@@ -1687,6 +1922,34 @@ var DrizzleAdapter = class {
|
|
|
1687
1922
|
return dbError;
|
|
1688
1923
|
}
|
|
1689
1924
|
};
|
|
1925
|
+
var IDENTITY_READ_BACK_CHUNK = 200;
|
|
1926
|
+
function rowsByIdentity(identity, rows) {
|
|
1927
|
+
if (identity.length === 1) {
|
|
1928
|
+
const [key] = identity;
|
|
1929
|
+
return {
|
|
1930
|
+
and: [
|
|
1931
|
+
{
|
|
1932
|
+
column: key.jsName,
|
|
1933
|
+
op: "IN",
|
|
1934
|
+
value: rows.map((row) => row[key.sqlName])
|
|
1935
|
+
}
|
|
1936
|
+
]
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
return {
|
|
1940
|
+
or: rows.map(
|
|
1941
|
+
(row) => ({
|
|
1942
|
+
and: identity.map(
|
|
1943
|
+
(key) => ({
|
|
1944
|
+
column: key.jsName,
|
|
1945
|
+
op: "=",
|
|
1946
|
+
value: row[key.sqlName]
|
|
1947
|
+
})
|
|
1948
|
+
)
|
|
1949
|
+
})
|
|
1950
|
+
)
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1690
1953
|
var ALLOWED_DEFAULT_SQL_EXPRESSIONS = /* @__PURE__ */ new Set([
|
|
1691
1954
|
"current_timestamp",
|
|
1692
1955
|
"now()",
|