Skip to content
Open
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
3 changes: 2 additions & 1 deletion doc/api/sqlite.md
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,8 @@ wrapper around [`sqlite3session_patchset()`][].

### `session.close()`

Closes the session. An exception is thrown if the database or the session is not open. This method is a
Closes the session. An exception is thrown if the database or the session is not open,
or if the session is currently generating a changeset or patchset. This method is a
wrapper around [`sqlite3session_delete()`][].

### `session[Symbol.dispose]()`
Expand Down
6 changes: 6 additions & 0 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4326,6 +4326,10 @@ void Session::Changeset(const FunctionCallbackInfo<Value>& args) {
env, session->session_ == nullptr, "session is not open");
THROW_AND_RETURN_IF_IN_AUTHORIZER(env, session->database_.get());

session->is_generating_changeset_ = true;
auto changeset_guard =
OnScopeLeave([&] { session->is_generating_changeset_ = false; });

int nChangeset;
void* pChangeset;
int r = sqliteChangesetFunc(session->session_, &nChangeset, &pChangeset);
Expand All @@ -4351,6 +4355,8 @@ void Session::Close(const FunctionCallbackInfo<Value>& args) {
env, !session->database_->IsOpen(), "database is not open");
THROW_AND_RETURN_ON_BAD_STATE(
env, session->session_ == nullptr, "session is not open");
THROW_AND_RETURN_ON_BAD_STATE(
env, session->is_generating_changeset_, "session is currently in use");

session->Delete();
}
Expand Down
1 change: 1 addition & 0 deletions src/node_sqlite.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ class Session : public BaseObject {
void Delete();
sqlite3_session* session_;
BaseObjectPtr<DatabaseSync> database_; // The Parent Database
bool is_generating_changeset_ = false;

friend class DatabaseSync;
};
Expand Down
22 changes: 22 additions & 0 deletions test/parallel/test-sqlite-session.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,28 @@ test('session.close() - closing twice', (t) => {
});
});

test('session.close() - while generating changes throws exception', (t) => {
for (const method of ['changeset', 'patchset']) {
const database = new DatabaseSync(':memory:');
database.exec('CREATE TABLE data(key INTEGER PRIMARY KEY, value TEXT)');

const session = database.createSession({ table: 'data' });
database.exec("INSERT INTO data VALUES (1, 'a'), (2, 'b'), (3, 'c')");
database.setAuthorizer(() => {
session.close();
return constants.SQLITE_OK;
});

t.assert.throws(() => session[method](), {
code: 'ERR_INVALID_STATE',
message: 'session is currently in use',
});

database.setAuthorizer(null);
t.assert.notStrictEqual(session[method]().length, 0);
}
});

test('session - keeps its database alive after the db handle is dropped', async (t) => {
const { gcUntil, onGC } = require('../common/gc');

Expand Down
Loading