diff --git a/changelog.d/5-internal/wpb-3915 b/changelog.d/5-internal/wpb-3915 new file mode 100644 index 0000000000..fcaeeec676 --- /dev/null +++ b/changelog.d/5-internal/wpb-3915 @@ -0,0 +1 @@ +Distinguish between update and upsert cassandra commands (follow-up to #3504) (#3513) \ No newline at end of file diff --git a/services/brig/src/Brig/API/OAuth.hs b/services/brig/src/Brig/API/OAuth.hs index c7ff94be6a..b2196be7be 100644 --- a/services/brig/src/Brig/API/OAuth.hs +++ b/services/brig/src/Brig/API/OAuth.hs @@ -313,7 +313,7 @@ updateOAuthClient' :: (MonadClient m) => OAuthClientId -> OAuthApplicationName - updateOAuthClient' cid name uri = retry x5 . write q $ params LocalQuorum (name, uri, cid) where q :: PrepQuery W (OAuthApplicationName, RedirectUrl, OAuthClientId) () - q = "UPDATE oauth_client SET name = ?, redirect_uri = ? WHERE id = ?" + q = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE oauth_client SET name = ?, redirect_uri = ? WHERE id = ?" insertOAuthClient :: (MonadClient m) => OAuthClientId -> OAuthApplicationName -> RedirectUrl -> Password -> m () insertOAuthClient cid name uri pw = retry x5 . write q $ params LocalQuorum (cid, name, uri, pw) diff --git a/services/brig/src/Brig/Data/Client.hs b/services/brig/src/Brig/Data/Client.hs index 2a8eebeafc..0d893fa656 100644 --- a/services/brig/src/Brig/Data/Client.hs +++ b/services/brig/src/Brig/Data/Client.hs @@ -382,10 +382,10 @@ insertClient :: PrepQuery W (UserId, ClientId, UTCTimeMillis, ClientType, Maybe insertClient = "INSERT INTO clients (user, client, tstamp, type, label, class, cookie, lat, lon, model, capabilities) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" updateClientLabelQuery :: PrepQuery W (Maybe Text, UserId, ClientId) () -updateClientLabelQuery = "UPDATE clients SET label = ? WHERE user = ? AND client = ?" +updateClientLabelQuery = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE clients SET label = ? WHERE user = ? AND client = ?" updateClientCapabilitiesQuery :: PrepQuery W (Maybe (C.Set ClientCapability), UserId, ClientId) () -updateClientCapabilitiesQuery = "UPDATE clients SET capabilities = ? WHERE user = ? AND client = ?" +updateClientCapabilitiesQuery = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE clients SET capabilities = ? WHERE user = ? AND client = ?" updateClientLastActiveQuery :: PrepQuery W (UTCTime, UserId, ClientId) Row updateClientLastActiveQuery = "UPDATE clients SET last_active = ? WHERE user = ? AND client = ? IF EXISTS" diff --git a/services/brig/src/Brig/Data/Connection.hs b/services/brig/src/Brig/Data/Connection.hs index f4d8b56e3e..16031d654e 100644 --- a/services/brig/src/Brig/Data/Connection.hs +++ b/services/brig/src/Brig/Data/Connection.hs @@ -340,7 +340,7 @@ connectionInsert :: PrepQuery W (UserId, UserId, RelationWithHistory, UTCTimeMil connectionInsert = "INSERT INTO connection (left, right, status, last_update, conv) VALUES (?, ?, ?, ?, ?)" connectionUpdate :: PrepQuery W (RelationWithHistory, UTCTimeMillis, UserId, UserId) () -connectionUpdate = "UPDATE connection SET status = ?, last_update = ? WHERE left = ? AND right = ?" +connectionUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE connection SET status = ?, last_update = ? WHERE left = ? AND right = ?" connectionSelect :: PrepQuery R (UserId, UserId) (UserId, UserId, RelationWithHistory, UTCTimeMillis, Maybe ConvId) connectionSelect = "SELECT left, right, status, last_update, conv FROM connection WHERE left = ? AND right = ?" @@ -391,7 +391,7 @@ remoteConnectionSelectFrom :: PrepQuery R (UserId, Domain, UserId) (RelationWith remoteConnectionSelectFrom = "SELECT status, last_update, conv_domain, conv_id FROM connection_remote where left = ? AND right_domain = ? AND right_user = ?" remoteConnectionUpdate :: PrepQuery W (RelationWithHistory, UTCTimeMillis, UserId, Domain, UserId) () -remoteConnectionUpdate = "UPDATE connection_remote set status = ?, last_update = ? WHERE left = ? and right_domain = ? and right_user = ?" +remoteConnectionUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE connection_remote set status = ?, last_update = ? WHERE left = ? and right_domain = ? and right_user = ?" remoteConnectionDelete :: PrepQuery W (UserId, Domain, UserId) () remoteConnectionDelete = "DELETE FROM connection_remote where left = ? AND right_domain = ? AND right_user = ?" diff --git a/services/brig/src/Brig/Data/MLS/KeyPackage.hs b/services/brig/src/Brig/Data/MLS/KeyPackage.hs index dce1b7cfc5..d06e364509 100644 --- a/services/brig/src/Brig/Data/MLS/KeyPackage.hs +++ b/services/brig/src/Brig/Data/MLS/KeyPackage.hs @@ -189,11 +189,11 @@ addKeyPackageRef :: MonadClient m => KeyPackageRef -> NewKeyPackageRef -> m () addKeyPackageRef ref nkpr = do retry x5 $ write - q + upsertQuery (params LocalQuorum (nkprClientId nkpr, qUnqualified (nkprConversation nkpr), qDomain (nkprConversation nkpr), qDomain (nkprUserId nkpr), qUnqualified (nkprUserId nkpr), ref)) where - q :: PrepQuery W (ClientId, ConvId, Domain, Domain, UserId, KeyPackageRef) x - q = "UPDATE mls_key_package_refs SET client = ?, conv = ?, conv_domain = ?, domain = ?, user = ? WHERE ref = ?" + upsertQuery :: PrepQuery W (ClientId, ConvId, Domain, Domain, UserId, KeyPackageRef) x + upsertQuery = "UPDATE mls_key_package_refs SET client = ?, conv = ?, conv_domain = ?, domain = ?, user = ? WHERE ref = ?" -- | Update key package ref, used in Galley when commit reveals key package ref update for the sender. -- Nothing is changed if the previous key package ref is not found in the table. diff --git a/services/brig/src/Brig/Data/User.hs b/services/brig/src/Brig/Data/User.hs index 1891e135f4..d170ed4e42 100644 --- a/services/brig/src/Brig/Data/User.hs +++ b/services/brig/src/Brig/Data/User.hs @@ -622,64 +622,64 @@ userInsert = \VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" userDisplayNameUpdate :: PrepQuery W (Name, UserId) () -userDisplayNameUpdate = "UPDATE user SET name = ? WHERE id = ?" +userDisplayNameUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET name = ? WHERE id = ?" userPictUpdate :: PrepQuery W (Pict, UserId) () -userPictUpdate = "UPDATE user SET picture = ? WHERE id = ?" +userPictUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET picture = ? WHERE id = ?" userAssetsUpdate :: PrepQuery W ([Asset], UserId) () -userAssetsUpdate = "UPDATE user SET assets = ? WHERE id = ?" +userAssetsUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET assets = ? WHERE id = ?" userAccentIdUpdate :: PrepQuery W (ColourId, UserId) () -userAccentIdUpdate = "UPDATE user SET accent_id = ? WHERE id = ?" +userAccentIdUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET accent_id = ? WHERE id = ?" userEmailUpdate :: PrepQuery W (Email, UserId) () -userEmailUpdate = "UPDATE user SET email = ? WHERE id = ?" +userEmailUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET email = ? WHERE id = ?" userEmailUnvalidatedUpdate :: PrepQuery W (Email, UserId) () -userEmailUnvalidatedUpdate = "UPDATE user SET email_unvalidated = ? WHERE id = ?" +userEmailUnvalidatedUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET email_unvalidated = ? WHERE id = ?" userEmailUnvalidatedDelete :: PrepQuery W (Identity UserId) () -userEmailUnvalidatedDelete = "UPDATE user SET email_unvalidated = null WHERE id = ?" +userEmailUnvalidatedDelete = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET email_unvalidated = null WHERE id = ?" userPhoneUpdate :: PrepQuery W (Phone, UserId) () -userPhoneUpdate = "UPDATE user SET phone = ? WHERE id = ?" +userPhoneUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET phone = ? WHERE id = ?" userSSOIdUpdate :: PrepQuery W (Maybe UserSSOId, UserId) () -userSSOIdUpdate = "UPDATE user SET sso_id = ? WHERE id = ?" +userSSOIdUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET sso_id = ? WHERE id = ?" userManagedByUpdate :: PrepQuery W (ManagedBy, UserId) () -userManagedByUpdate = "UPDATE user SET managed_by = ? WHERE id = ?" +userManagedByUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET managed_by = ? WHERE id = ?" userHandleUpdate :: PrepQuery W (Handle, UserId) () -userHandleUpdate = "UPDATE user SET handle = ? WHERE id = ?" +userHandleUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET handle = ? WHERE id = ?" userSupportedProtocolUpdate :: PrepQuery W (Set BaseProtocolTag, UserId) () -userSupportedProtocolUpdate = "UPDATE user SET supported_protocols = ? WHERE id = ?" +userSupportedProtocolUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET supported_protocols = ? WHERE id = ?" userPasswordUpdate :: PrepQuery W (Password, UserId) () -userPasswordUpdate = "UPDATE user SET password = ? WHERE id = ?" +userPasswordUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET password = ? WHERE id = ?" userStatusUpdate :: PrepQuery W (AccountStatus, UserId) () -userStatusUpdate = "UPDATE user SET status = ? WHERE id = ?" +userStatusUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET status = ? WHERE id = ?" userDeactivatedUpdate :: PrepQuery W (Identity UserId) () -userDeactivatedUpdate = "UPDATE user SET activated = false WHERE id = ?" +userDeactivatedUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET activated = false WHERE id = ?" userActivatedUpdate :: PrepQuery W (Maybe Email, Maybe Phone, UserId) () -userActivatedUpdate = "UPDATE user SET activated = true, email = ?, phone = ? WHERE id = ?" +userActivatedUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET activated = true, email = ?, phone = ? WHERE id = ?" userLocaleUpdate :: PrepQuery W (Language, Maybe Country, UserId) () -userLocaleUpdate = "UPDATE user SET language = ?, country = ? WHERE id = ?" +userLocaleUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET language = ?, country = ? WHERE id = ?" userEmailDelete :: PrepQuery W (Identity UserId) () -userEmailDelete = "UPDATE user SET email = null WHERE id = ?" +userEmailDelete = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET email = null WHERE id = ?" userPhoneDelete :: PrepQuery W (Identity UserId) () -userPhoneDelete = "UPDATE user SET phone = null WHERE id = ?" +userPhoneDelete = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET phone = null WHERE id = ?" userRichInfoUpdate :: PrepQuery W (RichInfoAssocList, UserId) () -userRichInfoUpdate = "UPDATE rich_info SET json = ? WHERE user = ?" +userRichInfoUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE rich_info SET json = ? WHERE user = ?" ------------------------------------------------------------------------------- -- Conversions diff --git a/services/brig/src/Brig/Provider/DB.hs b/services/brig/src/Brig/Provider/DB.hs index ab7f85df1b..e83919f5bb 100644 --- a/services/brig/src/Brig/Provider/DB.hs +++ b/services/brig/src/Brig/Provider/DB.hs @@ -72,11 +72,11 @@ updateAccountProfile p name url descr = retry x5 . batch $ do for_ descr $ \x -> addPrepQuery cqlDescr (x, p) where cqlName :: PrepQuery W (Name, ProviderId) () - cqlName = "UPDATE provider SET name = ? WHERE id = ?" + cqlName = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE provider SET name = ? WHERE id = ?" cqlUrl :: PrepQuery W (HttpsUrl, ProviderId) () - cqlUrl = "UPDATE provider SET url = ? WHERE id = ?" + cqlUrl = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE provider SET url = ? WHERE id = ?" cqlDescr :: PrepQuery W (Text, ProviderId) () - cqlDescr = "UPDATE provider SET descr = ? WHERE id = ?" + cqlDescr = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE provider SET descr = ? WHERE id = ?" -- | Lookup the raw account data of a (possibly unverified) provider. lookupAccountData :: @@ -136,7 +136,7 @@ updateAccountPassword pid pwd = do retry x5 $ write cql $ params LocalQuorum (p, pid) where cql :: PrepQuery W (Password, ProviderId) () - cql = "UPDATE provider SET password = ? where id = ?" + cql = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE provider SET password = ? where id = ?" -------------------------------------------------------------------------------- -- Unique (Natural) Keys @@ -156,10 +156,12 @@ insertKey p old new = retry x5 . batch $ do where cqlKeyInsert :: PrepQuery W (Text, ProviderId) () cqlKeyInsert = "INSERT INTO provider_keys (key, provider) VALUES (?, ?)" + cqlKeyDelete :: PrepQuery W (Identity Text) () cqlKeyDelete = "DELETE FROM provider_keys WHERE key = ?" + cqlEmail :: PrepQuery W (Email, ProviderId) () - cqlEmail = "UPDATE provider SET email = ? WHERE id = ?" + cqlEmail = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE provider SET email = ? WHERE id = ?" lookupKey :: MonadClient m => @@ -306,15 +308,15 @@ updateService pid sid svcName svcTags nameChange summary descr assets tagsChange for_ assets $ \x -> addPrepQuery cqlAssets (x, pid, sid) where cqlName :: PrepQuery W (Name, ProviderId, ServiceId) () - cqlName = "UPDATE service SET name = ? WHERE provider = ? AND id = ?" + cqlName = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET name = ? WHERE provider = ? AND id = ?" cqlSummary :: PrepQuery W (Text, ProviderId, ServiceId) () - cqlSummary = "UPDATE service SET summary = ? WHERE provider = ? AND id = ?" + cqlSummary = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET summary = ? WHERE provider = ? AND id = ?" cqlDescr :: PrepQuery W (Text, ProviderId, ServiceId) () - cqlDescr = "UPDATE service SET descr = ? WHERE provider = ? AND id = ?" + cqlDescr = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET descr = ? WHERE provider = ? AND id = ?" cqlAssets :: PrepQuery W ([Asset], ProviderId, ServiceId) () - cqlAssets = "UPDATE service SET assets = ? WHERE provider = ? AND id = ?" + cqlAssets = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET assets = ? WHERE provider = ? AND id = ?" cqlTags :: PrepQuery W (C.Set ServiceTag, ProviderId, ServiceId) () - cqlTags = "UPDATE service SET tags = ? WHERE provider = ? AND id = ?" + cqlTags = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET tags = ? WHERE provider = ? AND id = ?" -- NB: can take a significant amount of time if many teams were using the service deleteService :: @@ -436,16 +438,17 @@ updateServiceConn pid sid url tokens keys enabled = retry x5 . batch $ do for_ enabled $ \x -> addPrepQuery cqlEnabled (x, pid, sid) where (pks, fps) = (fmap fst &&& fmap snd) (unzip . toList <$> keys) + cqlBaseUrl :: PrepQuery W (HttpsUrl, ProviderId, ServiceId) () - cqlBaseUrl = "UPDATE service SET base_url = ? WHERE provider = ? AND id = ?" + cqlBaseUrl = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET base_url = ? WHERE provider = ? AND id = ?" cqlTokens :: PrepQuery W (List1 ServiceToken, ProviderId, ServiceId) () - cqlTokens = "UPDATE service SET auth_tokens = ? WHERE provider = ? AND id = ?" + cqlTokens = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET auth_tokens = ? WHERE provider = ? AND id = ?" cqlKeys :: PrepQuery W ([ServiceKey], ProviderId, ServiceId) () - cqlKeys = "UPDATE service SET pubkeys = ? WHERE provider = ? AND id = ?" + cqlKeys = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET pubkeys = ? WHERE provider = ? AND id = ?" cqlFps :: PrepQuery W ([Fingerprint Rsa], ProviderId, ServiceId) () - cqlFps = "UPDATE service SET fingerprints = ? WHERE provider = ? AND id = ?" + cqlFps = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET fingerprints = ? WHERE provider = ? AND id = ?" cqlEnabled :: PrepQuery W (Bool, ProviderId, ServiceId) () - cqlEnabled = "UPDATE service SET enabled = ? WHERE provider = ? AND id = ?" + cqlEnabled = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE service SET enabled = ? WHERE provider = ? AND id = ?" -------------------------------------------------------------------------------- -- Service "Indexes" (tag and prefix); contain only enabled services diff --git a/services/brig/src/Brig/Unique.hs b/services/brig/src/Brig/Unique.hs index 88e325e8c4..6c5d5f0a2a 100644 --- a/services/brig/src/Brig/Unique.hs +++ b/services/brig/src/Brig/Unique.hs @@ -66,13 +66,13 @@ withClaim u v t io = do -- [Note: Guarantees] claim = do let ttl = max minTtl (fromIntegral (t #> Second)) - retry x5 $ write cql $ params LocalQuorum (ttl * 2, C.Set [u], v) + retry x5 $ write upsertQuery $ params LocalQuorum (ttl * 2, C.Set [u], v) claimed <- (== [u]) <$> lookupClaims v if claimed then liftIO $ timeout (fromIntegral ttl # Second) io else pure Nothing - cql :: PrepQuery W (Int32, C.Set (Id a), Text) () - cql = "UPDATE unique_claims USING TTL ? SET claims = claims + ? WHERE value = ?" + upsertQuery :: PrepQuery W (Int32, C.Set (Id a), Text) () + upsertQuery = "UPDATE unique_claims USING TTL ? SET claims = claims + ? WHERE value = ?" deleteClaim :: MonadClient m => @@ -91,7 +91,7 @@ deleteClaim u v t = do retry x5 $ write cql $ params LocalQuorum (ttl * 2, C.Set [u], v) where cql :: PrepQuery W (Int32, C.Set (Id a), Text) () - cql = "UPDATE unique_claims USING TTL ? SET claims = claims - ? WHERE value = ?" + cql = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE unique_claims USING TTL ? SET claims = claims - ? WHERE value = ?" -- | Lookup the current claims on a value. lookupClaims :: MonadClient m => Text -> m [Id a] diff --git a/services/brig/test/integration/API/User/Auth.hs b/services/brig/test/integration/API/User/Auth.hs index 8979a1b941..eb97912341 100644 --- a/services/brig/test/integration/API/User/Auth.hs +++ b/services/brig/test/integration/API/User/Auth.hs @@ -202,17 +202,20 @@ testLoginWith6CharPassword brig db = do (PasswordLogin (PasswordLoginData (LoginByEmail email) pw Nothing Nothing)) PersistentCookie !!! const expectedStatusCode === statusCode + -- Since 8 char passwords are required, when setting a password via the API, -- we need to write this directly to the db, to be able to test this writeDirectlyToDB :: UserId -> PlainTextPassword6 -> Http () writeDirectlyToDB uid pw = liftIO (runClient db (updatePassword uid pw >> revokeAllCookies uid)) + updatePassword :: MonadClient m => UserId -> PlainTextPassword6 -> m () updatePassword u t = do p <- liftIO $ mkSafePassword t retry x5 $ write userPasswordUpdate (params LocalQuorum (p, u)) + userPasswordUpdate :: PrepQuery W (Password, UserId) () - userPasswordUpdate = "UPDATE user SET password = ? WHERE id = ?" + userPasswordUpdate = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET password = ? WHERE id = ?" -------------------------------------------------------------------------------- -- ZAuth test environment for generating arbitrary tokens. diff --git a/services/galley/src/Galley/Cassandra/Client.hs b/services/galley/src/Galley/Cassandra/Client.hs index c9fdc5e01b..cb30b34018 100644 --- a/services/galley/src/Galley/Cassandra/Client.hs +++ b/services/galley/src/Galley/Cassandra/Client.hs @@ -41,7 +41,7 @@ import UnliftIO qualified updateClient :: Bool -> UserId -> ClientId -> Client () updateClient add usr cls = do - let q = if add then Cql.addMemberClient else Cql.rmMemberClient + let q = if add then Cql.upsertMemberAddClient else Cql.upsertMemberRmClient retry x5 $ write (q cls) (params LocalQuorum (Identity usr)) -- Do, at most, 16 parallel lookups of up to 128 users each diff --git a/services/galley/src/Galley/Cassandra/CustomBackend.hs b/services/galley/src/Galley/Cassandra/CustomBackend.hs index 0878c0a0a7..cabe4a3a43 100644 --- a/services/galley/src/Galley/Cassandra/CustomBackend.hs +++ b/services/galley/src/Galley/Cassandra/CustomBackend.hs @@ -51,7 +51,7 @@ getCustomBackend domain = setCustomBackend :: MonadClient m => Domain -> CustomBackend -> m () setCustomBackend domain CustomBackend {..} = do - retry x5 $ write Cql.updateCustomBackend (params LocalQuorum (backendConfigJsonUrl, backendWebappWelcomeUrl, domain)) + retry x5 $ write Cql.upsertCustomBackend (params LocalQuorum (backendConfigJsonUrl, backendWebappWelcomeUrl, domain)) deleteCustomBackend :: MonadClient m => Domain -> m () deleteCustomBackend domain = do diff --git a/services/galley/src/Galley/Cassandra/Queries.hs b/services/galley/src/Galley/Cassandra/Queries.hs index 4a03725ae4..8118d0cd54 100644 --- a/services/galley/src/Galley/Cassandra/Queries.hs +++ b/services/galley/src/Galley/Cassandra/Queries.hs @@ -176,6 +176,10 @@ deleteTeamAdmin = "delete from team_admin where team = ? and user = ?" listTeamAdmins :: PrepQuery R (Identity TeamId) (Identity UserId) listTeamAdmins = "select user from team_admin where team = ?" +-- | This is not an upsert, but we can't add `IF EXISTS` here, or cassandra will yell `Invalid +-- "Batch with conditions cannot span multiple tables"` at us. So we make sure in the +-- application logic to only call this if the user exists (in the handler, not entirely +-- race-condition-proof, unfortunately). updatePermissions :: PrepQuery W (Permissions, TeamId, UserId) () updatePermissions = "update team_member set perms = ? where team = ? and user = ?" @@ -186,25 +190,25 @@ deleteUserTeam :: PrepQuery W (UserId, TeamId) () deleteUserTeam = "delete from user_team where user = ? and team = ?" markTeamDeleted :: PrepQuery W (TeamStatus, TeamId) () -markTeamDeleted = "update team set status = ? where team = ?" +markTeamDeleted = {- `IF EXISTS`, but that requires benchmarking -} "update team set status = ? where team = ?" deleteTeam :: PrepQuery W (TeamStatus, TeamId) () -deleteTeam = "update team using timestamp 32503680000000000 set name = 'default', icon = 'default', status = ? where team = ? " +deleteTeam = {- `IF EXISTS`, but that requires benchmarking -} "update team using timestamp 32503680000000000 set name = 'default', icon = 'default', status = ? where team = ? " updateTeamName :: PrepQuery W (Text, TeamId) () -updateTeamName = "update team set name = ? where team = ?" +updateTeamName = {- `IF EXISTS`, but that requires benchmarking -} "update team set name = ? where team = ?" updateTeamIcon :: PrepQuery W (Text, TeamId) () -updateTeamIcon = "update team set icon = ? where team = ?" +updateTeamIcon = {- `IF EXISTS`, but that requires benchmarking -} "update team set icon = ? where team = ?" updateTeamIconKey :: PrepQuery W (Text, TeamId) () -updateTeamIconKey = "update team set icon_key = ? where team = ?" +updateTeamIconKey = {- `IF EXISTS`, but that requires benchmarking -} "update team set icon_key = ? where team = ?" updateTeamStatus :: PrepQuery W (TeamStatus, TeamId) () -updateTeamStatus = "update team set status = ? where team = ?" +updateTeamStatus = {- `IF EXISTS`, but that requires benchmarking -} "update team set status = ? where team = ?" updateTeamSplashScreen :: PrepQuery W (Text, TeamId) () -updateTeamSplashScreen = "update team set splash_screen = ? where team = ?" +updateTeamSplashScreen = {- `IF EXISTS`, but that requires benchmarking -} "update team set splash_screen = ? where team = ?" -- Conversations ------------------------------------------------------------ @@ -264,34 +268,34 @@ insertMLSSelfConv = <> ", ?, ?)" updateConvAccess :: PrepQuery W (C.Set Access, C.Set AccessRole, ConvId) () -updateConvAccess = "update conversation set access = ?, access_roles_v2 = ? where conv = ?" +updateConvAccess = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set access = ?, access_roles_v2 = ? where conv = ?" updateConvReceiptMode :: PrepQuery W (ReceiptMode, ConvId) () -updateConvReceiptMode = "update conversation set receipt_mode = ? where conv = ?" +updateConvReceiptMode = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set receipt_mode = ? where conv = ?" updateConvMessageTimer :: PrepQuery W (Maybe Milliseconds, ConvId) () -updateConvMessageTimer = "update conversation set message_timer = ? where conv = ?" +updateConvMessageTimer = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set message_timer = ? where conv = ?" updateConvName :: PrepQuery W (Text, ConvId) () -updateConvName = "update conversation set name = ? where conv = ?" +updateConvName = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set name = ? where conv = ?" updateConvType :: PrepQuery W (ConvType, ConvId) () -updateConvType = "update conversation set type = ? where conv = ?" +updateConvType = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set type = ? where conv = ?" updateConvEpoch :: PrepQuery W (Epoch, ConvId) () -updateConvEpoch = "update conversation set epoch = ? where conv = ?" +updateConvEpoch = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set epoch = ? where conv = ?" deleteConv :: PrepQuery W (Identity ConvId) () deleteConv = "delete from conversation using timestamp 32503680000000000 where conv = ?" markConvDeleted :: PrepQuery W (Identity ConvId) () -markConvDeleted = "update conversation set deleted = true where conv = ?" +markConvDeleted = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set deleted = true where conv = ?" selectPublicGroupState :: PrepQuery R (Identity ConvId) (Identity (Maybe OpaquePublicGroupState)) selectPublicGroupState = "select public_group_state from conversation where conv = ?" updatePublicGroupState :: PrepQuery W (OpaquePublicGroupState, ConvId) () -updatePublicGroupState = "update conversation set public_group_state = ? where conv = ?" +updatePublicGroupState = {- `IF EXISTS`, but that requires benchmarking -} "update conversation set public_group_state = ? where conv = ?" -- Conversations accessible by code ----------------------------------------- @@ -349,16 +353,16 @@ removeMember :: PrepQuery W (ConvId, UserId) () removeMember = "delete from member where conv = ? and user = ?" updateOtrMemberMutedStatus :: PrepQuery W (MutedStatus, Maybe Text, ConvId, UserId) () -updateOtrMemberMutedStatus = "update member set otr_muted_status = ?, otr_muted_ref = ? where conv = ? and user = ?" +updateOtrMemberMutedStatus = {- `IF EXISTS`, but that requires benchmarking -} "update member set otr_muted_status = ?, otr_muted_ref = ? where conv = ? and user = ?" updateOtrMemberArchived :: PrepQuery W (Bool, Maybe Text, ConvId, UserId) () -updateOtrMemberArchived = "update member set otr_archived = ?, otr_archived_ref = ? where conv = ? and user = ?" +updateOtrMemberArchived = {- `IF EXISTS`, but that requires benchmarking -} "update member set otr_archived = ?, otr_archived_ref = ? where conv = ? and user = ?" updateMemberHidden :: PrepQuery W (Bool, Maybe Text, ConvId, UserId) () -updateMemberHidden = "update member set hidden = ?, hidden_ref = ? where conv = ? and user = ?" +updateMemberHidden = {- `IF EXISTS`, but that requires benchmarking -} "update member set hidden = ?, hidden_ref = ? where conv = ? and user = ?" updateMemberConvRoleName :: PrepQuery W (RoleName, ConvId, UserId) () -updateMemberConvRoleName = "update member set conversation_role = ? where conv = ? and user = ?" +updateMemberConvRoleName = {- `IF EXISTS`, but that requires benchmarking -} "update member set conversation_role = ? where conv = ? and user = ?" -- Federated conversations ----------------------------------------------------- -- @@ -379,7 +383,7 @@ selectRemoteMembers :: PrepQuery R (Identity ConvId) (Domain, UserId, RoleName) selectRemoteMembers = "select user_remote_domain, user_remote_id, conversation_role from member_remote_user where conv = ?" updateRemoteMemberConvRoleName :: PrepQuery W (RoleName, ConvId, Domain, UserId) () -updateRemoteMemberConvRoleName = "update member_remote_user set conversation_role = ? where conv = ? and user_remote_domain = ? and user_remote_id = ?" +updateRemoteMemberConvRoleName = {- `IF EXISTS`, but that requires benchmarking -} "update member_remote_user set conversation_role = ? where conv = ? and user_remote_domain = ? and user_remote_id = ?" removeRemoteDomain :: PrepQuery W (ConvId, Domain) () removeRemoteDomain = "delete from member_remote_user where conv = ? and user_remote_domain = ?" @@ -424,13 +428,13 @@ selectLocalMembersByDomain = "select conv_remote_id, user from user_remote_conv -- remote conversation status for local user updateRemoteOtrMemberMutedStatus :: PrepQuery W (MutedStatus, Maybe Text, Domain, ConvId, UserId) () -updateRemoteOtrMemberMutedStatus = "update user_remote_conv set otr_muted_status = ?, otr_muted_ref = ? where conv_remote_domain = ? and conv_remote_id = ? and user = ?" +updateRemoteOtrMemberMutedStatus = {- `IF EXISTS`, but that requires benchmarking -} "update user_remote_conv set otr_muted_status = ?, otr_muted_ref = ? where conv_remote_domain = ? and conv_remote_id = ? and user = ?" updateRemoteOtrMemberArchived :: PrepQuery W (Bool, Maybe Text, Domain, ConvId, UserId) () -updateRemoteOtrMemberArchived = "update user_remote_conv set otr_archived = ?, otr_archived_ref = ? where conv_remote_domain = ? and conv_remote_id = ? and user = ?" +updateRemoteOtrMemberArchived = {- `IF EXISTS`, but that requires benchmarking -} "update user_remote_conv set otr_archived = ?, otr_archived_ref = ? where conv_remote_domain = ? and conv_remote_id = ? and user = ?" updateRemoteMemberHidden :: PrepQuery W (Bool, Maybe Text, Domain, ConvId, UserId) () -updateRemoteMemberHidden = "update user_remote_conv set hidden = ?, hidden_ref = ? where conv_remote_domain = ? and conv_remote_id = ? and user = ?" +updateRemoteMemberHidden = {- `IF EXISTS`, but that requires benchmarking -} "update user_remote_conv set hidden = ?, hidden_ref = ? where conv_remote_domain = ? and conv_remote_id = ? and user = ?" selectRemoteMemberStatus :: PrepQuery R (Domain, ConvId, UserId) (Maybe MutedStatus, Maybe Text, Maybe Bool, Maybe Text, Maybe Bool, Maybe Text) selectRemoteMemberStatus = "select otr_muted_status, otr_muted_ref, otr_archived, otr_archived_ref, hidden, hidden_ref from user_remote_conv where conv_remote_domain = ? and conv_remote_id = ? and user = ?" @@ -443,13 +447,13 @@ selectClients = "select user, clients from clients where user in ?" rmClients :: PrepQuery W (Identity UserId) () rmClients = "delete from clients where user = ?" -addMemberClient :: ClientId -> QueryString W (Identity UserId) () -addMemberClient c = +upsertMemberAddClient :: ClientId -> QueryString W (Identity UserId) () +upsertMemberAddClient c = let t = LT.fromStrict (client c) in QueryString $ "update clients set clients = clients + {'" <> t <> "'} where user = ?" -rmMemberClient :: ClientId -> QueryString W (Identity UserId) () -rmMemberClient c = +upsertMemberRmClient :: ClientId -> QueryString W (Identity UserId) () +upsertMemberRmClient c = let t = LT.fromStrict (client c) in QueryString $ "update clients set clients = clients - {'" <> t <> "'} where user = ?" @@ -566,7 +570,7 @@ selectSearchVisibility = updateSearchVisibility :: PrepQuery W (TeamSearchVisibility, TeamId) () updateSearchVisibility = - "update team set search_visibility = ? where team = ?" + {- `IF EXISTS`, but that requires benchmarking -} "update team set search_visibility = ? where team = ?" -- Custom Backend ----------------------------------------------------------- @@ -574,8 +578,8 @@ selectCustomBackend :: PrepQuery R (Identity Domain) (HttpsUrl, HttpsUrl) selectCustomBackend = "select config_json_url, webapp_welcome_url from custom_backend where domain = ?" -updateCustomBackend :: PrepQuery W (HttpsUrl, HttpsUrl, Domain) () -updateCustomBackend = +upsertCustomBackend :: PrepQuery W (HttpsUrl, HttpsUrl, Domain) () +upsertCustomBackend = "update custom_backend set config_json_url = ?, webapp_welcome_url = ? where domain = ?" deleteCustomBackend :: PrepQuery W (Identity Domain) () diff --git a/services/galley/src/Galley/Cassandra/Team.hs b/services/galley/src/Galley/Cassandra/Team.hs index bd22909d67..bbea1852b8 100644 --- a/services/galley/src/Galley/Cassandra/Team.hs +++ b/services/galley/src/Galley/Cassandra/Team.hs @@ -232,6 +232,7 @@ addTeamMember t m = m ^? invitation . _Just . _2 ) addPrepQuery Cql.insertUserTeam (m ^. userId, t) + when (m `hasPermission` SetBilling) $ addPrepQuery Cql.insertBillingTeamMember (t, m ^. userId) diff --git a/services/gundeck/src/Gundeck/Push/Data.hs b/services/gundeck/src/Gundeck/Push/Data.hs index f00ddc1903..c688f64f4d 100644 --- a/services/gundeck/src/Gundeck/Push/Data.hs +++ b/services/gundeck/src/Gundeck/Push/Data.hs @@ -52,7 +52,7 @@ updateArn :: MonadClient m => UserId -> Transport -> AppName -> Token -> Endpoin updateArn uid transport app token arn = retry x5 $ write q (params LocalQuorum (arn, uid, transport, app, token)) where q :: PrepQuery W (EndpointArn, UserId, Transport, AppName, Token) () - q = "update user_push set arn = ? where usr = ? and transport = ? and app = ? and ptoken = ?" + q = {- `IF EXISTS`, but that requires benchmarking -} "update user_push set arn = ? where usr = ? and transport = ? and app = ? and ptoken = ?" delete :: MonadClient m => UserId -> Transport -> AppName -> Token -> m () delete u t a p = retry x5 $ write q (params LocalQuorum (u, t, a, p)) diff --git a/services/spar/src/Spar/Sem/IdPConfigStore/Cassandra.hs b/services/spar/src/Spar/Sem/IdPConfigStore/Cassandra.hs index 2b3d347007..30c985dd9d 100644 --- a/services/spar/src/Spar/Sem/IdPConfigStore/Cassandra.hs +++ b/services/spar/src/Spar/Sem/IdPConfigStore/Cassandra.hs @@ -356,7 +356,7 @@ setReplacedBy (Replaced old) (Replacing new) = do retry x5 . write ins $ params LocalQuorum (new, old) where ins :: PrepQuery W (SAML.IdPId, SAML.IdPId) () - ins = "UPDATE idp SET replaced_by = ? WHERE idp = ?" + ins = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE idp SET replaced_by = ? WHERE idp = ?" -- | See also: 'setReplacedBy'. clearReplacedBy :: @@ -367,7 +367,7 @@ clearReplacedBy (Replaced old) = do retry x5 . write ins $ params LocalQuorum (Identity old) where ins :: PrepQuery W (Identity SAML.IdPId) () - ins = "UPDATE idp SET replaced_by = null WHERE idp = ?" + ins = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE idp SET replaced_by = null WHERE idp = ?" -- | If the IdP is 'WireIdPAPIV1', it must be deleted globally, if it is 'WireIdPAPIV2', it -- must be deleted inside one team. 'V1' can be either in the old table without team index, diff --git a/tools/db/migrate-sso-feature-flag/src/Work.hs b/tools/db/migrate-sso-feature-flag/src/Work.hs index ff56a09447..d64570ce43 100644 --- a/tools/db/migrate-sso-feature-flag/src/Work.hs +++ b/tools/db/migrate-sso-feature-flag/src/Work.hs @@ -67,5 +67,6 @@ writeSsoFlags = mapM_ (`setSSOTeamConfig` FeatureStatusEnabled) setSSOTeamConfig :: MonadClient m => TeamId -> FeatureStatus -> m () setSSOTeamConfig tid ssoTeamConfigStatus = do retry x5 $ write updateSSOTeamConfig (params LocalQuorum (ssoTeamConfigStatus, tid)) + updateSSOTeamConfig :: PrepQuery W (FeatureStatus, TeamId) () - updateSSOTeamConfig = "update team_features set sso_status = ? where team_id = ?" + updateSSOTeamConfig = {- `IF EXISTS`, but that requires benchmarking -} "update team_features set sso_status = ? where team_id = ?" diff --git a/tools/db/repair-handles/src/Work.hs b/tools/db/repair-handles/src/Work.hs index 7b91c7c98c..313c402117 100644 --- a/tools/db/repair-handles/src/Work.hs +++ b/tools/db/repair-handles/src/Work.hs @@ -142,7 +142,7 @@ executeAction env = \case params LocalQuorum (handle, uid) where updateHandle :: PrepQuery W (Handle, UserId) () - updateHandle = "UPDATE user SET handle = ? WHERE id = ?" + updateHandle = {- `IF EXISTS`, but that requires benchmarking -} "UPDATE user SET handle = ? WHERE id = ?" removeHandle :: Env -> Handle -> IO () removeHandle Env {..} handle =