diff --git a/.changeset/lemon-moles-lie.md b/.changeset/lemon-moles-lie.md new file mode 100644 index 000000000..113833ce4 --- /dev/null +++ b/.changeset/lemon-moles-lie.md @@ -0,0 +1,5 @@ +--- +'@powersync/drizzle-driver': patch +--- + +Added support for custom column types when converting a Drizzle schema to a PowerSync app schema. diff --git a/packages/drizzle-driver/src/utils/schema.ts b/packages/drizzle-driver/src/utils/schema.ts index 993f03c39..7a61dcefc 100644 --- a/packages/drizzle-driver/src/utils/schema.ts +++ b/packages/drizzle-driver/src/utils/schema.ts @@ -12,6 +12,7 @@ import { CasingCache } from 'drizzle-orm/casing'; import { getTableConfig, SQLiteBoolean, + SQLiteCustomColumn, SQLiteInteger, SQLiteReal, SQLiteText, @@ -44,24 +45,7 @@ export function toPowerSyncTable>( continue; } - let mappedType: BaseColumnType; - switch (drizzleColumn.columnType) { - case SQLiteText[entityKind]: - case SQLiteTextJson[entityKind]: - mappedType = column.text; - break; - case SQLiteInteger[entityKind]: - case SQLiteTimestamp[entityKind]: - case SQLiteBoolean[entityKind]: - mappedType = column.integer; - break; - case SQLiteReal[entityKind]: - mappedType = column.real; - break; - default: - throw new Error(`Unsupported column type: ${drizzleColumn.columnType}`); - } - columns[name] = mappedType; + columns[name] = mapDrizzleColumnToType(drizzleColumn); } const indexes: IndexShorthand = {}; @@ -82,6 +66,34 @@ export function toPowerSyncTable>( return new Table(columns, { ...options, indexes }) as Table>>; } +function mapDrizzleColumnToType(drizzleColumn: SQLiteColumn): BaseColumnType { + switch (drizzleColumn.columnType) { + case SQLiteText[entityKind]: + case SQLiteTextJson[entityKind]: + return column.text; + case SQLiteInteger[entityKind]: + case SQLiteTimestamp[entityKind]: + case SQLiteBoolean[entityKind]: + return column.integer; + case SQLiteReal[entityKind]: + return column.real; + case SQLiteCustomColumn[entityKind]: + const sqlName = (drizzleColumn as SQLiteCustomColumn).getSQLType(); + switch (sqlName) { + case 'text': + return column.text; + case 'integer': + return column.integer; + case 'real': + return column.real; + default: + throw new Error(`Unsupported custom column type: ${drizzleColumn.columnType}: ${sqlName}`); + } + default: + throw new Error(`Unsupported column type: ${drizzleColumn.columnType}`); + } +} + export type DrizzleTablePowerSyncOptions = Omit; export type DrizzleTableWithPowerSyncOptions = { diff --git a/packages/drizzle-driver/tests/sqlite/schema.test.ts b/packages/drizzle-driver/tests/sqlite/schema.test.ts index 4a91f0a47..52124a901 100644 --- a/packages/drizzle-driver/tests/sqlite/schema.test.ts +++ b/packages/drizzle-driver/tests/sqlite/schema.test.ts @@ -1,5 +1,5 @@ import { column, Schema, Table } from '@powersync/common'; -import { index, integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core'; +import { customType, index, integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { describe, expect, it } from 'vitest'; import { DrizzleAppSchema, DrizzleTableWithPowerSyncOptions, toPowerSyncTable } from '../../src/utils/schema'; import { CasingCache } from 'drizzle-orm/casing'; @@ -105,6 +105,61 @@ describe('toPowerSyncTable', () => { expect(convertedList).toEqual(expectedLists); }); + + it('custom column conversion', () => { + const customSqliteText = customType<{ data: string; driverData: string }>({ + dataType() { + return 'text'; + }, + fromDriver(value) { + return value; + }, + toDriver(value) { + return value; + } + }); + + const customSqliteInteger = customType<{ data: number; driverData: number }>({ + dataType() { + return 'integer'; + }, + fromDriver(value) { + return Number(value); + }, + toDriver(value) { + return value; + } + }); + + const customSqliteReal = customType<{ data: number; driverData: number }>({ + dataType() { + return 'real'; + }, + fromDriver(value) { + return Number(value); + }, + toDriver(value) { + return value; + } + }); + + const lists = sqliteTable('lists', { + id: text('id').primaryKey(), + text_col: customSqliteText('text_col'), + int_col: customSqliteInteger('int_col'), + real_col: customSqliteReal('real_col') + }); + + const convertedList = toPowerSyncTable(lists); + + const expectedLists = new Table({ + text_col: column.text, + int_col: column.integer, + real_col: column.real + }); + + expect(convertedList).toEqual(expectedLists); + }); }); describe('DrizzleAppSchema constructor', () => {