Skip to content

Commit ad2d40a

Browse files
committed
Added @sqlitecloud/drivers support, updated list of external tests, updated envs, fixed test not closing connection on completion
1 parent 201fcb3 commit ad2d40a

File tree

14 files changed

+937
-76
lines changed

14 files changed

+937
-76
lines changed

.github/workflows/release-feature-branch.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
matrix:
1515
shard:
1616
- gel
17-
- planetscale
17+
# - planetscale
1818
- singlestore-core
1919
- singlestore-proxy
2020
- singlestore-prefixed
@@ -145,6 +145,7 @@ jobs:
145145
LIBSQL_REMOTE_URL: ${{ secrets.LIBSQL_REMOTE_URL }}
146146
LIBSQL_REMOTE_TOKEN: ${{ secrets.LIBSQL_REMOTE_TOKEN }}
147147
SINGLESTORE_CONNECTION_STRING: singlestore://root:singlestore@localhost:33307/
148+
SQLITE_CLOUD_CONNECTION_STRING: ${{ secrets.SQLITE_CLOUD_CONNECTION_STRING }}
148149
working-directory: integration-tests
149150
run: |
150151
if [[ ${{ github.event_name }} != "push" && "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then

.github/workflows/release-latest.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ jobs:
139139
LIBSQL_REMOTE_URL: ${{ secrets.LIBSQL_REMOTE_URL }}
140140
LIBSQL_REMOTE_TOKEN: ${{ secrets.LIBSQL_REMOTE_TOKEN }}
141141
SINGLESTORE_CONNECTION_STRING: singlestore://root:singlestore@localhost:33307/
142+
SQLITE_CLOUD_CONNECTION_STRING: ${{ secrets.SQLITE_CLOUD_CONNECTION_STRING }}
142143
working-directory: integration-tests
143144
run: |
144145
case ${{ matrix.shard }} in

drizzle-orm/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"@opentelemetry/api": "^1.4.1",
5656
"@planetscale/database": ">=1.13",
5757
"@prisma/client": "*",
58+
"@sqlitecloud/drivers": ">=1.0.653",
5859
"@tidbcloud/serverless": "*",
5960
"@tursodatabase/database": ">=0.2.1",
6061
"@tursodatabase/database-common": ">=0.2.1",
@@ -145,6 +146,9 @@
145146
"@electric-sql/pglite": {
146147
"optional": true
147148
},
149+
"@sqlitecloud/drivers": {
150+
"optional": true
151+
},
148152
"@tidbcloud/serverless": {
149153
"optional": true
150154
},
@@ -181,6 +185,7 @@
181185
"@originjs/vite-plugin-commonjs": "^1.0.3",
182186
"@planetscale/database": "^1.16.0",
183187
"@prisma/client": "5.14.0",
188+
"@sqlitecloud/drivers": "^1.0.653",
184189
"@tidbcloud/serverless": "^0.1.1",
185190
"@tursodatabase/database": "0.2.1",
186191
"@tursodatabase/database-common": "^0.2.1",
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { Database } from '@sqlitecloud/drivers';
2+
import * as V1 from '~/_relations.ts';
3+
import { entityKind } from '~/entity.ts';
4+
import { DefaultLogger } from '~/logger.ts';
5+
import type { AnyRelations, EmptyRelations } from '~/relations.ts';
6+
import { BaseSQLiteDatabase } from '~/sqlite-core/db.ts';
7+
import { SQLiteAsyncDialect } from '~/sqlite-core/dialect.ts';
8+
import { type DrizzleConfig, isConfig } from '~/utils.ts';
9+
import { SQLiteCloudSession } from './session.ts';
10+
11+
export type SQLiteCloudRunResult = unknown;
12+
13+
export class SQLiteCloudDatabase<
14+
TSchema extends Record<string, unknown> = Record<string, never>,
15+
TRelations extends AnyRelations = EmptyRelations,
16+
> extends BaseSQLiteDatabase<'async', SQLiteCloudRunResult, TSchema, TRelations> {
17+
static override readonly [entityKind]: string = 'SQLiteCloudDatabase';
18+
19+
/** @internal */
20+
declare readonly session: SQLiteCloudSession<
21+
TSchema,
22+
TRelations,
23+
V1.ExtractTablesWithRelations<TSchema>
24+
>;
25+
}
26+
27+
/** @internal */
28+
export function construct<
29+
TSchema extends Record<string, unknown> = Record<string, never>,
30+
TRelations extends AnyRelations = EmptyRelations,
31+
>(
32+
client: Database,
33+
config: DrizzleConfig<TSchema, TRelations> = {},
34+
): SQLiteCloudDatabase<TSchema, TRelations> & {
35+
$client: Database;
36+
} {
37+
const dialect = new SQLiteAsyncDialect({ casing: config.casing });
38+
let logger;
39+
if (config.logger === true) {
40+
logger = new DefaultLogger();
41+
} else if (config.logger !== false) {
42+
logger = config.logger;
43+
}
44+
45+
let schema: V1.RelationalSchemaConfig<V1.TablesRelationalConfig> | undefined;
46+
if (config.schema) {
47+
const tablesConfig = V1.extractTablesRelationalConfig(
48+
config.schema,
49+
V1.createTableRelationsHelpers,
50+
);
51+
schema = {
52+
fullSchema: config.schema,
53+
schema: tablesConfig.tables,
54+
tableNamesMap: tablesConfig.tableNamesMap,
55+
};
56+
}
57+
58+
const relations = config.relations ?? {} as TRelations;
59+
const session = new SQLiteCloudSession(
60+
client,
61+
dialect,
62+
relations,
63+
schema,
64+
{ logger, cache: config.cache },
65+
);
66+
const db = new SQLiteCloudDatabase(
67+
'async',
68+
dialect,
69+
session as SQLiteCloudSession<
70+
TSchema,
71+
TRelations,
72+
V1.ExtractTablesWithRelations<TSchema>
73+
>,
74+
relations,
75+
schema as V1.RelationalSchemaConfig<any>,
76+
) as SQLiteCloudDatabase<TSchema, TRelations>;
77+
(<any> db).$client = client;
78+
(<any> db).$cache = config.cache;
79+
if ((<any> db).$cache) {
80+
(<any> db).$cache['invalidate'] = config.cache?.onMutate;
81+
}
82+
return db as any;
83+
}
84+
85+
export type DatabaseOpts = (Database extends { new(path: string, opts: infer D): any } ? D : any) & {
86+
path: string;
87+
};
88+
89+
export function drizzle<
90+
TSchema extends Record<string, unknown> = Record<string, never>,
91+
TRelations extends AnyRelations = EmptyRelations,
92+
TClient extends Database = Database,
93+
>(
94+
...params: [
95+
TClient | string,
96+
] | [
97+
TClient | string,
98+
DrizzleConfig<TSchema, TRelations>,
99+
] | [
100+
(
101+
& DrizzleConfig<TSchema, TRelations>
102+
& ({
103+
connection: string | DatabaseOpts;
104+
} | {
105+
client: TClient;
106+
})
107+
),
108+
]
109+
): SQLiteCloudDatabase<TSchema, TRelations> & {
110+
$client: TClient;
111+
} {
112+
if (typeof params[0] === 'string') {
113+
const instance = new Database(params[0]);
114+
115+
return construct(instance, params[1]) as any;
116+
}
117+
118+
if (isConfig(params[0])) {
119+
const { connection, client, ...drizzleConfig } = params[0] as
120+
& { connection?: DatabaseOpts; client?: TClient }
121+
& DrizzleConfig<TSchema, TRelations>;
122+
123+
if (client) return construct(client, drizzleConfig) as any;
124+
125+
const instance = typeof connection === 'string'
126+
? new Database(connection)
127+
: new Database(connection.path, connection);
128+
129+
return construct(instance, drizzleConfig) as any;
130+
}
131+
132+
return construct(params[0] as TClient, params[1] as DrizzleConfig<TSchema, TRelations> | undefined) as any;
133+
}
134+
135+
export namespace drizzle {
136+
export function mock<
137+
TSchema extends Record<string, unknown> = Record<string, never>,
138+
TRelations extends AnyRelations = EmptyRelations,
139+
>(
140+
config?: DrizzleConfig<TSchema, TRelations>,
141+
): SQLiteCloudDatabase<TSchema, TRelations> & {
142+
$client: '$client is not available on drizzle.mock()';
143+
} {
144+
return construct({} as any, config) as any;
145+
}
146+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './driver.ts';
2+
export * from './session.ts';
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import type { MigrationConfig } from '~/migrator.ts';
2+
import { readMigrationFiles } from '~/migrator.ts';
3+
import type { AnyRelations } from '~/relations.ts';
4+
import { type SQL, sql } from '~/sql/sql.ts';
5+
import type { SQLiteCloudDatabase } from './driver.ts';
6+
7+
export async function migrate<TSchema extends Record<string, unknown>, TRelations extends AnyRelations>(
8+
db: SQLiteCloudDatabase<TSchema, TRelations>,
9+
config: MigrationConfig,
10+
) {
11+
const migrations = readMigrationFiles(config);
12+
const { session } = db;
13+
14+
const migrationsTable = config === undefined
15+
? '__drizzle_migrations'
16+
: typeof config === 'string'
17+
? '__drizzle_migrations'
18+
: config.migrationsTable ?? '__drizzle_migrations';
19+
20+
const migrationTableCreate = sql`
21+
CREATE TABLE IF NOT EXISTS ${sql.identifier(migrationsTable)} (
22+
id INTEGER PRIMARY KEY,
23+
hash text NOT NULL,
24+
created_at numeric
25+
)
26+
`;
27+
await session.run(migrationTableCreate);
28+
29+
const dbMigrations = await session.values<[number, string, string]>(
30+
sql`SELECT id, hash, created_at FROM ${sql.identifier(migrationsTable)} ORDER BY created_at DESC LIMIT 1`,
31+
);
32+
33+
const lastDbMigration = dbMigrations[0] ?? undefined;
34+
35+
await session.run(sql`BEGIN TRANSACTION`);
36+
try {
37+
const stmts = sql.join(
38+
migrations.reduce(
39+
(statements, migration) => {
40+
if (!lastDbMigration || Number(lastDbMigration[2])! < migration.folderMillis) {
41+
statements.push(
42+
sql.raw(migration.sql.join('')),
43+
sql`INSERT INTO ${
44+
sql.identifier(migrationsTable)
45+
} ("hash", "created_at") VALUES(${migration.hash}, ${migration.folderMillis});\n`,
46+
);
47+
}
48+
49+
return statements;
50+
},
51+
[] as SQL[],
52+
),
53+
);
54+
55+
await session.run(stmts);
56+
57+
await session.run(sql`COMMIT`);
58+
} catch (error) {
59+
await session.run(sql`ROLLBACK`);
60+
throw error;
61+
}
62+
}

0 commit comments

Comments
 (0)