Skip to content

Commit

Permalink
sqlite: Observer database changes through WAL hook
Browse files Browse the repository at this point in the history
  • Loading branch information
skabbes committed Mar 9, 2015
1 parent dc80cdc commit ebf1b96
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 60 deletions.
4 changes: 4 additions & 0 deletions djinni/view_model.djinni
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ user_list_vm = interface +c {
count(): i32;
# get the data for an individual user
get(index: i32): optional<user_list_vm_cell>;

# Delete a row from the view model. This view model will still remain immutable but a
# new view model will be sent through the observer interface.
delete_row(index: i32);
}

user_list_vm_observer = interface +o +j {
Expand Down
116 changes: 116 additions & 0 deletions example_ios/MX3/MX3.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,55 @@
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
B024F7A71AAD5BFA008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F0C771AE192A79CC006469AB /* mx3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = E7E6BC8A67A80E09A179E565;
remoteInfo = libmx3_android;
};
B024F7AF1AAD5BFA008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 92E822B5EA37A89C8AAD9BB4;
remoteInfo = libmx3;
};
B024F7B11AAD5BFA008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 39EA06E18557EEFB64C5347F;
remoteInfo = libmx3_objc;
};
B024F7B31AAD5BFA008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = E7E6BC8A67A80E09A179E565;
remoteInfo = libmx3_android;
};
B024F7B51AAD5BFA008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 6FCBE719222158C1FBFFBADD;
remoteInfo = play_objc;
};
B024F7B71AAD5BFA008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8E5AC5F54A231F301800298F;
remoteInfo = test;
};
B024F7B91AAD5C01008B8BB9 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F0C771AE192A79CC006469AB /* mx3.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 05EB53E0AEA2C8B114F5F0A6;
remoteInfo = libmx3;
};
B0F70B2E1A222B2200E36720 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F0C771AE192A79CC006469AB /* mx3.xcodeproj */;
Expand Down Expand Up @@ -72,6 +121,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = mx3.xcodeproj; path = ../../build_ios/mx3.xcodeproj; sourceTree = "<group>"; };
B02CAAE8192CFB0C007D3B2F /* libCppSQLite.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCppSQLite.a; path = "../../deps/build/Debug-iphoneos/libCppSQLite.a"; sourceTree = "<group>"; };
B09B174E1A1CD61100C8D6A7 /* libdjinni_objc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libdjinni_objc.a; path = "../../deps/djinni/support-lib/build/Debug-iphoneos/libdjinni_objc.a"; sourceTree = "<group>"; };
F0C7716F192A7821006469AB /* MX3.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MX3.app; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -130,9 +180,22 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
B024F7A01AAD5BFA008B8BB9 /* Products */ = {
isa = PBXGroup;
children = (
B024F7B01AAD5BFA008B8BB9 /* libmx3.a */,
B024F7B21AAD5BFA008B8BB9 /* libmx3_objc.a */,
B024F7B41AAD5BFA008B8BB9 /* libmx3_android.dylib */,
B024F7B61AAD5BFA008B8BB9 /* play_objc */,
B024F7B81AAD5BFA008B8BB9 /* test */,
);
name = Products;
sourceTree = "<group>";
};
F0C77166192A7821006469AB = {
isa = PBXGroup;
children = (
B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */,
F0C77178192A7821006469AB /* MX3 */,
F0C77191192A7821006469AB /* MX3Tests */,
F0C77171192A7821006469AB /* Frameworks */,
Expand Down Expand Up @@ -215,6 +278,7 @@
children = (
F0C771BC192A79CC006469AB /* libmx3.a */,
F0C771BE192A79CC006469AB /* libmx3_objc.a */,
B024F7A81AAD5BFA008B8BB9 /* libmx3_android.dylib */,
F0C771C0192A79CC006469AB /* play_objc */,
F0C771C4192A79CC006469AB /* test */,
);
Expand All @@ -235,6 +299,7 @@
buildRules = (
);
dependencies = (
B024F7BA1AAD5C01008B8BB9 /* PBXTargetDependency */,
B0F70B2F1A222B2200E36720 /* PBXTargetDependency */,
);
name = MX3;
Expand Down Expand Up @@ -289,6 +354,10 @@
ProductGroup = F0C771AF192A79CC006469AB /* Products */;
ProjectRef = F0C771AE192A79CC006469AB /* mx3.xcodeproj */;
},
{
ProductGroup = B024F7A01AAD5BFA008B8BB9 /* Products */;
ProjectRef = B024F79F1AAD5BFA008B8BB9 /* mx3.xcodeproj */;
},
);
projectRoot = "";
targets = (
Expand All @@ -299,6 +368,48 @@
/* End PBXProject section */

/* Begin PBXReferenceProxy section */
B024F7A81AAD5BFA008B8BB9 /* libmx3_android.dylib */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.dylib";
path = libmx3_android.dylib;
remoteRef = B024F7A71AAD5BFA008B8BB9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B024F7B01AAD5BFA008B8BB9 /* libmx3.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libmx3.a;
remoteRef = B024F7AF1AAD5BFA008B8BB9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B024F7B21AAD5BFA008B8BB9 /* libmx3_objc.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libmx3_objc.a;
remoteRef = B024F7B11AAD5BFA008B8BB9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B024F7B41AAD5BFA008B8BB9 /* libmx3_android.dylib */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.dylib";
path = libmx3_android.dylib;
remoteRef = B024F7B31AAD5BFA008B8BB9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B024F7B61AAD5BFA008B8BB9 /* play_objc */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.executable";
path = play_objc;
remoteRef = B024F7B51AAD5BFA008B8BB9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B024F7B81AAD5BFA008B8BB9 /* test */ = {
isa = PBXReferenceProxy;
fileType = "compiled.mach-o.executable";
path = test;
remoteRef = B024F7B71AAD5BFA008B8BB9 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
F0C771BC192A79CC006469AB /* libmx3.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
Expand Down Expand Up @@ -369,6 +480,11 @@
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
B024F7BA1AAD5C01008B8BB9 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = libmx3;
targetProxy = B024F7B91AAD5C01008B8BB9 /* PBXContainerItemProxy */;
};
B0F70B2F1A222B2200E36720 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = libmx3_objc;
Expand Down
6 changes: 5 additions & 1 deletion example_ios/MX3/MX3/MXSampleDataTableViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ - (void)onUpdate:(NSMutableArray *)changes newData:(id <MX3UserListVm>)newData {
}
}

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.viewModel deleteRow: (int32_t)indexPath.row];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.viewModel != nil ? [self.viewModel count] : 0;
}
Expand All @@ -73,7 +77,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];

MX3UserListVmCell * cellData = [self.viewModel get:indexPath.row];
MX3UserListVmCell * cellData = [self.viewModel get:(int32_t)indexPath.row];
cell.textLabel.text = [cellData name];
cell.detailTextLabel.text = @"If you manage to get the deps right";
return cell;
Expand Down
1 change: 1 addition & 0 deletions src/api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,5 @@ Api::_setup_db() {
for (const auto& cmd : setup_commands) {
m_sqlite->exec(cmd);
}
m_sqlite->enable_wal();
}
2 changes: 1 addition & 1 deletion src/sqlite/db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ Db::wal_hook(const WalHookFn& wal_fn) {

std::pair<int, int>
Db::wal_checkpoint_v2(const optional<string>& db_name, Checkpoint mode) {
int sqlite_mode;
int sqlite_mode = 0;
switch (mode) {
case Checkpoint::PASSIVE:
sqlite_mode = SQLITE_CHECKPOINT_PASSIVE;
Expand Down
2 changes: 2 additions & 0 deletions src/sqlite/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class Value final {
};
};

using Row = vector<Value>;

// a comparison operator that also compares double and int values correctly
bool operator<(const Value& l, const Value& r);
std::ostream& operator <<(std::ostream& os, const mx3::sqlite::Value& v);
Expand Down
1 change: 0 additions & 1 deletion src/sqlite_query/query_diff.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include "query_diff.hpp"
#include <algorithm>

namespace mx3 { namespace sqlite {

Expand Down
3 changes: 1 addition & 2 deletions src/sqlite_query/query_diff.hpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#pragma once
#include "stl.hpp"
#include "../sqlite/value.hpp"
#include <algorithm>

namespace mx3 {
namespace sqlite {

using Row = vector<Value>;

/* A representation of a change between 2 different row-based sets of data.
*/
struct ListChange final {
Expand Down
65 changes: 65 additions & 0 deletions src/sqlite_query/query_monitor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "query_monitor.hpp"

namespace mx3 {
namespace sqlite {

class QueryMonitor::only_for_internal_use_by_make_shared_t {};

shared_ptr<QueryMonitor> QueryMonitor::create_shared(const shared_ptr<Db> & write_db) {
return make_shared<QueryMonitor>(only_for_internal_use_by_make_shared_t{}, write_db);
}

QueryMonitor::QueryMonitor(
only_for_internal_use_by_make_shared_t,
const shared_ptr<Db> & write_db) : m_write_db(write_db)
{
_install_hooks();
}


QueryMonitor::~QueryMonitor() {
m_write_db->update_hook(nullptr);
m_write_db->commit_hook(nullptr);
m_write_db->rollback_hook(nullptr);
m_write_db->wal_hook(nullptr);
}

void QueryMonitor::listen_to_changes(const function<void()> & fn) {
m_listeners.push_back(fn);
}

void QueryMonitor::_install_hooks() {
m_write_db->update_hook([this] (Db::Change change) {
this->_on_update(std::move(change));
});
m_write_db->commit_hook([this] () {
return this->_on_commit();
});
m_write_db->rollback_hook([this] () {
this->_on_rollback();
});
m_write_db->wal_hook([this] (const string& db_name, int pages) {
this->_on_wal_commit(db_name, pages);
});
}


void QueryMonitor::_on_update(Db::Change) {}
bool QueryMonitor::_on_commit() {
return true;
}
void QueryMonitor::_on_rollback() {}
bool QueryMonitor::_on_wal_commit(const string& db_name, int pages) {
if (pages > 1000) {
m_write_db->wal_checkpoint_v2(db_name, Checkpoint::PASSIVE);
}
// Grab size before iterating in case m_listeners is modified in a callback.
// In actuality this should be posted to an event loop, but oh well - its only a demo app
const size_t total = m_listeners.size();
for (size_t i=0; i < total; i++) {
m_listeners[i]();
}
return true;
}

} }
30 changes: 30 additions & 0 deletions src/sqlite_query/query_monitor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once
#include "../sqlite/sqlite.hpp"

namespace mx3 {
namespace sqlite {

/* This class listens to changes in WAL mode databases.
* And will notify listeners when changes are made.
*/
class QueryMonitor final {
public:
static shared_ptr<QueryMonitor> create_shared(const shared_ptr<Db> & write_db);
~QueryMonitor();
void listen_to_changes(const function<void()> & fn);
private:
class only_for_internal_use_by_make_shared_t;
public:
// make_shared constructor
QueryMonitor(only_for_internal_use_by_make_shared_t flag, const shared_ptr<Db> & write_db);
private:
void _install_hooks();
void _on_update(Db::Change);
bool _on_commit();
void _on_rollback();
bool _on_wal_commit(const string& db_name, int pages);
const shared_ptr<Db> m_write_db;
vector<function<void()>> m_listeners;
};

} }
Loading

0 comments on commit ebf1b96

Please sign in to comment.