fix exec to run over multiple statements in the string

This commit is contained in:
Nikita Sivukhin
2025-09-25 12:03:52 +04:00
parent ddfa77997d
commit a938bdcf09
9 changed files with 169 additions and 57 deletions

View File

@@ -167,16 +167,30 @@ class Database {
}
/**
* Executes a SQL statement.
* Executes the given SQL string
* Unlike prepared statements, this can execute strings that contain multiple SQL statements
*
* @param {string} sql - The SQL statement string to execute.
* @param {string} sql - The string containing SQL statements to execute
*/
exec(sql) {
let stmt = this.prepare(sql);
const exec = this.db.executor(sql);
try {
stmt.run();
while (true) {
const stepResult = exec.stepSync();
if (stepResult === STEP_IO) {
this.db.ioLoopSync();
continue;
}
if (stepResult === STEP_DONE) {
break;
}
if (stepResult === STEP_ROW) {
// For exec(), we don't need the row data, just continue
continue;
}
}
} finally {
stmt.close();
exec.reset();
}
}

View File

@@ -165,16 +165,32 @@ class Database {
}
/**
* Executes a SQL statement.
* Executes the given SQL string
* Unlike prepared statements, this can execute strings that contain multiple SQL statements
*
* @param {string} sql - The SQL statement string to execute.
* @param {string} sql - The string containing SQL statements to execute
*/
async exec(sql) {
const stmt = this.prepare(sql);
await this.execLock.acquire();
const exec = this.db.executor(sql);
try {
await stmt.run();
while (true) {
const stepResult = exec.stepSync();
if (stepResult === STEP_IO) {
await this.db.ioLoopAsync();
continue;
}
if (stepResult === STEP_DONE) {
break;
}
if (stepResult === STEP_ROW) {
// For exec(), we don't need the row data, just continue
continue;
}
}
} finally {
await stmt.close();
exec.reset();
this.execLock.release();
}
}

View File

@@ -19,6 +19,7 @@ export interface NativeDatabase {
ioLoopAsync(): Promise<void>;
prepare(sql: string): NativeStatement;
executor(sql: string): NativeExecutor;
defaultSafeIntegers(toggle: boolean);
totalChanges(): number;
@@ -38,6 +39,10 @@ export interface TableColumn {
type: string
}
export interface NativeExecutor {
stepSync(): number;
reset();
}
export interface NativeStatement {
stepAsync(): Promise<number>;
stepSync(): number;

View File

@@ -11,6 +11,14 @@ test('in-memory db', () => {
expect(rows).toEqual([{ x: 1 }, { x: 3 }]);
})
test('exec multiple statements', async () => {
const db = new Database(":memory:");
db.exec("CREATE TABLE t(x); INSERT INTO t VALUES (1); INSERT INTO t VALUES (2)");
const stmt = db.prepare("SELECT * FROM t");
const rows = stmt.all();
expect(rows).toEqual([{ x: 1 }, { x: 2 }]);
})
test('readonly-db', () => {
const path = `test-${(Math.random() * 10000) | 0}.db`;
try {

View File

@@ -1,5 +1,10 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export declare class BatchExecutor {
stepSync(): number
reset(): void
}
/** A database connection. */
export declare class Database {
/**
@@ -39,6 +44,7 @@ export declare class Database {
* A `Statement` instance.
*/
prepare(sql: string): Statement
executor(sql: string): BatchExecutor
/**
* Returns the rowid of the last row inserted.
*

View File

@@ -508,6 +508,7 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { Database, Statement } = nativeBinding
const { BatchExecutor, Database, Statement } = nativeBinding
export { BatchExecutor }
export { Database }
export { Statement }

View File

@@ -31,6 +31,14 @@ test('in-memory-db-async', async () => {
expect(rows).toEqual([{ x: 1 }, { x: 3 }]);
})
test('exec multiple statements', async () => {
const db = await connect(":memory:");
await db.exec("CREATE TABLE t(x); INSERT INTO t VALUES (1); INSERT INTO t VALUES (2)");
const stmt = db.prepare("SELECT * FROM t");
const rows = await stmt.all();
expect(rows).toEqual([{ x: 1 }, { x: 2 }]);
})
test('readonly-db', async () => {
const path = `test-${(Math.random() * 10000) | 0}.db`;
try {