| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent f703652 commit 67f5f46
4 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -107,6 +107,11 @@ added: v22.5.0 | |||
| 107 | 107 | * `open` {boolean} If `true`, the database is opened by the constructor. When | |
| 108 | 108 | this value is `false`, the database must be opened via the `open()` method. | |
| 109 | 109 | **Default:** `true`. | |
| 110 | + * `enableForeignKeyConstraints` {boolean} If `true`, foreign key constraints | ||
| 111 | + are enabled. This is recommended but can be disabled for compatibility with | ||
| 112 | + legacy database schemas. The enforcement of foreign key constraints can be | ||
| 113 | + enabled and disabled after opening the database using | ||
| 114 | + [`PRAGMA foreign_keys`][]. **Default:** `true`. | ||
| 110 | 115 | ||
| 111 | 116 | Constructs a new `DatabaseSync` instance. | |
| 112 | 117 | ||
@@ -317,6 +322,7 @@ exception. | |||
| 317 | 322 | ||
| 318 | 323 | [SQL injection]: https://en.wikipedia.org/wiki/SQL_injection | |
| 319 | 324 | [`--experimental-sqlite`]: cli.md#--experimental-sqlite | |
| 325 | + [`PRAGMA foreign_keys`]: https://www.sqlite.org/pragma.html#pragma_foreign_keys | ||
| 320 | 326 | [`sqlite3_changes64()`]: https://www.sqlite.org/c3ref/changes.html | |
| 321 | 327 | [`sqlite3_close_v2()`]: https://www.sqlite.org/c3ref/close.html | |
| 322 | 328 | [`sqlite3_exec()`]: https://www.sqlite.org/c3ref/exec.html | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -91,12 +91,14 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, const char* message) { | |||
| 91 | 91 | DatabaseSync::DatabaseSync(Environment* env, | |
| 92 | 92 | Local<Object> object, | |
| 93 | 93 | Local<String> location, | |
| 94 | - bool open) | ||
| 94 | + bool open, | ||
| 95 | + bool enable_foreign_keys_on_open) | ||
| 95 | 96 | : BaseObject(env, object) { | |
| 96 | 97 | MakeWeak(); | |
| 97 | 98 | node::Utf8Value utf8_location(env->isolate(), location); | |
| 98 | 99 | location_ = utf8_location.ToString(); | |
| 99 | 100 | connection_ = nullptr; | |
| 101 | + enable_foreign_keys_on_open_ = enable_foreign_keys_on_open; | ||
| 100 | 102 | ||
| 101 | 103 | if (open) { | |
| 102 | 104 | Open(); | |
@@ -125,6 +127,15 @@ bool DatabaseSync::Open() { | |||
| 125 | 127 | int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; | |
| 126 | 128 | int r = sqlite3_open_v2(location_.c_str(), &connection_, flags, nullptr); | |
| 127 | 129 | CHECK_ERROR_OR_THROW(env()->isolate(), connection_, r, SQLITE_OK, false); | |
| 130 | + | ||
| 131 | + int foreign_keys_enabled; | ||
| 132 | + r = sqlite3_db_config(connection_, | ||
| 133 | + SQLITE_DBCONFIG_ENABLE_FKEY, | ||
| 134 | + static_cast<int>(enable_foreign_keys_on_open_), | ||
| 135 | + &foreign_keys_enabled); | ||
| 136 | + CHECK_ERROR_OR_THROW(env()->isolate(), connection_, r, SQLITE_OK, false); | ||
| 137 | + CHECK_EQ(foreign_keys_enabled, enable_foreign_keys_on_open_); | ||
| 138 | + | ||
| 128 | 139 | return true; | |
| 129 | 140 | } | |
| 130 | 141 | ||
@@ -166,6 +177,7 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) { | |||
| 166 | 177 | } | |
| 167 | 178 | ||
| 168 | 179 | bool open = true; | |
| 180 | + bool enable_foreign_keys = true; | ||
| 169 | 181 | ||
| 170 | 182 | if (args.Length() > 1) { | |
| 171 | 183 | if (!args[1]->IsObject()) { | |
@@ -188,9 +200,28 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) { | |||
| 188 | 200 | } | |
| 189 | 201 | open = open_v.As<Boolean>()->Value(); | |
| 190 | 202 | } | |
| 203 | + | ||
| 204 | + Local<String> enable_foreign_keys_string = | ||
| 205 | + FIXED_ONE_BYTE_STRING(env->isolate(), "enableForeignKeyConstraints"); | ||
| 206 | + Local<Value> enable_foreign_keys_v; | ||
| 207 | + if (!options->Get(env->context(), enable_foreign_keys_string) | ||
| 208 | + .ToLocal(&enable_foreign_keys_v)) { | ||
| 209 | + return; | ||
| 210 | + } | ||
| 211 | + if (!enable_foreign_keys_v->IsUndefined()) { | ||
| 212 | + if (!enable_foreign_keys_v->IsBoolean()) { | ||
| 213 | + node::THROW_ERR_INVALID_ARG_TYPE( | ||
| 214 | + env->isolate(), | ||
| 215 | + "The \"options.enableForeignKeyConstraints\" argument must be a " | ||
| 216 | + "boolean."); | ||
| 217 | + return; | ||
| 218 | + } | ||
| 219 | + enable_foreign_keys = enable_foreign_keys_v.As<Boolean>()->Value(); | ||
| 220 | + } | ||
| 191 | 221 | } | |
| 192 | 222 | ||
| 193 | - new DatabaseSync(env, args.This(), args[0].As<String>(), open); | ||
| 223 | + new DatabaseSync( | ||
| 224 | + env, args.This(), args[0].As<String>(), open, enable_foreign_keys); | ||
| 194 | 225 | } | |
| 195 | 226 | ||
| 196 | 227 | void DatabaseSync::Open(const FunctionCallbackInfo<Value>& args) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,8 @@ class DatabaseSync : public BaseObject { | |||
| 21 | 21 | DatabaseSync(Environment* env, | |
| 22 | 22 | v8::Local<v8::Object> object, | |
| 23 | 23 | v8::Local<v8::String> location, | |
| 24 | - bool open); | ||
| 24 | + bool open, | ||
| 25 | + bool enable_foreign_keys_on_open); | ||
| 25 | 26 | void MemoryInfo(MemoryTracker* tracker) const override; | |
| 26 | 27 | static void New(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 27 | 28 | static void Open(const v8::FunctionCallbackInfo<v8::Value>& args); | |
@@ -43,6 +44,7 @@ class DatabaseSync : public BaseObject { | |||
| 43 | 44 | std::string location_; | |
| 44 | 45 | sqlite3* connection_; | |
| 45 | 46 | std::unordered_set<StatementSync*> statements_; | |
| 47 | + bool enable_foreign_keys_on_open_; | ||
| 46 | 48 | }; | |
| 47 | 49 | ||
| 48 | 50 | class StatementSync : public BaseObject { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -50,6 +50,42 @@ suite('DatabaseSync() constructor', () => { | |||
| 50 | 50 | message: /The "options\.open" argument must be a boolean/, | |
| 51 | 51 | }); | |
| 52 | 52 | }); | |
| 53 | + | ||
| 54 | + test('throws if options.enableForeignKeyConstraints is provided but is not a boolean', (t) => { | ||
| 55 | + t.assert.throws(() => { | ||
| 56 | + new DatabaseSync('foo', { enableForeignKeyConstraints: 5 }); | ||
| 57 | + }, { | ||
| 58 | + code: 'ERR_INVALID_ARG_TYPE', | ||
| 59 | + message: /The "options\.enableForeignKeyConstraints" argument must be a boolean/, | ||
| 60 | + }); | ||
| 61 | + }); | ||
| 62 | + | ||
| 63 | + test('enables foreign key constraints by default', (t) => { | ||
| 64 | + const dbPath = nextDb(); | ||
| 65 | + const db = new DatabaseSync(dbPath); | ||
| 66 | + db.exec(` | ||
| 67 | + CREATE TABLE foo (id INTEGER PRIMARY KEY); | ||
| 68 | + CREATE TABLE bar (foo_id INTEGER REFERENCES foo(id)); | ||
| 69 | + `); | ||
| 70 | + t.after(() => { db.close(); }); | ||
| 71 | + t.assert.throws(() => { | ||
| 72 | + db.exec('INSERT INTO bar (foo_id) VALUES (1)'); | ||
| 73 | + }, { | ||
| 74 | + code: 'ERR_SQLITE_ERROR', | ||
| 75 | + message: 'FOREIGN KEY constraint failed', | ||
| 76 | + }); | ||
| 77 | + }); | ||
| 78 | + | ||
| 79 | + test('allows disabling foreign key constraints', (t) => { | ||
| 80 | + const dbPath = nextDb(); | ||
| 81 | + const db = new DatabaseSync(dbPath, { enableForeignKeyConstraints: false }); | ||
| 82 | + db.exec(` | ||
| 83 | + CREATE TABLE foo (id INTEGER PRIMARY KEY); | ||
| 84 | + CREATE TABLE bar (foo_id INTEGER REFERENCES foo(id)); | ||
| 85 | + `); | ||
| 86 | + t.after(() => { db.close(); }); | ||
| 87 | + db.exec('INSERT INTO bar (foo_id) VALUES (1)'); | ||
| 88 | + }); | ||
| 53 | 89 | }); | |
| 54 | 90 | ||
| 55 | 91 | suite('DatabaseSync.prototype.open()', () => { | |
| Back | FazBrowse Home | New Git URL |
0 commit comments