-
-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
35156ce
commit b1d0b07
Showing
19 changed files
with
1,296 additions
and
181 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,8 +21,21 @@ console.log(version); | |
db.close(); | ||
``` | ||
|
||
Using [stdext/sql](https://jsr.io/@stdext/sql) interfaces: | ||
|
||
```ts | ||
import { SqliteClient } from "jsr:@db/[email protected]/std_sql"; | ||
|
||
await using db = new SqliteClient("test.db"); | ||
|
||
const [version] = await db.queryArray("select sqlite_version()"); | ||
console.log(version); | ||
``` | ||
|
||
## Usage | ||
|
||
### Permissions | ||
|
||
Since this library depends on the unstable FFI API, you must pass `--allow-env`, | ||
`--allow-ffi` and `--unstable-ffi` flags. Network and FS permissions are also | ||
needed to download and cache prebuilt library. | ||
|
@@ -34,6 +47,37 @@ access. | |
deno run -A --unstable-ffi <file> | ||
``` | ||
|
||
### std/sql | ||
|
||
In addition to the existing `Database` class, a new entrypoint is also exported | ||
to provide compatibility with the [stdext/sql](https://jsr.io/@stdext/sql) | ||
interfaces. Due to the specs, this relies on promises. | ||
|
||
```ts | ||
import { SqliteClient } from "jsr:@db/[email protected]/std_sql"; | ||
|
||
await using db = new SqliteClient("test.db"); | ||
|
||
await db.execute("create table people (name TEXT)"); // 0 | ||
await db.execute("insert into people (name) values ('Alex'), ('Luca');"); // 2 | ||
await db.query("select * from people"); // [{name:"Alex"}, {name:"Luca"}] | ||
await db.queryOne("select * from people"); // {name:"Alex"} | ||
Array.fromAsync(db.queryMany("select * from people")); // [{name:"Alex"}, {name:"Luca"}] | ||
await db.queryArray("select * from people"); // [["Alex"], ["Luca"]] | ||
await db.queryOneArray("select * from people"); // ["Alex"] | ||
Array.fromAsync(db.queryManyArray("select * from people")); // [["Alex"], ["Luca"]] | ||
await db.sql`select * from people`; // [{name:"Alex"}, {name:"Luca"}] | ||
await db.sqlArray`select * from people`; // [["Alex"], ["Luca"]] | ||
``` | ||
|
||
> In general, the `SqliteClient` is good for most cases, and conforms to the | ||
> generalized interfaces in the standard library. However if you are facing | ||
> speed bottlenecks, the `Database` from the main export whould give you some | ||
> more performance. | ||
For more documentation regarding the standard interface, read the | ||
[docs](https://jsr.io/@stdext/sql) | ||
|
||
## Benchmark | ||
|
||
![image](./bench/results.png) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,15 +3,19 @@ | |
"version": "0.11.2", | ||
"github": "https://github.com/denodrivers/sqlite3", | ||
|
||
"exports": "./mod.ts", | ||
"exports": { | ||
".": "./mod.ts", | ||
"./std_sql": "./std_sql.ts" | ||
}, | ||
|
||
"exclude": [ | ||
"sqlite", | ||
"scripts" | ||
], | ||
|
||
"tasks": { | ||
"test": "deno test --unstable-ffi -A test/test.ts", | ||
"test": "DENO_SQLITE_LOCAL=1 deno test --unstable-ffi -A", | ||
"test:remote": "deno test --unstable-ffi -A", | ||
"build": "deno run -A scripts/build.ts", | ||
"bench-deno": "deno run -A --unstable-ffi bench/bench_deno.js 50 1000000", | ||
"bench-deno-ffi": "deno run -A --unstable-ffi bench/bench_deno_ffi.js 50 1000000", | ||
|
@@ -45,5 +49,14 @@ | |
"explicit-module-boundary-types" | ||
] | ||
} | ||
}, | ||
|
||
"imports": { | ||
"@denosaurs/plug": "jsr:@denosaurs/plug@^1.0.5", | ||
"@std/assert": "jsr:@std/assert@^0.221.0", | ||
"@std/log": "jsr:@std/log@^0.223.0", | ||
"@std/path": "jsr:@std/path@^0.217.0", | ||
"@stdext/collections": "jsr:@stdext/collections@^0.0.5", | ||
"@stdext/sql": "jsr:@stdext/[email protected]" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { assert, assertEquals } from "@std/assert"; | ||
import { SqliteConnectable, SqliteConnection } from "./connection.ts"; | ||
import { _testSqlConnectable, testSqlConnection } from "@stdext/sql/testing"; | ||
|
||
Deno.test("connection and connectable contstructs", () => { | ||
const connection = new SqliteConnection(":memory:"); | ||
testSqlConnection(connection, { connectionUrl: ":memory:" }); | ||
const connectable = new SqliteConnectable(connection); | ||
_testSqlConnectable(connectable, connection); | ||
}); | ||
|
||
Deno.test("connection can connect and query", async () => { | ||
await using connection = new SqliteConnection(":memory:"); | ||
await connection.connect(); | ||
assert(connection.connected, "connection should be connected"); | ||
const executeResult = await connection.execute(`select 1 as one`); | ||
assertEquals(executeResult, 0); | ||
const queryManyResult = connection.queryMany(`select 1 as one`); | ||
const queryManyResultNext1 = await queryManyResult.next(); | ||
assertEquals(queryManyResultNext1, { done: false, value: { one: 1 } }); | ||
const queryManyResultNext2 = await queryManyResult.next(); | ||
assertEquals(queryManyResultNext2, { done: true, value: undefined }); | ||
const queryManyArrayResult = connection.queryManyArray(`select 1 as one`); | ||
const queryManyArrayResultNext1 = await queryManyArrayResult.next(); | ||
assertEquals(queryManyArrayResultNext1, { done: false, value: [1] }); | ||
const queryManyArrayResultNext2 = await queryManyArrayResult.next(); | ||
assertEquals(queryManyArrayResultNext2, { done: true, value: undefined }); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// deno-lint-ignore-file require-await | ||
import type { | ||
ArrayRow, | ||
Row, | ||
SqlConnectable, | ||
SqlConnection, | ||
SqlConnectionOptions, | ||
} from "@stdext/sql"; | ||
import { fromFileUrl } from "@std/path"; | ||
import ffi from "./ffi.ts"; | ||
import { Database, type DatabaseOpenOptions } from "../mod.ts"; | ||
import type { SqliteParameterType, SqliteQueryOptions } from "./core.ts"; | ||
import { transformToAsyncGenerator } from "./util.ts"; | ||
|
||
/** Various options that can be configured when opening Database connection. */ | ||
export interface SqliteConnectionOptions | ||
extends SqlConnectionOptions, DatabaseOpenOptions { | ||
} | ||
|
||
/** | ||
* Represents a SQLx based SQLite3 database connection. | ||
* | ||
* Example: | ||
* ```ts | ||
* // Open a database from file, creates if doesn't exist. | ||
* const db = new SqliteClient("myfile.db"); | ||
* | ||
* // Open an in-memory database. | ||
* const db = new SqliteClient(":memory:"); | ||
* | ||
* // Open a read-only database. | ||
* const db = new SqliteClient("myfile.db", { readonly: true }); | ||
* | ||
* // Or open using File URL | ||
* const db = new SqliteClient(new URL("./myfile.db", import.meta.url)); | ||
* ``` | ||
*/ | ||
export class SqliteConnection implements | ||
SqlConnection< | ||
SqliteConnectionOptions, | ||
SqliteParameterType, | ||
SqliteQueryOptions | ||
> { | ||
readonly connectionUrl: string; | ||
readonly options: SqliteConnectionOptions; | ||
|
||
/** | ||
* The FFI SQLite methods. | ||
*/ | ||
readonly ffi = ffi; | ||
|
||
_db: Database | null = null; | ||
|
||
get db(): Database { | ||
if (this._db === null) { | ||
throw new Error("Database connection is not open"); | ||
} | ||
return this._db; | ||
} | ||
|
||
set db(value: Database | null) { | ||
this._db = value; | ||
} | ||
|
||
get connected(): boolean { | ||
return Boolean(this._db?.open); | ||
} | ||
|
||
constructor( | ||
connectionUrl: string | URL, | ||
options: SqliteConnectionOptions = {}, | ||
) { | ||
this.connectionUrl = connectionUrl instanceof URL | ||
? fromFileUrl(connectionUrl) | ||
: connectionUrl; | ||
this.options = options; | ||
} | ||
|
||
async connect(): Promise<void> { | ||
this.db = new Database(this.connectionUrl, this.options); | ||
} | ||
|
||
async close(): Promise<void> { | ||
this._db?.close(); | ||
this._db = null; | ||
} | ||
|
||
execute( | ||
sql: string, | ||
params?: SqliteParameterType[], | ||
_options?: SqliteQueryOptions, | ||
): Promise<number | undefined> { | ||
return Promise.resolve(this.db.exec(sql, ...(params || []))); | ||
} | ||
queryMany<T extends Row<any> = Row<any>>( | ||
sql: string, | ||
params?: SqliteParameterType[], | ||
options?: SqliteQueryOptions, | ||
): AsyncGenerator<T, any, unknown> { | ||
return transformToAsyncGenerator( | ||
this.db.prepare(sql).getMany<T>(params, options), | ||
); | ||
} | ||
queryManyArray<T extends ArrayRow<any> = ArrayRow<any>>( | ||
sql: string, | ||
params?: SqliteParameterType[], | ||
options?: SqliteQueryOptions, | ||
): AsyncGenerator<T, any, unknown> { | ||
return transformToAsyncGenerator( | ||
this.db.prepare(sql).valueMany<T>(params, options), | ||
); | ||
} | ||
|
||
async [Symbol.asyncDispose](): Promise<void> { | ||
await this.close(); | ||
} | ||
|
||
[Symbol.for("Deno.customInspect")](): string { | ||
return `SQLite3.SqliteConnection { path: ${this.connectionUrl} }`; | ||
} | ||
} | ||
|
||
export class SqliteConnectable implements | ||
SqlConnectable< | ||
SqliteConnectionOptions, | ||
SqliteConnection | ||
> { | ||
readonly connection: SqliteConnection; | ||
readonly options: SqliteConnectionOptions; | ||
get connected(): boolean { | ||
return this.connection.connected; | ||
} | ||
|
||
constructor( | ||
connection: SqliteConnectable["connection"], | ||
options: SqliteConnectable["options"] = {}, | ||
) { | ||
this.connection = connection; | ||
this.options = options; | ||
} | ||
[Symbol.asyncDispose](): Promise<void> { | ||
return this.connection.close(); | ||
} | ||
} |
Oops, something went wrong.