diff --git a/.github/workflows/esrupgrade-common.yml b/.github/workflows/esrupgrade-common.yml new file mode 100644 index 0000000000..b0cac7d6d2 --- /dev/null +++ b/.github/workflows/esrupgrade-common.yml @@ -0,0 +1,159 @@ +name: ESR Upgrade +on: + workflow_call: + inputs: + db-dump-url: + required: true + type: string + initial-version: + required: true + type: string + final-version: + required: true + type: string +env: + COMPOSE_PROJECT_NAME: ghactions + BUILD_IMAGE: mattermost/mattermost-enterprise-edition:${{ inputs.final-version }} + MYSQL_CONN_ARGS: -h localhost -P 3306 --protocol=tcp -ummuser -pmostest mattermost_test + DUMP_SERVER_NAME: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.dump.server.sql + DUMP_SCRIPT_NAME: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.dump.script.sql + MIGRATION_SCRIPT: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.mysql.up.sql + CLEANUP_SCRIPT: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.mysql.cleanup.sql + PREPROCESS_SCRIPT: esr.common.mysql.preprocess.sql + DIFF_NAME: esr.${{ inputs.initial-version }}-${{ inputs.final-version }}.diff +jobs: + esr-upgrade-server: + runs-on: ubuntu-latest-8-cores + timeout-minutes: 30 + steps: + - name: Checkout mattermost-server + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - name: Run docker compose + run: | + cd server/build + docker-compose --no-ansi run --rm start_dependencies + cat ../tests/test-data.ldif | docker-compose --no-ansi exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest'; + docker-compose --no-ansi exec -T minio sh -c 'mkdir -p /data/mattermost-test'; + docker-compose --no-ansi ps + - name: Wait for docker compose + run: | + until docker network inspect ghactions_mm-test; do echo "Waiting for Docker Compose Network..."; sleep 1; done; + docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://mysql:3306; do echo waiting for mysql; sleep 5; done;" + docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://elasticsearch:9200; do echo waiting for elasticsearch; sleep 5; done;" + - name: Initialize the database with the source DB dump + run: | + curl ${{ inputs.db-dump-url }} | zcat | docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS + - name: Common preprocessing of the DB dump + run: | + cd server/scripts/esrupgrades + docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $PREPROCESS_SCRIPT + - name: Pull EE image + run: | + docker pull $BUILD_IMAGE + - name: Run migration through server + run: | + mkdir -p client/plugins + cd server/build + # Run the server in the background to trigger the migrations + docker run --name mmserver \ + --net ghactions_mm-test \ + --ulimit nofile=8096:8096 \ + --env-file=dotenv/test.env \ + --env MM_SQLSETTINGS_DRIVERNAME="mysql" \ + --env MM_SQLSETTINGS_DATASOURCE="mmuser:mostest@tcp(mysql:3306)/mattermost_test?charset=utf8mb4,utf8&multiStatements=true" \ + -v ~/work/mattermost-server:/mattermost-server \ + -w /mattermost-server/mattermost-server \ + $BUILD_IMAGE & + # In parallel, wait for the migrations to finish. + # To verify this, we check that the server has finished the startup job through the log line "Server is listening on" + until docker logs mmserver | grep "Server is listening on"; do\ + echo "Waiting for migrations to finish..."; \ + sleep 1; \ + done; + # Make sure to stop the server. Also, redirect output to null; + # otherwise, the name of the container gets written to the console, which is weird + docker stop mmserver > /dev/null + - name: Cleanup DB + run : | + cd server/scripts/esrupgrades + docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $CLEANUP_SCRIPT + - name: Dump upgraded database + run: | + # Use --skip-opt to have each INSERT into one line. + # Use --set-gtid-purged=OFF to suppress GTID-related statements. + docker exec -i ghactions_mysql_1 mysqldump \ + --skip-opt --set-gtid-purged=OFF \ + $MYSQL_CONN_ARGS > $DUMP_SERVER_NAME + - name: Cleanup dump and compress + run: | + # We skip the very last line, which simply contains the date of the dump + head -n -1 ${DUMP_SERVER_NAME} | gzip > ${DUMP_SERVER_NAME}.gz + - name: Upload dump + uses: actions/upload-artifact@v3 + with: + name: upgraded-dump-server + path: ${{ env.DUMP_SERVER_NAME }}.gz + esr-upgrade-script: + runs-on: ubuntu-latest-8-cores + timeout-minutes: 30 + steps: + - name: Checkout mattermost-server + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - name: Run docker compose + run: | + cd server/build + docker-compose --no-ansi run --rm start_dependencies + cat ../tests/test-data.ldif | docker-compose --no-ansi exec -T openldap bash -c 'ldapadd -x -D "cn=admin,dc=mm,dc=test,dc=com" -w mostest'; + docker-compose --no-ansi exec -T minio sh -c 'mkdir -p /data/mattermost-test'; + docker-compose --no-ansi ps + - name: Wait for docker compose + run: | + until docker network inspect ghactions_mm-test; do echo "Waiting for Docker Compose Network..."; sleep 1; done; + docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://mysql:3306; do echo waiting for mysql; sleep 5; done;" + docker run --net ghactions_mm-test appropriate/curl:latest sh -c "until curl --max-time 5 --output - http://elasticsearch:9200; do echo waiting for elasticsearch; sleep 5; done;" + - name: Initialize the database with the source DB dump + run: | + curl ${{ inputs.db-dump-url }} | zcat | docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS + - name: Preprocess the DB dump + run: | + cd server/scripts/esrupgrades + docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $PREPROCESS_SCRIPT + - name: Run migration through script + run : | + cd server/scripts/esrupgrades + docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $MIGRATION_SCRIPT + - name: Cleanup DB + run : | + cd server/scripts/esrupgrades + docker exec -i ghactions_mysql_1 mysql -AN $MYSQL_CONN_ARGS < $CLEANUP_SCRIPT + - name: Dump upgraded database + run: | + docker exec -i ghactions_mysql_1 mysqldump --skip-opt --set-gtid-purged=OFF $MYSQL_CONN_ARGS > $DUMP_SCRIPT_NAME + - name: Cleanup dump and compress + run: | + # We skip the very last line, which simply contains the date of the dump + head -n -1 ${DUMP_SCRIPT_NAME} | gzip > ${DUMP_SCRIPT_NAME}.gz + - name: Upload dump + uses: actions/upload-artifact@v3 + with: + name: upgraded-dump-script + path: ${{ env.DUMP_SCRIPT_NAME }}.gz + esr-upgrade-diff: + runs-on: ubuntu-latest-8-cores + needs: + - esr-upgrade-server + - esr-upgrade-script + steps: + - name: Retrieve dumps + uses: actions/download-artifact@v3 + - name: Diff dumps + run: | + gzip -d upgraded-dump-server/${DUMP_SERVER_NAME}.gz + gzip -d upgraded-dump-script/${DUMP_SCRIPT_NAME}.gz + diff upgraded-dump-server/$DUMP_SERVER_NAME upgraded-dump-script/$DUMP_SCRIPT_NAME > $DIFF_NAME + - name: Upload diff + if: failure() # Upload the diff only if the previous step failed; i.e., if the diff is non-empty + uses: actions/upload-artifact@v3 + with: + name: dumps-diff + path: ${{ env.DIFF_NAME }} diff --git a/.github/workflows/esrupgrade.yml b/.github/workflows/esrupgrade.yml new file mode 100644 index 0000000000..71624f826a --- /dev/null +++ b/.github/workflows/esrupgrade.yml @@ -0,0 +1,33 @@ +name: ESR Upgrade +on: + pull_request: + paths: + - 'server/scripts/esrupgrades/*' + - '.github/workflows/esr*' + push: + branches: + - master + - cloud + - release-* +jobs: + esr-upgrade-5_37-7_8: + name: Run ESR upgrade script from 5.37 to 7.8 + uses: ./.github/workflows/esrupgrade-common.yml + with: + db-dump-url: https://lt-public-data.s3.amazonaws.com/47K_537_mysql_collationfixed.sql.gz + initial-version: 5.37 + final-version: 7.8 + esr-upgrade-5_37-6_3: + name: Run ESR upgrade script from 5.37 to 6.3 + uses: ./.github/workflows/esrupgrade-common.yml + with: + db-dump-url: https://lt-public-data.s3.amazonaws.com/47K_537_mysql_collationfixed.sql.gz + initial-version: 5.37 + final-version: 6.3 + esr-upgrade-6_3-7_8: + name: Run ESR upgrade script from 6.3 to 7.8 + uses: ./.github/workflows/esrupgrade-common.yml + with: + db-dump-url: https://lt-public-data.s3.amazonaws.com/47K_63_mysql.sql.gz + initial-version: 6.3 + final-version: 7.8 diff --git a/server/scripts/esrupgrades/README.md b/server/scripts/esrupgrades/README.md new file mode 100644 index 0000000000..e71dcb2487 --- /dev/null +++ b/server/scripts/esrupgrades/README.md @@ -0,0 +1 @@ +A collection of ad-hoc scripts to upgrade between ESRs. diff --git a/server/scripts/esrupgrades/esr.5.37-6.3.mysql.cleanup.sql b/server/scripts/esrupgrades/esr.5.37-6.3.mysql.cleanup.sql new file mode 100644 index 0000000000..3a13b11f83 --- /dev/null +++ b/server/scripts/esrupgrades/esr.5.37-6.3.mysql.cleanup.sql @@ -0,0 +1,160 @@ +/* Product notices are controlled externally, via the mattermost/notices repository. + When there is a new notice specified there, the server may have time, right after + the migration and before it is shut down, to download it and modify the + ProductNoticeViewState table, adding a row for all users that have not seen it or + removing old notices that no longer need to be shown. This can happen in the + UpdateProductNotices function that is executed periodically to update the notices + cache. The script will never do this, so we need to remove all rows in that table + to avoid any unwanted diff. */ +DELETE FROM ProductNoticeViewState; + +/* The script does not update the Systems row that tracks the version, so it is manually updated + here so that it does not show in the diff. */ +UPDATE Systems SET Value = '6.3.0' WHERE Name = 'Version'; + +/* The script does not update the schema_migrations table, which is automatically used by the + migrate library to track the version, so we drop it altogether to avoid spurious errors in + the diff */ +DROP TABLE IF EXISTS schema_migrations; + +/* Migration 000054_create_crt_channelmembership_count.up sets + ChannelMembers.LastUpdateAt to the results of SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000) + which will be different each time the migration is run. Thus, the column will always be + different when comparing the server and script migrations. To bypass this, we update all + rows in ChannelMembers so that they contain the same value for such column. */ +UPDATE ChannelMembers SET LastUpdateAt = 1; + +/* Migration 000055_create_crt_thread_count_and_unreads.up sets + ThreadMemberships.LastUpdated to the results of SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000) + which will be different each time the migration is run. Thus, the column will always be + different when comparing the server and script migrations. To bypass this, we update all + rows in ThreadMemberships so that they contain the same value for such column. */ +UPDATE ThreadMemberships SET LastUpdated = 1; + +/* The security update check in the server may update the LastSecurityTime system value. To + avoid any spurious difference in the migrations, we update it to a fixed value. */ +UPDATE Systems SET Value = 1 WHERE Name = 'LastSecurityTime'; + +/* The server migration contains an in-app migration that adds new roles for Playbooks: + doPlaybooksRolesCreationMigration, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L345-L469 + The roles are the ones defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/model/role.go#L874-L929 + When this migration finishes, it also adds a new row to the Systems table with the key of the migration. + This in-app migration does not happen in the script, so we remove those rows here. */ +DELETE FROM Roles WHERE Name = 'playbook_member'; +DELETE FROM Roles WHERE Name = 'playbook_admin'; +DELETE FROM Roles WHERE Name = 'run_member'; +DELETE FROM Roles WHERE Name = 'run_admin'; +DELETE FROM Systems WHERE Name = 'PlaybookRolesCreationMigrationComplete'; + +/* The server migration contains an in-app migration that add playbooks permissions to certain roles: + getAddPlaybooksPermissions, defined in https://github.com/mattermost/mattermost-server/blob/f9b996934cabf9a8fad5901835e7e9b418917402/app/permissions_migrations.go#L918-L951 + The specific roles ('%playbook%') are removed in the procedure below, but the migrations also add a new row to the Systems table marking the migration as complete. + This in-app migration does not happen in the script, so we remove that rows here. */ +DELETE FROM Systems WHERE Name = 'playbooks_permissions'; + +/* The rest of this script defines and executes a procedure to update the Roles table. It performs several changes: + 1. Set the UpdateAt column of all rows to a fixed value, so that the server migration changes to this column + do not appear in the diff. + 2. Remove the set of specific permissions added in the server migration that is not covered by the script, as + this logic happens all in-app after the normal DB migrations. + 3. Set a consistent order in the Permissions column, which is modelled a space-separated string containing each of + the different permissions each role has. This change is the reason why we need a complex procedure, which creates + a temporary table that pairs each single permission to its corresponding ID. So if the Roles table contains two + rows like: + Id: 'abcd' + Permissions: 'view_team read_public_channel invite_user' + Id: 'efgh' + Permissions: 'view_team create_emojis' + then the new temporary table will contain five rows like: + Id: 'abcd' + Permissions: 'view_team' + Id: 'abcd' + Permissions: 'read_public_channel' + Id: 'abcd' + Permissions: 'invite_user' + Id: 'efgh' + Permissions: 'view_team' + Id: 'efgh' + Permissions: 'create_emojis' +*/ + +DROP PROCEDURE IF EXISTS splitPermissions; +DROP PROCEDURE IF EXISTS sortAndFilterPermissionsInRoles; + +DROP TEMPORARY TABLE IF EXISTS temp_roles; +CREATE TEMPORARY TABLE temp_roles(id varchar(26), permission longtext); + +DELIMITER // + +/* Auxiliary procedure that splits the space-separated permissions string into single rows that are inserted + in the temporary temp_roles table along with their corresponding ID. */ +CREATE PROCEDURE splitPermissions( + IN id varchar(26), + IN permissionsString longtext +) +BEGIN + DECLARE idx INT DEFAULT 0; + SELECT TRIM(permissionsString) INTO permissionsString; + SELECT LOCATE(' ', permissionsString) INTO idx; + WHILE idx > 0 DO + INSERT INTO temp_roles SELECT id, TRIM(LEFT(permissionsString, idx)); + SELECT SUBSTR(permissionsString, idx+1) INTO permissionsString; + SELECT LOCATE(' ', permissionsString) INTO idx; + END WHILE; + INSERT INTO temp_roles(id, permission) VALUES(id, TRIM(permissionsString)); +END; // + +/* Main procedure that does update the Roles table */ +CREATE PROCEDURE sortAndFilterPermissionsInRoles() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rolesId varchar(26) DEFAULT ''; + DECLARE rolesPermissions longtext DEFAULT ''; + DECLARE cur1 CURSOR FOR SELECT Id, Permissions FROM Roles; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + /* 1. Set a fixed value in the UpdateAt column for all rows in Roles table */ + UPDATE Roles SET UpdateAt = 1; + + /* Call splitPermissions for every row in the Roles table, thus populating the + temp_roles table. */ + OPEN cur1; + read_loop: LOOP + FETCH cur1 INTO rolesId, rolesPermissions; + IF done THEN + LEAVE read_loop; + END IF; + CALL splitPermissions(rolesId, rolesPermissions); + END LOOP; + CLOSE cur1; + + /* 2. Filter out the new permissions added by the in-app migrations */ + DELETE FROM temp_roles WHERE permission LIKE '%playbook%'; + DELETE FROM temp_roles WHERE permission LIKE 'run_create'; + DELETE FROM temp_roles WHERE permission LIKE 'run_manage_members'; + DELETE FROM temp_roles WHERE permission LIKE 'run_manage_properties'; + DELETE FROM temp_roles WHERE permission LIKE 'run_view'; + + /* Temporarily set to the maximum permitted value, since the call to group_concat + below needs a value bigger than the default */ + SET group_concat_max_len = 18446744073709551615; + + /* 3. Update the Permissions column in the Roles table with the filtered, sorted permissions, + concatenated again as a space-separated string */ + UPDATE + Roles INNER JOIN ( + SELECT temp_roles.id as Id, TRIM(group_concat(temp_roles.permission ORDER BY temp_roles.permission SEPARATOR ' ')) as Permissions + FROM Roles JOIN temp_roles ON Roles.Id = temp_roles.id + GROUP BY temp_roles.id + ) AS Sorted + ON Roles.Id = Sorted.Id + SET Roles.Permissions = Sorted.Permissions; + + /* Reset group_concat_max_len to its default value */ + SET group_concat_max_len = 1024; +END; // +DELIMITER ; + +CALL sortAndFilterPermissionsInRoles(); + +DROP TEMPORARY TABLE IF EXISTS temp_roles; diff --git a/server/scripts/esrupgrades/esr.5.37-6.3.mysql.up.sql b/server/scripts/esrupgrades/esr.5.37-6.3.mysql.up.sql new file mode 100644 index 0000000000..53c1c211fa --- /dev/null +++ b/server/scripts/esrupgrades/esr.5.37-6.3.mysql.up.sql @@ -0,0 +1,695 @@ +/* ==> mysql/000054_create_crt_channelmembership_count.up.sql <== */ +/* fixCRTChannelMembershipCounts fixes the channel counts, i.e. the total message count, +total root message count, mention count, and mention count in root messages for users +who have viewed the channel after the last post in the channel */ + +DELIMITER // +CREATE PROCEDURE MigrateCRTChannelMembershipCounts () +BEGIN + IF( + SELECT + EXISTS ( + SELECT + * FROM Systems + WHERE + Name = 'CRTChannelMembershipCountsMigrationComplete') = 0) THEN + UPDATE + ChannelMembers + INNER JOIN Channels ON Channels.Id = ChannelMembers.ChannelId SET + MentionCount = 0, MentionCountRoot = 0, MsgCount = Channels.TotalMsgCount, MsgCountRoot = Channels.TotalMsgCountRoot, LastUpdateAt = ( + SELECT + (SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000))) + WHERE + ChannelMembers.LastViewedAt >= Channels.LastPostAt; + INSERT INTO Systems + VALUES('CRTChannelMembershipCountsMigrationComplete', 'true'); + END IF; +END// +DELIMITER ; +CALL MigrateCRTChannelMembershipCounts (); +DROP PROCEDURE IF EXISTS MigrateCRTChannelMembershipCounts; + +/* ==> mysql/000055_create_crt_thread_count_and_unreads.up.sql <== */ +/* fixCRTThreadCountsAndUnreads Marks threads as read for users where the last +reply time of the thread is earlier than the time the user viewed the channel. +Marking a thread means setting the mention count to zero and setting the +last viewed at time of the the thread as the last viewed at time +of the channel */ + +DELIMITER // +CREATE PROCEDURE MigrateCRTThreadCountsAndUnreads () +BEGIN + IF(SELECT EXISTS(SELECT * FROM Systems WHERE Name = 'CRTThreadCountsAndUnreadsMigrationComplete') = 0) THEN + UPDATE + ThreadMemberships + INNER JOIN ( + SELECT + PostId, + UserId, + ChannelMembers.LastViewedAt AS CM_LastViewedAt, + Threads.LastReplyAt + FROM + Threads + INNER JOIN ChannelMembers ON ChannelMembers.ChannelId = Threads.ChannelId + WHERE + Threads.LastReplyAt <= ChannelMembers.LastViewedAt) AS q ON ThreadMemberships.Postid = q.PostId + AND ThreadMemberships.UserId = q.UserId SET LastViewed = q.CM_LastViewedAt + 1, UnreadMentions = 0, LastUpdated = ( + SELECT + (SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000))); + INSERT INTO Systems + VALUES('CRTThreadCountsAndUnreadsMigrationComplete', 'true'); + END IF; +END// +DELIMITER ; +CALL MigrateCRTThreadCountsAndUnreads (); +DROP PROCEDURE IF EXISTS MigrateCRTThreadCountsAndUnreads; + +/* ==> mysql/000056_upgrade_channels_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND index_name = 'idx_channels_team_id_display_name' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_channels_team_id_display_name ON Channels(TeamId, DisplayName);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND index_name = 'idx_channels_team_id_type' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_channels_team_id_type ON Channels(TeamId, Type);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND index_name = 'idx_channels_team_id' + ) > 0, + 'DROP INDEX idx_channels_team_id ON Channels;', + 'SELECT 1' +)); + +PREPARE removeIndexIfExists FROM @preparedStatement; +EXECUTE removeIndexIfExists; +DEALLOCATE PREPARE removeIndexIfExists; + +/* ==> mysql/000057_upgrade_command_webhooks_v6.0.up.sql <== */ + +DELIMITER // +CREATE PROCEDURE MigrateRootId_CommandWebhooks () BEGIN DECLARE ParentId_EXIST INT; +SELECT COUNT(*) +FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_NAME = 'CommandWebhooks' + AND table_schema = DATABASE() + AND COLUMN_NAME = 'ParentId' INTO ParentId_EXIST; +IF(ParentId_EXIST > 0) THEN + UPDATE CommandWebhooks SET RootId = ParentId WHERE RootId = '' AND RootId != ParentId; +END IF; +END// +DELIMITER ; +CALL MigrateRootId_CommandWebhooks (); +DROP PROCEDURE IF EXISTS MigrateRootId_CommandWebhooks; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'CommandWebhooks' + AND table_schema = DATABASE() + AND column_name = 'ParentId' + ) > 0, + 'ALTER TABLE CommandWebhooks DROP COLUMN ParentId;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000058_upgrade_channelmembers_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND column_name = 'NotifyProps' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE ChannelMembers MODIFY COLUMN NotifyProps JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_channelmembers_user_id' + ) > 0, + 'DROP INDEX idx_channelmembers_user_id ON ChannelMembers;', + 'SELECT 1' +)); + +PREPARE removeIndexIfExists FROM @preparedStatement; +EXECUTE removeIndexIfExists; +DEALLOCATE PREPARE removeIndexIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_channelmembers_user_id_channel_id_last_viewed_at' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_channelmembers_user_id_channel_id_last_viewed_at ON ChannelMembers(UserId, ChannelId, LastViewedAt);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_channelmembers_channel_id_scheme_guest_user_id' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_channelmembers_channel_id_scheme_guest_user_id ON ChannelMembers(ChannelId, SchemeGuest, UserId);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000059_upgrade_users_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Props' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE Users MODIFY COLUMN Props JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'NotifyProps' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE Users MODIFY COLUMN NotifyProps JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Timezone' + AND column_default IS NOT NULL + ) > 0, + 'ALTER TABLE Users ALTER Timezone DROP DEFAULT;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Timezone' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE Users MODIFY COLUMN Timezone JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND column_type != 'text' + ) > 0, + 'ALTER TABLE Users MODIFY COLUMN Roles text;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000060_upgrade_jobs_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Jobs' + AND table_schema = DATABASE() + AND column_name = 'Data' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE Jobs MODIFY COLUMN Data JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + + +/* ==> mysql/000061_upgrade_link_metadata_v6.0.up.sql <== */ + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'LinkMetadata' + AND table_schema = DATABASE() + AND column_name = 'Data' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE LinkMetadata MODIFY COLUMN Data JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000062_upgrade_sessions_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Sessions' + AND table_schema = DATABASE() + AND column_name = 'Props' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE Sessions MODIFY COLUMN Props JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + + +/* ==> mysql/000063_upgrade_threads_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'Participants' + AND column_type != 'JSON' + ) > 0, + 'ALTER TABLE Threads MODIFY COLUMN Participants JSON;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND index_name = 'idx_threads_channel_id_last_reply_at' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_threads_channel_id_last_reply_at ON Threads(ChannelId, LastReplyAt);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND index_name = 'idx_threads_channel_id' + ) > 0, + 'DROP INDEX idx_threads_channel_id ON Threads;', + 'SELECT 1' +)); + +PREPARE removeIndexIfExists FROM @preparedStatement; +EXECUTE removeIndexIfExists; +DEALLOCATE PREPARE removeIndexIfExists; + +/* ==> mysql/000064_upgrade_status_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Status' + AND table_schema = DATABASE() + AND index_name = 'idx_status_status_dndendtime' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_status_status_dndendtime ON Status(Status, DNDEndTime);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Status' + AND table_schema = DATABASE() + AND index_name = 'idx_status_status' + ) > 0, + 'DROP INDEX idx_status_status ON Status;', + 'SELECT 1' +)); + +PREPARE removeIndexIfExists FROM @preparedStatement; +EXECUTE removeIndexIfExists; +DEALLOCATE PREPARE removeIndexIfExists; + +/* ==> mysql/000065_upgrade_groupchannels_v6.0.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'GroupChannels' + AND table_schema = DATABASE() + AND index_name = 'idx_groupchannels_schemeadmin' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_groupchannels_schemeadmin ON GroupChannels(SchemeAdmin);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000066_upgrade_posts_v6.0.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateRootId_Posts () +BEGIN +DECLARE ParentId_EXIST INT; +DECLARE Alter_FileIds INT; +DECLARE Alter_Props INT; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS +WHERE TABLE_NAME = 'Posts' + AND table_schema = DATABASE() + AND COLUMN_NAME = 'ParentId' INTO ParentId_EXIST; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND column_name = 'FileIds' + AND column_type != 'text' INTO Alter_FileIds; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND column_name = 'Props' + AND column_type != 'JSON' INTO Alter_Props; +IF (Alter_Props OR Alter_FileIds) THEN + IF(ParentId_EXIST > 0) THEN + UPDATE Posts SET RootId = ParentId WHERE RootId = '' AND RootId != ParentId; + ALTER TABLE Posts MODIFY COLUMN FileIds text, MODIFY COLUMN Props JSON, DROP COLUMN ParentId; + ELSE + ALTER TABLE Posts MODIFY COLUMN FileIds text, MODIFY COLUMN Props JSON; + END IF; +END IF; +END// +DELIMITER ; +CALL MigrateRootId_Posts (); +DROP PROCEDURE IF EXISTS MigrateRootId_Posts; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND index_name = 'idx_posts_root_id_delete_at' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_posts_root_id_delete_at ON Posts(RootId, DeleteAt);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND index_name = 'idx_posts_root_id' + ) > 0, + 'DROP INDEX idx_posts_root_id ON Posts;', + 'SELECT 1' +)); + +PREPARE removeIndexIfExists FROM @preparedStatement; +EXECUTE removeIndexIfExists; +DEALLOCATE PREPARE removeIndexIfExists; + +/* ==> mysql/000067_upgrade_channelmembers_v6.1.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND column_type != 'text' + ) > 0, + 'ALTER TABLE ChannelMembers MODIFY COLUMN Roles text;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000068_upgrade_teammembers_v6.1.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'TeamMembers' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND column_type != 'text' + ) > 0, + 'ALTER TABLE TeamMembers MODIFY COLUMN Roles text;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000069_upgrade_jobs_v6.1.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Jobs' + AND table_schema = DATABASE() + AND index_name = 'idx_jobs_status_type' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_jobs_status_type ON Jobs(Status, Type);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000070_upgrade_cte_v6.1.up.sql <== */ +DELIMITER // +CREATE PROCEDURE Migrate_LastRootPostAt () +BEGIN +DECLARE + LastRootPostAt_EXIST INT; + SELECT + COUNT(*) + FROM + INFORMATION_SCHEMA.COLUMNS + WHERE + TABLE_NAME = 'Channels' + AND table_schema = DATABASE() + AND COLUMN_NAME = 'LastRootPostAt' INTO LastRootPostAt_EXIST; + IF(LastRootPostAt_EXIST = 0) THEN + ALTER TABLE Channels ADD COLUMN LastRootPostAt bigint DEFAULT 0; + UPDATE + Channels + INNER JOIN ( + SELECT + Channels.Id channelid, + COALESCE(MAX(Posts.CreateAt), 0) AS lastrootpost + FROM + Channels + LEFT JOIN Posts FORCE INDEX (idx_posts_channel_id_update_at) ON Channels.Id = Posts.ChannelId + WHERE + Posts.RootId = '' + GROUP BY + Channels.Id) AS q ON q.channelid = Channels.Id SET LastRootPostAt = lastrootpost; + END IF; +END// +DELIMITER ; +CALL Migrate_LastRootPostAt (); +DROP PROCEDURE IF EXISTS Migrate_LastRootPostAt; + +/* ==> mysql/000071_upgrade_sessions_v6.1.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Sessions' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND column_type != 'text' + ) > 0, + 'ALTER TABLE Sessions MODIFY COLUMN Roles text;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000072_upgrade_schemes_v6.3.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultPlaybookAdminRole' + ) > 0, + 'SELECT 1', + 'ALTER TABLE Schemes ADD COLUMN DefaultPlaybookAdminRole VARCHAR(64) DEFAULT "";' +)); + +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultPlaybookMemberRole' + ) > 0, + 'SELECT 1', + 'ALTER TABLE Schemes ADD COLUMN DefaultPlaybookMemberRole VARCHAR(64) DEFAULT "";' +)); + +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultRunAdminRole' + ) > 0, + 'SELECT 1', + 'ALTER TABLE Schemes ADD COLUMN DefaultRunAdminRole VARCHAR(64) DEFAULT "";' +)); + +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultRunMemberRole' + ) > 0, + 'SELECT 1', + 'ALTER TABLE Schemes ADD COLUMN DefaultRunMemberRole VARCHAR(64) DEFAULT "";' +)); + +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; + +/* ==> mysql/000073_upgrade_plugin_key_value_store_v6.3.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT Count(*) FROM Information_Schema.Columns + WHERE table_name = 'PluginKeyValueStore' + AND table_schema = DATABASE() + AND column_name = 'PKey' + AND column_type != 'varchar(150)' + ) > 0, + 'ALTER TABLE PluginKeyValueStore MODIFY COLUMN PKey varchar(150);', + 'SELECT 1' +)); + +PREPARE alterTypeIfExists FROM @preparedStatement; +EXECUTE alterTypeIfExists; +DEALLOCATE PREPARE alterTypeIfExists; + +/* ==> mysql/000074_upgrade_users_v6.3.up.sql <== */ + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'AcceptedTermsOfServiceId' + ) > 0, + 'ALTER TABLE Users DROP COLUMN AcceptedTermsOfServiceId;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; diff --git a/server/scripts/esrupgrades/esr.5.37-7.8.mysql.cleanup.sql b/server/scripts/esrupgrades/esr.5.37-7.8.mysql.cleanup.sql new file mode 100644 index 0000000000..4c23874cb1 --- /dev/null +++ b/server/scripts/esrupgrades/esr.5.37-7.8.mysql.cleanup.sql @@ -0,0 +1,199 @@ +/* Product notices are controlled externally, via the mattermost/notices repository. + When there is a new notice specified there, the server may have time, right after + the migration and before it is shut down, to download it and modify the + ProductNoticeViewState table, adding a row for all users that have not seen it or + removing old notices that no longer need to be shown. This can happen in the + UpdateProductNotices function that is executed periodically to update the notices + cache. The script will never do this, so we need to remove all rows in that table + to avoid any unwanted diff. */ +DELETE FROM ProductNoticeViewState; + +/* Remove migration-related tables that are only updated through the server to track which + migrations have been applied */ +DROP TABLE IF EXISTS db_lock; +DROP TABLE IF EXISTS db_migrations; + +/* Migration 000054_create_crt_channelmembership_count.up sets + ChannelMembers.LastUpdateAt to the results of SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000) + which will be different each time the migration is run. Thus, the column will always be + different when comparing the server and script migrations. To bypass this, we update all + rows in ChannelMembers so that they contain the same value for such column. */ +UPDATE ChannelMembers SET LastUpdateAt = 1; + +/* Migration 000055_create_crt_thread_count_and_unreads.up sets + ThreadMemberships.LastUpdated to the results of SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000) + which will be different each time the migration is run. Thus, the column will always be + different when comparing the server and script migrations. To bypass this, we update all + rows in ThreadMemberships so that they contain the same value for such column. */ +UPDATE ThreadMemberships SET LastUpdated = 1; + +/* The security update check in the server may update the LastSecurityTime system value. To + avoid any spurious difference in the migrations, we update it to a fixed value. */ +UPDATE Systems SET Value = 1 WHERE Name = 'LastSecurityTime'; + +/* The server migration may contain a row in the Systems table marking the onboarding as complete. + There are no migrations related to this, so we can simply drop it here. */ +DELETE FROM Systems WHERE Name = 'FirstAdminSetupComplete'; + +/* The server migration contains an in-app migration that adds new roles for Playbooks: + doPlaybooksRolesCreationMigration, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L345-L469 + The roles are the ones defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/model/role.go#L874-L929 + When this migration finishes, it also adds a new row to the Systems table with the key of the migration. + This in-app migration does not happen in the script, so we remove those rows here. */ +DELETE FROM Roles WHERE Name = 'playbook_member'; +DELETE FROM Roles WHERE Name = 'playbook_admin'; +DELETE FROM Roles WHERE Name = 'run_member'; +DELETE FROM Roles WHERE Name = 'run_admin'; +DELETE FROM Systems WHERE Name = 'PlaybookRolesCreationMigrationComplete'; + +/* The server migration contains two in-app migrations that add playbooks permissions to certain roles: + getAddPlaybooksPermissions and getPlaybooksPermissionsAddManageRoles, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L1021-L1072 + The specific roles ('%playbook%') are removed in the procedure below, but the migrations also add new rows to the Systems table marking the migrations as complete. + These in-app migrations do not happen in the script, so we remove those rows here. */ +DELETE FROM Systems WHERE Name = 'playbooks_manage_roles'; +DELETE FROM Systems WHERE Name = 'playbooks_permissions'; + +/* The server migration contains an in-app migration that adds boards permissions to certain roles: + getProductsBoardsPermissions, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L1074-L1093 + The specific roles (sysconsole_read_product_boards and sysconsole_write_product_boards) are removed in the procedure below, + but the migrations also adds a new row to the Systems table marking the migrations as complete. + This in-app migration does not happen in the script, so we remove that row here. */ +DELETE FROM Systems WHERE Name = 'products_boards'; + +/* TODO: REVIEW STARTING HERE */ + +/* The server migration contain an in-app migration that adds Ids to the Teams whose InviteId is an empty string: + doRemainingSchemaMigrations, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L515-L540 + The migration is not replicated in the script, since it happens in-app, but the server adds a new row to the + Systems table marking the table as complete, which the script doesn't do, so we remove that row here. */ +DELETE FROM Systems WHERE Name = 'RemainingSchemaMigrations'; + +/* The server migration contains three in-app migration that adds a new role and new permissions + related to custom groups. The migrations are: + - doCustomGroupAdminRoleCreationMigration https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L345-L469 + - getAddCustomUserGroupsPermissions https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L974-L995 + - getAddCustomUserGroupsPermissionRestore https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L997-L1019 + The specific roles and permissions are removed in the procedure below, but the migrations also + adds a new row to the Roles table for the new role and new rows to the Systems table marking the + migrations as complete. + This in-app migration does not happen in the script, so we remove that row here. */ +DELETE FROM Roles WHERE Name = 'system_custom_group_admin'; +DELETE FROM Systems WHERE Name = 'CustomGroupAdminRoleCreationMigrationComplete'; +DELETE FROM Systems WHERE Name = 'custom_groups_permissions'; +DELETE FROM Systems WHERE Name = 'custom_groups_permission_restore'; + +/* The server migration contains an in-app migration that updates the config, setting ServiceSettings.PostPriority + to true, doPostPriorityConfigDefaultTrueMigration, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L542-L560 + The migration is not replicated in the script, since it happens in-app, but the server adds a new row to the + Systems table marking the table as complete, which the script doesn't do, so we remove that row here. */ +DELETE FROM Systems WHERE Name = 'PostPriorityConfigDefaultTrueMigrationComplete'; + +/* The rest of this script defines and executes a procedure to update the Roles table. It performs several changes: + 1. Set the UpdateAt column of all rows to a fixed value, so that the server migration changes to this column + do not appear in the diff. + 2. Remove the set of specific permissions added in the server migration that is not covered by the script, as + this logic happens all in-app after the normal DB migrations. + 3. Set a consistent order in the Permissions column, which is modelled a space-separated string containing each of + the different permissions each role has. This change is the reason why we need a complex procedure, which creates + a temporary table that pairs each single permission to its corresponding ID. So if the Roles table contains two + rows like: + Id: 'abcd' + Permissions: 'view_team read_public_channel invite_user' + Id: 'efgh' + Permissions: 'view_team create_emojis' + then the new temporary table will contain five rows like: + Id: 'abcd' + Permissions: 'view_team' + Id: 'abcd' + Permissions: 'read_public_channel' + Id: 'abcd' + Permissions: 'invite_user' + Id: 'efgh' + Permissions: 'view_team' + Id: 'efgh' + Permissions: 'create_emojis' +*/ + +DROP PROCEDURE IF EXISTS splitPermissions; +DROP PROCEDURE IF EXISTS sortAndFilterPermissionsInRoles; + +DROP TEMPORARY TABLE IF EXISTS temp_roles; +CREATE TEMPORARY TABLE temp_roles(id varchar(26), permission longtext); + +DELIMITER // + +/* Auxiliary procedure that splits the space-separated permissions string into single rows that are inserted + in the temporary temp_roles table along with their corresponding ID. */ +CREATE PROCEDURE splitPermissions( + IN id varchar(26), + IN permissionsString longtext +) +BEGIN + DECLARE idx INT DEFAULT 0; + SELECT TRIM(permissionsString) INTO permissionsString; + SELECT LOCATE(' ', permissionsString) INTO idx; + WHILE idx > 0 DO + INSERT INTO temp_roles SELECT id, TRIM(LEFT(permissionsString, idx)); + SELECT SUBSTR(permissionsString, idx+1) INTO permissionsString; + SELECT LOCATE(' ', permissionsString) INTO idx; + END WHILE; + INSERT INTO temp_roles(id, permission) VALUES(id, TRIM(permissionsString)); +END; // + +/* Main procedure that does update the Roles table */ +CREATE PROCEDURE sortAndFilterPermissionsInRoles() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rolesId varchar(26) DEFAULT ''; + DECLARE rolesPermissions longtext DEFAULT ''; + DECLARE cur1 CURSOR FOR SELECT Id, Permissions FROM Roles; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + /* 1. Set a fixed value in the UpdateAt column for all rows in Roles table */ + UPDATE Roles SET UpdateAt = 1; + + /* Call splitPermissions for every row in the Roles table, thus populating the + temp_roles table. */ + OPEN cur1; + read_loop: LOOP + FETCH cur1 INTO rolesId, rolesPermissions; + IF done THEN + LEAVE read_loop; + END IF; + CALL splitPermissions(rolesId, rolesPermissions); + END LOOP; + CLOSE cur1; + + /* 2. Filter out the new permissions added by the in-app migrations */ + DELETE FROM temp_roles WHERE permission LIKE 'sysconsole_read_products_boards'; + DELETE FROM temp_roles WHERE permission LIKE 'sysconsole_write_products_boards'; + DELETE FROM temp_roles WHERE permission LIKE '%playbook%'; + DELETE FROM temp_roles WHERE permission LIKE 'run_create'; + DELETE FROM temp_roles WHERE permission LIKE 'run_manage_members'; + DELETE FROM temp_roles WHERE permission LIKE 'run_manage_properties'; + DELETE FROM temp_roles WHERE permission LIKE 'run_view'; + DELETE FROM temp_roles WHERE permission LIKE '%custom_group%'; + + /* Temporarily set to the maximum permitted value, since the call to group_concat + below needs a value bigger than the default */ + SET group_concat_max_len = 18446744073709551615; + + /* 3. Update the Permissions column in the Roles table with the filtered, sorted permissions, + concatenated again as a space-separated string */ + UPDATE + Roles INNER JOIN ( + SELECT temp_roles.id as Id, TRIM(group_concat(temp_roles.permission ORDER BY temp_roles.permission SEPARATOR ' ')) as Permissions + FROM Roles JOIN temp_roles ON Roles.Id = temp_roles.id + GROUP BY temp_roles.id + ) AS Sorted + ON Roles.Id = Sorted.Id + SET Roles.Permissions = Sorted.Permissions; + + /* Reset group_concat_max_len to its default value */ + SET group_concat_max_len = 1024; +END; // +DELIMITER ; + +CALL sortAndFilterPermissionsInRoles(); + +DROP TEMPORARY TABLE IF EXISTS temp_roles; diff --git a/server/scripts/esrupgrades/esr.5.37-7.8.mysql.up.sql b/server/scripts/esrupgrades/esr.5.37-7.8.mysql.up.sql new file mode 100644 index 0000000000..63e5899860 --- /dev/null +++ b/server/scripts/esrupgrades/esr.5.37-7.8.mysql.up.sql @@ -0,0 +1,1385 @@ +/* ==> mysql/000041_create_upload_sessions.up.sql <== */ +/* Release 5.37 was meant to contain the index idx_uploadsessions_type, but a bug prevented that. + This part of the migration #41 adds such index */ +/* ==> mysql/000075_alter_upload_sessions_index.up.sql <== */ +/* ==> mysql/000090_create_enums.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateUploadSessions () +BEGIN + -- 'CREATE INDEX idx_uploadsessions_type ON UploadSessions(Type);' + DECLARE CreateIndex BOOLEAN; + DECLARE CreateIndexQuery TEXT DEFAULT NULL; + + -- 'DROP INDEX idx_uploadsessions_user_id ON UploadSessions; CREATE INDEX idx_uploadsessions_user_id ON UploadSessions(UserId);' + DECLARE AlterIndex BOOLEAN; + DECLARE AlterIndexQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE UploadSessions MODIFY COLUMN Type ENUM("attachment", "import");' + DECLARE AlterColumn BOOLEAN; + DECLARE AlterColumnQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'UploadSessions' + AND table_schema = DATABASE() + AND index_name = 'idx_uploadsessions_type' + INTO CreateIndex; + + SELECT IFNULL(GROUP_CONCAT(column_name ORDER BY seq_in_index), '') = 'Type' FROM information_schema.statistics + WHERE table_name = 'UploadSessions' + AND table_schema = DATABASE() + AND index_name = 'idx_uploadsessions_user_id' + GROUP BY index_name + INTO AlterIndex; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'UploadSessions' + AND table_schema = DATABASE() + AND column_name = 'Type' + AND REPLACE(LOWER(column_type), '"', "'") != "enum('attachment','import')" + INTO AlterColumn; + + IF CreateIndex THEN + SET CreateIndexQuery = 'ADD INDEX idx_uploadsessions_type (Type)'; + END IF; + + IF AlterIndex THEN + SET AlterIndexQuery = 'DROP INDEX idx_uploadsessions_user_id, ADD INDEX idx_uploadsessions_user_id (UserId)'; + END IF; + + IF AlterColumn THEN + SET AlterColumnQuery = 'MODIFY COLUMN Type ENUM("attachment", "import")'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', CreateIndexQuery, AlterIndexQuery, AlterColumnQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE UploadSessions ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateUploadSessions procedure starting.') AS DEBUG; +CALL MigrateUploadSessions(); +SELECT CONCAT('-- ', NOW(), ' MigrateUploadSessions procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateUploadSessions; + +/* ==> mysql/000055_create_crt_thread_count_and_unreads.up.sql <== */ +/* fixCRTThreadCountsAndUnreads Marks threads as read for users where the last +reply time of the thread is earlier than the time the user viewed the channel. +Marking a thread means setting the mention count to zero and setting the +last viewed at time of the the thread as the last viewed at time +of the channel */ +DELIMITER // +CREATE PROCEDURE MigrateThreadMemberships () +BEGIN + -- UPDATE ThreadMemberships SET LastViewed = ..., UnreadMentions = ..., LastUpdated = ... + DECLARE UpdateThreadMemberships BOOLEAN; + DECLARE UpdateThreadMembershipsQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM Systems + WHERE Name = 'CRTThreadCountsAndUnreadsMigrationComplete' + INTO UpdateThreadMemberships; + + IF UpdateThreadMemberships THEN + UPDATE ThreadMemberships INNER JOIN ( + SELECT PostId, UserId, ChannelMembers.LastViewedAt AS CM_LastViewedAt, Threads.LastReplyAt + FROM Threads INNER JOIN ChannelMembers ON ChannelMembers.ChannelId = Threads.ChannelId + WHERE Threads.LastReplyAt <= ChannelMembers.LastViewedAt + ) AS q ON ThreadMemberships.Postid = q.PostId AND ThreadMemberships.UserId = q.UserId + SET LastViewed = q.CM_LastViewedAt + 1, UnreadMentions = 0, LastUpdated = (SELECT (SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000))); + INSERT INTO Systems VALUES('CRTThreadCountsAndUnreadsMigrationComplete', 'true'); + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateThreadMemberships procedure starting.') AS DEBUG; +CALL MigrateThreadMemberships(); +SELECT CONCAT('-- ', NOW(), ' MigrateThreadMemberships procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateThreadMemberships; + +/* ==> mysql/000056_upgrade_channels_v6.0.up.sql <== */ +/* ==> mysql/000070_upgrade_cte_v6.1.up.sql <== */ +/* ==> mysql/000090_create_enums.up.sql <== */ +/* ==> mysql/000076_upgrade_lastrootpostat.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateChannels () +BEGIN + -- 'DROP INDEX idx_channels_team_id ON Channels;' + DECLARE DropIndex BOOLEAN; + DECLARE DropIndexQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_channels_team_id_display_name ON Channels(TeamId, DisplayName);' + DECLARE CreateIndexTeamDisplay BOOLEAN; + DECLARE CreateIndexTeamDisplayQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_channels_team_id_type ON Channels(TeamId, Type);' + DECLARE CreateIndexTeamType BOOLEAN; + DECLARE CreateIndexTeamTypeQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Channels ADD COLUMN LastRootPostAt bigint DEFAULT 0;'' + -- UPDATE Channels INNER JOIN ... + DECLARE AddLastRootPostAt BOOLEAN; + DECLARE AddLastRootPostAtQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Channels MODIFY COLUMN Type ENUM("D", "O", "G", "P");', + DECLARE ModifyColumn BOOLEAN; + DECLARE ModifyColumnQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Channels ALTER COLUMN LastRootPostAt SET DEFAULT 0;', + DECLARE SetDefault BOOLEAN; + DECLARE SetDefaultQuery TEXT DEFAULT NULL; + + -- 'UPDATE Channels SET LastRootPostAt = ...', + DECLARE UpdateLastRootPostAt BOOLEAN; + DECLARE UpdateLastRootPostAtQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND index_name = 'idx_channels_team_id' + INTO DropIndex; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND index_name = 'idx_channels_team_id_display_name' + INTO CreateIndexTeamDisplay; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND index_name = 'idx_channels_team_id_type' + INTO CreateIndexTeamType; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'Channels' + AND table_schema = DATABASE() + AND COLUMN_NAME = 'LastRootPostAt' + INTO AddLastRootPostAt; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND column_name = 'Type' + AND REPLACE(LOWER(column_type), '"', "'") != "enum('d','o','g','p')" + INTO ModifyColumn; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'Channels' + AND TABLE_SCHEMA = DATABASE() + AND COLUMN_NAME = 'LastRootPostAt' + AND (COLUMN_DEFAULT IS NULL OR COLUMN_DEFAULT != 0) + INTO SetDefault; + + IF DropIndex THEN + SET DropIndexQuery = 'DROP INDEX idx_channels_team_id'; + END IF; + + IF CreateIndexTeamDisplay THEN + SET CreateIndexTeamDisplayQuery = 'ADD INDEX idx_channels_team_id_display_name (TeamId, DisplayName)'; + END IF; + + IF CreateIndexTeamType THEN + SET CreateIndexTeamTypeQuery = 'ADD INDEX idx_channels_team_id_type (TeamId, Type)'; + END IF; + + IF AddLastRootPostAt THEN + SET AddLastRootPostAtQuery = 'ADD COLUMN LastRootPostAt bigint DEFAULT 0'; + END IF; + + IF ModifyColumn THEN + SET ModifyColumnQuery = 'MODIFY COLUMN Type ENUM("D", "O", "G", "P")'; + END IF; + + IF SetDefault THEN + SET SetDefaultQuery = 'ALTER COLUMN LastRootPostAt SET DEFAULT 0'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', DropIndexQuery, CreateIndexTeamDisplayQuery, CreateIndexTeamTypeQuery, AddLastRootPostAtQuery, ModifyColumnQuery, SetDefaultQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Channels ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + + IF AddLastRootPostAt THEN + UPDATE Channels INNER JOIN ( + SELECT Channels.Id channelid, COALESCE(MAX(Posts.CreateAt), 0) AS lastrootpost + FROM Channels LEFT JOIN Posts FORCE INDEX (idx_posts_channel_id_update_at) ON Channels.Id = Posts.ChannelId + WHERE Posts.RootId = '' GROUP BY Channels.Id + ) AS q ON q.channelid = Channels.Id + SET LastRootPostAt = lastrootpost; + END IF; + + -- Cover the case where LastRootPostAt was already present and there are rows with it set to NULL + IF (SELECT COUNT(*) FROM Channels WHERE LastRootPostAt IS NULL) THEN + -- fixes migrate cte and sets the LastRootPostAt for channels that don't have it set + UPDATE Channels INNER JOIN ( + SELECT Channels.Id channelid, COALESCE(MAX(Posts.CreateAt), 0) AS lastrootpost + FROM Channels LEFT JOIN Posts FORCE INDEX (idx_posts_channel_id_update_at) ON Channels.Id = Posts.ChannelId + WHERE Posts.RootId = '' + GROUP BY Channels.Id + ) AS q ON q.channelid = Channels.Id + SET LastRootPostAt = lastrootpost + WHERE LastRootPostAt IS NULL; + -- sets LastRootPostAt to 0, for channels with no posts + UPDATE Channels SET LastRootPostAt=0 WHERE LastRootPostAt IS NULL; + END IF; + +END// +DELIMITER ; + +SELECT CONCAT('-- ', NOW(), ' MigrateChannels procedure starting.') AS DEBUG; +CALL MigrateChannels(); +SELECT CONCAT('-- ', NOW(), ' MigrateChannels procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateChannels; + +/* ==> mysql/000057_upgrade_command_webhooks_v6.0.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateCommandWebhooks () +BEGIN + DECLARE DropParentId BOOLEAN; + + SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'CommandWebhooks' + AND table_schema = DATABASE() + AND COLUMN_NAME = 'ParentId' + INTO DropParentId; + + IF DropParentId THEN + UPDATE CommandWebhooks SET RootId = ParentId WHERE RootId = '' AND RootId != ParentId; + ALTER TABLE CommandWebhooks DROP COLUMN ParentId; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateCommandWebhooks procedure starting.') AS DEBUG; +CALL MigrateCommandWebhooks(); +SELECT CONCAT('-- ', NOW(), ' MigrateCommandWebhooks procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateCommandWebhooks; + +/* ==> mysql/000054_create_crt_channelmembership_count.up.sql <== */ +/* ==> mysql/000058_upgrade_channelmembers_v6.0.up.sql <== */ +/* ==> mysql/000067_upgrade_channelmembers_v6.1.up.sql <== */ +/* ==> mysql/000097_create_posts_priority.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateChannelMembers () +BEGIN + -- 'ALTER TABLE ChannelMembers MODIFY COLUMN NotifyProps JSON;', + DECLARE ModifyNotifyProps BOOLEAN; + DECLARE ModifyNotifyPropsQuery TEXT DEFAULT NULL; + + -- 'DROP INDEX idx_channelmembers_user_id ON ChannelMembers;', + DECLARE DropIndex BOOLEAN; + DECLARE DropIndexQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_channelmembers_user_id_channel_id_last_viewed_at ON ChannelMembers(UserId, ChannelId, LastViewedAt);' + DECLARE CreateIndexLastViewedAt BOOLEAN; + DECLARE CreateIndexLastViewedAtQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_channelmembers_channel_id_scheme_guest_user_id ON ChannelMembers(ChannelId, SchemeGuest, UserId);' + DECLARE CreateIndexSchemeGuest BOOLEAN; + DECLARE CreateIndexSchemeGuestQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE ChannelMembers MODIFY COLUMN Roles text;', + DECLARE ModifyRoles BOOLEAN; + DECLARE ModifyRolesQuery TEXT DEFAULT NOT NULL; + + -- 'ALTER TABLE ChannelMembers ADD COLUMN UrgentMentionCount bigint(20);', + DECLARE AddUrgentMentionCount BOOLEAN; + DECLARE AddUrgentMentionCountQuery TEXT DEFAULT NOT NULL; + + DECLARE MigrateMemberships BOOLEAN; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND column_name = 'NotifyProps' + AND LOWER(column_type) != 'json' + INTO ModifyNotifyProps; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_channelmembers_user_id' + INTO DropIndex; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_channelmembers_user_id_channel_id_last_viewed_at' + INTO CreateIndexLastViewedAt; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_channelmembers_channel_id_scheme_guest_user_id' + INTO CreateIndexSchemeGuest; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND LOWER(column_type) != 'text' + INTO ModifyRoles; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND column_name = 'UrgentMentionCount' + INTO AddUrgentMentionCount; + + SELECT COUNT(*) = 0 FROM Systems + WHERE Name = 'CRTChannelMembershipCountsMigrationComplete' + INTO MigrateMemberships; + + IF ModifyNotifyProps THEN + SET ModifyNotifyPropsQuery = 'MODIFY COLUMN NotifyProps JSON'; + END IF; + + IF DropIndex THEN + SET DropIndexQuery = 'DROP INDEX idx_channelmembers_user_id'; + END IF; + + IF CreateIndexLastViewedAt THEN + SET CreateIndexLastViewedAtQuery = 'ADD INDEX idx_channelmembers_user_id_channel_id_last_viewed_at (UserId, ChannelId, LastViewedAt)'; + END IF; + + IF CreateIndexSchemeGuest THEN + SET CreateIndexSchemeGuestQuery = 'ADD INDEX idx_channelmembers_channel_id_scheme_guest_user_id (ChannelId, SchemeGuest, UserId)'; + END IF; + + IF ModifyRoles THEN + SET ModifyRolesQuery = 'MODIFY COLUMN Roles text'; + END IF; + + IF AddUrgentMentionCount THEN + SET AddUrgentMentionCountQuery = 'ADD COLUMN UrgentMentionCount bigint(20)'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', ModifyNotifyPropsQuery, DropIndexQuery, CreateIndexLastViewedAtQuery, CreateIndexSchemeGuestQuery, ModifyRolesQuery, AddUrgentMentionCountQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE ChannelMembers ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + + IF MigrateMemberships THEN + UPDATE ChannelMembers INNER JOIN Channels ON Channels.Id = ChannelMembers.ChannelId + SET MentionCount = 0, MentionCountRoot = 0, MsgCount = Channels.TotalMsgCount, MsgCountRoot = Channels.TotalMsgCountRoot, LastUpdateAt = (SELECT (SELECT ROUND(UNIX_TIMESTAMP(NOW(3))*1000))) + WHERE ChannelMembers.LastViewedAt >= Channels.LastPostAt; + INSERT INTO Systems VALUES('CRTChannelMembershipCountsMigrationComplete', 'true'); + END IF; + +END// +DELIMITER ; + +SELECT CONCAT('-- ', NOW(), ' MigrateChannelMembers procedure starting.') AS DEBUG; +CALL MigrateChannelMembers(); +SELECT CONCAT('-- ', NOW(), ' MigrateChannelMembers procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateChannelMembers; + +/* ==> mysql/000059_upgrade_users_v6.0.up.sql <== */ +/* ==> mysql/000074_upgrade_users_v6.3.up.sql <== */ +/* ==> mysql/000077_upgrade_users_v6.5.up.sql <== */ +/* ==> mysql/000088_remaining_migrations.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateUsers () +BEGIN + -- 'ALTER TABLE Users MODIFY COLUMN Props JSON;', + DECLARE ChangeProps BOOLEAN; + DECLARE ChangePropsQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users MODIFY COLUMN NotifyProps JSON;', + DECLARE ChangeNotifyProps BOOLEAN; + DECLARE ChangeNotifyPropsQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users ALTER Timezone DROP DEFAULT;', + DECLARE DropTimezoneDefault BOOLEAN; + DECLARE DropTimezoneDefaultQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users MODIFY COLUMN Timezone JSON;', + DECLARE ChangeTimezone BOOLEAN; + DECLARE ChangeTimezoneQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users MODIFY COLUMN Roles text;', + DECLARE ChangeRoles BOOLEAN; + DECLARE ChangeRolesQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users DROP COLUMN AcceptedTermsOfServiceId;', + DECLARE DropTermsOfService BOOLEAN; + DECLARE DropTermsOfServiceQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users DROP COLUMN AcceptedServiceTermsId;', + DECLARE DropServiceTerms BOOLEAN; + DECLARE DropServiceTermsQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Users DROP COLUMN ThemeProps', + DECLARE DropThemeProps BOOLEAN; + DECLARE DropThemePropsQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Props' + AND LOWER(column_type) != 'json' + INTO ChangeProps; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'NotifyProps' + AND LOWER(column_type) != 'json' + INTO ChangeNotifyProps; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND column_default IS NOT NULL + INTO DropTimezoneDefault; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Timezone' + AND LOWER(column_type) != 'json' + INTO ChangeTimezone; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND LOWER(column_type) != 'text' + INTO ChangeRoles; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'AcceptedTermsOfServiceId' + INTO DropTermsOfService; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'AcceptedServiceTermsId' + INTO DropServiceTerms; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'ThemeProps' + INTO DropThemeProps; + + IF ChangeProps THEN + SET ChangePropsQuery = 'MODIFY COLUMN Props JSON'; + END IF; + + IF ChangeNotifyProps THEN + SET ChangeNotifyPropsQuery = 'MODIFY COLUMN NotifyProps JSON'; + END IF; + + IF DropTimezoneDefault THEN + SET DropTimezoneDefaultQuery = 'ALTER Timezone DROP DEFAULT'; + END IF; + + IF ChangeTimezone THEN + SET ChangeTimezoneQuery = 'MODIFY COLUMN Timezone JSON'; + END IF; + + IF ChangeRoles THEN + SET ChangeRolesQuery = 'MODIFY COLUMN Roles text'; + END IF; + + IF DropTermsOfService THEN + SET DropTermsOfServiceQuery = 'DROP COLUMN AcceptedTermsOfServiceId'; + END IF; + + IF DropServiceTerms THEN + SET DropServiceTermsQuery = 'DROP COLUMN AcceptedServiceTermsId'; + END IF; + + IF DropThemeProps THEN + INSERT INTO Preferences(UserId, Category, Name, Value) SELECT Id, '', '', ThemeProps FROM Users WHERE Users.ThemeProps != 'null'; + SET DropThemePropsQuery = 'DROP COLUMN ThemeProps'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', ChangePropsQuery, ChangeNotifyPropsQuery, DropTimezoneDefaultQuery, ChangeTimezoneQuery, ChangeRolesQuery, DropTermsOfServiceQuery, DropServiceTermsQuery, DropThemePropsQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Users ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateUsers procedure starting.') AS DEBUG; +CALL MigrateUsers(); +SELECT CONCAT('-- ', NOW(), ' MigrateUsers procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateUsers; + +/* ==> mysql/000060_upgrade_jobs_v6.0.up.sql <== */ +/* ==> mysql/000069_upgrade_jobs_v6.1.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateJobs () +BEGIN + -- 'ALTER TABLE Jobs MODIFY COLUMN Data JSON;', + DECLARE ModifyData BOOLEAN; + DECLARE ModifyDataQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_jobs_status_type ON Jobs(Status, Type);' + DECLARE CreateIndex BOOLEAN; + DECLARE CreateIndexQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Jobs' + AND table_schema = DATABASE() + AND column_name = 'Data' + AND LOWER(column_type) != 'JSON' + INTO ModifyData; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Jobs' + AND table_schema = DATABASE() + AND index_name = 'idx_jobs_status_type' + INTO CreateIndex; + + IF ModifyData THEN + SET ModifyDataQuery = 'MODIFY COLUMN Data JSON'; + END IF; + + IF CreateIndex THEN + SET CreateIndexQuery = 'ADD INDEX idx_jobs_status_type (Status, Type)'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', ModifyDataQuery, CreateIndexQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Jobs ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateJobs procedure starting.') AS DEBUG; +CALL MigrateJobs(); +SELECT CONCAT('-- ', NOW(), ' MigrateJobs procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateJobs; + +/* ==> mysql/000061_upgrade_link_metadata_v6.0.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateLinkMetadata () +BEGIN + -- ALTER TABLE LinkMetadata MODIFY COLUMN Data JSON; + DECLARE ModifyData BOOLEAN; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'LinkMetadata' + AND table_schema = DATABASE() + AND column_name = 'Data' + AND LOWER(column_type) != 'JSON' + INTO ModifyData; + + IF ModifyData THEN + ALTER TABLE LinkMetadata MODIFY COLUMN Data JSON; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateLinkMetadata procedure starting.') AS DEBUG; +CALL MigrateLinkMetadata(); +SELECT CONCAT('-- ', NOW(), ' MigrateLinkMetadata procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateLinkMetadata; + +/* ==> mysql/000062_upgrade_sessions_v6.0.up.sql <== */ +/* ==> mysql/000071_upgrade_sessions_v6.1.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateSessions () +BEGIN + -- 'ALTER TABLE Sessions MODIFY COLUMN Props JSON;', + DECLARE ModifyProps BOOLEAN; + DECLARE ModifyPropsQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Sessions MODIFY COLUMN Roles text;', + DECLARE ModifyRoles BOOLEAN; + DECLARE ModifyRolesQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Sessions' + AND table_schema = DATABASE() + AND column_name = 'Props' + AND LOWER(column_type) != 'json' + INTO ModifyProps; + + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Sessions' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND LOWER(column_type) != 'text' + INTO ModifyRoles; + + IF ModifyProps THEN + SET ModifyPropsQuery = 'MODIFY COLUMN Props JSON'; + END IF; + + IF ModifyRoles THEN + SET ModifyRolesQuery = 'MODIFY COLUMN Roles text'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', ModifyPropsQuery, ModifyRolesQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Sessions ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateSessions procedure starting.') AS DEBUG; +CALL MigrateSessions(); +SELECT CONCAT('-- ', NOW(), ' MigrateSessions procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateSessions; + +/* ==> mysql/000063_upgrade_threads_v6.0.up.sql <== */ +/* ==> mysql/000083_threads_threaddeleteat.up.sql <== */ +/* ==> mysql/000096_threads_threadteamid.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateThreads () +BEGIN + -- 'ALTER TABLE Threads MODIFY COLUMN Participants JSON;' + DECLARE ChangeParticipants BOOLEAN; + DECLARE ChangeParticipantsQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Threads DROP COLUMN DeleteAt;' + DECLARE DropDeleteAt BOOLEAN; + DECLARE DropDeleteAtQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Threads ADD COLUMN ThreadDeleteAt bigint(20);' + DECLARE CreateThreadDeleteAt BOOLEAN; + DECLARE CreateThreadDeleteAtQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Threads DROP COLUMN TeamId;' + DECLARE DropTeamId BOOLEAN; + DECLARE DropTeamIdQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Threads ADD COLUMN ThreadTeamId varchar(26) DEFAULT NULL;' + DECLARE CreateThreadTeamId BOOLEAN; + DECLARE CreateThreadTeamIdQuery TEXT DEFAULT NULL; + + -- CREATE INDEX idx_threads_channel_id_last_reply_at ON Threads(ChannelId, LastReplyAt); + DECLARE CreateIndex BOOLEAN; + DECLARE CreateIndexQuery TEXT DEFAULT NULL; + + -- DROP INDEX idx_threads_channel_id ON Threads; + DECLARE DropIndex BOOLEAN; + DECLARE DropIndexQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'Participants' + AND LOWER(column_type) != 'json' + INTO ChangeParticipants; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'DeleteAt' + INTO DropDeleteAt; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'ThreadDeleteAt' + INTO CreateThreadDeleteAt; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'TeamId' + INTO DropTeamId; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'ThreadTeamId' + INTO CreateThreadTeamId; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND index_name = 'idx_threads_channel_id_last_reply_at' + INTO CreateIndex; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND index_name = 'idx_threads_channel_id' + INTO DropIndex; + + IF ChangeParticipants THEN + SET ChangeParticipantsQuery = 'MODIFY COLUMN Participants JSON'; + END IF; + + IF DropDeleteAt THEN + SET DropDeleteAtQuery = 'DROP COLUMN DeleteAt'; + END IF; + + IF CreateThreadDeleteAt THEN + SET CreateThreadDeleteAtQuery = 'ADD COLUMN ThreadDeleteAt bigint(20)'; + END IF; + + IF DropTeamId THEN + SET DropTeamIdQuery = 'DROP COLUMN TeamId'; + END IF; + + IF CreateThreadTeamId THEN + SET CreateThreadTeamIdQuery = 'ADD COLUMN ThreadTeamId varchar(26) DEFAULT NULL'; + END IF; + + IF CreateIndex THEN + SET CreateIndexQuery = 'ADD INDEX idx_threads_channel_id_last_reply_at (ChannelId, LastReplyAt)'; + END IF; + + IF DropIndex THEN + SET DropIndexQuery = 'DROP INDEX idx_threads_channel_id'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', ChangeParticipantsQuery, DropDeleteAtQuery, CreateThreadDeleteAtQuery, DropTeamIdQuery, CreateThreadTeamIdQuery, CreateIndexQuery, DropIndexQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Threads ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + + UPDATE Threads, Posts + SET Threads.ThreadDeleteAt = Posts.DeleteAt + WHERE Posts.Id = Threads.PostId + AND Threads.ThreadDeleteAt IS NULL; + + UPDATE Threads, Channels + SET Threads.ThreadTeamId = Channels.TeamId + WHERE Channels.Id = Threads.ChannelId + AND Threads.ThreadTeamId IS NULL; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateThreads procedure starting.') AS DEBUG; +CALL MigrateThreads(); +SELECT CONCAT('-- ', NOW(), ' MigrateThreads procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateThreads; + +/* ==> mysql/000064_upgrade_status_v6.0.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateStatus () +BEGIN + -- 'CREATE INDEX idx_status_status_dndendtime ON Status(Status, DNDEndTime);' + DECLARE CreateIndex BOOLEAN; + DECLARE CreateIndexQuery TEXT DEFAULT NULL; + + -- 'DROP INDEX idx_status_status ON Status;', + DECLARE DropIndex BOOLEAN; + DECLARE DropIndexQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Status' + AND table_schema = DATABASE() + AND index_name = 'idx_status_status_dndendtime' + INTO CreateIndex; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Status' + AND table_schema = DATABASE() + AND index_name = 'idx_status_status' + INTO DropIndex; + + IF CreateIndex THEN + SET CreateIndexQuery = 'ADD INDEX idx_status_status_dndendtime (Status, DNDEndTime)'; + END IF; + + IF DropIndex THEN + SET DropIndexQuery = 'DROP INDEX idx_status_status'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', CreateIndexQuery, DropIndexQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Status ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateStatus procedure starting.') AS DEBUG; +CALL MigrateStatus (); +SELECT CONCAT('-- ', NOW(), ' MigrateStatus procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateStatus; + +/* ==> mysql/000065_upgrade_groupchannels_v6.0.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateGroupChannels () +BEGIN + -- 'CREATE INDEX idx_groupchannels_schemeadmin ON GroupChannels(SchemeAdmin);' + DECLARE CreateIndex BOOLEAN; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'GroupChannels' + AND table_schema = DATABASE() + AND index_name = 'idx_groupchannels_schemeadmin' + INTO CreateIndex; + + IF CreateIndex THEN + CREATE INDEX idx_groupchannels_schemeadmin ON GroupChannels(SchemeAdmin); + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateGroupChannels procedure starting.') AS DEBUG; +CALL MigrateGroupChannels (); +SELECT CONCAT('-- ', NOW(), ' MigrateGroupChannels procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateGroupChannels; + +/* ==> mysql/000066_upgrade_posts_v6.0.up.sql <== */ +/* ==> mysql/000080_posts_createat_id.up.sql <== */ +/* ==> mysql/000095_remove_posts_parentid.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigratePosts () +BEGIN + -- DROP COLUMN ParentId + DECLARE DropParentId BOOLEAN; + DECLARE DropParentIdQuery TEXT DEFAULT NULL; + + -- MODIFY COLUMN FileIds + DECLARE ModifyFileIds BOOLEAN; + DECLARE ModifyFileIdsQuery TEXT DEFAULT NULL; + + -- MODIFY COLUMN Props + DECLARE ModifyProps BOOLEAN; + DECLARE ModifyPropsQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_posts_root_id_delete_at ON Posts(RootId, DeleteAt);' + DECLARE CreateIndexRootId BOOLEAN; + DECLARE CreateIndexRootIdQuery TEXT DEFAULT NULL; + + -- 'DROP INDEX idx_posts_root_id ON Posts;', + DECLARE DropIndex BOOLEAN; + DECLARE DropIndexQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_posts_create_at_id on Posts(CreateAt, Id) LOCK=NONE;' + DECLARE CreateIndexCreateAt BOOLEAN; + DECLARE CreateIndexCreateAtQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'Posts' + AND table_schema = DATABASE() + AND COLUMN_NAME = 'ParentId' + INTO DropParentId; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND column_name = 'FileIds' + AND LOWER(column_type) != 'text' + INTO ModifyFileIds; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND column_name = 'Props' + AND LOWER(column_type) != 'json' + INTO ModifyProps; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND index_name = 'idx_posts_root_id_delete_at' + INTO CreateIndexRootId; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND index_name = 'idx_posts_root_id' + INTO DropIndex; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND index_name = 'idx_posts_create_at_id' + INTO CreateIndexCreateAt; + + IF DropParentId THEN + SET DropParentIdQuery = 'DROP COLUMN ParentId'; + UPDATE Posts SET RootId = ParentId WHERE RootId = '' AND RootId != ParentId; + END IF; + + IF ModifyFileIds THEN + SET ModifyFileIdsQuery = 'MODIFY COLUMN FileIds text'; + END IF; + + IF ModifyProps THEN + SET ModifyPropsQuery = 'MODIFY COLUMN Props JSON'; + END IF; + + IF CreateIndexRootId THEN + SET CreateIndexRootIdQuery = 'ADD INDEX idx_posts_root_id_delete_at (RootId, DeleteAt)'; + END IF; + + IF DropIndex THEN + SET DropIndexQuery = 'DROP INDEX idx_posts_root_id'; + END IF; + + IF CreateIndexCreateAt THEN + SET CreateIndexCreateAtQuery = 'ADD INDEX idx_posts_create_at_id (CreateAt, Id)'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', DropParentIdQuery, ModifyFileIdsQuery, ModifyPropsQuery, CreateIndexRootIdQuery, DropIndexQuery, CreateIndexCreateAtQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Posts ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigratePosts procedure starting.') AS DEBUG; +CALL MigratePosts (); +SELECT CONCAT('-- ', NOW(), ' MigratePosts procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigratePosts; + +/* ==> mysql/000068_upgrade_teammembers_v6.1.up.sql <== */ +/* ==> mysql/000092_add_createat_to_teammembers.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateTeamMembers () +BEGIN + -- 'ALTER TABLE TeamMembers MODIFY COLUMN Roles text;', + DECLARE ModifyRoles BOOLEAN; + DECLARE ModifyRolesQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE TeamMembers ADD COLUMN CreateAt bigint DEFAULT 0;', + DECLARE AddCreateAt BOOLEAN; + DECLARE AddCreateAtQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_teammembers_createat ON TeamMembers(CreateAt);' + DECLARE CreateIndex BOOLEAN; + DECLARE CreateIndexQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'TeamMembers' + AND table_schema = DATABASE() + AND column_name = 'Roles' + AND LOWER(column_type) != 'text' + INTO ModifyRoles; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'TeamMembers' + AND table_schema = DATABASE() + AND column_name = 'CreateAt' + INTO AddCreateAt; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'TeamMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_teammembers_createat' + INTO CreateIndex; + + IF ModifyRoles THEN + SET ModifyRolesQuery = 'MODIFY COLUMN Roles text'; + END IF; + + IF AddCreateAt THEN + SET AddCreateAtQuery = 'ADD COLUMN CreateAt bigint DEFAULT 0'; + END IF; + + IF CreateIndex THEN + SET CreateIndexQuery = 'ADD INDEX idx_teammembers_createat (CreateAt)'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', ModifyRolesQuery, AddCreateAtQuery, CreateIndexQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE TeamMembers ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateTeamMembers procedure starting.') AS DEBUG; +CALL MigrateTeamMembers (); +SELECT CONCAT('-- ', NOW(), ' MigrateTeamMembers procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateTeamMembers; + +/* ==> mysql/000072_upgrade_schemes_v6.3.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateSchemes () +BEGIN + -- 'ALTER TABLE Schemes ADD COLUMN DefaultPlaybookAdminRole VARCHAR(64) DEFAULT "";' + DECLARE AddDefaultPlaybookAdminRole BOOLEAN; + DECLARE AddDefaultPlaybookAdminRoleQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Schemes ADD COLUMN DefaultPlaybookMemberRole VARCHAR(64) DEFAULT "";' + DECLARE AddDefaultPlaybookMemberRole BOOLEAN; + DECLARE AddDefaultPlaybookMemberRoleQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Schemes ADD COLUMN DefaultRunAdminRole VARCHAR(64) DEFAULT "";' + DECLARE AddDefaultRunAdminRole BOOLEAN; + DECLARE AddDefaultRunAdminRoleQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Schemes ADD COLUMN DefaultRunMemberRole VARCHAR(64) DEFAULT "";' + DECLARE AddDefaultRunMemberRole BOOLEAN; + DECLARE AddDefaultRunMemberRoleQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultPlaybookAdminRole' + INTO AddDefaultPlaybookAdminRole; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultPlaybookMemberRole' + INTO AddDefaultPlaybookMemberRole; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultRunAdminRole' + INTO AddDefaultRunAdminRole; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Schemes' + AND table_schema = DATABASE() + AND column_name = 'DefaultRunMemberRole' + INTO AddDefaultRunMemberRole; + + IF AddDefaultPlaybookAdminRole THEN + SET AddDefaultPlaybookAdminRoleQuery = 'ADD COLUMN DefaultPlaybookAdminRole VARCHAR(64) DEFAULT ""'; + END IF; + + IF AddDefaultPlaybookMemberRole THEN + SET AddDefaultPlaybookMemberRoleQuery = 'ADD COLUMN DefaultPlaybookMemberRole VARCHAR(64) DEFAULT ""'; + END IF; + + IF AddDefaultRunAdminRole THEN + SET AddDefaultRunAdminRoleQuery = 'ADD COLUMN DefaultRunAdminRole VARCHAR(64) DEFAULT ""'; + END IF; + + IF AddDefaultRunMemberRole THEN + SET AddDefaultRunMemberRoleQuery = 'ADD COLUMN DefaultRunMemberRole VARCHAR(64) DEFAULT ""'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', AddDefaultPlaybookAdminRoleQuery, AddDefaultPlaybookMemberRoleQuery, AddDefaultRunAdminRoleQuery, AddDefaultRunMemberRoleQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Schemes ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateSchemes procedure starting.') AS DEBUG; +CALL MigrateSchemes (); +SELECT CONCAT('-- ', NOW(), ' MigrateSchemes procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateSchemes; + +/* ==> mysql/000073_upgrade_plugin_key_value_store_v6.3.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigratePluginKeyValueStore () +BEGIN + -- 'ALTER TABLE PluginKeyValueStore MODIFY COLUMN PKey varchar(150);', + DECLARE ModifyPKey BOOLEAN; + + SELECT COUNT(*) FROM Information_Schema.Columns + WHERE table_name = 'PluginKeyValueStore' + AND table_schema = DATABASE() + AND column_name = 'PKey' + AND LOWER(column_type) != 'varchar(150)' + INTO ModifyPKey; + + IF ModifyPKey THEN + ALTER TABLE PluginKeyValueStore MODIFY COLUMN PKey varchar(150); + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigratePluginKeyValueStore procedure starting.') AS DEBUG; +CALL MigratePluginKeyValueStore (); +SELECT CONCAT('-- ', NOW(), ' MigratePluginKeyValueStore procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigratePluginKeyValueStore; + +/* ==> mysql/000078_create_oauth_mattermost_app_id.up.sql <== */ +/* ==> mysql/000082_upgrade_oauth_mattermost_app_id.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateOAuthApps () +BEGIN + -- 'ALTER TABLE OAuthApps ADD COLUMN MattermostAppID varchar(32);' + DECLARE AddMattermostAppID BOOLEAN; + DECLARE AddMattermostAppIDQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'OAuthApps' + AND table_schema = DATABASE() + AND column_name = 'MattermostAppID' + INTO AddMattermostAppID; + + IF AddMattermostAppID THEN + SET AddMattermostAppIDQuery = 'ADD COLUMN MattermostAppID varchar(32) NOT NULL DEFAULT ""'; + SET @query = CONCAT('ALTER TABLE OAuthApps ', CONCAT_WS(', ', AddMattermostAppIDQuery)); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + + IF AddMattermostAppID THEN + UPDATE OAuthApps SET MattermostAppID = "" WHERE MattermostAppID IS NULL; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateOAuthApps procedure starting.') AS DEBUG; +CALL MigrateOAuthApps (); +SELECT CONCAT('-- ', NOW(), ' MigrateOAuthApps procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateOAuthApps; + +/* ==> mysql/000079_usergroups_displayname_index.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateUserGroups () +BEGIN + -- 'CREATE INDEX idx_usergroups_displayname ON UserGroups(DisplayName);' + DECLARE CreateIndex BOOLEAN; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'UserGroups' + AND table_schema = DATABASE() + AND index_name = 'idx_usergroups_displayname' + INTO CreateIndex; + + IF CreateIndex THEN + CREATE INDEX idx_usergroups_displayname ON UserGroups(DisplayName); + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateUserGroups procedure starting.') AS DEBUG; +CALL MigrateUserGroups (); +SELECT CONCAT('-- ', NOW(), ' MigrateUserGroups procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateUserGroups; + +/* ==> mysql/000081_threads_deleteat.up.sql <== */ +-- Replaced by 000083_threads_threaddeleteat.up.sql + +/* ==> mysql/000084_recent_searches.up.sql <== */ +CREATE TABLE IF NOT EXISTS RecentSearches ( + UserId CHAR(26), + SearchPointer int, + Query json, + CreateAt bigint NOT NULL, + PRIMARY KEY (UserId, SearchPointer) +); + +/* ==> mysql/000085_fileinfo_add_archived_column.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateFileInfo () +BEGIN + -- 'ALTER TABLE FileInfo ADD COLUMN Archived boolean NOT NULL DEFAULT false;' + DECLARE AddArchived BOOLEAN; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'FileInfo' + AND table_schema = DATABASE() + AND column_name = 'Archived' + INTO AddArchived; + + IF AddArchived THEN + ALTER TABLE FileInfo ADD COLUMN Archived boolean NOT NULL DEFAULT false; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateFileInfo procedure starting.') AS DEBUG; +CALL MigrateFileInfo (); +SELECT CONCAT('-- ', NOW(), ' MigrateFileInfo procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateFileInfo; + +/* ==> mysql/000086_add_cloud_limits_archived.up.sql <== */ +/* ==> mysql/000090_create_enums.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateTeams () +BEGIN + -- 'ALTER TABLE Teams ADD COLUMN CloudLimitsArchived BOOLEAN NOT NULL DEFAULT FALSE;', + DECLARE AddCloudLimitsArchived BOOLEAN; + DECLARE AddCloudLimitsArchivedQuery TEXT DEFAULT NULL; + + -- 'ALTER TABLE Teams MODIFY COLUMN Type ENUM("I", "O");', + DECLARE ModifyType BOOLEAN; + DECLARE ModifyTypeQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Teams' + AND table_schema = DATABASE() + AND column_name = 'CloudLimitsArchived' + INTO AddCloudLimitsArchived; + + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Teams' + AND table_schema = DATABASE() + AND column_name = 'Type' + AND REPLACE(LOWER(column_type), '"', "'") != "enum('i','o')" + INTO ModifyType; + + IF AddCloudLimitsArchived THEN + SET AddCloudLimitsArchivedQuery = 'ADD COLUMN CloudLimitsArchived BOOLEAN NOT NULL DEFAULT FALSE'; + END IF; + + IF ModifyType THEN + SET ModifyTypeQuery = 'MODIFY COLUMN Type ENUM("I", "O")'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', AddCloudLimitsArchivedQuery, ModifyTypeQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Teams ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateTeams procedure starting.') AS DEBUG; +CALL MigrateTeams (); +SELECT CONCAT('-- ', NOW(), ' MigrateTeams procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateTeams; + +/* ==> mysql/000087_sidebar_categories_index.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateSidebarCategories () +BEGIN + -- 'CREATE INDEX idx_sidebarcategories_userid_teamid on SidebarCategories(UserId, TeamId) LOCK=NONE;' + DECLARE CreateIndex BOOLEAN; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'SidebarCategories' + AND table_schema = DATABASE() + AND index_name = 'idx_sidebarcategories_userid_teamid' + INTO CreateIndex; + + IF CreateIndex THEN + CREATE INDEX idx_sidebarcategories_userid_teamid on SidebarCategories(UserId, TeamId) LOCK=NONE; + END IF; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateSidebarCategories procedure starting.') AS DEBUG; +CALL MigrateSidebarCategories (); +SELECT CONCAT('-- ', NOW(), ' MigrateSidebarCategories procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateSidebarCategories; + +/* ==> mysql/000088_remaining_migrations.up.sql <== */ +DROP TABLE IF EXISTS JobStatuses; +DROP TABLE IF EXISTS PasswordRecovery; + +/* ==> mysql/000089_add-channelid-to-reaction.up.sql <== */ +DELIMITER // +CREATE PROCEDURE MigrateReactions () +BEGIN + -- 'ALTER TABLE Reactions ADD COLUMN ChannelId varchar(26) NOT NULL DEFAULT "";', + DECLARE AddChannelId BOOLEAN; + DECLARE AddChannelIdQuery TEXT DEFAULT NULL; + + -- 'CREATE INDEX idx_reactions_channel_id ON Reactions(ChannelId);' + DECLARE CreateIndex BOOLEAN; + DECLARE CreateIndexQuery TEXT DEFAULT NULL; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Reactions' + AND table_schema = DATABASE() + AND column_name = 'ChannelId' + INTO AddChannelId; + + SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Reactions' + AND table_schema = DATABASE() + AND index_name = 'idx_reactions_channel_id' + INTO CreateIndex; + + IF AddChannelId THEN + SET AddChannelIdQuery = 'ADD COLUMN ChannelId varchar(26) NOT NULL DEFAULT ""'; + END IF; + + IF CreateIndex THEN + SET CreateIndexQuery = 'ADD INDEX idx_reactions_channel_id (ChannelId)'; + END IF; + + SET @alterQuery = CONCAT_WS(', ', AddChannelIdQuery, CreateIndexQuery); + IF @alterQuery <> '' THEN + SET @query = CONCAT('ALTER TABLE Reactions ', @alterQuery); + PREPARE stmt FROM @query; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + END IF; + + UPDATE Reactions SET ChannelId = COALESCE((select ChannelId from Posts where Posts.Id = Reactions.PostId), '') WHERE ChannelId=""; +END// +DELIMITER ; +SELECT CONCAT('-- ', NOW(), ' MigrateReactions procedure starting.') AS DEBUG; +CALL MigrateReactions (); +SELECT CONCAT('-- ', NOW(), ' MigrateReactions procedure finished.') AS DEBUG; +DROP PROCEDURE IF EXISTS MigrateReactions; + +/* ==> mysql/000091_create_post_reminder.up.sql <== */ +CREATE TABLE IF NOT EXISTS PostReminders ( + PostId varchar(26) NOT NULL, + UserId varchar(26) NOT NULL, + TargetTime bigint, + INDEX idx_postreminders_targettime (TargetTime), + PRIMARY KEY (PostId, UserId) +); + +/* ==> mysql/000093_notify_admin.up.sql <== */ +CREATE TABLE IF NOT EXISTS NotifyAdmin ( + UserId varchar(26) NOT NULL, + CreateAt bigint(20) DEFAULT NULL, + RequiredPlan varchar(26) NOT NULL, + RequiredFeature varchar(100) NOT NULL, + Trial BOOLEAN NOT NULL, + PRIMARY KEY (UserId, RequiredFeature, RequiredPlan) +); + +/* ==> mysql/000094_threads_teamid.up.sql <== */ +-- Replaced by 000096_threads_threadteamid.up.sql + +/* ==> mysql/000097_create_posts_priority.up.sql <== */ +CREATE TABLE IF NOT EXISTS PostsPriority ( + PostId varchar(26) NOT NULL, + ChannelId varchar(26) NOT NULL, + Priority varchar(32) NOT NULL, + RequestedAck tinyint(1), + PersistentNotifications tinyint(1), + PRIMARY KEY (PostId) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +/* ==> mysql/000098_create_post_acknowledgements.up.sql <== */ +CREATE TABLE IF NOT EXISTS PostAcknowledgements ( + PostId varchar(26) NOT NULL, + UserId varchar(26) NOT NULL, + AcknowledgedAt bigint(20) DEFAULT NULL, + PRIMARY KEY (PostId, UserId) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +/* ==> mysql/000099_create_drafts.up.sql <== */ +/* ==> mysql/000100_add_draft_priority_column.up.sql <== */ +CREATE TABLE IF NOT EXISTS Drafts ( + CreateAt bigint(20) DEFAULT NULL, + UpdateAt bigint(20) DEFAULT NULL, + DeleteAt bigint(20) DEFAULT NULL, + UserId varchar(26) NOT NULL, + ChannelId varchar(26) NOT NULL, + RootId varchar(26) DEFAULT '', + Message text, + Props text, + FileIds text, + Priority text, + PRIMARY KEY (UserId, ChannelId, RootId) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +/* ==> mysql/000101_create_true_up_review_history.up.sql <== */ +CREATE TABLE IF NOT EXISTS TrueUpReviewHistory ( + DueDate bigint(20), + Completed boolean, + PRIMARY KEY (DueDate) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/server/scripts/esrupgrades/esr.6.3-7.8.mysql.cleanup.sql b/server/scripts/esrupgrades/esr.6.3-7.8.mysql.cleanup.sql new file mode 100644 index 0000000000..43af4c4844 --- /dev/null +++ b/server/scripts/esrupgrades/esr.6.3-7.8.mysql.cleanup.sql @@ -0,0 +1,168 @@ +/* Product notices are controlled externally, via the mattermost/notices repository. + When there is a new notice specified there, the server may have time, right after + the migration and before it is shut down, to download it and modify the + ProductNoticeViewState table, adding a row for all users that have not seen it or + removing old notices that no longer need to be shown. This can happen in the + UpdateProductNotices function that is executed periodically to update the notices + cache. The script will never do this, so we need to remove all rows in that table + to avoid any unwanted diff. */ +DELETE FROM ProductNoticeViewState; + +/* Remove migration-related tables that are only updated through the server to track which + migrations have been applied */ +DROP TABLE IF EXISTS db_lock; +DROP TABLE IF EXISTS db_migrations; + +/* The security update check in the server may update the LastSecurityTime system value. To + avoid any spurious difference in the migrations, we update it to a fixed value. */ +UPDATE Systems SET Value = 1 WHERE Name = 'LastSecurityTime'; + +/* The server migration may contain a row in the Systems table marking the onboarding as complete. + There are no migrations related to this, so we can simply drop it here. */ +DELETE FROM Systems WHERE Name = 'FirstAdminSetupComplete'; + +/* The server migration contains an in-app migration that add playbooks permissions to certain roles: + getPlaybooksPermissionsAddManageRoles, defined in https://github.com/mattermost/mattermost-server/blob/56a093ceaee6389a01a35b6d4626ef5a9fea4759/app/permissions_migrations.go#L1056-L1072 + The specific roles ('%playbook%') are removed in the procedure below, but the migrations also add new rows to the Systems table marking the migrations as complete. + This in-app migration does not happen in the script, so we remove that rows here. */ +DELETE FROM Systems WHERE Name = 'playbooks_manage_roles'; + +/* The server migration contains an in-app migration that adds boards permissions to certain roles: + getProductsBoardsPermissions, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L1074-L1093 + The specific roles (sysconsole_read_product_boards and sysconsole_write_product_boards) are removed in the procedure below, + but the migrations also adds a new row to the Systems table marking the migrations as complete. + This in-app migration does not happen in the script, so we remove that row here. */ +DELETE FROM Systems WHERE Name = 'products_boards'; + +/* The server migration contains an in-app migration that adds Ids to the Teams whose InviteId is an empty string: + doRemainingSchemaMigrations, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L515-L540 + The migration is not replicated in the script, since it happens in-app, but the server adds a new row to the + Systems table marking the table as complete, which the script doesn't do, so we remove that row here. */ +DELETE FROM Systems WHERE Name = 'RemainingSchemaMigrations'; + +/* The server migration contains three in-app migration that adds a new role and new permissions + related to custom groups. The migrations are: + - doCustomGroupAdminRoleCreationMigration https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L345-L469 + - getAddCustomUserGroupsPermissions https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L974-L995 + - getAddCustomUserGroupsPermissionRestore https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/permissions_migrations.go#L997-L1019 + The specific roles and permissions are removed in the procedure below, but the migrations also + adds a new row to the Roles table for the new role and new rows to the Systems table marking the + migrations as complete. + This in-app migration does not happen in the script, so we remove that row here. */ +DELETE FROM Roles WHERE Name = 'system_custom_group_admin'; +DELETE FROM Systems WHERE Name = 'CustomGroupAdminRoleCreationMigrationComplete'; +DELETE FROM Systems WHERE Name = 'custom_groups_permissions'; +DELETE FROM Systems WHERE Name = 'custom_groups_permission_restore'; + +/* The server migration contains an in-app migration that updates the config, setting ServiceSettings.PostPriority + to true, doPostPriorityConfigDefaultTrueMigration, defined in https://github.com/mattermost/mattermost-server/blob/282bd351e3767dcfd8c8340da2e0915197c0dbcb/app/migrations.go#L542-L560 + The migration is not replicated in the script, since it happens in-app, but the server adds a new row to the + Systems table marking the table as complete, which the script doesn't do, so we remove that row here. */ +DELETE FROM Systems WHERE Name = 'PostPriorityConfigDefaultTrueMigrationComplete'; + +/* The rest of this script defines and executes a procedure to update the Roles table. It performs several changes: + 1. Set the UpdateAt column of all rows to a fixed value, so that the server migration changes to this column + do not appear in the diff. + 2. Remove the set of specific permissions added in the server migration that is not covered by the script, as + this logic happens all in-app after the normal DB migrations. + 3. Set a consistent order in the Permissions column, which is modelled a space-separated string containing each of + the different permissions each role has. This change is the reason why we need a complex procedure, which creates + a temporary table that pairs each single permission to its corresponding ID. So if the Roles table contains two + rows like: + Id: 'abcd' + Permissions: 'view_team read_public_channel invite_user' + Id: 'efgh' + Permissions: 'view_team create_emojis' + then the new temporary table will contain five rows like: + Id: 'abcd' + Permissions: 'view_team' + Id: 'abcd' + Permissions: 'read_public_channel' + Id: 'abcd' + Permissions: 'invite_user' + Id: 'efgh' + Permissions: 'view_team' + Id: 'efgh' + Permissions: 'create_emojis' +*/ + +DROP PROCEDURE IF EXISTS splitPermissions; +DROP PROCEDURE IF EXISTS sortAndFilterPermissionsInRoles; + +DROP TEMPORARY TABLE IF EXISTS temp_roles; +CREATE TEMPORARY TABLE temp_roles(id varchar(26), permission longtext); + +DELIMITER // + +/* Auxiliary procedure that splits the space-separated permissions string into single rows that are inserted + in the temporary temp_roles table along with their corresponding ID. */ +CREATE PROCEDURE splitPermissions( + IN id varchar(26), + IN permissionsString longtext +) +BEGIN + DECLARE idx INT DEFAULT 0; + SELECT TRIM(permissionsString) INTO permissionsString; + SELECT LOCATE(' ', permissionsString) INTO idx; + WHILE idx > 0 DO + INSERT INTO temp_roles SELECT id, TRIM(LEFT(permissionsString, idx)); + SELECT SUBSTR(permissionsString, idx+1) INTO permissionsString; + SELECT LOCATE(' ', permissionsString) INTO idx; + END WHILE; + INSERT INTO temp_roles(id, permission) VALUES(id, TRIM(permissionsString)); +END; // + +/* Main procedure that does update the Roles table */ +CREATE PROCEDURE sortAndFilterPermissionsInRoles() +BEGIN + DECLARE done INT DEFAULT FALSE; + DECLARE rolesId varchar(26) DEFAULT ''; + DECLARE rolesPermissions longtext DEFAULT ''; + DECLARE cur1 CURSOR FOR SELECT Id, Permissions FROM Roles; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; + + /* 1. Set a fixed value in the UpdateAt column for all rows in Roles table */ + UPDATE Roles SET UpdateAt = 1; + + /* Call splitPermissions for every row in the Roles table, thus populating the + temp_roles table. */ + OPEN cur1; + read_loop: LOOP + FETCH cur1 INTO rolesId, rolesPermissions; + IF done THEN + LEAVE read_loop; + END IF; + CALL splitPermissions(rolesId, rolesPermissions); + END LOOP; + CLOSE cur1; + + /* 2. Filter out the new permissions added by the in-app migrations */ + DELETE FROM temp_roles WHERE permission LIKE 'sysconsole_read_products_boards'; + DELETE FROM temp_roles WHERE permission LIKE 'sysconsole_write_products_boards'; + DELETE FROM temp_roles WHERE permission LIKE 'playbook_public_manage_roles'; + DELETE FROM temp_roles WHERE permission LIKE 'playbook_private_manage_roles'; + DELETE FROM temp_roles WHERE permission LIKE '%custom_group%'; + + /* Temporarily set to the maximum permitted value, since the call to group_concat + below needs a value bigger than the default */ + SET group_concat_max_len = 18446744073709551615; + + /* 3. Update the Permissions column in the Roles table with the filtered, sorted permissions, + concatenated again as a space-separated string */ + UPDATE + Roles INNER JOIN ( + SELECT temp_roles.id as Id, TRIM(group_concat(temp_roles.permission ORDER BY temp_roles.permission SEPARATOR ' ')) as Permissions + FROM Roles JOIN temp_roles ON Roles.Id = temp_roles.id + GROUP BY temp_roles.id + ) AS Sorted + ON Roles.Id = Sorted.Id + SET Roles.Permissions = Sorted.Permissions; + + /* Reset group_concat_max_len to its default value */ + SET group_concat_max_len = 1024; +END; // +DELIMITER ; + +CALL sortAndFilterPermissionsInRoles(); + +DROP TEMPORARY TABLE IF EXISTS temp_roles; diff --git a/server/scripts/esrupgrades/esr.6.3-7.8.mysql.up.sql b/server/scripts/esrupgrades/esr.6.3-7.8.mysql.up.sql new file mode 100644 index 0000000000..543d4f68bf --- /dev/null +++ b/server/scripts/esrupgrades/esr.6.3-7.8.mysql.up.sql @@ -0,0 +1,599 @@ +/* ==> mysql/000041_create_upload_sessions.up.sql <== */ +/* Release 5.37 was meant to contain the index idx_uploadsessions_type, but a bug prevented that. + This part of the migration #41 adds such index */ + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'UploadSessions' + AND table_schema = DATABASE() + AND index_name = 'idx_uploadsessions_type' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_uploadsessions_type ON UploadSessions(Type);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000075_alter_upload_sessions_index.up.sql <== */ +DELIMITER // +CREATE PROCEDURE AlterIndex() +BEGIN + DECLARE columnName varchar(26) default ''; + + SELECT IFNULL(GROUP_CONCAT(column_name ORDER BY seq_in_index), '') INTO columnName + FROM information_schema.statistics + WHERE table_schema = DATABASE() + AND table_name = 'UploadSessions' + AND index_name = 'idx_uploadsessions_user_id' + GROUP BY index_name; + + IF columnName = 'Type' THEN + DROP INDEX idx_uploadsessions_user_id ON UploadSessions; + CREATE INDEX idx_uploadsessions_user_id ON UploadSessions(UserId); + END IF; +END// +DELIMITER ; +CALL AlterIndex(); +DROP PROCEDURE IF EXISTS AlterIndex; + +/* ==> mysql/000076_upgrade_lastrootpostat.up.sql <== */ +DELIMITER // +CREATE PROCEDURE Migrate_LastRootPostAt_Default () +BEGIN + IF ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'Channels' + AND TABLE_SCHEMA = DATABASE() + AND COLUMN_NAME = 'LastRootPostAt' + AND (COLUMN_DEFAULT IS NULL OR COLUMN_DEFAULT != 0) + ) = 1 THEN + ALTER TABLE Channels ALTER COLUMN LastRootPostAt SET DEFAULT 0; + END IF; +END// +DELIMITER ; +CALL Migrate_LastRootPostAt_Default (); +DROP PROCEDURE IF EXISTS Migrate_LastRootPostAt_Default; + +DELIMITER // +CREATE PROCEDURE Migrate_LastRootPostAt_Fix () +BEGIN + IF ( + SELECT COUNT(*) + FROM Channels + WHERE LastRootPostAt IS NULL + ) > 0 THEN + -- fixes migrate cte and sets the LastRootPostAt for channels that don't have it set + UPDATE + Channels + INNER JOIN ( + SELECT + Channels.Id channelid, + COALESCE(MAX(Posts.CreateAt), 0) AS lastrootpost + FROM + Channels + LEFT JOIN Posts FORCE INDEX (idx_posts_channel_id_update_at) ON Channels.Id = Posts.ChannelId + WHERE + Posts.RootId = '' + GROUP BY + Channels.Id) AS q ON q.channelid = Channels.Id + SET + LastRootPostAt = lastrootpost + WHERE + LastRootPostAt IS NULL; + + -- sets LastRootPostAt to 0, for channels with no posts + UPDATE Channels SET LastRootPostAt=0 WHERE LastRootPostAt IS NULL; + END IF; +END// +DELIMITER ; +CALL Migrate_LastRootPostAt_Fix (); +DROP PROCEDURE IF EXISTS Migrate_LastRootPostAt_Fix; + +/* ==> mysql/000077_upgrade_users_v6.5.up.sql <== */ + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'AcceptedServiceTermsId' + ) > 0, + 'ALTER TABLE Users DROP COLUMN AcceptedServiceTermsId;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000078_create_oauth_mattermost_app_id.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'OAuthApps' + AND table_schema = DATABASE() + AND column_name = 'MattermostAppID' + ) > 0, + 'SELECT 1', + 'ALTER TABLE OAuthApps ADD COLUMN MattermostAppID varchar(32);' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000079_usergroups_displayname_index.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'UserGroups' + AND table_schema = DATABASE() + AND index_name = 'idx_usergroups_displayname' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_usergroups_displayname ON UserGroups(DisplayName);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000080_posts_createat_id.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND index_name = 'idx_posts_create_at_id' + ) > 0, + 'SELECT 1;', + 'CREATE INDEX idx_posts_create_at_id on Posts(CreateAt, Id) LOCK=NONE;' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000081_threads_deleteat.up.sql <== */ +-- Replaced by 000083_threads_threaddeleteat.up.sql + +/* ==> mysql/000082_upgrade_oauth_mattermost_app_id.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'OAuthApps' + AND table_schema = DATABASE() + AND column_name = 'MattermostAppID' + ) > 0, + 'UPDATE OAuthApps SET MattermostAppID = "" WHERE MattermostAppID IS NULL;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'OAuthApps' + AND table_schema = DATABASE() + AND column_name = 'MattermostAppID' + ) > 0, + 'ALTER TABLE OAuthApps MODIFY MattermostAppID varchar(32) NOT NULL DEFAULT "";', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000083_threads_threaddeleteat.up.sql <== */ +-- Drop any existing DeleteAt column from 000081_threads_deleteat.up.sql +SET @preparedStatement = (SELECT IF( + EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'DeleteAt' + ) > 0, + 'ALTER TABLE Threads DROP COLUMN DeleteAt;', + 'SELECT 1;' +)); + +PREPARE removeColumnIfExists FROM @preparedStatement; +EXECUTE removeColumnIfExists; +DEALLOCATE PREPARE removeColumnIfExists; + +SET @preparedStatement = (SELECT IF( + NOT EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'ThreadDeleteAt' + ), + 'ALTER TABLE Threads ADD COLUMN ThreadDeleteAt bigint(20);', + 'SELECT 1;' +)); + +PREPARE addColumnIfNotExists FROM @preparedStatement; +EXECUTE addColumnIfNotExists; +DEALLOCATE PREPARE addColumnIfNotExists; + +UPDATE Threads, Posts +SET Threads.ThreadDeleteAt = Posts.DeleteAt +WHERE Posts.Id = Threads.PostId +AND Threads.ThreadDeleteAt IS NULL; + +/* ==> mysql/000084_recent_searches.up.sql <== */ +CREATE TABLE IF NOT EXISTS RecentSearches ( + UserId CHAR(26), + SearchPointer int, + Query json, + CreateAt bigint NOT NULL, + PRIMARY KEY (UserId, SearchPointer) +); +/* ==> mysql/000085_fileinfo_add_archived_column.up.sql <== */ + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'FileInfo' + AND table_schema = DATABASE() + AND column_name = 'Archived' + ) > 0, + 'SELECT 1', + 'ALTER TABLE FileInfo ADD COLUMN Archived boolean NOT NULL DEFAULT false;' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000086_add_cloud_limits_archived.up.sql <== */ +SET @preparedStatement = (SELECT IF( + NOT EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Teams' + AND table_schema = DATABASE() + AND column_name = 'CloudLimitsArchived' + ), + 'ALTER TABLE Teams ADD COLUMN CloudLimitsArchived BOOLEAN NOT NULL DEFAULT FALSE;', + 'SELECT 1' +)); + +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; + +/* ==> mysql/000087_sidebar_categories_index.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'SidebarCategories' + AND table_schema = DATABASE() + AND index_name = 'idx_sidebarcategories_userid_teamid' + ) > 0, + 'SELECT 1;', + 'CREATE INDEX idx_sidebarcategories_userid_teamid on SidebarCategories(UserId, TeamId) LOCK=NONE;' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000088_remaining_migrations.up.sql <== */ +DROP TABLE IF EXISTS JobStatuses; + +DROP TABLE IF EXISTS PasswordRecovery; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'ThemeProps' + ) > 0, + 'INSERT INTO Preferences(UserId, Category, Name, Value) SELECT Id, \'\', \'\', ThemeProps FROM Users WHERE Users.ThemeProps != \'null\'', + 'SELECT 1' +)); + +PREPARE migrateTheme FROM @preparedStatement; +EXECUTE migrateTheme; +DEALLOCATE PREPARE migrateTheme; + +-- We have to do this twice because the prepared statement doesn't support multiple SQL queries +-- in a single string. + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'ThemeProps' + ) > 0, + 'ALTER TABLE Users DROP COLUMN ThemeProps', + 'SELECT 1' +)); + +PREPARE migrateTheme FROM @preparedStatement; +EXECUTE migrateTheme; +DEALLOCATE PREPARE migrateTheme; + +/* ==> mysql/000089_add-channelid-to-reaction.up.sql <== */ +SET @preparedStatement = (SELECT IF( + NOT EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Reactions' + AND table_schema = DATABASE() + AND column_name = 'ChannelId' + ), + 'ALTER TABLE Reactions ADD COLUMN ChannelId varchar(26) NOT NULL DEFAULT "";', + 'SELECT 1;' +)); + +PREPARE addColumnIfNotExists FROM @preparedStatement; +EXECUTE addColumnIfNotExists; +DEALLOCATE PREPARE addColumnIfNotExists; + + +UPDATE Reactions SET ChannelId = COALESCE((select ChannelId from Posts where Posts.Id = Reactions.PostId), '') WHERE ChannelId=""; + + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Reactions' + AND table_schema = DATABASE() + AND index_name = 'idx_reactions_channel_id' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_reactions_channel_id ON Reactions(ChannelId);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000090_create_enums.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Channels' + AND table_schema = DATABASE() + AND column_name = 'Type' + AND column_type != 'ENUM("D", "O", "G", "P")' + ) > 0, + 'ALTER TABLE Channels MODIFY COLUMN Type ENUM("D", "O", "G", "P");', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Teams' + AND table_schema = DATABASE() + AND column_name = 'Type' + AND column_type != 'ENUM("I", "O")' + ) > 0, + 'ALTER TABLE Teams MODIFY COLUMN Type ENUM("I", "O");', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'UploadSessions' + AND table_schema = DATABASE() + AND column_name = 'Type' + AND column_type != 'ENUM("attachment", "import")' + ) > 0, + 'ALTER TABLE UploadSessions MODIFY COLUMN Type ENUM("attachment", "import");', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; +/* ==> mysql/000091_create_post_reminder.up.sql <== */ +CREATE TABLE IF NOT EXISTS PostReminders ( + PostId varchar(26) NOT NULL, + UserId varchar(26) NOT NULL, + TargetTime bigint, + PRIMARY KEY (PostId, UserId) +); + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'PostReminders' + AND table_schema = DATABASE() + AND index_name = 'idx_postreminders_targettime' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_postreminders_targettime ON PostReminders(TargetTime);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; +/* ==> mysql/000092_add_createat_to_teammembers.up.sql <== */ +SET @preparedStatement = (SELECT IF( + NOT EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'TeamMembers' + AND table_schema = DATABASE() + AND column_name = 'CreateAt' + ), + 'ALTER TABLE TeamMembers ADD COLUMN CreateAt bigint DEFAULT 0;', + 'SELECT 1;' +)); + +PREPARE addColumnIfNotExists FROM @preparedStatement; +EXECUTE addColumnIfNotExists; +DEALLOCATE PREPARE addColumnIfNotExists; + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'TeamMembers' + AND table_schema = DATABASE() + AND index_name = 'idx_teammembers_create_at' + ) > 0, + 'SELECT 1', + 'CREATE INDEX idx_teammembers_createat ON TeamMembers(CreateAt);' +)); + +PREPARE createIndexIfNotExists FROM @preparedStatement; +EXECUTE createIndexIfNotExists; +DEALLOCATE PREPARE createIndexIfNotExists; + +/* ==> mysql/000093_notify_admin.up.sql <== */ +CREATE TABLE IF NOT EXISTS NotifyAdmin ( + UserId varchar(26) NOT NULL, + CreateAt bigint(20) DEFAULT NULL, + RequiredPlan varchar(26) NOT NULL, + RequiredFeature varchar(100) NOT NULL, + Trial BOOLEAN NOT NULL, + PRIMARY KEY (UserId, RequiredFeature, RequiredPlan) +); + +/* ==> mysql/000094_threads_teamid.up.sql <== */ +-- Replaced by 000096_threads_threadteamid.up.sql + +/* ==> mysql/000095_remove_posts_parentid.up.sql <== */ +-- While upgrading from 5.x to 6.x with manual queries, there is a chance that this +-- migration is skipped. In that case, we need to make sure that the column is dropped. + +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Posts' + AND table_schema = DATABASE() + AND column_name = 'ParentId' + ) > 0, + 'ALTER TABLE Posts DROP COLUMN ParentId;', + 'SELECT 1' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000096_threads_threadteamid.up.sql <== */ +-- Drop any existing TeamId column from 000094_threads_teamid.up.sql +SET @preparedStatement = (SELECT IF( + EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'TeamId' + ) > 0, + 'ALTER TABLE Threads DROP COLUMN TeamId;', + 'SELECT 1;' +)); + +PREPARE removeColumnIfExists FROM @preparedStatement; +EXECUTE removeColumnIfExists; +DEALLOCATE PREPARE removeColumnIfExists; + +SET @preparedStatement = (SELECT IF( + NOT EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Threads' + AND table_schema = DATABASE() + AND column_name = 'ThreadTeamId' + ), + 'ALTER TABLE Threads ADD COLUMN ThreadTeamId varchar(26) DEFAULT NULL;', + 'SELECT 1;' +)); + +PREPARE addColumnIfNotExists FROM @preparedStatement; +EXECUTE addColumnIfNotExists; +DEALLOCATE PREPARE addColumnIfNotExists; + +UPDATE Threads, Channels +SET Threads.ThreadTeamId = Channels.TeamId +WHERE Channels.Id = Threads.ChannelId +AND Threads.ThreadTeamId IS NULL; + +/* ==> mysql/000097_create_posts_priority.up.sql <== */ +CREATE TABLE IF NOT EXISTS PostsPriority ( + PostId varchar(26) NOT NULL, + ChannelId varchar(26) NOT NULL, + Priority varchar(32) NOT NULL, + RequestedAck tinyint(1), + PersistentNotifications tinyint(1), + PRIMARY KEY (PostId) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +SET @preparedStatement = (SELECT IF( + NOT EXISTS( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'ChannelMembers' + AND table_schema = DATABASE() + AND column_name = 'UrgentMentionCount' + ), + 'ALTER TABLE ChannelMembers ADD COLUMN UrgentMentionCount bigint(20);', + 'SELECT 1;' +)); + +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; + +/* ==> mysql/000098_create_post_acknowledgements.up.sql <== */ +CREATE TABLE IF NOT EXISTS PostAcknowledgements ( + PostId varchar(26) NOT NULL, + UserId varchar(26) NOT NULL, + AcknowledgedAt bigint(20) DEFAULT NULL, + PRIMARY KEY (PostId, UserId) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +/* ==> mysql/000099_create_drafts.up.sql <== */ +CREATE TABLE IF NOT EXISTS Drafts ( + CreateAt bigint(20) DEFAULT NULL, + UpdateAt bigint(20) DEFAULT NULL, + DeleteAt bigint(20) DEFAULT NULL, + UserId varchar(26) NOT NULL, + ChannelId varchar(26) NOT NULL, + RootId varchar(26) DEFAULT '', + Message text, + Props text, + FileIds text, + PRIMARY KEY (UserId, ChannelId, RootId) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +/* ==> mysql/000100_add_draft_priority_column.up.sql <== */ +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Drafts' + AND table_schema = DATABASE() + AND column_name = 'Priority' + ) > 0, + 'SELECT 1', + 'ALTER TABLE Drafts ADD COLUMN Priority text;' +)); + +PREPARE alterIfExists FROM @preparedStatement; +EXECUTE alterIfExists; +DEALLOCATE PREPARE alterIfExists; + +/* ==> mysql/000101_create_true_up_review_history.up.sql <== */ +CREATE TABLE IF NOT EXISTS TrueUpReviewHistory ( + DueDate bigint(20), + Completed boolean, + PRIMARY KEY (DueDate) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/server/scripts/esrupgrades/esr.common.mysql.preprocess.sql b/server/scripts/esrupgrades/esr.common.mysql.preprocess.sql new file mode 100644 index 0000000000..4c06e1ba19 --- /dev/null +++ b/server/scripts/esrupgrades/esr.common.mysql.preprocess.sql @@ -0,0 +1,23 @@ +/* The sessions in the DB dump may have expired before the CI tests run, making + the server remove the rows and generating a spurious diff that we want to avoid. + In order to do so, we mark all sessions' ExpiresAt value to 0, so they never expire. */ +UPDATE Sessions SET ExpiresAt = 0; + +/* The dump may not contain a system-bot user, in which case the server will create + one if it's not shutdown before a job requests it. This situation creates a flaky + tests in which, in rare ocassions, the system-bot is indeed created, generating a + spurious diff. We avoid this by making sure that there is a system-bot user and + corresponding bot */ +DELIMITER // +CREATE PROCEDURE AddSystemBotIfNeeded () +BEGIN + DECLARE CreateSystemBot BOOLEAN; + SELECT COUNT(*) = 0 FROM Users WHERE Username = 'system-bot' INTO CreateSystemBot; + IF CreateSystemBot THEN + /* These values are retrieved from a real system-bot created by a server */ + INSERT INTO `Bots` VALUES ('nc7y5x1i8jgr9btabqo5m3579c','','phxrtijfrtfg7k4bwj9nophqyc',0,1681308600015,1681308600015,0); + INSERT INTO `Users` VALUES ('nc7y5x1i8jgr9btabqo5m3579c',1681308600014,1681308600014,0,'system-bot','',NULL,'','system-bot@localhost',0,'','System','','','system_user',0,'{}','{\"push\": \"mention\", \"email\": \"true\", \"channel\": \"true\", \"desktop\": \"mention\", \"comments\": \"never\", \"first_name\": \"false\", \"push_status\": \"away\", \"mention_keys\": \"\", \"push_threads\": \"all\", \"desktop_sound\": \"true\", \"email_threads\": \"all\", \"desktop_threads\": \"all\"}',1681308600014,0,0,'en','{\"manualTimezone\": \"\", \"automaticTimezone\": \"\", \"useAutomaticTimezone\": \"true\"}',0,'',NULL); + END IF; +END// +DELIMITER ; +CALL AddSystemBotIfNeeded();