Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: WiseLibs/better-sqlite3
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: missinglink/better-sqlite3
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: unsafe_statement
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 1 commit
  • 7 files changed
  • 1 contributor

Commits on Mar 26, 2020

  1. Copy the full SHA
    35d9469 View commit details
Showing with 244 additions and 153 deletions.
  1. +94 −81 src/better_sqlite3.cpp
  2. +69 −65 src/better_sqlite3.hpp
  3. +9 −1 src/objects/database.lzz
  4. +9 −5 src/objects/statement.lzz
  5. +2 −0 src/util/constants.lzz
  6. +1 −1 src/util/query-macros.lzz
  7. +60 −0 test/44.unsafe.mode.js
175 changes: 94 additions & 81 deletions src/better_sqlite3.cpp

Large diffs are not rendered by default.

134 changes: 69 additions & 65 deletions src/better_sqlite3.hpp
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@
#endif
#line 153 "./src/util/macros.lzz"
template <class T> using CopyablePersistent = v8::Persistent<T, v8::CopyablePersistentTraits<T>>;
#line 158 "./src/util/constants.lzz"
#line 160 "./src/util/constants.lzz"
typedef v8::Persistent<v8::String> ConstantString;
#define LZZ_INLINE inline
#line 18 "./src/util/macros.lzz"
@@ -105,21 +105,23 @@ class CS
static ConstantString totalPages;
#line 30 "./src/util/constants.lzz"
static ConstantString remainingPages;
#line 32 "./src/util/constants.lzz"
private:
#line 31 "./src/util/constants.lzz"
static ConstantString unsafe;
#line 33 "./src/util/constants.lzz"
private:
#line 34 "./src/util/constants.lzz"
friend void RegisterModule (v8::Local <v8 :: Object> exports, v8::Local <v8 :: Object> module);
#line 33 "./src/util/constants.lzz"
#line 34 "./src/util/constants.lzz"
static void Init (v8::Isolate * isolate, v8::Local <v8 :: Object> exports, v8::Local <v8 :: Object> module);
#line 143 "./src/util/constants.lzz"
#line 145 "./src/util/constants.lzz"
static v8::Local <v8::String> InternalizedFromLatin1 (v8::Isolate * isolate, char const * str);
#line 146 "./src/util/constants.lzz"
#line 148 "./src/util/constants.lzz"
static void AddString (v8::Isolate * isolate, ConstantString & constant, char const * str);
#line 149 "./src/util/constants.lzz"
#line 151 "./src/util/constants.lzz"
static void AddCode (v8::Isolate * isolate, int code, char const * str);
#line 152 "./src/util/constants.lzz"
explicit CS (char _);
#line 154 "./src/util/constants.lzz"
explicit CS (char _);
#line 156 "./src/util/constants.lzz"
static std::unordered_map <int, ConstantString> codes;
};
#line 1 "./src/util/bind-map.lzz"
@@ -291,61 +293,61 @@ class Database : public node::ObjectWrap
static void JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 176 "./src/objects/database.lzz"
static void JS_prepare (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 182 "./src/objects/database.lzz"
#line 190 "./src/objects/database.lzz"
static void JS_exec (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 202 "./src/objects/database.lzz"
#line 210 "./src/objects/database.lzz"
static void JS_pragma (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 206 "./src/objects/database.lzz"
#line 214 "./src/objects/database.lzz"
static void JS_checkpoint (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 249 "./src/objects/database.lzz"
#line 257 "./src/objects/database.lzz"
static void JS_backup (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 258 "./src/objects/database.lzz"
#line 266 "./src/objects/database.lzz"
static void JS_function (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 280 "./src/objects/database.lzz"
#line 288 "./src/objects/database.lzz"
static void JS_aggregate (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 307 "./src/objects/database.lzz"
#line 315 "./src/objects/database.lzz"
static void JS_loadExtension (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 321 "./src/objects/database.lzz"
#line 329 "./src/objects/database.lzz"
static void JS_close (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 332 "./src/objects/database.lzz"
#line 340 "./src/objects/database.lzz"
static void JS_defaultSafeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 339 "./src/objects/database.lzz"
#line 347 "./src/objects/database.lzz"
static void JS_open (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info);
#line 343 "./src/objects/database.lzz"
#line 351 "./src/objects/database.lzz"
static void JS_inTransaction (v8::Local <v8 :: String> _, v8::PropertyCallbackInfo <v8 :: Value> const & info);
#line 348 "./src/objects/database.lzz"
#line 356 "./src/objects/database.lzz"
void CloseHandles ();
#line 360 "./src/objects/database.lzz"
#line 368 "./src/objects/database.lzz"
static void AtExit (void * _);
#line 365 "./src/objects/database.lzz"
#line 373 "./src/objects/database.lzz"
static std::set <Database*, Database::CompareDatabase> dbs;
#line 366 "./src/objects/database.lzz"
#line 374 "./src/objects/database.lzz"
static v8::Persistent <v8::Function> SqliteError;
#line 367 "./src/objects/database.lzz"
#line 375 "./src/objects/database.lzz"
static int const MAX_BUFFER_SIZE = node::Buffer::kMaxLength > INT_MAX ? INT_MAX : static_cast<int>(node::Buffer::kMaxLength);
#line 368 "./src/objects/database.lzz"
#line 376 "./src/objects/database.lzz"
static int const MAX_STRING_SIZE = v8::String::kMaxLength > INT_MAX ? INT_MAX : static_cast<int>(v8::String::kMaxLength);
#line 370 "./src/objects/database.lzz"
#line 378 "./src/objects/database.lzz"
sqlite3 * const db_handle;
#line 371 "./src/objects/database.lzz"
#line 379 "./src/objects/database.lzz"
bool open;
#line 372 "./src/objects/database.lzz"
#line 380 "./src/objects/database.lzz"
bool busy;
#line 373 "./src/objects/database.lzz"
#line 381 "./src/objects/database.lzz"
bool pragma_mode;
#line 374 "./src/objects/database.lzz"
#line 382 "./src/objects/database.lzz"
bool safe_ints;
#line 375 "./src/objects/database.lzz"
#line 383 "./src/objects/database.lzz"
bool was_js_error;
#line 376 "./src/objects/database.lzz"
#line 384 "./src/objects/database.lzz"
bool const has_logger;
#line 377 "./src/objects/database.lzz"
#line 385 "./src/objects/database.lzz"
unsigned short int iterators;
#line 378 "./src/objects/database.lzz"
#line 386 "./src/objects/database.lzz"
CopyablePersistent <v8::Value> const logger;
#line 379 "./src/objects/database.lzz"
#line 387 "./src/objects/database.lzz"
std::set <Statement*, Database::CompareStatement> stmts;
#line 380 "./src/objects/database.lzz"
#line 388 "./src/objects/database.lzz"
std::set <Backup*, Database::CompareBackup> backups;
};
#line 1 "./src/objects/statement.lzz"
@@ -356,7 +358,7 @@ class Statement : public node::ObjectWrap
#line 3 "./src/objects/statement.lzz"
public:
#line 6 "./src/objects/statement.lzz"
static v8::MaybeLocal <v8::Object> New (v8::Isolate * isolate, v8::Local <v8::Object> database, v8::Local <v8::String> source);
static v8::MaybeLocal <v8::Object> New (v8::Isolate * isolate, v8::Local <v8::Object> database, v8::Local <v8::String> source, v8::Local <v8::Boolean> unsafe);
#line 16 "./src/objects/statement.lzz"
static bool Compare (Statement const * const a, Statement const * const b);
#line 21 "./src/objects/statement.lzz"
@@ -380,58 +382,60 @@ class Statement : public node::ObjectWrap
sqlite3_uint64 const id;
};
#line 56 "./src/objects/statement.lzz"
explicit Statement (Database * _db, sqlite3_stmt * _handle, bool _returns_data);
#line 74 "./src/objects/statement.lzz"
explicit Statement (Database * _db, sqlite3_stmt * _handle, bool _returns_data, bool _unsafe);
#line 75 "./src/objects/statement.lzz"
friend void RegisterModule (v8::Local <v8 :: Object> exports, v8::Local <v8 :: Object> module);
#line 74 "./src/objects/statement.lzz"
#line 75 "./src/objects/statement.lzz"
static void Init (v8::Isolate * isolate, v8::Local <v8 :: Object> exports, v8::Local <v8 :: Object> module);
#line 97 "./src/objects/statement.lzz"
#line 98 "./src/objects/statement.lzz"
static void JS_new (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 139 "./src/objects/statement.lzz"
#line 142 "./src/objects/statement.lzz"
static void JS_run (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 157 "./src/objects/statement.lzz"
#line 160 "./src/objects/statement.lzz"
static void JS_get (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 172 "./src/objects/statement.lzz"
#line 175 "./src/objects/statement.lzz"
static void JS_all (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 193 "./src/objects/statement.lzz"
#line 196 "./src/objects/statement.lzz"
static void JS_iterate (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 198 "./src/objects/statement.lzz"
#line 201 "./src/objects/statement.lzz"
static void JS_bind (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 209 "./src/objects/statement.lzz"
#line 212 "./src/objects/statement.lzz"
static void JS_pluck (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 220 "./src/objects/statement.lzz"
#line 223 "./src/objects/statement.lzz"
static void JS_expand (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 231 "./src/objects/statement.lzz"
#line 234 "./src/objects/statement.lzz"
static void JS_raw (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 242 "./src/objects/statement.lzz"
#line 245 "./src/objects/statement.lzz"
static void JS_safeIntegers (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 251 "./src/objects/statement.lzz"
#line 254 "./src/objects/statement.lzz"
static void JS_columns (v8::FunctionCallbackInfo <v8 :: Value> const & info);
#line 292 "./src/objects/statement.lzz"
#line 295 "./src/objects/statement.lzz"
static v8::Persistent <v8::Function> constructor;
#line 293 "./src/objects/statement.lzz"
#line 296 "./src/objects/statement.lzz"
static sqlite3_uint64 next_id;
#line 294 "./src/objects/statement.lzz"
#line 297 "./src/objects/statement.lzz"
static bool constructing_privileges;
#line 296 "./src/objects/statement.lzz"
#line 299 "./src/objects/statement.lzz"
Database * const db;
#line 297 "./src/objects/statement.lzz"
#line 300 "./src/objects/statement.lzz"
sqlite3_stmt * const handle;
#line 298 "./src/objects/statement.lzz"
#line 301 "./src/objects/statement.lzz"
Extras * const extras;
#line 299 "./src/objects/statement.lzz"
#line 302 "./src/objects/statement.lzz"
bool alive;
#line 300 "./src/objects/statement.lzz"
#line 303 "./src/objects/statement.lzz"
bool locked;
#line 301 "./src/objects/statement.lzz"
#line 304 "./src/objects/statement.lzz"
bool bound;
#line 302 "./src/objects/statement.lzz"
#line 305 "./src/objects/statement.lzz"
bool has_bind_map;
#line 303 "./src/objects/statement.lzz"
#line 306 "./src/objects/statement.lzz"
bool safe_ints;
#line 304 "./src/objects/statement.lzz"
#line 307 "./src/objects/statement.lzz"
char mode;
#line 305 "./src/objects/statement.lzz"
#line 308 "./src/objects/statement.lzz"
bool const unsafe;
#line 309 "./src/objects/statement.lzz"
bool const returns_data;
};
#line 1 "./src/objects/statement-iterator.lzz"
10 changes: 9 additions & 1 deletion src/objects/database.lzz
Original file line number Diff line number Diff line change
@@ -175,7 +175,15 @@ private:

NODE_METHOD(JS_prepare) {
REQUIRE_ARGUMENT_STRING(first, v8::Local<v8::String> source);
v8::MaybeLocal<v8::Object> maybe_statement = Statement::New(OnlyIsolate, info.This(), source);

// optionally enable unsafe mode
bool unsafe = false;
if (info.Length() > 1) {
REQUIRE_ARGUMENT_BOOLEAN(second, unsafe);
}

UseIsolate;
v8::MaybeLocal<v8::Object> maybe_statement = Statement::New(isolate, info.This(), source, unsafe ? v8::True(isolate) : v8::False(isolate));
if (!maybe_statement.IsEmpty()) info.GetReturnValue().Set(maybe_statement.ToLocalChecked());
}

14 changes: 9 additions & 5 deletions src/objects/statement.lzz
Original file line number Diff line number Diff line change
@@ -3,11 +3,11 @@ friend class StatementIterator;
public:

// Provides public access to the constructor.
static v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate, v8::Local<v8::Object> database, v8::Local<v8::String> source) {
static v8::MaybeLocal<v8::Object> New(v8::Isolate* isolate, v8::Local<v8::Object> database, v8::Local<v8::String> source, v8::Local<v8::Boolean> unsafe) {
v8::Local<v8::Function> c = v8::Local<v8::Function>::New(isolate, constructor);
v8::Local<v8::Value> args[2] = { database, source };
v8::Local<v8::Value> args[3] = { database, source, unsafe };
constructing_privileges = true;
v8::MaybeLocal<v8::Object> maybe_statement = c->NewInstance(OnlyContext, 2, args);
v8::MaybeLocal<v8::Object> maybe_statement = c->NewInstance(OnlyContext, 3, args);
constructing_privileges = false;
return maybe_statement;
}
@@ -53,7 +53,7 @@ private:
const sqlite3_uint64 id;
};

explicit Statement(Database* _db, sqlite3_stmt* _handle, bool _returns_data) : node::ObjectWrap(),
explicit Statement(Database* _db, sqlite3_stmt* _handle, bool _returns_data, bool _unsafe) : node::ObjectWrap(),
db(_db),
handle(_handle),
extras(new Extras(next_id++)),
@@ -63,6 +63,7 @@ private:
has_bind_map(false),
safe_ints(_db->GetState()->safe_ints),
mode(Data::FLAT),
unsafe(_unsafe),
returns_data(_returns_data) {
assert(db != NULL);
assert(handle != NULL);
@@ -101,6 +102,7 @@ private:
assert(info.IsConstructCall());
REQUIRE_ARGUMENT_OBJECT(first, v8::Local<v8::Object> database);
REQUIRE_ARGUMENT_STRING(second, v8::Local<v8::String> source);
REQUIRE_ARGUMENT_BOOLEAN(third, bool unsafe);
Database* db = Unwrap<Database>(database);
REQUIRE_DATABASE_OPEN(db->GetState());
REQUIRE_DATABASE_NOT_BUSY(db->GetState());
@@ -127,9 +129,10 @@ private:
bool returns_data = (sqlite3_stmt_readonly(handle) && sqlite3_column_count(handle) >= 1) || db->GetState()->pragma_mode;

UseContext;
Statement* stmt = new Statement(db, handle, returns_data);
Statement* stmt = new Statement(db, handle, returns_data, unsafe);
stmt->Wrap(info.This());
SetFrozen(isolate, ctx, info.This(), CS::reader, v8::Boolean::New(isolate, returns_data));
SetFrozen(isolate, ctx, info.This(), CS::unsafe, v8::Boolean::New(isolate, unsafe));
SetFrozen(isolate, ctx, info.This(), CS::source, source);
SetFrozen(isolate, ctx, info.This(), CS::database, database);

@@ -302,5 +305,6 @@ private:
bool has_bind_map;
bool safe_ints;
char mode;
const bool unsafe;
const bool returns_data;
};
2 changes: 2 additions & 0 deletions src/util/constants.lzz
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ public:
static ConstantString type;
static ConstantString totalPages;
static ConstantString remainingPages;
static ConstantString unsafe;

private:
REGISTER(Init) {
@@ -50,6 +51,7 @@ private:
AddString(isolate, CS::type, "type");
AddString(isolate, CS::totalPages, "totalPages");
AddString(isolate, CS::remainingPages, "remainingPages");
AddString(isolate, CS::unsafe, "unsafe");

AddCode(isolate, SQLITE_OK, "SQLITE_OK");
AddCode(isolate, SQLITE_ERROR, "SQLITE_ERROR");
2 changes: 1 addition & 1 deletion src/util/query-macros.lzz
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
Database* db = stmt->db; \
REQUIRE_DATABASE_OPEN(db->GetState()); \
REQUIRE_DATABASE_NOT_BUSY(db->GetState()); \
MUTATE_CHECK(); \
if (!stmt->unsafe) MUTATE_CHECK(); \
const bool bound = stmt->bound; \
if (!bound) { \
STATEMENT_BIND(handle); \
60 changes: 60 additions & 0 deletions test/44.unsafe.mode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use strict'
const Database = require('../.');

// writing to a table while reading the same table within an iterator.
function writeWhileIterating(db, unsafe) {
// create table & prepared statements
db.prepare(`CREATE TABLE test (a TEXT)`).run();
const stmt = {
insert: db.prepare(`INSERT INTO test VALUES (?)`, unsafe),
each: db.prepare(`SELECT * FROM test WHERE a == 'FOO'`, unsafe)
};
if (unsafe === true){
expect(stmt.insert.unsafe).to.be.true;
expect(stmt.each.unsafe).to.be.true;
} else {
expect(stmt.insert.unsafe).to.be.false;
expect(stmt.each.unsafe).to.be.false;
}


// populate table with two FOO values
stmt.insert.run('FOO');
stmt.insert.run('FOO');

// iterate and insert from/to the same table at the same time
// note: we insert 'BAR' which is not matched by the iterator.
// warning: this is unsafe, if you insert 'FOO' here then the
// database connection will hang forever, you were warned!
for (let _ of stmt.each.iterate()) {
stmt.insert.run('BAR');
}

return stmt;
};

describe('unsafe mode', () => {
afterEach(() => {
if (this.db) this.db.close();
});

it('disabled: writeWhileIterating', () => {
this.db = new Database(util.next());
expect(() => {
writeWhileIterating(this.db);
}).to.throw(TypeError);
});
it('enabled: writeWhileIterating', () => {
this.db = new Database(util.next());
expect(() => {
const stmt = writeWhileIterating(this.db, true);
expect(stmt.insert.unsafe).to.be.true;
expect(stmt.each.unsafe).to.be.true;
}).not.throw();
const aggs = this.db.prepare(`SELECT a, COUNT(a) AS total FROM test GROUP BY a`).all();
expect(aggs).to.eql([
{ a: 'BAR', total: 2 }, // two 'BAR' rows
{ a: 'FOO', total: 2 } // two 'FOO' rows
]);
});
});