@flowblade/sqlduck 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -1
- package/dist/index.cjs +356 -35
- package/dist/index.d.cts +153 -4
- package/dist/index.d.mts +153 -4
- package/dist/index.mjs +349 -36
- package/package.json +11 -7
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { BIGINT, BOOLEAN, DOUBLE, DuckDBDataChunk, DuckDBTimestampValue, FLOAT, HUGEINT, INTEGER, SMALLINT, TIMESTAMP, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR } from "@duckdb/node-api";
|
|
2
|
+
import { getLogger } from "@logtape/logtape";
|
|
2
3
|
import * as z from "zod";
|
|
4
|
+
import { assertNever } from "@httpx/assert";
|
|
5
|
+
import { isPlainObject } from "@httpx/plain-object";
|
|
3
6
|
//#region src/helpers/duck-exec.ts
|
|
4
7
|
var DuckExec = class {
|
|
5
8
|
#conn;
|
|
@@ -117,6 +120,12 @@ const createOnDataAppendedCollector = () => {
|
|
|
117
120
|
};
|
|
118
121
|
};
|
|
119
122
|
//#endregion
|
|
123
|
+
//#region src/config/flowblade-logtape-sqlduck.config.ts
|
|
124
|
+
const flowbladeLogtapeSqlduckConfig = { categories: ["flowblade", "sqlduck"] };
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/logger/sqlduck-default-logtape-logger.ts
|
|
127
|
+
const sqlduckDefaultLogtapeLogger = getLogger(flowbladeLogtapeSqlduckConfig.categories);
|
|
128
|
+
//#endregion
|
|
120
129
|
//#region src/table/get-duckdb-number-column-type.ts
|
|
121
130
|
const isFloatValue = (value) => {
|
|
122
131
|
if (!Number.isFinite(value)) return true;
|
|
@@ -150,6 +159,16 @@ const createOptions = {
|
|
|
150
159
|
CREATE_OR_REPLACE: "CREATE OR REPLACE TABLE",
|
|
151
160
|
IF_NOT_EXISTS: "CREATE TABLE IF NOT EXISTS"
|
|
152
161
|
};
|
|
162
|
+
const duckDbTypesMap = new Map([
|
|
163
|
+
["VARCHAR", VARCHAR],
|
|
164
|
+
["BIGINT", BIGINT],
|
|
165
|
+
["TIMESTAMP", TIMESTAMP],
|
|
166
|
+
["UUID", UUID],
|
|
167
|
+
["BOOLEAN", BOOLEAN],
|
|
168
|
+
["INTEGER", INTEGER],
|
|
169
|
+
["DOUBLE", DOUBLE],
|
|
170
|
+
["FLOAT", FLOAT]
|
|
171
|
+
]);
|
|
153
172
|
const getTableCreateFromZod = (params) => {
|
|
154
173
|
const { table, schema, options } = params;
|
|
155
174
|
const { create = "CREATE" } = options ?? {};
|
|
@@ -162,9 +181,10 @@ const getTableCreateFromZod = (params) => {
|
|
|
162
181
|
if (json.properties === void 0) throw new TypeError("Schema must have at least one property");
|
|
163
182
|
const columnTypesMap = /* @__PURE__ */ new Map();
|
|
164
183
|
for (const [columnName, def] of Object.entries(json.properties)) {
|
|
165
|
-
const { type, nullable, format, primaryKey, minimum, maximum } = def;
|
|
184
|
+
const { type, duckdbType, nullable, format, primaryKey, minimum, maximum } = def;
|
|
166
185
|
const c = { name: columnName };
|
|
167
|
-
|
|
186
|
+
if (duckdbType !== void 0 && duckDbTypesMap.has(duckdbType)) c.duckdbType = duckDbTypesMap.get(duckdbType);
|
|
187
|
+
else switch (type) {
|
|
168
188
|
case "string":
|
|
169
189
|
switch (format) {
|
|
170
190
|
case "date-time":
|
|
@@ -220,15 +240,18 @@ const getTableCreateFromZod = (params) => {
|
|
|
220
240
|
//#endregion
|
|
221
241
|
//#region src/table/create-table-from-zod.ts
|
|
222
242
|
const createTableFromZod = async (params) => {
|
|
223
|
-
const { conn, table, schema, options } = params;
|
|
243
|
+
const { conn, table, schema, options, logger = sqlduckDefaultLogtapeLogger } = params;
|
|
224
244
|
const { ddl, columnTypes } = getTableCreateFromZod({
|
|
225
245
|
table,
|
|
226
246
|
schema,
|
|
227
247
|
options
|
|
228
248
|
});
|
|
249
|
+
logger.debug(`Generate DDL for table '${table.getFullName()}'`, { ddl });
|
|
229
250
|
try {
|
|
230
251
|
await conn.run(ddl);
|
|
252
|
+
logger.info(`Table '${table.getFullName()}' successfully created`, { ddl });
|
|
231
253
|
} catch (e) {
|
|
254
|
+
logger.error(`Failed to create table '${table.getFullName()}': ${e.message}`, { ddl });
|
|
232
255
|
throw new Error(`Failed to create table '${table.getFullName()}': ${e.message}`, { cause: e });
|
|
233
256
|
}
|
|
234
257
|
return {
|
|
@@ -281,11 +304,11 @@ async function* rowsToColumnsChunks(params) {
|
|
|
281
304
|
//#endregion
|
|
282
305
|
//#region src/sql-duck.ts
|
|
283
306
|
var SqlDuck = class {
|
|
284
|
-
#
|
|
307
|
+
#conn;
|
|
285
308
|
#logger;
|
|
286
309
|
constructor(params) {
|
|
287
|
-
this.#
|
|
288
|
-
this.#logger = params.logger;
|
|
310
|
+
this.#conn = params.conn;
|
|
311
|
+
this.#logger = params.logger ?? sqlduckDefaultLogtapeLogger;
|
|
289
312
|
}
|
|
290
313
|
/**
|
|
291
314
|
* Create a table from a Zod schema and fill it with data from a row stream.
|
|
@@ -329,12 +352,12 @@ var SqlDuck = class {
|
|
|
329
352
|
if (!Number.isSafeInteger(chunkSize) || chunkSize < 1 || chunkSize > 2048) throw new Error("chunkSize must be a number between 1 and 2048");
|
|
330
353
|
const timeStart = Date.now();
|
|
331
354
|
const { columnTypes, ddl } = await createTableFromZod({
|
|
332
|
-
conn: this.#
|
|
355
|
+
conn: this.#conn,
|
|
333
356
|
schema,
|
|
334
357
|
table,
|
|
335
358
|
options: createOptions
|
|
336
359
|
});
|
|
337
|
-
const appender = await this.#
|
|
360
|
+
const appender = await this.#conn.createAppender(table.tableName, table.schemaName, table.databaseName);
|
|
338
361
|
const chunkTypes = Array.from(columnTypes.values());
|
|
339
362
|
let totalRows = 0;
|
|
340
363
|
const dataAppendedCollector = createOnDataAppendedCollector();
|
|
@@ -342,29 +365,74 @@ var SqlDuck = class {
|
|
|
342
365
|
rows: rowStream,
|
|
343
366
|
chunkSize
|
|
344
367
|
});
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
368
|
+
try {
|
|
369
|
+
for await (const dataChunk of columnStream) {
|
|
370
|
+
const chunk = DuckDBDataChunk.create(chunkTypes);
|
|
371
|
+
this.#logger.debug(`Inserting chunk of ${dataChunk.length} rows`, { table: table.getFullName() });
|
|
372
|
+
totalRows += dataChunk?.[0]?.length ?? 0;
|
|
373
|
+
chunk.setColumns(dataChunk);
|
|
374
|
+
appender.appendDataChunk(chunk);
|
|
375
|
+
appender.flushSync();
|
|
376
|
+
if (onDataAppended !== void 0) {
|
|
377
|
+
const payload = dataAppendedCollector(totalRows);
|
|
378
|
+
if (isOnDataAppendedAsyncCb(onDataAppended)) await onDataAppended(payload);
|
|
379
|
+
else onDataAppended(payload);
|
|
380
|
+
}
|
|
356
381
|
}
|
|
382
|
+
appender.closeSync();
|
|
383
|
+
const timeMs = Math.round(Date.now() - timeStart);
|
|
384
|
+
this.#logger.info(`Successfully appended ${totalRows} rows into '${table.getFullName()}' in ${timeMs}ms`, {
|
|
385
|
+
table: table.getFullName(),
|
|
386
|
+
timeMs,
|
|
387
|
+
totalRows
|
|
388
|
+
});
|
|
389
|
+
return {
|
|
390
|
+
timeMs,
|
|
391
|
+
totalRows,
|
|
392
|
+
createTableDDL: ddl
|
|
393
|
+
};
|
|
394
|
+
} catch (e) {
|
|
395
|
+
appender.closeSync();
|
|
396
|
+
const msg = `Failed to append data into table '${table.getFullName()}' - ${e?.message ?? ""}`;
|
|
397
|
+
this.#logger.error(msg, { table: table.getFullName() });
|
|
398
|
+
throw new Error(msg, { cause: e });
|
|
357
399
|
}
|
|
358
|
-
|
|
400
|
+
};
|
|
401
|
+
};
|
|
402
|
+
//#endregion
|
|
403
|
+
//#region src/utils/zod-codecs.ts
|
|
404
|
+
const zodCodecs = {
|
|
405
|
+
dateToString: z.codec(z.date(), z.iso.datetime(), {
|
|
406
|
+
decode: (date) => date.toISOString(),
|
|
407
|
+
encode: (isoString) => new Date(isoString)
|
|
408
|
+
}),
|
|
409
|
+
bigintToString: z.codec(z.bigint(), z.string().meta({ format: "int64" }), {
|
|
410
|
+
decode: (bigint) => bigint.toString(),
|
|
411
|
+
encode: BigInt
|
|
412
|
+
})
|
|
413
|
+
};
|
|
414
|
+
//#endregion
|
|
415
|
+
//#region src/objects/database.ts
|
|
416
|
+
var Database = class {
|
|
417
|
+
#params;
|
|
418
|
+
get alias() {
|
|
419
|
+
return this.#params.alias;
|
|
420
|
+
}
|
|
421
|
+
constructor(params) {
|
|
422
|
+
this.#params = params;
|
|
423
|
+
}
|
|
424
|
+
toJson() {
|
|
359
425
|
return {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
createTableDDL: ddl
|
|
426
|
+
type: "database",
|
|
427
|
+
params: { alias: this.#params.alias }
|
|
363
428
|
};
|
|
364
|
-
}
|
|
429
|
+
}
|
|
430
|
+
[Symbol.toStringTag]() {
|
|
431
|
+
return this.alias;
|
|
432
|
+
}
|
|
365
433
|
};
|
|
366
434
|
//#endregion
|
|
367
|
-
//#region src/
|
|
435
|
+
//#region src/objects/table.ts
|
|
368
436
|
var Table = class Table {
|
|
369
437
|
#fqTable;
|
|
370
438
|
get tableName() {
|
|
@@ -406,16 +474,261 @@ var Table = class Table {
|
|
|
406
474
|
};
|
|
407
475
|
};
|
|
408
476
|
//#endregion
|
|
409
|
-
//#region src/
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
477
|
+
//#region src/validation/core/duckdb-reserved-keywords.ts
|
|
478
|
+
/**
|
|
479
|
+
* DuckDB reserved keywords that cannot be used as unquoted identifiers.
|
|
480
|
+
* @see https://duckdb.org/docs/sql/keywords-and-identifiers.html
|
|
481
|
+
*/
|
|
482
|
+
const duckdbReservedKeywords = [
|
|
483
|
+
"ALL",
|
|
484
|
+
"ANALYSE",
|
|
485
|
+
"ANALYZE",
|
|
486
|
+
"AND",
|
|
487
|
+
"ANY",
|
|
488
|
+
"ARRAY",
|
|
489
|
+
"AS",
|
|
490
|
+
"ASC",
|
|
491
|
+
"ASYMMETRIC",
|
|
492
|
+
"BOTH",
|
|
493
|
+
"CASE",
|
|
494
|
+
"CAST",
|
|
495
|
+
"CHECK",
|
|
496
|
+
"COLLATE",
|
|
497
|
+
"COLUMN",
|
|
498
|
+
"CONSTRAINT",
|
|
499
|
+
"CREATE",
|
|
500
|
+
"CROSS",
|
|
501
|
+
"CURRENT_CATALOG",
|
|
502
|
+
"CURRENT_DATE",
|
|
503
|
+
"CURRENT_ROLE",
|
|
504
|
+
"CURRENT_SCHEMA",
|
|
505
|
+
"CURRENT_TIME",
|
|
506
|
+
"CURRENT_TIMESTAMP",
|
|
507
|
+
"CURRENT_USER",
|
|
508
|
+
"DEFAULT",
|
|
509
|
+
"DEFERRABLE",
|
|
510
|
+
"DESC",
|
|
511
|
+
"DISTINCT",
|
|
512
|
+
"DO",
|
|
513
|
+
"ELSE",
|
|
514
|
+
"END",
|
|
515
|
+
"EXCEPT",
|
|
516
|
+
"EXISTS",
|
|
517
|
+
"EXTRACT",
|
|
518
|
+
"FALSE",
|
|
519
|
+
"FETCH",
|
|
520
|
+
"FOR",
|
|
521
|
+
"FOREIGN",
|
|
522
|
+
"FROM",
|
|
523
|
+
"GRANT",
|
|
524
|
+
"GROUP",
|
|
525
|
+
"HAVING",
|
|
526
|
+
"IF",
|
|
527
|
+
"ILIKE",
|
|
528
|
+
"IN",
|
|
529
|
+
"INITIALLY",
|
|
530
|
+
"INNER",
|
|
531
|
+
"INTERSECT",
|
|
532
|
+
"INTO",
|
|
533
|
+
"IS",
|
|
534
|
+
"ISNULL",
|
|
535
|
+
"JOIN",
|
|
536
|
+
"LATERAL",
|
|
537
|
+
"LEADING",
|
|
538
|
+
"LEFT",
|
|
539
|
+
"LIKE",
|
|
540
|
+
"LIMIT",
|
|
541
|
+
"LOCALTIME",
|
|
542
|
+
"LOCALTIMESTAMP",
|
|
543
|
+
"NATURAL",
|
|
544
|
+
"NOT",
|
|
545
|
+
"NOTNULL",
|
|
546
|
+
"NULL",
|
|
547
|
+
"OFFSET",
|
|
548
|
+
"ON",
|
|
549
|
+
"ONLY",
|
|
550
|
+
"OR",
|
|
551
|
+
"ORDER",
|
|
552
|
+
"OUTER",
|
|
553
|
+
"OVERLAPS",
|
|
554
|
+
"PLACING",
|
|
555
|
+
"PRIMARY",
|
|
556
|
+
"REFERENCES",
|
|
557
|
+
"RETURNING",
|
|
558
|
+
"RIGHT",
|
|
559
|
+
"ROW",
|
|
560
|
+
"SELECT",
|
|
561
|
+
"SESSION_USER",
|
|
562
|
+
"SIMILAR",
|
|
563
|
+
"SOME",
|
|
564
|
+
"SYMMETRIC",
|
|
565
|
+
"TABLE",
|
|
566
|
+
"THEN",
|
|
567
|
+
"TO",
|
|
568
|
+
"TRAILING",
|
|
569
|
+
"TRUE",
|
|
570
|
+
"UNION",
|
|
571
|
+
"UNIQUE",
|
|
572
|
+
"USING",
|
|
573
|
+
"VARIADIC",
|
|
574
|
+
"VERBOSE",
|
|
575
|
+
"WHEN",
|
|
576
|
+
"WHERE",
|
|
577
|
+
"WINDOW",
|
|
578
|
+
"WITH"
|
|
579
|
+
];
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/validation/zod/duckdb-valid-names.schemas.ts
|
|
582
|
+
const duckdbMaximumObjectNameLength = 120;
|
|
583
|
+
const duckDbObjectNameRegex = /^[a-z_]\w*$/i;
|
|
584
|
+
const duckdbReservedKeywordsSet = new Set(duckdbReservedKeywords.map((k) => k.toUpperCase()));
|
|
585
|
+
const duckTableNameSchema = z.string().min(1).max(duckdbMaximumObjectNameLength).regex(duckDbObjectNameRegex, "Table name must start with a letter or underscore, and contain only letters, numbers and underscores").refine((value) => !duckdbReservedKeywordsSet.has(value.toUpperCase()), { error: `Value is a DuckDB reserved keyword and cannot be used as a table name` });
|
|
586
|
+
const duckTableAliasSchema = duckTableNameSchema;
|
|
587
|
+
//#endregion
|
|
588
|
+
//#region src/manager/database/duck-database-manager.schemas.ts
|
|
589
|
+
const duckdbAttachOptionsSchema = z.strictObject({
|
|
590
|
+
ACCESS_MODE: z.optional(z.enum([
|
|
591
|
+
"READ_ONLY",
|
|
592
|
+
"READ_WRITE",
|
|
593
|
+
"AUTOMATIC"
|
|
594
|
+
])),
|
|
595
|
+
COMPRESS: z.optional(z.enum(["true", "false"])),
|
|
596
|
+
TYPE: z.optional(z.enum(["DUCKDB", "SQLITE"])),
|
|
597
|
+
BLOCK_SIZE: z.optional(z.int32().min(16384).max(262144)),
|
|
598
|
+
ROW_GROUP_SIZE: z.optional(z.int32().positive()),
|
|
599
|
+
STORAGE_VERSION: z.optional(z.string().startsWith("v").regex(/^v?\d{1,4}\.\d{1,4}\.\d{1,4}$/)),
|
|
600
|
+
ENCRYPTION_KEY: z.optional(z.string().min(8)),
|
|
601
|
+
ENCRYPTION_CIPHER: z.optional(z.enum([
|
|
602
|
+
"CBC",
|
|
603
|
+
"CTR",
|
|
604
|
+
"GCM"
|
|
605
|
+
]))
|
|
606
|
+
});
|
|
607
|
+
const duckDatabaseManagerDbParamsSchema = z.discriminatedUnion("type", [z.strictObject({
|
|
608
|
+
type: z.literal(":memory:"),
|
|
609
|
+
alias: duckTableAliasSchema,
|
|
610
|
+
options: z.optional(duckdbAttachOptionsSchema)
|
|
611
|
+
}), z.strictObject({
|
|
612
|
+
type: z.literal("duckdb"),
|
|
613
|
+
path: z.string().min(4).endsWith(".db"),
|
|
614
|
+
alias: duckTableAliasSchema,
|
|
615
|
+
options: z.optional(duckdbAttachOptionsSchema)
|
|
616
|
+
})]);
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region src/manager/database/commands/duck-database-attach-command.ts
|
|
619
|
+
var DuckDatabaseAttachCommand = class {
|
|
620
|
+
options;
|
|
621
|
+
dbParams;
|
|
622
|
+
constructor(dbParams, options) {
|
|
623
|
+
this.dbParams = dbParams;
|
|
624
|
+
this.options = options ?? {};
|
|
625
|
+
}
|
|
626
|
+
getRawSql = () => {
|
|
627
|
+
const dbParams = this.dbParams;
|
|
628
|
+
const parts = ["ATTACH", this.options.behaviour].filter(Boolean);
|
|
629
|
+
const { type, alias } = dbParams;
|
|
630
|
+
switch (type) {
|
|
631
|
+
case ":memory:":
|
|
632
|
+
parts.push("':memory:'");
|
|
633
|
+
break;
|
|
634
|
+
case "duckdb":
|
|
635
|
+
parts.push(`'${dbParams.path}'`);
|
|
636
|
+
break;
|
|
637
|
+
default: assertNever(type);
|
|
638
|
+
}
|
|
639
|
+
if (alias !== null) parts.push("AS", `${alias}`);
|
|
640
|
+
const options = isPlainObject(dbParams.options) ? Object.entries(dbParams.options).map(([key, value]) => {
|
|
641
|
+
return key === "ACCESS_MODE" ? value : `${key} '${value}'`;
|
|
642
|
+
}) : [];
|
|
643
|
+
if (options.length > 0) parts.push(`(${options.join(", ")})`);
|
|
644
|
+
return parts.filter(Boolean).join(" ");
|
|
645
|
+
};
|
|
646
|
+
};
|
|
647
|
+
//#endregion
|
|
648
|
+
//#region src/manager/database/duck-database-manager.ts
|
|
649
|
+
var DuckDatabaseManager = class {
|
|
650
|
+
#conn;
|
|
651
|
+
#logger;
|
|
652
|
+
constructor(conn, params) {
|
|
653
|
+
this.#conn = conn;
|
|
654
|
+
this.#logger = params?.logger ?? sqlduckDefaultLogtapeLogger.with({ source: "DuckDatabaseManager" });
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Attach a database to the current connection
|
|
658
|
+
*
|
|
659
|
+
* @example
|
|
660
|
+
* ```typescript
|
|
661
|
+
* const dbManager = new DuckDatabaseManager(conn);
|
|
662
|
+
* const database = dbManager.attach({
|
|
663
|
+
* type: ':memory:', // can be 'duckdb', 's3'...
|
|
664
|
+
* alias: 'mydb',
|
|
665
|
+
* options: { COMPRESS: 'true' }
|
|
666
|
+
* });
|
|
667
|
+
*
|
|
668
|
+
* console.log(database.alias); // 'mydb'
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
671
|
+
attach = async (dbParams, options) => {
|
|
672
|
+
const params = z.parse(duckDatabaseManagerDbParamsSchema, dbParams);
|
|
673
|
+
const rawSql = new DuckDatabaseAttachCommand(params, options).getRawSql();
|
|
674
|
+
await this.#executeRawSqlCommand(`attach(${params.alias})`, rawSql);
|
|
675
|
+
return new Database({ alias: params.alias });
|
|
676
|
+
};
|
|
677
|
+
attachOrReplace = async (dbParams) => {
|
|
678
|
+
return this.attach(dbParams, { behaviour: "OR REPLACE" });
|
|
679
|
+
};
|
|
680
|
+
attachIfNotExists = async (dbParams) => {
|
|
681
|
+
return this.attach(dbParams, { behaviour: "IF NOT EXISTS" });
|
|
682
|
+
};
|
|
683
|
+
showDatabases = async () => {
|
|
684
|
+
return await this.#executeRawSqlCommand("showDatabases()", `SHOW DATABASES`);
|
|
685
|
+
};
|
|
686
|
+
detach = async (dbAlias) => {
|
|
687
|
+
const safeAlias = z.parse(duckTableAliasSchema, dbAlias);
|
|
688
|
+
await this.#executeRawSqlCommand(`detach(${safeAlias})`, `DETACH ${safeAlias}`);
|
|
689
|
+
return true;
|
|
690
|
+
};
|
|
691
|
+
detachIfExists = async (dbAlias) => {
|
|
692
|
+
const safeAlias = z.parse(duckTableAliasSchema, dbAlias);
|
|
693
|
+
await this.#executeRawSqlCommand(`detachIfExists(${safeAlias})`, `DETACH IF EXISTS ${safeAlias}`);
|
|
694
|
+
return true;
|
|
695
|
+
};
|
|
696
|
+
/**
|
|
697
|
+
* The statistics recomputed by the ANALYZE statement are only used for join order optimization.
|
|
698
|
+
*
|
|
699
|
+
* It is therefore recommended to recompute these statistics for improved join orders,
|
|
700
|
+
* especially after performing large updates (inserts and/or deletes).
|
|
701
|
+
*
|
|
702
|
+
* @link https://duckdb.org/docs/stable/sql/statements/analyze
|
|
703
|
+
*/
|
|
704
|
+
analyze = async () => {
|
|
705
|
+
await this.#executeRawSqlCommand("analyze()", "ANALYZE");
|
|
706
|
+
return true;
|
|
707
|
+
};
|
|
708
|
+
checkpoint = async (dbAlias) => {
|
|
709
|
+
const safeAlias = z.parse(duckTableAliasSchema, dbAlias);
|
|
710
|
+
await this.#executeRawSqlCommand(`checkpoint(${safeAlias})`, `CHECKPOINT ${safeAlias}`);
|
|
711
|
+
return true;
|
|
712
|
+
};
|
|
713
|
+
#executeRawSqlCommand = async (name, rawSql) => {
|
|
714
|
+
const startTime = Date.now();
|
|
715
|
+
try {
|
|
716
|
+
const result = await this.#conn.runAndReadAll(rawSql);
|
|
717
|
+
const timeMs = Math.round(Date.now() - startTime);
|
|
718
|
+
const data = result.getRowObjectsJS();
|
|
719
|
+
this.#logger.info(`DuckDatabaseManager.${name} in ${timeMs}ms`, { timeMs });
|
|
720
|
+
return data;
|
|
721
|
+
} catch (e) {
|
|
722
|
+
const msg = `DuckDatabaseManager: failed to run "${name}" - ${e?.message ?? ""}`;
|
|
723
|
+
const timeMs = Math.round(Date.now() - startTime);
|
|
724
|
+
this.#logger.error(msg, {
|
|
725
|
+
name,
|
|
726
|
+
sql: rawSql,
|
|
727
|
+
timeMs
|
|
728
|
+
});
|
|
729
|
+
throw new Error(msg, { cause: e });
|
|
730
|
+
}
|
|
731
|
+
};
|
|
419
732
|
};
|
|
420
733
|
//#endregion
|
|
421
|
-
export { DuckMemory, SqlDuck, Table, getTableCreateFromZod, zodCodecs };
|
|
734
|
+
export { Database, DuckDatabaseManager, DuckMemory, SqlDuck, Table, duckDatabaseManagerDbParamsSchema, duckTableAliasSchema, duckTableNameSchema, duckdbReservedKeywords, flowbladeLogtapeSqlduckConfig, getTableCreateFromZod, sqlduckDefaultLogtapeLogger, zodCodecs };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flowblade/sqlduck",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -57,8 +57,11 @@
|
|
|
57
57
|
"@flowblade/core": "^0.2.26",
|
|
58
58
|
"@flowblade/source-duckdb": "^0.20.1",
|
|
59
59
|
"@flowblade/sql-tag": "^0.3.2",
|
|
60
|
+
"@httpx/assert": "^0.16.8",
|
|
61
|
+
"@httpx/plain-object": "^2.1.8",
|
|
62
|
+
"@logtape/logtape": "^2.0.5",
|
|
60
63
|
"@standard-schema/spec": "^1.1.0",
|
|
61
|
-
"p-
|
|
64
|
+
"p-queue": "9.1.0",
|
|
62
65
|
"valibot": "^1.3.1",
|
|
63
66
|
"zod": "^4.3.6"
|
|
64
67
|
},
|
|
@@ -67,7 +70,7 @@
|
|
|
67
70
|
},
|
|
68
71
|
"devDependencies": {
|
|
69
72
|
"@belgattitude/eslint-config-bases": "8.10.0",
|
|
70
|
-
"@dotenvx/dotenvx": "1.
|
|
73
|
+
"@dotenvx/dotenvx": "1.57.2",
|
|
71
74
|
"@duckdb/node-api": "1.5.1-r.1",
|
|
72
75
|
"@faker-js/faker": "10.3.0",
|
|
73
76
|
"@flowblade/source-kysely": "^1.3.0",
|
|
@@ -81,7 +84,7 @@
|
|
|
81
84
|
"@types/node": "25.5.0",
|
|
82
85
|
"@typescript-eslint/eslint-plugin": "8.57.2",
|
|
83
86
|
"@typescript-eslint/parser": "8.57.2",
|
|
84
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
87
|
+
"@typescript/native-preview": "7.0.0-dev.20260324.1",
|
|
85
88
|
"@vitest/coverage-v8": "4.1.1",
|
|
86
89
|
"@vitest/ui": "4.1.1",
|
|
87
90
|
"ansis": "4.2.0",
|
|
@@ -89,6 +92,7 @@
|
|
|
89
92
|
"core-js": "3.49.0",
|
|
90
93
|
"cross-env": "10.1.0",
|
|
91
94
|
"es-check": "9.6.3",
|
|
95
|
+
"es-toolkit": "1.45.1",
|
|
92
96
|
"esbuild": "0.27.4",
|
|
93
97
|
"eslint": "8.57.1",
|
|
94
98
|
"execa": "9.6.1",
|
|
@@ -105,11 +109,11 @@
|
|
|
105
109
|
"tarn": "3.0.2",
|
|
106
110
|
"tedious": "19.2.1",
|
|
107
111
|
"testcontainers": "11.13.0",
|
|
108
|
-
"tsdown": "0.21.
|
|
112
|
+
"tsdown": "0.21.5",
|
|
109
113
|
"tsx": "4.21.0",
|
|
110
|
-
"typedoc": "0.28.
|
|
114
|
+
"typedoc": "0.28.18",
|
|
111
115
|
"typedoc-plugin-markdown": "4.11.0",
|
|
112
|
-
"typescript": "
|
|
116
|
+
"typescript": "6.0.2",
|
|
113
117
|
"vitest": "4.1.1"
|
|
114
118
|
},
|
|
115
119
|
"files": [
|