pi-para 0.7.7 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/node_modules/better-sqlite3/LICENSE +21 -0
  3. package/node_modules/better-sqlite3/README.md +99 -0
  4. package/node_modules/better-sqlite3/binding.gyp +60 -0
  5. package/node_modules/better-sqlite3/deps/common.gypi +68 -0
  6. package/node_modules/better-sqlite3/deps/copy.js +31 -0
  7. package/node_modules/better-sqlite3/deps/defines.gypi +42 -0
  8. package/node_modules/better-sqlite3/deps/download.sh +123 -0
  9. package/node_modules/better-sqlite3/deps/patches/1208.patch +15 -0
  10. package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +269668 -0
  11. package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +14349 -0
  12. package/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +739 -0
  13. package/node_modules/better-sqlite3/deps/sqlite3.gyp +80 -0
  14. package/node_modules/better-sqlite3/deps/test_extension.c +21 -0
  15. package/node_modules/better-sqlite3/lib/binding.js +62 -0
  16. package/node_modules/better-sqlite3/lib/darwin-arm64.js +3 -0
  17. package/node_modules/better-sqlite3/lib/darwin-x64.js +3 -0
  18. package/node_modules/better-sqlite3/lib/database.js +109 -0
  19. package/node_modules/better-sqlite3/lib/index.js +3 -0
  20. package/node_modules/better-sqlite3/lib/linux-arm64.js +3 -0
  21. package/node_modules/better-sqlite3/lib/linux-x64.js +3 -0
  22. package/node_modules/better-sqlite3/lib/linuxmusl-arm64.js +4 -0
  23. package/node_modules/better-sqlite3/lib/linuxmusl-x64.js +4 -0
  24. package/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
  25. package/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
  26. package/node_modules/better-sqlite3/lib/methods/explain.js +9 -0
  27. package/node_modules/better-sqlite3/lib/methods/function.js +31 -0
  28. package/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
  29. package/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
  30. package/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
  31. package/node_modules/better-sqlite3/lib/methods/table.js +195 -0
  32. package/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
  33. package/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
  34. package/node_modules/better-sqlite3/lib/sqlite-error.js +25 -0
  35. package/node_modules/better-sqlite3/lib/util.js +12 -0
  36. package/node_modules/better-sqlite3/lib/win32-arm64.js +3 -0
  37. package/node_modules/better-sqlite3/lib/win32-x64.js +3 -0
  38. package/node_modules/better-sqlite3/package.json +67 -0
  39. package/node_modules/better-sqlite3/prebuilds/darwin-arm64.node +0 -0
  40. package/node_modules/better-sqlite3/prebuilds/darwin-x64.node +0 -0
  41. package/node_modules/better-sqlite3/prebuilds/linux-arm64.node +0 -0
  42. package/node_modules/better-sqlite3/prebuilds/linux-x64.node +0 -0
  43. package/node_modules/better-sqlite3/prebuilds/linuxmusl-arm64.node +0 -0
  44. package/node_modules/better-sqlite3/prebuilds/linuxmusl-x64.node +0 -0
  45. package/node_modules/better-sqlite3/prebuilds/win32-arm64.node +0 -0
  46. package/node_modules/better-sqlite3/prebuilds/win32-x64.node +0 -0
  47. package/node_modules/better-sqlite3/src/addon.cpp +59 -0
  48. package/node_modules/better-sqlite3/src/better_sqlite3.cpp +73 -0
  49. package/node_modules/better-sqlite3/src/objects/backup.cpp +121 -0
  50. package/node_modules/better-sqlite3/src/objects/backup.hpp +32 -0
  51. package/node_modules/better-sqlite3/src/objects/database.cpp +432 -0
  52. package/node_modules/better-sqlite3/src/objects/database.hpp +100 -0
  53. package/node_modules/better-sqlite3/src/objects/statement-iterator.cpp +119 -0
  54. package/node_modules/better-sqlite3/src/objects/statement-iterator.hpp +71 -0
  55. package/node_modules/better-sqlite3/src/objects/statement.cpp +380 -0
  56. package/node_modules/better-sqlite3/src/objects/statement.hpp +65 -0
  57. package/node_modules/better-sqlite3/src/util/bind-map.cpp +73 -0
  58. package/node_modules/better-sqlite3/src/util/binder.cpp +210 -0
  59. package/node_modules/better-sqlite3/src/util/constants.cpp +172 -0
  60. package/node_modules/better-sqlite3/src/util/custom-aggregate.cpp +121 -0
  61. package/node_modules/better-sqlite3/src/util/custom-function.cpp +59 -0
  62. package/node_modules/better-sqlite3/src/util/custom-table.cpp +406 -0
  63. package/node_modules/better-sqlite3/src/util/data-converter.cpp +17 -0
  64. package/node_modules/better-sqlite3/src/util/data.cpp +134 -0
  65. package/node_modules/better-sqlite3/src/util/helpers.cpp +190 -0
  66. package/node_modules/better-sqlite3/src/util/macros.cpp +74 -0
  67. package/node_modules/better-sqlite3/src/util/query-macros.cpp +71 -0
  68. package/node_modules/better-sqlite3/src/util/row-builder.cpp +53 -0
  69. package/node_modules/better-sqlite3/src/util/row-builder.hpp +22 -0
  70. package/node_modules/node-addon-api/LICENSE.md +9 -0
  71. package/node_modules/node-addon-api/README.md +95 -0
  72. package/node_modules/node-addon-api/common.gypi +21 -0
  73. package/node_modules/node-addon-api/except.gypi +25 -0
  74. package/node_modules/node-addon-api/index.js +14 -0
  75. package/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  76. package/node_modules/node-addon-api/napi-inl.h +7165 -0
  77. package/node_modules/node-addon-api/napi.h +3364 -0
  78. package/node_modules/node-addon-api/node_addon_api.gyp +42 -0
  79. package/node_modules/node-addon-api/node_api.gyp +9 -0
  80. package/node_modules/node-addon-api/noexcept.gypi +26 -0
  81. package/node_modules/node-addon-api/nothing.c +0 -0
  82. package/node_modules/node-addon-api/package-support.json +21 -0
  83. package/node_modules/node-addon-api/package.json +480 -0
  84. package/node_modules/node-addon-api/tools/README.md +73 -0
  85. package/node_modules/node-addon-api/tools/check-napi.js +99 -0
  86. package/node_modules/node-addon-api/tools/clang-format.js +71 -0
  87. package/node_modules/node-addon-api/tools/conversion.js +301 -0
  88. package/package.json +6 -3
@@ -0,0 +1,121 @@
1
+ const napi_type_tag Backup::TYPE_TAG = RandomTypeTag();
2
+
3
+ Backup::Backup(const Napi::CallbackInfo& info) :
4
+ Napi::ObjectWrap<Backup>(info),
5
+ db(NULL),
6
+ dest_handle(NULL),
7
+ backup_handle(NULL),
8
+ id(0),
9
+ alive(false),
10
+ unlink(false) {
11
+ TYPE_TAG_CONSTRUCTOR(info);
12
+ JS_new(info);
13
+ }
14
+
15
+ Backup::~Backup() {
16
+ if (alive) db->RemoveBackup(this);
17
+ CloseHandles();
18
+ }
19
+
20
+ // Whenever this is used, db->RemoveBackup must be invoked beforehand.
21
+ void Backup::CloseHandles() {
22
+ if (alive) {
23
+ alive = false;
24
+ std::string filename(sqlite3_db_filename(dest_handle, "main"));
25
+ sqlite3_backup_finish(backup_handle);
26
+ int status = sqlite3_close(dest_handle);
27
+ assert(status == SQLITE_OK); ((void)status);
28
+ if (unlink) remove(filename.c_str());
29
+ }
30
+ }
31
+
32
+ INIT(Backup::Init) {
33
+ return DefineClass(env, "Backup", {
34
+ PrototypeMethod<Backup, &Backup::JS_transfer>("transfer", addon),
35
+ PrototypeMethod<Backup, &Backup::JS_close>("close", addon),
36
+ }, addon);
37
+ }
38
+
39
+ NODE_METHOD(Backup::JS_new) {
40
+ UseAddon;
41
+ if (!addon->privileged_info) return ThrowTypeError(info.Env(), "Disabled constructor");
42
+ assert(info.IsConstructCall());
43
+ const Napi::CallbackInfo& pinfo = *addon->privileged_info;
44
+ UNWRAP_OR_RETURN(Database, db, pinfo.This());
45
+ REQUIRE_DATABASE_OPEN(db->GetState());
46
+ REQUIRE_DATABASE_NOT_BUSY(db->GetState());
47
+
48
+ Napi::Object database = pinfo[0].As<Napi::Object>();
49
+ Napi::String attachedName = pinfo[1].As<Napi::String>();
50
+ Napi::String destFile = pinfo[2].As<Napi::String>();
51
+ bool unlink = pinfo[3].As<Napi::Boolean>().Value();
52
+
53
+ UseIsolate;
54
+ sqlite3* dest_handle;
55
+ std::string dest_file = destFile.Utf8Value();
56
+ std::string attached_name = attachedName.Utf8Value();
57
+ int mask = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
58
+
59
+ if (sqlite3_open_v2(dest_file.c_str(), &dest_handle, mask, NULL) != SQLITE_OK) {
60
+ Database::ThrowSqliteError(env, addon, dest_handle);
61
+ int status = sqlite3_close(dest_handle);
62
+ assert(status == SQLITE_OK); ((void)status);
63
+ return env.Undefined();
64
+ }
65
+
66
+ sqlite3_extended_result_codes(dest_handle, 1);
67
+ sqlite3_limit(dest_handle, SQLITE_LIMIT_LENGTH, INT_MAX);
68
+ sqlite3_backup* backup_handle = sqlite3_backup_init(dest_handle, "main", db->GetHandle(), attached_name.c_str());
69
+ if (backup_handle == NULL) {
70
+ Database::ThrowSqliteError(env, addon, dest_handle);
71
+ int status = sqlite3_close(dest_handle);
72
+ assert(status == SQLITE_OK); ((void)status);
73
+ return env.Undefined();
74
+ }
75
+
76
+ this->db = db;
77
+ this->dest_handle = dest_handle;
78
+ this->backup_handle = backup_handle;
79
+ this->id = addon->NextId();
80
+ this->unlink = unlink;
81
+ this->alive = true;
82
+ db->AddBackup(this);
83
+
84
+ SetFrozen(env, info.This().As<Napi::Object>(), addon->cs.database, database);
85
+
86
+ return info.This();
87
+ }
88
+
89
+ NODE_METHOD(Backup::JS_transfer) {
90
+ UNWRAP_OR_RETURN(Backup, backup, info.This());
91
+ REQUIRE_ARGUMENT_INT32(first, int pages);
92
+ REQUIRE_DATABASE_OPEN(backup->db->GetState());
93
+ assert(backup->db->GetState()->busy == false);
94
+ assert(backup->alive == true);
95
+
96
+ UseIsolate;
97
+ sqlite3_backup* backup_handle = backup->backup_handle;
98
+ int status = sqlite3_backup_step(backup_handle, pages) & 0xff;
99
+
100
+ Addon* addon = backup->db->GetAddon();
101
+ if (status == SQLITE_OK || status == SQLITE_DONE || status == SQLITE_BUSY) {
102
+ int total_pages = sqlite3_backup_pagecount(backup_handle);
103
+ int remaining_pages = sqlite3_backup_remaining(backup_handle);
104
+ Napi::Object result = Napi::Object::New(env);
105
+ result.Set(addon->cs.totalPages.Value(), Napi::Number::New(env, total_pages));
106
+ result.Set(addon->cs.remainingPages.Value(), Napi::Number::New(env, remaining_pages));
107
+ if (status == SQLITE_DONE) backup->unlink = false;
108
+ return result;
109
+ } else {
110
+ Database::ThrowSqliteError(env, addon, sqlite3_errstr(status), status);
111
+ return env.Undefined();
112
+ }
113
+ }
114
+
115
+ NODE_METHOD(Backup::JS_close) {
116
+ UNWRAP_OR_RETURN(Backup, backup, info.This());
117
+ assert(backup->db->GetState()->busy == false);
118
+ if (backup->alive) backup->db->RemoveBackup(backup);
119
+ backup->CloseHandles();
120
+ return info.This();
121
+ }
@@ -0,0 +1,32 @@
1
+ class Backup : public Napi::ObjectWrap<Backup> {
2
+ public:
3
+
4
+ explicit Backup(const Napi::CallbackInfo& info);
5
+ ~Backup();
6
+
7
+ // Whenever this is used, db->RemoveBackup must be invoked beforehand.
8
+ void CloseHandles();
9
+
10
+ // Used to support ordered containers.
11
+ static inline bool Compare(Backup const * const a, Backup const * const b) {
12
+ return a->id < b->id;
13
+ }
14
+
15
+ // Identifies objects that are backed by this class (see IsInstanceOf).
16
+ static const napi_type_tag TYPE_TAG;
17
+
18
+ static INIT(Init);
19
+
20
+ private:
21
+
22
+ NODE_METHOD(JS_new);
23
+ static NODE_METHOD(JS_transfer);
24
+ static NODE_METHOD(JS_close);
25
+
26
+ Database* db;
27
+ sqlite3* dest_handle;
28
+ sqlite3_backup* backup_handle;
29
+ sqlite3_uint64 id;
30
+ bool alive;
31
+ bool unlink;
32
+ };
@@ -0,0 +1,432 @@
1
+ // Node-API does not expose the engine's maximum Buffer/String sizes, so we
2
+ // hardcode V8's known limits: Buffers are larger than what sqlite3_limit can
3
+ // express (INT_MAX), and strings are limited to v8::String::kMaxLength, which
4
+ // is (1 << 29) - 24 bytes on 64-bit platforms (as of Node.js 22). Keeping
5
+ // these limits ensures that oversized values fail cleanly inside SQLite with
6
+ // SQLITE_TOOBIG, instead of failing during data conversion.
7
+ const int Database::MAX_BUFFER_SIZE = INT_MAX;
8
+ const int Database::MAX_STRING_SIZE = (1 << 29) - 24;
9
+
10
+ const napi_type_tag Database::TYPE_TAG = RandomTypeTag();
11
+
12
+ Database::Database(const Napi::CallbackInfo& info) :
13
+ Napi::ObjectWrap<Database>(info),
14
+ db_handle(NULL),
15
+ open(false),
16
+ busy(false),
17
+ safe_ints(false),
18
+ unsafe_mode(false),
19
+ was_js_error(false),
20
+ has_logger(false),
21
+ iterators(0),
22
+ addon(static_cast<Addon*>(info.Data())),
23
+ logger(),
24
+ stmts(),
25
+ backups() {
26
+ TYPE_TAG_CONSTRUCTOR(info);
27
+ JS_new(info);
28
+ }
29
+
30
+ Database::~Database() {
31
+ if (open) addon->dbs.erase(this);
32
+ CloseHandles();
33
+ }
34
+
35
+ // Whenever this is used, addon->dbs.erase() must be invoked beforehand.
36
+ void Database::CloseHandles() {
37
+ if (open) {
38
+ open = false;
39
+ for (Statement* stmt : stmts) stmt->CloseHandles();
40
+ for (Backup* backup : backups) backup->CloseHandles();
41
+ stmts.clear();
42
+ backups.clear();
43
+ int status = sqlite3_close(db_handle);
44
+ assert(status == SQLITE_OK); ((void)status);
45
+ }
46
+ }
47
+
48
+ void Database::ThrowDatabaseError(Napi::Env env) {
49
+ if (was_js_error) was_js_error = false;
50
+ else ThrowSqliteError(env, addon, db_handle);
51
+ }
52
+
53
+ void Database::ThrowSqliteError(Napi::Env env, Addon* addon, sqlite3* db_handle) {
54
+ assert(db_handle != NULL);
55
+ ThrowSqliteError(env, addon, sqlite3_errmsg(db_handle), sqlite3_extended_errcode(db_handle));
56
+ }
57
+
58
+ void Database::ThrowSqliteError(Napi::Env env, Addon* addon, const char* message, int code) {
59
+ assert(message != NULL);
60
+ assert((code & 0xff) != SQLITE_OK);
61
+ assert((code & 0xff) != SQLITE_ROW);
62
+ assert((code & 0xff) != SQLITE_DONE);
63
+ Napi::Object error = addon->SqliteError.New({
64
+ StringFromUtf8(env, message, -1),
65
+ addon->cs.Code(env, code)
66
+ });
67
+ // Constructing the SqliteError can itself throw (e.g., if the user's error
68
+ // constructor throws); in that case, let that exception propagate instead
69
+ // of trying to throw a second time.
70
+ if (!env.IsExceptionPending()) {
71
+ Napi::Error(env, error).ThrowAsJavaScriptException();
72
+ }
73
+ }
74
+
75
+ // Allows Statements to log their executed SQL.
76
+ bool Database::Log(Napi::Env env, sqlite3_stmt* handle) {
77
+ assert(was_js_error == false);
78
+ if (!has_logger) return false;
79
+ char* expanded = sqlite3_expanded_sql(handle);
80
+ napi_value arg = StringFromUtf8(env, expanded ? expanded : sqlite3_sql(handle), -1);
81
+ SafeCall(env, logger.Value().As<Napi::Function>(), env.Undefined(), 1, &arg);
82
+ was_js_error = env.IsExceptionPending();
83
+ if (expanded) sqlite3_free(expanded);
84
+ return was_js_error;
85
+ }
86
+
87
+ bool Database::Deserialize(
88
+ Napi::Env env,
89
+ Napi::Object buffer,
90
+ Addon* addon,
91
+ sqlite3* db_handle,
92
+ bool readonly
93
+ ) {
94
+ Napi::Buffer<char> buf = buffer.As<Napi::Buffer<char>>();
95
+ size_t length = buf.Length();
96
+ unsigned char* data = (unsigned char*)sqlite3_malloc64(length);
97
+ unsigned int flags = SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE;
98
+
99
+ if (readonly) {
100
+ flags |= SQLITE_DESERIALIZE_READONLY;
101
+ }
102
+ if (length) {
103
+ if (!data) {
104
+ ThrowError(env, "Out of memory");
105
+ return false;
106
+ }
107
+ memcpy(data, buf.Data(), length);
108
+ }
109
+
110
+ int status = sqlite3_deserialize(db_handle, "main", data, length, length, flags);
111
+ if (status != SQLITE_OK) {
112
+ ThrowSqliteError(env, addon, status == SQLITE_ERROR ? "unable to deserialize database" : sqlite3_errstr(status), status);
113
+ return false;
114
+ }
115
+
116
+ return true;
117
+ }
118
+
119
+ void Database::FreeSerialization(Napi::Env env, char* data) {
120
+ sqlite3_free(data);
121
+ }
122
+
123
+ INIT(Database::Init) {
124
+ return DefineClass(env, "Database", {
125
+ PrototypeMethod<Database, &Database::JS_prepare>("prepare", addon),
126
+ PrototypeMethod<Database, &Database::JS_exec>("exec", addon),
127
+ PrototypeMethod<Database, &Database::JS_backup>("backup", addon),
128
+ PrototypeMethod<Database, &Database::JS_serialize>("serialize", addon),
129
+ PrototypeMethod<Database, &Database::JS_function>("function", addon),
130
+ PrototypeMethod<Database, &Database::JS_aggregate>("aggregate", addon),
131
+ PrototypeMethod<Database, &Database::JS_table>("table", addon),
132
+ PrototypeMethod<Database, &Database::JS_loadExtension>("loadExtension", addon),
133
+ PrototypeMethod<Database, &Database::JS_close>("close", addon),
134
+ PrototypeMethod<Database, &Database::JS_defaultSafeIntegers>("defaultSafeIntegers", addon),
135
+ PrototypeMethod<Database, &Database::JS_unsafeMode>("unsafeMode", addon),
136
+ }, addon);
137
+ }
138
+
139
+ NODE_METHOD(Database::JS_new) {
140
+ assert(info.IsConstructCall());
141
+ REQUIRE_ARGUMENT_STRING(first, Napi::String filename);
142
+ REQUIRE_ARGUMENT_STRING(second, Napi::String filenameGiven);
143
+ REQUIRE_ARGUMENT_BOOLEAN(third, bool in_memory);
144
+ REQUIRE_ARGUMENT_BOOLEAN(fourth, bool readonly);
145
+ REQUIRE_ARGUMENT_BOOLEAN(fifth, bool must_exist);
146
+ REQUIRE_ARGUMENT_INT32(sixth, int timeout);
147
+ REQUIRE_ARGUMENT_ANY(seventh, Napi::Value logger);
148
+ REQUIRE_ARGUMENT_ANY(eighth, Napi::Value buffer);
149
+
150
+ UseAddon;
151
+ UseIsolate;
152
+ sqlite3* db_handle;
153
+ std::string utf8 = filename.Utf8Value();
154
+ int mask = readonly ? SQLITE_OPEN_READONLY
155
+ : must_exist ? SQLITE_OPEN_READWRITE
156
+ : (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
157
+
158
+ if (sqlite3_open_v2(utf8.c_str(), &db_handle, mask, NULL) != SQLITE_OK) {
159
+ ThrowSqliteError(env, addon, db_handle);
160
+ int status = sqlite3_close(db_handle);
161
+ assert(status == SQLITE_OK); ((void)status);
162
+ return env.Undefined();
163
+ }
164
+
165
+ assert(sqlite3_db_mutex(db_handle) == NULL);
166
+ sqlite3_extended_result_codes(db_handle, 1);
167
+ sqlite3_busy_timeout(db_handle, timeout);
168
+ sqlite3_limit(db_handle, SQLITE_LIMIT_LENGTH, MAX_BUFFER_SIZE < MAX_STRING_SIZE ? MAX_BUFFER_SIZE : MAX_STRING_SIZE);
169
+ sqlite3_limit(db_handle, SQLITE_LIMIT_SQL_LENGTH, MAX_STRING_SIZE);
170
+ int status = sqlite3_db_config(db_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
171
+ assert(status == SQLITE_OK); ((void)status);
172
+ status = sqlite3_db_config(db_handle, SQLITE_DBCONFIG_DEFENSIVE, 1, NULL);
173
+ assert(status == SQLITE_OK); ((void)status);
174
+
175
+ if (buffer.IsBuffer() && !Deserialize(env, buffer.As<Napi::Object>(), addon, db_handle, readonly)) {
176
+ int status = sqlite3_close(db_handle);
177
+ assert(status == SQLITE_OK); ((void)status);
178
+ return env.Undefined();
179
+ }
180
+
181
+ this->db_handle = db_handle;
182
+ open = true;
183
+ has_logger = logger.IsFunction();
184
+ if (has_logger) this->logger.Reset(logger, 1);
185
+ addon->dbs.insert(this);
186
+
187
+ Napi::Object _this = info.This().As<Napi::Object>();
188
+ SetFrozen(env, _this, addon->cs.memory, Napi::Boolean::New(env, in_memory));
189
+ SetFrozen(env, _this, addon->cs.readonly, Napi::Boolean::New(env, readonly));
190
+ SetFrozen(env, _this, addon->cs.name, filenameGiven);
191
+ SetInstanceGetter<Database, &Database::JS_open>(_this, "open", addon);
192
+ SetInstanceGetter<Database, &Database::JS_inTransaction>(_this, "inTransaction", addon);
193
+
194
+ return info.This();
195
+ }
196
+
197
+ NODE_METHOD(Database::JS_prepare) {
198
+ REQUIRE_ARGUMENT_STRING(first, Napi::String source);
199
+ REQUIRE_ARGUMENT_OBJECT(second, Napi::Object database);
200
+ REQUIRE_ARGUMENT_BOOLEAN(third, bool pragmaMode);
201
+ REQUIRE_ARGUMENT_BOOLEAN(fourth, bool explainMode);
202
+ (void)source;
203
+ (void)database;
204
+ (void)pragmaMode;
205
+ (void)explainMode;
206
+ UseAddon;
207
+ UseIsolate;
208
+ Napi::Function c = addon->Statement.Value();
209
+ addon->privileged_info = &info;
210
+ Napi::Object statement = SafeConstruct(env, c);
211
+ addon->privileged_info = NULL;
212
+ if (env.IsExceptionPending()) return env.Undefined();
213
+ return statement;
214
+ }
215
+
216
+ NODE_METHOD(Database::JS_exec) {
217
+ UNWRAP_OR_RETURN(Database, db, info.This());
218
+ REQUIRE_ARGUMENT_STRING(first, Napi::String source);
219
+ REQUIRE_DATABASE_OPEN(db);
220
+ REQUIRE_DATABASE_NOT_BUSY(db);
221
+ REQUIRE_DATABASE_NO_ITERATORS_UNLESS_UNSAFE(db);
222
+ db->busy = true;
223
+
224
+ UseIsolate;
225
+ std::string utf8 = source.Utf8Value();
226
+ const char* sql = utf8.c_str();
227
+ const char* tail;
228
+
229
+ int status;
230
+ const bool has_logger = db->has_logger;
231
+ sqlite3* const db_handle = db->db_handle;
232
+ sqlite3_stmt* handle;
233
+
234
+ for (;;) {
235
+ while (IS_SKIPPED(*sql)) ++sql;
236
+ status = sqlite3_prepare_v2(db_handle, sql, -1, &handle, &tail);
237
+ sql = tail;
238
+ if (!handle) break;
239
+ if (has_logger && db->Log(env, handle)) {
240
+ sqlite3_finalize(handle);
241
+ status = -1;
242
+ break;
243
+ }
244
+ do status = sqlite3_step(handle);
245
+ while (status == SQLITE_ROW);
246
+ status = sqlite3_finalize(handle);
247
+ if (status != SQLITE_OK) break;
248
+ }
249
+
250
+ db->busy = false;
251
+ if (status != SQLITE_OK) {
252
+ db->ThrowDatabaseError(env);
253
+ }
254
+ return env.Undefined();
255
+ }
256
+
257
+ NODE_METHOD(Database::JS_backup) {
258
+ REQUIRE_ARGUMENT_OBJECT(first, Napi::Object database);
259
+ REQUIRE_ARGUMENT_STRING(second, Napi::String attachedName);
260
+ REQUIRE_ARGUMENT_STRING(third, Napi::String destFile);
261
+ REQUIRE_ARGUMENT_BOOLEAN(fourth, bool unlink);
262
+ (void)database;
263
+ (void)attachedName;
264
+ (void)destFile;
265
+ (void)unlink;
266
+ UseAddon;
267
+ UseIsolate;
268
+ Napi::Function c = addon->Backup.Value();
269
+ addon->privileged_info = &info;
270
+ Napi::Object backup = SafeConstruct(env, c);
271
+ addon->privileged_info = NULL;
272
+ if (env.IsExceptionPending()) return env.Undefined();
273
+ return backup;
274
+ }
275
+
276
+ NODE_METHOD(Database::JS_serialize) {
277
+ UNWRAP_OR_RETURN(Database, db, info.This());
278
+ REQUIRE_ARGUMENT_STRING(first, Napi::String attachedName);
279
+ REQUIRE_DATABASE_OPEN(db);
280
+ REQUIRE_DATABASE_NOT_BUSY(db);
281
+ REQUIRE_DATABASE_NO_ITERATORS(db);
282
+
283
+ UseIsolate;
284
+ std::string attached_name = attachedName.Utf8Value();
285
+ sqlite3_int64 length = -1;
286
+ unsigned char* data = sqlite3_serialize(db->db_handle, attached_name.c_str(), &length, 0);
287
+
288
+ if (!data && length) {
289
+ return ThrowError(env, "Out of memory");
290
+ }
291
+
292
+ return Napi::Buffer<char>::NewOrCopy(env, reinterpret_cast<char*>(data), length, FreeSerialization);
293
+ }
294
+
295
+ NODE_METHOD(Database::JS_function) {
296
+ UNWRAP_OR_RETURN(Database, db, info.This());
297
+ REQUIRE_ARGUMENT_FUNCTION(first, Napi::Function fn);
298
+ REQUIRE_ARGUMENT_STRING(second, Napi::String nameString);
299
+ REQUIRE_ARGUMENT_INT32(third, int argc);
300
+ REQUIRE_ARGUMENT_INT32(fourth, int safe_ints);
301
+ REQUIRE_ARGUMENT_BOOLEAN(fifth, bool deterministic);
302
+ REQUIRE_ARGUMENT_BOOLEAN(sixth, bool direct_only);
303
+ REQUIRE_DATABASE_OPEN(db);
304
+ REQUIRE_DATABASE_NOT_BUSY(db);
305
+ REQUIRE_DATABASE_NO_ITERATORS(db);
306
+
307
+ UseIsolate;
308
+ std::string name = nameString.Utf8Value();
309
+ int mask = SQLITE_UTF8;
310
+ if (deterministic) mask |= SQLITE_DETERMINISTIC;
311
+ if (direct_only) mask |= SQLITE_DIRECTONLY;
312
+ safe_ints = safe_ints < 2 ? safe_ints : static_cast<int>(db->safe_ints);
313
+
314
+ if (sqlite3_create_function_v2(db->db_handle, name.c_str(), argc, mask, new CustomFunction(env, db, name.c_str(), fn, safe_ints), CustomFunction::xFunc, NULL, NULL, CustomFunction::xDestroy) != SQLITE_OK) {
315
+ db->ThrowDatabaseError(env);
316
+ }
317
+ return env.Undefined();
318
+ }
319
+
320
+ NODE_METHOD(Database::JS_aggregate) {
321
+ UNWRAP_OR_RETURN(Database, db, info.This());
322
+ REQUIRE_ARGUMENT_ANY(first, Napi::Value start);
323
+ REQUIRE_ARGUMENT_FUNCTION(second, Napi::Function step);
324
+ REQUIRE_ARGUMENT_ANY(third, Napi::Value inverse);
325
+ REQUIRE_ARGUMENT_ANY(fourth, Napi::Value result);
326
+ REQUIRE_ARGUMENT_STRING(fifth, Napi::String nameString);
327
+ REQUIRE_ARGUMENT_INT32(sixth, int argc);
328
+ REQUIRE_ARGUMENT_INT32(seventh, int safe_ints);
329
+ REQUIRE_ARGUMENT_BOOLEAN(eighth, bool deterministic);
330
+ REQUIRE_ARGUMENT_BOOLEAN(ninth, bool direct_only);
331
+ REQUIRE_DATABASE_OPEN(db);
332
+ REQUIRE_DATABASE_NOT_BUSY(db);
333
+ REQUIRE_DATABASE_NO_ITERATORS(db);
334
+
335
+ UseIsolate;
336
+ std::string name = nameString.Utf8Value();
337
+ auto xInverse = inverse.IsFunction() ? CustomAggregate::xInverse : NULL;
338
+ auto xValue = xInverse ? CustomAggregate::xValue : NULL;
339
+ int mask = SQLITE_UTF8;
340
+ if (deterministic) mask |= SQLITE_DETERMINISTIC;
341
+ if (direct_only) mask |= SQLITE_DIRECTONLY;
342
+ safe_ints = safe_ints < 2 ? safe_ints : static_cast<int>(db->safe_ints);
343
+
344
+ if (sqlite3_create_window_function(db->db_handle, name.c_str(), argc, mask, new CustomAggregate(env, db, name.c_str(), start, step, inverse, result, safe_ints), CustomAggregate::xStep, CustomAggregate::xFinal, xValue, xInverse, CustomAggregate::xDestroy) != SQLITE_OK) {
345
+ db->ThrowDatabaseError(env);
346
+ }
347
+ return env.Undefined();
348
+ }
349
+
350
+ NODE_METHOD(Database::JS_table) {
351
+ UNWRAP_OR_RETURN(Database, db, info.This());
352
+ REQUIRE_ARGUMENT_FUNCTION(first, Napi::Function factory);
353
+ REQUIRE_ARGUMENT_STRING(second, Napi::String nameString);
354
+ REQUIRE_ARGUMENT_BOOLEAN(third, bool eponymous);
355
+ REQUIRE_DATABASE_OPEN(db);
356
+ REQUIRE_DATABASE_NOT_BUSY(db);
357
+ REQUIRE_DATABASE_NO_ITERATORS(db);
358
+
359
+ UseIsolate;
360
+ std::string name = nameString.Utf8Value();
361
+ sqlite3_module* module = eponymous ? &CustomTable::EPONYMOUS_MODULE : &CustomTable::MODULE;
362
+
363
+ db->busy = true;
364
+ if (sqlite3_create_module_v2(db->db_handle, name.c_str(), module, new CustomTable(env, db, name.c_str(), factory), CustomTable::Destructor) != SQLITE_OK) {
365
+ db->ThrowDatabaseError(env);
366
+ }
367
+ db->busy = false;
368
+ return env.Undefined();
369
+ }
370
+
371
+ NODE_METHOD(Database::JS_loadExtension) {
372
+ UNWRAP_OR_RETURN(Database, db, info.This());
373
+ Napi::String entryPoint;
374
+ REQUIRE_ARGUMENT_STRING(first, Napi::String filename);
375
+ if (info.Length() > 1) { REQUIRE_ARGUMENT_STRING(second, entryPoint); }
376
+ REQUIRE_DATABASE_OPEN(db);
377
+ REQUIRE_DATABASE_NOT_BUSY(db);
378
+ REQUIRE_DATABASE_NO_ITERATORS(db);
379
+ UseIsolate;
380
+ char* error;
381
+ std::string filename_utf8 = filename.Utf8Value();
382
+ std::string entry_utf8;
383
+ bool has_entry = !entryPoint.IsEmpty();
384
+ if (has_entry) entry_utf8 = entryPoint.Utf8Value();
385
+ int status = sqlite3_load_extension(
386
+ db->db_handle,
387
+ filename_utf8.c_str(),
388
+ has_entry ? entry_utf8.c_str() : NULL,
389
+ &error
390
+ );
391
+ if (status != SQLITE_OK) {
392
+ ThrowSqliteError(env, db->addon, error, status);
393
+ }
394
+ sqlite3_free(error);
395
+ return env.Undefined();
396
+ }
397
+
398
+ NODE_METHOD(Database::JS_close) {
399
+ UNWRAP_OR_RETURN(Database, db, info.This());
400
+ if (db->open) {
401
+ REQUIRE_DATABASE_NOT_BUSY(db);
402
+ REQUIRE_DATABASE_NO_ITERATORS(db);
403
+ db->addon->dbs.erase(db);
404
+ db->CloseHandles();
405
+ }
406
+ return info.Env().Undefined();
407
+ }
408
+
409
+ NODE_METHOD(Database::JS_defaultSafeIntegers) {
410
+ UNWRAP_OR_RETURN(Database, db, info.This());
411
+ if (info.Length() == 0) db->safe_ints = true;
412
+ else { REQUIRE_ARGUMENT_BOOLEAN(first, db->safe_ints); }
413
+ return info.Env().Undefined();
414
+ }
415
+
416
+ NODE_METHOD(Database::JS_unsafeMode) {
417
+ UNWRAP_OR_RETURN(Database, db, info.This());
418
+ if (info.Length() == 0) db->unsafe_mode = true;
419
+ else { REQUIRE_ARGUMENT_BOOLEAN(first, db->unsafe_mode); }
420
+ sqlite3_db_config(db->db_handle, SQLITE_DBCONFIG_DEFENSIVE, static_cast<int>(!db->unsafe_mode), NULL);
421
+ return info.Env().Undefined();
422
+ }
423
+
424
+ NODE_GETTER(Database::JS_open) {
425
+ UNWRAP_OR_RETURN(Database, db, info.This());
426
+ return Napi::Boolean::New(info.Env(), db->open);
427
+ }
428
+
429
+ NODE_GETTER(Database::JS_inTransaction) {
430
+ UNWRAP_OR_RETURN(Database, db, info.This());
431
+ return Napi::Boolean::New(info.Env(), db->open && !static_cast<bool>(sqlite3_get_autocommit(db->db_handle)));
432
+ }
@@ -0,0 +1,100 @@
1
+ class Database : public Napi::ObjectWrap<Database> {
2
+ public:
3
+
4
+ explicit Database(const Napi::CallbackInfo& info);
5
+ ~Database();
6
+
7
+ // Whenever this is used, addon->dbs.erase() must be invoked beforehand.
8
+ void CloseHandles();
9
+
10
+ // Used to support ordered containers.
11
+ class CompareDatabase { public:
12
+ inline bool operator() (Database const * const a, Database const * const b) const {
13
+ return a < b;
14
+ }
15
+ };
16
+ class CompareStatement { public:
17
+ inline bool operator() (Statement const * const a, Statement const * const b) const {
18
+ return Statement::Compare(a, b);
19
+ }
20
+ };
21
+ class CompareBackup { public:
22
+ inline bool operator() (Backup const * const a, Backup const * const b) const {
23
+ return Backup::Compare(a, b);
24
+ }
25
+ };
26
+
27
+ // Proper error handling logic for when an sqlite3 operation fails.
28
+ void ThrowDatabaseError(Napi::Env env);
29
+ static void ThrowSqliteError(Napi::Env env, Addon* addon, sqlite3* db_handle);
30
+ static void ThrowSqliteError(Napi::Env env, Addon* addon, const char* message, int code);
31
+
32
+ // Allows Statements to log their executed SQL.
33
+ bool Log(Napi::Env env, sqlite3_stmt* handle);
34
+
35
+ // Allow Statements to manage themselves when created and garbage collected.
36
+ inline void AddStatement(Statement* stmt) { stmts.insert(stmts.end(), stmt); }
37
+ inline void RemoveStatement(Statement* stmt) { stmts.erase(stmt); }
38
+
39
+ // Allow Backups to manage themselves when created and garbage collected.
40
+ inline void AddBackup(Backup* backup) { backups.insert(backups.end(), backup); }
41
+ inline void RemoveBackup(Backup* backup) { backups.erase(backup); }
42
+
43
+ // A view for Statements to see and modify Database state.
44
+ // The order of these fields must exactly match their actual order.
45
+ struct State {
46
+ const bool open;
47
+ bool busy;
48
+ const bool safe_ints;
49
+ const bool unsafe_mode;
50
+ bool was_js_error;
51
+ const bool has_logger;
52
+ unsigned short iterators;
53
+ Addon* const addon;
54
+ };
55
+
56
+ inline State* GetState() { return reinterpret_cast<State*>(&open); }
57
+ inline sqlite3* GetHandle() { return db_handle; }
58
+ inline Addon* GetAddon() { return addon; }
59
+
60
+ // Identifies objects that are backed by this class (see IsInstanceOf).
61
+ static const napi_type_tag TYPE_TAG;
62
+
63
+ static INIT(Init);
64
+
65
+ private:
66
+
67
+ NODE_METHOD(JS_new);
68
+ static NODE_METHOD(JS_prepare);
69
+ static NODE_METHOD(JS_exec);
70
+ static NODE_METHOD(JS_backup);
71
+ static NODE_METHOD(JS_serialize);
72
+ static NODE_METHOD(JS_function);
73
+ static NODE_METHOD(JS_aggregate);
74
+ static NODE_METHOD(JS_table);
75
+ static NODE_METHOD(JS_loadExtension);
76
+ static NODE_METHOD(JS_close);
77
+ static NODE_METHOD(JS_defaultSafeIntegers);
78
+ static NODE_METHOD(JS_unsafeMode);
79
+ static NODE_GETTER(JS_open);
80
+ static NODE_GETTER(JS_inTransaction);
81
+
82
+ static bool Deserialize(Napi::Env env, Napi::Object buffer, Addon* addon, sqlite3* db_handle, bool readonly);
83
+ static void FreeSerialization(Napi::Env env, char* data);
84
+
85
+ static const int MAX_BUFFER_SIZE;
86
+ static const int MAX_STRING_SIZE;
87
+
88
+ sqlite3* db_handle;
89
+ bool open;
90
+ bool busy;
91
+ bool safe_ints;
92
+ bool unsafe_mode;
93
+ bool was_js_error;
94
+ bool has_logger;
95
+ unsigned short iterators;
96
+ Addon* const addon;
97
+ Napi::Reference<Napi::Value> logger;
98
+ std::set<Statement*, CompareStatement> stmts;
99
+ std::set<Backup*, CompareBackup> backups;
100
+ };