diff --git a/keyserver/src/creators/invite-link-creator.js b/keyserver/src/creators/invite-link-creator.js --- a/keyserver/src/creators/invite-link-creator.js +++ b/keyserver/src/creators/invite-link-creator.js @@ -30,9 +30,9 @@ threadPermissions.MANAGE_INVITE_LINKS, ); const existingPrimaryLinksPromise = fetchPrimaryInviteLinks(viewer); - const fetchThreadInfoPromise = fetchServerThreadInfos( - SQL`t.id = ${request.communityID}`, - ); + const fetchThreadInfoPromise = fetchServerThreadInfos({ + threadID: request.communityID, + }); const [hasPermission, existingPrimaryLinks, { threadInfos }] = await Promise.all([ permissionPromise, diff --git a/keyserver/src/creators/message-creator.js b/keyserver/src/creators/message-creator.js --- a/keyserver/src/creators/message-creator.js +++ b/keyserver/src/creators/message-creator.js @@ -268,7 +268,7 @@ if (updatedThreads.length > 0) { const [{ threadInfos: serverThreadInfos }] = await Promise.all([ - fetchServerThreadInfos(SQL`t.id IN (${updatedThreads})`), + fetchServerThreadInfos({ threadIDs: new Set(updatedThreads) }), dbQuery(updateThreads), dbQuery(updateMemberships), ]); diff --git a/keyserver/src/creators/update-creator.js b/keyserver/src/creators/update-creator.js --- a/keyserver/src/creators/update-creator.js +++ b/keyserver/src/creators/update-creator.js @@ -440,10 +440,9 @@ const promises = {}; if (!viewerInfo.threadInfos && threadIDsNeedingFetch.size > 0) { - promises.threadResult = fetchThreadInfos( - viewer, - SQL`t.id IN (${[...threadIDsNeedingFetch]})`, - ); + promises.threadResult = fetchThreadInfos(viewer, { + threadIDs: threadIDsNeedingFetch, + }); } let calendarQuery: ?CalendarQuery = viewerInfo.calendarQuery diff --git a/keyserver/src/deleters/thread-deleters.js b/keyserver/src/deleters/thread-deleters.js --- a/keyserver/src/deleters/thread-deleters.js +++ b/keyserver/src/deleters/thread-deleters.js @@ -54,7 +54,7 @@ const threadIDs = await fetchContainedThreadIDs(threadID); const [{ threadInfos: serverThreadInfos }] = await Promise.all([ - fetchServerThreadInfos(SQL`t.id IN (${threadIDs})`), + fetchServerThreadInfos({ threadIDs: new Set(threadID) }), rescindPushNotifs( SQL`n.thread IN (${threadIDs})`, SQL`IF(m.thread IN (${threadIDs}), NULL, m.thread)`, diff --git a/keyserver/src/fetchers/thread-fetchers.js b/keyserver/src/fetchers/thread-fetchers.js --- a/keyserver/src/fetchers/thread-fetchers.js +++ b/keyserver/src/fetchers/thread-fetchers.js @@ -19,18 +19,53 @@ import { ServerError } from 'lib/utils/errors.js'; import { getUploadURL } from './upload-fetchers.js'; -import { dbQuery, SQL } from '../database/database.js'; +import { dbQuery, SQL, mergeAndConditions } from '../database/database.js'; import type { SQLStatementType } from '../database/types.js'; import type { Viewer } from '../session/viewer.js'; +type FetchThreadInfosFilter = $Shape<{ + +threadID: string, + +threadIDs: $ReadOnlySet, + +parentThreadID: string, + +sourceMessageID: string, +}>; +function constructWhereClause( + filter: FetchThreadInfosFilter, +): SQLStatementType { + const conditions = []; + + if (filter.threadID) { + conditions.push(SQL`t.id = ${filter.threadID}`); + } + + if (filter.threadIDs) { + conditions.push(SQL`t.id IN (${[...filter.threadIDs]})`); + } + + if (filter.parentThreadID) { + conditions.push(SQL`t.parent_thread_id = ${filter.parentThreadID}`); + } + + if (filter.sourceMessageID) { + conditions.push(SQL`t.source_message = ${filter.sourceMessageID}`); + } + + if (conditions.length === 0) { + return SQL``; + } + + const clause = mergeAndConditions(conditions); + return SQL`WHERE `.append(clause); +} + type FetchServerThreadInfosResult = { +threadInfos: { +[id: string]: ServerThreadInfo }, }; async function fetchServerThreadInfos( - condition?: SQLStatementType, + filter?: FetchThreadInfosFilter, ): Promise { - const whereClause = condition ? SQL`WHERE `.append(condition) : ''; + const whereClause = filter ? constructWhereClause(filter) : ''; const rolesQuery = SQL` SELECT t.id, t.default_role, r.id AS role, r.name, r.permissions @@ -156,9 +191,9 @@ async function fetchThreadInfos( viewer: Viewer, - condition?: SQLStatementType, + filter?: FetchThreadInfosFilter, ): Promise { - const serverResult = await fetchServerThreadInfos(condition); + const serverResult = await fetchServerThreadInfos(filter); return rawThreadInfosFromServerThreadInfos(viewer, serverResult); } @@ -230,9 +265,9 @@ if (!parentThreadID) { return { containingThreadID: null, community: null, depth: 0 }; } - const parentThreadInfos = await fetchServerThreadInfos( - SQL`t.id = ${parentThreadID}`, - ); + const parentThreadInfos = await fetchServerThreadInfos({ + threadID: parentThreadID, + }); const parentThreadInfo = parentThreadInfos.threadInfos[parentThreadID]; if (!parentThreadInfo) { throw new ServerError('invalid_parameters'); @@ -276,7 +311,7 @@ message: RawMessageInfo | MessageInfo, ): Promise { const threadID = message.threadID; - const threads = await fetchServerThreadInfos(SQL`t.id = ${threadID}`); + const threads = await fetchServerThreadInfos({ threadID }); return threads.threadInfos[threadID]; } diff --git a/keyserver/src/fetchers/thread-permission-fetchers.js b/keyserver/src/fetchers/thread-permission-fetchers.js --- a/keyserver/src/fetchers/thread-permission-fetchers.js +++ b/keyserver/src/fetchers/thread-permission-fetchers.js @@ -153,7 +153,7 @@ } const [{ threadInfos }, userInfos] = await Promise.all([ - fetchThreadInfos(viewer, SQL`t.id IN (${[...threadIDs]})`), + fetchThreadInfos(viewer, { threadIDs: new Set(threadIDs) }), fetchKnownUserInfos(viewer), ]); diff --git a/keyserver/src/push/send.js b/keyserver/src/push/send.js --- a/keyserver/src/push/send.js +++ b/keyserver/src/push/send.js @@ -495,9 +495,7 @@ const promises = {}; // These threadInfos won't have currentUser set - promises.threadResult = fetchServerThreadInfos( - SQL`t.id IN (${[...threadIDs]})`, - ); + promises.threadResult = fetchServerThreadInfos({ threadIDs }); if (threadWithChangedNamesToMessages.size > 0) { const typesThatAffectName = [ messageTypes.CHANGE_SETTINGS, diff --git a/keyserver/src/responders/message-responders.js b/keyserver/src/responders/message-responders.js --- a/keyserver/src/responders/message-responders.js +++ b/keyserver/src/responders/message-responders.js @@ -43,7 +43,6 @@ } from 'lib/utils/validation-utils.js'; import createMessages from '../creators/message-creator.js'; -import { SQL } from '../database/database.js'; import { fetchMessageInfos, fetchMessageInfoForLocalID, @@ -303,7 +302,7 @@ const [serverThreadInfos, hasPermission, targetMessageUserInfos] = await Promise.all([ - fetchServerThreadInfos(SQL`t.id = ${threadID}`), + fetchServerThreadInfos({ threadID }), checkThreadPermission( viewer, threadID, @@ -393,11 +392,12 @@ const [serverThreadInfos, hasPermission, rawSidebarThreadInfos] = await Promise.all([ - fetchServerThreadInfos(SQL`t.id = ${threadID}`), + fetchServerThreadInfos({ threadID }), checkThreadPermission(viewer, threadID, threadPermissions.EDIT_MESSAGE), - fetchServerThreadInfos( - SQL`t.parent_thread_id = ${threadID} AND t.source_message = ${targetMessageID}`, - ), + fetchServerThreadInfos({ + parentThreadID: threadID, + sourceMessageID: targetMessageID, + }), ]); const targetMessageThreadInfo = serverThreadInfos.threadInfos[threadID]; diff --git a/keyserver/src/scripts/add-thread-ancestry.js b/keyserver/src/scripts/add-thread-ancestry.js deleted file mode 100644 --- a/keyserver/src/scripts/add-thread-ancestry.js +++ /dev/null @@ -1,84 +0,0 @@ -// @flow - -import { - getContainingThreadID, - getCommunity, -} from 'lib/shared/thread-utils.js'; -import type { ServerThreadInfo } from 'lib/types/thread-types.js'; - -import { main } from './utils.js'; -import { dbQuery, SQL } from '../database/database.js'; -import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; - -async function addColumnAndIndexes() { - await dbQuery(SQL` - ALTER TABLE threads - ADD containing_thread_id BIGINT(20) NULL AFTER parent_thread_id, - ADD community BIGINT(20) NULL AFTER containing_thread_id, - ADD depth INT UNSIGNED NOT NULL DEFAULT 0 AFTER community, - ADD INDEX parent_thread_id (parent_thread_id), - ADD INDEX community (community), - ADD INDEX containing_thread_id (containing_thread_id); - `); -} - -async function setColumn() { - const stack = [[null, SQL`t.parent_thread_id IS NULL`]]; - - while (stack.length > 0) { - const [parentThreadInfo, predicate] = stack.shift(); - const { threadInfos } = await fetchServerThreadInfos(predicate); - const updatedThreadInfos = await setColumnForLayer( - parentThreadInfo, - threadInfos, - ); - for (const threadInfo of updatedThreadInfos) { - stack.push([threadInfo, SQL`t.parent_thread_id = ${threadInfo.id}`]); - } - } -} - -async function setColumnForLayer( - parentThreadInfo: ?ServerThreadInfo, - threadInfos: { +[id: string]: ServerThreadInfo }, -): Promise { - const updatedThreadInfos = []; - for (const threadID in threadInfos) { - const threadInfo = threadInfos[threadID]; - const containingThreadID = getContainingThreadID( - parentThreadInfo, - threadInfo.type, - ); - const community = getCommunity(parentThreadInfo); - if (!containingThreadID && !community) { - console.log( - `containingThreadID and community are null for ${threadID}, ` + - 'skipping...', - ); - updatedThreadInfos.push(threadInfo); - continue; - } - const depth = parentThreadInfo ? parentThreadInfo.depth + 1 : 0; - console.log( - `setting containingThreadID to ${containingThreadID ?? 'null'}, ` + - `community to ${community ?? 'null'}, and ` + - `depth to ${depth} for ${threadID}`, - ); - await dbQuery(SQL` - UPDATE threads - SET containing_thread_id = ${containingThreadID}, - community = ${community}, - depth = ${depth} - WHERE id = ${threadID} - `); - updatedThreadInfos.push({ - ...threadInfo, - containingThreadID, - community, - depth, - }); - } - return updatedThreadInfos; -} - -main([addColumnAndIndexes, setColumn]); diff --git a/keyserver/src/scripts/soft-launch-migration.js b/keyserver/src/scripts/soft-launch-migration.js deleted file mode 100644 --- a/keyserver/src/scripts/soft-launch-migration.js +++ /dev/null @@ -1,430 +0,0 @@ -// @flow - -import invariant from 'invariant'; - -import ashoat from 'lib/facts/ashoat.js'; -import bots from 'lib/facts/bots.js'; -import genesis from 'lib/facts/genesis.js'; -import testers from 'lib/facts/testers.js'; -import { messageTypes } from 'lib/types/message-types-enum.js'; -import { threadTypes, type ThreadType } from 'lib/types/thread-types-enum.js'; - -import { main } from './utils.js'; -import createMessages from '../creators/message-creator.js'; -import { createThread } from '../creators/thread-creator.js'; -import { dbQuery, SQL } from '../database/database.js'; -import { fetchServerThreadInfos } from '../fetchers/thread-fetchers.js'; -import { fetchAllUserIDs } from '../fetchers/user-fetchers.js'; -import { createScriptViewer } from '../session/scripts.js'; -import type { Viewer } from '../session/viewer.js'; -import { - recalculateThreadPermissions, - commitMembershipChangeset, - saveMemberships, -} from '../updaters/thread-permission-updaters.js'; -import { updateThread } from '../updaters/thread-updaters.js'; - -const batchSize = 10; -const createThreadOptions = { forceAddMembers: true }; -const updateThreadOptions = { - forceUpdateRoot: true, - silenceMessages: true, - ignorePermissions: true, -}; -const convertUnadminnedToCommunities = ['311733', '421638']; -const convertToAnnouncementCommunities = ['375310']; -const convertToAnnouncementSubthreads = ['82649']; -const threadsWithMissingParent = ['534395']; -const personalThreadsWithMissingMembers = [ - '82161', - '103111', - '210609', - '227049', -]; -const excludeFromTestersThread = new Set([ - '1402', - '39227', - '156159', - '526973', - '740732', -]); - -async function createGenesisCommunity() { - const genesisThreadInfos = await fetchServerThreadInfos( - SQL`t.id = ${genesis.id}`, - ); - const genesisThreadInfo = genesisThreadInfos.threadInfos[genesis.id]; - if (genesisThreadInfo && genesisThreadInfo.type === threadTypes.GENESIS) { - return; - } else if (genesisThreadInfo) { - await updateGenesisCommunityType(); - return; - } - - console.log('creating GENESIS community'); - - const idInsertQuery = SQL` - INSERT INTO ids(id, table_name) - VALUES ${[[genesis.id, 'threads']]} - `; - await dbQuery(idInsertQuery); - - const ashoatViewer = createScriptViewer(ashoat.id); - const allUserIDs = await fetchAllUserIDs(); - const nonAshoatUserIDs = allUserIDs.filter(id => id !== ashoat.id); - - await createThread( - ashoatViewer, - { - id: genesis.id, - type: threadTypes.GENESIS, - name: genesis.name, - description: genesis.description, - initialMemberIDs: nonAshoatUserIDs, - }, - createThreadOptions, - ); - - await createMessages( - ashoatViewer, - genesis.introMessages.map(message => ({ - type: messageTypes.TEXT, - threadID: genesis.id, - creatorID: ashoat.id, - time: Date.now(), - text: message, - })), - ); - - console.log('creating testers thread'); - - const testerUserIDs = nonAshoatUserIDs.filter( - userID => !excludeFromTestersThread.has(userID), - ); - const { newThreadID } = await createThread( - ashoatViewer, - { - type: threadTypes.COMMUNITY_SECRET_SUBTHREAD, - name: testers.name, - description: testers.description, - initialMemberIDs: testerUserIDs, - }, - createThreadOptions, - ); - invariant( - newThreadID, - 'newThreadID for tester thread creation should be set', - ); - - await createMessages( - ashoatViewer, - testers.introMessages.map(message => ({ - type: messageTypes.TEXT, - threadID: newThreadID, - creatorID: ashoat.id, - time: Date.now(), - text: message, - })), - ); -} - -async function updateGenesisCommunityType() { - console.log('updating GENESIS community to GENESIS type'); - - const ashoatViewer = createScriptViewer(ashoat.id); - await updateThread( - ashoatViewer, - { - threadID: genesis.id, - changes: { - type: threadTypes.GENESIS, - }, - }, - updateThreadOptions, - ); -} - -async function convertExistingCommunities() { - const communityQuery = SQL` - SELECT t.id, t.name - FROM threads t - LEFT JOIN roles r ON r.thread = t.id - LEFT JOIN memberships m ON m.thread = t.id - WHERE t.type = ${threadTypes.COMMUNITY_SECRET_SUBTHREAD} - AND t.parent_thread_id IS NULL - GROUP BY t.id - HAVING COUNT(DISTINCT r.id) > 1 AND COUNT(DISTINCT m.user) > 2 - `; - const [convertToCommunity] = await dbQuery(communityQuery); - - const botViewer = createScriptViewer(bots.commbot.userID); - await convertThreads( - botViewer, - convertToCommunity, - threadTypes.COMMUNITY_ROOT, - ); -} - -async function convertThreads( - viewer: Viewer, - threads: Array<{ +id: number, +name: string }>, - type: ThreadType, -) { - while (threads.length > 0) { - const batch = threads.splice(0, batchSize); - await Promise.all( - batch.map(async thread => { - console.log(`converting ${JSON.stringify(thread)} to ${type}`); - return await updateThread( - viewer, - { - threadID: thread.id.toString(), - changes: { type }, - }, - updateThreadOptions, - ); - }), - ); - } -} - -async function convertUnadminnedCommunities() { - const communityQuery = SQL` - SELECT id, name - FROM threads - WHERE id IN (${convertUnadminnedToCommunities}) AND - type = ${threadTypes.COMMUNITY_SECRET_SUBTHREAD} - `; - const [convertToCommunity] = await dbQuery(communityQuery); - - // We use ashoat here to make sure he becomes the admin of these communities - const ashoatViewer = createScriptViewer(ashoat.id); - await convertThreads( - ashoatViewer, - convertToCommunity, - threadTypes.COMMUNITY_ROOT, - ); -} - -async function convertAnnouncementCommunities() { - const announcementCommunityQuery = SQL` - SELECT id, name - FROM threads - WHERE id IN (${convertToAnnouncementCommunities}) AND - type != ${threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT} - `; - const [convertToAnnouncementCommunity] = await dbQuery( - announcementCommunityQuery, - ); - - const botViewer = createScriptViewer(bots.commbot.userID); - await convertThreads( - botViewer, - convertToAnnouncementCommunity, - threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT, - ); -} - -async function convertAnnouncementSubthreads() { - const announcementSubthreadQuery = SQL` - SELECT id, name - FROM threads - WHERE id IN (${convertToAnnouncementSubthreads}) AND - type != ${threadTypes.COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD} - `; - const [convertToAnnouncementSubthread] = await dbQuery( - announcementSubthreadQuery, - ); - - const botViewer = createScriptViewer(bots.commbot.userID); - await convertThreads( - botViewer, - convertToAnnouncementSubthread, - threadTypes.COMMUNITY_OPEN_ANNOUNCEMENT_SUBTHREAD, - ); -} - -async function fixThreadsWithMissingParent() { - const threadsWithMissingParentQuery = SQL` - SELECT id, name - FROM threads - WHERE id IN (${threadsWithMissingParent}) AND - type != ${threadTypes.COMMUNITY_SECRET_SUBTHREAD} - `; - const [threadsWithMissingParentResult] = await dbQuery( - threadsWithMissingParentQuery, - ); - - const botViewer = createScriptViewer(bots.commbot.userID); - while (threadsWithMissingParentResult.length > 0) { - const batch = threadsWithMissingParentResult.splice(0, batchSize); - await Promise.all( - batch.map(async thread => { - console.log(`fixing ${JSON.stringify(thread)} with missing parent`); - return await updateThread( - botViewer, - { - threadID: thread.id.toString(), - changes: { - parentThreadID: null, - type: threadTypes.COMMUNITY_SECRET_SUBTHREAD, - }, - }, - updateThreadOptions, - ); - }), - ); - } -} - -async function fixPersonalThreadsWithMissingMembers() { - const missingMembersQuery = SQL` - SELECT thread, user - FROM memberships - WHERE thread IN (${personalThreadsWithMissingMembers}) AND role <= 0 - `; - const [missingMembers] = await dbQuery(missingMembersQuery); - - const botViewer = createScriptViewer(bots.commbot.userID); - for (const row of missingMembers) { - console.log(`fixing ${JSON.stringify(row)} with missing member`); - await updateThread( - botViewer, - { - threadID: row.thread.toString(), - changes: { - newMemberIDs: [row.user.toString()], - }, - }, - updateThreadOptions, - ); - } -} - -async function moveThreadsToGenesis() { - const noParentQuery = SQL` - SELECT id, name - FROM threads - WHERE type != ${threadTypes.COMMUNITY_ROOT} - AND type != ${threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT} - AND type != ${threadTypes.GENESIS} - AND parent_thread_id IS NULL - `; - const [noParentThreads] = await dbQuery(noParentQuery); - - const botViewer = createScriptViewer(bots.commbot.userID); - while (noParentThreads.length > 0) { - const batch = noParentThreads.splice(0, batchSize); - await Promise.all( - batch.map(async thread => { - console.log(`processing ${JSON.stringify(thread)}`); - return await updateThread( - botViewer, - { - threadID: thread.id.toString(), - changes: { - parentThreadID: genesis.id, - }, - }, - updateThreadOptions, - ); - }), - ); - } - - const childQuery = SQL` - SELECT id, name - FROM threads - WHERE type != ${threadTypes.COMMUNITY_ROOT} - AND type != ${threadTypes.COMMUNITY_ANNOUNCEMENT_ROOT} - AND type != ${threadTypes.GENESIS} - AND parent_thread_id IS NOT NULL - AND parent_thread_id != ${genesis.id} - `; - const [childThreads] = await dbQuery(childQuery); - - for (const childThread of childThreads) { - // We go one by one because the changes in a parent thread can affect a - // child thread. If the child thread update starts at the same time as an - // update for its parent thread, a race can cause incorrect results for the - // child thread (in particular for the permissions on the memberships table) - console.log(`processing ${JSON.stringify(childThread)}`); - await updateThread( - botViewer, - { - threadID: childThread.id.toString(), - changes: {}, - }, - updateThreadOptions, - ); - } -} - -async function clearMembershipPermissions() { - const membershipPermissionQuery = SQL` - SELECT DISTINCT thread - FROM memberships - WHERE JSON_EXTRACT(permissions, '$.membership') IS NOT NULL - `; - const [membershipPermissionResult] = await dbQuery(membershipPermissionQuery); - if (membershipPermissionResult.length === 0) { - return; - } - - const botViewer = createScriptViewer(bots.commbot.userID); - for (const row of membershipPermissionResult) { - const threadID = row.thread.toString(); - console.log(`clearing membership permissions for ${threadID}`); - const changeset = await recalculateThreadPermissions(threadID); - await commitMembershipChangeset(botViewer, changeset); - } - - console.log('clearing -1 rows...'); - const emptyMembershipDeletionQuery = SQL` - DELETE FROM memberships - WHERE role = -1 AND permissions IS NULL - `; - await dbQuery(emptyMembershipDeletionQuery); - - await createMembershipsForFormerMembers(); -} - -async function createMembershipsForFormerMembers() { - const [result] = await dbQuery(SQL` - SELECT DISTINCT thread, user - FROM messages m - WHERE NOT EXISTS ( - SELECT thread, user FROM memberships mm - WHERE m.thread = mm.thread AND m.user = mm.user - ) - `); - - const rowsToSave = []; - for (const row of result) { - rowsToSave.push({ - operation: 'save', - userID: row.user.toString(), - threadID: row.thread.toString(), - userNeedsFullThreadDetails: false, - intent: 'none', - permissions: null, - permissionsForChildren: null, - role: '-1', - oldRole: '-1', - }); - } - - await saveMemberships(rowsToSave); -} - -main([ - createGenesisCommunity, - convertExistingCommunities, - convertUnadminnedCommunities, - convertAnnouncementCommunities, - convertAnnouncementSubthreads, - fixThreadsWithMissingParent, - fixPersonalThreadsWithMissingMembers, - moveThreadsToGenesis, - clearMembershipPermissions, -]); diff --git a/keyserver/src/socket/session-utils.js b/keyserver/src/socket/session-utils.js --- a/keyserver/src/socket/session-utils.js +++ b/keyserver/src/socket/session-utils.js @@ -41,7 +41,6 @@ import { createOlmSession } from '../creators/olm-session-creator.js'; import { saveOneTimeKeys } from '../creators/one-time-keys-creator.js'; import createReport from '../creators/report-creator.js'; -import { SQL } from '../database/database.js'; import { fetchEntryInfos, fetchEntryInfosByID, @@ -424,7 +423,7 @@ fetchAllEntries = false, fetchAllUserInfos = false, fetchUserInfo = false; - const threadIDsToFetch = [], + const threadIDsToFetch = new Set(), entryIDsToFetch = [], userIDsToFetch = []; for (const key of invalidKeys) { @@ -438,7 +437,7 @@ fetchUserInfo = true; } else if (key.startsWith('threadInfo|')) { const [, threadID] = key.split('|'); - threadIDsToFetch.push(threadID); + threadIDsToFetch.add(threadID); } else if (key.startsWith('entryInfo|')) { const [, entryID] = key.split('|'); entryIDsToFetch.push(entryID); @@ -451,11 +450,10 @@ const fetchPromises = {}; if (fetchAllThreads) { fetchPromises.threadsResult = fetchThreadInfos(viewer); - } else if (threadIDsToFetch.length > 0) { - fetchPromises.threadsResult = fetchThreadInfos( - viewer, - SQL`t.id IN (${threadIDsToFetch})`, - ); + } else if (threadIDsToFetch.size > 0) { + fetchPromises.threadsResult = fetchThreadInfos(viewer, { + threadIDs: threadIDsToFetch, + }); } if (fetchAllEntries) { fetchPromises.entriesResult = fetchEntryInfos(viewer, [calendarQuery]); diff --git a/keyserver/src/updaters/thread-permission-updaters.js b/keyserver/src/updaters/thread-permission-updaters.js --- a/keyserver/src/updaters/thread-permission-updaters.js +++ b/keyserver/src/updaters/thread-permission-updaters.js @@ -1052,9 +1052,9 @@ rescindPushNotifsForMemberDeletion(toRescindPushNotifs), ]); - const serverThreadInfoFetchResult = await fetchServerThreadInfos( - SQL`t.id IN (${[...changedThreadIDs]})`, - ); + const serverThreadInfoFetchResult = await fetchServerThreadInfos({ + threadIDs: changedThreadIDs, + }); const { threadInfos: serverThreadInfos } = serverThreadInfoFetchResult; const time = Date.now(); diff --git a/keyserver/src/updaters/thread-updaters.js b/keyserver/src/updaters/thread-updaters.js --- a/keyserver/src/updaters/thread-updaters.js +++ b/keyserver/src/updaters/thread-updaters.js @@ -77,7 +77,7 @@ request.threadID, threadPermissions.CHANGE_ROLE, ), - fetchThreadInfos(viewer, SQL`t.id = ${request.threadID}`), + fetchThreadInfos(viewer, { threadID: request.threadID }), ]); if (memberIDs.length === 0) { throw new ServerError('invalid_parameters'); @@ -237,7 +237,7 @@ } const [fetchThreadResult, hasPermission] = await Promise.all([ - fetchThreadInfos(viewer, SQL`t.id = ${request.threadID}`), + fetchThreadInfos(viewer, { threadID: request.threadID }), checkThreadPermission( viewer, request.threadID, @@ -376,9 +376,9 @@ throw new ServerError('invalid_parameters'); } - validationPromises.serverThreadInfos = fetchServerThreadInfos( - SQL`t.id = ${request.threadID}`, - ); + validationPromises.serverThreadInfos = fetchServerThreadInfos({ + threadID: request.threadID, + }); validationPromises.hasNecessaryPermissions = (async () => { if (ignorePermissions) { @@ -895,7 +895,7 @@ } const [{ threadInfos: serverThreadInfos }] = await Promise.all([ - fetchServerThreadInfos(SQL`t.id = ${threadID}`), + fetchServerThreadInfos({ threadID }), dbQuery(togglePinQuery), dbQuery(updateThreadQuery), ]); diff --git a/keyserver/src/updaters/user-subscription-updaters.js b/keyserver/src/updaters/user-subscription-updaters.js --- a/keyserver/src/updaters/user-subscription-updaters.js +++ b/keyserver/src/updaters/user-subscription-updaters.js @@ -21,10 +21,9 @@ throw new ServerError('not_logged_in'); } - const { threadInfos } = await fetchThreadInfos( - viewer, - SQL`t.id = ${update.threadID}`, - ); + const { threadInfos } = await fetchThreadInfos(viewer, { + threadID: update.threadID, + }); const threadInfo = threadInfos[update.threadID]; if (!viewerIsMember(threadInfo)) { throw new ServerError('not_member');