Last active
September 14, 2026 14:05
-
-
Save mootari/da43ddcec9b306b192a3e1ded7bb4ac0 to your computer and use it in GitHub Desktop.
Scrappy DuckDB type layer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as d from "@duckdb/node-api"; | |
| import type * as D from "@duckdb/node-api"; | |
| // DECIMAL covers the parameterized case | |
| export const t = { | |
| BOOLEAN: d.DuckDBBooleanType.create, | |
| USMALLINT: d.DuckDBUSmallIntType.create, | |
| UINTEGER: d.DuckDBUIntegerType.create, | |
| VARCHAR: d.DuckDBVarCharType.create, | |
| TIMESTAMP_MS: d.DuckDBTimestampMillisecondsType.create, | |
| DECIMAL: d.DECIMAL // No static .create on type class | |
| } satisfies {[k: string]: (...a: any[]) => D.DuckDBType}; | |
| type ColumnDef = {type: D.DuckDBType, notNull?: true, primaryKey?: true}; | |
| type NonNullable = {notNull: true} | {primaryKey: true}; | |
| type JSType<T extends D.DuckDBType> = D.JSTypeForTypeId[T["typeId"]]; | |
| type ColumnType<C extends ColumnDef, T = JSType<C["type"]>> = C extends NonNullable ? T : T | null; | |
| export type TableDef = {[P: string]: ColumnDef}; | |
| export type RowType<T extends TableSchema<TableDef>> = {[K in keyof T["fields"]]: ColumnType<T["fields"][K]>}; | |
| export class TableSchema<F extends TableDef> { | |
| readonly fields: F; | |
| constructor(fields: F) { | |
| this.fields = fields; | |
| } | |
| ddl() { | |
| return Object.entries(this.fields) | |
| .map(([name, {type, primaryKey, notNull}]) => [ | |
| name, | |
| type, | |
| primaryKey ? "PRIMARY KEY" : "", | |
| notNull ? "NOT NULL": "" | |
| ].join(" ")) | |
| .join(",\n"); | |
| } | |
| append(appender: D.DuckDBAppender, rows: RowType<this>[]) { | |
| const writer = d.DuckDBDataChunkWriter.forAppender(appender, { | |
| converter: d.JSToDuckDBValueConverter, | |
| }); | |
| const entries = Object.entries(this.fields); | |
| for(const row of rows) writer.appendRow(entries.map(([k]) => row[k])); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import type { RowType } from "./duckdb.ts"; | |
| import { t, TableSchema } from "./duckdb.ts"; | |
| export const stageSchema = new TableSchema({ | |
| id: {type: t.VARCHAR(), notNull: true}, | |
| changes_time: {type: t.TIMESTAMP_MS(), notNull: true}, | |
| changes_seq: {type: t.UINTEGER(), notNull: true}, | |
| changes_rev: {type: t.VARCHAR(), notNull: true}, | |
| changes_rev_num: {type: t.UINTEGER(), notNull: true}, | |
| changes_deleted: {type: t.BOOLEAN(), notNull: true}, | |
| }); | |
| export type StageRow = RowType<typeof stageSchema>; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { stageSchema } from './schemas.ts'; | |
| // ... | |
| const conn = await db.connect(); // DuckDBInstance | |
| await conn.run(`CREATE TEMP TABLE "stage" (${stageSchema.ddl()})`); | |
| const appender = await conn.createAppender("stage"); | |
| // ... | |
| stageSchema.append(appender, rows); | |
| appender.flushSync(); | |
| // ... |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment