Skip to content
Merged
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
48 changes: 48 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,54 @@ Prepares a SQL statement for execution.

The function returns a `Statement` object.

### run(sql[, ...bindParameters][, queryOptions]) ⇒ object

Convenience wrapper that prepares `sql` and executes `Statement.run`. Returns the same info object as `Statement.run` (`changes` and `lastInsertRowid`).

| Param | Type | Description |
| -------------- | ------------------- | -------------------------------------------------------------------- |
| sql | <code>string</code> | The SQL statement string. |
| bindParameters | <code>any</code> | Optional positional or named bind parameters. |
| queryOptions | <code>object</code> | Optional per-query overrides (for example, `{ queryTimeout: 100 }`). |

**Note:** This is an extension in libSQL and not available in `better-sqlite3`.

### get(sql[, ...bindParameters][, queryOptions]) ⇒ row

Convenience wrapper that prepares `sql` and executes `Statement.get`. Returns the first row, or `undefined` if no row matched.

| Param | Type | Description |
| -------------- | ------------------- | -------------------------------------------------------------------- |
| sql | <code>string</code> | The SQL statement string. |
| bindParameters | <code>any</code> | Optional positional or named bind parameters. |
| queryOptions | <code>object</code> | Optional per-query overrides (for example, `{ queryTimeout: 100 }`). |

**Note:** This is an extension in libSQL and not available in `better-sqlite3`.

### all(sql[, ...bindParameters][, queryOptions]) ⇒ array of rows

Convenience wrapper that prepares `sql` and executes `Statement.all`. Returns all matching rows as an array.

| Param | Type | Description |
| -------------- | ------------------- | -------------------------------------------------------------------- |
| sql | <code>string</code> | The SQL statement string. |
| bindParameters | <code>any</code> | Optional positional or named bind parameters. |
| queryOptions | <code>object</code> | Optional per-query overrides (for example, `{ queryTimeout: 100 }`). |

**Note:** This is an extension in libSQL and not available in `better-sqlite3`.

### iterate(sql[, ...bindParameters][, queryOptions]) ⇒ iterator

Convenience wrapper that prepares `sql` and executes `Statement.iterate`. Returns an async iterator over the resulting rows.

| Param | Type | Description |
| -------------- | ------------------- | -------------------------------------------------------------------- |
| sql | <code>string</code> | The SQL statement string. |
| bindParameters | <code>any</code> | Optional positional or named bind parameters. |
| queryOptions | <code>object</code> | Optional per-query overrides (for example, `{ queryTimeout: 100 }`). |

**Note:** This is an extension in libSQL and not available in `better-sqlite3`.

### transaction(function) ⇒ function

Returns a function that runs the given function in a transaction.
Expand Down
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,8 @@ export declare class Statement {
}
/** A raw iterator over rows. The JavaScript layer wraps this in a iterable. */
export declare class RowsIterator {
close(): void
next(): Promise<Record>
close(): void
}
export declare class Record {
get value(): unknown
Expand Down
97 changes: 97 additions & 0 deletions integration-tests/tests/async.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,103 @@ test.serial("Statement.reader [DELETE RETURNING is true]", async (t) => {
t.is(stmt.reader, true);
});

test.serial("Database.run() [positional]", async (t) => {
const db = t.context.db;

const info = await db.run(
"INSERT INTO users(name, email) VALUES (?, ?)",
"Carol",
"carol@example.net"
);
t.is(info.changes, 1);
t.is(info.lastInsertRowid, 3);

const row = await db.get("SELECT name, email FROM users WHERE id = ?", 3);
t.is(row.name, "Carol");
t.is(row.email, "carol@example.net");
});

test.serial("Database.run() [named]", async (t) => {
const db = t.context.db;

const info = await db.run(
"INSERT INTO users(name, email) VALUES (:name, :email)",
{ name: "Carol", email: "carol@example.net" }
);
t.is(info.changes, 1);
t.is(info.lastInsertRowid, 3);
});

test.serial("Database.get() returns no rows", async (t) => {
const db = t.context.db;
t.is(await db.get("SELECT * FROM users WHERE id = ?", 0), undefined);
});

test.serial("Database.get() [positional]", async (t) => {
const db = t.context.db;
t.is((await db.get("SELECT * FROM users WHERE id = ?", 1)).name, "Alice");
t.is((await db.get("SELECT * FROM users WHERE id = ?", 2)).name, "Bob");
});

test.serial("Database.get() [named]", async (t) => {
const db = t.context.db;
t.is(
(await db.get("SELECT * FROM users WHERE id = :id", { id: 1 })).name,
"Alice"
);
});

test.serial("Database.all()", async (t) => {
const db = t.context.db;
const expected = [
{ id: 1, name: "Alice", email: "alice@example.org" },
{ id: 2, name: "Bob", email: "bob@example.com" },
];
t.deepEqual(await db.all("SELECT * FROM users"), expected);
});

test.serial("Database.all() [positional]", async (t) => {
const db = t.context.db;
const expected = [{ id: 1, name: "Alice", email: "alice@example.org" }];
t.deepEqual(await db.all("SELECT * FROM users WHERE id = ?", 1), expected);
});

test.serial("Database.iterate()", async (t) => {
const db = t.context.db;
const expected = [1, 2];
let idx = 0;
for await (const row of await db.iterate("SELECT * FROM users")) {
t.is(row.id, expected[idx++]);
}
t.is(idx, 2);
});

test.serial("Database.iterate() [positional]", async (t) => {
const db = t.context.db;
let count = 0;
for await (const row of await db.iterate(
"SELECT * FROM users WHERE id = ?",
2
)) {
t.is(row.name, "Bob");
count++;
}
t.is(count, 1);
});

test.serial("Database.run() forwards queryOptions", async (t) => {
const db = t.context.db;
await t.throwsAsync(
async () => {
await db.run(
"WITH RECURSIVE infinite(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM infinite) SELECT count(*) FROM infinite",
{ queryTimeout: 50 }
);
},
{ instanceOf: t.context.errorType, code: "SQLITE_INTERRUPT" }
);
});

const connect = async (path_opt, options = {}) => {
const path = path_opt ?? "hello.db";
const provider = process.env.PROVIDER;
Expand Down
49 changes: 48 additions & 1 deletion promise.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ function splitBindParameters(bindParameters) {
if (bindParameters.length === 0) {
return { params: undefined, queryOptions: undefined };
}
if (bindParameters.length > 1 && isQueryOptions(bindParameters[bindParameters.length - 1])) {
if (isQueryOptions(bindParameters[bindParameters.length - 1])) {
if (bindParameters.length === 1) {
return { params: undefined, queryOptions: bindParameters[0] };
}
return {
params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1),
queryOptions: bindParameters[bindParameters.length - 1],
Expand Down Expand Up @@ -169,6 +172,50 @@ class Database {
return properties.default.value;
}

/**
* Prepares the SQL and executes it as `Statement.run`, returning the run info.
*
* @param {string} sql - The SQL statement string.
* @param {...any} bindParameters - Bind parameters, optionally followed by a query options object.
*/
async run(sql, ...bindParameters) {
const stmt = await this.prepare(sql);
return await stmt.run(...bindParameters);
}

/**
* Prepares the SQL and executes it as `Statement.get`, returning the first row.
*
* @param {string} sql - The SQL statement string.
* @param {...any} bindParameters - Bind parameters, optionally followed by a query options object.
*/
async get(sql, ...bindParameters) {
const stmt = await this.prepare(sql);
return await stmt.get(...bindParameters);
}

/**
* Prepares the SQL and executes it as `Statement.all`, returning all rows.
*
* @param {string} sql - The SQL statement string.
* @param {...any} bindParameters - Bind parameters, optionally followed by a query options object.
*/
async all(sql, ...bindParameters) {
const stmt = await this.prepare(sql);
return await stmt.all(...bindParameters);
}

/**
* Prepares the SQL and executes it as `Statement.iterate`, returning an async iterator over rows.
*
* @param {string} sql - The SQL statement string.
* @param {...any} bindParameters - Bind parameters, optionally followed by a query options object.
*/
async iterate(sql, ...bindParameters) {
const stmt = await this.prepare(sql);
return await stmt.iterate(...bindParameters);
}

/**
* Execute a pragma statement
* @param {string} source - The pragma statement to execute, without the `PRAGMA` prefix.
Expand Down
Loading