| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 079339a commit 5da6eaa
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -349,6 +349,7 @@ class CustomAggregate { | |||
| 349 | 349 | Global<Function> CustomAggregate::*mptr) { | |
| 350 | 350 | CustomAggregate* self = | |
| 351 | 351 | static_cast<CustomAggregate*>(sqlite3_user_data(ctx)); | |
| 352 | + CallbackDepthGuard guard(self->db_); | ||
| 352 | 353 | Environment* env = self->env_; | |
| 353 | 354 | Isolate* isolate = env->isolate(); | |
| 354 | 355 | auto agg = self->GetAggregate(ctx); | |
@@ -395,12 +396,18 @@ class CustomAggregate { | |||
| 395 | 396 | return; | |
| 396 | 397 | } | |
| 397 | 398 | ||
| 399 | + if (!self->db_->IsOpen()) { | ||
| 400 | + THROW_ERR_INVALID_STATE(env, "database is not open"); | ||
| 401 | + return; | ||
| 402 | + } | ||
| 403 | + | ||
| 398 | 404 | agg->value.Reset(isolate, ret); | |
| 399 | 405 | } | |
| 400 | 406 | ||
| 401 | 407 | static inline void xValueBase(sqlite3_context* ctx, bool is_final) { | |
| 402 | 408 | CustomAggregate* self = | |
| 403 | 409 | static_cast<CustomAggregate*>(sqlite3_user_data(ctx)); | |
| 410 | + CallbackDepthGuard guard(self->db_); | ||
| 404 | 411 | Environment* env = self->env_; | |
| 405 | 412 | Isolate* isolate = env->isolate(); | |
| 406 | 413 | auto agg = self->GetAggregate(ctx); | |
@@ -426,6 +433,9 @@ class CustomAggregate { | |||
| 426 | 433 | .ToLocal(&result)) { | |
| 427 | 434 | self->db_->SetIgnoreNextSQLiteError(true); | |
| 428 | 435 | sqlite3_result_error(ctx, "", 0); | |
| 436 | + } else if (!self->db_->IsOpen()) { | ||
| 437 | + THROW_ERR_INVALID_STATE(env, "database is not open"); | ||
| 438 | + return; | ||
| 429 | 439 | } | |
| 430 | 440 | } else { | |
| 431 | 441 | result = Local<Value>::New(isolate, agg->value); | |
@@ -457,6 +467,10 @@ class CustomAggregate { | |||
| 457 | 467 | auto fn = start_v.As<Function>(); | |
| 458 | 468 | MaybeLocal<Value> retval = | |
| 459 | 469 | fn->Call(env_->context(), Null(isolate), 0, nullptr); | |
| 470 | + if (!db_->IsOpen()) { | ||
| 471 | + THROW_ERR_INVALID_STATE(env_, "database is not open"); | ||
| 472 | + return nullptr; | ||
| 473 | + } | ||
| 460 | 474 | if (!retval.ToLocal(&start_v)) { | |
| 461 | 475 | db_->SetIgnoreNextSQLiteError(true); | |
| 462 | 476 | sqlite3_result_error(ctx, "", 0); | |
@@ -669,6 +683,7 @@ void UserDefinedFunction::xFunc(sqlite3_context* ctx, | |||
| 669 | 683 | sqlite3_value** argv) { | |
| 670 | 684 | UserDefinedFunction* self = | |
| 671 | 685 | static_cast<UserDefinedFunction*>(sqlite3_user_data(ctx)); | |
| 686 | + CallbackDepthGuard guard(self->db_); | ||
| 672 | 687 | Environment* env = self->env_; | |
| 673 | 688 | Isolate* isolate = env->isolate(); | |
| 674 | 689 | auto recv = Undefined(isolate); | |
@@ -700,6 +715,12 @@ void UserDefinedFunction::xFunc(sqlite3_context* ctx, | |||
| 700 | 715 | ||
| 701 | 716 | MaybeLocal<Value> retval = | |
| 702 | 717 | fn->Call(env->context(), recv, argc, js_argv.data()); | |
| 718 | + | ||
| 719 | + if (!self->db_->IsOpen()) { | ||
| 720 | + THROW_ERR_INVALID_STATE(env, "database is not open"); | ||
| 721 | + return; | ||
| 722 | + } | ||
| 723 | + | ||
| 703 | 724 | Local<Value> result; | |
| 704 | 725 | if (!retval.ToLocal(&result)) { | |
| 705 | 726 | // Ignore the SQLite error because a JavaScript exception is pending. | |
@@ -1433,6 +1454,8 @@ void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) { | |||
| 1433 | 1454 | ASSIGN_OR_RETURN_UNWRAP(&db, args.This()); | |
| 1434 | 1455 | Environment* env = Environment::GetCurrent(args); | |
| 1435 | 1456 | THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open"); | |
| 1457 | + THROW_AND_RETURN_ON_BAD_STATE( | ||
| 1458 | + env, db->IsInCallback(), "database cannot be closed while in a callback"); | ||
| 1436 | 1459 | db->FinalizeStatements(); | |
| 1437 | 1460 | db->DeleteSessions(); | |
| 1438 | 1461 | int r = sqlite3_close_v2(db->connection_); | |
@@ -2381,13 +2404,17 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) { | |||
| 2381 | 2404 | BaseObjectPtr<DatabaseSync> guard(db); | |
| 2382 | 2405 | ||
| 2383 | 2406 | ArrayBufferViewContents<uint8_t> buf(args[0]); | |
| 2384 | - int r = sqlite3changeset_apply( | ||
| 2385 | - db->connection_, | ||
| 2386 | - buf.length(), | ||
| 2387 | - const_cast<void*>(static_cast<const void*>(buf.data())), | ||
| 2388 | - context.filterCallback ? xFilter : nullptr, | ||
| 2389 | - xConflict, | ||
| 2390 | - static_cast<void*>(&context)); | ||
| 2407 | + int r; | ||
| 2408 | + { | ||
| 2409 | + CallbackDepthGuard guard(db); | ||
| 2410 | + r = sqlite3changeset_apply( | ||
| 2411 | + db->connection_, | ||
| 2412 | + buf.length(), | ||
| 2413 | + const_cast<void*>(static_cast<const void*>(buf.data())), | ||
| 2414 | + context.filterCallback ? xFilter : nullptr, | ||
| 2415 | + xConflict, | ||
| 2416 | + static_cast<void*>(&context)); | ||
| 2417 | + } | ||
| 2391 | 2418 | if (r == SQLITE_OK) { | |
| 2392 | 2419 | args.GetReturnValue().Set(true); | |
| 2393 | 2420 | return; | |
@@ -2522,6 +2549,7 @@ int DatabaseSync::AuthorizerCallback(void* user_data, | |||
| 2522 | 2549 | const char* param3, | |
| 2523 | 2550 | const char* param4) { | |
| 2524 | 2551 | DatabaseSync* db = static_cast<DatabaseSync*>(user_data); | |
| 2552 | + CallbackDepthGuard guard(db); | ||
| 2525 | 2553 | Environment* env = db->env(); | |
| 2526 | 2554 | Isolate* isolate = env->isolate(); | |
| 2527 | 2555 | HandleScope handle_scope(isolate); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -229,6 +229,10 @@ class DatabaseSync : public BaseObject { | |||
| 229 | 229 | void SetIgnoreNextSQLiteError(bool ignore); | |
| 230 | 230 | bool ShouldIgnoreSQLiteError(); | |
| 231 | 231 | ||
| 232 | + void IncrementCallbackDepth() { ++callback_depth_; } | ||
| 233 | + void DecrementCallbackDepth() { --callback_depth_; } | ||
| 234 | + bool IsInCallback() const { return callback_depth_ > 0; } | ||
| 235 | + | ||
| 232 | 236 | SET_MEMORY_INFO_NAME(DatabaseSync) | |
| 233 | 237 | SET_SELF_SIZE(DatabaseSync) | |
| 234 | 238 | ||
@@ -242,6 +246,7 @@ class DatabaseSync : public BaseObject { | |||
| 242 | 246 | bool enable_load_extension_; | |
| 243 | 247 | sqlite3* connection_; | |
| 244 | 248 | bool ignore_next_sqlite_error_; | |
| 249 | + int callback_depth_ = 0; | ||
| 245 | 250 | ||
| 246 | 251 | std::set<BackupJob*> backups_; | |
| 247 | 252 | std::unordered_set<Session*> sessions_; | |
@@ -401,6 +406,19 @@ class SQLTagStore : public BaseObject { | |||
| 401 | 406 | friend class StatementExecutionHelper; | |
| 402 | 407 | }; | |
| 403 | 408 | ||
| 409 | + class CallbackDepthGuard { | ||
| 410 | + public: | ||
| 411 | + explicit CallbackDepthGuard(DatabaseSync* db) : db_(db) { | ||
| 412 | + db_->IncrementCallbackDepth(); | ||
| 413 | + } | ||
| 414 | + ~CallbackDepthGuard() { db_->DecrementCallbackDepth(); } | ||
| 415 | + CallbackDepthGuard(const CallbackDepthGuard&) = delete; | ||
| 416 | + CallbackDepthGuard& operator=(const CallbackDepthGuard&) = delete; | ||
| 417 | + | ||
| 418 | + private: | ||
| 419 | + DatabaseSync* db_; | ||
| 420 | + }; | ||
| 421 | + | ||
| 404 | 422 | class UserDefinedFunction { | |
| 405 | 423 | public: | |
| 406 | 424 | UserDefinedFunction(Environment* env, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,39 @@ | |||
| 1 | + 'use strict'; | ||
| 2 | + | ||
| 3 | + const { skipIfSQLiteMissing } = require('../common'); | ||
| 4 | + skipIfSQLiteMissing(); | ||
| 5 | + const assert = require('node:assert'); | ||
| 6 | + const { test } = require('node:test'); | ||
| 7 | + const { DatabaseSync } = require('node:sqlite'); | ||
| 8 | + | ||
| 9 | + for (const method of ['all', 'get', 'run', 'iterate']) { | ||
| 10 | + test(`database.close() from a UDF during statement.${method}()`, () => { | ||
| 11 | + const db = new DatabaseSync(':memory:'); | ||
| 12 | + db.exec(` | ||
| 13 | + CREATE TABLE data (value INTEGER); | ||
| 14 | + INSERT INTO data VALUES (1), (2), (3); | ||
| 15 | + `); | ||
| 16 | + | ||
| 17 | + db.function('close_db', (value) => { | ||
| 18 | + db.close(); | ||
| 19 | + return value; | ||
| 20 | + }); | ||
| 21 | + | ||
| 22 | + const statement = db.prepare('SELECT close_db(value) FROM data'); | ||
| 23 | + assert.throws(() => { | ||
| 24 | + if (method === 'iterate') { | ||
| 25 | + for (const row of statement.iterate()) { | ||
| 26 | + assert.ok(row); | ||
| 27 | + } | ||
| 28 | + } else { | ||
| 29 | + statement[method](); | ||
| 30 | + } | ||
| 31 | + }, { | ||
| 32 | + code: 'ERR_INVALID_STATE', | ||
| 33 | + message: 'database cannot be closed while in a callback', | ||
| 34 | + }); | ||
| 35 | + | ||
| 36 | + assert.strictEqual(db.isOpen, true); | ||
| 37 | + db.close(); | ||
| 38 | + }); | ||
| 39 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments