diff --git a/docs/api.md b/docs/api.md
index 026e0f3..eb1e55d 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -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 | string | The SQL statement string. |
+| bindParameters | any | Optional positional or named bind parameters. |
+| queryOptions | object | 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 | string | The SQL statement string. |
+| bindParameters | any | Optional positional or named bind parameters. |
+| queryOptions | object | 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 | string | The SQL statement string. |
+| bindParameters | any | Optional positional or named bind parameters. |
+| queryOptions | object | 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 | string | The SQL statement string. |
+| bindParameters | any | Optional positional or named bind parameters. |
+| queryOptions | object | 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.
diff --git a/index.d.ts b/index.d.ts
index 9cdec0a..91e6140 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -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
+ close(): void
}
export declare class Record {
get value(): unknown
diff --git a/integration-tests/tests/async.test.js b/integration-tests/tests/async.test.js
index 28b4381..38f24d2 100644
--- a/integration-tests/tests/async.test.js
+++ b/integration-tests/tests/async.test.js
@@ -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;
diff --git a/promise.js b/promise.js
index 95ca75b..f020de7 100644
--- a/promise.js
+++ b/promise.js
@@ -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],
@@ -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.