drizzle-databend 0.1.10 → 0.1.11
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/dialect.d.ts +2 -2
- package/dist/driver.d.ts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +600 -528
- package/dist/session.d.ts +3 -3
- package/package.json +9 -6
- package/src/client.ts +1 -1
- package/src/columns.ts +1 -1
- package/src/dialect.ts +6 -6
- package/src/driver.ts +9 -9
- package/src/index.ts +3 -3
- package/src/migrator.ts +1 -1
- package/src/pool.ts +3 -1
- package/src/session.ts +6 -6
- package/src/sql/result-mapper.ts +3 -4
- package/src/sql/selection.ts +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,195 +1,12 @@
|
|
|
1
|
-
// src/driver.ts
|
|
2
|
-
import { Client } from "databend-driver";
|
|
3
|
-
import { entityKind as entityKind3 } from "drizzle-orm/entity";
|
|
4
|
-
import { DefaultLogger } from "drizzle-orm/logger";
|
|
5
|
-
import { PgDatabase } from "drizzle-orm/pg-core/db";
|
|
6
|
-
import {
|
|
7
|
-
createTableRelationsHelpers,
|
|
8
|
-
extractTablesRelationalConfig
|
|
9
|
-
} from "drizzle-orm/relations";
|
|
10
|
-
|
|
11
|
-
// src/session.ts
|
|
12
|
-
import { entityKind } from "drizzle-orm/entity";
|
|
13
|
-
import { NoopLogger } from "drizzle-orm/logger";
|
|
14
|
-
import { PgTransaction } from "drizzle-orm/pg-core";
|
|
15
|
-
import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
|
|
16
|
-
import { fillPlaceholders, sql } from "drizzle-orm/sql/sql";
|
|
17
|
-
|
|
18
|
-
// src/sql/result-mapper.ts
|
|
19
|
-
import {
|
|
20
|
-
Column,
|
|
21
|
-
SQL,
|
|
22
|
-
getTableName,
|
|
23
|
-
is
|
|
24
|
-
} from "drizzle-orm";
|
|
25
|
-
import {
|
|
26
|
-
PgCustomColumn,
|
|
27
|
-
PgDate,
|
|
28
|
-
PgDateString,
|
|
29
|
-
PgTime,
|
|
30
|
-
PgTimestamp,
|
|
31
|
-
PgTimestampString
|
|
32
|
-
} from "drizzle-orm/pg-core";
|
|
33
|
-
function toDecoderInput(decoder, value) {
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
function normalizeTimestampString(value, withTimezone) {
|
|
37
|
-
if (value instanceof Date) {
|
|
38
|
-
const iso = value.toISOString().replace("T", " ");
|
|
39
|
-
return withTimezone ? iso.replace("Z", "+00") : iso.replace("Z", "");
|
|
40
|
-
}
|
|
41
|
-
if (typeof value === "string") {
|
|
42
|
-
const normalized = value.replace("T", " ");
|
|
43
|
-
if (withTimezone) {
|
|
44
|
-
return normalized.includes("+") ? normalized : `${normalized}+00`;
|
|
45
|
-
}
|
|
46
|
-
return normalized.replace(/\+00$/, "");
|
|
47
|
-
}
|
|
48
|
-
return value;
|
|
49
|
-
}
|
|
50
|
-
function normalizeTimestamp(value, withTimezone) {
|
|
51
|
-
if (value instanceof Date) {
|
|
52
|
-
return value;
|
|
53
|
-
}
|
|
54
|
-
if (typeof value === "string") {
|
|
55
|
-
const hasOffset = value.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(value.trim());
|
|
56
|
-
const spaced = value.replace(" ", "T");
|
|
57
|
-
const normalized = withTimezone || hasOffset ? spaced : `${spaced}+00`;
|
|
58
|
-
return new Date(normalized);
|
|
59
|
-
}
|
|
60
|
-
return value;
|
|
61
|
-
}
|
|
62
|
-
function normalizeDateString(value) {
|
|
63
|
-
if (value instanceof Date) {
|
|
64
|
-
return value.toISOString().slice(0, 10);
|
|
65
|
-
}
|
|
66
|
-
if (typeof value === "string") {
|
|
67
|
-
return value.slice(0, 10);
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
function normalizeDateValue(value) {
|
|
72
|
-
if (value instanceof Date) {
|
|
73
|
-
return value;
|
|
74
|
-
}
|
|
75
|
-
if (typeof value === "string") {
|
|
76
|
-
return new Date(`${value.slice(0, 10)}T00:00:00Z`);
|
|
77
|
-
}
|
|
78
|
-
return value;
|
|
79
|
-
}
|
|
80
|
-
function normalizeTime(value) {
|
|
81
|
-
if (typeof value === "bigint") {
|
|
82
|
-
const totalMillis = Number(value) / 1000;
|
|
83
|
-
const date = new Date(totalMillis);
|
|
84
|
-
return date.toISOString().split("T")[1].replace("Z", "");
|
|
85
|
-
}
|
|
86
|
-
if (value instanceof Date) {
|
|
87
|
-
return value.toISOString().split("T")[1].replace("Z", "");
|
|
88
|
-
}
|
|
89
|
-
return value;
|
|
90
|
-
}
|
|
91
|
-
function mapDriverValue(decoder, rawValue) {
|
|
92
|
-
if (is(decoder, PgTimestampString)) {
|
|
93
|
-
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeTimestampString(rawValue, decoder.withTimezone)));
|
|
94
|
-
}
|
|
95
|
-
if (is(decoder, PgTimestamp)) {
|
|
96
|
-
const normalized = normalizeTimestamp(rawValue, decoder.withTimezone);
|
|
97
|
-
if (normalized instanceof Date) {
|
|
98
|
-
return normalized;
|
|
99
|
-
}
|
|
100
|
-
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalized));
|
|
101
|
-
}
|
|
102
|
-
if (is(decoder, PgDateString)) {
|
|
103
|
-
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeDateString(rawValue)));
|
|
104
|
-
}
|
|
105
|
-
if (is(decoder, PgDate)) {
|
|
106
|
-
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeDateValue(rawValue)));
|
|
107
|
-
}
|
|
108
|
-
if (is(decoder, PgTime)) {
|
|
109
|
-
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalizeTime(rawValue)));
|
|
110
|
-
}
|
|
111
|
-
return decoder.mapFromDriverValue(toDecoderInput(decoder, rawValue));
|
|
112
|
-
}
|
|
113
|
-
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
114
|
-
const nullifyMap = {};
|
|
115
|
-
const result = columns.reduce((acc, { path, field }, columnIndex) => {
|
|
116
|
-
let decoder;
|
|
117
|
-
if (is(field, Column)) {
|
|
118
|
-
decoder = field;
|
|
119
|
-
} else if (is(field, SQL)) {
|
|
120
|
-
decoder = field.decoder;
|
|
121
|
-
} else {
|
|
122
|
-
const col = field.sql.queryChunks.find((chunk) => is(chunk, Column));
|
|
123
|
-
if (is(col, PgCustomColumn)) {
|
|
124
|
-
decoder = col;
|
|
125
|
-
} else {
|
|
126
|
-
decoder = field.sql.decoder;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
let node = acc;
|
|
130
|
-
for (const [pathChunkIndex, pathChunk] of path.entries()) {
|
|
131
|
-
if (pathChunkIndex < path.length - 1) {
|
|
132
|
-
if (!(pathChunk in node)) {
|
|
133
|
-
node[pathChunk] = {};
|
|
134
|
-
}
|
|
135
|
-
node = node[pathChunk];
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
const rawValue = row[columnIndex];
|
|
139
|
-
const value = node[pathChunk] = rawValue === null ? null : mapDriverValue(decoder, rawValue);
|
|
140
|
-
if (joinsNotNullableMap && is(field, Column) && path.length === 2) {
|
|
141
|
-
const objectName = path[0];
|
|
142
|
-
if (!(objectName in nullifyMap)) {
|
|
143
|
-
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
144
|
-
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
145
|
-
nullifyMap[objectName] = false;
|
|
146
|
-
}
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
if (joinsNotNullableMap && is(field, SQL.Aliased) && path.length === 2) {
|
|
150
|
-
const col = field.sql.queryChunks.find((chunk) => is(chunk, Column));
|
|
151
|
-
const tableName = col?.table && getTableName(col?.table);
|
|
152
|
-
if (!tableName) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
const objectName = path[0];
|
|
156
|
-
if (!(objectName in nullifyMap)) {
|
|
157
|
-
nullifyMap[objectName] = value === null ? tableName : false;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if (nullifyMap[objectName] && nullifyMap[objectName] !== tableName) {
|
|
161
|
-
nullifyMap[objectName] = false;
|
|
162
|
-
}
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return acc;
|
|
167
|
-
}, {});
|
|
168
|
-
if (joinsNotNullableMap && Object.keys(nullifyMap).length > 0) {
|
|
169
|
-
for (const [objectName, tableName] of Object.entries(nullifyMap)) {
|
|
170
|
-
if (typeof tableName === "string" && !joinsNotNullableMap[tableName]) {
|
|
171
|
-
result[objectName] = null;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return result;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// src/session.ts
|
|
179
|
-
import { TransactionRollbackError } from "drizzle-orm/errors";
|
|
180
|
-
|
|
181
1
|
// src/client.ts
|
|
182
2
|
function isPool(client) {
|
|
183
3
|
return typeof client.acquire === "function";
|
|
184
4
|
}
|
|
185
5
|
function prepareParams(params, typings) {
|
|
186
6
|
return params.map((param, i) => {
|
|
187
|
-
if (param ===
|
|
188
|
-
|
|
189
|
-
if (param
|
|
190
|
-
return param.toISOString().replace(/'/g, "''");
|
|
191
|
-
if (typeof param === "bigint")
|
|
192
|
-
return param.toString();
|
|
7
|
+
if (param === void 0) return null;
|
|
8
|
+
if (param instanceof Date) return param.toISOString().replace(/'/g, "''");
|
|
9
|
+
if (typeof param === "bigint") return param.toString();
|
|
193
10
|
if (typeof param === "string") {
|
|
194
11
|
const typing = typings?.[i];
|
|
195
12
|
if (typing === "decimal" && /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(param)) {
|
|
@@ -204,27 +21,25 @@ function prepareParams(params, typings) {
|
|
|
204
21
|
});
|
|
205
22
|
}
|
|
206
23
|
function isTransientError(error) {
|
|
207
|
-
if (!(error instanceof Error))
|
|
208
|
-
return false;
|
|
24
|
+
if (!(error instanceof Error)) return false;
|
|
209
25
|
const msg = error.message?.toLowerCase() ?? "";
|
|
210
26
|
return msg.includes("connection closed") || msg.includes("econnreset") || msg.includes("epipe") || msg.includes("socket hang up") || msg.includes("connection refused");
|
|
211
27
|
}
|
|
212
28
|
async function withRetry(fn, maxRetries = 2) {
|
|
213
29
|
let lastError;
|
|
214
|
-
for (let attempt = 0;attempt <= maxRetries; attempt++) {
|
|
30
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
215
31
|
try {
|
|
216
32
|
return await fn();
|
|
217
33
|
} catch (error) {
|
|
218
34
|
lastError = error;
|
|
219
|
-
if (!isTransientError(error) || attempt === maxRetries)
|
|
220
|
-
|
|
221
|
-
await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));
|
|
35
|
+
if (!isTransientError(error) || attempt === maxRetries) throw error;
|
|
36
|
+
await new Promise((r) => setTimeout(r, 100 * 2 ** attempt));
|
|
222
37
|
}
|
|
223
38
|
}
|
|
224
39
|
throw lastError;
|
|
225
40
|
}
|
|
226
41
|
function deduplicateColumns(columns) {
|
|
227
|
-
const counts = new Map;
|
|
42
|
+
const counts = /* @__PURE__ */ new Map();
|
|
228
43
|
let hasDuplicates = false;
|
|
229
44
|
for (const column of columns) {
|
|
230
45
|
const next = (counts.get(column) ?? 0) + 1;
|
|
@@ -256,7 +71,7 @@ async function executeOnClient(client, query, params, typings) {
|
|
|
256
71
|
});
|
|
257
72
|
}
|
|
258
73
|
const prepared = prepareParams(params, typings);
|
|
259
|
-
const paramValue = prepared.length > 0 ? prepared :
|
|
74
|
+
const paramValue = prepared.length > 0 ? prepared : void 0;
|
|
260
75
|
const rows = await client.queryAll(query, paramValue);
|
|
261
76
|
if (!rows || rows.length === 0) {
|
|
262
77
|
return [];
|
|
@@ -275,7 +90,7 @@ async function executeArraysOnClient(client, query, params, typings) {
|
|
|
275
90
|
});
|
|
276
91
|
}
|
|
277
92
|
const prepared = prepareParams(params, typings);
|
|
278
|
-
const paramValue = prepared.length > 0 ? prepared :
|
|
93
|
+
const paramValue = prepared.length > 0 ? prepared : void 0;
|
|
279
94
|
const iter = await client.queryIter(query, paramValue);
|
|
280
95
|
const schema = iter.schema();
|
|
281
96
|
const fields = schema.fields();
|
|
@@ -283,10 +98,8 @@ async function executeArraysOnClient(client, query, params, typings) {
|
|
|
283
98
|
const rows = [];
|
|
284
99
|
while (true) {
|
|
285
100
|
const row = await iter.next();
|
|
286
|
-
if (row === null)
|
|
287
|
-
|
|
288
|
-
if (row instanceof Error)
|
|
289
|
-
throw row;
|
|
101
|
+
if (row === null) break;
|
|
102
|
+
if (row instanceof Error) throw row;
|
|
290
103
|
rows.push(row.values());
|
|
291
104
|
}
|
|
292
105
|
return { columns, rows };
|
|
@@ -303,7 +116,7 @@ async function execOnClient(client, query, params, typings) {
|
|
|
303
116
|
});
|
|
304
117
|
}
|
|
305
118
|
const prepared = prepareParams(params, typings);
|
|
306
|
-
const paramValue = prepared.length > 0 ? prepared :
|
|
119
|
+
const paramValue = prepared.length > 0 ? prepared : void 0;
|
|
307
120
|
return await client.exec(query, paramValue);
|
|
308
121
|
}
|
|
309
122
|
async function closeClientConnection(connection) {
|
|
@@ -312,179 +125,155 @@ async function closeClientConnection(connection) {
|
|
|
312
125
|
}
|
|
313
126
|
}
|
|
314
127
|
|
|
315
|
-
// src/
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
128
|
+
// src/columns.ts
|
|
129
|
+
import { customType } from "drizzle-orm/pg-core";
|
|
130
|
+
var databendVariant = (name) => customType({
|
|
131
|
+
dataType() {
|
|
132
|
+
return "VARIANT";
|
|
133
|
+
},
|
|
134
|
+
toDriver(value) {
|
|
135
|
+
if (typeof value === "string") {
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
return JSON.stringify(value);
|
|
139
|
+
},
|
|
140
|
+
fromDriver(value) {
|
|
141
|
+
if (typeof value === "string") {
|
|
142
|
+
try {
|
|
143
|
+
return JSON.parse(value);
|
|
144
|
+
} catch {
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return value;
|
|
336
149
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
150
|
+
})(name);
|
|
151
|
+
var databendArray = (name, elementType) => customType({
|
|
152
|
+
dataType() {
|
|
153
|
+
return `ARRAY(${elementType})`;
|
|
154
|
+
},
|
|
155
|
+
toDriver(value) {
|
|
156
|
+
return value;
|
|
157
|
+
},
|
|
158
|
+
fromDriver(value) {
|
|
159
|
+
if (typeof value === "string") {
|
|
160
|
+
try {
|
|
161
|
+
return JSON.parse(value);
|
|
162
|
+
} catch {
|
|
344
163
|
return [];
|
|
345
164
|
}
|
|
346
|
-
return customResultMapper ? customResultMapper(rows2) : rows2.map((row) => mapResultRow(fields, row, joinsNotNullableMap));
|
|
347
165
|
}
|
|
348
|
-
|
|
349
|
-
return rows;
|
|
350
|
-
}
|
|
351
|
-
all(placeholderValues = {}) {
|
|
352
|
-
return this.execute(placeholderValues);
|
|
353
|
-
}
|
|
354
|
-
isResponseInArrayMode() {
|
|
355
|
-
return this._isResponseInArrayMode;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
class DatabendSession extends PgSession {
|
|
360
|
-
client;
|
|
361
|
-
schema;
|
|
362
|
-
options;
|
|
363
|
-
static [entityKind] = "DatabendSession";
|
|
364
|
-
dialect;
|
|
365
|
-
logger;
|
|
366
|
-
rollbackOnly = false;
|
|
367
|
-
constructor(client, dialect, schema, options = {}) {
|
|
368
|
-
super(dialect);
|
|
369
|
-
this.client = client;
|
|
370
|
-
this.schema = schema;
|
|
371
|
-
this.options = options;
|
|
372
|
-
this.dialect = dialect;
|
|
373
|
-
this.logger = options.logger ?? new NoopLogger;
|
|
374
|
-
}
|
|
375
|
-
prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
|
|
376
|
-
return new DatabendPreparedQuery(this.client, query.sql, query.params, this.logger, fields, isResponseInArrayMode, customResultMapper, query.typings);
|
|
166
|
+
return value;
|
|
377
167
|
}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
const tx = new DatabendTransaction(this.dialect, session, this.schema);
|
|
389
|
-
try {
|
|
390
|
-
await tx.execute(sql`BEGIN`);
|
|
391
|
-
if (config) {
|
|
392
|
-
await tx.setTransaction(config);
|
|
393
|
-
}
|
|
168
|
+
})(name);
|
|
169
|
+
var databendTuple = (name, types) => customType({
|
|
170
|
+
dataType() {
|
|
171
|
+
return `TUPLE(${types.join(", ")})`;
|
|
172
|
+
},
|
|
173
|
+
toDriver(value) {
|
|
174
|
+
return value;
|
|
175
|
+
},
|
|
176
|
+
fromDriver(value) {
|
|
177
|
+
if (typeof value === "string") {
|
|
394
178
|
try {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
throw new TransactionRollbackError;
|
|
399
|
-
}
|
|
400
|
-
await tx.execute(sql`COMMIT`);
|
|
401
|
-
return result;
|
|
402
|
-
} catch (error) {
|
|
403
|
-
await tx.execute(sql`ROLLBACK`);
|
|
404
|
-
throw error;
|
|
405
|
-
}
|
|
406
|
-
} finally {
|
|
407
|
-
if (pinnedConnection && pool) {
|
|
408
|
-
await pool.release(pinnedConnection);
|
|
179
|
+
return JSON.parse(value);
|
|
180
|
+
} catch {
|
|
181
|
+
return value;
|
|
409
182
|
}
|
|
410
183
|
}
|
|
184
|
+
return value;
|
|
411
185
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
class DatabendTransaction extends PgTransaction {
|
|
431
|
-
static [entityKind] = "DatabendTransaction";
|
|
432
|
-
rollback() {
|
|
433
|
-
throw new TransactionRollbackError;
|
|
186
|
+
})(name);
|
|
187
|
+
var databendMap = (name, keyType, valueType) => customType({
|
|
188
|
+
dataType() {
|
|
189
|
+
return `MAP(${keyType}, ${valueType})`;
|
|
190
|
+
},
|
|
191
|
+
toDriver(value) {
|
|
192
|
+
return value;
|
|
193
|
+
},
|
|
194
|
+
fromDriver(value) {
|
|
195
|
+
if (typeof value === "string") {
|
|
196
|
+
try {
|
|
197
|
+
return JSON.parse(value);
|
|
198
|
+
} catch {
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return value;
|
|
434
203
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
204
|
+
})(name);
|
|
205
|
+
var databendTimestamp = (name) => customType({
|
|
206
|
+
dataType() {
|
|
207
|
+
return "TIMESTAMP";
|
|
208
|
+
},
|
|
209
|
+
toDriver(value) {
|
|
210
|
+
if (value instanceof Date) {
|
|
211
|
+
return value.toISOString();
|
|
438
212
|
}
|
|
439
|
-
|
|
440
|
-
|
|
213
|
+
return value;
|
|
214
|
+
},
|
|
215
|
+
fromDriver(value) {
|
|
216
|
+
if (value instanceof Date) {
|
|
217
|
+
return value;
|
|
441
218
|
}
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
219
|
+
const str = String(value);
|
|
220
|
+
const hasOffset = str.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(str);
|
|
221
|
+
const normalized = hasOffset ? str.replace(" ", "T") : `${str.replace(" ", "T")}Z`;
|
|
222
|
+
return new Date(normalized);
|
|
223
|
+
}
|
|
224
|
+
})(name);
|
|
225
|
+
var databendDate = (name) => customType({
|
|
226
|
+
dataType() {
|
|
227
|
+
return "DATE";
|
|
228
|
+
},
|
|
229
|
+
toDriver(value) {
|
|
230
|
+
if (value instanceof Date) {
|
|
231
|
+
return value.toISOString().slice(0, 10);
|
|
445
232
|
}
|
|
446
|
-
|
|
447
|
-
|
|
233
|
+
return value;
|
|
234
|
+
},
|
|
235
|
+
fromDriver(value) {
|
|
236
|
+
if (value instanceof Date) {
|
|
237
|
+
return value.toISOString().slice(0, 10);
|
|
448
238
|
}
|
|
449
|
-
return
|
|
450
|
-
}
|
|
451
|
-
setTransaction(config) {
|
|
452
|
-
return this.session.execute(sql`SET TRANSACTION ${this.getTransactionConfigSQL(config)}`);
|
|
453
|
-
}
|
|
454
|
-
async transaction(transaction) {
|
|
455
|
-
const internals = this;
|
|
456
|
-
const nestedTx = new DatabendTransaction(internals.dialect, internals.session, this.schema, this.nestedIndex + 1);
|
|
457
|
-
return transaction(nestedTx).catch((error) => {
|
|
458
|
-
internals.session.markRollbackOnly();
|
|
459
|
-
throw error;
|
|
460
|
-
});
|
|
239
|
+
return value.slice(0, 10);
|
|
461
240
|
}
|
|
462
|
-
}
|
|
241
|
+
})(name);
|
|
242
|
+
|
|
243
|
+
// src/driver.ts
|
|
244
|
+
import { Client } from "databend-driver";
|
|
245
|
+
import { entityKind as entityKind3 } from "drizzle-orm/entity";
|
|
246
|
+
import { DefaultLogger } from "drizzle-orm/logger";
|
|
247
|
+
import { PgDatabase } from "drizzle-orm/pg-core/db";
|
|
248
|
+
import {
|
|
249
|
+
createTableRelationsHelpers,
|
|
250
|
+
extractTablesRelationalConfig
|
|
251
|
+
} from "drizzle-orm/relations";
|
|
463
252
|
|
|
464
253
|
// src/dialect.ts
|
|
465
|
-
import {
|
|
254
|
+
import {
|
|
255
|
+
sql
|
|
256
|
+
} from "drizzle-orm";
|
|
257
|
+
import { entityKind, is } from "drizzle-orm/entity";
|
|
466
258
|
import {
|
|
467
259
|
PgBigInt53,
|
|
468
260
|
PgBigInt64,
|
|
469
|
-
PgDate
|
|
470
|
-
PgDateString
|
|
261
|
+
PgDate,
|
|
262
|
+
PgDateString,
|
|
471
263
|
PgDialect,
|
|
472
264
|
PgDoublePrecision,
|
|
473
265
|
PgInteger,
|
|
474
266
|
PgNumeric,
|
|
475
267
|
PgReal,
|
|
476
268
|
PgSmallInt,
|
|
477
|
-
PgTime
|
|
478
|
-
PgTimestamp
|
|
479
|
-
PgTimestampString
|
|
269
|
+
PgTime,
|
|
270
|
+
PgTimestamp,
|
|
271
|
+
PgTimestampString,
|
|
480
272
|
PgUUID
|
|
481
273
|
} from "drizzle-orm/pg-core";
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
class DatabendDialect extends PgDialect {
|
|
487
|
-
static [entityKind2] = "DatabendPgDialect";
|
|
274
|
+
var DatabendDialect = class extends PgDialect {
|
|
275
|
+
static [entityKind] = "DatabendPgDialect";
|
|
276
|
+
// Databend does not support savepoints
|
|
488
277
|
areSavepointsUnsupported() {
|
|
489
278
|
return true;
|
|
490
279
|
}
|
|
@@ -492,67 +281,79 @@ class DatabendDialect extends PgDialect {
|
|
|
492
281
|
const migrationConfig = typeof config === "string" ? { migrationsFolder: config } : config;
|
|
493
282
|
const migrationsSchema = migrationConfig.migrationsSchema ?? "default";
|
|
494
283
|
const migrationsTable = migrationConfig.migrationsTable ?? "__drizzle_migrations";
|
|
495
|
-
const migrationTableCreate =
|
|
496
|
-
CREATE TABLE IF NOT EXISTS ${
|
|
284
|
+
const migrationTableCreate = sql`
|
|
285
|
+
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsSchema)}.${sql.identifier(
|
|
286
|
+
migrationsTable
|
|
287
|
+
)} (
|
|
497
288
|
id INT NOT NULL,
|
|
498
289
|
hash VARCHAR NOT NULL,
|
|
499
290
|
created_at BIGINT
|
|
500
291
|
)
|
|
501
292
|
`;
|
|
502
293
|
await session.execute(migrationTableCreate);
|
|
503
|
-
const dbMigrations = await session.all(
|
|
294
|
+
const dbMigrations = await session.all(
|
|
295
|
+
sql`SELECT id, hash, created_at FROM ${sql.identifier(
|
|
296
|
+
migrationsSchema
|
|
297
|
+
)}.${sql.identifier(migrationsTable)} ORDER BY created_at DESC LIMIT 1`
|
|
298
|
+
);
|
|
504
299
|
const lastDbMigration = dbMigrations[0];
|
|
505
300
|
await session.transaction(async (tx) => {
|
|
506
301
|
for await (const migration of migrations) {
|
|
507
302
|
if (!lastDbMigration || Number(lastDbMigration.created_at) < migration.folderMillis) {
|
|
508
303
|
for (const stmt of migration.sql) {
|
|
509
|
-
await tx.execute(
|
|
304
|
+
await tx.execute(sql.raw(stmt));
|
|
510
305
|
}
|
|
511
|
-
await tx.execute(
|
|
306
|
+
await tx.execute(
|
|
307
|
+
sql`INSERT INTO ${sql.identifier(
|
|
308
|
+
migrationsSchema
|
|
309
|
+
)}.${sql.identifier(migrationsTable)} (id, hash, created_at)
|
|
512
310
|
VALUES (
|
|
513
|
-
(SELECT COALESCE(MAX(id), 0) + 1 FROM ${
|
|
311
|
+
(SELECT COALESCE(MAX(id), 0) + 1 FROM ${sql.identifier(
|
|
312
|
+
migrationsSchema
|
|
313
|
+
)}.${sql.identifier(migrationsTable)}),
|
|
514
314
|
${migration.hash},
|
|
515
315
|
${migration.folderMillis}
|
|
516
|
-
)`
|
|
316
|
+
)`
|
|
317
|
+
);
|
|
517
318
|
}
|
|
518
319
|
}
|
|
519
320
|
});
|
|
520
321
|
}
|
|
521
322
|
prepareTyping(encoder) {
|
|
522
|
-
if (
|
|
323
|
+
if (is(encoder, PgNumeric) || is(encoder, PgInteger) || is(encoder, PgSmallInt) || is(encoder, PgReal) || is(encoder, PgDoublePrecision) || is(encoder, PgBigInt53) || is(encoder, PgBigInt64)) {
|
|
523
324
|
return "decimal";
|
|
524
|
-
} else if (
|
|
325
|
+
} else if (is(encoder, PgTime)) {
|
|
525
326
|
return "time";
|
|
526
|
-
} else if (
|
|
327
|
+
} else if (is(encoder, PgTimestamp) || is(encoder, PgTimestampString)) {
|
|
527
328
|
return "timestamp";
|
|
528
|
-
} else if (
|
|
329
|
+
} else if (is(encoder, PgDate) || is(encoder, PgDateString)) {
|
|
529
330
|
return "date";
|
|
530
|
-
} else if (
|
|
331
|
+
} else if (is(encoder, PgUUID)) {
|
|
531
332
|
return "uuid";
|
|
532
333
|
} else {
|
|
533
334
|
return "none";
|
|
534
335
|
}
|
|
535
336
|
}
|
|
536
|
-
}
|
|
337
|
+
};
|
|
537
338
|
|
|
538
339
|
// src/pool.ts
|
|
539
340
|
function createDatabendConnectionPool(client, options = {}) {
|
|
540
341
|
const size = options.size && options.size > 0 ? options.size : 4;
|
|
541
|
-
const acquireTimeout = options.acquireTimeout ??
|
|
342
|
+
const acquireTimeout = options.acquireTimeout ?? 3e4;
|
|
542
343
|
const maxWaitingRequests = options.maxWaitingRequests ?? 100;
|
|
543
344
|
const maxLifetimeMs = options.maxLifetimeMs;
|
|
544
345
|
const idleTimeoutMs = options.idleTimeoutMs;
|
|
545
|
-
const metadata = new WeakMap;
|
|
346
|
+
const metadata = /* @__PURE__ */ new WeakMap();
|
|
546
347
|
const idle = [];
|
|
547
348
|
const waiting = [];
|
|
548
349
|
let total = 0;
|
|
549
350
|
let closed = false;
|
|
550
351
|
let pendingAcquires = 0;
|
|
551
352
|
const shouldRecycle = (conn, now) => {
|
|
552
|
-
if (maxLifetimeMs !==
|
|
353
|
+
if (maxLifetimeMs !== void 0 && now - conn.createdAt >= maxLifetimeMs) {
|
|
553
354
|
return true;
|
|
554
355
|
}
|
|
555
|
-
if (idleTimeoutMs !==
|
|
356
|
+
if (idleTimeoutMs !== void 0 && now - conn.lastUsedAt >= idleTimeoutMs) {
|
|
556
357
|
return true;
|
|
557
358
|
}
|
|
558
359
|
return false;
|
|
@@ -598,7 +399,9 @@ function createDatabendConnectionPool(client, options = {}) {
|
|
|
598
399
|
}
|
|
599
400
|
}
|
|
600
401
|
if (waiting.length >= maxWaitingRequests) {
|
|
601
|
-
throw new Error(
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Databend connection pool queue is full (max ${maxWaitingRequests} waiting requests)`
|
|
404
|
+
);
|
|
602
405
|
}
|
|
603
406
|
return await new Promise((resolve, reject) => {
|
|
604
407
|
const timeoutId = setTimeout(() => {
|
|
@@ -606,7 +409,11 @@ function createDatabendConnectionPool(client, options = {}) {
|
|
|
606
409
|
if (idx !== -1) {
|
|
607
410
|
waiting.splice(idx, 1);
|
|
608
411
|
}
|
|
609
|
-
reject(
|
|
412
|
+
reject(
|
|
413
|
+
new Error(
|
|
414
|
+
`Databend connection pool acquire timeout after ${acquireTimeout}ms`
|
|
415
|
+
)
|
|
416
|
+
);
|
|
610
417
|
}, acquireTimeout);
|
|
611
418
|
waiting.push({ resolve, reject, timeoutId });
|
|
612
419
|
});
|
|
@@ -617,7 +424,7 @@ function createDatabendConnectionPool(client, options = {}) {
|
|
|
617
424
|
clearTimeout(waiter.timeoutId);
|
|
618
425
|
const now2 = Date.now();
|
|
619
426
|
const meta = metadata.get(connection) ?? { createdAt: now2, lastUsedAt: now2 };
|
|
620
|
-
const expired = maxLifetimeMs !==
|
|
427
|
+
const expired = maxLifetimeMs !== void 0 && now2 - meta.createdAt >= maxLifetimeMs;
|
|
621
428
|
if (closed) {
|
|
622
429
|
await closeClientConnection(connection);
|
|
623
430
|
total = Math.max(0, total - 1);
|
|
@@ -652,7 +459,7 @@ function createDatabendConnectionPool(client, options = {}) {
|
|
|
652
459
|
const existingMeta = metadata.get(connection) ?? { createdAt: now, lastUsedAt: now };
|
|
653
460
|
existingMeta.lastUsedAt = now;
|
|
654
461
|
metadata.set(connection, existingMeta);
|
|
655
|
-
if (maxLifetimeMs !==
|
|
462
|
+
if (maxLifetimeMs !== void 0 && now - existingMeta.createdAt >= maxLifetimeMs) {
|
|
656
463
|
await closeClientConnection(connection);
|
|
657
464
|
total -= 1;
|
|
658
465
|
metadata.delete(connection);
|
|
@@ -672,10 +479,14 @@ function createDatabendConnectionPool(client, options = {}) {
|
|
|
672
479
|
waiter.reject(new Error("Databend connection pool is closed"));
|
|
673
480
|
}
|
|
674
481
|
const toClose = idle.splice(0, idle.length);
|
|
675
|
-
await Promise.allSettled(
|
|
482
|
+
await Promise.allSettled(
|
|
483
|
+
toClose.map((item) => closeClientConnection(item.connection))
|
|
484
|
+
);
|
|
676
485
|
total = Math.max(0, total - toClose.length);
|
|
677
|
-
|
|
678
|
-
|
|
486
|
+
for (const item of toClose) {
|
|
487
|
+
metadata.delete(item.connection);
|
|
488
|
+
}
|
|
489
|
+
const maxWait = 5e3;
|
|
679
490
|
const start = Date.now();
|
|
680
491
|
while (pendingAcquires > 0 && Date.now() - start < maxWait) {
|
|
681
492
|
await new Promise((r) => setTimeout(r, 10));
|
|
@@ -689,36 +500,393 @@ function createDatabendConnectionPool(client, options = {}) {
|
|
|
689
500
|
};
|
|
690
501
|
}
|
|
691
502
|
|
|
692
|
-
// src/
|
|
693
|
-
|
|
694
|
-
|
|
503
|
+
// src/session.ts
|
|
504
|
+
import { entityKind as entityKind2 } from "drizzle-orm/entity";
|
|
505
|
+
import { TransactionRollbackError } from "drizzle-orm/errors";
|
|
506
|
+
import { NoopLogger } from "drizzle-orm/logger";
|
|
507
|
+
import { PgTransaction } from "drizzle-orm/pg-core";
|
|
508
|
+
import { PgPreparedQuery, PgSession } from "drizzle-orm/pg-core/session";
|
|
509
|
+
import { fillPlaceholders, sql as sql2 } from "drizzle-orm/sql/sql";
|
|
510
|
+
|
|
511
|
+
// src/sql/result-mapper.ts
|
|
512
|
+
import {
|
|
513
|
+
Column,
|
|
514
|
+
getTableName,
|
|
515
|
+
is as is2,
|
|
516
|
+
SQL
|
|
517
|
+
} from "drizzle-orm";
|
|
518
|
+
import {
|
|
519
|
+
PgCustomColumn,
|
|
520
|
+
PgDate as PgDate2,
|
|
521
|
+
PgDateString as PgDateString2,
|
|
522
|
+
PgTime as PgTime2,
|
|
523
|
+
PgTimestamp as PgTimestamp2,
|
|
524
|
+
PgTimestampString as PgTimestampString2
|
|
525
|
+
} from "drizzle-orm/pg-core";
|
|
526
|
+
function toDecoderInput(decoder, value) {
|
|
527
|
+
void decoder;
|
|
528
|
+
return value;
|
|
529
|
+
}
|
|
530
|
+
function normalizeTimestampString(value, withTimezone) {
|
|
531
|
+
if (value instanceof Date) {
|
|
532
|
+
const iso = value.toISOString().replace("T", " ");
|
|
533
|
+
return withTimezone ? iso.replace("Z", "+00") : iso.replace("Z", "");
|
|
534
|
+
}
|
|
535
|
+
if (typeof value === "string") {
|
|
536
|
+
const normalized = value.replace("T", " ");
|
|
537
|
+
if (withTimezone) {
|
|
538
|
+
return normalized.includes("+") ? normalized : `${normalized}+00`;
|
|
539
|
+
}
|
|
540
|
+
return normalized.replace(/\+00$/, "");
|
|
541
|
+
}
|
|
542
|
+
return value;
|
|
543
|
+
}
|
|
544
|
+
function normalizeTimestamp(value, withTimezone) {
|
|
545
|
+
if (value instanceof Date) {
|
|
546
|
+
return value;
|
|
547
|
+
}
|
|
548
|
+
if (typeof value === "string") {
|
|
549
|
+
const hasOffset = value.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(value.trim());
|
|
550
|
+
const spaced = value.replace(" ", "T");
|
|
551
|
+
const normalized = withTimezone || hasOffset ? spaced : `${spaced}+00`;
|
|
552
|
+
return new Date(normalized);
|
|
553
|
+
}
|
|
554
|
+
return value;
|
|
555
|
+
}
|
|
556
|
+
function normalizeDateString(value) {
|
|
557
|
+
if (value instanceof Date) {
|
|
558
|
+
return value.toISOString().slice(0, 10);
|
|
559
|
+
}
|
|
560
|
+
if (typeof value === "string") {
|
|
561
|
+
return value.slice(0, 10);
|
|
562
|
+
}
|
|
563
|
+
return value;
|
|
564
|
+
}
|
|
565
|
+
function normalizeDateValue(value) {
|
|
566
|
+
if (value instanceof Date) {
|
|
567
|
+
return value;
|
|
568
|
+
}
|
|
569
|
+
if (typeof value === "string") {
|
|
570
|
+
return /* @__PURE__ */ new Date(`${value.slice(0, 10)}T00:00:00Z`);
|
|
571
|
+
}
|
|
572
|
+
return value;
|
|
573
|
+
}
|
|
574
|
+
function normalizeTime(value) {
|
|
575
|
+
if (typeof value === "bigint") {
|
|
576
|
+
const totalMillis = Number(value) / 1e3;
|
|
577
|
+
const date = new Date(totalMillis);
|
|
578
|
+
return date.toISOString().split("T")[1].replace("Z", "");
|
|
579
|
+
}
|
|
580
|
+
if (value instanceof Date) {
|
|
581
|
+
return value.toISOString().split("T")[1].replace("Z", "");
|
|
582
|
+
}
|
|
583
|
+
return value;
|
|
584
|
+
}
|
|
585
|
+
function mapDriverValue(decoder, rawValue) {
|
|
586
|
+
if (is2(decoder, PgTimestampString2)) {
|
|
587
|
+
return decoder.mapFromDriverValue(
|
|
588
|
+
toDecoderInput(
|
|
589
|
+
decoder,
|
|
590
|
+
normalizeTimestampString(rawValue, decoder.withTimezone)
|
|
591
|
+
)
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
if (is2(decoder, PgTimestamp2)) {
|
|
595
|
+
const normalized = normalizeTimestamp(rawValue, decoder.withTimezone);
|
|
596
|
+
if (normalized instanceof Date) {
|
|
597
|
+
return normalized;
|
|
598
|
+
}
|
|
599
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, normalized));
|
|
600
|
+
}
|
|
601
|
+
if (is2(decoder, PgDateString2)) {
|
|
602
|
+
return decoder.mapFromDriverValue(
|
|
603
|
+
toDecoderInput(decoder, normalizeDateString(rawValue))
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
if (is2(decoder, PgDate2)) {
|
|
607
|
+
return decoder.mapFromDriverValue(
|
|
608
|
+
toDecoderInput(decoder, normalizeDateValue(rawValue))
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
if (is2(decoder, PgTime2)) {
|
|
612
|
+
return decoder.mapFromDriverValue(
|
|
613
|
+
toDecoderInput(decoder, normalizeTime(rawValue))
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
return decoder.mapFromDriverValue(toDecoderInput(decoder, rawValue));
|
|
617
|
+
}
|
|
618
|
+
function mapResultRow(columns, row, joinsNotNullableMap) {
|
|
619
|
+
const nullifyMap = {};
|
|
620
|
+
const result = columns.reduce(
|
|
621
|
+
(acc, { path, field }, columnIndex) => {
|
|
622
|
+
let decoder;
|
|
623
|
+
if (is2(field, Column)) {
|
|
624
|
+
decoder = field;
|
|
625
|
+
} else if (is2(field, SQL)) {
|
|
626
|
+
decoder = field.decoder;
|
|
627
|
+
} else {
|
|
628
|
+
const col = field.sql.queryChunks.find((chunk) => is2(chunk, Column));
|
|
629
|
+
if (is2(col, PgCustomColumn)) {
|
|
630
|
+
decoder = col;
|
|
631
|
+
} else {
|
|
632
|
+
decoder = field.sql.decoder;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
let node = acc;
|
|
636
|
+
for (const [pathChunkIndex, pathChunk] of path.entries()) {
|
|
637
|
+
if (pathChunkIndex < path.length - 1) {
|
|
638
|
+
if (!(pathChunk in node)) {
|
|
639
|
+
node[pathChunk] = {};
|
|
640
|
+
}
|
|
641
|
+
node = node[pathChunk];
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
const rawValue = row[columnIndex];
|
|
645
|
+
const value = node[pathChunk] = rawValue === null ? null : mapDriverValue(decoder, rawValue);
|
|
646
|
+
if (joinsNotNullableMap && is2(field, Column) && path.length === 2) {
|
|
647
|
+
const objectName = path[0];
|
|
648
|
+
if (!(objectName in nullifyMap)) {
|
|
649
|
+
nullifyMap[objectName] = value === null ? getTableName(field.table) : false;
|
|
650
|
+
} else if (typeof nullifyMap[objectName] === "string" && nullifyMap[objectName] !== getTableName(field.table)) {
|
|
651
|
+
nullifyMap[objectName] = false;
|
|
652
|
+
}
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (joinsNotNullableMap && is2(field, SQL.Aliased) && path.length === 2) {
|
|
656
|
+
const col = field.sql.queryChunks.find((chunk) => is2(chunk, Column));
|
|
657
|
+
const tableName = col?.table && getTableName(col?.table);
|
|
658
|
+
if (!tableName) {
|
|
659
|
+
continue;
|
|
660
|
+
}
|
|
661
|
+
const objectName = path[0];
|
|
662
|
+
if (!(objectName in nullifyMap)) {
|
|
663
|
+
nullifyMap[objectName] = value === null ? tableName : false;
|
|
664
|
+
continue;
|
|
665
|
+
}
|
|
666
|
+
if (nullifyMap[objectName] && nullifyMap[objectName] !== tableName) {
|
|
667
|
+
nullifyMap[objectName] = false;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return acc;
|
|
672
|
+
},
|
|
673
|
+
{}
|
|
674
|
+
);
|
|
675
|
+
if (joinsNotNullableMap && Object.keys(nullifyMap).length > 0) {
|
|
676
|
+
for (const [objectName, tableName] of Object.entries(nullifyMap)) {
|
|
677
|
+
if (typeof tableName === "string" && !joinsNotNullableMap[tableName]) {
|
|
678
|
+
result[objectName] = null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return result;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/session.ts
|
|
686
|
+
var DatabendPreparedQuery = class extends PgPreparedQuery {
|
|
687
|
+
constructor(client, queryString, params, logger, fields, _isResponseInArrayMode, customResultMapper, typings) {
|
|
688
|
+
super({ sql: queryString, params });
|
|
689
|
+
this.client = client;
|
|
690
|
+
this.queryString = queryString;
|
|
691
|
+
this.params = params;
|
|
692
|
+
this.logger = logger;
|
|
693
|
+
this.fields = fields;
|
|
694
|
+
this._isResponseInArrayMode = _isResponseInArrayMode;
|
|
695
|
+
this.customResultMapper = customResultMapper;
|
|
696
|
+
this.typings = typings;
|
|
697
|
+
}
|
|
698
|
+
static [entityKind2] = "DatabendPreparedQuery";
|
|
699
|
+
async execute(placeholderValues = {}) {
|
|
700
|
+
const params = fillPlaceholders(this.params, placeholderValues);
|
|
701
|
+
this.logger.logQuery(this.queryString, params);
|
|
702
|
+
const { fields, joinsNotNullableMap, customResultMapper, typings } = this;
|
|
703
|
+
if (fields) {
|
|
704
|
+
const { rows: rows2 } = await executeArraysOnClient(
|
|
705
|
+
this.client,
|
|
706
|
+
this.queryString,
|
|
707
|
+
params,
|
|
708
|
+
typings
|
|
709
|
+
);
|
|
710
|
+
if (rows2.length === 0) {
|
|
711
|
+
return [];
|
|
712
|
+
}
|
|
713
|
+
return customResultMapper ? customResultMapper(rows2) : rows2.map(
|
|
714
|
+
(row) => mapResultRow(fields, row, joinsNotNullableMap)
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
const rows = await executeOnClient(this.client, this.queryString, params, typings);
|
|
718
|
+
return rows;
|
|
719
|
+
}
|
|
720
|
+
all(placeholderValues = {}) {
|
|
721
|
+
return this.execute(placeholderValues);
|
|
722
|
+
}
|
|
723
|
+
isResponseInArrayMode() {
|
|
724
|
+
return this._isResponseInArrayMode;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
var DatabendSession = class _DatabendSession extends PgSession {
|
|
728
|
+
constructor(client, dialect, schema, options = {}) {
|
|
729
|
+
super(dialect);
|
|
730
|
+
this.client = client;
|
|
731
|
+
this.schema = schema;
|
|
732
|
+
this.options = options;
|
|
733
|
+
this.dialect = dialect;
|
|
734
|
+
this.logger = options.logger ?? new NoopLogger();
|
|
735
|
+
}
|
|
736
|
+
static [entityKind2] = "DatabendSession";
|
|
695
737
|
dialect;
|
|
696
|
-
|
|
697
|
-
|
|
738
|
+
logger;
|
|
739
|
+
rollbackOnly = false;
|
|
740
|
+
prepareQuery(query, fields, name, isResponseInArrayMode, customResultMapper) {
|
|
741
|
+
void name;
|
|
742
|
+
return new DatabendPreparedQuery(
|
|
743
|
+
this.client,
|
|
744
|
+
query.sql,
|
|
745
|
+
query.params,
|
|
746
|
+
this.logger,
|
|
747
|
+
fields,
|
|
748
|
+
isResponseInArrayMode,
|
|
749
|
+
customResultMapper,
|
|
750
|
+
query.typings
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
async transaction(transaction, config) {
|
|
754
|
+
let pinnedConnection;
|
|
755
|
+
let pool;
|
|
756
|
+
let clientForTx = this.client;
|
|
757
|
+
if (isPool(this.client)) {
|
|
758
|
+
pool = this.client;
|
|
759
|
+
pinnedConnection = await pool.acquire();
|
|
760
|
+
clientForTx = pinnedConnection;
|
|
761
|
+
}
|
|
762
|
+
const session = new _DatabendSession(
|
|
763
|
+
clientForTx,
|
|
764
|
+
this.dialect,
|
|
765
|
+
this.schema,
|
|
766
|
+
this.options
|
|
767
|
+
);
|
|
768
|
+
const tx = new DatabendTransaction(
|
|
769
|
+
this.dialect,
|
|
770
|
+
session,
|
|
771
|
+
this.schema
|
|
772
|
+
);
|
|
773
|
+
try {
|
|
774
|
+
await tx.execute(sql2`BEGIN`);
|
|
775
|
+
if (config) {
|
|
776
|
+
await tx.setTransaction(config);
|
|
777
|
+
}
|
|
778
|
+
try {
|
|
779
|
+
const result = await transaction(tx);
|
|
780
|
+
if (session.isRollbackOnly()) {
|
|
781
|
+
await tx.execute(sql2`ROLLBACK`);
|
|
782
|
+
throw new TransactionRollbackError();
|
|
783
|
+
}
|
|
784
|
+
await tx.execute(sql2`COMMIT`);
|
|
785
|
+
return result;
|
|
786
|
+
} catch (error) {
|
|
787
|
+
await tx.execute(sql2`ROLLBACK`);
|
|
788
|
+
throw error;
|
|
789
|
+
}
|
|
790
|
+
} finally {
|
|
791
|
+
if (pinnedConnection && pool) {
|
|
792
|
+
await pool.release(pinnedConnection);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
markRollbackOnly() {
|
|
797
|
+
this.rollbackOnly = true;
|
|
798
|
+
}
|
|
799
|
+
isRollbackOnly() {
|
|
800
|
+
return this.rollbackOnly;
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
var VALID_TRANSACTION_ISOLATION_LEVELS = /* @__PURE__ */ new Set([
|
|
804
|
+
"read uncommitted",
|
|
805
|
+
"read committed",
|
|
806
|
+
"repeatable read",
|
|
807
|
+
"serializable"
|
|
808
|
+
]);
|
|
809
|
+
var VALID_TRANSACTION_ACCESS_MODES = /* @__PURE__ */ new Set([
|
|
810
|
+
"read only",
|
|
811
|
+
"read write"
|
|
812
|
+
]);
|
|
813
|
+
var DatabendTransaction = class _DatabendTransaction extends PgTransaction {
|
|
814
|
+
static [entityKind2] = "DatabendTransaction";
|
|
815
|
+
rollback() {
|
|
816
|
+
throw new TransactionRollbackError();
|
|
817
|
+
}
|
|
818
|
+
getTransactionConfigSQL(config) {
|
|
819
|
+
if (config.isolationLevel && !VALID_TRANSACTION_ISOLATION_LEVELS.has(config.isolationLevel)) {
|
|
820
|
+
throw new Error(
|
|
821
|
+
`Invalid transaction isolation level "${config.isolationLevel}". Expected one of: ${Array.from(
|
|
822
|
+
VALID_TRANSACTION_ISOLATION_LEVELS
|
|
823
|
+
).join(", ")}.`
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
if (config.accessMode && !VALID_TRANSACTION_ACCESS_MODES.has(config.accessMode)) {
|
|
827
|
+
throw new Error(
|
|
828
|
+
`Invalid transaction access mode "${config.accessMode}". Expected one of: ${Array.from(
|
|
829
|
+
VALID_TRANSACTION_ACCESS_MODES
|
|
830
|
+
).join(", ")}.`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
const chunks = [];
|
|
834
|
+
if (config.isolationLevel) {
|
|
835
|
+
chunks.push(`isolation level ${config.isolationLevel}`);
|
|
836
|
+
}
|
|
837
|
+
if (config.accessMode) {
|
|
838
|
+
chunks.push(config.accessMode);
|
|
839
|
+
}
|
|
840
|
+
return sql2.raw(chunks.join(" "));
|
|
841
|
+
}
|
|
842
|
+
setTransaction(config) {
|
|
843
|
+
return this.session.execute(
|
|
844
|
+
sql2`SET TRANSACTION ${this.getTransactionConfigSQL(config)}`
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
async transaction(transaction) {
|
|
848
|
+
const internals = this;
|
|
849
|
+
const nestedTx = new _DatabendTransaction(
|
|
850
|
+
internals.dialect,
|
|
851
|
+
internals.session,
|
|
852
|
+
this.schema,
|
|
853
|
+
this.nestedIndex + 1
|
|
854
|
+
);
|
|
855
|
+
return transaction(nestedTx).catch((error) => {
|
|
856
|
+
internals.session.markRollbackOnly();
|
|
857
|
+
throw error;
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/driver.ts
|
|
863
|
+
var DatabendDriver = class {
|
|
698
864
|
constructor(client, dialect, options = {}) {
|
|
699
865
|
this.client = client;
|
|
700
866
|
this.dialect = dialect;
|
|
701
867
|
this.options = options;
|
|
702
868
|
}
|
|
869
|
+
static [entityKind3] = "DatabendDriver";
|
|
703
870
|
createSession(schema) {
|
|
704
871
|
return new DatabendSession(this.client, this.dialect, schema, {
|
|
705
872
|
logger: this.options.logger
|
|
706
873
|
});
|
|
707
874
|
}
|
|
708
|
-
}
|
|
875
|
+
};
|
|
709
876
|
function isConfigObject(data) {
|
|
710
|
-
if (typeof data !== "object" || data === null)
|
|
711
|
-
|
|
712
|
-
if (data.constructor?.name !== "Object")
|
|
713
|
-
return false;
|
|
877
|
+
if (typeof data !== "object" || data === null) return false;
|
|
878
|
+
if (data.constructor?.name !== "Object") return false;
|
|
714
879
|
return "connection" in data || "client" in data || "pool" in data || "schema" in data || "logger" in data;
|
|
715
880
|
}
|
|
716
881
|
function createFromClient(client, config = {}, databendClient) {
|
|
717
|
-
const dialect = new DatabendDialect;
|
|
718
|
-
const logger = config.logger === true ? new DefaultLogger : config.logger ||
|
|
882
|
+
const dialect = new DatabendDialect();
|
|
883
|
+
const logger = config.logger === true ? new DefaultLogger() : config.logger || void 0;
|
|
719
884
|
let schema;
|
|
720
885
|
if (config.schema) {
|
|
721
|
-
const tablesConfig = extractTablesRelationalConfig(
|
|
886
|
+
const tablesConfig = extractTablesRelationalConfig(
|
|
887
|
+
config.schema,
|
|
888
|
+
createTableRelationsHelpers
|
|
889
|
+
);
|
|
722
890
|
schema = {
|
|
723
891
|
fullSchema: config.schema,
|
|
724
892
|
schema: tablesConfig.tables,
|
|
@@ -727,7 +895,13 @@ function createFromClient(client, config = {}, databendClient) {
|
|
|
727
895
|
}
|
|
728
896
|
const driver = new DatabendDriver(client, dialect, { logger });
|
|
729
897
|
const session = driver.createSession(schema);
|
|
730
|
-
const db = new DatabendDatabase(
|
|
898
|
+
const db = new DatabendDatabase(
|
|
899
|
+
dialect,
|
|
900
|
+
session,
|
|
901
|
+
schema,
|
|
902
|
+
client,
|
|
903
|
+
databendClient
|
|
904
|
+
);
|
|
731
905
|
return db;
|
|
732
906
|
}
|
|
733
907
|
async function createFromDsn(dsn, config = {}) {
|
|
@@ -749,24 +923,26 @@ function drizzle(clientOrConfigOrDsn, config) {
|
|
|
749
923
|
if ("connection" in configObj) {
|
|
750
924
|
const connConfig = configObj;
|
|
751
925
|
const { connection, ...restConfig } = connConfig;
|
|
752
|
-
return createFromDsn(
|
|
926
|
+
return createFromDsn(
|
|
927
|
+
connection,
|
|
928
|
+
restConfig
|
|
929
|
+
);
|
|
753
930
|
}
|
|
754
931
|
if ("client" in configObj) {
|
|
755
932
|
const clientConfig = configObj;
|
|
756
933
|
const { client: clientValue, ...restConfig } = clientConfig;
|
|
757
|
-
return createFromClient(
|
|
934
|
+
return createFromClient(
|
|
935
|
+
clientValue,
|
|
936
|
+
restConfig
|
|
937
|
+
);
|
|
758
938
|
}
|
|
759
|
-
throw new Error(
|
|
939
|
+
throw new Error(
|
|
940
|
+
"Invalid drizzle config: either connection or client must be provided"
|
|
941
|
+
);
|
|
760
942
|
}
|
|
761
943
|
return createFromClient(clientOrConfigOrDsn, config);
|
|
762
944
|
}
|
|
763
|
-
|
|
764
|
-
class DatabendDatabase extends PgDatabase {
|
|
765
|
-
dialect;
|
|
766
|
-
session;
|
|
767
|
-
static [entityKind3] = "DatabendDatabase";
|
|
768
|
-
$client;
|
|
769
|
-
$databendClient;
|
|
945
|
+
var DatabendDatabase = class extends PgDatabase {
|
|
770
946
|
constructor(dialect, session, schema, client, databendClient) {
|
|
771
947
|
super(dialect, session, schema);
|
|
772
948
|
this.dialect = dialect;
|
|
@@ -774,6 +950,11 @@ class DatabendDatabase extends PgDatabase {
|
|
|
774
950
|
this.$client = client;
|
|
775
951
|
this.$databendClient = databendClient;
|
|
776
952
|
}
|
|
953
|
+
static [entityKind3] = "DatabendDatabase";
|
|
954
|
+
/** The underlying connection or pool */
|
|
955
|
+
$client;
|
|
956
|
+
/** The Databend Client instance (when created from DSN) */
|
|
957
|
+
$databendClient;
|
|
777
958
|
async close() {
|
|
778
959
|
if (isPool(this.$client) && this.$client.close) {
|
|
779
960
|
await this.$client.close();
|
|
@@ -785,147 +966,38 @@ class DatabendDatabase extends PgDatabase {
|
|
|
785
966
|
async transaction(transaction) {
|
|
786
967
|
return await this.session.transaction(transaction);
|
|
787
968
|
}
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
import { customType } from "drizzle-orm/pg-core";
|
|
791
|
-
var databendVariant = (name) => customType({
|
|
792
|
-
dataType() {
|
|
793
|
-
return "VARIANT";
|
|
794
|
-
},
|
|
795
|
-
toDriver(value) {
|
|
796
|
-
if (typeof value === "string") {
|
|
797
|
-
return value;
|
|
798
|
-
}
|
|
799
|
-
return JSON.stringify(value);
|
|
800
|
-
},
|
|
801
|
-
fromDriver(value) {
|
|
802
|
-
if (typeof value === "string") {
|
|
803
|
-
try {
|
|
804
|
-
return JSON.parse(value);
|
|
805
|
-
} catch {
|
|
806
|
-
return value;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
return value;
|
|
810
|
-
}
|
|
811
|
-
})(name);
|
|
812
|
-
var databendArray = (name, elementType) => customType({
|
|
813
|
-
dataType() {
|
|
814
|
-
return `ARRAY(${elementType})`;
|
|
815
|
-
},
|
|
816
|
-
toDriver(value) {
|
|
817
|
-
return value;
|
|
818
|
-
},
|
|
819
|
-
fromDriver(value) {
|
|
820
|
-
if (typeof value === "string") {
|
|
821
|
-
try {
|
|
822
|
-
return JSON.parse(value);
|
|
823
|
-
} catch {
|
|
824
|
-
return [];
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
return value;
|
|
828
|
-
}
|
|
829
|
-
})(name);
|
|
830
|
-
var databendTuple = (name, types) => customType({
|
|
831
|
-
dataType() {
|
|
832
|
-
return `TUPLE(${types.join(", ")})`;
|
|
833
|
-
},
|
|
834
|
-
toDriver(value) {
|
|
835
|
-
return value;
|
|
836
|
-
},
|
|
837
|
-
fromDriver(value) {
|
|
838
|
-
if (typeof value === "string") {
|
|
839
|
-
try {
|
|
840
|
-
return JSON.parse(value);
|
|
841
|
-
} catch {
|
|
842
|
-
return value;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
return value;
|
|
846
|
-
}
|
|
847
|
-
})(name);
|
|
848
|
-
var databendMap = (name, keyType, valueType) => customType({
|
|
849
|
-
dataType() {
|
|
850
|
-
return `MAP(${keyType}, ${valueType})`;
|
|
851
|
-
},
|
|
852
|
-
toDriver(value) {
|
|
853
|
-
return value;
|
|
854
|
-
},
|
|
855
|
-
fromDriver(value) {
|
|
856
|
-
if (typeof value === "string") {
|
|
857
|
-
try {
|
|
858
|
-
return JSON.parse(value);
|
|
859
|
-
} catch {
|
|
860
|
-
return value;
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
return value;
|
|
864
|
-
}
|
|
865
|
-
})(name);
|
|
866
|
-
var databendTimestamp = (name) => customType({
|
|
867
|
-
dataType() {
|
|
868
|
-
return "TIMESTAMP";
|
|
869
|
-
},
|
|
870
|
-
toDriver(value) {
|
|
871
|
-
if (value instanceof Date) {
|
|
872
|
-
return value.toISOString();
|
|
873
|
-
}
|
|
874
|
-
return value;
|
|
875
|
-
},
|
|
876
|
-
fromDriver(value) {
|
|
877
|
-
if (value instanceof Date) {
|
|
878
|
-
return value;
|
|
879
|
-
}
|
|
880
|
-
const str = String(value);
|
|
881
|
-
const hasOffset = str.endsWith("Z") || /[+-]\d{2}:?\d{2}$/.test(str);
|
|
882
|
-
const normalized = hasOffset ? str.replace(" ", "T") : `${str.replace(" ", "T")}Z`;
|
|
883
|
-
return new Date(normalized);
|
|
884
|
-
}
|
|
885
|
-
})(name);
|
|
886
|
-
var databendDate = (name) => customType({
|
|
887
|
-
dataType() {
|
|
888
|
-
return "DATE";
|
|
889
|
-
},
|
|
890
|
-
toDriver(value) {
|
|
891
|
-
if (value instanceof Date) {
|
|
892
|
-
return value.toISOString().slice(0, 10);
|
|
893
|
-
}
|
|
894
|
-
return value;
|
|
895
|
-
},
|
|
896
|
-
fromDriver(value) {
|
|
897
|
-
if (value instanceof Date) {
|
|
898
|
-
return value.toISOString().slice(0, 10);
|
|
899
|
-
}
|
|
900
|
-
return value.slice(0, 10);
|
|
901
|
-
}
|
|
902
|
-
})(name);
|
|
969
|
+
};
|
|
970
|
+
|
|
903
971
|
// src/migrator.ts
|
|
904
972
|
import { readMigrationFiles } from "drizzle-orm/migrator";
|
|
905
973
|
async function migrate(db, config) {
|
|
906
974
|
const migrationConfig = typeof config === "string" ? { migrationsFolder: config } : config;
|
|
907
975
|
const migrations = readMigrationFiles(migrationConfig);
|
|
908
|
-
await db.dialect.migrate(
|
|
976
|
+
await db.dialect.migrate(
|
|
977
|
+
migrations,
|
|
978
|
+
db.session,
|
|
979
|
+
migrationConfig
|
|
980
|
+
);
|
|
909
981
|
}
|
|
910
982
|
export {
|
|
911
|
-
|
|
912
|
-
migrate,
|
|
913
|
-
isPool,
|
|
914
|
-
executeOnClient,
|
|
915
|
-
executeArraysOnClient,
|
|
916
|
-
execOnClient,
|
|
917
|
-
drizzle,
|
|
918
|
-
databendVariant,
|
|
919
|
-
databendTuple,
|
|
920
|
-
databendTimestamp,
|
|
921
|
-
databendMap,
|
|
922
|
-
databendDate,
|
|
923
|
-
databendArray,
|
|
924
|
-
createDatabendConnectionPool,
|
|
925
|
-
closeClientConnection,
|
|
926
|
-
DatabendTransaction,
|
|
927
|
-
DatabendSession,
|
|
928
|
-
DatabendPreparedQuery,
|
|
983
|
+
DatabendDatabase,
|
|
929
984
|
DatabendDriver,
|
|
930
|
-
|
|
985
|
+
DatabendPreparedQuery,
|
|
986
|
+
DatabendSession,
|
|
987
|
+
DatabendTransaction,
|
|
988
|
+
closeClientConnection,
|
|
989
|
+
createDatabendConnectionPool,
|
|
990
|
+
databendArray,
|
|
991
|
+
databendDate,
|
|
992
|
+
databendMap,
|
|
993
|
+
databendTimestamp,
|
|
994
|
+
databendTuple,
|
|
995
|
+
databendVariant,
|
|
996
|
+
drizzle,
|
|
997
|
+
execOnClient,
|
|
998
|
+
executeArraysOnClient,
|
|
999
|
+
executeOnClient,
|
|
1000
|
+
isPool,
|
|
1001
|
+
migrate,
|
|
1002
|
+
prepareParams
|
|
931
1003
|
};
|