diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h @@ -81,12 +81,18 @@ virtual void beginTransaction() const = 0; virtual void commitTransaction() const = 0; virtual void rollbackTransaction() const = 0; + virtual int getContentAccountID() const = 0; + virtual int getNotifsAccountID() const = 0; virtual std::vector getOlmPersistSessionsData() const = 0; - virtual std::optional getOlmPersistAccountData() const = 0; + virtual std::optional + getOlmPersistAccountData(int accountID) const = 0; virtual void storeOlmPersistSession(const OlmPersistSession &session) const = 0; - virtual void storeOlmPersistAccount(const std::string &accountData) const = 0; - virtual void storeOlmPersistData(crypto::Persist persist) const = 0; + virtual void storeOlmPersistAccount( + int accountID, + const std::string &accountData) const = 0; + virtual void + storeOlmPersistData(int accountID, crypto::Persist persist) const = 0; virtual void setNotifyToken(std::string token) const = 0; virtual void clearNotifyToken() const = 0; virtual void setCurrentUserID(std::string userID) const = 0; @@ -113,7 +119,7 @@ virtual void replaceThreadWeb(const WebThread &thread) const = 0; virtual std::vector getAllMessagesWeb() const = 0; virtual void replaceMessageWeb(const WebMessage &message) const = 0; - virtual NullableString getOlmPersistAccountDataWeb() const = 0; + virtual NullableString getOlmPersistAccountDataWeb(int accountID) const = 0; #else virtual void createMainCompaction(std::string backupID) const = 0; virtual void captureBackupLogs() const = 0; diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h @@ -95,11 +95,16 @@ void beginTransaction() const override; void commitTransaction() const override; void rollbackTransaction() const override; + int getContentAccountID() const override; + int getNotifsAccountID() const override; std::vector getOlmPersistSessionsData() const override; - std::optional getOlmPersistAccountData() const override; + std::optional + getOlmPersistAccountData(int accountID) const override; void storeOlmPersistSession(const OlmPersistSession &session) const override; - void storeOlmPersistAccount(const std::string &accountData) const override; - void storeOlmPersistData(crypto::Persist persist) const override; + void storeOlmPersistAccount(int accountID, const std::string &accountData) + const override; + void + storeOlmPersistData(int accountID, crypto::Persist persist) const override; void setNotifyToken(std::string token) const override; void clearNotifyToken() const override; void setCurrentUserID(std::string userID) const override; @@ -125,7 +130,7 @@ void replaceThreadWeb(const WebThread &thread) const override; std::vector getAllMessagesWeb() const override; void replaceMessageWeb(const WebMessage &message) const override; - NullableString getOlmPersistAccountDataWeb() const override; + NullableString getOlmPersistAccountDataWeb(int accountID) const override; #else static void clearSensitiveData(); static void initialize(std::string &databasePath); diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp --- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp +++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp @@ -16,7 +16,8 @@ #include "StaffUtils.h" #endif -#define ACCOUNT_ID 1 +const int CONTENT_ACCOUNT_ID = 1; +const int NOTIFS_ACCOUNT_ID = 2; namespace comm { @@ -1579,6 +1580,14 @@ executeQuery(SQLiteQueryExecutor::getConnection(), "ROLLBACK;"); } +int SQLiteQueryExecutor::getContentAccountID() const { + return CONTENT_ACCOUNT_ID; +} + +int SQLiteQueryExecutor::getNotifsAccountID() const { + return NOTIFS_ACCOUNT_ID; +} + std::vector SQLiteQueryExecutor::getOlmPersistSessionsData() const { static std::string getAllOlmPersistSessionsSQL = @@ -1589,30 +1598,30 @@ } std::optional -SQLiteQueryExecutor::getOlmPersistAccountData() const { - static std::string getAllOlmPersistAccountSQL = +SQLiteQueryExecutor::getOlmPersistAccountData(int accountID) const { + static std::string getOlmPersistAccountSQL = "SELECT * " - "FROM olm_persist_account;"; - std::vector result = getAllEntities( - SQLiteQueryExecutor::getConnection(), getAllOlmPersistAccountSQL); - if (result.size() > 1) { - throw std::system_error( - ECANCELED, - std::generic_category(), - "Multiple records found for the olm_persist_account table"); + "FROM olm_persist_account " + "WHERE id = ?;"; + std::unique_ptr result = + getEntityByIntegerPrimaryKey( + SQLiteQueryExecutor::getConnection(), + getOlmPersistAccountSQL, + accountID); + if (result == nullptr) { + return std::nullopt; } - return (result.size() == 0) - ? std::nullopt - : std::optional(result[0].account_data); + return result->account_data; } void SQLiteQueryExecutor::storeOlmPersistAccount( + int accountID, const std::string &accountData) const { static std::string replaceOlmPersistAccountSQL = "REPLACE INTO olm_persist_account (id, account_data) " "VALUES (?, ?);"; - OlmPersistAccount persistAccount = {ACCOUNT_ID, accountData}; + OlmPersistAccount persistAccount = {accountID, accountData}; replaceEntity( SQLiteQueryExecutor::getConnection(), @@ -1632,10 +1641,19 @@ session); } -void SQLiteQueryExecutor::storeOlmPersistData(crypto::Persist persist) const { +void SQLiteQueryExecutor::storeOlmPersistData( + int accountID, + crypto::Persist persist) const { + + if (accountID != CONTENT_ACCOUNT_ID && persist.sessions.size() > 0) { + throw std::runtime_error( + "Attempt to store notifications sessions in SQLite. Notifications " + "sessions must be stored in storage shared with NSE."); + } + std::string accountData = std::string(persist.account.begin(), persist.account.end()); - this->storeOlmPersistAccount(accountData); + this->storeOlmPersistAccount(accountID, accountData); for (auto it = persist.sessions.begin(); it != persist.sessions.end(); it++) { OlmPersistSession persistSession = { @@ -1803,8 +1821,10 @@ this->replaceMessage(message.toMessage()); }; -NullableString SQLiteQueryExecutor::getOlmPersistAccountDataWeb() const { - std::optional accountData = this->getOlmPersistAccountData(); +NullableString +SQLiteQueryExecutor::getOlmPersistAccountDataWeb(int accountID) const { + std::optional accountData = + this->getOlmPersistAccountData(accountID); if (!accountData.has_value()) { return NullableString(); } diff --git a/native/cpp/CommonCpp/DatabaseManagers/entities/EntityQueryHelpers.h b/native/cpp/CommonCpp/DatabaseManagers/entities/EntityQueryHelpers.h --- a/native/cpp/CommonCpp/DatabaseManagers/entities/EntityQueryHelpers.h +++ b/native/cpp/CommonCpp/DatabaseManagers/entities/EntityQueryHelpers.h @@ -21,6 +21,25 @@ return allEntities; } +template +std::unique_ptr +getEntityByPrimaryKeyCommon(SQLiteStatementWrapper &preparedSQL) { + int stepResult = sqlite3_step(preparedSQL); + if (stepResult == SQLITE_DONE) { + return nullptr; + } + + if (stepResult != SQLITE_ROW) { + std::stringstream error_message; + error_message << "Failed to fetch row by primary key. Details: " + << sqlite3_errstr(stepResult) << std::endl; + throw std::runtime_error(error_message.str()); + } + + T entity = T::fromSQLResult(preparedSQL, 0); + return std::make_unique(std::move(entity)); +} + template std::unique_ptr getEntityByPrimaryKey( sqlite3 *db, @@ -36,20 +55,24 @@ throw std::runtime_error(error_message.str()); } - int stepResult = sqlite3_step(preparedSQL); - if (stepResult == SQLITE_DONE) { - return nullptr; - } + return getEntityByPrimaryKeyCommon(preparedSQL); +} - if (stepResult != SQLITE_ROW) { +template +std::unique_ptr getEntityByIntegerPrimaryKey( + sqlite3 *db, + std::string getEntityByPrimaryKeySQL, + int primaryKey) { + SQLiteStatementWrapper preparedSQL( + db, getEntityByPrimaryKeySQL, "Failed to fetch row by primary key."); + int bindResult = bindIntToSQL(primaryKey, preparedSQL, 1); + if (bindResult != SQLITE_OK) { std::stringstream error_message; - error_message << "Failed to fetch row by primary key. Details: " - << sqlite3_errstr(stepResult) << std::endl; + error_message << "Failed to bind primary key to SQL statement. Details: " + << sqlite3_errstr(bindResult) << std::endl; throw std::runtime_error(error_message.str()); } - - T entity = T::fromSQLResult(preparedSQL, 0); - return std::make_unique(std::move(entity)); + return getEntityByPrimaryKeyCommon(preparedSQL); } template diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp @@ -350,7 +350,9 @@ GlobalDBSingleton::instance.scheduleOrRunCancellable( [=, &persistencePromise]() { try { - DatabaseManager::getQueryExecutor().storeOlmPersistData(newPersist); + DatabaseManager::getQueryExecutor().storeOlmPersistData( + DatabaseManager::getQueryExecutor().getContentAccountID(), + newPersist); persistencePromise.set_value(); } catch (std::system_error &e) { persistencePromise.set_exception(std::make_exception_ptr(e)); @@ -375,7 +377,8 @@ std::string error; try { std::optional accountData = - DatabaseManager::getQueryExecutor().getOlmPersistAccountData(); + DatabaseManager::getQueryExecutor().getOlmPersistAccountData( + DatabaseManager::getQueryExecutor().getContentAccountID()); if (accountData.has_value()) { persist.account = crypto::OlmBuffer(accountData->begin(), accountData->end()); @@ -407,6 +410,8 @@ std::string error; try { DatabaseManager::getQueryExecutor().storeOlmPersistData( + DatabaseManager::getQueryExecutor() + .getContentAccountID(), newPersist); } catch (std::system_error &e) { error = e.what(); diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp --- a/web/cpp/SQLiteQueryExecutorBindings.cpp +++ b/web/cpp/SQLiteQueryExecutorBindings.cpp @@ -177,6 +177,9 @@ .function("getAllCommunities", &SQLiteQueryExecutor::getAllCommunities) .function("beginTransaction", &SQLiteQueryExecutor::beginTransaction) .function("commitTransaction", &SQLiteQueryExecutor::commitTransaction) + .function( + "getContentAccountID", &SQLiteQueryExecutor::getContentAccountID) + .function("getNotifsAccountID", &SQLiteQueryExecutor::getNotifsAccountID) .function( "getOlmPersistSessionsData", &SQLiteQueryExecutor::getOlmPersistSessionsData) diff --git a/web/database/_generated/comm-query-executor.js b/web/database/_generated/comm-query-executor.js --- a/web/database/_generated/comm-query-executor.js +++ b/web/database/_generated/comm-query-executor.js @@ -116,7 +116,7 @@ V.prototype.xd=function(a){this.Tc&&(a=this.Tc(a));return a};V.prototype.Hc=function(a){this.pb&&this.pb(a)};V.prototype.argPackAdvance=8;V.prototype.readValueFromPointer=lb;V.prototype.deleteObject=function(a){if(null!==a)a["delete"]()}; V.prototype.fromWireType=function(a){function b(){return this.Vb?Jb(this.Ta.Eb,{Xa:this.Md,Sa:c,fb:this,ab:a}):Jb(this.Ta.Eb,{Xa:this,Sa:a})}var c=this.xd(a);if(!c)return this.Hc(a),null;var d=Ib(this.Ta,c);if(void 0!==d){if(0===d.Na.count.value)return d.Na.Sa=c,d.Na.ab=a,d.clone();d=d.clone();this.Hc(a);return d}d=this.Ta.vd(c);d=Db[d];if(!d)return b.call(this);d=this.Tb?d.jd:d.pointerType;var f=Cb(c,this.Ta,d.Ta);return null===f?b.call(this):this.Vb?Jb(d.Ta.Eb,{Xa:d,Sa:f,fb:this,ab:a}):Jb(d.Ta.Eb, {Xa:d,Sa:f})};Wb=e.UnboundTypeError=rb("UnboundTypeError");e.count_emval_handles=function(){for(var a=0,b=5;bf?-28:N.Fc(d,f).fd;case 1:case 2:return 0;case 3:return d.flags;case 4:return f=hb(),d.flags|=f,0;case 5:return f=hb(),D[f+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return E[Kc()>>2]=28,-1;default:return-28}}catch(g){if("undefined"== typeof N||!(g instanceof N.Ma))throw g;return-g.Ra}},X:function(a,b){try{var c=Q(a);return fb(N.stat,c.path,b)}catch(d){if("undefined"==typeof N||!(d instanceof N.Ma))throw d;return-d.Ra}},E:function(a,b,c){try{b=c+2097152>>>0<4194305-!!b?(b>>>0)+4294967296*c:NaN;if(isNaN(b))return-61;N.ud(a,b);return 0}catch(d){if("undefined"==typeof N||!(d instanceof N.Ma))throw d;return-d.Ra}},S:function(a,b){try{if(0===b)return-28;var c=N.cwd(),d=ta(c)+1;if(b>>0,(G=h,1<=+Math.abs(G)?0>>0:~~+Math.ceil((G-+(~~G>>>0))/4294967296)>>> @@ -129,7 +129,7 @@ c();for(p in h)h[p].write(t,q[p]);null!==n&&n.push(d,t);return t},argPackAdvance:8,readValueFromPointer:lb,ib:d}]})},F:function(){},fa:function(a,b,c,d,f){var g=vb(c);b=S(b);R(a,{name:b,fromWireType:function(k){return!!k},toWireType:function(k,h){return h?d:f},argPackAdvance:8,readValueFromPointer:function(k){if(1===c)var h=B;else if(2===c)h=D;else if(4===c)h=E;else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(h[k>>g])},ib:null})},ka:function(a,b,c,d,f,g,k,h,n,q,p, t,x){p=S(p);g=W(f,g);h&&(h=W(k,h));q&&(q=W(n,q));x=W(t,x);var l=pb(p);Mb(l,function(){Zb("Cannot construct "+p+" due to unbound types",[d])});ub([a,b,c],d?[d]:[],function(u){u=u[0];if(d){var w=u.Ta;var C=w.Eb}else C=U.prototype;u=qb(l,function(){if(Object.getPrototypeOf(this)!==K)throw new xb("Use 'new' to construct "+p);if(void 0===H.tb)throw new xb(p+" has no accessible constructor");var I=H.tb[arguments.length];if(void 0===I)throw new xb("Tried to invoke ctor of "+p+" with invalid number of parameters ("+ arguments.length+") - expected ("+Object.keys(H.tb).toString()+") parameters instead!");return I.apply(this,arguments)});var K=Object.create(C,{constructor:{value:u}});u.prototype=K;var H=new Nb(p,u,K,x,w,g,h,q);w=new V(p,H,!0,!1);C=new V(p+"*",H,!1,!1);var Ga=new V(p+" const*",H,!1,!0);Db[a]={pointerType:C,jd:Ga};Ub(l,u);return[w,C,Ga]})},ja:function(a,b,c,d,f,g){0{Zb("Cannot construct "+h.name+" due to unbound types",k)};ub([],k,function(q){q.splice(1,0,null);h.Ta.tb[b-1]=bc(n,q,null,f,g);return[]});return[]})},a:function(a,b,c,d,f,g,k,h){var n=$b(c,d);b=S(b);g=W(f,g);ub([],[a],function(q){function p(){Zb("Cannot call "+ +1])throw new xb("Cannot register multiple constructors with identical number of parameters ("+(b-1)+") for class '"+h.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");h.Ta.tb[b-1]=()=>{Zb("Cannot construct "+h.name+" due to unbound types",k)};ub([],k,function(q){q.splice(1,0,null);h.Ta.tb[b-1]=bc(n,q,null,f,g);return[]});return[]})},b:function(a,b,c,d,f,g,k,h){var n=$b(c,d);b=S(b);g=W(f,g);ub([],[a],function(q){function p(){Zb("Cannot call "+ t+" due to unbound types",n)}q=q[0];var t=q.name+"."+b;b.startsWith("@@")&&(b=Symbol[b.substring(2)]);h&&q.Ta.Nd.push(b);var x=q.Ta.Eb,l=x[b];void 0===l||void 0===l.$a&&l.className!==q.name&&l.Qb===c-2?(p.Qb=c-2,p.className=q.name,x[b]=p):(Lb(x,b,t),x[b].$a[c-2]=p);ub([],n,function(u){u=bc(t,u,q,g,k);void 0===x[b].$a?(u.Qb=c-2,x[b]=u):x[b].$a[c-2]=u;return[]});return[]})},ea:function(a,b){b=S(b);R(a,{name:b,fromWireType:function(c){var d=ec(c);dc(c);return d},toWireType:function(c,d){return Sb(d)}, argPackAdvance:8,readValueFromPointer:lb,ib:null})},x:function(a,b,c){c=vb(c);b=S(b);R(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,f){return f},argPackAdvance:8,readValueFromPointer:fc(b,c),ib:null})},ha:function(a,b,c,d,f,g){var k=$b(b,c);a=S(a);f=W(d,f);Mb(a,function(){Zb("Cannot call "+a+" due to unbound types",k)},b-1);ub([],k,function(h){h=[h[0],null].concat(h.slice(1));Ub(a,bc(a,h,null,f,g),b-1);return[]})},i:function(a,b,c,d,f){b=S(b);-1===f&&(f=4294967295);f=vb(c);var g= h=>h;if(0===d){var k=32-8*c;g=h=>h<>>k}c=b.includes("unsigned")?function(h,n){return n>>>0}:function(h,n){return n};R(a,{name:b,fromWireType:g,toWireType:c,argPackAdvance:8,readValueFromPointer:gc(b,f,0!==d),ib:null})},e:function(a,b,c){function d(g){g>>=2;var k=F;return new f(ua,k[g+1],k[g])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=S(c);R(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{Ad:!0})},y:function(a,b){b= diff --git a/web/database/_generated/comm_query_executor.wasm b/web/database/_generated/comm_query_executor.wasm index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ { - const olmAccount = queryExecutor.getOlmPersistAccountDataWeb(); - expect(olmAccount.value).toBe('accountData'); + const contentOlmAccount = queryExecutor.getOlmPersistAccountDataWeb( + queryExecutor.getContentAccountID(), + ); + expect(contentOlmAccount.value).toBe('contentAccountData'); + + const notifsOlmAccount = queryExecutor.getOlmPersistAccountDataWeb( + queryExecutor.getNotifsAccountID(), + ); + expect(notifsOlmAccount.value).toBe('notifsAccountData'); }); it('should return all olm sessions', () => { diff --git a/web/database/types/sqlite-query-executor.js b/web/database/types/sqlite-query-executor.js --- a/web/database/types/sqlite-query-executor.js +++ b/web/database/types/sqlite-query-executor.js @@ -118,9 +118,11 @@ commitTransaction(): void; rollbackTransaction(): void; - getOlmPersistAccountDataWeb(): NullableString; + getContentAccountID(): number; + getNotifsAccountID(): number; + getOlmPersistAccountDataWeb(accountID: number): NullableString; getOlmPersistSessionsData(): $ReadOnlyArray; - storeOlmPersistAccount(accountData: string): void; + storeOlmPersistAccount(accountID: number, accountData: string): void; storeOlmPersistSession(session: OlmPersistSession): void; restoreFromMainCompaction(