diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 9af88bd9f9..97281b4e35 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -1688,7 +1688,7 @@ "$(inherited)", "$(PROJECT_DIR)/Libraries/sim", ); - MARKETING_VERSION = 5.2.2; + MARKETING_VERSION = 5.3; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -1734,7 +1734,7 @@ "$(inherited)", "$(PROJECT_DIR)/Libraries/sim", ); - MARKETING_VERSION = 5.2.2; + MARKETING_VERSION = 5.3; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; diff --git a/apps/multiplatform/android/src/main/AndroidManifest.xml b/apps/multiplatform/android/src/main/AndroidManifest.xml index f3d48fe88f..459b1bc05a 100644 --- a/apps/multiplatform/android/src/main/AndroidManifest.xml +++ b/apps/multiplatform/android/src/main/AndroidManifest.xml @@ -24,9 +24,8 @@ , chatArchiveTime: MutableState, chatLastStart: MutableState, - privacyFullBackup: SharedPreference, appFilesCountAndSize: MutableState>, chatItemTTL: MutableState, currentUser: User?, @@ -168,6 +166,13 @@ fun DatabaseLayout( SectionView(stringResource(MR.strings.run_chat_section)) { RunChatSetting(runChat, stopped, startChat, stopChatAlert) } + SectionTextFooter( + if (stopped) { + stringResource(MR.strings.you_must_use_the_most_recent_version_of_database) + } else { + stringResource(MR.strings.stop_chat_to_enable_database_actions) + } + ) SectionDividerSpaced() SectionView(stringResource(MR.strings.chat_database_section)) { @@ -180,8 +185,6 @@ fun DatabaseLayout( iconColor = if (unencrypted) WarningOrange else MaterialTheme.colors.secondary, disabled = operationsDisabled ) - AppDataBackupPreference(privacyFullBackup, initialRandomDBPassphrase) - SectionDividerSpaced(maxBottomPadding = false) SettingsActionItem( painterResource(MR.images.ic_ios_share), stringResource(MR.strings.export_database), @@ -225,13 +228,6 @@ fun DatabaseLayout( disabled = operationsDisabled ) } - SectionTextFooter( - if (stopped) { - stringResource(MR.strings.you_must_use_the_most_recent_version_of_database) - } else { - stringResource(MR.strings.stop_chat_to_enable_database_actions) - } - ) SectionDividerSpaced(maxTopPadding = true) SectionView(stringResource(MR.strings.files_and_media_section).uppercase()) { @@ -258,23 +254,6 @@ fun DatabaseLayout( } } -@Composable -private fun AppDataBackupPreference(privacyFullBackup: SharedPreference, initialRandomDBPassphrase: SharedPreference) { - SettingsPreferenceItem( - painterResource(MR.images.ic_backup), - iconColor = MaterialTheme.colors.secondary, - pref = privacyFullBackup, - text = stringResource(MR.strings.full_backup) - ) { - if (initialRandomDBPassphrase.get()) { - exportProhibitedAlert() - privacyFullBackup.set(false) - } else { - privacyFullBackup.set(it) - } - } -} - private fun setChatItemTTLAlert( m: ChatModel, selectedChatItemTTL: MutableState, progressIndicator: MutableState, @@ -683,7 +662,6 @@ fun PreviewDatabaseLayout() { chatArchiveName = remember { mutableStateOf("dummy_archive") }, chatArchiveTime = remember { mutableStateOf(Clock.System.now()) }, chatLastStart = remember { mutableStateOf(Clock.System.now()) }, - privacyFullBackup = SharedPreference({ true }, {}), appFilesCountAndSize = remember { mutableStateOf(0 to 0L) }, chatItemTTL = remember { mutableStateOf(ChatItemTTL.None) }, currentUser = User.sampleData, diff --git a/cabal.project b/cabal.project index 99c090872a..4eb8bea101 100644 --- a/cabal.project +++ b/cabal.project @@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: 82aec2cd8f7b4033dbf08d5de33ced216f574bbb + tag: e2065ab3525ca67e39700ee8040839d667f75ea2 source-repository-package type: git diff --git a/package.yaml b/package.yaml index 1d4a88fca6..94a16f1001 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: simplex-chat -version: 5.3.0.2 +version: 5.3.0.4 #synopsis: #description: homepage: https://github.com/simplex-chat/simplex-chat#readme diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index a5148be937..eb51dc5e12 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."82aec2cd8f7b4033dbf08d5de33ced216f574bbb" = "1x3rjq10d3c8qb6wf66a2j127xi9xdg21pyw5r4n124f8yvlb0nc"; + "https://github.com/simplex-chat/simplexmq.git"."e2065ab3525ca67e39700ee8040839d667f75ea2" = "13rz58bdpkhfrp1d58ylpbazhzs26q5fjg0sclxgvdqnnmac5cgz"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb"; "https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd"; diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 7945becde1..d52e8014c0 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,12 +5,12 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 5.3.0.2 +version: 5.3.0.4 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat maintainer: chat@simplex.chat -copyright: 2020-22 simplex.chat +copyright: 2020-23 simplex.chat license: AGPL-3 license-file: LICENSE build-type: Simple diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index fb9e72c0b2..3bff04fb99 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -48,7 +48,7 @@ import Data.Time (NominalDiffTime, addUTCTime, defaultTimeLocale, formatTime) import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime, nominalDay, nominalDiffTimeToSeconds) import Data.Time.Clock.System (SystemTime, systemToUTCTime) import Data.Word (Word32) -import qualified Database.SQLite.Simple as DB +import qualified Database.SQLite.Simple as SQL import Simplex.Chat.Archive import Simplex.Chat.Call import Simplex.Chat.Controller @@ -74,11 +74,12 @@ import Simplex.FileTransfer.Client.Presets (defaultXFTPServers) import Simplex.FileTransfer.Description (ValidFileDescription, gb, kb, mb) import Simplex.FileTransfer.Protocol (FileParty (..), FilePartyI) import Simplex.Messaging.Agent as Agent -import Simplex.Messaging.Agent.Client (AgentStatsKey (..), temporaryAgentError) +import Simplex.Messaging.Agent.Client (AgentStatsKey (..), agentClientStore, temporaryAgentError) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig) import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration, withConnection) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations import Simplex.Messaging.Client (defaultNetworkConfig) import qualified Simplex.Messaging.Crypto as C @@ -397,7 +398,7 @@ processChatCommand = \case asks currentUser >>= atomically . (`writeTVar` Just user'') pure $ CRActiveUser user'' SetActiveUser uName viewPwd_ -> do - tryError (withStore (`getUserIdByName` uName)) >>= \case + tryChatError (withStore (`getUserIdByName` uName)) >>= \case Left _ -> throwChatError CEUserUnknown Right userId -> processChatCommand $ APISetActiveUser userId viewPwd_ SetAllContactReceipts onOff -> withUser $ \_ -> withStore' (`updateAllContactReceipts` onOff) >> ok_ @@ -491,6 +492,13 @@ processChatCommand = \case APIStorageEncryption cfg -> withStoreChanged $ sqlCipherExport cfg ExecChatStoreSQL query -> CRSQLResult <$> withStore' (`execSQL` query) ExecAgentStoreSQL query -> CRSQLResult <$> withAgent (`execAgentStoreSQL` query) + SlowSQLQueries -> do + ChatController {chatStore, smpAgent} <- ask + chatQueries <- slowQueries chatStore + agentQueries <- slowQueries $ agentClientStore smpAgent + pure CRSlowSQLQueries {chatQueries, agentQueries} + where + slowQueries st = liftIO $ map (uncurry SlowSQLQuery . first SQL.fromQuery) . sortOn snd . M.assocs <$> withConnection st (readTVarIO . DB.slow) APIGetChats userId withPCC -> withUserId userId $ \user -> CRApiChats user <$> withStoreCtx' (Just "APIGetChats, getChatPreviews") (\db -> getChatPreviews db user withPCC) APIGetChat (ChatRef cType cId) pagination search -> withUser $ \user -> case cType of @@ -1706,7 +1714,7 @@ processChatCommand = \case QuitChat -> liftIO exitSuccess ShowVersion -> do let versionInfo = coreVersionInfo $(simplexmqCommitQ) - chatMigrations <- map upMigration <$> withStore' Migrations.getCurrent + chatMigrations <- map upMigration <$> withStore' (Migrations.getCurrent . DB.conn) agentMigrations <- withAgent getAgentMigrations pure $ CRVersionInfo {versionInfo, chatMigrations, agentMigrations} DebugLocks -> do @@ -1959,7 +1967,7 @@ processChatCommand = \case drgRandomBytes n = asks idsDrg >>= liftIO . (`randomBytes` n) privateGetUser :: UserId -> m User privateGetUser userId = - tryError (withStore (`getUser` userId)) >>= \case + tryChatError (withStore (`getUser` userId)) >>= \case Left _ -> throwChatError CEUserUnknown Right user -> pure user validateUserPassword :: User -> User -> Maybe UserPwd -> m () @@ -5040,6 +5048,7 @@ chatCommandP = "/db decrypt " *> (APIStorageEncryption . (`DBEncryptionConfig` "") <$> dbKeyP), "/sql chat " *> (ExecChatStoreSQL <$> textP), "/sql agent " *> (ExecAgentStoreSQL <$> textP), + "/sql slow" $> SlowSQLQueries, "/_get chats " *> (APIGetChats <$> A.decimal <*> (" pcc=on" $> True <|> " pcc=off" $> False <|> pure False)), "/_get chat " *> (APIGetChat <$> chatRefP <* A.space <*> chatPaginationP <*> optional (" search=" *> stringP)), "/_get items " *> (APIGetChatItems <$> chatPaginationP <*> optional (" search=" *> stringP)), diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index a02eb0b73b..3b741168e6 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -229,6 +229,7 @@ data ChatCommand | APIStorageEncryption DBEncryptionConfig | ExecChatStoreSQL Text | ExecAgentStoreSQL Text + | SlowSQLQueries | APIGetChats {userId :: UserId, pendingConnections :: Bool} | APIGetChat ChatRef ChatPagination (Maybe String) | APIGetChatItems ChatPagination (Maybe String) @@ -563,6 +564,7 @@ data ChatResponse | CRNewContactConnection {user :: User, connection :: PendingContactConnection} | CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection} | CRSQLResult {rows :: [Text]} + | CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]} | CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks} | CRAgentStats {agentStats :: [[String]]} | CRConnectionDisabled {connectionEntity :: ConnectionEntity} @@ -801,6 +803,14 @@ data SendFileMode | SendFileXFTP deriving (Show, Generic) +data SlowSQLQuery = SlowSQLQuery + { query :: Text, + duration :: Int64 + } + deriving (Show, Generic) + +instance ToJSON SlowSQLQuery where toEncoding = J.genericToEncoding J.defaultOptions + data ChatError = ChatError {errorType :: ChatErrorType} | ChatErrorAgent {agentError :: AgentErrorType, connectionEntity_ :: Maybe ConnectionEntity} diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs index 4b66291fe9..e31598812e 100644 --- a/src/Simplex/Chat/Store/Connections.hs +++ b/src/Simplex/Chat/Store/Connections.hs @@ -16,7 +16,6 @@ import Data.Maybe (fromMaybe) import Data.Text (Text) import Data.Time.Clock (UTCTime (..)) import Database.SQLite.Simple ((:.) (..)) -import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Store.Files import Simplex.Chat.Store.Groups @@ -25,6 +24,7 @@ import Simplex.Chat.Protocol import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow') +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB getConnectionEntity :: DB.Connection -> User -> AgentConnId -> ExceptT StoreError IO ConnectionEntity getConnectionEntity db user@User {userId, userContactId} agentConnId = do diff --git a/src/Simplex/Chat/Store/Direct.hs b/src/Simplex/Chat/Store/Direct.hs index 9c18350b85..de1c5014bf 100644 --- a/src/Simplex/Chat/Store/Direct.hs +++ b/src/Simplex/Chat/Store/Direct.hs @@ -68,13 +68,13 @@ import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) import Data.Time.Clock (UTCTime (..), getCurrentTime) import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..)) -import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Store.Shared import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Messaging.Agent.Protocol (ConnId, InvitationId, UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB getPendingContactConnection :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO PendingContactConnection getPendingContactConnection db userId connId = do diff --git a/src/Simplex/Chat/Store/Files.hs b/src/Simplex/Chat/Store/Files.hs index 49e149c7f0..249dfedc37 100644 --- a/src/Simplex/Chat/Store/Files.hs +++ b/src/Simplex/Chat/Store/Files.hs @@ -83,7 +83,6 @@ import Data.Time (addUTCTime) import Data.Time.Clock (UTCTime (..), getCurrentTime, nominalDay) import Data.Type.Equality import Database.SQLite.Simple (Only (..), (:.) (..)) -import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Store.Direct import Simplex.Chat.Store.Messages @@ -96,6 +95,7 @@ import Simplex.Chat.Types import Simplex.Chat.Util (week) import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB getLiveSndFileTransfers :: DB.Connection -> User -> IO [SndFileTransfer] getLiveSndFileTransfers db User {userId} = do diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index 274d6edc6f..32a3b91102 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -94,7 +94,6 @@ import Data.Maybe (fromMaybe, isNothing) import Data.Text (Text) import Data.Time.Clock (UTCTime (..), getCurrentTime) import Database.SQLite.Simple (NamedParam (..), Only (..), Query (..), (:.) (..)) -import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Messages import Simplex.Chat.Store.Direct @@ -103,6 +102,7 @@ import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Messaging.Agent.Protocol (ConnId, UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Util (eitherToMaybe) import UnliftIO.STM @@ -242,11 +242,11 @@ getGroupAndMember db User {userId, userContactId} groupMemberId = LEFT JOIN connections c ON c.connection_id = ( SELECT max(cc.connection_id) FROM connections cc - where cc.group_member_id = m.group_member_id + where cc.user_id = ? AND cc.group_member_id = m.group_member_id ) WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ? |] - (groupMemberId, userId, userContactId) + (userId, groupMemberId, userId, userContactId) where toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember) toGroupAndMember (groupInfoRow :. memberRow :. connRow) = @@ -530,7 +530,7 @@ groupMemberQuery = LEFT JOIN connections c ON c.connection_id = ( SELECT max(cc.connection_id) FROM connections cc - where cc.group_member_id = m.group_member_id + where cc.user_id = ? AND cc.group_member_id = m.group_member_id ) |] @@ -540,7 +540,7 @@ getGroupMember db user@User {userId} groupId groupMemberId = DB.query db (groupMemberQuery <> " WHERE m.group_id = ? AND m.group_member_id = ? AND m.user_id = ?") - (groupId, groupMemberId, userId) + (userId, groupId, groupMemberId, userId) getGroupMemberById :: DB.Connection -> User -> GroupMemberId -> ExceptT StoreError IO GroupMember getGroupMemberById db user@User {userId} groupMemberId = @@ -548,7 +548,7 @@ getGroupMemberById db user@User {userId} groupMemberId = DB.query db (groupMemberQuery <> " WHERE m.group_member_id = ? AND m.user_id = ?") - (groupMemberId, userId) + (userId, groupMemberId, userId) getGroupMembers :: DB.Connection -> User -> GroupInfo -> IO [GroupMember] getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do @@ -556,7 +556,7 @@ getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do <$> DB.query db (groupMemberQuery <> " WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)") - (groupId, userId, userContactId) + (userId, groupId, userId, userContactId) getGroupMembersForExpiration :: DB.Connection -> User -> GroupInfo -> IO [GroupMember] getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {groupId} = do @@ -572,7 +572,7 @@ getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {gro ) |] ) - (groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted) + (userId, groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted) toContactMember :: User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember toContactMember User {userContactId} (memberRow :. connRow) = @@ -998,11 +998,11 @@ getViaGroupMember db User {userId, userContactId} Contact {contactId} = LEFT JOIN connections c ON c.connection_id = ( SELECT max(cc.connection_id) FROM connections cc - where cc.group_member_id = m.group_member_id + where cc.user_id = ? AND cc.group_member_id = m.group_member_id ) WHERE ct.user_id = ? AND ct.contact_id = ? AND mu.contact_id = ? AND ct.deleted = 0 |] - (userId, contactId, userContactId) + (userId, userId, contactId, userContactId) where toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember) toGroupAndMember (groupInfoRow :. memberRow :. connRow) = @@ -1296,11 +1296,11 @@ getXGrpMemIntroContDirect db User {userId} Contact {contactId} = do LEFT JOIN connections ch ON ch.connection_id = ( SELECT max(cc.connection_id) FROM connections cc - where cc.group_member_id = mh.group_member_id + where cc.user_id = ? AND cc.group_member_id = mh.group_member_id ) WHERE ct.user_id = ? AND ct.contact_id = ? AND ct.deleted = 0 AND mh.member_category = ? |] - (userId, contactId, GCHostMember) + (userId, userId, contactId, GCHostMember) where toCont :: (Int64, GroupId, GroupMemberId, MemberId, Maybe ConnReqInvitation) -> Maybe (Int64, XGrpMemIntroCont) toCont (hostConnId, groupId, groupMemberId, memberId, connReq_) = case connReq_ of @@ -1326,11 +1326,11 @@ getXGrpMemIntroContGroup db User {userId} GroupMember {groupMemberId} = do LEFT JOIN connections ch ON ch.connection_id = ( SELECT max(cc.connection_id) FROM connections cc - where cc.group_member_id = mh.group_member_id + where cc.user_id = ? AND cc.group_member_id = mh.group_member_id ) WHERE m.user_id = ? AND m.group_member_id = ? AND mh.member_category = ? AND ct.deleted = 0 |] - (userId, groupMemberId, GCHostMember) + (userId, userId, groupMemberId, GCHostMember) where toCont :: (Int64, Maybe ConnReqInvitation) -> Maybe (Int64, ConnReqInvitation) toCont (hostConnId, connReq_) = case connReq_ of diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index b185b3a972..7bc2eaf4d4 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -110,7 +110,6 @@ import Data.Text (Text) import Data.Time (addUTCTime) import Data.Time.Clock (UTCTime (..), getCurrentTime) import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..)) -import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import Simplex.Chat.Markdown import Simplex.Chat.Messages @@ -122,6 +121,7 @@ import Simplex.Chat.Store.Shared import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, MsgMeta (..), UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow', maybeFirstRow) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import Simplex.Messaging.Util (eitherToMaybe) import UnliftIO.STM diff --git a/src/Simplex/Chat/Store/Profiles.hs b/src/Simplex/Chat/Store/Profiles.hs index 4577712f0e..48f2dd144e 100644 --- a/src/Simplex/Chat/Store/Profiles.hs +++ b/src/Simplex/Chat/Store/Profiles.hs @@ -66,7 +66,6 @@ import Data.Text (Text) import Data.Text.Encoding (decodeLatin1, encodeUtf8) import Data.Time.Clock (UTCTime (..), getCurrentTime) import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..)) -import qualified Database.SQLite.Simple as DB import Database.SQLite.Simple.QQ (sql) import GHC.Generics (Generic) import Simplex.Chat.Call @@ -78,6 +77,7 @@ import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolTypeI (..)) diff --git a/src/Simplex/Chat/Store/Shared.hs b/src/Simplex/Chat/Store/Shared.hs index ad3116695d..c4e6b8d909 100644 --- a/src/Simplex/Chat/Store/Shared.hs +++ b/src/Simplex/Chat/Store/Shared.hs @@ -25,7 +25,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Time.Clock (UTCTime (..), getCurrentTime) import Database.SQLite.Simple (NamedParam (..), Only (..), Query, SQLError, (:.) (..)) -import qualified Database.SQLite.Simple as DB +import qualified Database.SQLite.Simple as SQL import Database.SQLite.Simple.QQ (sql) import GHC.Generics (Generic) import Simplex.Chat.Messages @@ -34,6 +34,7 @@ import Simplex.Chat.Types import Simplex.Chat.Types.Preferences import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, UserId) import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON) import Simplex.Messaging.Util (allFinally) import UnliftIO.STM @@ -107,7 +108,7 @@ checkConstraint err action = ExceptT $ runExceptT action `E.catch` (pure . Left handleSQLError :: StoreError -> SQLError -> StoreError handleSQLError err e - | DB.sqlError e == DB.ErrorConstraint = err + | SQL.sqlError e == SQL.ErrorConstraint = err | otherwise = SEInternalError $ show e storeFinally :: ExceptT StoreError IO a -> ExceptT StoreError IO b -> ExceptT StoreError IO a @@ -309,7 +310,7 @@ withLocalDisplayName db userId displayName action = getLdnSuffix >>= (`tryCreate E.try (insertName ldn currentTs) >>= \case Right () -> action ldn Left e - | DB.sqlError e == DB.ErrorConstraint -> tryCreateName (ldnSuffix + 1) (attempts - 1) + | SQL.sqlError e == SQL.ErrorConstraint -> tryCreateName (ldnSuffix + 1) (attempts - 1) | otherwise -> E.throwIO e where insertName ldn ts = @@ -335,7 +336,7 @@ createWithRandomBytes size gVar create = tryCreate 3 liftIO (E.try $ create id') >>= \case Right x -> pure x Left e - | DB.sqlError e == DB.ErrorConstraint -> tryCreate (n - 1) + | SQL.sqlError e == SQL.ErrorConstraint -> tryCreate (n - 1) | otherwise -> throwError . SEInternalError $ show e encodedRandomBytes :: TVar ChaChaDRG -> Int -> IO ByteString diff --git a/src/Simplex/Chat/Terminal/Input.hs b/src/Simplex/Chat/Terminal/Input.hs index ed6c3203d2..36cec49d7c 100644 --- a/src/Simplex/Chat/Terminal/Input.hs +++ b/src/Simplex/Chat/Terminal/Input.hs @@ -25,7 +25,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Database.SQLite.Simple (Only (..)) -import qualified Database.SQLite.Simple as DB +import qualified Database.SQLite.Simple as SQL import Database.SQLite.Simple.QQ (sql) import GHC.Weak (deRefWeak) import Simplex.Chat @@ -36,6 +36,7 @@ import Simplex.Chat.Styled import Simplex.Chat.Terminal.Output import Simplex.Chat.Types (User (..)) import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore, withTransaction) +import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import Simplex.Messaging.Util (catchAll_, safeDecodeUtf8, whenM) import System.Exit (exitSuccess) import System.Terminal hiding (insertChars) @@ -299,7 +300,7 @@ updateTermState user_ st ac live tw (key, ms) ts@TerminalState {inputString = s, getNameSfxs table pfx = getNameSfxs_ pfx (userId, pfx <> "%") $ "SELECT local_display_name FROM " <> table <> " WHERE user_id = ? AND local_display_name LIKE ?" - getNameSfxs_ :: DB.ToRow p => Text -> p -> DB.Query -> IO [String] + getNameSfxs_ :: SQL.ToRow p => Text -> p -> SQL.Query -> IO [String] getNameSfxs_ pfx ps q = withTransaction st (\db -> hasPfx pfx . map fromOnly <$> DB.query db q ps) `catchAll_` pure [] commands = diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index bd273dec2b..2db39bccee 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -247,6 +247,9 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView CRNtfToken _ status mode -> ["device token status: " <> plain (smpEncode status) <> ", notifications mode: " <> plain (strEncode mode)] CRNtfMessages {} -> [] CRSQLResult rows -> map plain rows + CRSlowSQLQueries {chatQueries, agentQueries} -> + let viewQuery SlowSQLQuery {query, duration} = sShow duration <> " ms: " <> plain (T.unwords $ T.lines query) + in ("Chat queries" : map viewQuery chatQueries) <> [""] <> ("Agent queries" : map viewQuery agentQueries) CRDebugLocks {chatLockName, agentLocks} -> [ maybe "no chat lock" (("chat lock: " <>) . plain) chatLockName, plain $ "agent locks: " <> LB.unpack (J.encode agentLocks) diff --git a/stack.yaml b/stack.yaml index ccbc5e797a..a39c671554 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: 82aec2cd8f7b4033dbf08d5de33ced216f574bbb + commit: e2065ab3525ca67e39700ee8040839d667f75ea2 - github: kazu-yamamoto/http2 commit: b5a1b7200cf5bc7044af34ba325284271f6dff25 # - ../direct-sqlcipher diff --git a/tests/SchemaDump.hs b/tests/SchemaDump.hs index 773a9b999f..f816704dbb 100644 --- a/tests/SchemaDump.hs +++ b/tests/SchemaDump.hs @@ -10,7 +10,7 @@ import Data.List (dropWhileEnd) import Data.Maybe (fromJust, isJust) import Simplex.Chat.Store (createChatStore) import qualified Simplex.Chat.Store as Store -import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore, createSQLiteStore, withConnection) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore, createSQLiteStore, withConnection') import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..), MigrationsToRun (..), toDownMigration) import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations import Simplex.Messaging.Util (ifM, whenM) @@ -53,14 +53,14 @@ testSchemaMigrations = withTmpFiles $ do putStrLn $ "down migration " <> name m let downMigr = fromJust $ toDownMigration m schema <- getSchema testDB testSchema - withConnection st (`Migrations.run` MTRUp [m]) + withConnection' st (`Migrations.run` MTRUp [m]) schema' <- getSchema testDB testSchema schema' `shouldNotBe` schema - withConnection st (`Migrations.run` MTRDown [downMigr]) + withConnection' st (`Migrations.run` MTRDown [downMigr]) unless (name m `elem` skipComparisonForDownMigrations) $ do schema'' <- getSchema testDB testSchema schema'' `shouldBe` schema - withConnection st (`Migrations.run` MTRUp [m]) + withConnection' st (`Migrations.run` MTRUp [m]) schema''' <- getSchema testDB testSchema schema''' `shouldBe` schema'