Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions drizzle-orm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"postgres": ">=3",
"sql.js": ">=1",
"sqlite3": ">=5",
"db0": ">=0.3.4",
"typebox": ">=1.0.0",
"valibot": ">=1.0.0-beta.7",
"zod": "^3.25.0 || ^4.0.0"
Expand Down Expand Up @@ -193,6 +194,9 @@
"@effect/sql-pg": {
"optional": true
},
"db0": {
"optional": true
},
"typebox": {
"optional": true
},
Expand Down Expand Up @@ -238,6 +242,7 @@
"better-sqlite3": "^11.9.1",
"bun-types": "^1.2.23",
"cpy": "^10.1.0",
"db0": "^0.3.4",
"effect": "^3.19.8",
"expo-sqlite": "^14.0.0",
"gel": "^2.0.0",
Expand Down
43 changes: 43 additions & 0 deletions drizzle-orm/src/db0/_row-mapping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { CasingCache } from '~/casing.ts';
import { Column } from '~/column.ts';
import { is } from '~/entity.ts';
import { SQL } from '~/sql/sql.ts';

type DialectWithCasing = { casing: CasingCache };

export function getFieldKey(field: unknown, dialect: DialectWithCasing): string | undefined {
if (is(field, SQL.Aliased)) {
return field.fieldAlias;
}
if (is(field, Column)) {
return dialect.casing.getColumnCasing(field);
}
return undefined;
}

export function mapDb0RowToArray(
row: Record<string, unknown>,
fields: ReadonlyArray<{ field: unknown }>,
dialect: DialectWithCasing,
): unknown[] {
const keys = fields.map((f) => getFieldKey(f.field, dialect));

// If we can't confidently map by key (unknown field type, duplicate keys, missing key),
// fall back to Object.values and accept db0 limitations for complex selections.
if (keys.some((k) => k === undefined)) {
return Object.values(row);
}

const unique = new Set(keys as string[]);
if (unique.size !== keys.length) {
return Object.values(row);
}

for (const key of keys as string[]) {
if (!(key in row)) {
return Object.values(row);
}
}

return (keys as string[]).map((key) => row[key]);
}
58 changes: 58 additions & 0 deletions drizzle-orm/src/db0/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Database } from 'db0';
import type { AnyRelations, EmptyRelations } from '~/relations.ts';
import type { DrizzleConfig } from '~/utils.ts';
import { isConfig } from '~/utils.ts';
import { constructPg, type Db0PgDatabase } from './pg/index.ts';
import { constructSqlite, type Db0SQLiteDatabase } from './sqlite/index.ts';

export type Db0Database = Db0SQLiteDatabase<any, any> | Db0PgDatabase<any, any>;

export function drizzle<
TSchema extends Record<string, unknown> = Record<string, never>,
TRelations extends AnyRelations = EmptyRelations,
>(
...params:
| [Database]
| [Database, DrizzleConfig<TSchema, TRelations>]
| [DrizzleConfig<TSchema, TRelations> & { client: Database }]
): Db0Database & { $client: Database } {
if (isConfig(params[0])) {
const { client, ...drizzleConfig } = params[0] as DrizzleConfig<TSchema, TRelations> & { client: Database };
return drizzleInternal(client, drizzleConfig);
}

return drizzleInternal(params[0] as Database, params[1] as DrizzleConfig<TSchema, TRelations> | undefined);
}

function drizzleInternal<
TSchema extends Record<string, unknown> = Record<string, never>,
TRelations extends AnyRelations = EmptyRelations,
>(
client: Database,
config: DrizzleConfig<TSchema, TRelations> = {},
): Db0Database & { $client: Database } {
const dialect = client.dialect;

switch (dialect) {
case 'sqlite':
case 'libsql':
return constructSqlite(client, config) as any;
case 'postgresql':
return constructPg(client, config) as any;
case 'mysql':
throw new Error('drizzle-orm/db0: MySQL support is not yet implemented');
default:
throw new Error(`drizzle-orm/db0: Unsupported db0 dialect: ${dialect}`);
}
}

export namespace drizzle {
export function mock<
TSchema extends Record<string, unknown> = Record<string, never>,
TRelations extends AnyRelations = EmptyRelations,
>(
config?: DrizzleConfig<TSchema, TRelations>,
): Db0Database & { $client: '$client is not available on drizzle.mock()' } {
return constructSqlite({} as any, config) as any;
}
}
20 changes: 20 additions & 0 deletions drizzle-orm/src/db0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export { drizzle, type Db0Database } from './driver.ts';
export {
constructPg,
Db0PgDatabase,
Db0PgPreparedQuery,
type Db0PgQueryResult,
type Db0PgQueryResultHKT,
Db0PgSession,
type Db0PgSessionOptions,
Db0PgTransaction,
} from './pg/index.ts';
export {
constructSqlite,
Db0SQLiteDatabase,
Db0SQLitePreparedQuery,
Db0SQLiteSession,
Db0SQLiteTransaction,
type Db0RunResult,
type Db0SQLiteSessionOptions,
} from './sqlite/index.ts';
65 changes: 65 additions & 0 deletions drizzle-orm/src/db0/pg/driver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { Database } from 'db0';
import * as V1 from '~/_relations.ts';
import { entityKind } from '~/entity.ts';
import type { Logger } from '~/logger.ts';
import { DefaultLogger } from '~/logger.ts';
import { PgAsyncDatabase } from '~/pg-core/async/db.ts';
import { PgDialect } from '~/pg-core/dialect.ts';
import type { AnyRelations, EmptyRelations } from '~/relations.ts';
import type { DrizzleConfig } from '~/utils.ts';
import { type Db0PgQueryResultHKT, Db0PgSession, type Db0PgSessionOptions } from './session.ts';

export class Db0PgDatabase<
TSchema extends Record<string, unknown> = Record<string, never>,
TRelations extends AnyRelations = EmptyRelations,
> extends PgAsyncDatabase<Db0PgQueryResultHKT, TSchema, TRelations> {
static override readonly [entityKind]: string = 'Db0PgDatabase';
}

export function constructPg<
TSchema extends Record<string, unknown> = Record<string, never>,
TRelations extends AnyRelations = EmptyRelations,
>(
client: Database,
config: DrizzleConfig<TSchema, TRelations> = {},
): Db0PgDatabase<TSchema, TRelations> & { $client: Database } {
const dialect = new PgDialect({ casing: config.casing });
let logger: Logger | undefined;
if (config.logger === true) {
logger = new DefaultLogger();
} else if (config.logger !== false) {
logger = config.logger;
}

let schema: V1.RelationalSchemaConfig<V1.TablesRelationalConfig> | undefined;
if (config.schema) {
const tablesConfig = V1.extractTablesRelationalConfig(
config.schema,
V1.createTableRelationsHelpers,
);
schema = {
fullSchema: config.schema,
schema: tablesConfig.tables,
tableNamesMap: tablesConfig.tableNamesMap,
};
}

const relations = config.relations ?? {} as TRelations;
const sessionOptions: Db0PgSessionOptions = { logger, cache: config.cache };
const session = new Db0PgSession(client, dialect, relations, schema as any, sessionOptions);

const db = new Db0PgDatabase(
dialect,
session,
relations,
schema as V1.RelationalSchemaConfig<any>,
) as Db0PgDatabase<TSchema, TRelations>;

(<any> db).$client = client;
(<any> db).$cache = config.cache;
if ((<any> db).$cache) {
(<any> db).$cache['invalidate'] = config.cache?.onMutate;
}

return db as any;
}
9 changes: 9 additions & 0 deletions drizzle-orm/src/db0/pg/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export { constructPg, Db0PgDatabase } from './driver.ts';
export {
Db0PgPreparedQuery,
type Db0PgQueryResult,
type Db0PgQueryResultHKT,
Db0PgSession,
type Db0PgSessionOptions,
Db0PgTransaction,
} from './session.ts';
13 changes: 13 additions & 0 deletions drizzle-orm/src/db0/pg/migrator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { MigrationConfig } from '~/migrator.ts';
import { readMigrationFiles } from '~/migrator.ts';
import { migrate as coreMigrate } from '~/pg-core/async/session.ts';
import type { AnyRelations } from '~/relations.ts';
import type { Db0PgDatabase } from './driver.ts';

export async function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(
db: Db0PgDatabase<TSchema, TRelations>,
config: MigrationConfig,
) {
const migrations = readMigrationFiles(config);
return await coreMigrate(migrations, db.session, config);
}
Loading