@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.
- package/CHANGELOG.md +30 -1
- package/README.md +4 -8
- package/binding.gyp +2 -0
- package/dist/index.cjs +247 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +145 -24
- package/dist/index.d.mts +145 -24
- package/dist/index.d.ts +145 -24
- package/dist/index.mjs +247 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -11
- package/prebuilds/darwin-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/darwin-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-arm64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/linux-x64/@photostructure+sqlite.musl.node +0 -0
- package/prebuilds/test_extension.so +0 -0
- package/prebuilds/win32-arm64/@photostructure+sqlite.glibc.node +0 -0
- package/prebuilds/win32-x64/@photostructure+sqlite.glibc.node +0 -0
- package/src/enhance.ts +382 -55
- package/src/index.ts +85 -2
- package/src/sqlite_impl.cpp +133 -10
- package/src/sqlite_impl.h +19 -0
- package/src/types/database-sync-instance.ts +43 -0
- package/src/types/database-sync-options.ts +19 -0
- package/src/upstream/node_sqlite.cc +312 -17
- package/src/upstream/node_sqlite.h +80 -0
- package/src/upstream/sqlite.js +0 -3
- package/src/upstream/sqlite3.c +5027 -3518
- package/src/upstream/sqlite3.h +195 -58
- package/src/upstream/sqlite3ext.h +10 -1
|
@@ -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
|
|
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(
|
|
2270
|
-
|
|
2271
|
-
|
|
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 =
|
|
2277
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
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
|
|