properly guard access to the private fields

This commit is contained in:
Nikita Sivukhin
2025-09-24 17:24:27 +04:00
parent cd9cf71568
commit 28c9850b57
6 changed files with 81 additions and 64 deletions

View File

@@ -11,29 +11,29 @@ async function init(): Promise<Worker> {
} }
class Database extends DatabasePromise { class Database extends DatabasePromise {
worker: Worker | null; #worker: Worker | null;
constructor(path: string, opts: DatabaseOpts = {}) { constructor(path: string, opts: DatabaseOpts = {}) {
super(new NativeDatabase(path, opts) as unknown as any) super(new NativeDatabase(path, opts) as unknown as any)
} }
override async connect() { override async connect() {
if (!this.db.memory) { if (!this.memory) {
const worker = await init(); const worker = await init();
await Promise.all([ await Promise.all([
registerFileAtWorker(worker, this.name), registerFileAtWorker(worker, this.name),
registerFileAtWorker(worker, `${this.name}-wal`) registerFileAtWorker(worker, `${this.name}-wal`)
]); ]);
this.worker = worker; this.#worker = worker;
} }
await super.connect(); await super.connect();
} }
async close() { async close() {
if (this.name != null && this.worker != null) { if (this.name != null && this.#worker != null) {
await Promise.all([ await Promise.all([
unregisterFileAtWorker(this.worker, this.name), unregisterFileAtWorker(this.#worker, this.name),
unregisterFileAtWorker(this.worker, `${this.name}-wal`) unregisterFileAtWorker(this.#worker, `${this.name}-wal`)
]); ]);
} }
this.db.close(); await super.close();
} }
} }

View File

@@ -11,29 +11,29 @@ async function init(): Promise<Worker> {
} }
class Database extends DatabasePromise { class Database extends DatabasePromise {
worker: Worker | null; #worker: Worker | null;
constructor(path: string, opts: DatabaseOpts = {}) { constructor(path: string, opts: DatabaseOpts = {}) {
super(new NativeDatabase(path, opts) as unknown as any) super(new NativeDatabase(path, opts) as unknown as any)
} }
override async connect() { override async connect() {
if (!this.db.memory) { if (!this.memory) {
const worker = await init(); const worker = await init();
await Promise.all([ await Promise.all([
registerFileAtWorker(worker, this.name), registerFileAtWorker(worker, this.name),
registerFileAtWorker(worker, `${this.name}-wal`) registerFileAtWorker(worker, `${this.name}-wal`)
]); ]);
this.worker = worker; this.#worker = worker;
} }
await super.connect(); await super.connect();
} }
async close() { async close() {
if (this.name != null && this.worker != null) { if (this.name != null && this.#worker != null) {
await Promise.all([ await Promise.all([
unregisterFileAtWorker(this.worker, this.name), unregisterFileAtWorker(this.#worker, this.name),
unregisterFileAtWorker(this.worker, `${this.name}-wal`) unregisterFileAtWorker(this.#worker, `${this.name}-wal`)
]); ]);
} }
this.db.close(); await super.close();
} }
} }

View File

@@ -11,29 +11,29 @@ async function init(): Promise<Worker> {
} }
class Database extends DatabasePromise { class Database extends DatabasePromise {
worker: Worker | null; #worker: Worker | null;
constructor(path: string, opts: DatabaseOpts = {}) { constructor(path: string, opts: DatabaseOpts = {}) {
super(new NativeDatabase(path, opts) as unknown as any) super(new NativeDatabase(path, opts) as unknown as any)
} }
override async connect() { override async connect() {
if (!this.db.memory) { if (!this.memory) {
const worker = await init(); const worker = await init();
await Promise.all([ await Promise.all([
registerFileAtWorker(worker, this.name), registerFileAtWorker(worker, this.name),
registerFileAtWorker(worker, `${this.name}-wal`) registerFileAtWorker(worker, `${this.name}-wal`)
]); ]);
this.worker = worker; this.#worker = worker;
} }
await super.connect(); await super.connect();
} }
async close() { async close() {
if (this.name != null && this.worker != null) { if (this.name != null && this.#worker != null) {
await Promise.all([ await Promise.all([
unregisterFileAtWorker(this.worker, this.name), unregisterFileAtWorker(this.#worker, this.name),
unregisterFileAtWorker(this.worker, `${this.name}-wal`) unregisterFileAtWorker(this.#worker, `${this.name}-wal`)
]); ]);
} }
this.db.close(); await super.close();
} }
} }

View File

@@ -11,29 +11,29 @@ async function init(): Promise<Worker> {
} }
class Database extends DatabasePromise { class Database extends DatabasePromise {
worker: Worker | null; #worker: Worker | null;
constructor(path: string, opts: DatabaseOpts = {}) { constructor(path: string, opts: DatabaseOpts = {}) {
super(new NativeDatabase(path, opts) as unknown as any) super(new NativeDatabase(path, opts) as unknown as any)
} }
override async connect() { override async connect() {
if (!this.db.memory) { if (!this.memory) {
const worker = await init(); const worker = await init();
await Promise.all([ await Promise.all([
registerFileAtWorker(worker, this.name), registerFileAtWorker(worker, this.name),
registerFileAtWorker(worker, `${this.name}-wal`) registerFileAtWorker(worker, `${this.name}-wal`)
]); ]);
this.worker = worker; this.#worker = worker;
} }
await super.connect(); await super.connect();
} }
async close() { async close() {
if (this.name != null && this.worker != null) { if (this.name != null && this.#worker != null) {
await Promise.all([ await Promise.all([
unregisterFileAtWorker(this.worker, this.name), unregisterFileAtWorker(this.#worker, this.name),
unregisterFileAtWorker(this.worker, `${this.name}-wal`) unregisterFileAtWorker(this.#worker, `${this.name}-wal`)
]); ]);
} }
this.db.close(); await super.close();
} }
} }

View File

@@ -29,7 +29,13 @@ function createErrorByName(name, message) {
* Database represents a connection that can prepare and execute SQL statements. * Database represents a connection that can prepare and execute SQL statements.
*/ */
class Database { class Database {
db: NativeDatabase; name: string;
readonly: boolean;
open: boolean;
memory: boolean;
inTransaction: boolean;
private db: NativeDatabase;
private _inTransaction: boolean = false; private _inTransaction: boolean = false;
/** /**
@@ -47,10 +53,11 @@ class Database {
this.db.connectSync(); this.db.connectSync();
Object.defineProperties(this, { Object.defineProperties(this, {
inTransaction: { get: () => this._inTransaction },
name: { get: () => this.db.path }, name: { get: () => this.db.path },
readonly: { get: () => this.db.readonly }, readonly: { get: () => this.db.readonly },
open: { get: () => this.db.open }, open: { get: () => this.db.open },
memory: { get: () => this.db.memory },
inTransaction: { get: () => this._inTransaction },
}); });
} }
@@ -65,7 +72,7 @@ class Database {
} }
try { try {
return new Statement(this.db.prepare(sql), this); return new Statement(this.db.prepare(sql), this.db);
} catch (err) { } catch (err) {
throw convertError(err); throw convertError(err);
} }
@@ -202,11 +209,11 @@ class Database {
*/ */
class Statement { class Statement {
stmt: NativeStatement; stmt: NativeStatement;
db: Database; db: NativeDatabase;
constructor(stmt: NativeStatement, database: Database) { constructor(stmt: NativeStatement, db: NativeDatabase) {
this.stmt = stmt; this.stmt = stmt;
this.db = database; this.db = db;
} }
/** /**
@@ -264,14 +271,14 @@ class Statement {
* Executes the SQL statement and returns an info object. * Executes the SQL statement and returns an info object.
*/ */
run(...bindParameters) { run(...bindParameters) {
const totalChangesBefore = this.db.db.totalChanges(); const totalChangesBefore = this.db.totalChanges();
this.stmt.reset(); this.stmt.reset();
bindParams(this.stmt, bindParameters); bindParams(this.stmt, bindParameters);
for (; ;) { for (; ;) {
const stepResult = this.stmt.stepSync(); const stepResult = this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
this.db.db.ioLoopSync(); this.db.ioLoopSync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -283,8 +290,8 @@ class Statement {
} }
} }
const lastInsertRowid = this.db.db.lastInsertRowid(); const lastInsertRowid = this.db.lastInsertRowid();
const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes(); const changes = this.db.totalChanges() === totalChangesBefore ? 0 : this.db.changes();
return { changes, lastInsertRowid }; return { changes, lastInsertRowid };
} }
@@ -300,7 +307,7 @@ class Statement {
for (; ;) { for (; ;) {
const stepResult = this.stmt.stepSync(); const stepResult = this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
this.db.db.ioLoopSync(); this.db.ioLoopSync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -324,7 +331,7 @@ class Statement {
while (true) { while (true) {
const stepResult = this.stmt.stepSync(); const stepResult = this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
this.db.db.ioLoopSync(); this.db.ioLoopSync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -348,7 +355,7 @@ class Statement {
for (; ;) { for (; ;) {
const stepResult = this.stmt.stepSync(); const stepResult = this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
this.db.db.ioLoopSync(); this.db.ioLoopSync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {

View File

@@ -30,18 +30,25 @@ function createErrorByName(name, message) {
* Database represents a connection that can prepare and execute SQL statements. * Database represents a connection that can prepare and execute SQL statements.
*/ */
class Database { class Database {
db: NativeDatabase;
execLock: AsyncLock;
name: string; name: string;
readonly: boolean;
open: boolean;
memory: boolean;
inTransaction: boolean;
private db: NativeDatabase;
private execLock: AsyncLock;
private _inTransaction: boolean = false; private _inTransaction: boolean = false;
constructor(db: NativeDatabase) { constructor(db: NativeDatabase) {
this.db = db; this.db = db;
this.execLock = new AsyncLock(); this.execLock = new AsyncLock();
Object.defineProperties(this, { Object.defineProperties(this, {
inTransaction: { get: () => this._inTransaction },
name: { get: () => this.db.path }, name: { get: () => this.db.path },
readonly: { get: () => this.db.readonly }, readonly: { get: () => this.db.readonly },
open: { get: () => this.db.open }, open: { get: () => this.db.open },
memory: { get: () => this.db.memory },
inTransaction: { get: () => this._inTransaction },
}); });
} }
@@ -60,7 +67,7 @@ class Database {
} }
try { try {
return new Statement(this.db.prepare(sql), this); return new Statement(this.db.prepare(sql), this.db, this.execLock);
} catch (err) { } catch (err) {
throw convertError(err); throw convertError(err);
} }
@@ -196,11 +203,14 @@ class Database {
* Statement represents a prepared SQL statement that can be executed. * Statement represents a prepared SQL statement that can be executed.
*/ */
class Statement { class Statement {
stmt: NativeStatement; private stmt: NativeStatement;
db: Database; private db: NativeDatabase;
constructor(stmt, database) { private execLock: AsyncLock;
constructor(stmt: NativeStatement, db: NativeDatabase, execLock: AsyncLock) {
this.stmt = stmt; this.stmt = stmt;
this.db = database; this.db = db;
this.execLock = execLock;
} }
/** /**
@@ -258,17 +268,17 @@ class Statement {
* Executes the SQL statement and returns an info object. * Executes the SQL statement and returns an info object.
*/ */
async run(...bindParameters) { async run(...bindParameters) {
const totalChangesBefore = this.db.db.totalChanges(); const totalChangesBefore = this.db.totalChanges();
this.stmt.reset(); this.stmt.reset();
bindParams(this.stmt, bindParameters); bindParams(this.stmt, bindParameters);
await this.db.execLock.acquire(); await this.execLock.acquire();
try { try {
while (true) { while (true) {
const stepResult = await this.stmt.stepSync(); const stepResult = await this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
await this.db.db.ioLoopAsync(); await this.db.ioLoopAsync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -280,12 +290,12 @@ class Statement {
} }
} }
const lastInsertRowid = this.db.db.lastInsertRowid(); const lastInsertRowid = this.db.lastInsertRowid();
const changes = this.db.db.totalChanges() === totalChangesBefore ? 0 : this.db.db.changes(); const changes = this.db.totalChanges() === totalChangesBefore ? 0 : this.db.changes();
return { changes, lastInsertRowid }; return { changes, lastInsertRowid };
} finally { } finally {
this.db.execLock.release(); this.execLock.release();
} }
} }
@@ -298,12 +308,12 @@ class Statement {
this.stmt.reset(); this.stmt.reset();
bindParams(this.stmt, bindParameters); bindParams(this.stmt, bindParameters);
await this.db.execLock.acquire(); await this.execLock.acquire();
try { try {
while (true) { while (true) {
const stepResult = await this.stmt.stepSync(); const stepResult = await this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
await this.db.db.ioLoopAsync(); await this.db.ioLoopAsync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -314,7 +324,7 @@ class Statement {
} }
} }
} finally { } finally {
this.db.execLock.release(); this.execLock.release();
} }
} }
@@ -327,12 +337,12 @@ class Statement {
this.stmt.reset(); this.stmt.reset();
bindParams(this.stmt, bindParameters); bindParams(this.stmt, bindParameters);
await this.db.execLock.acquire(); await this.execLock.acquire();
try { try {
while (true) { while (true) {
const stepResult = await this.stmt.stepSync(); const stepResult = await this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
await this.db.db.ioLoopAsync(); await this.db.ioLoopAsync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -343,7 +353,7 @@ class Statement {
} }
} }
} finally { } finally {
this.db.execLock.release(); this.execLock.release();
} }
} }
@@ -357,12 +367,12 @@ class Statement {
bindParams(this.stmt, bindParameters); bindParams(this.stmt, bindParameters);
const rows: any[] = []; const rows: any[] = [];
await this.db.execLock.acquire(); await this.execLock.acquire();
try { try {
while (true) { while (true) {
const stepResult = await this.stmt.stepSync(); const stepResult = await this.stmt.stepSync();
if (stepResult === STEP_IO) { if (stepResult === STEP_IO) {
await this.db.db.ioLoopAsync(); await this.db.ioLoopAsync();
continue; continue;
} }
if (stepResult === STEP_DONE) { if (stepResult === STEP_DONE) {
@@ -375,7 +385,7 @@ class Statement {
return rows; return rows;
} }
finally { finally {
this.db.execLock.release(); this.execLock.release();
} }
} }