drizzle-databend 0.1.9 → 0.1.10
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/client.d.ts +8 -4
- package/dist/index.mjs +78 -34
- package/dist/session.d.ts +3 -2
- package/package.json +5 -2
- package/src/client.ts +71 -27
- package/src/dialect.ts +11 -1
- package/src/session.ts +10 -10
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Connection } from 'databend-driver';
|
|
2
|
+
import type { QueryTypingsValue } from 'drizzle-orm';
|
|
2
3
|
export interface DatabendConnectionPool {
|
|
3
4
|
acquire(): Promise<Connection>;
|
|
4
5
|
release(connection: Connection): void | Promise<void>;
|
|
@@ -14,9 +15,12 @@ export declare function isPool(client: DatabendClientLike): client is DatabendCo
|
|
|
14
15
|
/**
|
|
15
16
|
* Convert Drizzle param array to a JSON value accepted by databend-driver's Params.
|
|
16
17
|
* Databend's Params is serde_json::Value, so we pass an array of JSON-serializable values.
|
|
18
|
+
*
|
|
19
|
+
* The databend-driver does client-side parameter substitution with no string escaping,
|
|
20
|
+
* so we must pre-escape single quotes here (SQL standard '' escaping).
|
|
17
21
|
*/
|
|
18
|
-
export declare function prepareParams(params: unknown[]): unknown[];
|
|
19
|
-
export declare function executeOnClient(client: DatabendClientLike, query: string, params: unknown[]): Promise<RowData[]>;
|
|
20
|
-
export declare function executeArraysOnClient(client: DatabendClientLike, query: string, params: unknown[]): Promise<ExecuteArraysResult>;
|
|
21
|
-
export declare function execOnClient(client: DatabendClientLike, query: string, params: unknown[]): Promise<number>;
|
|
22
|
+
export declare function prepareParams(params: unknown[], typings?: QueryTypingsValue[]): unknown[];
|
|
23
|
+
export declare function executeOnClient(client: DatabendClientLike, query: string, params: unknown[], typings?: QueryTypingsValue[]): Promise<RowData[]>;
|
|
24
|
+
export declare function executeArraysOnClient(client: DatabendClientLike, query: string, params: unknown[], typings?: QueryTypingsValue[]): Promise<ExecuteArraysResult>;
|
|
25
|
+
export declare function execOnClient(client: DatabendClientLike, query: string, params: unknown[], typings?: QueryTypingsValue[]): Promise<number>;
|
|
22
26
|
export declare function closeClientConnection(connection: Connection): Promise<void>;
|
package/dist/index.mjs
CHANGED
|
@@ -182,17 +182,47 @@ import { TransactionRollbackError } from "drizzle-orm/errors";
|
|
|
182
182
|
function isPool(client) {
|
|
183
183
|
return typeof client.acquire === "function";
|
|
184
184
|
}
|
|
185
|
-
function prepareParams(params) {
|
|
186
|
-
return params.map((param) => {
|
|
185
|
+
function prepareParams(params, typings) {
|
|
186
|
+
return params.map((param, i) => {
|
|
187
187
|
if (param === undefined)
|
|
188
188
|
return null;
|
|
189
189
|
if (param instanceof Date)
|
|
190
|
-
return param.toISOString();
|
|
190
|
+
return param.toISOString().replace(/'/g, "''");
|
|
191
191
|
if (typeof param === "bigint")
|
|
192
192
|
return param.toString();
|
|
193
|
+
if (typeof param === "string") {
|
|
194
|
+
const typing = typings?.[i];
|
|
195
|
+
if (typing === "decimal" && /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(param)) {
|
|
196
|
+
return Number(param);
|
|
197
|
+
}
|
|
198
|
+
return param.replace(/'/g, "''");
|
|
199
|
+
}
|
|
200
|
+
if (typeof param === "object" && param !== null) {
|
|
201
|
+
return JSON.stringify(param).replace(/'/g, "''");
|
|
202
|
+
}
|
|
193
203
|
return param;
|
|
194
204
|
});
|
|
195
205
|
}
|
|
206
|
+
function isTransientError(error) {
|
|
207
|
+
if (!(error instanceof Error))
|
|
208
|
+
return false;
|
|
209
|
+
const msg = error.message?.toLowerCase() ?? "";
|
|
210
|
+
return msg.includes("connection closed") || msg.includes("econnreset") || msg.includes("epipe") || msg.includes("socket hang up") || msg.includes("connection refused");
|
|
211
|
+
}
|
|
212
|
+
async function withRetry(fn, maxRetries = 2) {
|
|
213
|
+
let lastError;
|
|
214
|
+
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
215
|
+
try {
|
|
216
|
+
return await fn();
|
|
217
|
+
} catch (error) {
|
|
218
|
+
lastError = error;
|
|
219
|
+
if (!isTransientError(error) || attempt === maxRetries)
|
|
220
|
+
throw error;
|
|
221
|
+
await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
throw lastError;
|
|
225
|
+
}
|
|
196
226
|
function deduplicateColumns(columns) {
|
|
197
227
|
const counts = new Map;
|
|
198
228
|
let hasDuplicates = false;
|
|
@@ -214,16 +244,18 @@ function deduplicateColumns(columns) {
|
|
|
214
244
|
return count === 0 ? column : `${column}_${count}`;
|
|
215
245
|
});
|
|
216
246
|
}
|
|
217
|
-
async function executeOnClient(client, query, params) {
|
|
247
|
+
async function executeOnClient(client, query, params, typings) {
|
|
218
248
|
if (isPool(client)) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
249
|
+
return withRetry(async () => {
|
|
250
|
+
const connection = await client.acquire();
|
|
251
|
+
try {
|
|
252
|
+
return await executeOnClient(connection, query, params, typings);
|
|
253
|
+
} finally {
|
|
254
|
+
await client.release(connection);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
225
257
|
}
|
|
226
|
-
const prepared = prepareParams(params);
|
|
258
|
+
const prepared = prepareParams(params, typings);
|
|
227
259
|
const paramValue = prepared.length > 0 ? prepared : undefined;
|
|
228
260
|
const rows = await client.queryAll(query, paramValue);
|
|
229
261
|
if (!rows || rows.length === 0) {
|
|
@@ -231,16 +263,18 @@ async function executeOnClient(client, query, params) {
|
|
|
231
263
|
}
|
|
232
264
|
return rows.map((r) => r.data());
|
|
233
265
|
}
|
|
234
|
-
async function executeArraysOnClient(client, query, params) {
|
|
266
|
+
async function executeArraysOnClient(client, query, params, typings) {
|
|
235
267
|
if (isPool(client)) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
268
|
+
return withRetry(async () => {
|
|
269
|
+
const connection = await client.acquire();
|
|
270
|
+
try {
|
|
271
|
+
return await executeArraysOnClient(connection, query, params, typings);
|
|
272
|
+
} finally {
|
|
273
|
+
await client.release(connection);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
242
276
|
}
|
|
243
|
-
const prepared = prepareParams(params);
|
|
277
|
+
const prepared = prepareParams(params, typings);
|
|
244
278
|
const paramValue = prepared.length > 0 ? prepared : undefined;
|
|
245
279
|
const iter = await client.queryIter(query, paramValue);
|
|
246
280
|
const schema = iter.schema();
|
|
@@ -257,16 +291,18 @@ async function executeArraysOnClient(client, query, params) {
|
|
|
257
291
|
}
|
|
258
292
|
return { columns, rows };
|
|
259
293
|
}
|
|
260
|
-
async function execOnClient(client, query, params) {
|
|
294
|
+
async function execOnClient(client, query, params, typings) {
|
|
261
295
|
if (isPool(client)) {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
296
|
+
return withRetry(async () => {
|
|
297
|
+
const connection = await client.acquire();
|
|
298
|
+
try {
|
|
299
|
+
return await execOnClient(connection, query, params, typings);
|
|
300
|
+
} finally {
|
|
301
|
+
await client.release(connection);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
268
304
|
}
|
|
269
|
-
const prepared = prepareParams(params);
|
|
305
|
+
const prepared = prepareParams(params, typings);
|
|
270
306
|
const paramValue = prepared.length > 0 ? prepared : undefined;
|
|
271
307
|
return await client.exec(query, paramValue);
|
|
272
308
|
}
|
|
@@ -285,8 +321,9 @@ class DatabendPreparedQuery extends PgPreparedQuery {
|
|
|
285
321
|
fields;
|
|
286
322
|
_isResponseInArrayMode;
|
|
287
323
|
customResultMapper;
|
|
324
|
+
typings;
|
|
288
325
|
static [entityKind] = "DatabendPreparedQuery";
|
|
289
|
-
constructor(client, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper) {
|
|
326
|
+
constructor(client, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, typings) {
|
|
290
327
|
super({ sql: queryString, params });
|
|
291
328
|
this.client = client;
|
|
292
329
|
this.queryString = queryString;
|
|
@@ -295,19 +332,20 @@ class DatabendPreparedQuery extends PgPreparedQuery {
|
|
|
295
332
|
this.fields = fields;
|
|
296
333
|
this._isResponseInArrayMode = _isResponseInArrayMode;
|
|
297
334
|
this.customResultMapper = customResultMapper;
|
|
335
|
+
this.typings = typings;
|
|
298
336
|
}
|
|
299
337
|
async execute(placeholderValues = {}) {
|
|
300
|
-
const params =
|
|
338
|
+
const params = fillPlaceholders(this.params, placeholderValues);
|
|
301
339
|
this.logger.logQuery(this.queryString, params);
|
|
302
|
-
const { fields, joinsNotNullableMap, customResultMapper } = this;
|
|
340
|
+
const { fields, joinsNotNullableMap, customResultMapper, typings } = this;
|
|
303
341
|
if (fields) {
|
|
304
|
-
const { rows: rows2 } = await executeArraysOnClient(this.client, this.queryString, params);
|
|
342
|
+
const { rows: rows2 } = await executeArraysOnClient(this.client, this.queryString, params, typings);
|
|
305
343
|
if (rows2.length === 0) {
|
|
306
344
|
return [];
|
|
307
345
|
}
|
|
308
346
|
return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
|
|
309
347
|
}
|
|
310
|
-
const rows = await executeOnClient(this.client, this.queryString, params);
|
|
348
|
+
const rows = await executeOnClient(this.client, this.queryString, params, typings);
|
|
311
349
|
return rows;
|
|
312
350
|
}
|
|
313
351
|
all(placeholderValues = {}) {
|
|
@@ -335,7 +373,7 @@ class DatabendSession extends PgSession {
|
|
|
335
373
|
this.logger = options.logger ?? new NoopLogger;
|
|
336
374
|
}
|
|
337
375
|
prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
|
|
338
|
-
return new DatabendPreparedQuery(this.client, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper);
|
|
376
|
+
return new DatabendPreparedQuery(this.client, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, query.typings);
|
|
339
377
|
}
|
|
340
378
|
async transaction(transaction, config) {
|
|
341
379
|
let pinnedConnection;
|
|
@@ -426,10 +464,16 @@ class DatabendTransaction extends PgTransaction {
|
|
|
426
464
|
// src/dialect.ts
|
|
427
465
|
import { entityKind as entityKind2, is as is2 } from "drizzle-orm/entity";
|
|
428
466
|
import {
|
|
467
|
+
PgBigInt53,
|
|
468
|
+
PgBigInt64,
|
|
429
469
|
PgDate as PgDate2,
|
|
430
470
|
PgDateString as PgDateString2,
|
|
431
471
|
PgDialect,
|
|
472
|
+
PgDoublePrecision,
|
|
473
|
+
PgInteger,
|
|
432
474
|
PgNumeric,
|
|
475
|
+
PgReal,
|
|
476
|
+
PgSmallInt,
|
|
433
477
|
PgTime as PgTime2,
|
|
434
478
|
PgTimestamp as PgTimestamp2,
|
|
435
479
|
PgTimestampString as PgTimestampString2,
|
|
@@ -475,7 +519,7 @@ class DatabendDialect extends PgDialect {
|
|
|
475
519
|
});
|
|
476
520
|
}
|
|
477
521
|
prepareTyping(encoder) {
|
|
478
|
-
if (is2(encoder, PgNumeric)) {
|
|
522
|
+
if (is2(encoder, PgNumeric) || is2(encoder, PgInteger) || is2(encoder, PgSmallInt) || is2(encoder, PgReal) || is2(encoder, PgDoublePrecision) || is2(encoder, PgBigInt53) || is2(encoder, PgBigInt64)) {
|
|
479
523
|
return "decimal";
|
|
480
524
|
} else if (is2(encoder, PgTime2)) {
|
|
481
525
|
return "time";
|
package/dist/session.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { SelectedFieldsOrdered } from 'drizzle-orm/pg-core/query-builders/s
|
|
|
5
5
|
import type { PgTransactionConfig, PreparedQueryConfig, PgQueryResultHKT } from 'drizzle-orm/pg-core/session';
|
|
6
6
|
import { PgPreparedQuery, PgSession } from 'drizzle-orm/pg-core/session';
|
|
7
7
|
import type { RelationalSchemaConfig, TablesRelationalConfig } from 'drizzle-orm/relations';
|
|
8
|
-
import { type Query, SQL } from 'drizzle-orm/sql/sql';
|
|
8
|
+
import { type Query, type QueryTypingsValue, SQL } from 'drizzle-orm/sql/sql';
|
|
9
9
|
import type { Assume } from 'drizzle-orm/utils';
|
|
10
10
|
import type { DatabendDialect } from './dialect.ts';
|
|
11
11
|
import type { DatabendClientLike, RowData } from './client.ts';
|
|
@@ -18,8 +18,9 @@ export declare class DatabendPreparedQuery<T extends PreparedQueryConfig> extend
|
|
|
18
18
|
private fields;
|
|
19
19
|
private _isResponseInArrayMode;
|
|
20
20
|
private customResultMapper;
|
|
21
|
+
private typings?;
|
|
21
22
|
static readonly [entityKind]: string;
|
|
22
|
-
constructor(client: DatabendClientLike, queryString: string, params: unknown[], logger: Logger, fields: SelectedFieldsOrdered | undefined, _isResponseInArrayMode: boolean, customResultMapper: ((rows: unknown[][]) => T['execute']) | undefined);
|
|
23
|
+
constructor(client: DatabendClientLike, queryString: string, params: unknown[], logger: Logger, fields: SelectedFieldsOrdered | undefined, _isResponseInArrayMode: boolean, customResultMapper: ((rows: unknown[][]) => T['execute']) | undefined, typings?: QueryTypingsValue[] | undefined);
|
|
23
24
|
execute(placeholderValues?: Record<string, unknown> | undefined): Promise<T['execute']>;
|
|
24
25
|
all(placeholderValues?: Record<string, unknown> | undefined): Promise<T['all']>;
|
|
25
26
|
isResponseInArrayMode(): boolean;
|
package/package.json
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
"module": "./dist/index.mjs",
|
|
4
4
|
"main": "./dist/index.mjs",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.10",
|
|
7
7
|
"description": "A drizzle ORM driver for use with Databend. Based on drizzle's Postgres driver surface.",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "bun build --target=node ./src/index.ts --outfile=./dist/index.mjs --packages=external && bun run build:declarations",
|
|
11
11
|
"build:declarations": "tsc --emitDeclarationOnly --project tsconfig.types.json",
|
|
12
|
-
"test": "vitest"
|
|
12
|
+
"test": "vitest",
|
|
13
|
+
"db:start": "docker start databend 2>/dev/null || docker run -d --name databend -p 8000:8000 datafuselabs/databend",
|
|
14
|
+
"db:stop": "docker stop databend",
|
|
15
|
+
"db:restart": "docker restart databend"
|
|
13
16
|
},
|
|
14
17
|
"peerDependencies": {
|
|
15
18
|
"databend-driver": ">=0.33.0",
|
package/src/client.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Connection } from 'databend-driver';
|
|
2
|
+
import type { QueryTypingsValue } from 'drizzle-orm';
|
|
2
3
|
|
|
3
4
|
export interface DatabendConnectionPool {
|
|
4
5
|
acquire(): Promise<Connection>;
|
|
@@ -20,16 +21,50 @@ export function isPool(
|
|
|
20
21
|
/**
|
|
21
22
|
* Convert Drizzle param array to a JSON value accepted by databend-driver's Params.
|
|
22
23
|
* Databend's Params is serde_json::Value, so we pass an array of JSON-serializable values.
|
|
24
|
+
*
|
|
25
|
+
* The databend-driver does client-side parameter substitution with no string escaping,
|
|
26
|
+
* so we must pre-escape single quotes here (SQL standard '' escaping).
|
|
23
27
|
*/
|
|
24
|
-
export function prepareParams(params: unknown[]): unknown[] {
|
|
25
|
-
return params.map((param) => {
|
|
28
|
+
export function prepareParams(params: unknown[], typings?: QueryTypingsValue[]): unknown[] {
|
|
29
|
+
return params.map((param, i) => {
|
|
26
30
|
if (param === undefined) return null;
|
|
27
|
-
if (param instanceof Date) return param.toISOString();
|
|
31
|
+
if (param instanceof Date) return param.toISOString().replace(/'/g, "''");
|
|
28
32
|
if (typeof param === 'bigint') return param.toString();
|
|
33
|
+
if (typeof param === 'string') {
|
|
34
|
+
const typing = typings?.[i];
|
|
35
|
+
if (typing === 'decimal' && /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(param)) {
|
|
36
|
+
return Number(param);
|
|
37
|
+
}
|
|
38
|
+
return param.replace(/'/g, "''");
|
|
39
|
+
}
|
|
40
|
+
if (typeof param === 'object' && param !== null) {
|
|
41
|
+
return JSON.stringify(param).replace(/'/g, "''");
|
|
42
|
+
}
|
|
29
43
|
return param;
|
|
30
44
|
});
|
|
31
45
|
}
|
|
32
46
|
|
|
47
|
+
function isTransientError(error: unknown): boolean {
|
|
48
|
+
if (!(error instanceof Error)) return false;
|
|
49
|
+
const msg = error.message?.toLowerCase() ?? '';
|
|
50
|
+
return msg.includes('connection closed') || msg.includes('econnreset') ||
|
|
51
|
+
msg.includes('epipe') || msg.includes('socket hang up') ||
|
|
52
|
+
msg.includes('connection refused');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 2): Promise<T> {
|
|
56
|
+
let lastError: unknown;
|
|
57
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
58
|
+
try { return await fn(); }
|
|
59
|
+
catch (error) {
|
|
60
|
+
lastError = error;
|
|
61
|
+
if (!isTransientError(error) || attempt === maxRetries) throw error;
|
|
62
|
+
await new Promise(r => setTimeout(r, 100 * Math.pow(2, attempt)));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw lastError;
|
|
66
|
+
}
|
|
67
|
+
|
|
33
68
|
function deduplicateColumns(columns: string[]): string[] {
|
|
34
69
|
const counts = new Map<string, number>();
|
|
35
70
|
let hasDuplicates = false;
|
|
@@ -58,18 +93,21 @@ function deduplicateColumns(columns: string[]): string[] {
|
|
|
58
93
|
export async function executeOnClient(
|
|
59
94
|
client: DatabendClientLike,
|
|
60
95
|
query: string,
|
|
61
|
-
params: unknown[]
|
|
96
|
+
params: unknown[],
|
|
97
|
+
typings?: QueryTypingsValue[]
|
|
62
98
|
): Promise<RowData[]> {
|
|
63
99
|
if (isPool(client)) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
100
|
+
return withRetry(async () => {
|
|
101
|
+
const connection = await client.acquire();
|
|
102
|
+
try {
|
|
103
|
+
return await executeOnClient(connection, query, params, typings);
|
|
104
|
+
} finally {
|
|
105
|
+
await client.release(connection);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
70
108
|
}
|
|
71
109
|
|
|
72
|
-
const prepared = prepareParams(params);
|
|
110
|
+
const prepared = prepareParams(params, typings);
|
|
73
111
|
const paramValue = prepared.length > 0 ? prepared : undefined;
|
|
74
112
|
const rows = await client.queryAll(query, paramValue);
|
|
75
113
|
|
|
@@ -83,18 +121,21 @@ export async function executeOnClient(
|
|
|
83
121
|
export async function executeArraysOnClient(
|
|
84
122
|
client: DatabendClientLike,
|
|
85
123
|
query: string,
|
|
86
|
-
params: unknown[]
|
|
124
|
+
params: unknown[],
|
|
125
|
+
typings?: QueryTypingsValue[]
|
|
87
126
|
): Promise<ExecuteArraysResult> {
|
|
88
127
|
if (isPool(client)) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
128
|
+
return withRetry(async () => {
|
|
129
|
+
const connection = await client.acquire();
|
|
130
|
+
try {
|
|
131
|
+
return await executeArraysOnClient(connection, query, params, typings);
|
|
132
|
+
} finally {
|
|
133
|
+
await client.release(connection);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
95
136
|
}
|
|
96
137
|
|
|
97
|
-
const prepared = prepareParams(params);
|
|
138
|
+
const prepared = prepareParams(params, typings);
|
|
98
139
|
const paramValue = prepared.length > 0 ? prepared : undefined;
|
|
99
140
|
const iter = await client.queryIter(query, paramValue);
|
|
100
141
|
const schema = iter.schema();
|
|
@@ -115,18 +156,21 @@ export async function executeArraysOnClient(
|
|
|
115
156
|
export async function execOnClient(
|
|
116
157
|
client: DatabendClientLike,
|
|
117
158
|
query: string,
|
|
118
|
-
params: unknown[]
|
|
159
|
+
params: unknown[],
|
|
160
|
+
typings?: QueryTypingsValue[]
|
|
119
161
|
): Promise<number> {
|
|
120
162
|
if (isPool(client)) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
163
|
+
return withRetry(async () => {
|
|
164
|
+
const connection = await client.acquire();
|
|
165
|
+
try {
|
|
166
|
+
return await execOnClient(connection, query, params, typings);
|
|
167
|
+
} finally {
|
|
168
|
+
await client.release(connection);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
127
171
|
}
|
|
128
172
|
|
|
129
|
-
const prepared = prepareParams(params);
|
|
173
|
+
const prepared = prepareParams(params, typings);
|
|
130
174
|
const paramValue = prepared.length > 0 ? prepared : undefined;
|
|
131
175
|
return await client.exec(query, paramValue);
|
|
132
176
|
}
|
package/src/dialect.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { entityKind, is } from 'drizzle-orm/entity';
|
|
2
2
|
import type { MigrationConfig, MigrationMeta } from 'drizzle-orm/migrator';
|
|
3
3
|
import {
|
|
4
|
+
PgBigInt53,
|
|
5
|
+
PgBigInt64,
|
|
4
6
|
PgDate,
|
|
5
7
|
PgDateString,
|
|
6
8
|
PgDialect,
|
|
9
|
+
PgDoublePrecision,
|
|
10
|
+
PgInteger,
|
|
7
11
|
PgNumeric,
|
|
12
|
+
PgReal,
|
|
8
13
|
PgSession,
|
|
14
|
+
PgSmallInt,
|
|
9
15
|
PgTime,
|
|
10
16
|
PgTimestamp,
|
|
11
17
|
PgTimestampString,
|
|
@@ -92,7 +98,11 @@ export class DatabendDialect extends PgDialect {
|
|
|
92
98
|
override prepareTyping(
|
|
93
99
|
encoder: DriverValueEncoder<unknown, unknown>
|
|
94
100
|
): QueryTypingsValue {
|
|
95
|
-
if (
|
|
101
|
+
if (
|
|
102
|
+
is(encoder, PgNumeric) || is(encoder, PgInteger) || is(encoder, PgSmallInt)
|
|
103
|
+
|| is(encoder, PgReal) || is(encoder, PgDoublePrecision)
|
|
104
|
+
|| is(encoder, PgBigInt53) || is(encoder, PgBigInt64)
|
|
105
|
+
) {
|
|
96
106
|
return 'decimal';
|
|
97
107
|
} else if (is(encoder, PgTime)) {
|
|
98
108
|
return 'time';
|
package/src/session.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
RelationalSchemaConfig,
|
|
14
14
|
TablesRelationalConfig,
|
|
15
15
|
} from 'drizzle-orm/relations';
|
|
16
|
-
import { fillPlaceholders, type Query, SQL, sql } from 'drizzle-orm/sql/sql';
|
|
16
|
+
import { fillPlaceholders, type Query, type QueryTypingsValue, SQL, sql } from 'drizzle-orm/sql/sql';
|
|
17
17
|
import type { Assume } from 'drizzle-orm/utils';
|
|
18
18
|
import { mapResultRow } from './sql/result-mapper.ts';
|
|
19
19
|
import { TransactionRollbackError } from 'drizzle-orm/errors';
|
|
@@ -26,7 +26,6 @@ import type {
|
|
|
26
26
|
import {
|
|
27
27
|
executeArraysOnClient,
|
|
28
28
|
executeOnClient,
|
|
29
|
-
prepareParams,
|
|
30
29
|
isPool,
|
|
31
30
|
} from './client.ts';
|
|
32
31
|
import type { Connection } from 'databend-driver';
|
|
@@ -47,7 +46,8 @@ export class DatabendPreparedQuery<
|
|
|
47
46
|
private _isResponseInArrayMode: boolean,
|
|
48
47
|
private customResultMapper:
|
|
49
48
|
| ((rows: unknown[][]) => T['execute'])
|
|
50
|
-
| undefined
|
|
49
|
+
| undefined,
|
|
50
|
+
private typings?: QueryTypingsValue[]
|
|
51
51
|
) {
|
|
52
52
|
super({ sql: queryString, params });
|
|
53
53
|
}
|
|
@@ -55,19 +55,18 @@ export class DatabendPreparedQuery<
|
|
|
55
55
|
async execute(
|
|
56
56
|
placeholderValues: Record<string, unknown> | undefined = {}
|
|
57
57
|
): Promise<T['execute']> {
|
|
58
|
-
const params =
|
|
59
|
-
fillPlaceholders(this.params, placeholderValues)
|
|
60
|
-
);
|
|
58
|
+
const params = fillPlaceholders(this.params, placeholderValues);
|
|
61
59
|
this.logger.logQuery(this.queryString, params);
|
|
62
60
|
|
|
63
|
-
const { fields, joinsNotNullableMap, customResultMapper } =
|
|
61
|
+
const { fields, joinsNotNullableMap, customResultMapper, typings } =
|
|
64
62
|
this as typeof this & { joinsNotNullableMap?: Record<string, boolean> };
|
|
65
63
|
|
|
66
64
|
if (fields) {
|
|
67
65
|
const { rows } = await executeArraysOnClient(
|
|
68
66
|
this.client,
|
|
69
67
|
this.queryString,
|
|
70
|
-
params
|
|
68
|
+
params,
|
|
69
|
+
typings
|
|
71
70
|
);
|
|
72
71
|
|
|
73
72
|
if (rows.length === 0) {
|
|
@@ -81,7 +80,7 @@ export class DatabendPreparedQuery<
|
|
|
81
80
|
);
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
const rows = await executeOnClient(this.client, this.queryString, params);
|
|
83
|
+
const rows = await executeOnClient(this.client, this.queryString, params, typings);
|
|
85
84
|
|
|
86
85
|
return rows as T['execute'];
|
|
87
86
|
}
|
|
@@ -137,7 +136,8 @@ export class DatabendSession<
|
|
|
137
136
|
this.logger,
|
|
138
137
|
fields,
|
|
139
138
|
isResponseInArrayMode,
|
|
140
|
-
customResultMapper
|
|
139
|
+
customResultMapper,
|
|
140
|
+
(query as any).typings
|
|
141
141
|
);
|
|
142
142
|
}
|
|
143
143
|
|