@photostructure/sqlite 0.4.0 → 1.0.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());
@@ -2261,20 +2529,38 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
2261
2529
  // Dates could be supported by converting them to numbers. However, there
2262
2530
  // would not be a good way to read the values back from SQLite with the
2263
2531
  // original type.
2532
+ Isolate* isolate = env()->isolate();
2264
2533
  int r;
2265
2534
  if (value->IsNumber()) {
2266
- double val = value.As<Number>()->Value();
2535
+ const double val = value.As<Number>()->Value();
2267
2536
  r = sqlite3_bind_double(statement_, index, val);
2268
2537
  } 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);
2538
+ Utf8Value val(isolate, value.As<String>());
2539
+ if (val.IsAllocated()) {
2540
+ // Avoid an extra SQLite copy for large strings by transferring ownership
2541
+ // of the malloc()'d buffer to SQLite.
2542
+ char* data = *val;
2543
+ const sqlite3_uint64 length = static_cast<sqlite3_uint64>(val.length());
2544
+ val.Release();
2545
+ r = sqlite3_bind_text64(
2546
+ statement_, index, data, length, std::free, SQLITE_UTF8);
2547
+ } else {
2548
+ r = sqlite3_bind_text64(statement_,
2549
+ index,
2550
+ *val,
2551
+ static_cast<sqlite3_uint64>(val.length()),
2552
+ SQLITE_TRANSIENT,
2553
+ SQLITE_UTF8);
2554
+ }
2272
2555
  } else if (value->IsNull()) {
2273
2556
  r = sqlite3_bind_null(statement_, index);
2274
2557
  } else if (value->IsArrayBufferView()) {
2275
2558
  ArrayBufferViewContents<uint8_t> buf(value);
2276
- r = sqlite3_bind_blob(
2277
- statement_, index, buf.data(), buf.length(), SQLITE_TRANSIENT);
2559
+ r = sqlite3_bind_blob64(statement_,
2560
+ index,
2561
+ buf.data(),
2562
+ static_cast<sqlite3_uint64>(buf.length()),
2563
+ SQLITE_TRANSIENT);
2278
2564
  } else if (value->IsBigInt()) {
2279
2565
  bool lossless;
2280
2566
  int64_t as_int = value.As<BigInt>()->Int64Value(&lossless);
@@ -2285,13 +2571,13 @@ bool StatementSync::BindValue(const Local<Value>& value, const int index) {
2285
2571
  r = sqlite3_bind_int64(statement_, index, as_int);
2286
2572
  } else {
2287
2573
  THROW_ERR_INVALID_ARG_TYPE(
2288
- env()->isolate(),
2574
+ isolate,
2289
2575
  "Provided value cannot be bound to SQLite parameter %d.",
2290
2576
  index);
2291
2577
  return false;
2292
2578
  }
2293
2579
 
2294
- CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
2580
+ CHECK_ERROR_OR_THROW(isolate, db_.get(), r, SQLITE_OK, false);
2295
2581
  return true;
2296
2582
  }
2297
2583
 
@@ -2406,7 +2692,7 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
2406
2692
  sqlite3_step(stmt);
2407
2693
  int r = sqlite3_reset(stmt);
2408
2694
  CHECK_ERROR_OR_THROW(isolate, db, r, SQLITE_OK, MaybeLocal<Object>());
2409
- Local<Object> result = Object::New(isolate);
2695
+
2410
2696
  sqlite3_int64 last_insert_rowid = sqlite3_last_insert_rowid(db->Connection());
2411
2697
  sqlite3_int64 changes = sqlite3_changes64(db->Connection());
2412
2698
  Local<Value> last_insert_rowid_val;
@@ -2420,13 +2706,18 @@ MaybeLocal<Object> StatementExecutionHelper::Run(Environment* env,
2420
2706
  changes_val = Number::New(isolate, changes);
2421
2707
  }
2422
2708
 
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()) {
2709
+ auto run_result_template = env->sqlite_run_result_template();
2710
+ if (run_result_template.IsEmpty()) {
2711
+ static constexpr std::string_view run_result_keys[] = {"changes",
2712
+ "lastInsertRowid"};
2713
+ run_result_template = DictionaryTemplate::New(isolate, run_result_keys);
2714
+ env->set_sqlite_run_result_template(run_result_template);
2715
+ }
2716
+
2717
+ MaybeLocal<Value> values[] = {changes_val, last_insert_rowid_val};
2718
+ Local<Object> result;
2719
+ if (!NewDictionaryInstance(env->context(), run_result_template, values)
2720
+ .ToLocal(&result)) {
2430
2721
  return MaybeLocal<Object>();
2431
2722
  }
2432
2723
 
@@ -3473,6 +3764,10 @@ static void Initialize(Local<Object> target,
3473
3764
  db_tmpl,
3474
3765
  FIXED_ONE_BYTE_STRING(isolate, "isTransaction"),
3475
3766
  DatabaseSync::IsTransactionGetter);
3767
+ SetSideEffectFreeGetter(isolate,
3768
+ db_tmpl,
3769
+ FIXED_ONE_BYTE_STRING(isolate, "limits"),
3770
+ DatabaseSync::LimitsGetter);
3476
3771
  Local<String> sqlite_type_key = FIXED_ONE_BYTE_STRING(isolate, "sqlite-type");
3477
3772
  Local<v8::Symbol> sqlite_type_symbol =
3478
3773
  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;
@@ -356,6 +405,37 @@ class UserDefinedFunction {
356
405
  bool use_bigint_args_;
357
406
  };
358
407
 
408
+ class DatabaseSyncLimits : public BaseObject {
409
+ public:
410
+ DatabaseSyncLimits(Environment* env,
411
+ v8::Local<v8::Object> object,
412
+ BaseObjectWeakPtr<DatabaseSync> database);
413
+ ~DatabaseSyncLimits() override;
414
+
415
+ void MemoryInfo(MemoryTracker* tracker) const override;
416
+ static v8::Local<v8::ObjectTemplate> GetTemplate(Environment* env);
417
+ static BaseObjectPtr<DatabaseSyncLimits> Create(
418
+ Environment* env, BaseObjectWeakPtr<DatabaseSync> database);
419
+
420
+ static v8::Intercepted LimitsGetter(
421
+ v8::Local<v8::Name> property,
422
+ const v8::PropertyCallbackInfo<v8::Value>& info);
423
+ static v8::Intercepted LimitsSetter(
424
+ v8::Local<v8::Name> property,
425
+ v8::Local<v8::Value> value,
426
+ const v8::PropertyCallbackInfo<void>& info);
427
+ static v8::Intercepted LimitsQuery(
428
+ v8::Local<v8::Name> property,
429
+ const v8::PropertyCallbackInfo<v8::Integer>& info);
430
+ static void LimitsEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info);
431
+
432
+ SET_MEMORY_INFO_NAME(DatabaseSyncLimits)
433
+ SET_SELF_SIZE(DatabaseSyncLimits)
434
+
435
+ private:
436
+ BaseObjectWeakPtr<DatabaseSync> database_;
437
+ };
438
+
359
439
  } // namespace sqlite
360
440
  } // namespace node
361
441
 
@@ -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');