@photostructure/sqlite 0.5.0 → 1.1.0

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.
@@ -12,7 +12,10 @@
12
12
  #include "threadpoolwork-inl.h"
13
13
  #include "util-inl.h"
14
14
 
15
+ #include <array>
15
16
  #include <cinttypes>
17
+ #include <cmath>
18
+ #include <limits>
16
19
 
17
20
  namespace node {
18
21
  namespace sqlite {
@@ -36,6 +39,7 @@ using v8::Global;
36
39
  using v8::HandleScope;
37
40
  using v8::Int32;
38
41
  using v8::Integer;
42
+ using v8::Intercepted;
39
43
  using v8::Isolate;
40
44
  using v8::JustVoid;
41
45
  using v8::Local;
@@ -43,12 +47,16 @@ using v8::LocalVector;
43
47
  using v8::Maybe;
44
48
  using v8::MaybeLocal;
45
49
  using v8::Name;
50
+ using v8::NamedPropertyHandlerConfiguration;
46
51
  using v8::NewStringType;
47
52
  using v8::Nothing;
48
53
  using v8::Null;
49
54
  using v8::Number;
50
55
  using v8::Object;
56
+ using v8::ObjectTemplate;
51
57
  using v8::Promise;
58
+ using v8::PropertyCallbackInfo;
59
+ using v8::PropertyHandlerFlags;
52
60
  using v8::SideEffectType;
53
61
  using v8::String;
54
62
  using v8::TryCatch;
@@ -133,6 +141,16 @@ Local<DictionaryTemplate> getLazyIterTemplate(Environment* env) {
133
141
  }
134
142
  } // namespace
135
143
 
144
+ // Helper function to find limit info from JS property name
145
+ static constexpr const LimitInfo* GetLimitInfoFromName(std::string_view name) {
146
+ for (const auto& info : kLimitMapping) {
147
+ if (name == info.js_name) {
148
+ return &info;
149
+ }
150
+ }
151
+ return nullptr;
152
+ }
153
+
136
154
  inline MaybeLocal<Object> CreateSQLiteError(Isolate* isolate,
137
155
  const char* message) {
138
156
  Local<String> js_msg;
@@ -668,6 +686,175 @@ void UserDefinedFunction::xDestroy(void* self) {
668
686
  delete static_cast<UserDefinedFunction*>(self);
669
687
  }
670
688
 
689
+ DatabaseSyncLimits::DatabaseSyncLimits(Environment* env,
690
+ Local<Object> object,
691
+ BaseObjectWeakPtr<DatabaseSync> database)
692
+ : BaseObject(env, object), database_(std::move(database)) {
693
+ MakeWeak();
694
+ }
695
+
696
+ DatabaseSyncLimits::~DatabaseSyncLimits() = default;
697
+
698
+ void DatabaseSyncLimits::MemoryInfo(MemoryTracker* tracker) const {
699
+ tracker->TrackField("database", database_);
700
+ }
701
+
702
+ Local<ObjectTemplate> DatabaseSyncLimits::GetTemplate(Environment* env) {
703
+ Local<ObjectTemplate> tmpl = env->sqlite_limits_template();
704
+ if (!tmpl.IsEmpty()) return tmpl;
705
+
706
+ Isolate* isolate = env->isolate();
707
+ tmpl = ObjectTemplate::New(isolate);
708
+ tmpl->SetInternalFieldCount(DatabaseSyncLimits::kInternalFieldCount);
709
+ tmpl->SetHandler(NamedPropertyHandlerConfiguration(
710
+ LimitsGetter,
711
+ LimitsSetter,
712
+ LimitsQuery,
713
+ nullptr, // deleter - not allowed
714
+ LimitsEnumerator,
715
+ nullptr, // definer
716
+ nullptr,
717
+ Local<Value>(),
718
+ PropertyHandlerFlags::kHasNoSideEffect));
719
+
720
+ env->set_sqlite_limits_template(tmpl);
721
+ return tmpl;
722
+ }
723
+
724
+ Intercepted DatabaseSyncLimits::LimitsGetter(
725
+ Local<Name> property, const PropertyCallbackInfo<Value>& info) {
726
+ // Skip symbols
727
+ if (!property->IsString()) {
728
+ return Intercepted::kNo;
729
+ }
730
+
731
+ DatabaseSyncLimits* limits;
732
+ ASSIGN_OR_RETURN_UNWRAP(&limits, info.This(), Intercepted::kNo);
733
+
734
+ Environment* env = limits->env();
735
+ Isolate* isolate = env->isolate();
736
+
737
+ Utf8Value prop_name(isolate, property);
738
+ const LimitInfo* limit_info = GetLimitInfoFromName(prop_name.ToStringView());
739
+
740
+ if (limit_info == nullptr) {
741
+ return Intercepted::kNo; // Unknown property, let default handling occur
742
+ }
743
+
744
+ if (!limits->database_ || !limits->database_->IsOpen()) {
745
+ THROW_ERR_INVALID_STATE(env, "database is not open");
746
+ return Intercepted::kYes;
747
+ }
748
+
749
+ int current_value = sqlite3_limit(
750
+ limits->database_->Connection(), limit_info->sqlite_limit_id, -1);
751
+ info.GetReturnValue().Set(Integer::New(isolate, current_value));
752
+ return Intercepted::kYes;
753
+ }
754
+
755
+ Intercepted DatabaseSyncLimits::LimitsSetter(
756
+ Local<Name> property,
757
+ Local<Value> value,
758
+ const PropertyCallbackInfo<void>& info) {
759
+ if (!property->IsString()) {
760
+ return Intercepted::kNo;
761
+ }
762
+
763
+ DatabaseSyncLimits* limits;
764
+ ASSIGN_OR_RETURN_UNWRAP(&limits, info.This(), Intercepted::kNo);
765
+
766
+ Environment* env = limits->env();
767
+ Isolate* isolate = env->isolate();
768
+
769
+ Utf8Value prop_name(isolate, property);
770
+ const LimitInfo* limit_info = GetLimitInfoFromName(prop_name.ToStringView());
771
+
772
+ if (limit_info == nullptr) {
773
+ return Intercepted::kNo;
774
+ }
775
+
776
+ if (!limits->database_ || !limits->database_->IsOpen()) {
777
+ THROW_ERR_INVALID_STATE(env, "database is not open");
778
+ return Intercepted::kYes;
779
+ }
780
+
781
+ if (!value->IsNumber()) {
782
+ THROW_ERR_INVALID_ARG_TYPE(
783
+ isolate, "Limit value must be a non-negative integer or Infinity.");
784
+ return Intercepted::kYes;
785
+ }
786
+
787
+ const double num_value = value.As<Number>()->Value();
788
+ int new_value;
789
+
790
+ if (std::isinf(num_value) && num_value > 0) {
791
+ // Positive Infinity resets the limit to the compile-time maximum
792
+ new_value = std::numeric_limits<int>::max();
793
+ } else if (!value->IsInt32()) {
794
+ THROW_ERR_INVALID_ARG_TYPE(
795
+ isolate, "Limit value must be a non-negative integer or Infinity.");
796
+ return Intercepted::kYes;
797
+ } else {
798
+ new_value = value.As<Int32>()->Value();
799
+ if (new_value < 0) {
800
+ THROW_ERR_OUT_OF_RANGE(isolate, "Limit value must be non-negative.");
801
+ return Intercepted::kYes;
802
+ }
803
+ }
804
+
805
+ sqlite3_limit(
806
+ limits->database_->Connection(), limit_info->sqlite_limit_id, new_value);
807
+ return Intercepted::kYes;
808
+ }
809
+
810
+ Intercepted DatabaseSyncLimits::LimitsQuery(
811
+ Local<Name> property, const PropertyCallbackInfo<Integer>& info) {
812
+ if (!property->IsString()) {
813
+ return Intercepted::kNo;
814
+ }
815
+
816
+ Isolate* isolate = info.GetIsolate();
817
+ Utf8Value prop_name(isolate, property);
818
+ const LimitInfo* limit_info = GetLimitInfoFromName(prop_name.ToStringView());
819
+
820
+ if (!limit_info) {
821
+ return Intercepted::kNo;
822
+ }
823
+
824
+ // Property exists and is writable
825
+ info.GetReturnValue().Set(
826
+ Integer::New(isolate, v8::PropertyAttribute::DontDelete));
827
+ return Intercepted::kYes;
828
+ }
829
+
830
+ void DatabaseSyncLimits::LimitsEnumerator(
831
+ const PropertyCallbackInfo<Array>& info) {
832
+ Isolate* isolate = info.GetIsolate();
833
+ LocalVector<Value> names(isolate);
834
+
835
+ for (const auto& [js_name, sqlite_limit_id] : kLimitMapping) {
836
+ Local<String> name;
837
+ if (!String::NewFromUtf8(
838
+ isolate, js_name.data(), NewStringType::kNormal, js_name.size())
839
+ .ToLocal(&name)) {
840
+ return;
841
+ }
842
+ names.push_back(name);
843
+ }
844
+
845
+ info.GetReturnValue().Set(Array::New(isolate, names.data(), names.size()));
846
+ }
847
+
848
+ BaseObjectPtr<DatabaseSyncLimits> DatabaseSyncLimits::Create(
849
+ Environment* env, BaseObjectWeakPtr<DatabaseSync> database) {
850
+ Local<Object> obj;
851
+ if (!GetTemplate(env)->NewInstance(env->context()).ToLocal(&obj)) {
852
+ return nullptr;
853
+ }
854
+
855
+ return MakeBaseObject<DatabaseSyncLimits>(env, obj, std::move(database));
856
+ }
857
+
671
858
  DatabaseSync::DatabaseSync(Environment* env,
672
859
  Local<Object> object,
673
860
  DatabaseOpenConfiguration&& open_config,
@@ -766,6 +953,14 @@ bool DatabaseSync::Open() {
766
953
 
767
954
  sqlite3_busy_timeout(connection_, open_config_.get_timeout());
768
955
 
956
+ // Apply initial limits
957
+ for (const auto& [js_name, sqlite_limit_id] : kLimitMapping) {
958
+ const auto& limit_value = open_config_.initial_limits()[sqlite_limit_id];
959
+ if (limit_value.has_value()) {
960
+ sqlite3_limit(connection_, sqlite_limit_id, *limit_value);
961
+ }
962
+ }
963
+
769
964
  if (allow_load_extension_) {
770
965
  if (env()->permission()->enabled()) [[unlikely]] {
771
966
  THROW_ERR_LOAD_SQLITE_EXTENSION(env(),
@@ -886,7 +1081,7 @@ std::optional<std::string> ValidateDatabasePath(Environment* env,
886
1081
  THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
887
1082
  "The \"%s\" argument must be a string, "
888
1083
  "Uint8Array, or URL without null bytes.",
889
- field_name.c_str());
1084
+ field_name);
890
1085
 
891
1086
  return std::nullopt;
892
1087
  }
@@ -1091,6 +1286,59 @@ void DatabaseSync::New(const FunctionCallbackInfo<Value>& args) {
1091
1286
  }
1092
1287
  open_config.set_enable_defensive(defensive_v.As<Boolean>()->Value());
1093
1288
  }
1289
+
1290
+ // Parse limits option
1291
+ Local<Value> limits_v;
1292
+ if (!options->Get(env->context(), env->limits_string())
1293
+ .ToLocal(&limits_v)) {
1294
+ return;
1295
+ }
1296
+ if (!limits_v->IsUndefined()) {
1297
+ if (!limits_v->IsObject()) {
1298
+ THROW_ERR_INVALID_ARG_TYPE(
1299
+ env->isolate(),
1300
+ "The \"options.limits\" argument must be an object.");
1301
+ return;
1302
+ }
1303
+
1304
+ Local<Object> limits_obj = limits_v.As<Object>();
1305
+
1306
+ // Iterate through known limit names and extract values
1307
+ for (const auto& [js_name, sqlite_limit_id] : kLimitMapping) {
1308
+ Local<String> key;
1309
+ if (!String::NewFromUtf8(env->isolate(),
1310
+ js_name.data(),
1311
+ NewStringType::kNormal,
1312
+ js_name.size())
1313
+ .ToLocal(&key)) {
1314
+ return;
1315
+ }
1316
+
1317
+ Local<Value> val;
1318
+ if (!limits_obj->Get(env->context(), key).ToLocal(&val)) {
1319
+ return;
1320
+ }
1321
+
1322
+ if (!val->IsUndefined()) {
1323
+ if (!val->IsInt32()) {
1324
+ std::string msg = "The \"options.limits." + std::string(js_name) +
1325
+ "\" argument must be an integer.";
1326
+ THROW_ERR_INVALID_ARG_TYPE(env->isolate(), msg);
1327
+ return;
1328
+ }
1329
+
1330
+ int limit_val = val.As<Int32>()->Value();
1331
+ if (limit_val < 0) {
1332
+ std::string msg = "The \"options.limits." + std::string(js_name) +
1333
+ "\" argument must be non-negative.";
1334
+ THROW_ERR_OUT_OF_RANGE(env->isolate(), msg);
1335
+ return;
1336
+ }
1337
+
1338
+ open_config.set_initial_limit(sqlite_limit_id, limit_val);
1339
+ }
1340
+ }
1341
+ }
1094
1342
  }
1095
1343
 
1096
1344
  new DatabaseSync(
@@ -1118,6 +1366,26 @@ void DatabaseSync::IsTransactionGetter(
1118
1366
  args.GetReturnValue().Set(sqlite3_get_autocommit(db->connection_) == 0);
1119
1367
  }
1120
1368
 
1369
+ void DatabaseSync::LimitsGetter(const FunctionCallbackInfo<Value>& args) {
1370
+ DatabaseSync* db;
1371
+ ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
1372
+ Environment* env = Environment::GetCurrent(args);
1373
+
1374
+ Local<Value> limits_val =
1375
+ db->object()->GetInternalField(kLimitsObject).template As<Value>();
1376
+
1377
+ if (limits_val->IsUndefined()) {
1378
+ BaseObjectPtr<DatabaseSyncLimits> limits =
1379
+ DatabaseSyncLimits::Create(env, BaseObjectWeakPtr<DatabaseSync>(db));
1380
+ if (limits) {
1381
+ db->object()->SetInternalField(kLimitsObject, limits->object());
1382
+ args.GetReturnValue().Set(limits->object());
1383
+ }
1384
+ } else {
1385
+ args.GetReturnValue().Set(limits_val);
1386
+ }
1387
+ }
1388
+
1121
1389
  void DatabaseSync::Close(const FunctionCallbackInfo<Value>& args) {
1122
1390
  DatabaseSync* db;
1123
1391
  ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
@@ -2153,6 +2421,11 @@ inline bool StatementSync::IsFinalized() {
2153
2421
  return statement_ == nullptr;
2154
2422
  }
2155
2423
 
2424
+ inline int StatementSync::ResetStatement() {
2425
+ reset_generation_++;
2426
+ return sqlite3_reset(statement_);
2427
+ }
2428
+
2156
2429
  bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
2157
2430
  int r = sqlite3_clear_bindings(statement_);
2158
2431
  CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
@@ -2261,20 +2534,38 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
2261
2534
  // Dates could be supported by converting them to numbers. However, there
2262
2535
  // would not be a good way to read the values back from SQLite with the
2263
2536
  // original type.
2537
+ Isolate* isolate = env()->isolate();
2264
2538
  int r;
2265
2539
  if (value->IsNumber()) {
2266
- double val = value.As<Number>()->Value();
2540
+ const double val = value.As<Number>()->Value();
2267
2541
  r = sqlite3_bind_double(statement_, index, val);
2268
2542
  } else if (value->IsString()) {
2269
- Utf8Value val(env()->isolate(), value.As<String>());
2270
- r = sqlite3_bind_text(
2271
- statement_, index, *val, val.length(), SQLITE_TRANSIENT);
2543
+ Utf8Value val(isolate, value.As<String>());
2544
+ if (val.IsAllocated()) {
2545
+ // Avoid an extra SQLite copy for large strings by transferring ownership
2546
+ // of the malloc()'d buffer to SQLite.
2547
+ char* data = *val;
2548
+ const sqlite3_uint64 length = static_cast<sqlite3_uint64>(val.length());
2549
+ val.Release();
2550
+ r = sqlite3_bind_text64(
2551
+ statement_, index, data, length, std::free, SQLITE_UTF8);
2552
+ } else {
2553
+ r = sqlite3_bind_text64(statement_,
2554
+ index,
2555
+ *val,
2556
+ static_cast<sqlite3_uint64>(val.length()),
2557
+ SQLITE_TRANSIENT,
2558
+ SQLITE_UTF8);
2559
+ }
2272
2560
  } else if (value->IsNull()) {
2273
2561
  r = sqlite3_bind_null(statement_, index);
2274
2562
  } else if (value->IsArrayBufferView()) {
2275
2563
  ArrayBufferViewContents<uint8_t> buf(value);
2276
- r = sqlite3_bind_blob(
2277
- statement_, index, buf.data(), buf.length(), SQLITE_TRANSIENT);
2564
+ r = sqlite3_bind_blob64(statement_,
2565
+ index,
2566
+ buf.data(),
2567
+ static_cast<sqlite3_uint64>(buf.length()),
2568
+ SQLITE_TRANSIENT);
2278
2569
  } else if (value->IsBigInt()) {
2279
2570
  bool lossless;
2280
2571
  int64_t as_int = value.As<BigInt>()->Int64Value(&lossless);
@@ -2285,13 +2576,13 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
2285
2576
  r = sqlite3_bind_int64(statement_, index, as_int);
2286
2577
  } else {
2287
2578
  THROW_ERR_INVALID_ARG_TYPE(
2288
- env()->isolate(),
2579
+ isolate,
2289
2580
  "Provided value cannot be bound to SQLite parameter %d.",
2290
2581
  index);
2291
2582
  return false;
2292
2583
  }
2293
2584
 
2294
- CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
2585
+ CHECK_ERROR_OR_THROW(isolate, db_.get(), r, SQLITE_OK, false);
2295
2586
  return true;
2296
2587
  }
2297
2588
 
@@ -2406,7 +2697,7 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
2406
2697
  sqlite3_step(stmt);
2407
2698
  int r = sqlite3_reset(stmt);
2408
2699
  CHECK_ERROR_OR_THROW(isolate, db, r, SQLITE_OK, MaybeLocal<Object>());
2409
- Local<Object> result = Object::New(isolate);
2700
+
2410
2701
  sqlite3_int64 last_insert_rowid = sqlite3_last_insert_rowid(db->Connection());
2411
2702
  sqlite3_int64 changes = sqlite3_changes64(db->Connection());
2412
2703
  Local<Value> last_insert_rowid_val;
@@ -2420,13 +2711,18 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
2420
2711
  changes_val = Number::New(isolate, changes);
2421
2712
  }
2422
2713
 
2423
- if (result
2424
- ->Set(env->context(),
2425
- env->last_insert_rowid_string(),
2426
- last_insert_rowid_val)
2427
- .IsNothing() ||
2428
- result->Set(env->context(), env->changes_string(), changes_val)
2429
- .IsNothing()) {
2714
+ auto run_result_template = env->sqlite_run_result_template();
2715
+ if (run_result_template.IsEmpty()) {
2716
+ static constexpr std::string_view run_result_keys[] = {"changes",
2717
+ "lastInsertRowid"};
2718
+ run_result_template = DictionaryTemplate::New(isolate, run_result_keys);
2719
+ env->set_sqlite_run_result_template(run_result_template);
2720
+ }
2721
+
2722
+ MaybeLocal<Value> values[] = {changes_val, last_insert_rowid_val};
2723
+ Local<Object> result;
2724
+ if (!NewDictionaryInstance(env->context(), run_result_template, values)
2725
+ .ToLocal(&result)) {
2430
2726
  return MaybeLocal<Object>();
2431
2727
  }
2432
2728
 
@@ -2521,7 +2817,7 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
2521
2817
  THROW_AND_RETURN_ON_BAD_STATE(
2522
2818
  env, stmt->IsFinalized(), "statement has been finalized");
2523
2819
  Isolate* isolate = env->isolate();
2524
- int r = sqlite3_reset(stmt->statement_);
2820
+ int r = stmt->ResetStatement();
2525
2821
  CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, void());
2526
2822
 
2527
2823
  if (!stmt->BindParams(args)) {
@@ -2547,7 +2843,7 @@ void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
2547
2843
  Environment* env = Environment::GetCurrent(args);
2548
2844
  THROW_AND_RETURN_ON_BAD_STATE(
2549
2845
  env, stmt->IsFinalized(), "statement has been finalized");
2550
- int r = sqlite3_reset(stmt->statement_);
2846
+ int r = stmt->ResetStatement();
2551
2847
  CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
2552
2848
 
2553
2849
  if (!stmt->BindParams(args)) {
@@ -2570,7 +2866,7 @@ void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
2570
2866
  Environment* env = Environment::GetCurrent(args);
2571
2867
  THROW_AND_RETURN_ON_BAD_STATE(
2572
2868
  env, stmt->IsFinalized(), "statement has been finalized");
2573
- int r = sqlite3_reset(stmt->statement_);
2869
+ int r = stmt->ResetStatement();
2574
2870
  CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
2575
2871
 
2576
2872
  if (!stmt->BindParams(args)) {
@@ -2594,7 +2890,7 @@ void StatementSync::Run(const FunctionCallbackInfo<Value>& args) {
2594
2890
  Environment* env = Environment::GetCurrent(args);
2595
2891
  THROW_AND_RETURN_ON_BAD_STATE(
2596
2892
  env, stmt->IsFinalized(), "statement has been finalized");
2597
- int r = sqlite3_reset(stmt->statement_);
2893
+ int r = stmt->ResetStatement();
2598
2894
  CHECK_ERROR_OR_THROW(env->isolate(), stmt->db_.get(), r, SQLITE_OK, void());
2599
2895
 
2600
2896
  if (!stmt->BindParams(args)) {
@@ -3139,6 +3435,7 @@ StatementSyncIterator::StatementSyncIterator(Environment* env,
3139
3435
  : BaseObject(env, object), stmt_(std::move(stmt)) {
3140
3436
  MakeWeak();
3141
3437
  done_ = false;
3438
+ statement_reset_generation_ = stmt_->reset_generation_;
3142
3439
  }
3143
3440
 
3144
3441
  StatementSyncIterator::~StatementSyncIterator() {}
@@ -3153,7 +3450,7 @@ Local<FunctionTemplate> StatementSyncIterator::GetConstructorTemplate(
3153
3450
  tmpl = NewFunctionTemplate(isolate, IllegalConstructor);
3154
3451
  tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "StatementSyncIterator"));
3155
3452
  tmpl->InstanceTemplate()->SetInternalFieldCount(
3156
- StatementSync::kInternalFieldCount);
3453
+ StatementSyncIterator::kInternalFieldCount);
3157
3454
  SetProtoMethod(isolate, tmpl, "next", StatementSyncIterator::Next);
3158
3455
  SetProtoMethod(isolate, tmpl, "return", StatementSyncIterator::Return);
3159
3456
  env->set_sqlite_statement_sync_iterator_constructor_template(tmpl);
@@ -3197,6 +3494,11 @@ void StatementSyncIterator::Next(const FunctionCallbackInfo<Value>& args) {
3197
3494
  return;
3198
3495
  }
3199
3496
 
3497
+ THROW_AND_RETURN_ON_BAD_STATE(
3498
+ env,
3499
+ iter->statement_reset_generation_ != iter->stmt_->reset_generation_,
3500
+ "iterator was invalidated");
3501
+
3200
3502
  int r = sqlite3_step(iter->stmt_->statement_);
3201
3503
  if (r != SQLITE_ROW) {
3202
3504
  CHECK_ERROR_OR_THROW(
@@ -3473,6 +3775,10 @@ static void Initialize(Local<Object> target,
3473
3775
  db_tmpl,
3474
3776
  FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
3475
3777
  DatabaseSync::IsTransactionGetter);
3778
+ SetSideEffectFreeGetter(isolate,
3779
+ db_tmpl,
3780
+ FIXED_ONE_BYTE_STRING(isolate, "limits"),
3781
+ DatabaseSync::LimitsGetter);
3476
3782
  Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
3477
3783
  Local<v8::Symbol> sqlite_type_symbol =
3478
3784
  v8::Symbol::For(isolate, sqlite_type_key);
@@ -9,13 +9,48 @@
9
9
  #include "sqlite3.h"
10
10
  #include "util.h"
11
11
 
12
+ #include <array>
12
13
  #include <list>
13
14
  #include <map>
15
+ #include <optional>
16
+ #include <string_view>
14
17
  #include <unordered_set>
15
18
 
16
19
  namespace node {
17
20
  namespace sqlite {
18
21
 
22
+ // Mapping from JavaScript property names to SQLite limit constants
23
+ struct LimitInfo {
24
+ std::string_view js_name;
25
+ int sqlite_limit_id;
26
+ };
27
+
28
+ inline constexpr std::array<LimitInfo, 11> kLimitMapping = {{
29
+ {"length", SQLITE_LIMIT_LENGTH},
30
+ {"sqlLength", SQLITE_LIMIT_SQL_LENGTH},
31
+ {"column", SQLITE_LIMIT_COLUMN},
32
+ {"exprDepth", SQLITE_LIMIT_EXPR_DEPTH},
33
+ {"compoundSelect", SQLITE_LIMIT_COMPOUND_SELECT},
34
+ {"vdbeOp", SQLITE_LIMIT_VDBE_OP},
35
+ {"functionArg", SQLITE_LIMIT_FUNCTION_ARG},
36
+ {"attach", SQLITE_LIMIT_ATTACHED},
37
+ {"likePatternLength", SQLITE_LIMIT_LIKE_PATTERN_LENGTH},
38
+ {"variableNumber", SQLITE_LIMIT_VARIABLE_NUMBER},
39
+ {"triggerDepth", SQLITE_LIMIT_TRIGGER_DEPTH},
40
+ }};
41
+
42
+ constexpr bool CheckLimitIndices() {
43
+ for (size_t i = 0; i < kLimitMapping.size(); ++i) {
44
+ if (kLimitMapping[i].sqlite_limit_id != static_cast<int>(i)) {
45
+ return false;
46
+ }
47
+ }
48
+ return true;
49
+ }
50
+ static_assert(
51
+ CheckLimitIndices(),
52
+ "Each kLimitMapping entry's sqlite_limit_id must match its index");
53
+
19
54
  class DatabaseOpenConfiguration {
20
55
  public:
21
56
  explicit DatabaseOpenConfiguration(std::string&& location)
@@ -69,6 +104,15 @@ class DatabaseOpenConfiguration {
69
104
 
70
105
  inline bool get_enable_defensive() const { return defensive_; }
71
106
 
107
+ inline void set_initial_limit(int sqlite_limit_id, int value) {
108
+ initial_limits_.at(sqlite_limit_id) = value;
109
+ }
110
+
111
+ inline const std::array<std::optional<int>, kLimitMapping.size()>&
112
+ initial_limits() const {
113
+ return initial_limits_;
114
+ }
115
+
72
116
  private:
73
117
  std::string location_;
74
118
  bool read_only_ = false;
@@ -80,9 +124,11 @@ class DatabaseOpenConfiguration {
80
124
  bool allow_bare_named_params_ = true;
81
125
  bool allow_unknown_named_params_ = false;
82
126
  bool defensive_ = true;
127
+ std::array<std::optional<int>, kLimitMapping.size()> initial_limits_{};
83
128
  };
84
129
 
85
130
  class DatabaseSync;
131
+ class DatabaseSyncLimits;
86
132
  class StatementSyncIterator;
87
133
  class StatementSync;
88
134
  class BackupJob;
@@ -118,6 +164,7 @@ class DatabaseSync : public BaseObject {
118
164
  public:
119
165
  enum InternalFields {
120
166
  kAuthorizerCallback = BaseObject::kInternalFieldCount,
167
+ kLimitsObject,
121
168
  kInternalFieldCount
122
169
  };
123
170
 
@@ -146,6 +193,7 @@ class DatabaseSync : public BaseObject {
146
193
  static void EnableLoadExtension(
147
194
  const v8::FunctionCallbackInfo<v8::Value>& args);
148
195
  static void EnableDefensive(const v8::FunctionCallbackInfo<v8::Value>& args);
196
+ static void LimitsGetter(const v8::FunctionCallbackInfo<v8::Value>& args);
149
197
  static void LoadExtension(const v8::FunctionCallbackInfo<v8::Value>& args);
150
198
  static void SetAuthorizer(const v8::FunctionCallbackInfo<v8::Value>& args);
151
199
  static int AuthorizerCallback(void* user_data,
@@ -195,6 +243,7 @@ class DatabaseSync : public BaseObject {
195
243
  std::set<sqlite3_session*> sessions_;
196
244
  std::unordered_set<StatementSync*> statements_;
197
245
 
246
+ friend class DatabaseSyncLimits;
198
247
  friend class Session;
199
248
  friend class SQLTagStore;
200
249
  friend class StatementExecutionHelper;
@@ -242,7 +291,9 @@ class StatementSync : public BaseObject {
242
291
  bool use_big_ints_;
243
292
  bool allow_bare_named_params_;
244
293
  bool allow_unknown_named_params_;
294
+ uint64_t reset_generation_ = 0;
245
295
  std::optional<std::map<std::string, std::string>> bare_named_params_;
296
+ inline int ResetStatement();
246
297
  bool BindParams(const v8::FunctionCallbackInfo<v8::Value>& args);
247
298
  bool BindValue(const v8::Local<v8::Value>& value, const int index);
248
299
 
@@ -272,6 +323,7 @@ class StatementSyncIterator : public BaseObject {
272
323
  ~StatementSyncIterator() override;
273
324
  BaseObjectPtr<StatementSync> stmt_;
274
325
  bool done_;
326
+ uint64_t statement_reset_generation_;
275
327
  };
276
328
 
277
329
  using Sqlite3ChangesetGenFunc = int (*)(sqlite3_session*, int*, void**);
@@ -356,6 +408,37 @@ class UserDefinedFunction {
356
408
  bool use_bigint_args_;
357
409
  };
358
410
 
411
+ class DatabaseSyncLimits : public BaseObject {
412
+ public:
413
+ DatabaseSyncLimits(Environment* env,
414
+ v8::Local<v8::Object> object,
415
+ BaseObjectWeakPtr<DatabaseSync> database);
416
+ ~DatabaseSyncLimits() override;
417
+
418
+ void MemoryInfo(MemoryTracker* tracker) const override;
419
+ static v8::Local<v8::ObjectTemplate> GetTemplate(Environment* env);
420
+ static BaseObjectPtr<DatabaseSyncLimits> Create(
421
+ Environment* env, BaseObjectWeakPtr<DatabaseSync> database);
422
+
423
+ static v8::Intercepted LimitsGetter(
424
+ v8::Local<v8::Name> property,
425
+ const v8::PropertyCallbackInfo<v8::Value>& info);
426
+ static v8::Intercepted LimitsSetter(
427
+ v8::Local<v8::Name> property,
428
+ v8::Local<v8::Value> value,
429
+ const v8::PropertyCallbackInfo<void>& info);
430
+ static v8::Intercepted LimitsQuery(
431
+ v8::Local<v8::Name> property,
432
+ const v8::PropertyCallbackInfo<v8::Integer>& info);
433
+ static void LimitsEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info);
434
+
435
+ SET_MEMORY_INFO_NAME(DatabaseSyncLimits)
436
+ SET_SELF_SIZE(DatabaseSyncLimits)
437
+
438
+ private:
439
+ BaseObjectWeakPtr<DatabaseSync> database_;
440
+ };
441
+
359
442
  } // namespace sqlite
360
443
  } // namespace node
361
444
 
@@ -1,6 +1,3 @@
1
1
  'use strict';
2
- const { emitExperimentalWarning } = require('internal/util');
3
-
4
- emitExperimentalWarning('SQLite');
5
2
 
6
3
  module.exports = internalBinding('sqlite');