@@ -201,6 +201,30 @@ void JSValueToSQLiteResult(Isolate* isolate,
201
201
}
202
202
}
203
203
204
+ inline int CREATE_INTERNAL_SAVEPOINT (sqlite3* db, const char * name) {
205
+ const std::string command =
206
+ std::string (" SAVEPOINT __nodesqlite_" ) + name;
207
+ return sqlite3_exec (db, command.c_str (), nullptr , nullptr , nullptr );
208
+ }
209
+
210
+ inline int RELEASE_INTERNAL_SAVEPOINT_ON_SUCCESS (sqlite3* db,
211
+ const char * name) {
212
+ const std::string command =
213
+ std::string (" RELEASE SAVEPOINT __nodesqlite_" ) + name;
214
+ return sqlite3_exec (db, command.c_str (), nullptr , nullptr , nullptr );
215
+ }
216
+
217
+ inline int RELEASE_INTERNAL_SAVEPOINT_ON_FAILURE (sqlite3* db,
218
+ const char * name) {
219
+ const std::string command =
220
+ std::string (" ROLLBACK TRANSACTION TO SAVEPOINT __nodesqlite_" ) + name;
221
+ int r = sqlite3_exec (db, command.c_str (), nullptr , nullptr , nullptr );
222
+ if (r != SQLITE_OK) {
223
+ return r;
224
+ }
225
+ return RELEASE_INTERNAL_SAVEPOINT_ON_SUCCESS (db, name);
226
+ }
227
+
204
228
class DatabaseSync ;
205
229
206
230
inline void THROW_ERR_SQLITE_ERROR (Isolate* isolate, DatabaseSync* db) {
@@ -1541,6 +1565,8 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
1541
1565
conflictCallback = nullptr ;
1542
1566
filterCallback = nullptr ;
1543
1567
1568
+ bool filterCallbackThrew = false ;
1569
+
1544
1570
DatabaseSync* db;
1545
1571
ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
1546
1572
Environment* env = Environment::GetCurrent (args);
@@ -1577,12 +1603,10 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
1577
1603
Local<Function> conflictFunc = conflictValue.As <Function>();
1578
1604
conflictCallback = [env, conflictFunc](int conflictType) -> int {
1579
1605
Local<Value> argv[] = {Integer::New (env->isolate (), conflictType)};
1580
- TryCatch try_catch (env->isolate ());
1581
- Local<Value> result =
1582
- conflictFunc->Call (env->context (), Null (env->isolate ()), 1 , argv)
1583
- .FromMaybe (Local<Value>());
1584
- if (try_catch.HasCaught ()) {
1585
- try_catch.ReThrow ();
1606
+ Local<Value> result;
1607
+ if (!conflictFunc->Call (env->context (), Null (env->isolate ()), 1 , argv)
1608
+ .ToLocal (&result)) {
1609
+ // An error will have been scheduled.
1586
1610
return SQLITE_CHANGESET_ABORT;
1587
1611
}
1588
1612
constexpr auto invalid_value = -1 ;
@@ -1591,19 +1615,13 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
1591
1615
};
1592
1616
}
1593
1617
1594
- bool hasIt;
1595
- if (!options->HasOwnProperty (env->context (), env->filter_string ())
1596
- .To (&hasIt)) {
1618
+ Local<Value> filterValue;
1619
+ if (!options->Get (env->context (), env->filter_string ())
1620
+ .ToLocal (&filterValue)) {
1621
+ // An error will have been scheduled.
1597
1622
return ;
1598
1623
}
1599
- if (hasIt) {
1600
- Local<Value> filterValue;
1601
- if (!options->Get (env->context (), env->filter_string ())
1602
- .ToLocal (&filterValue)) {
1603
- // An error will have been scheduled.
1604
- return ;
1605
- }
1606
-
1624
+ if (!filterValue->IsUndefined ()) {
1607
1625
if (!filterValue->IsFunction ()) {
1608
1626
THROW_ERR_INVALID_ARG_TYPE (
1609
1627
env->isolate (),
@@ -1613,23 +1631,47 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
1613
1631
1614
1632
Local<Function> filterFunc = filterValue.As <Function>();
1615
1633
1616
- filterCallback = [env, filterFunc](std::string item) -> bool {
1617
- // TODO(@jasnell): The use of ToLocalChecked here means that if
1618
- // the filter function throws an error the process will crash.
1619
- // The filterCallback should be updated to avoid the check and
1620
- // propagate the error correctly.
1621
- Local<Value> argv[] = {String::NewFromUtf8 (env->isolate (),
1622
- item.c_str (),
1623
- NewStringType::kNormal )
1624
- .ToLocalChecked ()};
1625
- Local<Value> result =
1626
- filterFunc->Call (env->context (), Null (env->isolate ()), 1 , argv)
1627
- .ToLocalChecked ();
1634
+ filterCallback =
1635
+ [env, &filterCallbackThrew, filterFunc](std::string item) -> bool {
1636
+ if (filterCallbackThrew) {
1637
+ // Do not call the user function again if a previous call already
1638
+ // threw an exception.
1639
+ return false ;
1640
+ }
1641
+
1642
+ Local<Value> tableName;
1643
+ if (!String::NewFromUtf8 (env->isolate (),
1644
+ item.c_str (),
1645
+ NewStringType::kNormal )
1646
+ .ToLocal (&tableName)) {
1647
+ filterCallbackThrew = true ;
1648
+ return false ;
1649
+ }
1650
+
1651
+ Local<Value> result;
1652
+ if (!filterFunc->Call (env->context (),
1653
+ Null (env->isolate ()),
1654
+ 1 ,
1655
+ &tableName)
1656
+ .ToLocal (&result)) {
1657
+ filterCallbackThrew = true ;
1658
+ return false ;
1659
+ }
1660
+
1628
1661
return result->BooleanValue (env->isolate ());
1629
1662
};
1630
1663
}
1631
1664
}
1632
1665
1666
+ constexpr const char * savepoint_name = " applychangeset" ;
1667
+ int create_savepoint_ret =
1668
+ CREATE_INTERNAL_SAVEPOINT (db->connection_ , savepoint_name);
1669
+ CHECK_ERROR_OR_THROW (env->isolate (),
1670
+ db,
1671
+ create_savepoint_ret,
1672
+ SQLITE_OK,
1673
+ void ());
1674
+
1633
1675
ArrayBufferViewContents<uint8_t > buf (args[0 ]);
1634
1676
int r = sqlite3changeset_apply (
1635
1677
db->connection_ ,
@@ -1638,15 +1680,24 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo<Value>& args) {
1638
1680
xFilter,
1639
1681
xConflict,
1640
1682
nullptr );
1683
+
1684
+ if (filterCallbackThrew) {
1685
+ RELEASE_INTERNAL_SAVEPOINT_ON_FAILURE (db->connection_ , savepoint_name);
1686
+ return ;
1687
+ }
1641
1688
if (r == SQLITE_OK) {
1689
+ RELEASE_INTERNAL_SAVEPOINT_ON_SUCCESS (db->connection_ , savepoint_name);
1642
1690
args.GetReturnValue ().Set (true );
1643
1691
return ;
1644
1692
}
1645
1693
if (r == SQLITE_ABORT) {
1646
1694
// this is not an error, return false
1695
+ RELEASE_INTERNAL_SAVEPOINT_ON_SUCCESS (db->connection_ , savepoint_name);
1647
1696
args.GetReturnValue ().Set (false );
1648
1697
return ;
1649
1698
}
1699
+
1700
+ RELEASE_INTERNAL_SAVEPOINT_ON_FAILURE (db->connection_ , savepoint_name);
1650
1701
THROW_ERR_SQLITE_ERROR (env->isolate (), r);
1651
1702
}
1652
1703
0 commit comments