Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SQLite] Add bigint mode to SQLite #558

Merged
merged 13 commits into from
Jun 2, 2023
57 changes: 53 additions & 4 deletions drizzle-orm/src/sqlite-core/columns/blob.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,54 @@
import type { ColumnBaseConfig, ColumnHKTBase } from '~/column';
import type { ColumnBuilderBaseConfig, ColumnBuilderHKTBase, MakeColumnConfig } from '~/column-builder';
import type { AnySQLiteTable } from '~/sqlite-core/table';
import type { Assume } from '~/utils';
import type { Assume, Equal } from '~/utils';
import { SQLiteColumn, SQLiteColumnBuilder } from './common';

type BlobMode = 'buffer' | 'json';
type BlobMode = 'buffer' | 'json' | 'bigint';

export interface SQLiteBigIntBuilderHKT extends ColumnBuilderHKTBase {
_type: SQLiteBigIntBuilder<Assume<this['config'], ColumnBuilderBaseConfig>>;
_columnHKT: SQLiteBigIntHKT;
}

export interface SQLiteBigIntHKT extends ColumnHKTBase {
_type: SQLiteBigInt<Assume<this['config'], ColumnBaseConfig>>;
}

export type SQLiteBigIntBuilderInitial<TName extends string> = SQLiteBigIntBuilder<{
name: TName;
data: bigint;
driverParam: Buffer;
notNull: false;
hasDefault: false;
}>;

export class SQLiteBigIntBuilder<T extends ColumnBuilderBaseConfig>
extends SQLiteColumnBuilder<SQLiteBigIntBuilderHKT, T>
{
/** @internal */
override build<TTableName extends string>(
table: AnySQLiteTable<{ name: TTableName }>,
): SQLiteBigInt<MakeColumnConfig<T, TTableName>> {
return new SQLiteBigInt<MakeColumnConfig<T, TTableName>>(table, this.config);
}
}

export class SQLiteBigInt<T extends ColumnBaseConfig> extends SQLiteColumn<SQLiteBigIntHKT, T> {
declare protected $sqliteColumnBrand: 'SQLiteBigInt';

getSQLType(): string {
return 'blob';
}

override mapFromDriverValue(value: Buffer): bigint {
return BigInt(value.toString());
}

override mapToDriverValue(value: bigint): Buffer {
return Buffer.from(value.toString());
}
}

export interface SQLiteBlobJsonBuilderHKT extends ColumnBuilderHKTBase {
_type: SQLiteBlobJsonBuilder<Assume<this['config'], ColumnBuilderBaseConfig>>;
Expand Down Expand Up @@ -88,13 +132,18 @@ export interface BlobConfig<TMode extends BlobMode = BlobMode> {
mode: TMode;
}

export function blob<TName extends string, TMode extends BlobMode = 'buffer'>(
export function blob<TName extends string, TMode extends BlobMode = BlobMode>(
name: TName,
config?: BlobConfig<TMode>,
): TMode extends 'buffer' ? SQLiteBlobBufferBuilderInitial<TName> : SQLiteBlobJsonBuilderInitial<TName>;
): Equal<TMode, 'bigint'> extends true ? SQLiteBigIntBuilderInitial<TName>
: Equal<TMode, 'buffer'> extends true ? SQLiteBlobBufferBuilderInitial<TName>
: SQLiteBlobJsonBuilderInitial<TName>;
export function blob(name: string, config?: BlobConfig) {
if (config?.mode === 'json') {
return new SQLiteBlobJsonBuilder(name);
}
if (config?.mode === 'bigint') {
return new SQLiteBigIntBuilder(name);
}
return new SQLiteBlobBufferBuilder(name);
}
6 changes: 5 additions & 1 deletion drizzle-zod/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
PgVarchar,
} from 'drizzle-orm/pg-core';
import {
SQLiteBigInt,
SQLiteBlobJson,
SQLiteCustomColumn,
SQLiteInteger,
Expand Down Expand Up @@ -287,7 +288,10 @@ function mapColumnToSchema(column: AnyColumn): z.ZodTypeAny {
|| column instanceof MySqlReal || column instanceof MySqlYear
) {
type = z.number();
} else if (column instanceof PgBigInt64 || column instanceof PgBigSerial64 || column instanceof MySqlBigInt64) {
} else if (
column instanceof PgBigInt64 || column instanceof PgBigSerial64 || column instanceof MySqlBigInt64
|| column instanceof SQLiteBigInt
) {
type = z.bigint();
} else if (column instanceof PgBoolean || column instanceof MySqlBoolean) {
type = z.boolean();
Expand Down
5 changes: 5 additions & 0 deletions drizzle-zod/tests/sqlite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const blobJsonSchema = z.object({
const users = sqliteTable('users', {
id: integer('id').primaryKey(),
blobJson: blob('blob', { mode: 'json' }).$type<z.infer<typeof blobJsonSchema>>().notNull(),
blobBigInt: blob('blob', { mode: 'bigint' }).notNull(),
numeric: numeric('numeric').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
createdAtMs: integer('created_at_ms', { mode: 'timestamp_ms' }).notNull(),
Expand Down Expand Up @@ -45,6 +46,7 @@ test('users insert schema', (t) => {
const expected = z.object({
id: z.number().positive().optional(),
blobJson: blobJsonSchema,
blobBigInt: z.bigint(),
numeric: z.string(),
createdAt: z.date(),
createdAtMs: z.date(),
Expand All @@ -62,6 +64,7 @@ test('users insert schema w/ defaults', (t) => {
const expected = z.object({
id: z.number().optional(),
blobJson: jsonSchema,
blobBigInt: z.bigint(),
numeric: z.string(),
createdAt: z.date(),
createdAtMs: z.date(),
Expand Down Expand Up @@ -98,6 +101,7 @@ test('users select schema', (t) => {
const expected = z.object({
id: z.number(),
blobJson: jsonSchema,
blobBigInt: z.bigint(),
numeric: z.string(),
createdAt: z.date(),
createdAtMs: z.date(),
Expand All @@ -115,6 +119,7 @@ test('users select schema w/ defaults', (t) => {
const expected = z.object({
id: z.number(),
blobJson: jsonSchema,
blobBigInt: z.bigint(),
numeric: z.string(),
createdAt: z.date(),
createdAtMs: z.date(),
Expand Down
33 changes: 33 additions & 0 deletions integration-tests/tests/better-sqlite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ const pkExampleTable = sqliteTable('pk_example', {
compositePk: primaryKey(table.id, table.name),
}));

const bigIntExample = sqliteTable('big_int_example', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
bigInt: blob('big_int', { mode: 'bigint' }).notNull(),
});

interface Context {
db: BetterSQLite3Database;
client: Database.Database;
Expand Down Expand Up @@ -113,6 +119,7 @@ test.beforeEach((t) => {
ctx.db.run(sql`drop table if exists ${coursesTable}`);
ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`);
ctx.db.run(sql`drop table if exists ${orders}`);
ctx.db.run(sql`drop table if exists ${bigIntExample}`);
ctx.db.run(sql`drop table if exists ${pkExampleTable}`);

ctx.db.run(sql`
Expand Down Expand Up @@ -167,6 +174,32 @@ test.beforeEach((t) => {
primary key (id, name)
)
`);
ctx.db.run(sql`
create table ${bigIntExample} (
id integer primary key,
name text not null,
big_int blob not null
)
`);
});

test.serial('insert bigint values', async (t) => {
const { db } = t.context;

await db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run();
await db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run();
await db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run();
await db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run();
await db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run();

const result = await db.select().from(bigIntExample).all();
t.deepEqual(result, [
{ id: 1, name: 'one', bigInt: BigInt('0') },
{ id: 2, name: 'two', bigInt: BigInt('127') },
{ id: 3, name: 'three', bigInt: BigInt('32767') },
{ id: 4, name: 'four', bigInt: BigInt('1234567890') },
{ id: 5, name: 'five', bigInt: BigInt('12345678900987654321') },
]);
});

test.serial('select all fields', (t) => {
Expand Down
33 changes: 33 additions & 0 deletions integration-tests/tests/libsql.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ const pkExampleTable = sqliteTable('pk_example', {
compositePk: primaryKey(table.id, table.name),
}));

const bigIntExample = sqliteTable('big_int_example', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
bigInt: blob('big_int', { mode: 'bigint' }).notNull(),
});

test.before(async (t) => {
const ctx = t.context;
const url = process.env['LIBSQL_URL'];
Expand Down Expand Up @@ -139,6 +145,7 @@ test.beforeEach(async (t) => {
await ctx.db.run(sql`drop table if exists ${coursesTable}`);
await ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`);
await ctx.db.run(sql`drop table if exists ${orders}`);
await ctx.db.run(sql`drop table if exists ${bigIntExample}`);
await ctx.db.run(sql`drop table if exists ${pkExampleTable}`);

await ctx.db.run(sql`
Expand Down Expand Up @@ -195,6 +202,32 @@ test.beforeEach(async (t) => {
primary key (id, name)
)
`);
await ctx.db.run(sql`
create table ${bigIntExample} (
id integer primary key,
name text not null,
big_int blob not null
)
`);
});

test.serial('insert bigint values', async (t) => {
const { db } = t.context;

await db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run();
await db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run();
await db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run();
await db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run();
await db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run();

const result = await db.select().from(bigIntExample).all();
t.deepEqual(result, [
{ id: 1, name: 'one', bigInt: BigInt('0') },
{ id: 2, name: 'two', bigInt: BigInt('127') },
{ id: 3, name: 'three', bigInt: BigInt('32767') },
{ id: 4, name: 'four', bigInt: BigInt('1234567890') },
{ id: 5, name: 'five', bigInt: BigInt('12345678900987654321') },
]);
});

test.serial('select all fields', async (t) => {
Expand Down
33 changes: 33 additions & 0 deletions integration-tests/tests/sql.js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ const pkExampleTable = sqliteTable('pk_example', {
compositePk: primaryKey(table.id, table.name),
}));

const bigIntExample = sqliteTable('big_int_example', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
bigInt: blob('big_int', { mode: 'bigint' }).notNull(),
});

interface Context {
db: SQLJsDatabase;
client: Database;
Expand Down Expand Up @@ -110,6 +116,7 @@ test.beforeEach((t) => {
ctx.db.run(sql`drop table if exists ${coursesTable}`);
ctx.db.run(sql`drop table if exists ${courseCategoriesTable}`);
ctx.db.run(sql`drop table if exists ${orders}`);
ctx.db.run(sql`drop table if exists ${bigIntExample}`);
ctx.db.run(sql`drop table if exists ${pkExampleTable}`);

ctx.db.run(sql`
Expand Down Expand Up @@ -164,6 +171,32 @@ test.beforeEach((t) => {
primary key (id, name)
)
`);
ctx.db.run(sql`
create table ${bigIntExample} (
id integer primary key,
name text not null,
big_int blob not null
)
`);
});

test.serial('insert bigint values', async (t) => {
const { db } = t.context;

await db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run();
await db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run();
await db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run();
await db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run();
await db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run();

const result = await db.select().from(bigIntExample).all();
t.deepEqual(result, [
{ id: 1, name: 'one', bigInt: BigInt('0') },
{ id: 2, name: 'two', bigInt: BigInt('127') },
{ id: 3, name: 'three', bigInt: BigInt('32767') },
{ id: 4, name: 'four', bigInt: BigInt('1234567890') },
{ id: 5, name: 'five', bigInt: BigInt('12345678900987654321') },
]);
});

test.serial('select all fields', (t) => {
Expand Down
33 changes: 33 additions & 0 deletions integration-tests/tests/sqlite-proxy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ const pkExampleTable = sqliteTable('pk_example', {
compositePk: primaryKey(table.id, table.name),
}));

const bigIntExample = sqliteTable('big_int_example', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
bigInt: blob('big_int', { mode: 'bigint' }).notNull(),
});

interface Context {
db: SqliteRemoteDatabase;
client: Database.Database;
Expand Down Expand Up @@ -119,6 +125,7 @@ test.beforeEach(async (t) => {
const ctx = t.context;
await ctx.db.run(sql`drop table if exists ${usersTable}`);
await ctx.db.run(sql`drop table if exists ${pkExampleTable}`);
await ctx.db.run(sql`drop table if exists ${bigIntExample}`);

await ctx.db.run(sql`
create table ${usersTable} (
Expand All @@ -137,6 +144,32 @@ test.beforeEach(async (t) => {
primary key (id, name)
)
`);
await ctx.db.run(sql`
create table ${bigIntExample} (
id integer primary key,
name text not null,
big_int blob not null
)
`);
});

test.serial('insert bigint values', async (t) => {
const { db } = t.context;

await db.insert(bigIntExample).values({ name: 'one', bigInt: BigInt('0') }).run();
await db.insert(bigIntExample).values({ name: 'two', bigInt: BigInt('127') }).run();
await db.insert(bigIntExample).values({ name: 'three', bigInt: BigInt('32767') }).run();
await db.insert(bigIntExample).values({ name: 'four', bigInt: BigInt('1234567890') }).run();
await db.insert(bigIntExample).values({ name: 'five', bigInt: BigInt('12345678900987654321') }).run();

const result = await db.select().from(bigIntExample).all();
t.deepEqual(result, [
{ id: 1, name: 'one', bigInt: BigInt('0') },
{ id: 2, name: 'two', bigInt: BigInt('127') },
{ id: 3, name: 'three', bigInt: BigInt('32767') },
{ id: 4, name: 'four', bigInt: BigInt('1234567890') },
{ id: 5, name: 'five', bigInt: BigInt('12345678900987654321') },
]);
});

test.serial('select all fields', async (t) => {
Expand Down