Skip to content

Commit

Permalink
what a mess
Browse files Browse the repository at this point in the history
  • Loading branch information
Sheraff committed Mar 17, 2024
1 parent 9e2c794 commit 1d6af2f
Show file tree
Hide file tree
Showing 13 changed files with 859 additions and 29 deletions.
4 changes: 4 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { WorkerDemo } from "client/components/WorkerDemo"
import { DbDemo } from "client/components/DbDemo"
import { Hero } from "client/components/Hero/Hero"
import styles from "client/App.module.css"
import { DrizzleTest } from "client/components/drizzle/Test"

fooBar()

Expand Down Expand Up @@ -37,6 +38,9 @@ export default function App() {
<Cell row>
<DbDemo />
</Cell>
<Cell row>
<DrizzleTest />
</Cell>
</Grid>
</main>
)
Expand Down
83 changes: 66 additions & 17 deletions client/src/components/drizzle/Test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,73 @@
import type { DB } from "@vlcn.io/crsqlite-wasm"
import { drizzle } from "./crsqlite"
import * as schema from "./schema"
import * as schema from "../../../../shared/src/drizzle-test/schema"
import type { Dialect, SQL } from "drizzle-orm"
import { useEffect } from "react"
import initWasm from "@vlcn.io/crsqlite-wasm"
import tblrx from "@vlcn.io/rx-tbl"

export function makeDrizzleDb(db: DB) {
return drizzle(db, { schema })
}
// export function makeDrizzleDb(db: DB) {
// return drizzle(db, { schema })
// }

// const db = makeDrizzleDb({} as DB)

// const res = db.query.countries
// .findMany({
// with: {
// cities: {
// where: (city, { eq, sql }) => eq(city.name, sql.placeholder("cityName")),
// },
// },
// })
// .prepare()

// type foo = keyof typeof res & {} & string
// // ^?

// res.finalize()

const db = makeDrizzleDb({} as DB)
// const data = await res.all({ cityName: "New York" })
// // ^?

const res = db.query.countries
.findMany({
with: {
cities: {
where: (city, { eq, sql }) => eq(city.name, sql.placeholder("cityName")),
},
},
})
.prepare()
// console.log(data)

const data = await res.all({ cityName: "New York" })
// ^?
import { migrate } from "./crsqlite/migrator"
import { useQueryClient } from "@tanstack/react-query"

console.log(data)
async function make() {
const sqlite = await initWasm()
const sql = await sqlite.open("test")
const db = drizzle(sql, { schema })
await migrate(db, { migrationsFolder: "drizzle" })
const rx = tblrx(sql)
return { db, rx }
}

export function DrizzleTest() {
const client = useQueryClient()
useEffect(() => {
const key = "test"
make()
.then((db) => {
client.setQueryData<DbStore>(key, {

Check failure on line 53 in client/src/components/drizzle/Test.tsx

View workflow job for this annotation

GitHub Actions / 🐙 Test (20.x, 8.x)

tsc > 2304

Cannot find name 'DbStore'. (2304)

Check failure on line 53 in client/src/components/drizzle/Test.tsx

View workflow job for this annotation

GitHub Actions / 🐙 Test (20.x, 8.x)

tsc > 2345

Argument of type 'string' is not assignable to parameter of type 'QueryKey'. (2345)

Check failure on line 53 in client/src/components/drizzle/Test.tsx

View workflow job for this annotation

GitHub Actions / 🐙 Test (21.x, 8.x)

tsc > 2304

Cannot find name 'DbStore'. (2304)

Check failure on line 53 in client/src/components/drizzle/Test.tsx

View workflow job for this annotation

GitHub Actions / 🐙 Test (21.x, 8.x)

tsc > 2345

Argument of type 'string' is not assignable to parameter of type 'QueryKey'. (2345)
// schema: cleanSchema,
// schemaName,
name: "test",
db,
})
})
.catch(console.error)
}, [])
return (
<div>
Drizzle
<hr />
<TestChild />
</div>
)
}

function TestChild() {
return <div>Test</div>
}
39 changes: 39 additions & 0 deletions client/src/components/drizzle/crsqlite/migrator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { MigrationConfig, MigrationMeta } from "drizzle-orm/migrator"
import type { CRSQLite3Database } from "./driver"
import migrationJournal from "shared/drizzle-migrations/meta/_journal.json"
import { migrations } from "shared/drizzle-migrations/index"

export async function migrate<TSchema extends Record<string, unknown>>(
db: CRSQLite3Database<TSchema>,
config: string | MigrationConfig
) {
const migrations = await getMigrations()
db.dialect.migrate(migrations, db.session, config)

Check failure on line 11 in client/src/components/drizzle/crsqlite/migrator.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (20.x, 8.x)

tsc > 2339

Property 'dialect' does not exist on type 'CRSQLite3Database<TSchema>'. (2339)

Check failure on line 11 in client/src/components/drizzle/crsqlite/migrator.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (20.x, 8.x)

tsc > 2339

Property 'session' does not exist on type 'CRSQLite3Database<TSchema>'. (2339)

Check failure on line 11 in client/src/components/drizzle/crsqlite/migrator.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (21.x, 8.x)

tsc > 2339

Property 'dialect' does not exist on type 'CRSQLite3Database<TSchema>'. (2339)

Check failure on line 11 in client/src/components/drizzle/crsqlite/migrator.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (21.x, 8.x)

tsc > 2339

Property 'session' does not exist on type 'CRSQLite3Database<TSchema>'. (2339)
}

export async function getMigrations() {
const journal = migrationJournal as {
entries: Array<{ idx: number; when: number; tag: string; breakpoints: boolean }>
}
const migrationQueries: MigrationMeta[] = []
for (const journalEntry of journal.entries) {
const query = migrations[journalEntry.tag as keyof typeof migrations]
const result = query.split("--> statement-breakpoint")
migrationQueries.push({
sql: result,
bps: journalEntry.breakpoints,
folderMillis: journalEntry.when,
hash: await createSha256Hash(query),
})
}
return migrationQueries
}

async function createSha256Hash(query: string) {
const encoder = new TextEncoder()
const data = encoder.encode(query)
const hash = await window.crypto.subtle.digest("SHA-256", data)
const hashArray = Array.from(new Uint8Array(hash))
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("")
return hashHex
}
107 changes: 97 additions & 10 deletions client/src/components/drizzle/crsqlite/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@ import type { Logger } from "drizzle-orm/logger"
import { NoopLogger } from "drizzle-orm/logger"
import type { RelationalSchemaConfig, TablesRelationalConfig } from "drizzle-orm/relations"
import type { PreparedQuery } from "drizzle-orm/session"
import { fillPlaceholders, type Query, sql } from "drizzle-orm/sql/sql"
import { fillPlaceholders, type Query, sql, type SQL } from "drizzle-orm/sql/sql"
import type { SQLiteAsyncDialect } from "drizzle-orm/sqlite-core/dialect"
import { type SQLiteTransaction } from "drizzle-orm/sqlite-core"
import { SQLiteTransaction } from "drizzle-orm/sqlite-core"
import type { SelectedFieldsOrdered } from "drizzle-orm/sqlite-core/query-builders/select.types"
import type {
PreparedQueryConfig,
SQLiteExecuteMethod,
SQLiteTransactionConfig,
} from "drizzle-orm/sqlite-core/session"
import { SQLitePreparedQuery, SQLiteSession } from "drizzle-orm/sqlite-core/session"
import type { Dialect } from "drizzle-orm"
// import { mapResultRow } from 'drizzle-orm/utils';

type Transaction = Awaited<ReturnType<DB["imperativeTx"]>>

interface CRSQLiteSessionOptions {
logger?: Logger
}
Expand All @@ -32,13 +35,14 @@ export class CRSQLiteSession<

constructor(
private client: DB,
dialect: SQLiteAsyncDialect,
private dialect: SQLiteAsyncDialect,
private schema: RelationalSchemaConfig<TSchema> | undefined,
private options: CRSQLiteSessionOptions
// private tx: Transaction | undefined,
private options: CRSQLiteSessionOptions,
private tx?: Transaction | undefined
) {
super(dialect)
this.logger = options.logger ?? new NoopLogger()
console.log("CRSQLiteSession.constructor")
}

prepareQuery<T extends PreparedQueryConfig>(
Expand All @@ -47,20 +51,57 @@ export class CRSQLiteSession<
executeMethod: SQLiteExecuteMethod,
customResultMapper?: (rows: unknown[][]) => unknown
): CRSQLPreparedQuery<T> {
console.log("CRSQLiteSession.prepareQuery")
return new CRSQLPreparedQuery(
this.client,
query,
this.logger,
fields,
// this.tx,
this.tx,
executeMethod,
customResultMapper
)
}

override async transaction<T>(
transaction: (db: CRSQLTransaction<TFullSchema, TSchema>) => T | Promise<T>,
_config?: SQLiteTransactionConfig
): Promise<T> {
console.log("CRSQLiteSession.transaction")
const crsqliteTx = await this.client.imperativeTx()
const session = new CRSQLiteSession(
this.client,
this.dialect,
this.schema,
this.options,
crsqliteTx
)
const tx = new CRSQLTransaction("async", this.dialect, session, this.schema)
try {
const result = await transaction(tx)
crsqliteTx[0]()
return result
} catch (err) {
crsqliteTx[0]()
throw err
}
}

// run(query: SQL<unknown>): Promise<void> {
// console.log("CRSQLiteSession.run")
// return this.client.exec(query.)

// }
}

type StmtAsync = Awaited<ReturnType<DB["prepare"]>>

// declare module "drizzle-orm/sqlite-core/session" {
// export interface PreparedQuery {
// finalize(): Promise<void>
// }
// }

export class CRSQLPreparedQuery<
T extends PreparedQueryConfig = PreparedQueryConfig,
> extends SQLitePreparedQuery<{
Expand All @@ -80,23 +121,27 @@ export class CRSQLPreparedQuery<
query: Query,
private logger: Logger,
fields: SelectedFieldsOrdered | undefined,
// private tx:
// | SQLiteTransaction<"async", void, Record<string, unknown>, TablesRelationalConfig>
// | undefined,
private tx: Transaction | undefined,
executeMethod: SQLiteExecuteMethod,
private customResultMapper?: (rows: unknown[][]) => unknown
) {
super("async", executeMethod, query)
this.stmt = this.client.prepare(query.sql)
}

/**
* execute query, no result expected
*/
async run(placeholderValues?: Record<string, unknown>): Promise<void> {
const params = fillPlaceholders(this.query.params, placeholderValues ?? {})
this.logger.logQuery(this.query.sql, params)
const stmt = await this.stmt
await stmt.run(null, ...params)
}

/**
* execute query and return all rows
*/
async all(placeholderValues?: Record<string, unknown>): Promise<unknown[]> {
const params = fillPlaceholders(this.query.params, placeholderValues ?? {})
this.logger.logQuery(this.query.sql, params)
Expand All @@ -105,6 +150,9 @@ export class CRSQLPreparedQuery<
return this.customResultMapper ? this.customResultMapper(rows) : rows

Check failure on line 150 in client/src/components/drizzle/crsqlite/session.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (20.x, 8.x)

tsc > 2322

Type 'unknown' is not assignable to type 'unknown[]'. (2322)

Check failure on line 150 in client/src/components/drizzle/crsqlite/session.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (21.x, 8.x)

tsc > 2322

Type 'unknown' is not assignable to type 'unknown[]'. (2322)
}

/**
* only query first row
*/
async get(placeholderValues?: Record<string, unknown>): Promise<unknown | undefined> {
const params = fillPlaceholders(this.query.params, placeholderValues ?? {})
this.logger.logQuery(this.query.sql, params)
Expand All @@ -113,12 +161,51 @@ export class CRSQLPreparedQuery<
return this.customResultMapper ? this.customResultMapper([row]) : row
}

/**
* directly extract first column value from each row
*/
async values(placeholderValues?: Record<string, unknown>): Promise<unknown[]> {
const params = fillPlaceholders(this.query.params, placeholderValues ?? {})
this.logger.logQuery(this.query.sql, params)
const stmt = await this.stmt
const rows = await stmt.all(null, ...params)
// wtf is `.values` supposed to do?
return rows.map((row) => Object.values(row)[0])
}

async finalize(): Promise<void> {
const stmt = await this.stmt
await stmt.finalize(null)
}
}

export class CRSQLTransaction<
TFullSchema extends Record<string, unknown>,
TSchema extends TablesRelationalConfig,
> extends SQLiteTransaction<"async", void, TFullSchema, TSchema> {
static readonly [entityKind]: string = "CRSQLTransaction"

private dialect: SQLiteAsyncDialect

Check failure on line 187 in client/src/components/drizzle/crsqlite/session.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (20.x, 8.x)

tsc > 2564

Property 'dialect' has no initializer and is not definitely assigned in the constructor. (2564)

Check failure on line 187 in client/src/components/drizzle/crsqlite/session.ts

View workflow job for this annotation

GitHub Actions / 🐙 Test (21.x, 8.x)

tsc > 2564

Property 'dialect' has no initializer and is not definitely assigned in the constructor. (2564)
private session: CRSQLiteSession<TFullSchema, TSchema>

override async transaction<T>(
transaction: (tx: CRSQLTransaction<TFullSchema, TSchema>) => Promise<T>
): Promise<T> {
const savepointName = `sp${this.nestedIndex}`
const tx = new CRSQLTransaction(
"async",
this.dialect,
this.session,
this.schema,
this.nestedIndex + 1
)
await this.session.run(sql.raw(`SAVEPOINT ${savepointName}`))
try {
const result = await transaction(tx)
await this.session.run(sql.raw(`RELEASE savepoint ${savepointName}`))
return result
} catch (err) {
await this.session.run(sql.raw(`ROLLBACK TO savepoint ${savepointName}`))
throw err
}
}
}
9 changes: 9 additions & 0 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from "drizzle-kit"

export default defineConfig({
schema: "./shared/src/drizzle-test/schema.ts",
driver: "better-sqlite",
out: "./shared/src/drizzle-migrations",
verbose: true,
strict: true,
})
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"root:format": "prettier --check '*.ts' '*.json' 'types/*' '.github/**/*' --config .prettierrc.json --log-level warn",
"root:format:fix": "prettier --write '*.ts' '*.json' 'types/*' '.github/**/*' --config .prettierrc.json --log-level warn",
"root:spell": "cspell --config .cspell.json --quiet --gitignore '*' 'types/*' '.github/**/*'",
"root:clear": "rm -rf dist; rm -rf node_modules; rm -rf .turbo; find . -name '*.tsbuildinfo' -type f -delete;"
"root:clear": "rm -rf dist; rm -rf node_modules; rm -rf .turbo; find . -name '*.tsbuildinfo' -type f -delete;",
"generate": "drizzle-kit generate:sqlite"
},
"dependencies": {
"@fastify/cookie": "^9.3.1",
Expand Down Expand Up @@ -70,6 +71,7 @@
"chalk": "^5.3.0",
"chokidar": "3.6.0",
"cspell": "^8.6.0",
"drizzle-kit": "^0.20.14",
"esbuild": "0.20.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
Expand Down
Loading

0 comments on commit 1d6af2f

Please sign in to comment.