Skip to content

Commit 36e1920

Browse files
committed
src, test: allow specifying options.table to createSession
1 parent c049c41 commit 36e1920

File tree

2 files changed

+138
-55
lines changed

2 files changed

+138
-55
lines changed

src/node_sqlite.cc

+34-4
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,40 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
223223
}
224224

225225
void DatabaseSync::CreateSession(const FunctionCallbackInfo<Value>& args) {
226+
std::string table;
227+
228+
Environment* env = Environment::GetCurrent(args);
229+
if (args.Length() > 0) {
230+
if (!args[0]->IsObject()) {
231+
node::THROW_ERR_INVALID_ARG_TYPE(
232+
env->isolate(), "The \"options\" argument must be an object.");
233+
return;
234+
}
235+
236+
Local<Object> options = args[0].As<Object>();
237+
238+
Local<String> table_key = String::NewFromUtf8(
239+
env->isolate(),
240+
"table",
241+
v8::NewStringType::kNormal).ToLocalChecked();
242+
if (options->HasOwnProperty(env->context(), table_key).FromJust()) {
243+
Local<Value> table_value = options->Get(
244+
env->context(),
245+
table_key).ToLocalChecked();
246+
247+
if (table_value->IsString()) {
248+
String::Utf8Value str(env->isolate(), table_value);
249+
table = std::string(*str);
250+
} else {
251+
node::THROW_ERR_INVALID_ARG_TYPE(
252+
env->isolate(), "The \"options.table\" argument must be a string.");
253+
return;
254+
}
255+
}
256+
}
257+
226258
DatabaseSync* db;
227259
ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
228-
Environment* env = Environment::GetCurrent(args);
229260
THROW_AND_RETURN_ON_BAD_STATE(
230261
env, db->connection_ == nullptr, "database is not open");
231262

@@ -235,8 +266,7 @@ void DatabaseSync::CreateSession(const FunctionCallbackInfo<Value>& args) {
235266
CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
236267
db->sessions_.insert(pSession);
237268

238-
// TODO(louwers): allow specifying table name
239-
r = sqlite3session_attach(pSession, nullptr);
269+
r = sqlite3session_attach(pSession, table == "" ? nullptr : table.c_str());
240270
CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
241271

242272

@@ -770,7 +800,7 @@ void Session::Changeset(const v8::FunctionCallbackInfo<v8::Value>& args) {
770800
Session* session;
771801
ASSIGN_OR_RETURN_UNWRAP(&session, args.This());
772802
Environment* env = Environment::GetCurrent(args);
773-
sqlite3 *db = session->database_ ? session->database_->connection_ : nullptr;
803+
sqlite3* db = session->database_ ? session->database_->connection_ : nullptr;
774804
THROW_AND_RETURN_ON_BAD_STATE(env, db == nullptr, "database is not open");
775805

776806
int nChangeset;

test/parallel/test-sqlite.js

+104-51
Original file line numberDiff line numberDiff line change
@@ -757,68 +757,121 @@ test('PRAGMAs are supported', (t) => {
757757
);
758758
});
759759

760-
test('creating and applying a changeset', (t) => {
761-
const createDatabase = () => {
762-
const database = new DatabaseSync(':memory:');
763-
database.exec(`
760+
suite('session extension', () => {
761+
test('creating and applying a changeset', (t) => {
762+
const createDataTableSql = `
764763
CREATE TABLE data(
765764
key INTEGER PRIMARY KEY,
766765
value TEXT
767-
) STRICT
768-
`);
769-
return database;
770-
};
766+
) STRICT`;
771767

772-
const databaseFrom = createDatabase();
773-
const session = databaseFrom.createSession();
768+
const createDatabase = () => {
769+
const database = new DatabaseSync(':memory:');
770+
database.exec(createDataTableSql);
771+
return database;
772+
};
774773

775-
const select = 'SELECT * FROM data ORDER BY key';
774+
const databaseFrom = createDatabase();
775+
const session = databaseFrom.createSession();
776776

777-
const insert = databaseFrom.prepare('INSERT INTO data (key, value) VALUES (?, ?)');
778-
insert.run(1, 'hello');
779-
insert.run(2, 'world');
777+
const select = 'SELECT * FROM data ORDER BY key';
780778

781-
const databaseTo = createDatabase();
779+
const insert = databaseFrom.prepare('INSERT INTO data (key, value) VALUES (?, ?)');
780+
insert.run(1, 'hello');
781+
insert.run(2, 'world');
782782

783-
databaseTo.applyChangeset(session.changeset());
784-
t.assert.deepStrictEqual(
785-
databaseFrom.prepare(select).all(),
786-
databaseTo.prepare(select).all()
787-
);
788-
});
783+
const databaseTo = createDatabase();
789784

790-
test('trying to create session when database is closed results in exception', (t) => {
791-
const database = new DatabaseSync(':memory:');
792-
database.close();
793-
t.assert.throws(() => {
794-
database.createSession();
795-
}, {
796-
name: 'Error',
797-
message: 'database is not open',
785+
databaseTo.applyChangeset(session.changeset());
786+
t.assert.deepStrictEqual(
787+
databaseFrom.prepare(select).all(),
788+
databaseTo.prepare(select).all()
789+
);
798790
});
799-
});
800791

801-
test('trying to create changeset when database is closed results in exception', (t) => {
802-
const database = new DatabaseSync(':memory:');
803-
const session = database.createSession();
804-
database.close();
805-
t.assert.throws(() => {
806-
session.changeset();
807-
}, {
808-
name: 'Error',
809-
message: 'database is not open',
792+
test('trying to create session when database is closed results in exception', (t) => {
793+
const database = new DatabaseSync(':memory:');
794+
database.close();
795+
t.assert.throws(() => {
796+
database.createSession();
797+
}, {
798+
name: 'Error',
799+
message: 'database is not open',
800+
});
810801
});
811-
});
812802

813-
test('trying to apply a changeset when database is closed results in exception', (t) => {
814-
const database = new DatabaseSync(':memory:');
815-
const session = database.createSession();
816-
const changeset = session.changeset();
817-
database.close();
818-
t.assert.throws(() => {
819-
database.applyChangeset(changeset);
820-
}, {
821-
name: 'Error',
822-
message: 'database is not open',
803+
test('trying to create changeset when database is closed results in exception', (t) => {
804+
const database = new DatabaseSync(':memory:');
805+
const session = database.createSession();
806+
database.close();
807+
t.assert.throws(() => {
808+
session.changeset();
809+
}, {
810+
name: 'Error',
811+
message: 'database is not open',
812+
});
813+
});
814+
815+
test('trying to apply a changeset when database is closed results in exception', (t) => {
816+
const database = new DatabaseSync(':memory:');
817+
const session = database.createSession();
818+
const changeset = session.changeset();
819+
database.close();
820+
t.assert.throws(() => {
821+
database.applyChangeset(changeset);
822+
}, {
823+
name: 'Error',
824+
message: 'database is not open',
825+
});
826+
});
827+
828+
test('set table with wrong type when creating session', (t) => {
829+
const database = new DatabaseSync(':memory:');
830+
t.assert.throws(() => {
831+
database.createSession({
832+
table: true
833+
});
834+
}, {
835+
name: 'TypeError',
836+
message: 'The "table" property must be a string.'
837+
});
823838
});
824-
});
839+
840+
test('setting options.table causes only one table to be tracked', (t) => {
841+
const database1 = new DatabaseSync(':memory:');
842+
const database2 = new DatabaseSync(':memory:');
843+
844+
const createData1TableSql = `CREATE TABLE data1 (
845+
key INTEGER PRIMARY KEY,
846+
value TEXT
847+
) STRICT
848+
`;
849+
const createData2TableSql = `CREATE TABLE data2 (
850+
key INTEGER PRIMARY KEY,
851+
value TEXT
852+
) STRICT
853+
`;
854+
database1.exec(createData1TableSql);
855+
database1.exec(createData2TableSql);
856+
database2.exec(createData1TableSql);
857+
database2.exec(createData2TableSql);
858+
859+
const session = database1.createSession({
860+
table: 'data1'
861+
});
862+
const insert1 = database1.prepare('INSERT INTO data1 (key, value) VALUES (?, ?)');
863+
insert1.run(1, 'hello');
864+
insert1.run(2, 'world');
865+
const insert2 = database1.prepare('INSERT INTO data2 (key, value) VALUES (?, ?)');
866+
insert2.run(1, 'hello');
867+
insert2.run(2, 'world');
868+
const select1 = 'SELECT * FROM data1 ORDER BY key';
869+
const select2 = 'SELECT * FROM data2 ORDER BY key';
870+
database2.applyChangeset(session.changeset());
871+
t.assert.deepStrictEqual(
872+
database1.prepare(select1).all(),
873+
database2.prepare(select1).all()); // data1 table should be equal
874+
t.assert.deepStrictEqual(database2.prepare(select2).all(), []); // data2 should be empty in database2
875+
t.assert.strictEqual(database1.prepare(select2).all().length, 2); // data1 should have values in database1
876+
});
877+
});

0 commit comments

Comments
 (0)