@flowblade/sqlduck 0.34.0 → 0.36.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.
@@ -255,7 +255,7 @@ type MapRelaxedZodSchemaField<T extends z.ZodTypeAny> = T extends z.ZodDate ? st
255
255
  type InferZodRelaxedDataSchema<T extends z.ZodObject<any>> = { [K in keyof T['shape']]: MapRelaxedZodSchemaField<T['shape'][K]>; };
256
256
  //#endregion
257
257
  //#region src/validation/zod/table-schema-zod.d.ts
258
- type ZodSchemaSupportedTypes = z.ZodString | z.ZodNumber | z.ZodInt | z.ZodInt32 | z.ZodUInt32 | z.ZodBigInt | z.ZodBoolean | z.ZodDate | z.ZodISODateTime | z.ZodISOTime | z.ZodISODate | z.ZodEmail | z.ZodURL | z.ZodUUID | z.ZodCUID | z.ZodCUID2 | z.ZodULID | z.ZodEnum;
258
+ type ZodSchemaSupportedTypes = z.ZodString | z.ZodNumber | z.ZodInt | z.ZodInt32 | z.ZodUInt32 | z.ZodBigInt | z.ZodBoolean | z.ZodDate | z.ZodISODateTime | z.ZodISOTime | z.ZodISODate | z.ZodEmail | z.ZodURL | z.ZodUUID | z.ZodCUID | z.ZodCUID2 | z.ZodULID | z.ZodEnum | z.ZodArray;
259
259
  type TableSchemaZod = z.ZodObject<Record<string, ZodSchemaSupportedTypes | z.ZodNullable<ZodSchemaSupportedTypes> | z.ZodCodec | z.ZodNullable<z.ZodCodec>>>;
260
260
  //#endregion
261
261
  export { duckDsnZodSchema as a, assertValidTableName as c, duckConnectionParamsZodSchema as d, duckValidatorsZod as i, DuckConnectionParams as l, InferZodRelaxedDataSchema as n, assertValidAliasName as o, ensureZodTableSchema as r, assertValidSchemaName as s, TableSchemaZod as t, duckAllConnectionOptionsZodSchema as u };
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { l as DuckConnectionParams, n as InferZodRelaxedDataSchema, t as TableSchemaZod } from "./index-BKsJVkXZ.mjs";
2
- import { DuckDBConnection, DuckDBType } from "@duckdb/node-api";
1
+ import { l as DuckConnectionParams, n as InferZodRelaxedDataSchema, t as TableSchemaZod } from "./index-Z9LfGsCV.mjs";
2
+ import { DuckDBConnection, DuckDBDecimalType, DuckDBListType, DuckDBType } from "@duckdb/node-api";
3
3
  import { Logger } from "@logtape/logtape";
4
4
  import * as z from "zod";
5
5
  import { ZodObject } from "zod";
@@ -91,6 +91,8 @@ type GetTableCreateFromZodParams<TSchema extends TableSchemaZod> = {
91
91
  schema: TSchema;
92
92
  options?: TableCreateOptions;
93
93
  };
94
+ declare const duckDbTypes: [["VARCHAR", import("@duckdb/node-api").DuckDBVarCharType], ["BIGINT", import("@duckdb/node-api").DuckDBBigIntType], ["UBIGINT", import("@duckdb/node-api").DuckDBUBigIntType], ["HUGEINT", import("@duckdb/node-api").DuckDBHugeIntType], ["TIMESTAMP", import("@duckdb/node-api").DuckDBTimestampType], ["TIMESTAMP_MS", import("@duckdb/node-api").DuckDBTimestampMillisecondsType], ["UUID", import("@duckdb/node-api").DuckDBUUIDType], ["BOOLEAN", import("@duckdb/node-api").DuckDBBooleanType], ["INTEGER", import("@duckdb/node-api").DuckDBIntegerType], ["DOUBLE", import("@duckdb/node-api").DuckDBDoubleType], ["FLOAT", import("@duckdb/node-api").DuckDBFloatType], ["DATE", import("@duckdb/node-api").DuckDBDateType], ["DECIMAL", DuckDBDecimalType], ["VARCHAR[]", DuckDBListType], ["INTEGER[]", DuckDBListType], ["BOOLEAN[]", DuckDBListType], ["BIGINT[]", DuckDBListType], ["UBIGINT[]", DuckDBListType], ["UUID[]", DuckDBListType], ["DATE[]", DuckDBListType], ["TIMESTAMP[]", DuckDBListType], ["TIMESTAMP_MS[]", DuckDBListType], ["DOUBLE[]", DuckDBListType], ["FLOAT[]", DuckDBListType]];
95
+ type SupportedCustomDuckDbTypes = (typeof duckDbTypes)[number][0];
94
96
  declare const getTableCreateFromZod: <TSchema extends TableSchemaZod>(params: GetTableCreateFromZodParams<TSchema>) => TableCreateFromZodResult<TSchema>;
95
97
  //#endregion
96
98
  //#region src/sql-duck.d.ts
@@ -381,6 +383,14 @@ declare class DuckDatabaseManager {
381
383
  analyze: () => Promise<boolean>;
382
384
  checkpoint: (dbAlias: string) => Promise<boolean>;
383
385
  vacuum: () => Promise<boolean>;
386
+ getCurrentDatabase: () => Promise<string | null>;
387
+ getCurrentSchema: () => Promise<string | null>;
388
+ getCurrentCatalog: () => Promise<string | null>;
389
+ /**
390
+ * Set the default database to use
391
+ * @throws if the database alias does not exist
392
+ */
393
+ use: (dbAlias: string) => Promise<true>;
384
394
  /**
385
395
  * Helper to create an initial database file.
386
396
  */
@@ -430,4 +440,4 @@ declare const flowbladeLogtapeSqlduckConfig: {
430
440
  //#region src/logger/sqlduck-default-logtape-logger.d.ts
431
441
  declare const sqlduckDefaultLogtapeLogger: import("@logtape/logtape").Logger;
432
442
  //#endregion
433
- export { Database, type DuckConnectionParams, DuckDatabaseManager, DuckExtensionsManager, DuckMemory, type DuckMemoryTag, DuckSettingsManager, type DuckdbReservedKeywords, type OnChunkAppendedCb, type OnChunkAppendedStats, SqlDuck, type SqlDuckParams, Table, type ToTableParams, duckReservedKeywords, flowbladeLogtapeSqlduckConfig, getTableCreateFromZod, sqlduckDefaultLogtapeLogger, zodCodecs };
443
+ export { Database, type DuckConnectionParams, DuckDatabaseManager, DuckExtensionsManager, DuckMemory, type DuckMemoryTag, DuckSettingsManager, type DuckdbReservedKeywords, type OnChunkAppendedCb, type OnChunkAppendedStats, SqlDuck, type SqlDuckParams, type SupportedCustomDuckDbTypes, Table, type ToTableParams, duckReservedKeywords, flowbladeLogtapeSqlduckConfig, getTableCreateFromZod, sqlduckDefaultLogtapeLogger, zodCodecs };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as sqlduckDefaultLogtapeLogger, r as flowbladeLogtapeSqlduckConfig, t as FileSystemUtils } from "./file-system-utils-DT6smf4C.mjs";
2
2
  import { t as duckReservedKeywords } from "./duck-reserved-keywords-BzYA7jvN.mjs";
3
3
  import { c as duckValidatorsZod, r as assertValidAliasName, s as duckConnectionParamsZodSchema } from "./zod-DB5FHzXW.mjs";
4
- import { BIGINT, BOOLEAN, DATE, DECIMAL, DOUBLE, DuckDBDataChunk, DuckDBDateValue, DuckDBDecimalType, DuckDBDecimalValue, DuckDBInstanceCache, DuckDBTimestampMillisecondsValue, DuckDBTypeId, ENUM, FLOAT, HUGEINT, INTEGER, SMALLINT, TIMESTAMP, TIMESTAMP_MS, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR } from "@duckdb/node-api";
4
+ import { BIGINT, BOOLEAN, DATE, DECIMAL, DOUBLE, DuckDBDataChunk, DuckDBDateValue, DuckDBDecimalType, DuckDBDecimalValue, DuckDBInstanceCache, DuckDBListType, DuckDBTimestampMillisecondsValue, DuckDBTypeId, ENUM, FLOAT, HUGEINT, INTEGER, LIST, SMALLINT, TIMESTAMP, TIMESTAMP_MS, TINYINT, UBIGINT, UHUGEINT, UINTEGER, USMALLINT, UTINYINT, UUID, VARCHAR } from "@duckdb/node-api";
5
5
  import * as z from "zod";
6
6
  import { assertNever } from "@httpx/assert";
7
7
  import { isPlainObject } from "@httpx/plain-object";
@@ -425,22 +425,33 @@ var DuckDatabaseManager = class {
425
425
  */
426
426
  attach = async (dbParams, options) => {
427
427
  const params = duckConnectionParamsZodSchema.parse(dbParams);
428
- const rawSql = new DuckDatabaseAttachCommand(params, options).getRawSql();
429
- const { behaviour } = options ?? {};
428
+ const rawSql = new DuckDatabaseAttachCommand(params, { behaviour: options?.behaviour }).getRawSql();
429
+ const restoreCurrentDb = await this.getCurrentDatabase();
430
+ const tempDbNameHack = "temp_run_detach_if_attached_hack_db";
431
+ let isTempDbNameHackCreated = false;
430
432
  try {
431
- await this.#executor.getRowObjectsJS([
432
- `attach(${params.alias}`,
433
- behaviour ?? null,
434
- ")"
435
- ].filter(Boolean).join(","), rawSql);
433
+ await this.#executor.getRowObjectsJS([`attach(${params.alias}`, options === void 0 ? null : JSON.stringify(options)].filter(Boolean).join(",") + ")", rawSql);
436
434
  } catch (e) {
437
435
  if (options?.behaviour === "OR REPLACE" && options?.runDetachIfAttachOrReplaceFailWithAlreadyAttached === true) {
438
436
  const alreadyAttached = getAlreadyAttachedDatabaseFromError(e);
439
437
  if (alreadyAttached.isAlreadyAttached === true) {
438
+ if (alreadyAttached.dbAlias === restoreCurrentDb) {
439
+ await this.attachIfNotExists({
440
+ type: "memory",
441
+ alias: tempDbNameHack
442
+ });
443
+ await this.use(tempDbNameHack);
444
+ isTempDbNameHackCreated = true;
445
+ }
440
446
  await this.detachOrIgnore(alreadyAttached.dbAlias);
441
447
  await this.attach(dbParams);
442
448
  } else throw e;
443
449
  } else throw e;
450
+ } finally {
451
+ if (restoreCurrentDb !== null && isTempDbNameHackCreated) {
452
+ await this.use(restoreCurrentDb);
453
+ await this.detachOrIgnore(tempDbNameHack);
454
+ }
444
455
  }
445
456
  return new Database({ alias: params.alias });
446
457
  };
@@ -576,6 +587,24 @@ var DuckDatabaseManager = class {
576
587
  await this.#executor.getRowObjectsJS("vacuum()", "VACUUM");
577
588
  return true;
578
589
  };
590
+ getCurrentDatabase = async () => {
591
+ return (await this.#executor.getRowObjectsJS("getCurrentDatabase()", "SELECT current_database() as current_database"))[0]?.current_database ?? null;
592
+ };
593
+ getCurrentSchema = async () => {
594
+ return (await this.#executor.getRowObjectsJS("getCurrentSchema()", "SELECT current_schema() as current_schema"))?.[0]?.current_schema ?? null;
595
+ };
596
+ getCurrentCatalog = async () => {
597
+ return (await this.#executor.getRowObjectsJS("getCurrentCatalog()", "SELECT current_catalog() as current_catalog"))?.[0]?.current_catalog ?? null;
598
+ };
599
+ /**
600
+ * Set the default database to use
601
+ * @throws if the database alias does not exist
602
+ */
603
+ use = async (dbAlias) => {
604
+ const safeAlias = duckValidatorsZod.aliasName.parse(dbAlias);
605
+ await this.#executor.getRowObjectsJS(`use(${safeAlias})`, `USE ${safeAlias}`);
606
+ return true;
607
+ };
579
608
  /**
580
609
  * Helper to create an initial database file.
581
610
  */
@@ -664,7 +693,18 @@ const duckDbTypes = [
664
693
  ["DOUBLE", DOUBLE],
665
694
  ["FLOAT", FLOAT],
666
695
  ["DATE", DATE],
667
- ["DECIMAL", new DuckDBDecimalType(18, 3)]
696
+ ["DECIMAL", new DuckDBDecimalType(18, 3)],
697
+ ["VARCHAR[]", new DuckDBListType(VARCHAR)],
698
+ ["INTEGER[]", new DuckDBListType(INTEGER)],
699
+ ["BOOLEAN[]", new DuckDBListType(BOOLEAN)],
700
+ ["BIGINT[]", new DuckDBListType(BIGINT)],
701
+ ["UBIGINT[]", new DuckDBListType(UBIGINT)],
702
+ ["UUID[]", new DuckDBListType(UUID)],
703
+ ["DATE[]", new DuckDBListType(DATE)],
704
+ ["TIMESTAMP[]", new DuckDBListType(TIMESTAMP)],
705
+ ["TIMESTAMP_MS[]", new DuckDBListType(TIMESTAMP_MS)],
706
+ ["DOUBLE[]", new DuckDBListType(DOUBLE)],
707
+ ["FLOAT[]", new DuckDBListType(FLOAT)]
668
708
  ];
669
709
  const duckDbTypesMap = new Map(duckDbTypes);
670
710
  const getTableCreateFromZod = (params) => {
@@ -681,8 +721,23 @@ const getTableCreateFromZod = (params) => {
681
721
  for (const [columnName, def] of Object.entries(json.properties)) {
682
722
  const { type, duckdbType, nullable, format, primaryKey, minimum, maximum, multipleOf } = def;
683
723
  const c = { name: columnName };
684
- if (duckdbType !== void 0 && duckDbTypesMap.has(duckdbType)) c.duckdbType = duckDbTypesMap.get(duckdbType);
685
- else switch (type) {
724
+ if (duckdbType !== void 0 && !duckDbTypesMap.has(duckdbType)) throw new Error(`The provided "duckdbType: '${duckdbType}'" for '${columnName}' isn't currently supported - ${JSON.stringify(def)}`);
725
+ const customDuckDbType = duckdbType === void 0 ? void 0 : duckDbTypesMap.get(duckdbType);
726
+ if (customDuckDbType === void 0) switch (type) {
727
+ case "array":
728
+ switch (def?.items?.type) {
729
+ case "string":
730
+ c.duckdbType = LIST(VARCHAR);
731
+ break;
732
+ case "number":
733
+ c.duckdbType = LIST(INTEGER);
734
+ break;
735
+ case "boolean":
736
+ c.duckdbType = LIST(BOOLEAN);
737
+ break;
738
+ default: throw new Error(`The inferred duckdb array for '${columnName}' is not supported - ${JSON.stringify(def)}`);
739
+ }
740
+ break;
686
741
  case "string":
687
742
  if (Array.isArray(def.enum)) c.duckdbType = ENUM(def.enum);
688
743
  else switch (format) {
@@ -719,6 +774,7 @@ const getTableCreateFromZod = (params) => {
719
774
  break;
720
775
  default: throw new Error(`Cannot guess '${columnName}' type - ${JSON.stringify(def)}`);
721
776
  }
777
+ else c.duckdbType = customDuckDbType;
722
778
  if (primaryKey === true) c.constraint = "PRIMARY KEY";
723
779
  else if (nullable !== true) c.constraint = "NOT NULL";
724
780
  columnTypesMap.set(columnName, c.duckdbType);
@@ -1,2 +1,2 @@
1
- import { a as duckDsnZodSchema, c as assertValidTableName, d as duckConnectionParamsZodSchema, i as duckValidatorsZod, n as InferZodRelaxedDataSchema, o as assertValidAliasName, r as ensureZodTableSchema, s as assertValidSchemaName, t as TableSchemaZod, u as duckAllConnectionOptionsZodSchema } from "../../index-BKsJVkXZ.mjs";
1
+ import { a as duckDsnZodSchema, c as assertValidTableName, d as duckConnectionParamsZodSchema, i as duckValidatorsZod, n as InferZodRelaxedDataSchema, o as assertValidAliasName, r as ensureZodTableSchema, s as assertValidSchemaName, t as TableSchemaZod, u as duckAllConnectionOptionsZodSchema } from "../../index-Z9LfGsCV.mjs";
2
2
  export { type InferZodRelaxedDataSchema, type TableSchemaZod, assertValidAliasName, assertValidSchemaName, assertValidTableName, duckAllConnectionOptionsZodSchema, duckConnectionParamsZodSchema, duckDsnZodSchema, duckValidatorsZod, ensureZodTableSchema };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowblade/sqlduck",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -95,7 +95,7 @@
95
95
  "@belgattitude/eslint-config-bases": "8.19.1",
96
96
  "@dotenvx/dotenvx": "2.21.0",
97
97
  "@duckdb/node-api": "1.5.5-r.4",
98
- "@faker-js/faker": "10.5.0",
98
+ "@faker-js/faker": "10.6.0",
99
99
  "@flowblade/source-kysely": "^1.7.0",
100
100
  "@httpx/assert": "0.18.0",
101
101
  "@mitata/counters": "0.0.8",