@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.
- package/CHANGELOG.md +22 -0
- package/README.md +4 -2
- package/binding.gyp +2 -0
- package/dist/index.cjs +65 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -1
- package/dist/index.d.mts +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.mjs +65 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
- 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 +7 -4
- package/src/index.ts +84 -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 +328 -22
- package/src/upstream/node_sqlite.h +83 -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());
|
|
@@ -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(
|
|
2270
|
-
|
|
2271
|
-
|
|
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 =
|
|
2277
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|