Skip to content

Commit f146b66

Browse files
committedMar 27, 2020
WIP: withUndefinedBehavior block
1 parent 66fcb86 commit f146b66

File tree

6 files changed

+191
-133
lines changed

6 files changed

+191
-133
lines changed
 

‎docs/api.md

+13
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [Database#aggregate()](#aggregatename-options---this)
1616
- [Database#loadExtension()](#loadextensionpath---this)
1717
- [Database#exec()](#execstring---this)
18+
- [Database#withUndefinedBehavior()](#withUndefinedBehaviorfunction---this)
1819
- [Database#close()](#close---this)
1920
- [Properties](#properties)
2021

@@ -263,6 +264,18 @@ const migration = fs.readFileSync('migrate-schema.sql', 'utf8');
263264
db.exec(migration);
264265
```
265266

267+
### .withUndefinedBehavior(*function*) -> *this*
268+
269+
Immediately synchronously executes the given function. Inside the function operations with potentially undefined behavior (as defined by the SQLite documentation) are allowed. This means certain sanity checks that better-sqlite3 performs are disabled. For example doing writes inside an iterator or executing statements inside the implementation of a user-defined function.
270+
271+
Only use `withUndefinedBehavior` if you absolutely know what you are doing. Use it as low as possible in your call chain to only cover the absolute minimum number of operations you need to do.
272+
273+
```js
274+
db.withUndefinedBehavior(() => {
275+
// TODO: docs
276+
});
277+
```
278+
266279
### .close() -> *this*
267280

268281
Closes the database connection. After invoking this method, no statements can be created or executed.

‎src/objects/database.lzz

+18-2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public:
5555
const bool safe_ints;
5656
bool was_js_error;
5757
unsigned short iterators;
58+
unsigned short undefinedBehaviorBlocks;
5859
};
5960

6061
inline State* GetState() {
@@ -98,6 +99,7 @@ private:
9899
was_js_error(false),
99100
has_logger(_logger->IsFunction()),
100101
iterators(0),
102+
undefinedBehaviorBlocks(0),
101103
logger(isolate, _logger),
102104
stmts(),
103105
backups() {
@@ -118,6 +120,7 @@ private:
118120
NODE_SET_PROTOTYPE_METHOD(t, "function", JS_function);
119121
NODE_SET_PROTOTYPE_METHOD(t, "aggregate", JS_aggregate);
120122
NODE_SET_PROTOTYPE_METHOD(t, "loadExtension", JS_loadExtension);
123+
NODE_SET_PROTOTYPE_METHOD(t, "withUndefinedBehavior", JS_withUndefinedBehavior);
121124
NODE_SET_PROTOTYPE_METHOD(t, "close", JS_close);
122125
NODE_SET_PROTOTYPE_METHOD(t, "defaultSafeIntegers", JS_defaultSafeIntegers);
123126
NODE_SET_PROTOTYPE_GETTER(t, "open", JS_open);
@@ -183,8 +186,8 @@ private:
183186
Database* db = Unwrap<Database>(info.This());
184187
REQUIRE_ARGUMENT_STRING(first, v8::Local<v8::String> source);
185188
REQUIRE_DATABASE_OPEN(db);
186-
REQUIRE_DATABASE_NOT_BUSY(db);
187-
REQUIRE_DATABASE_NO_ITERATORS(db);
189+
PREFER_DATABASE_NOT_BUSY(db);
190+
PREFER_DATABASE_NO_ITERATORS(db);
188191
db->busy = true;
189192
UseIsolate;
190193
if (db->Log(isolate, source)) {
@@ -318,6 +321,18 @@ private:
318321
sqlite3_free(error);
319322
}
320323

324+
NODE_METHOD(JS_withUndefinedBehavior) {
325+
Database* db = Unwrap<Database>(info.This());
326+
REQUIRE_ARGUMENT_FUNCTION(first, v8::Local<v8::Function> fn);
327+
assert(db->undefinedBehaviorBlocks < USHRT_MAX);
328+
db->undefinedBehaviorBlocks += 1;
329+
UseIsolate;
330+
v8::Local<v8::Function>::Cast(v8::Local<v8::Value>::New(isolate, fn))
331+
->Call(OnlyContext, v8::Undefined(isolate), 0, NULL);
332+
assert(db->undefinedBehaviorBlocks > 0);
333+
db->undefinedBehaviorBlocks -= 1;
334+
}
335+
321336
NODE_METHOD(JS_close) {
322337
Database* db = Unwrap<Database>(info.This());
323338
if (db->open) {
@@ -375,6 +390,7 @@ private:
375390
bool was_js_error;
376391
const bool has_logger;
377392
unsigned short iterators;
393+
unsigned short undefinedBehaviorBlocks;
378394
const CopyablePersistent<v8::Value> logger;
379395
std::set<Statement*, Database::CompareStatement> stmts;
380396
std::set<Backup*, Database::CompareBackup> backups;

‎src/objects/statement-iterator.lzz

+2-2
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,14 @@ private:
6868

6969
NODE_METHOD(JS_next) {
7070
StatementIterator* iter = Unwrap<StatementIterator>(info.This());
71-
REQUIRE_DATABASE_NOT_BUSY(iter->db_state);
71+
PREFER_DATABASE_NOT_BUSY(iter->db_state);
7272
if (iter->alive) iter->Next(info);
7373
else info.GetReturnValue().Set(DoneRecord(OnlyIsolate));
7474
}
7575

7676
NODE_METHOD(JS_return) {
7777
StatementIterator* iter = Unwrap<StatementIterator>(info.This());
78-
REQUIRE_DATABASE_NOT_BUSY(iter->db_state);
78+
PREFER_DATABASE_NOT_BUSY(iter->db_state);
7979
if (iter->alive) iter->Return(info);
8080
else info.GetReturnValue().Set(DoneRecord(OnlyIsolate));
8181
}

‎src/util/macros.lzz

+6
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,15 @@ void ThrowRangeError(const char* message) { EasyIsolate; isolate->ThrowException
6363
#define REQUIRE_DATABASE_NOT_BUSY(db) \
6464
if (db->busy) \
6565
return ThrowTypeError("This database connection is busy executing a query")
66+
#define PREFER_DATABASE_NOT_BUSY(db) \
67+
if (db->busy && !db->undefinedBehaviorBlocks) \
68+
return ThrowTypeError("This database connection is busy executing a query")
6669
#define REQUIRE_DATABASE_NO_ITERATORS(db) \
6770
if (db->iterators) \
6871
return ThrowTypeError("This database connection is busy executing a query")
72+
#define PREFER_DATABASE_NO_ITERATORS(db) \
73+
if (db->iterators && !db->undefinedBehaviorBlocks) \
74+
return ThrowTypeError("This database connection is busy executing a query")
6975
#define REQUIRE_STATEMENT_NOT_LOCKED(stmt) \
7076
if (stmt->locked) \
7177
return ThrowTypeError("This statement is busy executing a query")

‎src/util/query-macros.lzz

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
sqlite3_stmt* handle = stmt->handle; \
2222
Database* db = stmt->db; \
2323
REQUIRE_DATABASE_OPEN(db->GetState()); \
24-
REQUIRE_DATABASE_NOT_BUSY(db->GetState()); \
24+
PREFER_DATABASE_NOT_BUSY(db->GetState()); \
2525
MUTATE_CHECK(); \
2626
const bool bound = stmt->bound; \
2727
if (!bound) { \

0 commit comments

Comments
 (0)
Please sign in to comment.