Skip to content

Commit bd63d41

Browse files
committed
initial commit pt. 2.
1 parent f79ddeb commit bd63d41

File tree

7 files changed

+197
-0
lines changed

7 files changed

+197
-0
lines changed

src/connection.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import type {CompiledQuery, DatabaseConnection, QueryResult, TransactionSettings} from 'kysely'
2+
import type {Sql} from 'postgres'
3+
4+
import {PostgresJSDialectError} from './errors.js'
5+
import type {PostgresJSDialectConfig} from './types.js'
6+
import {freeze} from './utils.js'
7+
8+
export class PostgresJSConnection implements DatabaseConnection {
9+
readonly #config: PostgresJSDialectConfig
10+
#sql: Sql
11+
#transaction?: Sql
12+
13+
constructor(config: PostgresJSDialectConfig, sql: Sql) {
14+
this.#config = freeze({...config})
15+
this.#sql = sql
16+
}
17+
18+
async beginTransaction(settings: TransactionSettings): Promise<void> {
19+
if (this.#transaction) {
20+
throw new PostgresJSDialectError('transaction already begun!')
21+
}
22+
23+
const {isolationLevel} = settings
24+
25+
this.#transaction = this.#config.postgres({...this.#config.options, max: 1})
26+
27+
const statement = `start transaction${isolationLevel ? ` ${isolationLevel}` : ''}`
28+
29+
await this.#transaction.unsafe(statement)
30+
}
31+
32+
async commitTransaction(): Promise<void> {
33+
if (!this.#transaction) {
34+
throw new PostgresJSDialectError('no transaction to commit!')
35+
}
36+
37+
await this.#transaction`commit`
38+
39+
this.#releaseTransaction()
40+
}
41+
42+
async executeQuery<R>(compiledQuery: CompiledQuery<unknown>): Promise<QueryResult<R>> {
43+
const result = await this.#resolveExecutor().unsafe<R[]>(compiledQuery.sql, compiledQuery.parameters.slice() as any)
44+
45+
const rows = Array.from(result.values())
46+
47+
if (['INSERT', 'UPDATE', 'DELETE'].includes(result.command)) {
48+
const numAffectedRows = BigInt(result.count)
49+
50+
return {numAffectedRows, rows}
51+
}
52+
53+
return {rows}
54+
}
55+
56+
async rollbackTransaction(): Promise<void> {
57+
if (!this.#transaction) {
58+
throw new PostgresJSDialectError('no transaction to rollback!')
59+
}
60+
61+
await this.#transaction`rollback`
62+
63+
this.#releaseTransaction()
64+
}
65+
66+
async *streamQuery<R>(
67+
compiledQuery: CompiledQuery<unknown>,
68+
chunkSize: number,
69+
): AsyncIterableIterator<QueryResult<R>> {
70+
if (!Number.isInteger(chunkSize) || chunkSize <= 0) {
71+
throw new PostgresJSDialectError('chunkSize must be a positive integer')
72+
}
73+
74+
const cursor = this.#resolveExecutor()
75+
.unsafe<R[]>(compiledQuery.sql, compiledQuery.parameters.slice() as any)
76+
.cursor(chunkSize)
77+
78+
for await (const rows of cursor) {
79+
yield {rows}
80+
}
81+
}
82+
83+
#resolveExecutor(): Sql {
84+
return this.#transaction || this.#sql
85+
}
86+
87+
#releaseTransaction(): void {
88+
if (this.#transaction) {
89+
this.#transaction.end()
90+
this.#transaction = undefined
91+
}
92+
}
93+
}

src/dialect.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import {
2+
PostgresAdapter,
3+
PostgresIntrospector,
4+
PostgresQueryCompiler,
5+
type DatabaseIntrospector,
6+
type Dialect,
7+
type DialectAdapter,
8+
type Driver,
9+
type Kysely,
10+
type QueryCompiler,
11+
} from 'kysely'
12+
13+
import {PostgresJSDriver} from './driver.js'
14+
import type {PostgresJSDialectConfig} from './types.js'
15+
import {freeze} from './utils.js'
16+
17+
export class PostgresJSDialect implements Dialect {
18+
readonly #config: PostgresJSDialectConfig
19+
20+
constructor(config: PostgresJSDialectConfig) {
21+
this.#config = freeze({...config})
22+
}
23+
24+
createAdapter(): DialectAdapter {
25+
return new PostgresAdapter()
26+
}
27+
28+
createDriver(): Driver {
29+
return new PostgresJSDriver(this.#config)
30+
}
31+
32+
createIntrospector(db: Kysely<any>): DatabaseIntrospector {
33+
return new PostgresIntrospector(db)
34+
}
35+
36+
createQueryCompiler(): QueryCompiler {
37+
return new PostgresQueryCompiler()
38+
}
39+
}

src/driver.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type {Driver, TransactionSettings} from 'kysely'
2+
import type {Sql} from 'postgres'
3+
4+
import {PostgresJSConnection} from './connection.js'
5+
import type {PostgresJSDialectConfig} from './types.js'
6+
import {freeze} from './utils.js'
7+
8+
export class PostgresJSDriver implements Driver {
9+
readonly #config: PostgresJSDialectConfig
10+
readonly #sql: Sql
11+
12+
constructor(config: PostgresJSDialectConfig) {
13+
this.#config = freeze({...config})
14+
this.#sql = this.#config.postgres(this.#config.options)
15+
}
16+
17+
async init(): Promise<void> {
18+
// noop
19+
}
20+
21+
async acquireConnection(): Promise<PostgresJSConnection> {
22+
return new PostgresJSConnection(this.#config, this.#sql)
23+
}
24+
25+
async beginTransaction(connection: PostgresJSConnection, settings: TransactionSettings): Promise<void> {
26+
await connection.beginTransaction(settings)
27+
}
28+
29+
async commitTransaction(connection: PostgresJSConnection): Promise<void> {
30+
await connection.commitTransaction()
31+
}
32+
33+
async rollbackTransaction(connection: PostgresJSConnection): Promise<void> {
34+
await connection.rollbackTransaction()
35+
}
36+
37+
async releaseConnection(_: PostgresJSConnection): Promise<void> {
38+
// noop
39+
}
40+
41+
async destroy(): Promise<void> {
42+
await this.#sql.end()
43+
}
44+
}

src/errors.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export class PostgresJSDialectError extends Error {
2+
constructor(message: string) {
3+
super(message)
4+
this.name = 'PostgresJSDialectError'
5+
}
6+
}

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from './connection.js'
2+
export * from './dialect.js'
3+
export * from './driver.js'
4+
export * from './errors.js'
5+
export type * from './types.js'

src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type postgres from 'postgres'
2+
import type {Options} from 'postgres'
3+
4+
export interface PostgresJSDialectConfig {
5+
options: Options<any>
6+
postgres: typeof postgres
7+
}

src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function freeze<T>(obj: T): Readonly<T> {
2+
return Object.freeze(obj)
3+
}

0 commit comments

Comments
 (0)