aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db/src
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-17 10:38:17 +0200
committerSimon Brandhof <simon.brandhof@sonarsource.com>2015-07-17 12:38:17 +0200
commitbdcfb7eeb7caaeb9019ff0d3946153c33bc6c5e9 (patch)
tree8e0bc944f83d81a003061543f9db9139966ba985 /sonar-db/src
parent48697451f2b0ba517c7d1861af3897b4ace896f9 (diff)
downloadsonarqube-bdcfb7eeb7caaeb9019ff0d3946153c33bc6c5e9.tar.gz
sonarqube-bdcfb7eeb7caaeb9019ff0d3946153c33bc6c5e9.zip
Move Java db migrations from sonar-server to sonar-db
Diffstat (limited to 'sonar-db/src')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java146
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java65
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java105
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DataChange.java53
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java70
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DdlChange.java67
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java72
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java97
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java32
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java108
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/Select.java167
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java98
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java42
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/Upsert.java33
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java70
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java143
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java71
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java283
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java71
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java60
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java212
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java63
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java194
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java88
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java122
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java92
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java330
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java172
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java58
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java68
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java75
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java163
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java66
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java59
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java84
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java66
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java66
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java66
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java78
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java70
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java89
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java113
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java58
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java58
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java37
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java56
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java43
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java58
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java136
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java56
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java163
-rw-r--r--sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java56
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java136
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/BaseDataChangeTest.java481
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/DropColumnsBuilderTest.java64
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java35
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest.java76
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest.java60
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/FeedFileSourcesTest.java319
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/FeedIssueLongDatesTest.java51
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/FileSourceDtoTest.java35
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest.java57
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest.java324
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest.java56
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest.java66
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/AddIssuesColumnsTest.java54
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/AddNewCharacteristicsTest.java138
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest.java102
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/DropIssuesColumnsTest.java52
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest.java53
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedEventsLongDatesTest.java88
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest.java97
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest.java53
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueComponentUuidsTest.java53
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueTagsTest.java64
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssuesLongDatesTest.java81
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest.java88
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest.java84
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/FeedUsersLongDatesTest.java51
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest.java59
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest.java56
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest.java65
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest.java49
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/FeedEventsComponentUuidTest.java64
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest.java55
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest.java55
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/FeedMetricsBooleansTest.java54
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest.java63
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/MoveProjectProfileAssociationTest.java66
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/RemoveComponentLibrariesTest.java56
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest.java78
-rw-r--r--sonar-db/src/test/java/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest.java56
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/batch-insert-result.xml8
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/insert-result.xml7
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/mass-update-result.xml5
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/persons.xml5
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/schema.sql8
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/scroll-and-update-result.xml5
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/update-null-result.xml5
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/after.xml20
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/before.xml17
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/no_changes.xml11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/schema.sql28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml34
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql77
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute-result.xml35
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute.xml33
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter-result.xml35
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter.xml31
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/no_changes.xml29
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/schema.sql40
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute-result.xml12
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute.xml12
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/schema.sql10
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-invalid-duplication.xml15
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-scm.xml15
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after.xml15
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/before.xml134
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/schema.sql119
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/before.xml75
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/schema.sql28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/after.xml28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/before.xml28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/schema.sql18
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components.xml93
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components_without_uuid.xml58
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_developer.xml40
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_disable_components.xml93
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_library.xml12
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_provisioned_project.xml11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_view.xml57
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_module_has_no_root_id.xml38
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_project_has_two_active_snapshots.xml48
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_migrate_already_migrated_components.xml113
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/schema.sql54
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute-result.xml33
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute.xml33
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/schema.sql10
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated-result.xml43
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated.xml50
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute-result.xml63
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute.xml70
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/schema.sql32
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddIssuesColumnsTest/schema.sql26
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/do_nothing_when_already_migrated.xml73
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/empty.xml3
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_as_characteristic.xml11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml16
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_usability_exists_as_sub_characteristic.xml11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists-result.xml38
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists.xml11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate-result.xml94
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate.xml35
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml19
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/schema.sql11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists-result.xml38
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists.xml16
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/before.xml49
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/no_authors.xml24
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/schema.sql22
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/before.xml52
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/schema.sql16
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/before.xml28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/schema.sql7
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/bad_data.xml8
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/data.xml25
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/schema.sql12
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/before.xml31
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/schema.sql14
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/after-result.xml22
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/before.xml30
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/schema.sql52
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/after-result.xml135
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/before.xml185
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/schema.sql59
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/before.xml34
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/schema.sql9
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/before.xml28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/schema.sql7
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/before.xml61
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/schema.sql18
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/before.xml26
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/schema.sql15
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate-result.xml24
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate.xml29
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/nothing_to_do_when_already_migrated.xml22
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/schema.sql49
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute-result.xml68
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute.xml69
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/schema.sql10
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml100
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml100
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml100
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/schema.sql22
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest/schema.sql11
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate-result.xml6
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate.xml13
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/not_migrate_already_migrated_data.xml13
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/schema.sql35
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate-result.xml10
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate.xml10
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/schema.sql8
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate-result.xml6
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate.xml9
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/schema.sql10
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate-result.xml9
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate.xml7
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/schema.sql7
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate-result.xml28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate.xml26
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/not_migrate_already_migrated_data.xml28
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/schema.sql31
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate-result.xml29
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate.xml41
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/schema.sql48
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries-result.xml17
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries.xml19
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/schema.sql22
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component-result.xml20
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component.xml31
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component-result.xml20
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component.xml31
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate-result.xml26
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate.xml35
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/schema.sql17
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries-result.xml24
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries.xml24
-rw-r--r--sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/schema.sql32
228 files changed, 13122 insertions, 0 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java
new file mode 100644
index 00000000000..ab6543fffe7
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/AddColumnsBuilder.java
@@ -0,0 +1,146 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Preconditions;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.db.dialect.Dialect;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class AddColumnsBuilder {
+
+ private final Dialect dialect;
+ private final String tableName;
+ private List<ColumnDef> columnDefs = newArrayList();
+
+ public AddColumnsBuilder(Dialect dialect, String tableName) {
+ this.tableName = tableName;
+ this.dialect = dialect;
+ }
+
+ public AddColumnsBuilder addColumn(ColumnDef columnDef) {
+ columnDefs.add(columnDef);
+ return this;
+ }
+
+ public String build() {
+ StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" ");
+ switch (dialect.getId()) {
+ case PostgreSql.ID:
+ addColumns(sql, "ADD COLUMN ");
+ break;
+ case MsSql.ID:
+ sql.append("ADD ");
+ addColumns(sql, "");
+ break;
+ default:
+ sql.append("ADD (");
+ addColumns(sql, "");
+ sql.append(")");
+ }
+ return sql.toString();
+ }
+
+ private void addColumns(StringBuilder sql, String columnPrefix) {
+ for (int i = 0; i < columnDefs.size(); i++) {
+ sql.append(columnPrefix);
+ addColumn(sql, columnDefs.get(i));
+ if (i < columnDefs.size() - 1) {
+ sql.append(", ");
+ }
+ }
+ }
+
+ private void addColumn(StringBuilder sql, ColumnDef columnDef) {
+ sql.append(columnDef.getName()).append(" ").append(typeToSql(columnDef));
+ Integer limit = columnDef.getLimit();
+ if (limit != null) {
+ sql.append(" (").append(Integer.toString(limit)).append(")");
+ }
+ sql.append(columnDef.isNullable() ? " NULL" : " NOT NULL");
+ }
+
+ private String typeToSql(ColumnDef columnDef) {
+ switch (columnDef.getType()) {
+ case STRING:
+ return "VARCHAR";
+ case BIG_INTEGER:
+ return !dialect.getId().equals(Oracle.ID) ? "BIGINT" : "NUMBER (38)";
+ default:
+ throw new IllegalArgumentException("Unsupported type : " + columnDef.getType());
+ }
+ }
+
+ public static class ColumnDef {
+ private String name;
+ private Type type;
+ private boolean isNullable;
+ private Integer limit;
+
+ public enum Type {
+ STRING, BIG_INTEGER
+ }
+
+ public ColumnDef setNullable(boolean isNullable) {
+ this.isNullable = isNullable;
+ return this;
+ }
+
+ public ColumnDef setLimit(@Nullable Integer limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ public ColumnDef setName(String name) {
+ Preconditions.checkArgument(CharMatcher.JAVA_LOWER_CASE.or(CharMatcher.anyOf("_")).matchesAllOf(name), "Column name should only contains lowercase and _ characters");
+ this.name = name;
+ return this;
+ }
+
+ public ColumnDef setType(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public boolean isNullable() {
+ return isNullable;
+ }
+
+ @CheckForNull
+ public Integer getLimit() {
+ return limit;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Type getType() {
+ return type;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java b/sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java
new file mode 100644
index 00000000000..800e66a7e34
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/BaseDataChange.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.apache.commons.dbutils.DbUtils;
+import org.sonar.db.Database;
+
+public abstract class BaseDataChange implements DataChange, MigrationStep {
+
+ private final Database db;
+
+ public BaseDataChange(Database db) {
+ this.db = db;
+ }
+
+ @Override
+ public final void execute() throws SQLException {
+ Connection readConnection = null;
+ Connection writeConnection = null;
+ try {
+ readConnection = openConnection();
+
+ writeConnection = db.getDataSource().getConnection();
+ writeConnection.setAutoCommit(false);
+ Context context = new Context(db, readConnection, writeConnection);
+ execute(context);
+
+ } finally {
+ DbUtils.closeQuietly(readConnection);
+ DbUtils.closeQuietly(writeConnection);
+ }
+ }
+
+ /**
+ * Do not forget to close it !
+ */
+ protected Connection openConnection() throws SQLException {
+ Connection connection = db.getDataSource().getConnection();
+ connection.setAutoCommit(false);
+ if (connection.getMetaData().supportsTransactionIsolationLevel(Connection.TRANSACTION_READ_UNCOMMITTED)) {
+ connection.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
+ }
+ return connection;
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java b/sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java
new file mode 100644
index 00000000000..f302311bb2d
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/BaseSqlStatement.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Date;
+import javax.annotation.Nullable;
+import org.apache.commons.dbutils.DbUtils;
+
+class BaseSqlStatement<CHILD extends SqlStatement> implements SqlStatement<CHILD> {
+ protected PreparedStatement pstmt;
+
+ protected BaseSqlStatement(PreparedStatement pstmt) {
+ this.pstmt = pstmt;
+ }
+
+ @Override
+ public CHILD close() {
+ DbUtils.closeQuietly(pstmt);
+ pstmt = null;
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setString(int columnIndex, @Nullable String value) throws SQLException {
+ pstmt.setString(columnIndex, value);
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setBytes(int columnIndex, @Nullable byte[] value) throws SQLException {
+ pstmt.setBytes(columnIndex, value);
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setInt(int columnIndex, @Nullable Integer value) throws SQLException {
+ if (value == null) {
+ pstmt.setNull(columnIndex, Types.INTEGER);
+ } else {
+ pstmt.setInt(columnIndex, value);
+ }
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setLong(int columnIndex, @Nullable Long value) throws SQLException {
+ if (value == null) {
+ pstmt.setNull(columnIndex, Types.BIGINT);
+ } else {
+ pstmt.setLong(columnIndex, value);
+ }
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException {
+ if (value == null) {
+ pstmt.setNull(columnIndex, Types.BOOLEAN);
+ } else {
+ pstmt.setBoolean(columnIndex, value);
+ }
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException {
+ if (value == null) {
+ pstmt.setNull(columnIndex, Types.DECIMAL);
+ } else {
+ pstmt.setDouble(columnIndex, value);
+ }
+ return (CHILD) this;
+ }
+
+ @Override
+ public CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException {
+ if (value == null) {
+ pstmt.setNull(columnIndex, Types.TIMESTAMP);
+ } else {
+ pstmt.setTimestamp(columnIndex, new Timestamp(value.getTime()));
+ }
+ return (CHILD) this;
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DataChange.java b/sonar-db/src/main/java/org/sonar/db/version/DataChange.java
new file mode 100644
index 00000000000..d06e4d7b093
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/DataChange.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+
+public interface DataChange {
+
+ class Context {
+ private final Database db;
+ private final Connection readConnection;
+ private final Connection writeConnection;
+
+ public Context(Database db, Connection readConnection, Connection writeConnection) {
+ this.db = db;
+ this.readConnection = readConnection;
+ this.writeConnection = writeConnection;
+ }
+
+ public Select prepareSelect(String sql) throws SQLException {
+ return SelectImpl.create(db, readConnection, sql);
+ }
+
+ public Upsert prepareUpsert(String sql) throws SQLException {
+ return UpsertImpl.create(writeConnection, sql);
+ }
+
+ public MassUpdate prepareMassUpdate() throws SQLException {
+ return new MassUpdate(db, readConnection, writeConnection);
+ }
+ }
+
+ void execute(Context context) throws SQLException;
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java b/sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java
new file mode 100644
index 00000000000..1d00155e13b
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/DatabaseMigration.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.util.Date;
+import javax.annotation.CheckForNull;
+
+public interface DatabaseMigration {
+ enum Status {
+ NONE, RUNNING, FAILED, SUCCEEDED
+ }
+
+ /**
+ * Starts the migration status and returns immediately.
+ * <p>
+ * Migration can not be started twice but calling this method wont raise an error.
+ * On the other hand, calling this method when no migration is needed will start the process anyway.
+ * </p>
+ * <p>
+ * <strong>This method should be named {@code start} but it can not be because it will be called by the pico container
+ * and this will cause unwanted behavior</strong>
+ * </p>
+ */
+ void startIt();
+
+ /**
+ * The time and day the last migration was started.
+ * <p>
+ * If no migration was ever started, the returned date is {@code null}. This value is reset when {@link #startIt()} is
+ * called.
+ * </p>
+ *
+ * @return a {@link Date} or {@code null}
+ */
+ @CheckForNull
+ Date startedAt();
+
+ /**
+ * Current status of the migration.
+ */
+ Status status();
+
+ /**
+ * The error of the last migration if it failed.
+ * <p>
+ * This value is reset when {@link #startIt()} is called.
+ * </p>
+ * @return a {@link Throwable} or {@code null}
+ */
+ @CheckForNull
+ Throwable failureError();
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DdlChange.java b/sonar-db/src/main/java/org/sonar/db/version/DdlChange.java
new file mode 100644
index 00000000000..1e2bcb47582
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/DdlChange.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.apache.commons.dbutils.DbUtils;
+import org.sonar.db.Database;
+
+public abstract class DdlChange implements MigrationStep {
+
+ private final Database db;
+
+ public DdlChange(Database db) {
+ this.db = db;
+ }
+
+ @Override
+ public final void execute() throws SQLException {
+ Connection writeConnection = null;
+ try {
+ writeConnection = db.getDataSource().getConnection();
+ writeConnection.setAutoCommit(false);
+ Context context = new Context(writeConnection);
+ execute(context);
+
+ } finally {
+ DbUtils.closeQuietly(writeConnection);
+ }
+ }
+
+ public static class Context {
+ private final Connection writeConnection;
+
+ public Context(Connection writeConnection) {
+ this.writeConnection = writeConnection;
+ }
+
+ public void execute(String sql) throws SQLException {
+ try {
+ UpsertImpl.create(writeConnection, sql).execute().commit();
+ } catch (Exception e) {
+ throw new IllegalStateException(String.format("Fail to execute %s", sql), e);
+ }
+ }
+ }
+
+ public abstract void execute(Context context) throws SQLException;
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java b/sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java
new file mode 100644
index 00000000000..61a0fbc45c8
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/DropColumnsBuilder.java
@@ -0,0 +1,72 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import org.sonar.db.dialect.Dialect;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.MySql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+
+public class DropColumnsBuilder {
+
+ private final Dialect dialect;
+ private final String tableName;
+ private final String[] columns;
+
+ public DropColumnsBuilder(Dialect dialect, String tableName, String... columns) {
+ this.tableName = tableName;
+ this.dialect = dialect;
+ this.columns = columns;
+ }
+
+ public String build() {
+ StringBuilder sql = new StringBuilder().append("ALTER TABLE ").append(tableName).append(" ");
+ switch (dialect.getId()) {
+ case PostgreSql.ID:
+ case MySql.ID:
+ dropColumns(sql, "DROP COLUMN ");
+ break;
+ case MsSql.ID:
+ sql.append("DROP COLUMN ");
+ dropColumns(sql, "");
+ break;
+ case Oracle.ID:
+ sql.append("DROP (");
+ dropColumns(sql, "");
+ sql.append(")");
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unsupported database '%s'", dialect.getId()));
+ }
+ return sql.toString();
+ }
+
+ private void dropColumns(StringBuilder sql, String columnPrefix) {
+ for (int i = 0; i < columns.length; i++) {
+ sql.append(columnPrefix);
+ sql.append(columns[i]);
+ if (i < columns.length - 1) {
+ sql.append(", ");
+ }
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java b/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java
new file mode 100644
index 00000000000..a09ed828a5c
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/MassUpdate.java
@@ -0,0 +1,97 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.concurrent.atomic.AtomicLong;
+import org.sonar.core.util.ProgressLogger;
+import org.sonar.db.Database;
+
+public class MassUpdate {
+
+ public interface Handler {
+ /**
+ * Convert some column values of a given row.
+ *
+ * @return true if the row must be updated, else false. If false, then the update parameter must not be touched.
+ */
+ boolean handle(Select.Row row, SqlStatement update) throws SQLException;
+ }
+
+ private final Database db;
+ private final Connection readConnection;
+ private final Connection writeConnection;
+ private final AtomicLong counter = new AtomicLong(0L);
+ private final ProgressLogger progress = ProgressLogger.create(getClass(), counter);
+
+ private Select select;
+ private Upsert update;
+
+ MassUpdate(Database db, Connection readConnection, Connection writeConnection) {
+ this.db = db;
+ this.readConnection = readConnection;
+ this.writeConnection = writeConnection;
+ }
+
+ public SqlStatement select(String sql) throws SQLException {
+ this.select = SelectImpl.create(db, readConnection, sql);
+ return this.select;
+ }
+
+ public MassUpdate update(String sql) throws SQLException {
+ this.update = UpsertImpl.create(writeConnection, sql);
+ return this;
+ }
+
+ public MassUpdate rowPluralName(String s) {
+ this.progress.setPluralLabel(s);
+ return this;
+ }
+
+ public void execute(final Handler handler) throws SQLException {
+ if (select == null || update == null) {
+ throw new IllegalStateException("SELECT or UPDATE requests are not defined");
+ }
+
+ progress.start();
+ try {
+ select.scroll(new Select.RowHandler() {
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ if (handler.handle(row, update)) {
+ update.addBatch();
+ }
+ counter.getAndIncrement();
+ }
+ });
+ if (((UpsertImpl) update).getBatchCount() > 0L) {
+ update.execute().commit();
+ }
+ update.close();
+
+ // log the total number of processed rows
+ progress.log();
+ } finally {
+ progress.stop();
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java
new file mode 100644
index 00000000000..f34fdcffcd8
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStep.java
@@ -0,0 +1,32 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.SQLException;
+
+/**
+ * Java alternative of ActiveRecord::Migration. Do not forget to declare implementation classes in {@link MigrationStepModule}
+ * @since 3.7
+ */
+public interface MigrationStep {
+
+ void execute() throws SQLException;
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
new file mode 100644
index 00000000000..4e1ea5cd811
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/MigrationStepModule.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import org.sonar.core.platform.Module;
+import org.sonar.db.version.v451.AddMissingCustomRuleParametersMigrationStep;
+import org.sonar.db.version.v451.DeleteUnescapedActivities;
+import org.sonar.db.version.v50.FeedFileSources;
+import org.sonar.db.version.v50.FeedIssueLongDates;
+import org.sonar.db.version.v50.FeedSnapshotSourcesUpdatedAt;
+import org.sonar.db.version.v50.InsertProjectsAuthorizationUpdatedAtMigrationStep;
+import org.sonar.db.version.v50.PopulateProjectsUuidColumnsMigrationStep;
+import org.sonar.db.version.v50.RemoveSortFieldFromIssueFiltersMigrationStep;
+import org.sonar.db.version.v50.ReplaceIssueFiltersProjectKeyByUuid;
+import org.sonar.db.version.v51.AddIssuesColumns;
+import org.sonar.db.version.v51.AddNewCharacteristics;
+import org.sonar.db.version.v51.CopyScmAccountsFromAuthorsToUsers;
+import org.sonar.db.version.v51.DropIssuesColumns;
+import org.sonar.db.version.v51.FeedAnalysisReportsLongDates;
+import org.sonar.db.version.v51.FeedEventsLongDates;
+import org.sonar.db.version.v51.FeedFileSourcesBinaryData;
+import org.sonar.db.version.v51.FeedIssueChangesLongDates;
+import org.sonar.db.version.v51.FeedIssueComponentUuids;
+import org.sonar.db.version.v51.FeedIssueTags;
+import org.sonar.db.version.v51.FeedIssuesLongDates;
+import org.sonar.db.version.v51.FeedManualMeasuresLongDates;
+import org.sonar.db.version.v51.FeedSnapshotsLongDates;
+import org.sonar.db.version.v51.FeedUsersLongDates;
+import org.sonar.db.version.v51.RemovePermissionsOnModulesMigrationStep;
+import org.sonar.db.version.v51.RenameComponentRelatedParamsInIssueFilters;
+import org.sonar.db.version.v51.UpdateProjectsModuleUuidPath;
+import org.sonar.db.version.v52.AddManualMeasuresComponentUuidColumn;
+import org.sonar.db.version.v52.FeedEventsComponentUuid;
+import org.sonar.db.version.v52.FeedFileSourcesDataType;
+import org.sonar.db.version.v52.FeedManualMeasuresComponentUuid;
+import org.sonar.db.version.v52.FeedMetricsBooleans;
+import org.sonar.db.version.v52.FeedProjectLinksComponentUuid;
+import org.sonar.db.version.v52.MoveProjectProfileAssociation;
+import org.sonar.db.version.v52.RemoveComponentLibraries;
+import org.sonar.db.version.v52.RemoveDuplicatedComponentKeys;
+import org.sonar.db.version.v52.RemoveSnapshotLibraries;
+
+public class MigrationStepModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ // 4.5.1
+ AddMissingCustomRuleParametersMigrationStep.class,
+ DeleteUnescapedActivities.class,
+
+ // 5.0
+ InsertProjectsAuthorizationUpdatedAtMigrationStep.class,
+ PopulateProjectsUuidColumnsMigrationStep.class,
+ ReplaceIssueFiltersProjectKeyByUuid.class,
+ FeedSnapshotSourcesUpdatedAt.class,
+ FeedFileSources.class,
+ FeedIssueLongDates.class,
+ RemoveSortFieldFromIssueFiltersMigrationStep.class,
+
+ // 5.1
+ FeedIssueTags.class,
+ FeedUsersLongDates.class,
+ RenameComponentRelatedParamsInIssueFilters.class,
+ CopyScmAccountsFromAuthorsToUsers.class,
+ FeedIssueChangesLongDates.class,
+ FeedAnalysisReportsLongDates.class,
+ UpdateProjectsModuleUuidPath.class,
+ FeedIssueComponentUuids.class,
+ FeedSnapshotsLongDates.class,
+ FeedIssuesLongDates.class,
+ FeedFileSourcesBinaryData.class,
+ FeedManualMeasuresLongDates.class,
+ FeedEventsLongDates.class,
+ AddNewCharacteristics.class,
+ RemovePermissionsOnModulesMigrationStep.class,
+ AddIssuesColumns.class,
+ DropIssuesColumns.class,
+
+ // 5.2
+ FeedProjectLinksComponentUuid.class,
+ FeedEventsComponentUuid.class,
+ MoveProjectProfileAssociation.class,
+ FeedFileSourcesDataType.class,
+ FeedMetricsBooleans.class,
+ AddManualMeasuresComponentUuidColumn.class,
+ FeedManualMeasuresComponentUuid.class,
+ RemoveSnapshotLibraries.class,
+ RemoveComponentLibraries.class,
+ RemoveDuplicatedComponentKeys.class);
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/Select.java b/sonar-db/src/main/java/org/sonar/db/version/Select.java
new file mode 100644
index 00000000000..ab9433d25e6
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/Select.java
@@ -0,0 +1,167 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.CheckForNull;
+
+public interface Select extends SqlStatement<Select> {
+
+ class Row {
+ private final ResultSet rs;
+
+ Row(ResultSet rs) {
+ this.rs = rs;
+ }
+
+ @CheckForNull
+ public Long getNullableLong(int columnIndex) throws SQLException {
+ long l = rs.getLong(columnIndex);
+ return rs.wasNull() ? null : l;
+ }
+
+ public long getLong(int columnIndex) throws SQLException {
+ return rs.getLong(columnIndex);
+ }
+
+ @CheckForNull
+ public Double getNullableDouble(int columnIndex) throws SQLException {
+ double d = rs.getDouble(columnIndex);
+ return rs.wasNull() ? null : d;
+ }
+
+ public double getDouble(int columnIndex) throws SQLException {
+ return rs.getDouble(columnIndex);
+ }
+
+ @CheckForNull
+ public Integer getNullableInt(int columnIndex) throws SQLException {
+ int i = rs.getInt(columnIndex);
+ return rs.wasNull() ? null : i;
+ }
+
+ public int getInt(int columnIndex) throws SQLException {
+ return rs.getInt(columnIndex);
+ }
+
+ @CheckForNull
+ public Boolean getNullableBoolean(int columnIndex) throws SQLException {
+ boolean b = rs.getBoolean(columnIndex);
+ return rs.wasNull() ? null : b;
+ }
+
+ public boolean getBoolean(int columnIndex) throws SQLException {
+ return rs.getBoolean(columnIndex);
+ }
+
+ @CheckForNull
+ public String getNullableString(int columnIndex) throws SQLException {
+ String s = rs.getString(columnIndex);
+ return rs.wasNull() ? null : s;
+ }
+
+ public String getString(int columnIndex) throws SQLException {
+ return rs.getString(columnIndex);
+ }
+
+ @CheckForNull
+ public Date getNullableDate(int columnIndex) throws SQLException {
+ Timestamp t = rs.getTimestamp(columnIndex);
+ return rs.wasNull() ? null : t;
+ }
+
+ public Date getDate(int columnIndex) throws SQLException {
+ return rs.getTimestamp(columnIndex);
+ }
+
+ @CheckForNull
+ public byte[] getNullableBytes(int columnIndex) throws SQLException {
+ byte[] b = rs.getBytes(columnIndex);
+ return rs.wasNull() ? null : b;
+ }
+
+ public byte[] getBytes(int columnIndex) throws SQLException {
+ return rs.getBytes(columnIndex);
+ }
+
+ @Override
+ public String toString() {
+ try {
+ ResultSetMetaData rsMetaData = rs.getMetaData();
+ StringBuilder sb = new StringBuilder();
+ for (int i = 1; i <= rsMetaData.getColumnCount(); i++) {
+ if (i > 1) {
+ sb.append(",");
+ }
+ sb.append(rsMetaData.getColumnLabel(i).toLowerCase());
+ sb.append("=");
+ sb.append(rs.getObject(i));
+ }
+ return sb.toString();
+ } catch (Exception e) {
+ return "Unavailable: " + e.getMessage();
+ }
+ }
+ }
+
+ interface RowReader<T> {
+ T read(Row row) throws SQLException;
+ }
+
+ class LongReader implements RowReader<Long> {
+ private LongReader() {
+ }
+
+ @Override
+ public Long read(Row row) throws SQLException {
+ return row.getNullableLong(1);
+ }
+ }
+
+ RowReader<Long> LONG_READER = new LongReader();
+
+ class StringReader implements RowReader<String> {
+ private StringReader() {
+ }
+
+ @Override
+ public String read(Row row) throws SQLException {
+ return row.getNullableString(1);
+ }
+ }
+
+ RowReader<String> STRING_READER = new StringReader();
+
+ interface RowHandler {
+ void handle(Row row) throws SQLException;
+ }
+
+ <T> List<T> list(RowReader<T> reader) throws SQLException;
+
+ @CheckForNull
+ <T> T get(RowReader<T> reader) throws SQLException;
+
+ void scroll(RowHandler handler) throws SQLException;
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java b/sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java
new file mode 100644
index 00000000000..90031326979
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/SelectImpl.java
@@ -0,0 +1,98 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.dbutils.DbUtils;
+import org.sonar.db.Database;
+
+class SelectImpl extends BaseSqlStatement<Select> implements Select {
+
+ private SelectImpl(PreparedStatement pstmt) {
+ super(pstmt);
+ }
+
+ @Override
+ public <T> List<T> list(Select.RowReader<T> reader) throws SQLException {
+ ResultSet rs = pstmt.executeQuery();
+ Select.Row row = new Select.Row(rs);
+ try {
+ List<T> rows = new ArrayList<>();
+ while (rs.next()) {
+ rows.add(reader.read(row));
+ }
+ return rows;
+ } catch (Exception e) {
+ throw newExceptionWithRowDetails(row, e);
+ } finally {
+ DbUtils.closeQuietly(rs);
+ close();
+ }
+ }
+
+ @Override
+ public <T> T get(Select.RowReader<T> reader) throws SQLException {
+ ResultSet rs = pstmt.executeQuery();
+ Select.Row row = new Select.Row(rs);
+ try {
+ if (rs.next()) {
+ return reader.read(row);
+ }
+ return null;
+ } catch (Exception e) {
+ throw newExceptionWithRowDetails(row, e);
+ } finally {
+ DbUtils.closeQuietly(rs);
+ close();
+ }
+ }
+
+ @Override
+ public void scroll(Select.RowHandler handler) throws SQLException {
+ ResultSet rs = pstmt.executeQuery();
+ Select.Row row = new Select.Row(rs);
+ try {
+ while (rs.next()) {
+ handler.handle(row);
+ }
+ } catch (Exception e) {
+ throw newExceptionWithRowDetails(row, e);
+ } finally {
+ DbUtils.closeQuietly(rs);
+ close();
+ }
+ }
+
+ private IllegalStateException newExceptionWithRowDetails(Select.Row row, Exception e) {
+ return new IllegalStateException("Error during processing of row: [" + row + "]", e);
+ }
+
+ static SelectImpl create(Database db, Connection connection, String sql) throws SQLException {
+ // TODO use DbClient#newScrollingSelectStatement()
+ PreparedStatement pstmt = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+ pstmt.setFetchSize(db.getDialect().getScrollDefaultFetchSize());
+ return new SelectImpl(pstmt);
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java b/sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java
new file mode 100644
index 00000000000..4418f659b16
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/SqlStatement.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.SQLException;
+import java.util.Date;
+import javax.annotation.Nullable;
+
+public interface SqlStatement<CHILD extends SqlStatement> {
+ CHILD setBoolean(int columnIndex, @Nullable Boolean value) throws SQLException;
+
+ CHILD setDate(int columnIndex, @Nullable Date value) throws SQLException;
+
+ CHILD setDouble(int columnIndex, @Nullable Double value) throws SQLException;
+
+ CHILD setInt(int columnIndex, @Nullable Integer value) throws SQLException;
+
+ CHILD setLong(int columnIndex, @Nullable Long value) throws SQLException;
+
+ CHILD setString(int columnIndex, @Nullable String value) throws SQLException;
+
+ CHILD setBytes(int columnIndex, @Nullable byte[] data) throws SQLException;
+
+ CHILD close();
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/Upsert.java b/sonar-db/src/main/java/org/sonar/db/version/Upsert.java
new file mode 100644
index 00000000000..31068959a1b
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/Upsert.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.SQLException;
+
+/**
+ * INSERT, UPDATE or DELETE
+ */
+public interface Upsert extends SqlStatement<Upsert> {
+ Upsert addBatch() throws SQLException;
+
+ Upsert execute() throws SQLException;
+
+ Upsert commit() throws SQLException;
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java b/sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java
new file mode 100644
index 00000000000..7f58429fac3
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/UpsertImpl.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import org.sonar.db.BatchSession;
+
+public class UpsertImpl extends BaseSqlStatement<Upsert> implements Upsert {
+
+ private long batchCount = 0L;
+
+ private UpsertImpl(PreparedStatement pstmt) {
+ super(pstmt);
+ }
+
+ @Override
+ public Upsert addBatch() throws SQLException {
+ pstmt.addBatch();
+ pstmt.clearParameters();
+ batchCount++;
+ if (batchCount % BatchSession.MAX_BATCH_SIZE == 0L) {
+ pstmt.executeBatch();
+ pstmt.getConnection().commit();
+ }
+ return this;
+ }
+
+ @Override
+ public Upsert execute() throws SQLException {
+ if (batchCount == 0L) {
+ pstmt.execute();
+ } else {
+ pstmt.executeBatch();
+ }
+ return this;
+ }
+
+ public long getBatchCount() {
+ return batchCount;
+ }
+
+ @Override
+ public Upsert commit() throws SQLException {
+ pstmt.getConnection().commit();
+ return this;
+ }
+
+ static UpsertImpl create(Connection connection, String sql) throws SQLException {
+ return new UpsertImpl(connection.prepareStatement(sql));
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java
new file mode 100644
index 00000000000..ec8aa9cdbe6
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationStep.java
@@ -0,0 +1,143 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v451;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.ProgressLogger;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.version.MigrationStep;
+import org.sonar.db.version.v45.Migration45Mapper;
+import org.sonar.db.version.v45.Rule;
+import org.sonar.db.version.v45.RuleParameter;
+
+/**
+ * See http://jira.sonarsource.com/browse/SONAR-5575
+ *
+ * Add missing parameters (with no value) on each custom rules
+ *
+ * @since 4.5.1
+ */
+public class AddMissingCustomRuleParametersMigrationStep implements MigrationStep {
+
+ private final DbClient db;
+ private final System2 system;
+ private final AtomicLong counter = new AtomicLong(0L);
+
+ public AddMissingCustomRuleParametersMigrationStep(DbClient db, System2 system) {
+ this.db = db;
+ this.system = system;
+ }
+
+ @Override
+ public void execute() {
+ ProgressLogger progress = ProgressLogger.create(getClass(), counter);
+ progress.start();
+
+ DbSession session = db.openSession(false);
+ try {
+ Migration45Mapper mapper = session.getMapper(Migration45Mapper.class);
+
+ List<RuleParameter> templateRuleParams = mapper.selectAllTemplateRuleParameters();
+ Multimap<Integer, RuleParameter> templateRuleParamsByRuleId = ArrayListMultimap.create();
+ for (RuleParameter templateRuleParam : templateRuleParams) {
+ templateRuleParamsByRuleId.put(templateRuleParam.getRuleId(), templateRuleParam);
+ }
+
+ List<Rule> customRules = mapper.selectAllCustomRules();
+ Multimap<Integer, Integer> customRuleIdsByTemplateRuleId = HashMultimap.create();
+ for (Rule customRule : customRules) {
+ customRuleIdsByTemplateRuleId.put(customRule.getTemplateId(), customRule.getId());
+ }
+
+ List<RuleParameter> customRuleParams = mapper.selectAllCustomRuleParameters();
+ Multimap<Integer, RuleParameter> customRuleParamsByRuleId = ArrayListMultimap.create();
+ for (RuleParameter customRuleParam : customRuleParams) {
+ customRuleParamsByRuleId.put(customRuleParam.getRuleId(), customRuleParam);
+ }
+
+ // For each parameters of template rules, verify that each custom rules has the parameter
+ for (Integer templateRuleId : templateRuleParamsByRuleId.keySet()) {
+ for (RuleParameter templateRuleParam : templateRuleParamsByRuleId.get(templateRuleId)) {
+ // Each custom rule should have this parameter
+ insertCustomRuleParameterIfNotAlreadyExisting(templateRuleParam, templateRuleId, customRuleIdsByTemplateRuleId, customRuleParamsByRuleId, session);
+ }
+ }
+
+ session.commit();
+
+ // log the total number of process rows
+ progress.log();
+ } finally {
+ session.close();
+ progress.stop();
+ }
+ }
+
+ private void insertCustomRuleParameterIfNotAlreadyExisting(RuleParameter templateRuleParam, Integer templateRuleId,
+ Multimap<Integer, Integer> customRuleIdsByTemplateRuleId,
+ Multimap<Integer, RuleParameter> customRuleParamsByRuleId,
+ DbSession session) {
+ for (Integer customRuleId : customRuleIdsByTemplateRuleId.get(templateRuleId)) {
+ if (!hasParameter(templateRuleParam.getName(), customRuleParamsByRuleId.get(customRuleId))) {
+ // Insert new custom rule parameter
+ session.getMapper(Migration45Mapper.class).insertRuleParameter(new RuleParameter()
+ .setRuleId(customRuleId)
+ .setRuleTemplateId(templateRuleId)
+ .setName(templateRuleParam.getName())
+ .setDescription(templateRuleParam.getDescription())
+ .setType(templateRuleParam.getType())
+ );
+
+ // Update updated at date of custom rule in order to allow E/S indexation
+ session.getMapper(Migration45Mapper.class).updateRuleUpdateAt(customRuleId, new Date(system.now()));
+
+ counter.getAndIncrement();
+ }
+ }
+ }
+
+ private boolean hasParameter(String parameter, Collection<RuleParameter> customRuleParams) {
+ return Iterables.any(customRuleParams, new MatchParameter(parameter));
+ }
+
+ private static class MatchParameter implements Predicate<RuleParameter> {
+ private final String parameter;
+
+ public MatchParameter(String parameter) {
+ this.parameter = parameter;
+ }
+
+ @Override
+ public boolean apply(@Nullable RuleParameter input) {
+ return input != null && input.getName().equals(parameter);
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java b/sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java
new file mode 100644
index 00000000000..d22ba822f47
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v451/DeleteUnescapedActivities.java
@@ -0,0 +1,71 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v451;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * See http://jira.sonarsource.com/browse/SONAR-5758
+ *
+ * @since 4.5.1
+ */
+public class DeleteUnescapedActivities extends BaseDataChange {
+
+ public DeleteUnescapedActivities(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id,data_field from activities where log_type='QPROFILE'");
+ massUpdate.update("delete from activities where id=?");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ String csv = row.getNullableString(2);
+ if (isUnescaped(csv)) {
+ update.setLong(1, row.getNullableLong(1));
+ return true;
+ }
+ return false;
+ }
+ });
+ }
+
+ static boolean isUnescaped(@Nullable String csv) {
+ if (csv != null) {
+ String[] splits = StringUtils.split(csv, ';');
+ for (String split : splits) {
+ if (StringUtils.countMatches(split, "=") != 1) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java
new file mode 100644
index 00000000000..5a4c3921bf5
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedFileSources.java
@@ -0,0 +1,283 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import java.nio.charset.StandardCharsets;
+import java.sql.SQLException;
+import java.util.Date;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.Select.RowReader;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Used in the Active Record Migration 714
+ *
+ * @since 5.0
+ */
+public class FeedFileSources extends BaseDataChange {
+
+ private static final String SELECT_FILES_AND_MEASURES_SQL = "SELECT " +
+ "p.uuid, " +
+ "f.uuid, " +
+ "ss.data, " +
+ "ss.updated_at, " +
+
+ // revisions_by_line
+ "m1.text_value, " +
+ "m1.measure_data, " +
+
+ // authors_by_line
+ "m2.text_value, " +
+ "m2.measure_data, " +
+
+ // dates_by_line
+ "m3.text_value, " +
+ "m3.measure_data, " +
+
+ // hits_by_line
+ "m4.text_value, " +
+ "m4.measure_data, " +
+
+ // cond_by_line
+ "m5.text_value, " +
+ "m5.measure_data, " +
+
+ // cover_cond_by_line
+ "m6.text_value, " +
+ "m6.measure_data, " +
+
+ // it_hits_by_line
+ "m7.text_value, " +
+ "m7.measure_data, " +
+
+ // it_cond_by_line
+ "m8.text_value, " +
+ "m8.measure_data, " +
+
+ // it_cover_cond_by_line
+ "m9.text_value, " +
+ "m9.measure_data, " +
+
+ // overall_hits_by_line
+ "m10.text_value, " +
+ "m10.measure_data, " +
+
+ // overall_cond_by_line
+ "m11.text_value, " +
+ "m11.measure_data, " +
+
+ // overall_cover_cond_by_line
+ "m12.text_value, " +
+ "m12.measure_data, " +
+
+ // duplication_data
+ "m13.text_value, " +
+ "m13.measure_data " +
+
+ "FROM snapshots s " +
+ "JOIN snapshot_sources ss " +
+ "ON s.id = ss.snapshot_id AND s.islast = ? " +
+ "JOIN projects p " +
+ "ON s.root_project_id = p.id " +
+ "JOIN projects f " +
+ "ON s.project_id = f.id " +
+ "LEFT JOIN file_sources fs " +
+ "ON fs.file_uuid = f.uuid " +
+ "LEFT JOIN project_measures m1 " +
+ "ON m1.snapshot_id = s.id AND m1.metric_id = ? " +
+ "LEFT JOIN project_measures m2 " +
+ "ON m2.snapshot_id = s.id AND m2.metric_id = ? " +
+ "LEFT JOIN project_measures m3 " +
+ "ON m3.snapshot_id = s.id AND m3.metric_id = ? " +
+ "LEFT JOIN project_measures m4 " +
+ "ON m4.snapshot_id = s.id AND m4.metric_id = ? " +
+ "LEFT JOIN project_measures m5 " +
+ "ON m5.snapshot_id = s.id AND m5.metric_id = ? " +
+ "LEFT JOIN project_measures m6 " +
+ "ON m6.snapshot_id = s.id AND m6.metric_id = ? " +
+ "LEFT JOIN project_measures m7 " +
+ "ON m7.snapshot_id = s.id AND m7.metric_id = ? " +
+ "LEFT JOIN project_measures m8 " +
+ "ON m8.snapshot_id = s.id AND m8.metric_id = ? " +
+ "LEFT JOIN project_measures m9 " +
+ "ON m9.snapshot_id = s.id AND m9.metric_id = ? " +
+ "LEFT JOIN project_measures m10 " +
+ "ON m10.snapshot_id = s.id AND m10.metric_id = ? " +
+ "LEFT JOIN project_measures m11 " +
+ "ON m11.snapshot_id = s.id AND m11.metric_id = ? " +
+ "LEFT JOIN project_measures m12 " +
+ "ON m12.snapshot_id = s.id AND m12.metric_id = ? " +
+ "LEFT JOIN project_measures m13 " +
+ "ON m13.snapshot_id = s.id AND m13.metric_id = ? " +
+ "WHERE " +
+ "f.enabled = ? " +
+ "AND f.scope = 'FIL' " +
+ "AND p.scope = 'PRJ' AND p.qualifier = 'TRK' " +
+ "AND fs.file_uuid IS NULL";
+
+ private static final class FileSourceBuilder implements MassUpdate.Handler {
+ private final long now;
+
+ public FileSourceBuilder(System2 system) {
+ now = system.now();
+ }
+
+ @Override
+ public boolean handle(Row row, SqlStatement update) throws SQLException {
+ String projectUuid = row.getNullableString(1);
+ String fileUuid = row.getNullableString(2);
+ String source = StringUtils.defaultIfBlank(row.getNullableString(3), "");
+ Date updatedAt = row.getNullableDate(4);
+ byte[] shortRevisions = row.getNullableBytes(5);
+ byte[] longRevisions = row.getNullableBytes(6);
+ byte[] shortAuthors = row.getNullableBytes(7);
+ byte[] longAuthors = row.getNullableBytes(8);
+ byte[] shortDates = row.getNullableBytes(9);
+ byte[] longDates = row.getNullableBytes(10);
+ byte[] shortUtHits = row.getNullableBytes(11);
+ byte[] longUtHits = row.getNullableBytes(12);
+ byte[] shortUtCond = row.getNullableBytes(13);
+ byte[] longUtCond = row.getNullableBytes(14);
+ byte[] shortUtCovCond = row.getNullableBytes(15);
+ byte[] longUtCovCond = row.getNullableBytes(16);
+ byte[] shortItHits = row.getNullableBytes(17);
+ byte[] longItHits = row.getNullableBytes(18);
+ byte[] shortItCond = row.getNullableBytes(19);
+ byte[] longItCond = row.getNullableBytes(20);
+ byte[] shortItCovCond = row.getNullableBytes(21);
+ byte[] longItCovCond = row.getNullableBytes(22);
+ byte[] shortOverallHits = row.getNullableBytes(23);
+ byte[] longOverallHits = row.getNullableBytes(24);
+ byte[] shortOverallCond = row.getNullableBytes(25);
+ byte[] longOverallCond = row.getNullableBytes(26);
+ byte[] shortOverallCovCond = row.getNullableBytes(27);
+ byte[] longOverallCovCond = row.getNullableBytes(28);
+ byte[] shortDuplicationData = row.getNullableBytes(29);
+ byte[] longDuplicationData = row.getNullableBytes(30);
+
+ String[] sourceData = new FileSourceDto(source,
+ ofNullableBytes(shortRevisions, longRevisions),
+ ofNullableBytes(shortAuthors, longAuthors),
+ ofNullableBytes(shortDates, longDates),
+ ofNullableBytes(shortUtHits, longUtHits),
+ ofNullableBytes(shortUtCond, longUtCond),
+ ofNullableBytes(shortUtCovCond, longUtCovCond),
+ ofNullableBytes(shortItHits, longItHits),
+ ofNullableBytes(shortItCond, longItCond),
+ ofNullableBytes(shortItCovCond, longItCovCond),
+ ofNullableBytes(shortOverallHits, longOverallHits),
+ ofNullableBytes(shortOverallCond, longOverallCond),
+ ofNullableBytes(shortOverallCovCond, longOverallCovCond),
+ ofNullableBytes(shortDuplicationData, longDuplicationData)
+ ).getSourceData();
+
+ update.setString(1, projectUuid)
+ .setString(2, fileUuid)
+ .setLong(3, now)
+ .setLong(4, updatedAt == null ? now : updatedAt.getTime())
+ .setString(5, sourceData[0])
+ .setString(6, sourceData[1])
+ .setString(7, "");
+
+ return true;
+ }
+ }
+
+ private static String ofNullableBytes(@Nullable byte[] shortBytes, @Nullable byte[] longBytes) {
+ byte[] result;
+ if (shortBytes == null) {
+ if (longBytes == null) {
+ return "";
+ } else {
+ result = longBytes;
+ }
+ } else {
+ result = shortBytes;
+ }
+ return new String(result, StandardCharsets.UTF_8);
+ }
+
+ private final System2 system;
+
+ public FeedFileSources(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ RowReader<Long> simpleLongReader = new RowReader<Long>() {
+ @Override
+ public Long read(Row row) throws SQLException {
+ Long longValue = row.getNullableLong(1);
+ return longValue == null ? Long.valueOf(0L) : longValue;
+ }
+ };
+ Long revisionMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'revisions_by_line'").get(simpleLongReader);
+ Long authorMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'authors_by_line'").get(simpleLongReader);
+ Long datesMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'last_commit_datetimes_by_line'").get(simpleLongReader);
+ Long utCoverageHitsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'coverage_line_hits_data'").get(simpleLongReader);
+ Long utConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'conditions_by_line'").get(simpleLongReader);
+ Long utCoveredConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'covered_conditions_by_line'").get(simpleLongReader);
+ Long itCoverageHitsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'it_coverage_line_hits_data'").get(simpleLongReader);
+ Long itConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'it_conditions_by_line'").get(simpleLongReader);
+ Long itCoveredConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'it_covered_conditions_by_line'").get(simpleLongReader);
+ Long overallCoverageHitsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'overall_coverage_line_hits_data'").get(simpleLongReader);
+ Long overallConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'overall_conditions_by_line'").get(simpleLongReader);
+ Long overallCoveredConditionsByLineMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'overall_covered_conditions_by_line'").get(simpleLongReader);
+ Long duplicationDataMetricId = context.prepareSelect("SELECT id FROM metrics WHERE name = 'duplications_data'").get(simpleLongReader);
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select(SELECT_FILES_AND_MEASURES_SQL)
+ .setBoolean(1, true)
+ .setLong(2, zeroIfNull(revisionMetricId))
+ .setLong(3, zeroIfNull(authorMetricId))
+ .setLong(4, zeroIfNull(datesMetricId))
+ .setLong(5, zeroIfNull(utCoverageHitsByLineMetricId))
+ .setLong(6, zeroIfNull(utConditionsByLineMetricId))
+ .setLong(7, zeroIfNull(utCoveredConditionsByLineMetricId))
+ .setLong(8, zeroIfNull(itCoverageHitsByLineMetricId))
+ .setLong(9, zeroIfNull(itConditionsByLineMetricId))
+ .setLong(10, zeroIfNull(itCoveredConditionsByLineMetricId))
+ .setLong(11, zeroIfNull(overallCoverageHitsByLineMetricId))
+ .setLong(12, zeroIfNull(overallConditionsByLineMetricId))
+ .setLong(13, zeroIfNull(overallCoveredConditionsByLineMetricId))
+ .setLong(14, zeroIfNull(duplicationDataMetricId))
+ .setBoolean(15, true);
+
+ massUpdate.update("INSERT INTO file_sources" +
+ "(project_uuid, file_uuid, created_at, updated_at, data, line_hashes, data_hash)" +
+ "VALUES " +
+ "(?, ?, ?, ?, ?, ?, ?)");
+ massUpdate.rowPluralName("files");
+
+ massUpdate.execute(new FileSourceBuilder(system));
+ }
+
+ private static long zeroIfNull(@Nullable Long value) {
+ return value == null ? 0L : value.longValue();
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java
new file mode 100644
index 00000000000..c9fd8cfbebe
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedIssueLongDates.java
@@ -0,0 +1,71 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedIssueLongDates extends BaseDataChange {
+
+ private final System2 system;
+
+ public FeedIssueLongDates(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final long now = system.now();
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT i.id, i.created_at, i.updated_at FROM issues i WHERE created_at_ms IS NULL");
+ massUpdate.update("UPDATE issues SET created_at_ms=?, updated_at_ms=? WHERE id=?");
+ massUpdate.rowPluralName("issues");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Long id = row.getNullableLong(1);
+ Date createdAt = row.getNullableDate(2);
+ Date updatedAt = row.getNullableDate(3);
+
+ if (createdAt == null) {
+ update.setLong(1, now);
+ } else {
+ update.setLong(1, Math.min(now, createdAt.getTime()));
+ }
+ if (updatedAt == null) {
+ update.setLong(2, now);
+ } else {
+ update.setLong(2, Math.min(now, updatedAt.getTime()));
+ }
+ update.setLong(3, id);
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java
new file mode 100644
index 00000000000..cc1e6859b5f
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FeedSnapshotSourcesUpdatedAt.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Used in the Active Record Migration 712
+ *
+ * @since 5.0
+ */
+public class FeedSnapshotSourcesUpdatedAt extends BaseDataChange {
+
+ public FeedSnapshotSourcesUpdatedAt(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT ss.id, s.build_date " +
+ "FROM snapshot_sources ss " +
+ "INNER JOIN snapshots s ON s.id = ss.snapshot_id " +
+ "WHERE ss.updated_at IS NULL");
+ massUpdate.update("UPDATE snapshot_sources " +
+ "SET updated_at=? " +
+ "WHERE id=?");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setDate(1, row.getNullableDate(2));
+ update.setLong(2, row.getNullableLong(1));
+ return true;
+ }
+ });
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java b/sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java
new file mode 100644
index 00000000000..ca1683ffa2d
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/FileSourceDto.java
@@ -0,0 +1,212 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.stax2.XMLInputFactory2;
+import org.codehaus.staxmate.SMInputFactory;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+import org.codehaus.staxmate.in.SMInputCursor;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.text.CsvWriter;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+class FileSourceDto {
+
+ private static final String SPACE_CHARS = "\t\n\r ";
+
+ private Iterator<String> sourceSplitter;
+
+ private final Map<Integer, String> revisions;
+ private final Map<Integer, String> authors;
+ private final Map<Integer, String> dates;
+ private final Map<Integer, String> utHits;
+ private final Map<Integer, String> utConditions;
+ private final Map<Integer, String> utCoveredConditions;
+ private final Map<Integer, String> itHits;
+ private final Map<Integer, String> itConditions;
+ private final Map<Integer, String> itCoveredConditions;
+ private final Map<Integer, String> overallHits;
+ private final Map<Integer, String> overallConditions;
+ private final Map<Integer, String> overallCoveredConditions;
+ private final List<List<Block>> duplicationGroups;
+
+ FileSourceDto(String source, String revisions, String authors, String dates,
+ String utHits, String utConditions, String utCoveredConditions,
+ String itHits, String itConditions, String itCoveredConditions,
+ String overallHits, String overallConditions, String overallCoveredConditions, String duplicationData) {
+ sourceSplitter = Splitter.onPattern("\r?\n|\r").split(source).iterator();
+ this.revisions = KeyValueFormat.parseIntString(revisions);
+ this.authors = KeyValueFormat.parseIntString(authors);
+ this.dates = KeyValueFormat.parseIntString(dates);
+ this.utHits = KeyValueFormat.parseIntString(utHits);
+ this.utConditions = KeyValueFormat.parseIntString(utConditions);
+ this.utCoveredConditions = KeyValueFormat.parseIntString(utCoveredConditions);
+ this.itHits = KeyValueFormat.parseIntString(itHits);
+ this.itConditions = KeyValueFormat.parseIntString(itConditions);
+ this.itCoveredConditions = KeyValueFormat.parseIntString(itCoveredConditions);
+ this.overallHits = KeyValueFormat.parseIntString(overallHits);
+ this.overallConditions = KeyValueFormat.parseIntString(overallConditions);
+ this.overallCoveredConditions = KeyValueFormat.parseIntString(overallCoveredConditions);
+ this.duplicationGroups = StringUtils.isNotBlank(duplicationData) ? parseDuplicationData(duplicationData) : Collections.<List<Block>>emptyList();
+ }
+
+ String[] getSourceData() {
+ String highlighting = "";
+ String symbolRefs = "";
+ Map<Integer, String> duplicationsPerLine = computeDuplicationsPerLine(duplicationGroups);
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ int line = 0;
+ String sourceLine = null;
+ CsvWriter csv = CsvWriter.of(new OutputStreamWriter(output, UTF_8));
+ StringBuilder lineHashes = new StringBuilder();
+ while (sourceSplitter.hasNext()) {
+ line++;
+ sourceLine = sourceSplitter.next();
+ lineHashes.append(lineChecksum(sourceLine)).append("\n");
+ csv.values(revisions.get(line), authors.get(line), dates.get(line),
+ utHits.get(line), utConditions.get(line), utCoveredConditions.get(line),
+ itHits.get(line), itConditions.get(line), itCoveredConditions.get(line),
+ overallHits.get(line), overallConditions.get(line), overallCoveredConditions.get(line),
+ highlighting, symbolRefs, duplicationsPerLine.get(line), sourceLine);
+ }
+ csv.close();
+ return new String[] {new String(output.toByteArray(), UTF_8), lineHashes.toString()};
+ }
+
+ public static String lineChecksum(String line) {
+ String reducedLine = StringUtils.replaceChars(line, SPACE_CHARS, "");
+ if (reducedLine.isEmpty()) {
+ return "";
+ }
+ return DigestUtils.md5Hex(reducedLine);
+ }
+
+ private Map<Integer, String> computeDuplicationsPerLine(List<List<Block>> duplicationGroups) {
+ Map<Integer, String> result = new HashMap<>();
+ if (duplicationGroups.isEmpty()) {
+ return result;
+ }
+ Map<Integer, StringBuilder> dupPerLine = new HashMap<>();
+ int blockId = 1;
+ for (List<Block> group : duplicationGroups) {
+ Block originBlock = group.get(0);
+ addBlock(blockId, originBlock, dupPerLine);
+ blockId++;
+ for (int i = 1; i < group.size(); i++) {
+ Block duplicate = group.get(i);
+ if (duplicate.resourceKey.equals(originBlock.resourceKey)) {
+ addBlock(blockId, duplicate, dupPerLine);
+ blockId++;
+ }
+ }
+ }
+ for (Map.Entry<Integer, StringBuilder> entry : dupPerLine.entrySet()) {
+ result.put(entry.getKey(), entry.getValue().toString());
+ }
+ return result;
+ }
+
+ private void addBlock(int blockId, Block block, Map<Integer, StringBuilder> dupPerLine) {
+ int currentLine = block.start;
+ for (int i = 0; i < block.length; i++) {
+ if (dupPerLine.get(currentLine) == null) {
+ dupPerLine.put(currentLine, new StringBuilder());
+ }
+ if (dupPerLine.get(currentLine).length() > 0) {
+ dupPerLine.get(currentLine).append(',');
+ }
+ dupPerLine.get(currentLine).append(blockId);
+ currentLine++;
+ }
+
+ }
+
+ /**
+ * Parses data of {@link CoreMetrics#DUPLICATIONS_DATA}.
+ */
+ private static List<List<Block>> parseDuplicationData(String data) {
+ ImmutableList.Builder<List<Block>> groups = ImmutableList.builder();
+ try {
+ StringReader reader = new StringReader(data);
+ SMInputFactory inputFactory = initStax();
+ SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
+ // <duplications>
+ rootC.advance();
+ SMInputCursor groupsCursor = rootC.childElementCursor("g");
+ while (groupsCursor.getNext() != null) {
+ // <g>
+ SMInputCursor blocksCursor = groupsCursor.childElementCursor("b");
+ ImmutableList.Builder<Block> group = ImmutableList.builder();
+ while (blocksCursor.getNext() != null) {
+ // <b>
+ String resourceKey = blocksCursor.getAttrValue("r");
+ int firstLine = getAttrIntValue(blocksCursor, "s");
+ int numberOfLines = getAttrIntValue(blocksCursor, "l");
+
+ group.add(new Block(resourceKey, firstLine, numberOfLines));
+ }
+ groups.add(group.build());
+ }
+ } catch (Exception e) {
+ // SONAR-6174 Ignore any issue while parsing duplication measure. There is nothing user can do and things will get solved after
+ // next analysis anyway
+ }
+ return groups.build();
+ }
+
+ private static int getAttrIntValue(SMInputCursor cursor, String attrName) throws XMLStreamException {
+ return cursor.getAttrIntValue(cursor.findAttrIndex(null, attrName));
+ }
+
+ private static SMInputFactory initStax() {
+ XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
+ return new SMInputFactory(xmlFactory);
+ }
+
+ private static class Block {
+
+ final String resourceKey;
+ final int start;
+ final int length;
+
+ public Block(String resourceKey, int s, int l) {
+ this.resourceKey = resourceKey;
+ this.start = s;
+ this.length = l;
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java
new file mode 100644
index 00000000000..d088be4f190
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationStep.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import java.sql.SQLException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Used in the Active Record Migration 716
+ *
+ * @since 5.0
+ */
+public class InsertProjectsAuthorizationUpdatedAtMigrationStep extends BaseDataChange {
+
+ private final System2 system;
+
+ public InsertProjectsAuthorizationUpdatedAtMigrationStep(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT p.id FROM projects p WHERE p.scope=? AND p.enabled=? and p.authorization_updated_at IS NULL").setString(1, "PRJ").setBoolean(2, true);
+ massUpdate.update("UPDATE projects SET authorization_updated_at=? WHERE id=?");
+ massUpdate.rowPluralName("projects");
+ massUpdate.execute(new MigrationHandler());
+ }
+
+ private class MigrationHandler implements MassUpdate.Handler {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Long id = row.getNullableLong(1);
+ update.setLong(1, system.now());
+ update.setLong(2, id);
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java
new file mode 100644
index 00000000000..181d0aeebf3
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationStep.java
@@ -0,0 +1,194 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+import org.sonar.api.resources.Scopes;
+import org.sonar.api.utils.internal.Uuids;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.core.util.ProgressLogger;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.version.MigrationStep;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static com.google.common.collect.Maps.newHashMap;
+
+/**
+ * Used in the Active Record Migration 705
+ *
+ * @since 5.0
+ */
+public class PopulateProjectsUuidColumnsMigrationStep implements MigrationStep {
+
+ private static final Logger LOG = Loggers.get(PopulateProjectsUuidColumnsMigrationStep.class);
+
+ private final DbClient db;
+ private final AtomicLong counter = new AtomicLong(0L);
+
+ public PopulateProjectsUuidColumnsMigrationStep(DbClient db) {
+ this.db = db;
+ }
+
+ @Override
+ public void execute() {
+ ProgressLogger progress = ProgressLogger.create(getClass(), counter);
+ progress.start();
+
+ final DbSession readSession = db.openSession(false);
+ final DbSession writeSession = db.openSession(true);
+ try {
+ readSession.select("org.sonar.db.version.v50.Migration50Mapper.selectRootProjects", new ResultHandler() {
+ @Override
+ public void handleResult(ResultContext context) {
+ Component project = (Component) context.getResultObject();
+ List<Component> components = readSession.getMapper(Migration50Mapper.class).selectComponentChildrenForProjects(project.getId());
+ MigrationContext migrationContext = new MigrationContext(readSession, writeSession, project, components);
+ migrateEnabledComponents(migrationContext);
+ migrateDisabledComponents(migrationContext);
+ }
+ });
+ writeSession.commit(true);
+ readSession.commit(true);
+
+ migrateComponentsWithoutUuid(readSession, writeSession);
+ writeSession.commit(true);
+
+ // log the total number of process rows
+ progress.log();
+ } finally {
+ readSession.close();
+ writeSession.close();
+ progress.stop();
+ }
+ }
+
+ private void migrateEnabledComponents(MigrationContext migrationContext) {
+ saveComponent(migrationContext.writeSession, migrationContext.project);
+ for (Component component : migrationContext.componentsToMigrate) {
+ migrationContext.updateComponent(component);
+ if (Strings.isNullOrEmpty(component.getModuleUuidPath())) {
+ LOG.warn(String.format("Ignoring component id '%s' because the module uuid path could not be created", component.getId()));
+ } else {
+ migrationContext.updateComponent(component);
+ saveComponent(migrationContext.writeSession, component);
+ }
+ }
+ }
+
+ private void migrateDisabledComponents(MigrationContext migrationContext) {
+ for (Component component : migrationContext.readSession.getMapper(Migration50Mapper.class).selectDisabledDirectComponentChildrenForProjects(migrationContext.project.getId())) {
+ migrationContext.updateComponent(component);
+ saveComponent(migrationContext.writeSession, component);
+ }
+ for (Component component : migrationContext.readSession.getMapper(Migration50Mapper.class).selectDisabledNoneDirectComponentChildrenForProjects(
+ migrationContext.project.getId())) {
+ migrationContext.updateComponent(component);
+ saveComponent(migrationContext.writeSession, component);
+ }
+ }
+
+ private void migrateComponentsWithoutUuid(DbSession readSession, DbSession writeSession) {
+ for (Component component : readSession.getMapper(Migration50Mapper.class).selectComponentsWithoutUuid()) {
+ String uuid = Uuids.create();
+ component.setUuid(uuid);
+ component.setProjectUuid(uuid);
+ saveComponent(writeSession, component);
+ }
+ }
+
+ private void saveComponent(DbSession writeSession, Component component) {
+ writeSession.getMapper(Migration50Mapper.class).updateComponentUuids(component);
+ counter.getAndIncrement();
+ }
+
+ private static class MigrationContext {
+ private final DbSession readSession;
+ private final DbSession writeSession;
+ private final Component project;
+ private final Map<Long, Component> componentsBySnapshotId = newHashMap();
+ private final Map<Long, String> uuidByComponentId = newHashMap();
+ private final List<Component> componentsToMigrate = newArrayList();
+
+ private MigrationContext(DbSession readSession, DbSession writeSession, Component project, List<Component> components) {
+ this.readSession = readSession;
+ this.writeSession = writeSession;
+ this.project = project;
+
+ project.setUuid(getOrCreateUuid(project));
+ project.setProjectUuid(project.getUuid());
+
+ componentsBySnapshotId.put(project.getSnapshotId(), project);
+ for (Component component : components) {
+ componentsBySnapshotId.put(component.getSnapshotId(), component);
+ if (component.getUuid() == null) {
+ componentsToMigrate.add(component);
+ }
+ }
+ }
+
+ public void updateComponent(Component component) {
+ component.setUuid(getOrCreateUuid(component));
+ component.setProjectUuid(getOrCreateUuid(project));
+
+ String snapshotPath = component.getSnapshotPath();
+ StringBuilder moduleUuidPath = new StringBuilder();
+ String lastModuleUuid = null;
+ if (!Strings.isNullOrEmpty(snapshotPath)) {
+ for (String s : Splitter.on(".").omitEmptyStrings().split(snapshotPath)) {
+ Long snapshotId = Long.valueOf(s);
+ Component currentComponent = componentsBySnapshotId.get(snapshotId);
+ if (currentComponent != null && currentComponent.getScope().equals(Scopes.PROJECT)) {
+ lastModuleUuid = getOrCreateUuid(currentComponent);
+ moduleUuidPath.append(lastModuleUuid).append(".");
+ }
+ }
+ }
+
+ if (moduleUuidPath.length() > 0 && lastModuleUuid != null) {
+ // Remove last '.'
+ moduleUuidPath.deleteCharAt(moduleUuidPath.length() - 1);
+
+ component.setModuleUuidPath(moduleUuidPath.toString());
+ component.setModuleUuid(lastModuleUuid);
+ }
+ }
+
+ private String getOrCreateUuid(Component component) {
+ String existingUuid = component.getUuid();
+ String uuid = existingUuid == null ? uuidByComponentId.get(component.getId()) : existingUuid;
+ if (uuid == null) {
+ String newUuid = Uuids.create();
+ uuidByComponentId.put(component.getId(), newUuid);
+ return newUuid;
+ }
+ return uuid;
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java
new file mode 100644
index 00000000000..50b1bd4c445
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationStep.java
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import com.google.common.collect.Lists;
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.List;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+// TODO could be refactored to benefit from existing code of ReplaceIssueFiltersProjectKeyByUuid
+// -> make any change of issue_filters easier
+public class RemoveSortFieldFromIssueFiltersMigrationStep extends BaseDataChange {
+
+ private static final char FIELD_SEPARATOR = '|';
+ private static final String SORT_KEY = "sort=";
+ private static final String ASC_KEY = "asc=";
+
+ private final System2 system;
+
+ public RemoveSortFieldFromIssueFiltersMigrationStep(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id,data from issue_filters where data like '%" + SORT_KEY + "%' or data like '%" + ASC_KEY + "%'");
+ massUpdate.update("update issue_filters set data=?, updated_at=? where id=?");
+ massUpdate.rowPluralName("issue filters");
+ massUpdate.execute(new FilterHandler(new Date(system.now())));
+ }
+
+ private static class FilterHandler implements MassUpdate.Handler {
+ private final Date now;
+
+ private FilterHandler(Date now) {
+ this.now = now;
+ }
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ String data = row.getNullableString(2);
+ String[] fields = StringUtils.split(data, FIELD_SEPARATOR);
+
+ boolean found = false;
+ List<String> fieldsToKeep = Lists.newArrayList();
+ for (String field : fields) {
+ if (field.startsWith(SORT_KEY) || field.startsWith(ASC_KEY)) {
+ found = true;
+ } else {
+ fieldsToKeep.add(field);
+ }
+ }
+ if (found) {
+ // data without 'sort' field
+ update.setString(1, StringUtils.join(fieldsToKeep, FIELD_SEPARATOR));
+ update.setDate(2, now);
+ update.setLong(3, row.getNullableLong(1));
+ }
+ return found;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java
new file mode 100644
index 00000000000..16e2ca6a2f3
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuid.java
@@ -0,0 +1,122 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Date;
+import javax.annotation.Nullable;
+import org.apache.commons.dbutils.DbUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Used in the Active Record Migration 710
+ *
+ * @since 5.0
+ */
+public class ReplaceIssueFiltersProjectKeyByUuid extends BaseDataChange {
+
+ private final System2 system;
+
+ public ReplaceIssueFiltersProjectKeyByUuid(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(final Context context) throws SQLException {
+ final Date now = new Date(system.now());
+
+ Connection connection = null;
+ PreparedStatement pstmt = null;
+ try {
+ connection = openConnection();
+ pstmt = connection.prepareStatement("SELECT p.uuid as uuid FROM projects p WHERE p.kee=?");
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT f.id, f.data FROM issue_filters f WHERE f.data like '%componentRoots=%'");
+ massUpdate.update("UPDATE issue_filters SET data=?, updated_at=? WHERE id=?");
+ final PreparedStatement finalPstmt = pstmt;
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Long id = row.getNullableLong(1);
+ String data = row.getNullableString(2);
+ if (data == null) {
+ return false;
+ }
+ update.setString(1, convertData(finalPstmt, data));
+ update.setDate(2, now);
+ update.setLong(3, id);
+ return true;
+ }
+ });
+ } finally {
+ DbUtils.closeQuietly(connection);
+ DbUtils.closeQuietly(pstmt);
+ }
+ }
+
+ private String convertData(PreparedStatement pstmt, String data) throws SQLException {
+ StringBuilder newFields = new StringBuilder();
+ String[] fields = data.split("\\|");
+ for (int i = 0; i < fields.length; i++) {
+ String field = fields[i];
+ if (field.contains("componentRoots")) {
+ String[] componentRootValues = field.split("=");
+ append(pstmt, newFields, componentRootValues.length == 2 ? componentRootValues[1] : null);
+ } else {
+ newFields.append(field);
+ }
+ if (i < fields.length - 1) {
+ newFields.append("|");
+ }
+ }
+ return newFields.toString();
+ }
+
+ private static void append(PreparedStatement pstmt, StringBuilder newFields, @Nullable String projectKey) throws SQLException {
+ if (projectKey != null) {
+ pstmt.setString(1, projectKey);
+ ResultSet rs = null;
+ try {
+ rs = pstmt.executeQuery();
+ if (rs.next()) {
+ String projectUuid = DatabaseUtils.getString(rs, "uuid");
+ if (projectUuid != null) {
+ newFields.append("projectUuids=").append(projectUuid);
+ }
+ }
+ } finally {
+ DbUtils.closeQuietly(rs);
+ }
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java b/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java
new file mode 100644
index 00000000000..09ffbdb94a8
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/AddIssuesColumns.java
@@ -0,0 +1,92 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+/**
+ * Add the following columns to the issues table :
+ * - issue_creation_date_ms
+ * - issue_update_date_ms
+ * - issue_close_date_ms
+ * - tags
+ * - component_uuid
+ * - project_uuid
+ */
+public class AddIssuesColumns extends DdlChange {
+
+ private final Database db;
+
+ public AddIssuesColumns(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(DdlChange.Context context) throws SQLException {
+ context.execute(generateSql());
+ }
+
+ private String generateSql() {
+ return new AddColumnsBuilder(db.getDialect(), "issues")
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("issue_creation_date_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true)
+ )
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("issue_update_date_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true)
+ )
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("issue_close_date_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true)
+ )
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("tags")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setLimit(4000)
+ .setNullable(true))
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("component_uuid")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setLimit(50)
+ .setNullable(true))
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("project_uuid")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setLimit(50)
+ .setNullable(true))
+ .build();
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java b/sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java
new file mode 100644
index 00000000000..2623dcb75e0
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/AddNewCharacteristics.java
@@ -0,0 +1,330 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.Select;
+
+/**
+ * See http://jira.sonarsource.com/browse/SONAR-6187
+ *
+ * Add a new Characteristic 'Usability' with 2 sub-characteristics 'Accessibility' and 'Ease of Use'
+ * and add a new sub-characteristic 'Compliance' for all characteristics.
+ *
+ * Nothing will be done if there's no characteristics in db, as they're all gonna be created by {@link org.sonar.server.startup.RegisterDebtModel}
+ *
+ * Before 4.3 the characteristics table contains requirements, then when selecting characteristics we should not forget to exclude them (with a filter on rule_id IS NULL)
+ *
+ */
+public class AddNewCharacteristics extends BaseDataChange {
+
+ private static final Logger LOGGER = Loggers.get(AddNewCharacteristics.class);
+
+ private static final String COMPLIANCE_NAME = "Compliance";
+ private static final String COMPLIANCE_KEY_SUFFIX = "_COMPLIANCE";
+
+ private static final String SECURITY_KEY = "SECURITY";
+ private static final String USABILITY_KEY = "USABILITY";
+
+ private static final String ERROR_SUFFIX = "Please restore your DB backup, start the previous version of SonarQube " +
+ "and update your SQALE model to fix this issue before trying again to run the migration.";
+
+ private final System2 system;
+
+ public AddNewCharacteristics(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ CharacteristicsContext characteristicsContext = new CharacteristicsContext(context, system);
+
+ // On an empty DB, there are no characteristics, they're all gonna be created after in RegisterDebtModel
+ if (!characteristicsContext.characteristics().isEmpty()) {
+ int usabilityOder = moveCharacteristicsDownToBeAbleToInsertUsability(characteristicsContext);
+ createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(characteristicsContext, usabilityOder);
+
+ createSubCharacteristic(characteristicsContext, "REUSABILITY" + COMPLIANCE_KEY_SUFFIX, "Reusability " + COMPLIANCE_NAME, "REUSABILITY");
+ createSubCharacteristic(characteristicsContext, "PORTABILITY" + COMPLIANCE_KEY_SUFFIX, "Portability " + COMPLIANCE_NAME, "PORTABILITY");
+ createSubCharacteristic(characteristicsContext, "MAINTAINABILITY" + COMPLIANCE_KEY_SUFFIX, "Maintainability " + COMPLIANCE_NAME, "MAINTAINABILITY");
+ createSubCharacteristic(characteristicsContext, SECURITY_KEY + COMPLIANCE_KEY_SUFFIX, "Security " + COMPLIANCE_NAME, SECURITY_KEY);
+ createSubCharacteristic(characteristicsContext, "EFFICIENCY" + COMPLIANCE_KEY_SUFFIX, "Efficiency " + COMPLIANCE_NAME, "EFFICIENCY");
+ createSubCharacteristic(characteristicsContext, "CHANGEABILITY" + COMPLIANCE_KEY_SUFFIX, "Changeability " + COMPLIANCE_NAME, "CHANGEABILITY");
+ createSubCharacteristic(characteristicsContext, "RELIABILITY" + COMPLIANCE_KEY_SUFFIX, "Reliability " + COMPLIANCE_NAME, "RELIABILITY");
+ createSubCharacteristic(characteristicsContext, "TESTABILITY" + COMPLIANCE_KEY_SUFFIX, "Testability " + COMPLIANCE_NAME, "TESTABILITY");
+ }
+ }
+
+ /**
+ * If the characteristic 'Security' exists, the new characteristic 'Usability' should be inserted just below it,
+ * so every existing characteristics below Security should move down.
+ *
+ * If the characteristic 'Security' does not exists, the new characteristic 'Usability' should be the first one,
+ * so every existing characteristics should move down.
+ *
+ * If the characteristic 'Usability' is already at the right place, nothing will be done.
+ */
+ private static int moveCharacteristicsDownToBeAbleToInsertUsability(CharacteristicsContext characteristicsContext) throws SQLException {
+ Characteristic security = characteristicsContext.findCharacteristicByKey(SECURITY_KEY);
+ Characteristic usability = characteristicsContext.findCharacteristicByKey(USABILITY_KEY);
+
+ int usabilityOder = 1;
+ int indexToStart = 0;
+ if (security != null) {
+ indexToStart = characteristicsContext.characteristics().indexOf(security) + 1;
+ usabilityOder = security.getOrder() + 1;
+ }
+
+ if (usability == null || usability.getOrder() != usabilityOder) {
+ // Move root characteristics one step lower
+ for (int i = indexToStart; i < characteristicsContext.characteristics().size(); i++) {
+ Characteristic characteristic = characteristicsContext.characteristics().get(i);
+ if (characteristic.getParentId() == null) {
+ characteristicsContext.updateCharacteristicOrder(characteristic.getKey(), characteristic.getOrder() + 1);
+ }
+ }
+ }
+ return usabilityOder;
+ }
+
+ private void createOrUpdateUsabilityCharacteristicAndItsSubCharacteristic(CharacteristicsContext characteristicsContext, int newUsabilityOrder)
+ throws SQLException {
+ String usabilityKey = USABILITY_KEY;
+ Characteristic usability = characteristicsContext.findCharacteristicByKey(usabilityKey);
+ if (usability != null) {
+ if (usability.getOrder() != newUsabilityOrder) {
+ usability.setOrder(newUsabilityOrder);
+ characteristicsContext.updateCharacteristicOrder(usability.getKey(), usability.getOrder());
+ }
+ } else {
+ usability = new Characteristic().setKey(usabilityKey).setName("Usability").setOrder(newUsabilityOrder);
+ characteristicsContext.insertCharacteristic(usability);
+ }
+
+ createSubCharacteristic(characteristicsContext, "USABILITY_ACCESSIBILITY", "Accessibility", usabilityKey);
+ createSubCharacteristic(characteristicsContext, "USABILITY_EASE_OF_USE", "Ease of Use", usabilityKey);
+ createSubCharacteristic(characteristicsContext, USABILITY_KEY + COMPLIANCE_KEY_SUFFIX, "Usability " + COMPLIANCE_NAME, usabilityKey);
+ }
+
+ private void createSubCharacteristic(CharacteristicsContext characteristicsContext,
+ String subCharacteristicKey, String subCharacteristicName, String parentKey) throws SQLException {
+ Characteristic parent = characteristicsContext.findCharacteristicByKey(parentKey);
+ if (parent != null) {
+ Characteristic subCharacteristic = characteristicsContext.findSubCharacteristicByKey(subCharacteristicKey, parent);
+ if (subCharacteristic == null) {
+ characteristicsContext.insertCharacteristic(new Characteristic().setKey(subCharacteristicKey).setName(subCharacteristicName).setParentId(parent.getId()));
+ }
+ }
+ // If the characteristic parent does not exits, the sub-characteristic is not added
+ }
+
+ private static class Characteristic {
+ private Integer id;
+ private String key;
+ private String name;
+ private Integer order;
+ private Integer parentId;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public Characteristic setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Characteristic setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Characteristic setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * On a characteristic, the order can never be null
+ */
+ public Integer getOrder() {
+ return parentId == null && order != null ? order : null;
+ }
+
+ public Characteristic setOrder(@Nullable Integer order) {
+ this.order = order;
+ return this;
+ }
+
+ @CheckForNull
+ public Integer getParentId() {
+ return parentId;
+ }
+
+ public Characteristic setParentId(@Nullable Integer parentId) {
+ this.parentId = parentId;
+ return this;
+ }
+ }
+
+ private static class CharacteristicsContext {
+ private final System2 system;
+ Context context;
+ Date now;
+ List<Characteristic> characteristics;
+
+ public CharacteristicsContext(Context context, System2 system) throws SQLException {
+ this.context = context;
+ this.system = system;
+ init();
+ }
+
+ private void init() throws SQLException {
+ now = new Date(system.now());
+ characteristics = selectEnabledCharacteristics();
+ }
+
+ public List<Characteristic> characteristics() {
+ return characteristics;
+ }
+
+ @CheckForNull
+ public Characteristic findCharacteristicByKey(final String key) {
+ Characteristic characteristic = Iterables.find(characteristics, new CharacteristicKey(key), null);
+ if (characteristic != null && characteristic.getParentId() != null) {
+ throw MessageException.of(String.format("'%s' must be a characteristic. " + ERROR_SUFFIX, characteristic.getName()));
+ }
+ return characteristic;
+ }
+
+ @CheckForNull
+ public Characteristic findSubCharacteristicByKey(final String key, Characteristic parent) {
+ Characteristic characteristic = Iterables.find(characteristics, new CharacteristicKey(key), null);
+ if (characteristic != null) {
+ Integer parentId = characteristic.getParentId();
+ if (parentId == null) {
+ throw MessageException.of(String.format("'%s' must be a sub-characteristic. " + ERROR_SUFFIX, characteristic.getName()));
+ } else if (!parentId.equals(parent.getId())) {
+ throw MessageException.of(String.format("'%s' must be defined under '%s'. " + ERROR_SUFFIX, characteristic.getName(), parent.getName()));
+ }
+ }
+ return characteristic;
+ }
+
+ private List<Characteristic> selectEnabledCharacteristics() throws SQLException {
+ return context.prepareSelect(
+ // Exclude requirements (to not fail when coming from a version older than 4.3)
+ "SELECT c.id, c.kee, c.name, c.characteristic_order, c.parent_id FROM characteristics c WHERE c.enabled=? AND c.rule_id IS NULL ORDER BY c.characteristic_order")
+ .setBoolean(1, true)
+ .list(new CharacteristicReader());
+ }
+
+ private int selectCharacteristicId(String key) throws SQLException {
+ Long id = context.prepareSelect(
+ "SELECT c.id FROM characteristics c WHERE c.kee = ? AND c.enabled=?")
+ .setString(1, key)
+ .setBoolean(2, true)
+ .get(Select.LONG_READER);
+ if (id != null) {
+ return id.intValue();
+ } else {
+ throw new IllegalStateException(String.format("Characteristic '%s' could not be inserted", key));
+ }
+ }
+
+ public void insertCharacteristic(Characteristic characteristic) throws SQLException {
+ if (characteristic.getParentId() == null) {
+ LOGGER.info("Insert new characteristic '{}'", characteristic.getKey());
+ } else {
+ LOGGER.info("Insert new sub characteristic '{}'", characteristic.getKey());
+ }
+
+ context.prepareUpsert("INSERT INTO characteristics (kee, name, parent_id, characteristic_order, enabled, created_at) VALUES (?, ?, ?, ?, ?, ?)")
+ .setString(1, characteristic.getKey())
+ .setString(2, characteristic.getName())
+ .setInt(3, characteristic.getParentId())
+ .setInt(4, characteristic.getOrder())
+ .setBoolean(5, true)
+ .setDate(6, now)
+ .execute()
+ .commit();
+ characteristic.setId(selectCharacteristicId(characteristic.getKey()));
+
+ characteristics.add(characteristic);
+ }
+
+ public void updateCharacteristicOrder(String key, Integer order) throws SQLException {
+ LOGGER.info("Update characteristic '{}' order to {}", key, order);
+
+ context.prepareUpsert("UPDATE characteristics SET characteristic_order=?, updated_at=? WHERE kee=?")
+ .setInt(1, order)
+ .setDate(2, now)
+ .setString(3, key)
+ .execute()
+ .commit();
+ }
+
+ private static class CharacteristicReader implements Select.RowReader<Characteristic> {
+ @Override
+ public Characteristic read(Select.Row row) throws SQLException {
+ return new Characteristic()
+ .setId(row.getInt(1))
+ .setKey(row.getString(2))
+ .setName(row.getString(3))
+ .setOrder(row.getNullableInt(4))
+ .setParentId(row.getNullableInt(5));
+ }
+ }
+ }
+
+ private static class CharacteristicKey implements Predicate<Characteristic> {
+ private final String key;
+
+ public CharacteristicKey(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public boolean apply(@Nullable Characteristic input) {
+ return input != null && input.key.equals(key);
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java b/sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java
new file mode 100644
index 00000000000..0ae65deb79f
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsers.java
@@ -0,0 +1,172 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.CheckForNull;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.ProgressLogger;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.Upsert;
+import org.sonar.db.version.UpsertImpl;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+public class CopyScmAccountsFromAuthorsToUsers extends BaseDataChange {
+
+ private static final char SCM_ACCOUNTS_SEPARATOR = '\n';
+
+ private final System2 system;
+ private final AtomicLong counter = new AtomicLong(0L);
+
+ public CopyScmAccountsFromAuthorsToUsers(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(final Context context) throws SQLException {
+ ProgressLogger progress = ProgressLogger.create(getClass(), counter);
+ progress.start();
+ final Long now = system.now();
+
+ try {
+ final Multimap<Long, String> authorsByPersonId = ArrayListMultimap.create();
+ context.prepareSelect("SELECT a.person_id, a.login FROM authors a," +
+ " (SELECT person_id, COUNT(*) AS nb FROM authors GROUP BY person_id HAVING COUNT(*) > 1) group_by_person" +
+ " WHERE a.person_id = group_by_person.person_id "
+ ).scroll(new AuthorsByPersonIdHandler(authorsByPersonId));
+
+ Upsert update = context.prepareUpsert("UPDATE users SET scm_accounts = ?, updated_at = ? WHERE id = ?");
+ for (Long personId : authorsByPersonId.keySet()) {
+ List<String> authors = newArrayList(authorsByPersonId.get(personId));
+ List<User> users = selectUsersFromLoginOrEmail(context, authors);
+ if (users.size() == 1) {
+ User user = users.get(0);
+ if (authors.contains(user.login)) {
+ authors.remove(user.login);
+ }
+ if (authors.contains(user.email)) {
+ authors.remove(user.email);
+ }
+ if (!authors.isEmpty()) {
+ update
+ .setString(1, encodeScmAccounts(authors))
+ .setLong(2, now)
+ .setLong(3, user.id)
+ .addBatch();
+ counter.getAndIncrement();
+ }
+ }
+ }
+ if (((UpsertImpl) update).getBatchCount() > 0L) {
+ update.execute().commit();
+ }
+ update.close();
+
+ progress.log();
+ } finally {
+ progress.stop();
+ }
+ }
+
+ private List<User> selectUsersFromLoginOrEmail(Context context, Collection<String> authors) throws SQLException {
+ final List<User> users = newArrayList();
+ StringBuilder sql = new StringBuilder("SELECT u.id, u.login, u.email, u.scm_accounts FROM users u WHERE u.active=? AND (");
+ for (int i = 0; i < authors.size(); i++) {
+ if (i < authors.size() - 1) {
+ sql.append("u.login=? OR u.email=? OR ");
+ } else {
+ sql.append("u.login=? OR u.email=?)");
+ }
+ }
+ Select select = context.prepareSelect(sql.toString());
+ select.setBoolean(1, true);
+ int currentIndex = 1;
+ for (String author : authors) {
+ currentIndex++;
+ select.setString(currentIndex, author);
+ currentIndex++;
+ select.setString(currentIndex, author);
+ }
+
+ select.scroll(new UsersHandler(users));
+ return users;
+ }
+
+ @CheckForNull
+ private static String encodeScmAccounts(List<String> scmAccounts) {
+ if (scmAccounts.isEmpty()) {
+ return null;
+ }
+ return SCM_ACCOUNTS_SEPARATOR + Joiner.on(SCM_ACCOUNTS_SEPARATOR).join(scmAccounts) + SCM_ACCOUNTS_SEPARATOR;
+ }
+
+ private static class User {
+ Long id;
+ String login;
+ String email;
+ String scmAccounts;
+
+ User(Long id, String login, String email, String scmAccounts) {
+ this.id = id;
+ this.login = login;
+ this.email = email;
+ this.scmAccounts = scmAccounts;
+ }
+ }
+
+ private static class AuthorsByPersonIdHandler implements Select.RowHandler {
+
+ private final Multimap<Long, String> authorsByPersonId;
+
+ private AuthorsByPersonIdHandler(Multimap<Long, String> authorsByPersonId) {
+ this.authorsByPersonId = authorsByPersonId;
+ }
+
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ authorsByPersonId.put(row.getNullableLong(1), row.getNullableString(2));
+ }
+ }
+
+ private static class UsersHandler implements Select.RowHandler {
+
+ private final List<User> users;
+
+ private UsersHandler(List<User> users) {
+ this.users = users;
+ }
+
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ users.add(new User(row.getNullableLong(1), row.getNullableString(2), row.getNullableString(3), row.getNullableString(4)));
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java b/sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java
new file mode 100644
index 00000000000..a8a0557e6a8
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/DropIssuesColumns.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import com.google.common.annotations.VisibleForTesting;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.DdlChange;
+import org.sonar.db.version.DropColumnsBuilder;
+
+/**
+ * Drop the following columns from the issues table :
+ * - issue_creation_date
+ * - issue_update_date
+ * - issue_close_date
+ * - component_id
+ * - root_component_id
+ */
+public class DropIssuesColumns extends DdlChange {
+
+ private final Database db;
+
+ public DropIssuesColumns(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(generateSql());
+ }
+
+ @VisibleForTesting
+ String generateSql() {
+ return new DropColumnsBuilder(db.getDialect(), "issues",
+ "issue_creation_date", "issue_update_date", "issue_close_date", "component_id", "root_component_id")
+ .build();
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java
new file mode 100644
index 00000000000..42434d8d727
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDates.java
@@ -0,0 +1,68 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedAnalysisReportsLongDates extends BaseDataChange {
+
+ private final System2 system;
+
+ public FeedAnalysisReportsLongDates(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final long now = system.now();
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT a.created_at, a.updated_at, a.started_at, a.finished_at, a.id FROM analysis_reports a WHERE created_at_ms IS NULL");
+ massUpdate.update("UPDATE analysis_reports SET created_at_ms=?, updated_at_ms=?, started_at_ms=?, finished_at_ms=? WHERE id=?");
+ massUpdate.rowPluralName("analysis_reports");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Date createdAt = row.getNullableDate(1);
+ Date updatedAt = row.getNullableDate(2);
+ Date startedAt = row.getNullableDate(3);
+ Date finishedAt = row.getNullableDate(4);
+ Long id = row.getNullableLong(5);
+
+ update.setLong(1, createdAt == null ? now : Math.min(now, createdAt.getTime()));
+ update.setLong(2, updatedAt == null ? now : Math.min(now, updatedAt.getTime()));
+ update.setLong(3, startedAt == null ? null : startedAt.getTime());
+ update.setLong(4, finishedAt == null ? null : finishedAt.getTime());
+ update.setLong(5, id);
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java
new file mode 100644
index 00000000000..151fa7c9cff
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedEventsLongDates.java
@@ -0,0 +1,75 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedEventsLongDates extends BaseDataChange {
+
+ private final System2 system2;
+
+ public FeedEventsLongDates(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate
+ .select("SELECT e.event_date, e.created_at, e.id FROM events e WHERE event_date_ms IS NULL");
+ massUpdate
+ .update("UPDATE events SET event_date_ms=?, created_at_ms=? WHERE id=?");
+ massUpdate.rowPluralName("events");
+ massUpdate.execute(new EventDateHandler(system2.now()));
+ }
+
+ private static class EventDateHandler implements MassUpdate.Handler {
+
+ private final long now;
+
+ public EventDateHandler(long now) {
+ this.now = now;
+ }
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Date eventDate = row.getNullableDate(1);
+ long eventTime = eventDate == null ? now : Math.min(now, eventDate.getTime());
+ update.setLong(1, eventTime);
+ Date createdAt = row.getNullableDate(2);
+ update.setLong(2, createdAt == null ? eventTime : Math.min(now, createdAt.getTime()));
+
+ Long id = row.getNullableLong(3);
+ update.setLong(3, id);
+
+ return true;
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java
new file mode 100644
index 00000000000..7e3d5e5ef74
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedFileSourcesBinaryData.java
@@ -0,0 +1,163 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import com.google.common.base.Function;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterables;
+import java.sql.SQLException;
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.db.Database;
+import org.sonar.db.source.FileSourceDto;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+import org.sonar.server.source.db.FileSourceDb;
+
+public class FeedFileSourcesBinaryData extends BaseDataChange {
+
+ public FeedFileSourcesBinaryData(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("issues");
+ update.select("SELECT id,data FROM file_sources WHERE binary_data is null");
+ update.update("UPDATE file_sources SET binary_data=? WHERE id=?");
+ update.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Long fileSourceId = row.getNullableLong(1);
+ update.setBytes(1, toBinary(fileSourceId, row.getNullableString(2)));
+ update.setLong(2, fileSourceId);
+ return true;
+ }
+ });
+ }
+
+ private byte[] toBinary(Long fileSourceId, @Nullable String data) {
+ FileSourceDb.Data.Builder dataBuilder = FileSourceDb.Data.newBuilder();
+ CSVParser parser = null;
+ try {
+ if (data != null) {
+ parser = CSVParser.parse(data, CSVFormat.DEFAULT);
+ Iterator<CSVRecord> rows = parser.iterator();
+ int line = 1;
+ while (rows.hasNext()) {
+ CSVRecord row = rows.next();
+ if (row.size() == 16) {
+
+ FileSourceDb.Line.Builder lineBuilder = dataBuilder.addLinesBuilder();
+ lineBuilder.setLine(line);
+ String s = row.get(0);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setScmRevision(s);
+ }
+ s = row.get(1);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setScmAuthor(s);
+ }
+ s = row.get(2);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setScmDate(DateUtils.parseDateTimeQuietly(s).getTime());
+ }
+ s = row.get(3);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setUtLineHits(Integer.parseInt(s));
+ }
+ s = row.get(4);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setUtConditions(Integer.parseInt(s));
+ }
+ s = row.get(5);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setUtCoveredConditions(Integer.parseInt(s));
+ }
+ s = row.get(6);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setItLineHits(Integer.parseInt(s));
+ }
+ s = row.get(7);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setItConditions(Integer.parseInt(s));
+ }
+ s = row.get(8);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setItCoveredConditions(Integer.parseInt(s));
+ }
+ s = row.get(9);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setOverallLineHits(Integer.parseInt(s));
+ }
+ s = row.get(10);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setOverallConditions(Integer.parseInt(s));
+ }
+ s = row.get(11);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setOverallCoveredConditions(Integer.parseInt(s));
+ }
+ s = row.get(12);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setHighlighting(s);
+ }
+ s = row.get(13);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.setSymbols(s);
+ }
+ s = row.get(14);
+ if (StringUtils.isNotEmpty(s)) {
+ lineBuilder.addAllDuplication(splitIntegers(s));
+ }
+ s = row.get(15);
+ if (s != null) {
+ lineBuilder.setSource(s);
+ }
+ }
+ line++;
+ }
+ }
+ return FileSourceDto.encodeSourceData(dataBuilder.build());
+ } catch (Exception e) {
+ throw new IllegalStateException("Invalid FILE_SOURCES.DATA on row with ID " + fileSourceId + ": " + data, e);
+ } finally {
+ IOUtils.closeQuietly(parser);
+ }
+ }
+
+ private static Iterable<Integer> splitIntegers(String s) {
+ return Iterables.transform(Splitter.on(',').omitEmptyStrings().trimResults().split(s), new Function<String, Integer>() {
+ @Override
+ public Integer apply(@Nonnull String input) {
+ return Integer.parseInt(input);
+ }
+ });
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java
new file mode 100644
index 00000000000..78c6a81b334
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueChangesLongDates.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedIssueChangesLongDates extends BaseDataChange {
+
+ private final System2 system;
+
+ public FeedIssueChangesLongDates(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final long now = system.now();
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT i.created_at, i.updated_at, i.issue_change_creation_date, i.id FROM issue_changes i WHERE created_at_ms IS NULL");
+ massUpdate.update("UPDATE issue_changes SET created_at_ms=?, updated_at_ms=?, issue_change_creation_date_ms=? WHERE id=?");
+ massUpdate.rowPluralName("issue_changes");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Date createdAt = row.getNullableDate(1);
+ Date updatedAt = row.getNullableDate(2);
+ Date functionalCreatedAt = row.getNullableDate(3);
+ Long id = row.getNullableLong(4);
+
+ update.setLong(1, createdAt == null ? now : Math.min(now, createdAt.getTime()));
+ update.setLong(2, updatedAt == null ? now : Math.min(now, updatedAt.getTime()));
+ update.setLong(3, functionalCreatedAt == null ? null : functionalCreatedAt.getTime());
+ update.setLong(4, id);
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java
new file mode 100644
index 00000000000..0bfe58afd52
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueComponentUuids.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.MassUpdate.Handler;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedIssueComponentUuids extends BaseDataChange {
+
+ public FeedIssueComponentUuids(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("issues");
+ update.select(
+ "SELECT c.uuid, c.project_uuid, i.id " +
+ "FROM issues i " +
+ "INNER JOIN projects c ON i.component_id=c.id " +
+ "WHERE i.component_uuid is null");
+ update.update("UPDATE issues SET component_uuid=?, project_uuid=? WHERE id=?");
+ update.execute(new SqlRowHandler());
+ }
+
+ private static class SqlRowHandler implements Handler {
+ @Override
+ public boolean handle(Row row, SqlStatement update) throws SQLException {
+ update.setString(1, row.getNullableString(1));
+ update.setString(2, row.getNullableString(2));
+ update.setLong(3, row.getNullableLong(3));
+
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java
new file mode 100644
index 00000000000..f84dd6b3688
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssueTags.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.Maps;
+import java.sql.SQLException;
+import java.util.Map;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.MassUpdate.Handler;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.Select.RowHandler;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * SONAR-5897
+ */
+public class FeedIssueTags extends BaseDataChange {
+
+ private static final Joiner TAG_JOINER = Joiner.on(',').skipNulls();
+
+ private final long now;
+
+ public FeedIssueTags(Database db, System2 system) {
+ super(db);
+ this.now = system.now();
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+
+ final Map<Integer, String> tagsByRuleId = Maps.newHashMap();
+ context.prepareSelect("SELECT id, system_tags, tags FROM rules").scroll(new RowHandler() {
+ @Override
+ public void handle(Row row) throws SQLException {
+ Integer id = row.getNullableInt(1);
+ tagsByRuleId.put(id, StringUtils.trimToNull(TAG_JOINER.join(
+ StringUtils.trimToNull(row.getNullableString(2)),
+ StringUtils.trimToNull(row.getNullableString(3)))));
+ }
+ });
+
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("issues");
+ update.select("SELECT id, rule_id FROM issues WHERE tags IS NULL");
+ update.update("UPDATE issues SET tags = ?, updated_at = ? WHERE id = ?");
+ update.execute(new Handler() {
+ @Override
+ public boolean handle(Row row, SqlStatement update) throws SQLException {
+ Long id = row.getNullableLong(1);
+ Integer ruleId = row.getNullableInt(2);
+ boolean updated = false;
+ if (tagsByRuleId.get(ruleId) != null) {
+ updated = true;
+ update.setString(1, tagsByRuleId.get(ruleId));
+ update.setLong(2, now);
+ update.setLong(3, id);
+ }
+ return updated;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java
new file mode 100644
index 00000000000..66ba13b9248
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedIssuesLongDates.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedIssuesLongDates extends BaseDataChange {
+
+ private final System2 system2;
+
+ public FeedIssuesLongDates(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final long now = system2.now();
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate
+ .select("SELECT i.issue_creation_date, i.issue_update_date, i.issue_close_date, i.id FROM issues i WHERE issue_creation_date_ms IS NULL");
+ massUpdate
+ .update("UPDATE issues SET issue_creation_date_ms=?, issue_update_date_ms=?, issue_close_date_ms=? WHERE id=?");
+ massUpdate.rowPluralName("issues");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ for (int i = 1; i <= 3; i++) {
+ Date date = row.getNullableDate(i);
+ update.setLong(i, date == null ? null : Math.min(now, date.getTime()));
+ }
+
+ Long id = row.getNullableLong(4);
+ update.setLong(4, id);
+
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java
new file mode 100644
index 00000000000..cb250c840d0
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedManualMeasuresLongDates.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedManualMeasuresLongDates extends BaseDataChange {
+
+ private final System2 system2;
+
+ public FeedManualMeasuresLongDates(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final long now = system2.now();
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate
+ .select("SELECT m.created_at, m.updated_at, m.id FROM manual_measures m WHERE created_at_ms IS NULL");
+ massUpdate
+ .update("UPDATE manual_measures SET created_at_ms=?, updated_at_ms=? WHERE id=?");
+ massUpdate.rowPluralName("manual measures");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ for (int i = 1; i <= 2; i++) {
+ Date date = row.getNullableDate(i);
+ update.setLong(i, date == null ? null : Math.min(now, date.getTime()));
+ }
+
+ Long id = row.getNullableLong(3);
+ update.setLong(3, id);
+
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java
new file mode 100644
index 00000000000..5e5ae4f149b
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedSnapshotsLongDates.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedSnapshotsLongDates extends BaseDataChange {
+
+ private final System2 system2;
+
+ public FeedSnapshotsLongDates(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final long now = system2.now();
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate
+ .select("SELECT s.created_at, s.build_date, s.period1_date, s.period2_date, s.period3_date, s.period4_date, s.period5_date, s.id FROM snapshots s WHERE created_at_ms IS NULL");
+ massUpdate
+ .update("UPDATE snapshots SET created_at_ms=?, build_date_ms=?, period1_date_ms=?, period2_date_ms=?, period3_date_ms=?, period4_date_ms=?, period5_date_ms=? WHERE id=?");
+ massUpdate.rowPluralName("snapshots");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ for (int i = 1; i <= 7; i++) {
+ Date date = row.getNullableDate(i);
+ update.setLong(i, date == null ? null : Math.min(now, date.getTime()));
+ }
+
+ Long id = row.getNullableLong(8);
+ update.setLong(8, id);
+
+ return true;
+ }
+ });
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java
new file mode 100644
index 00000000000..0196e62fc57
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/FeedUsersLongDates.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import java.util.Date;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedUsersLongDates extends BaseDataChange {
+
+ private final System2 system;
+
+ public FeedUsersLongDates(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT u.id, u.created_at, u.updated_at FROM users u WHERE created_at_ms IS NULL");
+ massUpdate.update("UPDATE users SET created_at_ms=?, updated_at_ms=? WHERE id=?");
+ massUpdate.rowPluralName("users");
+ massUpdate.execute(new RowHandler(system.now()));
+ }
+
+ private static class RowHandler implements MassUpdate.Handler {
+
+ private final long now;
+
+ private RowHandler(long now) {
+ this.now = now;
+ }
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ Long id = row.getNullableLong(1);
+ Date createdAt = row.getNullableDate(2);
+ Date updatedAt = row.getNullableDate(3);
+
+ if (createdAt == null) {
+ update.setLong(1, now);
+ } else {
+ update.setLong(1, Math.min(now, createdAt.getTime()));
+ }
+ if (updatedAt == null) {
+ update.setLong(2, now);
+ } else {
+ update.setLong(2, Math.min(now, updatedAt.getTime()));
+ }
+ update.setLong(3, id);
+ return true;
+ }
+ }
+
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java b/sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java
new file mode 100644
index 00000000000..bb70c544e89
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationStep.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * See http://jira.sonarsource.com/browse/SONAR-5596
+ *
+ * It's no possible to set permission on a module or a sub-view, but the batch was setting default permission on it on their creation.
+ * As now it's no more the case, we need to purge this useless data.
+ *
+ * @since 5.1
+ */
+public class RemovePermissionsOnModulesMigrationStep extends BaseDataChange {
+
+ public RemovePermissionsOnModulesMigrationStep(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ removeUserRolePermissions(context, "user_roles", "user roles");
+ removeUserRolePermissions(context, "group_roles", "group roles");
+ }
+
+ private void removeUserRolePermissions(Context context, String tableName, String pluralName) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT r.id " +
+ "FROM " + tableName + " r " +
+ " INNER JOIN projects ON projects.id = r.resource_id " +
+ "WHERE projects.module_uuid IS NOT NULL");
+ massUpdate.update("DELETE FROM " + tableName + " WHERE id=?");
+ massUpdate.rowPluralName(pluralName);
+ massUpdate.execute(MigrationHandler.INSTANCE);
+ }
+
+ private enum MigrationHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setLong(1, row.getLong(1));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java b/sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java
new file mode 100644
index 00000000000..73e8e8b9a79
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFilters.java
@@ -0,0 +1,89 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import com.google.common.collect.Lists;
+import java.sql.SQLException;
+import java.util.Date;
+import java.util.List;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class RenameComponentRelatedParamsInIssueFilters extends BaseDataChange {
+
+ private static final char FIELD_SEPARATOR = '|';
+ private static final String LIKE_PREFIX = "data like '%";
+ private static final String LIKE_SUFFIX = "%' or ";
+ private static final String COMPONENT_UUIDS = "componentUuids=";
+ private static final String COMPONENT_ROOT_UUIDS = "componentRootUuids=";
+
+ private final System2 system;
+
+ public RenameComponentRelatedParamsInIssueFilters(Database db, System2 system) {
+ super(db);
+ this.system = system;
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ final Date now = new Date(system.now());
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id,data from issue_filters where " +
+ LIKE_PREFIX + COMPONENT_UUIDS + LIKE_SUFFIX +
+ LIKE_PREFIX + COMPONENT_ROOT_UUIDS + "%'");
+ massUpdate.update("update issue_filters set data=?, updated_at=? where id=?");
+ massUpdate.rowPluralName("issue filters");
+ massUpdate.execute(new RenameComponentRelatedParamsHandler(now));
+ }
+
+ private static final class RenameComponentRelatedParamsHandler implements MassUpdate.Handler {
+ private final Date now;
+
+ private RenameComponentRelatedParamsHandler(Date now) {
+ this.now = now;
+ }
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ String data = row.getNullableString(2);
+ String[] fields = StringUtils.split(data, FIELD_SEPARATOR);
+
+ List<String> fieldsToKeep = Lists.newArrayList();
+ for (String field : fields) {
+ if (field.startsWith(COMPONENT_UUIDS) || field.startsWith(COMPONENT_ROOT_UUIDS)) {
+ fieldsToKeep.add(
+ field.replace(COMPONENT_UUIDS, "fileUuids=")
+ .replace(COMPONENT_ROOT_UUIDS, "moduleUuids="));
+ } else {
+ fieldsToKeep.add(field);
+ }
+ }
+ update.setString(1, StringUtils.join(fieldsToKeep, FIELD_SEPARATOR));
+ update.setDate(2, now);
+ update.setLong(3, row.getNullableLong(1));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java b/sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java
new file mode 100644
index 00000000000..d07f9acc2f8
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPath.java
@@ -0,0 +1,113 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.MassUpdate.Handler;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * SONAR-6054
+ * SONAR-6119
+ */
+public class UpdateProjectsModuleUuidPath extends BaseDataChange {
+
+ private static final String SEP = ".";
+
+ public UpdateProjectsModuleUuidPath(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("components");
+ update.select("SELECT p.id, p.module_uuid_path, p.uuid, p.scope, p.qualifier FROM projects p");
+ update.update("UPDATE projects SET module_uuid_path=? WHERE id=?");
+ update.execute(new ModuleUuidPathUpdateHandler());
+ }
+
+ private static final class ModuleUuidPathUpdateHandler implements Handler {
+ @Override
+ public boolean handle(Row row, SqlStatement update) throws SQLException {
+ Long id = row.getNullableLong(1);
+ String moduleUuidPath = row.getNullableString(2);
+ String uuid = row.getNullableString(3);
+ String scope = row.getNullableString(4);
+ String qualifier = row.getNullableString(5);
+
+ boolean needUpdate = false;
+ String newModuleUuidPath = moduleUuidPath;
+
+ if (needUpdateForEmptyPath(newModuleUuidPath)) {
+ newModuleUuidPath = SEP + uuid + SEP;
+ needUpdate = true;
+ }
+
+ if (needUpdateForSeparators(newModuleUuidPath)) {
+ newModuleUuidPath = newModuleUuidPathWithSeparators(newModuleUuidPath);
+ needUpdate = true;
+ }
+
+ if (needUpdateToIncludeItself(newModuleUuidPath, uuid, scope, qualifier)) {
+ newModuleUuidPath = newModuleUuidPathIncludingItself(newModuleUuidPath, uuid);
+ needUpdate = true;
+ }
+
+ if (needUpdate) {
+ update.setString(1, newModuleUuidPath);
+ update.setLong(2, id);
+ }
+ return needUpdate;
+ }
+
+ private static boolean needUpdateForEmptyPath(@Nullable String moduleUuidPath) {
+ return StringUtils.isEmpty(moduleUuidPath) || SEP.equals(moduleUuidPath);
+ }
+
+ private static boolean needUpdateForSeparators(String moduleUuidPath) {
+ return !(moduleUuidPath.startsWith(SEP) && moduleUuidPath.endsWith(SEP));
+ }
+
+ private static String newModuleUuidPathWithSeparators(String oldModuleUuidPath) {
+ StringBuilder newModuleUuidPath = new StringBuilder(oldModuleUuidPath);
+ newModuleUuidPath.insert(0, SEP);
+ newModuleUuidPath.append(SEP);
+ return newModuleUuidPath.toString();
+ }
+
+ private static boolean needUpdateToIncludeItself(String moduleUuidPath, @Nullable String uuid, @Nullable String scope, @Nullable String qualifier) {
+ return "PRJ".equals(scope) && !("DEV_PRJ".equals(qualifier)) && !(moduleUuidPath.contains(uuid));
+ }
+
+ private static String newModuleUuidPathIncludingItself(String moduleUuidPath, String uuid) {
+ StringBuilder newModuleUuidPath = new StringBuilder(moduleUuidPath);
+ newModuleUuidPath.append(uuid);
+ newModuleUuidPath.append(SEP);
+ return newModuleUuidPath.toString();
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java b/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java
new file mode 100644
index 00000000000..686db41ae2f
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumn.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.AddColumnsBuilder;
+import org.sonar.db.version.DdlChange;
+
+import static org.sonar.db.version.AddColumnsBuilder.ColumnDef.Type.STRING;
+
+/**
+ * Add the following column to the manual_measures table :
+ * - component_uuid
+ */
+public class AddManualMeasuresComponentUuidColumn extends DdlChange {
+ private final Database db;
+
+ public AddManualMeasuresComponentUuidColumn(Database db) {
+ super(db);
+ this.db = db;
+ }
+
+ @Override
+ public void execute(DdlChange.Context context) throws SQLException {
+ context.execute(generateSql());
+ }
+
+ private String generateSql() {
+ return new AddColumnsBuilder(db.getDialect(), "manual_measures")
+ .addColumn(
+ new AddColumnsBuilder.ColumnDef()
+ .setName("component_uuid")
+ .setType(STRING)
+ .setLimit(50)
+ .setNullable(true)
+ )
+ .build();
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java
new file mode 100644
index 00000000000..a6139e1e946
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedEventsComponentUuid.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedEventsComponentUuid extends BaseDataChange {
+
+ public FeedEventsComponentUuid(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("events");
+ update.select(
+ "SELECT p.uuid, event.id " +
+ "FROM events event " +
+ "INNER JOIN projects p ON p.id=event.resource_id " +
+ "WHERE event.component_uuid is null");
+ update.update("UPDATE events SET component_uuid=? WHERE id=?");
+ update.execute(MigrationHandler.INSTANCE);
+ }
+
+ private enum MigrationHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setString(1, row.getString(1));
+ update.setLong(2, row.getLong(2));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java
new file mode 100644
index 00000000000..b37e60054b3
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedFileSourcesDataType.java
@@ -0,0 +1,37 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+
+public class FeedFileSourcesDataType extends BaseDataChange {
+
+ public FeedFileSourcesDataType(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareUpsert("update file_sources set data_type = 'SOURCE'").execute().commit();
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java
new file mode 100644
index 00000000000..36b14ac2e83
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuid.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedManualMeasuresComponentUuid extends BaseDataChange {
+
+ public FeedManualMeasuresComponentUuid(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("manual measures");
+ update.select(
+ "SELECT p.uuid, mm.resource_id " +
+ "FROM manual_measures mm " +
+ "INNER JOIN projects p ON mm.resource_id = p.id " +
+ "WHERE mm.component_uuid IS NULL");
+ update.update("UPDATE manual_measures SET component_uuid=? WHERE resource_id=?");
+ update.execute(new SqlRowHandler());
+ }
+
+ private static class SqlRowHandler implements MassUpdate.Handler {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setString(1, row.getString(1));
+ update.setLong(2, row.getLong(2));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java
new file mode 100644
index 00000000000..f28bc096e5b
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedMetricsBooleans.java
@@ -0,0 +1,43 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+
+public class FeedMetricsBooleans extends BaseDataChange {
+
+ public FeedMetricsBooleans(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareUpsert("update metrics set optimized_best_value=?, hidden=?, delete_historical_data=? " +
+ "where user_managed=? or optimized_best_value is null or hidden is null or delete_historical_data is null")
+ .setBoolean(1, false)
+ .setBoolean(2, false)
+ .setBoolean(3, false)
+ .setBoolean(4, true)
+ .execute().commit();
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java
new file mode 100644
index 00000000000..4162aacb2ca
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuid.java
@@ -0,0 +1,58 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.SqlStatement;
+
+public class FeedProjectLinksComponentUuid extends BaseDataChange {
+
+ public FeedProjectLinksComponentUuid(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("project links");
+ update.select(
+ "SELECT p.uuid, link.id " +
+ "FROM project_links link " +
+ "INNER JOIN projects p ON p.id=link.project_id " +
+ "WHERE link.component_uuid is null");
+ update.update("UPDATE project_links SET component_uuid=? WHERE id=?");
+ update.execute(MigrationHandler.INSTANCE);
+ }
+
+ private enum MigrationHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Row row, SqlStatement update) throws SQLException {
+ update.setString(1, row.getString(1));
+ update.setLong(2, row.getLong(2));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java b/sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java
new file mode 100644
index 00000000000..d533aa73ef4
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/MoveProjectProfileAssociation.java
@@ -0,0 +1,136 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v52;
+
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.Table;
+import java.sql.SQLException;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.MassUpdate.Handler;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.Select.RowReader;
+import org.sonar.db.version.SqlStatement;
+import org.sonar.db.version.Upsert;
+
+/**
+ * SonarQube 5.2
+ * SONAR-6328
+ *
+ */
+public class MoveProjectProfileAssociation extends BaseDataChange {
+
+ private static final class ProjectProfileAssociationHandler implements Handler {
+ private final Upsert setDefaultProfile;
+ private final Upsert associateProjectToProfile;
+ private final Table<String, String, String> profileKeysByLanguageThenName;
+
+ private ProjectProfileAssociationHandler(Upsert setDefaultProfile, Upsert associateProjectToProfile, Table<String, String, String> profileKeysByLanguageThenName) {
+ this.setDefaultProfile = setDefaultProfile;
+ this.associateProjectToProfile = associateProjectToProfile;
+ this.profileKeysByLanguageThenName = profileKeysByLanguageThenName;
+ }
+
+ @Override
+ public boolean handle(Row row, SqlStatement update) throws SQLException {
+ Long id = row.getLong(1);
+ String profileLanguage = extractLanguage(row.getString(2));
+ String profileName = row.getString(3);
+ Long projectId = row.getNullableLong(4);
+ String projectUuid = row.getString(5);
+
+ if (profileKeysByLanguageThenName.contains(profileLanguage, profileName)) {
+ String profileKey = profileKeysByLanguageThenName.get(profileLanguage, profileName);
+
+ if (projectUuid == null) {
+ if (projectId == null) {
+ setDefaultProfile.setBoolean(1, true).setString(2, profileKey).execute();
+ } else {
+ LOGGER.warn(String.format("Profile with language '%s' and name '%s' is associated with unknown project '%d', ignored", profileLanguage, profileName, projectId));
+ }
+ } else {
+ associateProjectToProfile.setString(1, projectUuid).setString(2, profileKey).execute();
+ }
+ } else {
+ LOGGER.warn(String.format("Unable to find profile with language '%s' and name '%s', ignored", profileLanguage, profileName));
+ }
+
+ update.setLong(1, id);
+ return true;
+ }
+ }
+
+ private static final Logger LOGGER = Loggers.get(MoveProjectProfileAssociation.class);
+
+ public MoveProjectProfileAssociation(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+
+ final Table<String, String, String> profileKeysByLanguageThenName = getProfileKeysByLanguageThenName(context);
+
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("SELECT prop.id, prop.prop_key, prop.text_value, prop.resource_id, proj.uuid " +
+ "FROM properties prop " +
+ "LEFT OUTER JOIN projects proj ON prop.resource_id = proj.id " +
+ "WHERE prop.prop_key LIKE 'sonar.profile.%'"
+ );
+ massUpdate.update("DELETE FROM properties WHERE id = ?");
+
+ final Upsert setDefaultProfile = context.prepareUpsert("UPDATE rules_profiles SET is_default = ? WHERE kee = ?");
+ final Upsert associateProjectToProfile = context.prepareUpsert("INSERT INTO project_qprofiles (project_uuid, profile_key) VALUES (?, ?)");
+
+ try {
+ massUpdate.execute(new ProjectProfileAssociationHandler(setDefaultProfile, associateProjectToProfile, profileKeysByLanguageThenName));
+ } finally {
+ associateProjectToProfile.close();
+ setDefaultProfile.close();
+ }
+ }
+
+ private static String extractLanguage(String propertyKey) {
+ return propertyKey.substring("sonar.profile.".length());
+ }
+
+ private Table<String, String, String> getProfileKeysByLanguageThenName(final Context context) throws SQLException {
+ final Table<String, String, String> profilesByLanguageAndName = HashBasedTable.create();
+
+ Select selectProfiles = context.prepareSelect("SELECT kee, name, language FROM rules_profiles");
+ try {
+ selectProfiles.list(new RowReader<Void>() {
+ @Override
+ public Void read(Row row) throws SQLException {
+ profilesByLanguageAndName.put(row.getString(3), row.getString(2), row.getString(1));
+ return null;
+ }
+ });
+ } finally {
+ selectProfiles.close();
+ }
+
+ return profilesByLanguageAndName;
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java
new file mode 100644
index 00000000000..11945b60316
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveComponentLibraries.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Remove all components having qualifier 'LIB'
+ */
+public class RemoveComponentLibraries extends BaseDataChange {
+
+ public RemoveComponentLibraries(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("component libraries");
+ update.select("SELECT p.id FROM projects p WHERE p.qualifier='LIB'");
+ update.update("DELETE FROM projects WHERE id=?");
+ update.execute(MigrationHandler.INSTANCE);
+ }
+
+ private enum MigrationHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setLong(1, row.getLong(1));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java
new file mode 100644
index 00000000000..557c562fca0
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeys.java
@@ -0,0 +1,163 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.annotation.Nonnull;
+import org.sonar.core.util.ProgressLogger;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.Upsert;
+
+/**
+ * Remove all duplicated component that have the same keys.
+ * For each duplicated component key :
+ * <ul>
+ * <li>Only the enabled one or last one (with the latest id) is kept</li>
+ * <li>When deleting a component, all its issues are linked to the remaining component</li>
+ * </ul>
+ */
+public class RemoveDuplicatedComponentKeys extends BaseDataChange {
+
+ private final AtomicLong counter = new AtomicLong(0L);
+
+ public RemoveDuplicatedComponentKeys(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(final Context context) throws SQLException {
+ Upsert componentUpdate = context.prepareUpsert("DELETE FROM projects WHERE id=?");
+ Upsert issuesUpdate = context.prepareUpsert("UPDATE issues SET component_uuid=?, project_uuid=? WHERE component_uuid=?");
+
+ ProgressLogger progress = ProgressLogger.create(getClass(), counter);
+ progress.start();
+ try {
+ RemoveDuplicatedComponentHandler handler = new RemoveDuplicatedComponentHandler(context, componentUpdate, issuesUpdate);
+ context.prepareSelect(
+ "SELECT p.kee, COUNT(p.kee) FROM projects p " +
+ "GROUP BY p.kee " +
+ "HAVING COUNT(p.kee) > 1")
+ .scroll(handler);
+ if (!handler.isEmpty) {
+ componentUpdate.execute().commit();
+ issuesUpdate.execute().commit();
+ }
+ progress.log();
+ } finally {
+ progress.stop();
+ componentUpdate.close();
+ issuesUpdate.close();
+ }
+ }
+
+ private class RemoveDuplicatedComponentHandler implements Select.RowHandler {
+ private final Context context;
+ private final Upsert componentUpdate;
+ private final Upsert issuesUpdate;
+
+ private boolean isEmpty = true;
+
+ public RemoveDuplicatedComponentHandler(Context context, Upsert componentUpdate, Upsert issuesUpdate) {
+ this.context = context;
+ this.componentUpdate = componentUpdate;
+ this.issuesUpdate = issuesUpdate;
+ }
+
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ List<Component> components = context
+ .prepareSelect("SELECT p.id, p.uuid, p.project_uuid, p.enabled FROM projects p WHERE p.kee=? ORDER BY id")
+ .setString(1, row.getString(1))
+ .list(ComponentRowReader.INSTANCE);
+ // We keep the enabled component or the last component of the list
+ Component refComponent = FluentIterable.from(components).firstMatch(EnabledComponent.INSTANCE).or(components.get(components.size() - 1));
+ for (Component componentToRemove : FluentIterable.from(components).filter(Predicates.not(new MatchComponentId(refComponent.id)))) {
+ componentUpdate
+ .setLong(1, componentToRemove.id)
+ .addBatch();
+ issuesUpdate
+ .setString(1, refComponent.uuid)
+ .setString(2, refComponent.projectUuid)
+ .setString(3, componentToRemove.uuid)
+ .addBatch();
+ counter.getAndIncrement();
+ isEmpty = false;
+ }
+ }
+
+ public boolean isEmpty() {
+ return isEmpty;
+ }
+ }
+
+ private enum EnabledComponent implements Predicate<Component> {
+ INSTANCE;
+
+ @Override
+ public boolean apply(@Nonnull Component input) {
+ return input.enabled;
+ }
+ }
+
+ private static class MatchComponentId implements Predicate<Component> {
+
+ private final long id;
+
+ public MatchComponentId(long id) {
+ this.id = id;
+ }
+
+ @Override
+ public boolean apply(@Nonnull Component input) {
+ return input.id == this.id;
+ }
+ }
+
+ private enum ComponentRowReader implements Select.RowReader<Component> {
+ INSTANCE;
+
+ @Override
+ public Component read(Select.Row row) throws SQLException {
+ return new Component(row.getLong(1), row.getString(2), row.getString(3), row.getBoolean(4));
+ }
+ }
+
+ private static class Component {
+ private final long id;
+ private final String uuid;
+ private final String projectUuid;
+ private final boolean enabled;
+
+ public Component(long id, String uuid, String projectUuid, boolean enabled) {
+ this.id = id;
+ this.uuid = uuid;
+ this.projectUuid = projectUuid;
+ this.enabled = enabled;
+ }
+ }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java
new file mode 100644
index 00000000000..108200dac7e
--- /dev/null
+++ b/sonar-db/src/main/java/org/sonar/db/version/v52/RemoveSnapshotLibraries.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.version.BaseDataChange;
+import org.sonar.db.version.MassUpdate;
+import org.sonar.db.version.Select;
+import org.sonar.db.version.SqlStatement;
+
+/**
+ * Remove all components having qualifier 'LIB'
+ */
+public class RemoveSnapshotLibraries extends BaseDataChange {
+
+ public RemoveSnapshotLibraries(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate update = context.prepareMassUpdate().rowPluralName("snapshot libraries");
+ update.select("SELECT s.id FROM snapshots s WHERE s.qualifier='LIB'");
+ update.update("DELETE FROM snapshots WHERE id=?");
+ update.execute(MigrationHandler.INSTANCE);
+ }
+
+ private enum MigrationHandler implements MassUpdate.Handler {
+ INSTANCE;
+
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ update.setLong(1, row.getLong(1));
+ return true;
+ }
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java b/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java
new file mode 100644
index 00000000000..e5160243c7f
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/AddColumnsBuilderTest.java
@@ -0,0 +1,136 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import org.junit.Test;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.MySql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+import org.sonar.db.version.AddColumnsBuilder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
+
+public class AddColumnsBuilderTest {
+
+ @Test
+ public void add_columns_on_h2() {
+ assertThat(new AddColumnsBuilder(new H2(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)");
+ }
+
+ @Test
+ public void add_columns_on_mysql() {
+ assertThat(new AddColumnsBuilder(new MySql(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL)");
+ }
+
+ @Test
+ public void add_columns_on_oracle() {
+ assertThat(new AddColumnsBuilder(new Oracle(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD (date_in_ms NUMBER (38) NULL, name VARCHAR (10) NOT NULL)");
+ }
+
+ @Test
+ public void add_columns_on_postgresql() {
+ assertThat(new AddColumnsBuilder(new PostgreSql(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD COLUMN date_in_ms BIGINT NULL, ADD COLUMN name VARCHAR (10) NOT NULL");
+ }
+
+ @Test
+ public void add_columns_on_mssql() {
+ assertThat(new AddColumnsBuilder(new MsSql(), "issues")
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("date_in_ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true))
+ .addColumn(new AddColumnsBuilder.ColumnDef()
+ .setName("name")
+ .setType(AddColumnsBuilder.ColumnDef.Type.STRING)
+ .setNullable(false)
+ .setLimit(10))
+ .build()).isEqualTo("ALTER TABLE issues ADD date_in_ms BIGINT NULL, name VARCHAR (10) NOT NULL");
+ }
+
+ @Test
+ public void fail_when_column_name_is_in_upper_case() {
+ try {
+ new AddColumnsBuilder.ColumnDef()
+ .setName("DATE_IN_MS")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true);
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Column name should only contains lowercase and _ characters");
+ }
+ }
+
+ @Test
+ public void fail_when_column_name_contains_invalid_character() {
+ try {
+ new AddColumnsBuilder.ColumnDef()
+ .setName("date-in/ms")
+ .setType(AddColumnsBuilder.ColumnDef.Type.BIG_INTEGER)
+ .setNullable(true);
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (IllegalArgumentException e) {
+ assertThat(e).hasMessage("Column name should only contains lowercase and _ characters");
+ }
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/BaseDataChangeTest.java b/sonar-db/src/test/java/org/sonar/db/version/BaseDataChangeTest.java
new file mode 100644
index 00000000000..56a5ed91799
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/BaseDataChangeTest.java
@@ -0,0 +1,481 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.BatchSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.Select.Row;
+import org.sonar.db.version.Select.RowReader;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.Assert.fail;
+
+@Category(DbTests.class)
+public class BaseDataChangeTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, BaseDataChangeTest.class, "schema.sql");
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table persons");
+ }
+
+ @Test
+ public void query() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final AtomicBoolean executed = new AtomicBoolean(false);
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ assertThat(context.prepareSelect("select id from persons order by id desc").list(Select.LONG_READER))
+ .containsExactly(3L, 2L, 1L);
+ assertThat(context.prepareSelect("select id from persons where id=?").setLong(1, 2L).get(Select.LONG_READER))
+ .isEqualTo(2L);
+ assertThat(context.prepareSelect("select id from persons where id=?").setLong(1, 12345L).get(Select.LONG_READER))
+ .isNull();
+ executed.set(true);
+ }
+ }.execute();
+ assertThat(executed.get()).isTrue();
+ }
+
+ @Test
+ public void read_column_types() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final List<Object[]> persons = new ArrayList<>();
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ persons.addAll(context
+ .prepareSelect("select id,login,age,enabled,updated_at,coeff from persons where id=2")
+ .list(new UserReader()));
+ }
+ }.execute();
+ assertThat(persons).hasSize(1);
+ assertThat(persons.get(0)[0]).isEqualTo(2L);
+ assertThat(persons.get(0)[1]).isEqualTo("emmerik");
+ assertThat(persons.get(0)[2]).isEqualTo(14);
+ assertThat(persons.get(0)[3]).isEqualTo(true);
+ assertThat(persons.get(0)[4]).isNotNull();
+ assertThat(persons.get(0)[5]).isEqualTo(5.2);
+ }
+
+ @Test
+ public void parameterized_query() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final List<Long> ids = new ArrayList<>();
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ ids.addAll(context.prepareSelect("select id from persons where id>=?").setLong(1, 2L).list(Select.LONG_READER));
+ }
+ }.execute();
+ assertThat(ids).containsOnly(2L, 3L);
+ }
+
+ @Test
+ public void display_current_row_details_if_error_during_get() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Error during processing of row: [id=2]");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareSelect("select id from persons where id>=?").setLong(1, 2L).get(new RowReader<Long>() {
+ @Override
+ public Long read(Row row) throws SQLException {
+ throw new IllegalStateException("Unexpected error");
+ }
+ });
+ }
+ }.execute();
+
+ }
+
+ @Test
+ public void display_current_row_details_if_error_during_list() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Error during processing of row: [id=2]");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareSelect("select id from persons where id>=?").setLong(1, 2L).list(new RowReader<Long>() {
+ @Override
+ public Long read(Row row) throws SQLException {
+ throw new IllegalStateException("Unexpected error");
+ }
+ });
+ }
+ }.execute();
+
+ }
+
+ @Test
+ public void bad_parameterized_query() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final List<Long> ids = new ArrayList<>();
+ BaseDataChange change = new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ // parameter value is not set
+ ids.addAll(context.prepareSelect("select id from persons where id>=?").list(Select.LONG_READER));
+ }
+ };
+
+ thrown.expect(SQLException.class);
+
+ change.execute();
+ }
+
+ @Test
+ public void scroll() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final List<Long> ids = new ArrayList<>();
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareSelect("select id from persons order by id desc").scroll(new Select.RowHandler() {
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ ids.add(row.getNullableLong(1));
+ }
+ });
+ }
+ }.execute();
+ assertThat(ids).containsExactly(3L, 2L, 1L);
+ }
+
+ @Test
+ public void insert() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.prepareUpsert("insert into persons(id,login,age,enabled,coeff) values (?,?,?,?,?)")
+ .setLong(1, 10L)
+ .setString(2, "kurt")
+ .setInt(3, 27)
+ .setBoolean(4, true)
+ .setDouble(5, 2.2)
+ .execute().commit().close();
+ }
+ }.execute();
+
+ db.assertDbUnit(getClass(), "insert-result.xml", "persons");
+ }
+
+ @Test
+ public void batch_insert() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ Upsert upsert = context.prepareUpsert("insert into persons(id,login,age,enabled,coeff) values (?,?,?,?,?)");
+ upsert
+ .setLong(1, 10L)
+ .setString(2, "kurt")
+ .setInt(3, 27)
+ .setBoolean(4, true)
+ .setDouble(5, 2.2)
+ .addBatch();
+ upsert
+ .setLong(1, 11L)
+ .setString(2, "courtney")
+ .setInt(3, 25)
+ .setBoolean(4, false)
+ .setDouble(5, 2.3)
+ .addBatch();
+ upsert.execute().commit().close();
+ }
+ }.execute();
+
+ db.assertDbUnit(getClass(), "batch-insert-result.xml", "persons");
+ }
+
+ @Test
+ public void update_null() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ Upsert upsert = context.prepareUpsert("update persons set login=?,age=?,enabled=?, updated_at=?, coeff=? where id=?");
+ upsert
+ .setString(1, null)
+ .setInt(2, null)
+ .setBoolean(3, null)
+ .setDate(4, null)
+ .setDouble(5, null)
+ .setLong(6, 2L)
+ .execute()
+ .commit()
+ .close();
+ }
+ }.execute();
+
+ db.assertDbUnit(getClass(), "update-null-result.xml", "persons");
+ }
+
+ @Test
+ public void mass_batch_insert() throws Exception {
+ db.executeUpdateSql("truncate table persons");
+
+ final int count = BatchSession.MAX_BATCH_SIZE + 10;
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ Upsert upsert = context.prepareUpsert("insert into persons(id,login,age,enabled,coeff) values (?,?,?,?,?)");
+ for (int i = 0; i < count; i++) {
+ upsert
+ .setLong(1, 10L + i)
+ .setString(2, "login" + i)
+ .setInt(3, 10 + i)
+ .setBoolean(4, true)
+ .setDouble(4, i + 0.5)
+ .addBatch();
+ }
+ upsert.execute().commit().close();
+
+ }
+ }.execute();
+
+ assertThat(db.countRowsOfTable("persons")).isEqualTo(count);
+ }
+
+ @Test
+ public void scroll_and_update() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ final Upsert upsert = context.prepareUpsert("update persons set login=?, age=? where id=?");
+ context.prepareSelect("select id from persons").scroll(new Select.RowHandler() {
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ long id = row.getNullableLong(1);
+ upsert.setString(1, "login" + id).setInt(2, 10 + (int) id).setLong(3, id);
+ upsert.execute();
+ }
+ });
+ upsert.commit().close();
+ }
+ }.execute();
+
+ db.assertDbUnit(getClass(), "scroll-and-update-result.xml", "persons");
+ }
+
+ @Test
+ public void display_current_row_details_if_error_during_scroll() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Error during processing of row: [id=1]");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ final Upsert upsert = context.prepareUpsert("update persons set login=?, age=? where id=?");
+ context.prepareSelect("select id from persons").scroll(new Select.RowHandler() {
+ @Override
+ public void handle(Select.Row row) throws SQLException {
+ throw new IllegalStateException("Unexpected error");
+ }
+ });
+ upsert.commit().close();
+ }
+ }.execute();
+ }
+
+ @Test
+ public void mass_update() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id from persons where id>=?").setLong(1, 2L);
+ massUpdate.update("update persons set login=?, age=? where id=?");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ long id = row.getNullableLong(1);
+ update
+ .setString(1, "login" + id)
+ .setInt(2, 10 + (int) id)
+ .setLong(3, id);
+ return true;
+ }
+ });
+ }
+ }.execute();
+
+ db.assertDbUnit(getClass(), "mass-update-result.xml", "persons");
+ }
+
+ @Test
+ public void display_current_row_details_if_error_during_mass_update() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Error during processing of row: [id=2]");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id from persons where id>=?").setLong(1, 2L);
+ massUpdate.update("update persons set login=?, age=? where id=?");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ throw new IllegalStateException("Unexpected error");
+ }
+ });
+ }
+ }.execute();
+ }
+
+ @Test
+ public void mass_update_nothing() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id from persons where id>=?").setLong(1, 2L);
+ massUpdate.update("update persons set login=?, age=? where id=?");
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ return false;
+ }
+ });
+ }
+ }.execute();
+
+ db.assertDbUnit(getClass(), "persons.xml", "persons");
+ }
+
+ @Test
+ public void bad_mass_update() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ BaseDataChange change = new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.select("select id from persons where id>=?").setLong(1, 2L);
+ // update is not set
+ massUpdate.execute(new MassUpdate.Handler() {
+ @Override
+ public boolean handle(Select.Row row, SqlStatement update) throws SQLException {
+ return false;
+ }
+ });
+ }
+ };
+ try {
+ change.execute();
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessage("SELECT or UPDATE requests are not defined");
+ }
+ }
+
+ @Test
+ public void read_not_null_fields() throws Exception {
+ db.prepareDbUnit(getClass(), "persons.xml");
+
+ final List<Object[]> persons = new ArrayList<>();
+ new BaseDataChange(db.database()) {
+ @Override
+ public void execute(Context context) throws SQLException {
+ persons.addAll(context
+ .prepareSelect("select id,login,age,enabled,updated_at,coeff from persons where id=2")
+ .list(new Select.RowReader<Object[]>() {
+ @Override
+ public Object[] read(Select.Row row) throws SQLException {
+ return new Object[] {
+ // id, login, age, enabled
+ row.getLong(1),
+ row.getString(2),
+ row.getInt(3),
+ row.getBoolean(4),
+ row.getDate(5),
+ row.getDouble(6),
+ };
+ }
+ }));
+ }
+ }.execute();
+ assertThat(persons).hasSize(1);
+ assertThat(persons.get(0)[0]).isEqualTo(2L);
+ assertThat(persons.get(0)[1]).isEqualTo("emmerik");
+ assertThat(persons.get(0)[2]).isEqualTo(14);
+ assertThat(persons.get(0)[3]).isEqualTo(true);
+ assertThat(persons.get(0)[4]).isNotNull();
+ assertThat(persons.get(0)[5]).isEqualTo(5.2);
+ }
+
+ static class UserReader implements Select.RowReader<Object[]> {
+ @Override
+ public Object[] read(Select.Row row) throws SQLException {
+ return new Object[] {
+ // id, login, age, enabled
+ row.getNullableLong(1),
+ row.getNullableString(2),
+ row.getNullableInt(3),
+ row.getNullableBoolean(4),
+ row.getNullableDate(5),
+ row.getNullableDouble(6),
+ };
+ }
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/DropColumnsBuilderTest.java b/sonar-db/src/test/java/org/sonar/db/version/DropColumnsBuilderTest.java
new file mode 100644
index 00000000000..e7c6955f185
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/DropColumnsBuilderTest.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import org.junit.Test;
+import org.sonar.db.dialect.H2;
+import org.sonar.db.dialect.MsSql;
+import org.sonar.db.dialect.MySql;
+import org.sonar.db.dialect.Oracle;
+import org.sonar.db.dialect.PostgreSql;
+import org.sonar.db.version.DropColumnsBuilder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DropColumnsBuilderTest {
+
+ @Test
+ public void drop_columns_on_mysql() {
+ assertThat(new DropColumnsBuilder(new MySql(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP COLUMN date_in_ms, DROP COLUMN name");
+ }
+
+ @Test
+ public void drop_columns_on_oracle() {
+ assertThat(new DropColumnsBuilder(new Oracle(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP (date_in_ms, name)");
+ }
+
+ @Test
+ public void drop_columns_on_postgresql() {
+ assertThat(new DropColumnsBuilder(new PostgreSql(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP COLUMN date_in_ms, DROP COLUMN name");
+ }
+
+ @Test
+ public void drop_columns_on_mssql() {
+ assertThat(new DropColumnsBuilder(new MsSql(), "issues", "date_in_ms", "name")
+ .build()).isEqualTo("ALTER TABLE issues DROP COLUMN date_in_ms, name");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void fail_to_drop_columns_on_h2() {
+ new DropColumnsBuilder(new H2(), "issues", "date_in_ms", "name")
+ .build();
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
new file mode 100644
index 00000000000..6c3fb4cba25
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/MigrationStepModuleTest.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+import org.sonar.db.version.MigrationStepModule;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MigrationStepModuleTest {
+ @Test
+ public void verify_count_of_added_MigrationStep_types() {
+ ComponentContainer container = new ComponentContainer();
+ new MigrationStepModule().configure(container);
+ assertThat(container.size()).isEqualTo(38);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest.java
new file mode 100644
index 00000000000..eb291dda5c9
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest.java
@@ -0,0 +1,76 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v451;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AddMissingCustomRuleParametersMigrationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, AddMissingCustomRuleParametersMigrationTest.class, "schema.sql");
+
+ MigrationStep migration;
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table rules");
+ db.executeUpdateSql("truncate table rules_parameters");
+ migration = new AddMissingCustomRuleParametersMigrationStep(db.getDbClient(), system);
+ when(system.now()).thenReturn(DateUtils.parseDate("2014-10-09").getTime());
+ }
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "execute.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "execute-result.xml", "rules", "rules_parameters");
+ }
+
+ @Test
+ public void execute_when_custom_rule_have_no_parameter() throws Exception {
+ db.prepareDbUnit(getClass(), "execute_when_custom_rule_have_no_parameter.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "execute_when_custom_rule_have_no_parameter-result.xml", "rules", "rules_parameters");
+ }
+
+ @Test
+ public void no_changes() throws Exception {
+ db.prepareDbUnit(getClass(), "no_changes.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "no_changes.xml", "rules", "rules_parameters");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest.java
new file mode 100644
index 00000000000..00808bb8f0a
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest.java
@@ -0,0 +1,60 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v451;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class DeleteUnescapedActivitiesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, DeleteUnescapedActivitiesTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Test
+ public void execute() throws Exception {
+ migration = new DeleteUnescapedActivities(db.database());
+ db.prepareDbUnit(getClass(), "execute.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "execute-result.xml", "activities");
+ }
+
+ @Test
+ public void is_unescaped() {
+ assertThat(DeleteUnescapedActivities.isUnescaped(
+ "ruleKey=findbugs:PT_RELATIVE_PATH_TRAVERSAL;profileKey=java-findbugs-74105;severity=MAJOR;" +
+ "key=java-findbugs-74105:findbugs:PT_RELATIVE_PATH_TRAVERSAL"))
+ .isFalse();
+ assertThat(DeleteUnescapedActivities.isUnescaped(null)).isFalse();
+ assertThat(DeleteUnescapedActivities.isUnescaped("")).isFalse();
+ assertThat(DeleteUnescapedActivities.isUnescaped("foo=bar")).isFalse();
+ assertThat(DeleteUnescapedActivities.isUnescaped("param_xpath=/foo/bar")).isFalse();
+
+ assertThat(DeleteUnescapedActivities.isUnescaped("param_xpath=/foo/bar;foo;ruleKey=S001")).isTrue();
+ assertThat(DeleteUnescapedActivities.isUnescaped("param_xpath=/foo=foo;ruleKey=S001")).isTrue();
+
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/FeedFileSourcesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/FeedFileSourcesTest.java
new file mode 100644
index 00000000000..1ac8362180a
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/FeedFileSourcesTest.java
@@ -0,0 +1,319 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.dbutils.DbUtils;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedFileSourcesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedFileSourcesTest.class, "schema.sql");
+
+ private static final long NOW = 1414770242000L;
+
+ FeedFileSources migration;
+
+ System2 system;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table metrics");
+ db.executeUpdateSql("truncate table snapshots");
+ db.executeUpdateSql("truncate table snapshot_sources");
+ db.executeUpdateSql("truncate table projects");
+ db.executeUpdateSql("truncate table project_measures");
+ db.executeUpdateSql("truncate table file_sources");
+
+ system = mock(System2.class);
+ when(system.now()).thenReturn(NOW);
+ migration = new FeedFileSources(db.database(), system);
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate_sources_with_no_scm_no_coverage() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ db.executeUpdateSql("insert into snapshot_sources " +
+ "(snapshot_id, data, updated_at) " +
+ "values " +
+ "(6, 'class Foo {\r\n // Empty\r\n}\r\n', '2014-10-31 16:44:02.000')");
+
+ db.executeUpdateSql("insert into snapshot_sources " +
+ "(snapshot_id, data, updated_at) " +
+ "values " +
+ "(7, '', '2014-10-31 16:44:02.000')");
+
+ migration.execute();
+
+ List<Map<String, Object>> results = db.select("select project_uuid as \"projectUuid\", file_uuid as \"fileUuid\", created_at as \"createdAt\", " +
+ "updated_at as \"updatedAt\", data as \"data\", data as \"data\", line_hashes as \"lineHashes\", data_hash as \"dataHash\" from file_sources");
+ assertThat(results).hasSize(2);
+
+ assertThat(results.get(0).get("projectUuid")).isEqualTo("uuid-MyProject");
+ assertThat(results.get(0).get("fileUuid")).isEqualTo("uuid-Migrated.xoo");
+ assertThat(results.get(0).get("data")).isEqualTo("");
+ assertThat(results.get(0).get("lineHashes")).isEqualTo("");
+ assertThat(results.get(0).get("dataHash")).isEqualTo("");
+ assertThat(results.get(0).get("updatedAt")).isEqualTo(NOW);
+ assertThat(results.get(0).get("createdAt")).isEqualTo(1416238020000L);
+
+ assertThat(results.get(1).get("projectUuid")).isEqualTo("uuid-MyProject");
+ assertThat(results.get(1).get("fileUuid")).isEqualTo("uuid-MyFile.xoo");
+ assertThat(results.get(1).get("data")).isEqualTo(",,,,,,,,,,,,,,,class Foo {\r\n,,,,,,,,,,,,,,, // Empty\r\n,,,,,,,,,,,,,,,}\r\n,,,,,,,,,,,,,,,\r\n");
+ assertThat(results.get(1).get("lineHashes")).isEqualTo("6a19ce786467960a3a9b0d26383a464a\naab2dbc5fdeaa80b050b1d049ede357c\ncbb184dd8e05c9709e5dcaedaa0495cf\n\n");
+ assertThat(results.get(1).get("dataHash")).isEqualTo("");
+ assertThat(formatLongDate((long) results.get(1).get("updatedAt")).toString()).startsWith("2014-10-31");
+ assertThat(results.get(1).get("createdAt")).isEqualTo(NOW);
+ }
+
+ @Test
+ public void migrate_sources_with_scm_and_coverage_in_text_value() throws Exception {
+ migrate_sources_with_scm_and_coverage_in("text_value");
+ }
+
+ @Test
+ public void migrate_sources_with_scm_and_coverage_in_measure_data() throws Exception {
+ migrate_sources_with_scm_and_coverage_in("measure_data");
+ }
+
+ private void migrate_sources_with_scm_and_coverage_in(String columnName) throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ Connection connection = null;
+ try {
+ connection = db.openConnection();
+
+ connection.prepareStatement("insert into snapshot_sources " +
+ "(snapshot_id, data, updated_at) " +
+ "values " +
+ "(6, 'class Foo {\r\n // Empty\r\n}\r\n', '2014-10-31 16:44:02.000')")
+ .executeUpdate();
+
+ db.executeUpdateSql("insert into snapshot_sources " +
+ "(snapshot_id, data, updated_at) " +
+ "values " +
+ "(7, '', '2014-10-31 16:44:02.000')");
+
+ PreparedStatement revisionStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(1, 6, ?)");
+ revisionStmt.setBytes(1, "1=aef12a;2=abe465;3=afb789;4=afb789".getBytes(StandardCharsets.UTF_8));
+ revisionStmt.executeUpdate();
+
+ PreparedStatement authorStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(2, 6, ?)");
+ authorStmt.setBytes(1, "1=alice;2=bob;3=carol;4=carol".getBytes(StandardCharsets.UTF_8));
+ authorStmt.executeUpdate();
+
+ PreparedStatement dateStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(3, 6, ?)");
+ dateStmt.setBytes(1, "1=2014-04-25T12:34:56+0100;2=2014-07-25T12:34:56+0100;3=2014-03-23T12:34:56+0100;4=2014-03-23T12:34:56+0100".getBytes(StandardCharsets.UTF_8));
+ dateStmt.executeUpdate();
+
+ PreparedStatement utHitsStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(4, 6, ?)");
+ utHitsStmt.setBytes(1, "1=1;3=0".getBytes(StandardCharsets.UTF_8));
+ utHitsStmt.executeUpdate();
+
+ PreparedStatement utCondStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(5, 6, ?)");
+ utCondStmt.setBytes(1, "1=4".getBytes(StandardCharsets.UTF_8));
+ utCondStmt.executeUpdate();
+
+ PreparedStatement utCoveredCondStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(6, 6, ?)");
+ utCoveredCondStmt.setBytes(1, "1=2".getBytes(StandardCharsets.UTF_8));
+ utCoveredCondStmt.executeUpdate();
+
+ PreparedStatement itHitsStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(7, 6, ?)");
+ itHitsStmt.setBytes(1, "1=2;3=0".getBytes(StandardCharsets.UTF_8));
+ itHitsStmt.executeUpdate();
+
+ PreparedStatement itCondStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(8, 6, ?)");
+ itCondStmt.setBytes(1, "1=5".getBytes(StandardCharsets.UTF_8));
+ itCondStmt.executeUpdate();
+
+ PreparedStatement itCoveredCondStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(9, 6, ?)");
+ itCoveredCondStmt.setBytes(1, "1=3".getBytes(StandardCharsets.UTF_8));
+ itCoveredCondStmt.executeUpdate();
+
+ PreparedStatement overallHitsStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(10, 6, ?)");
+ overallHitsStmt.setBytes(1, "1=3;3=0".getBytes(StandardCharsets.UTF_8));
+ overallHitsStmt.executeUpdate();
+
+ PreparedStatement overallCondStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(11, 6, ?)");
+ overallCondStmt.setBytes(1, "1=6".getBytes(StandardCharsets.UTF_8));
+ overallCondStmt.executeUpdate();
+
+ PreparedStatement overallCoveredCondStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(12, 6, ?)");
+ overallCoveredCondStmt.setBytes(1, "1=4".getBytes(StandardCharsets.UTF_8));
+ overallCoveredCondStmt.executeUpdate();
+
+ PreparedStatement duplicationDataStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, " + columnName + ") " +
+ "values " +
+ "(13, 6, ?)");
+ duplicationDataStmt
+ .setBytes(
+ 1,
+ "<duplications><g><b s=\"1\" l=\"1\" r=\"MyProject:src/main/xoo/prj/MyFile.xoo\"/><b s=\"2\" l=\"1\" r=\"MyProject:src/main/xoo/prj/MyFile.xoo\"/><b s=\"3\" l=\"1\" r=\"MyProject:src/main/xoo/prj/AnotherFile.xoo\"/></g></duplications>"
+ .getBytes(StandardCharsets.UTF_8));
+ duplicationDataStmt.executeUpdate();
+ } finally {
+ DbUtils.commitAndCloseQuietly(connection);
+ }
+
+ migration.execute();
+
+ List<Map<String, Object>> results = db.select("select project_uuid as \"projectUuid\", file_uuid as \"fileUuid\", created_at as \"createdAt\", " +
+ "updated_at as \"updatedAt\", data as \"data\", data as \"data\", line_hashes as \"lineHashes\", data_hash as \"dataHash\" from file_sources");
+ assertThat(results).hasSize(2);
+
+ assertThat(results.get(0).get("projectUuid")).isEqualTo("uuid-MyProject");
+ assertThat(results.get(0).get("fileUuid")).isEqualTo("uuid-Migrated.xoo");
+ assertThat(results.get(0).get("data")).isEqualTo("");
+ assertThat(results.get(0).get("lineHashes")).isEqualTo("");
+ assertThat(results.get(0).get("dataHash")).isEqualTo("");
+ assertThat(results.get(0).get("updatedAt")).isEqualTo(NOW);
+ assertThat(results.get(0).get("createdAt")).isEqualTo(1416238020000L);
+
+ assertThat(results.get(1).get("projectUuid")).isEqualTo("uuid-MyProject");
+ assertThat(results.get(1).get("fileUuid")).isEqualTo("uuid-MyFile.xoo");
+ assertThat(results.get(1).get("data")).isEqualTo(
+ "aef12a,alice,2014-04-25T12:34:56+0100,1,4,2,2,5,3,3,6,4,,,1,class Foo {\r\nabe465,bob,2014-07-25T12:34:56+0100,,,,,,,,,,,,2, " +
+ "// Empty\r\nafb789,carol,2014-03-23T12:34:56+0100,0,,,0,,,0,,,,,,}\r\nafb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,,,,\r\n");
+ assertThat(results.get(1).get("lineHashes")).isEqualTo("6a19ce786467960a3a9b0d26383a464a\naab2dbc5fdeaa80b050b1d049ede357c\ncbb184dd8e05c9709e5dcaedaa0495cf\n\n");
+ assertThat(results.get(1).get("dataHash")).isEqualTo("");
+ assertThat(formatLongDate((long) results.get(1).get("updatedAt")).toString()).startsWith("2014-10-31");
+ assertThat(results.get(1).get("createdAt")).isEqualTo(NOW);
+ }
+
+ @Test
+ public void migrate_sources_with_invalid_duplication() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ Connection connection = null;
+ try {
+ connection = db.openConnection();
+
+ connection.prepareStatement("insert into snapshot_sources " +
+ "(snapshot_id, data, updated_at) " +
+ "values " +
+ "(6, 'class Foo {\r\n // Empty\r\n}\r\n', '2014-10-31 16:44:02.000')")
+ .executeUpdate();
+
+ db.executeUpdateSql("insert into snapshot_sources " +
+ "(snapshot_id, data, updated_at) " +
+ "values " +
+ "(7, '', '2014-10-31 16:44:02.000')");
+
+ PreparedStatement duplicationDataStmt = connection.prepareStatement("insert into project_measures " +
+ "(metric_id, snapshot_id, text_value) " +
+ "values " +
+ "(13, 6, ?)");
+ duplicationDataStmt
+ .setBytes(
+ 1,
+ "<duplications><g><b s=\"1\" l=\"1\" r=\"MyProject:src/main/xoo/prj/MyFile.xoo\"/><b s=\"2\" l=\"1\" r=\"MyProject:src/main/xoo/prj/MyFile.xoo\"/><b s=\"3\" l=\"1\" r=\"MyProject:src/main/xoo/prj/AnotherFile.xoo\"/"
+ .getBytes(StandardCharsets.UTF_8));
+ duplicationDataStmt.executeUpdate();
+ } finally {
+ DbUtils.commitAndCloseQuietly(connection);
+ }
+
+ migration.execute();
+
+ // db.assertDbUnit(getClass(), "after-with-invalid-duplication.xml", "file_sources");
+
+ List<Map<String, Object>> results = db.select("select project_uuid as \"projectUuid\", file_uuid as \"fileUuid\", created_at as \"createdAt\", " +
+ "updated_at as \"updatedAt\", data as \"data\", data as \"data\", line_hashes as \"lineHashes\", data_hash as \"dataHash\" from file_sources");
+ assertThat(results).hasSize(2);
+
+ assertThat(results.get(0).get("projectUuid")).isEqualTo("uuid-MyProject");
+ assertThat(results.get(0).get("fileUuid")).isEqualTo("uuid-Migrated.xoo");
+ assertThat(results.get(0).get("data")).isEqualTo("");
+ assertThat(results.get(0).get("lineHashes")).isEqualTo("");
+ assertThat(results.get(0).get("dataHash")).isEqualTo("");
+ assertThat(results.get(0).get("updatedAt")).isEqualTo(NOW);
+ assertThat(results.get(0).get("createdAt")).isEqualTo(1416238020000L);
+
+ assertThat(results.get(1).get("projectUuid")).isEqualTo("uuid-MyProject");
+ assertThat(results.get(1).get("fileUuid")).isEqualTo("uuid-MyFile.xoo");
+ assertThat(results.get(1).get("data")).isEqualTo(",,,,,,,,,,,,,,,class Foo {\r\n,,,,,,,,,,,,,,, // Empty\r\n,,,,,,,,,,,,,,,}\r\n,,,,,,,,,,,,,,,\r\n");
+ assertThat(results.get(1).get("lineHashes")).isEqualTo("6a19ce786467960a3a9b0d26383a464a\naab2dbc5fdeaa80b050b1d049ede357c\ncbb184dd8e05c9709e5dcaedaa0495cf\n\n");
+ assertThat(results.get(1).get("dataHash")).isEqualTo("");
+ assertThat(formatLongDate((long) results.get(1).get("updatedAt")).toString()).startsWith("2014-10-31");
+ assertThat(results.get(1).get("createdAt")).isEqualTo(NOW);
+ }
+
+ private String formatLongDate(long dateInMs) {
+ return DateUtils.formatDateTime(DateUtils.longToDate(dateInMs));
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/FeedIssueLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/FeedIssueLongDatesTest.java
new file mode 100644
index 00000000000..5969a262803
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/FeedIssueLongDatesTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedIssueLongDatesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedIssueLongDatesTest.class, "schema.sql");
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1500000000000L);
+ MigrationStep migration = new FeedIssueLongDates(db.database(), system);
+ migration.execute();
+
+ int count = db.countSql("select count(*) from issues where created_at_ms is not null and updated_at_ms is not null");
+ assertThat(count).isEqualTo(3);
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/FileSourceDtoTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/FileSourceDtoTest.java
new file mode 100644
index 00000000000..ebb137512cd
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/FileSourceDtoTest.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import org.junit.Test;
+import org.sonar.db.version.v50.FileSourceDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FileSourceDtoTest {
+
+ @Test
+ public void checksumOfBlankLine() {
+ assertThat(FileSourceDto.lineChecksum("")).isEmpty();
+ assertThat(FileSourceDto.lineChecksum(" \r\n")).isEmpty();
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest.java
new file mode 100644
index 00000000000..4226635344a
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class InsertProjectsAuthorizationUpdatedAtMigrationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, InsertProjectsAuthorizationUpdatedAtMigrationTest.class, "schema.sql");
+
+ MigrationStep migration;
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table projects");
+ migration = new InsertProjectsAuthorizationUpdatedAtMigrationStep(db.database(), system);
+ when(system.now()).thenReturn(123456789L);
+ }
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "after.xml", "projects");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest.java
new file mode 100644
index 00000000000..4de215aae6f
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest.java
@@ -0,0 +1,324 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v50;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PopulateProjectsUuidColumnsMigrationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, PopulateProjectsUuidColumnsMigrationTest.class, "schema.sql");
+
+ DbClient dbClient = db.getDbClient();
+
+ Migration50Mapper mapper;
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table projects");
+ db.executeUpdateSql("truncate table snapshots");
+ mapper = db.getSession().getMapper(Migration50Mapper.class);
+ migration = new PopulateProjectsUuidColumnsMigrationStep(dbClient);
+ }
+
+ @Test
+ public void migrate_components() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_components.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isNotNull();
+ assertThat(root.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+
+ Component module = mapper.selectComponentByKey("org.struts:struts-core");
+ assertThat(module.getUuid()).isNotNull();
+ assertThat(module.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(module.getModuleUuid()).isEqualTo(root.getUuid());
+ assertThat(module.getModuleUuidPath()).isEqualTo(root.getUuid());
+
+ Component subModule = mapper.selectComponentByKey("org.struts:struts-db");
+ assertThat(subModule.getUuid()).isNotNull();
+ assertThat(subModule.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(subModule.getModuleUuid()).isEqualTo(module.getUuid());
+ assertThat(subModule.getModuleUuidPath()).isEqualTo(root.getUuid() + "." + module.getUuid());
+
+ Component directory = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts");
+ assertThat(directory.getUuid()).isNotNull();
+ assertThat(directory.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(directory.getModuleUuid()).isEqualTo(subModule.getUuid());
+ assertThat(directory.getModuleUuidPath()).isEqualTo(root.getUuid() + "." + module.getUuid() + "." + subModule.getUuid());
+
+ Component file = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java");
+ assertThat(file.getUuid()).isNotNull();
+ assertThat(file.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(file.getModuleUuid()).isEqualTo(subModule.getUuid());
+ assertThat(file.getModuleUuidPath()).isEqualTo(root.getUuid() + "." + module.getUuid() + "." + subModule.getUuid());
+
+ // Verify that each generated uuid is unique
+ assertThat(ImmutableSet.of(root.getUuid(), module.getUuid(), subModule.getUuid(), directory.getUuid(), file.getUuid())).hasSize(5);
+ }
+
+ @Test
+ public void not_migrate_already_migrated_components() throws Exception {
+ db.prepareDbUnit(getClass(), "not_migrate_already_migrated_components.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isEqualTo("ABCD");
+ assertThat(root.getProjectUuid()).isEqualTo("ABCD");
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+
+ Component module = mapper.selectComponentByKey("org.struts:struts-core");
+ assertThat(module.getUuid()).isEqualTo("BCDE");
+ assertThat(module.getProjectUuid()).isEqualTo("ABCD");
+ assertThat(module.getModuleUuid()).isEqualTo("ABCD");
+ assertThat(module.getModuleUuidPath()).isEqualTo("ABCD");
+
+ Component subModule = mapper.selectComponentByKey("org.struts:struts-db");
+ assertThat(subModule.getUuid()).isNotNull();
+ assertThat(subModule.getProjectUuid()).isEqualTo("ABCD");
+ assertThat(subModule.getModuleUuid()).isEqualTo("BCDE");
+ assertThat(subModule.getModuleUuidPath()).isEqualTo("ABCD.BCDE");
+
+ Component directory = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts");
+ assertThat(directory.getUuid()).isNotNull();
+ assertThat(directory.getProjectUuid()).isEqualTo("ABCD");
+ assertThat(directory.getModuleUuid()).isEqualTo(subModule.getUuid());
+ assertThat(directory.getModuleUuidPath()).isEqualTo("ABCD.BCDE." + subModule.getUuid());
+
+ Component file = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java");
+ assertThat(file.getUuid()).isNotNull();
+ assertThat(file.getProjectUuid()).isEqualTo("ABCD");
+ assertThat(file.getModuleUuid()).isEqualTo(subModule.getUuid());
+ assertThat(file.getModuleUuidPath()).isEqualTo("ABCD.BCDE." + subModule.getUuid());
+
+ Component removedFile = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext2.java");
+ assertThat(removedFile.getUuid()).isEqualTo("DCBA");
+ assertThat(removedFile.getProjectUuid()).isEqualTo("ABCD");
+ assertThat(removedFile.getModuleUuid()).isEqualTo("BCDE");
+ assertThat(removedFile.getModuleUuidPath()).isEqualTo("ABCD.BCDE");
+ }
+
+ @Test
+ public void migrate_disable_components() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_disable_components.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isNotNull();
+
+ Component module = mapper.selectComponentByKey("org.struts:struts-core");
+ assertThat(module.getUuid()).isNotNull();
+ assertThat(module.getProjectUuid()).isEqualTo(root.getUuid());
+ // Module and module path will always be null for removed components
+ assertThat(module.getModuleUuid()).isNull();
+ assertThat(module.getModuleUuidPath()).isEmpty();
+
+ Component subModule = mapper.selectComponentByKey("org.struts:struts-db");
+ assertThat(subModule.getUuid()).isNotNull();
+ assertThat(subModule.getProjectUuid()).isEqualTo(root.getUuid());
+ // Module and module path will always be null for removed components
+ assertThat(subModule.getModuleUuid()).isNull();
+ assertThat(subModule.getModuleUuidPath()).isEmpty();
+
+ Component directory = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts");
+ assertThat(directory.getUuid()).isNotNull();
+ assertThat(directory.getProjectUuid()).isEqualTo(root.getUuid());
+ // Module and module path will always be null for removed components
+ assertThat(directory.getModuleUuid()).isNull();
+ assertThat(directory.getModuleUuidPath()).isEmpty();
+
+ Component file = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java");
+ assertThat(file.getUuid()).isNotNull();
+ assertThat(file.getProjectUuid()).isEqualTo(root.getUuid());
+ // Module and module path will always be null for removed components
+ assertThat(file.getModuleUuid()).isNull();
+ assertThat(file.getModuleUuidPath()).isEmpty();
+ }
+
+ @Test
+ public void migrate_provisioned_project() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_provisioned_project.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isNotNull();
+ assertThat(root.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+ }
+
+ @Test
+ public void migrate_library() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_library.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component root = mapper.selectComponentByKey("org.hamcrest:hamcrest-library");
+ assertThat(root.getUuid()).isNotNull();
+ assertThat(root.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+ }
+
+ @Test
+ public void migrate_view() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_view.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component view = mapper.selectComponentByKey("view");
+ assertThat(view.getUuid()).isNotNull();
+ assertThat(view.getProjectUuid()).isEqualTo(view.getUuid());
+ assertThat(view.getModuleUuid()).isNull();
+ assertThat(view.getModuleUuidPath()).isEmpty();
+
+ Component subView = mapper.selectComponentByKey("subView");
+ assertThat(subView.getUuid()).isNotNull();
+ assertThat(subView.getProjectUuid()).isEqualTo(view.getUuid());
+ assertThat(subView.getModuleUuid()).isEqualTo(view.getUuid());
+ assertThat(subView.getModuleUuidPath()).isEqualTo(view.getUuid());
+
+ Component techProject = mapper.selectComponentByKey("vieworg.struts:struts");
+ assertThat(techProject.getUuid()).isNotNull();
+ assertThat(techProject.getProjectUuid()).isEqualTo(view.getUuid());
+ assertThat(techProject.getModuleUuid()).isEqualTo(subView.getUuid());
+ assertThat(techProject.getModuleUuidPath()).isEqualTo(view.getUuid() + "." + subView.getUuid());
+ }
+
+ @Test
+ public void migrate_developer() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_developer.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ Component dev = mapper.selectComponentByKey("DEV:developer@company.net");
+ assertThat(dev.getUuid()).isNotNull();
+ assertThat(dev.getProjectUuid()).isEqualTo(dev.getUuid());
+ assertThat(dev.getModuleUuid()).isNull();
+ assertThat(dev.getModuleUuidPath()).isEmpty();
+
+ Component techDev = mapper.selectComponentByKey("DEV:developer@company.net:org.struts:struts");
+ assertThat(techDev.getUuid()).isNotNull();
+ assertThat(techDev.getProjectUuid()).isEqualTo(dev.getUuid());
+ assertThat(techDev.getModuleUuid()).isEqualTo(dev.getUuid());
+ assertThat(techDev.getModuleUuidPath()).isEqualTo(dev.getUuid());
+ }
+
+ @Test
+ public void migrate_components_without_uuid() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate_components_without_uuid.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ // Root project migrated
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isNotNull();
+ assertThat(root.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+
+ // Module with a snapshot having no islast=true
+ Component module = mapper.selectComponentByKey("org.struts:struts-core");
+ assertThat(module.getUuid()).isNotNull();
+ assertThat(module.getProjectUuid()).isEqualTo(module.getUuid());
+ assertThat(module.getModuleUuid()).isNull();
+ assertThat(module.getModuleUuidPath()).isEmpty();
+
+ // File linked on a no more existing project
+ Component file = mapper.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java");
+ assertThat(file.getUuid()).isNotNull();
+ assertThat(file.getProjectUuid()).isEqualTo(file.getUuid());
+ assertThat(file.getModuleUuid()).isNull();
+ assertThat(file.getModuleUuidPath()).isEmpty();
+ }
+
+ @Test
+ public void not_fail_when_module_has_no_root_id() throws Exception {
+ db.prepareDbUnit(getClass(), "not_fail_when_module_has_no_root_id.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ // Root project migrated
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isNotNull();
+ assertThat(root.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+
+ // The module without uuid will be migrated as a standalone component
+ Component module = mapper.selectComponentByKey("org.struts:struts-core");
+ assertThat(module.getUuid()).isNotNull();
+ assertThat(module.getProjectUuid()).isEqualTo(module.getUuid());
+ assertThat(module.getModuleUuid()).isNull();
+ assertThat(module.getModuleUuidPath()).isEmpty();
+ }
+
+ @Test
+ public void not_fail_when_project_has_two_active_snapshots() throws Exception {
+ db.prepareDbUnit(getClass(), "not_fail_when_project_has_two_active_snapshots.xml");
+
+ migration.execute();
+ db.getSession().commit();
+
+ // Root project migrated
+ Component root = mapper.selectComponentByKey("org.struts:struts");
+ assertThat(root.getUuid()).isNotNull();
+ assertThat(root.getProjectUuid()).isEqualTo(root.getUuid());
+ assertThat(root.getModuleUuid()).isNull();
+ assertThat(root.getModuleUuidPath()).isEmpty();
+
+ // The module linked on second active snapshot should be migrated a standalone component
+ Component module = mapper.selectComponentByKey("org.struts:struts-core");
+ assertThat(module.getUuid()).isNotNull();
+ assertThat(module.getProjectUuid()).isEqualTo(module.getUuid());
+ assertThat(module.getModuleUuid()).isNull();
+ assertThat(module.getModuleUuidPath()).isEmpty();
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest.java
new file mode 100644
index 00000000000..bf96e3af226
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RemoveSortFieldFromIssueFiltersMigrationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, RemoveSortFieldFromIssueFiltersMigrationTest.class, "schema.sql");
+
+ MigrationStep migration;
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table issue_filters");
+ migration = new RemoveSortFieldFromIssueFiltersMigrationStep(db.database(), system);
+ when(system.now()).thenReturn(DateUtils.parseDate("2014-10-29").getTime());
+ }
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "execute.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "execute-result.xml", "issue_filters");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest.java b/sonar-db/src/test/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest.java
new file mode 100644
index 00000000000..e51c5fd2b32
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v50;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ReplaceIssueFiltersProjectKeyByUuidTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, ReplaceIssueFiltersProjectKeyByUuidTest.class, "schema.sql");
+
+ MigrationStep migration;
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table issue_filters");
+ migration = new ReplaceIssueFiltersProjectKeyByUuid(db.database(), system);
+ when(system.now()).thenReturn(DateUtils.parseDate("2014-10-29").getTime());
+ }
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "execute.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "execute-result.xml", "issue_filters");
+ }
+
+ @Test
+ public void do_not_execute_if_already_migrated() throws Exception {
+ db.prepareDbUnit(getClass(), "do_not_execute_if_already_migrated.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "do_not_execute_if_already_migrated-result.xml", "issue_filters");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/AddIssuesColumnsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/AddIssuesColumnsTest.java
new file mode 100644
index 00000000000..40683d1ebb4
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/AddIssuesColumnsTest.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import java.sql.Types;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class AddIssuesColumnsTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, AddIssuesColumnsTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ migration = new AddIssuesColumns(db.database());
+ }
+
+ @Test
+ public void update_columns() throws Exception {
+ migration.execute();
+
+ db.assertColumnDefinition("issues", "issue_creation_date_ms", Types.BIGINT, null);
+ db.assertColumnDefinition("issues", "issue_update_date_ms", Types.BIGINT, null);
+ db.assertColumnDefinition("issues", "issue_close_date_ms", Types.BIGINT, null);
+ db.assertColumnDefinition("issues", "tags", Types.VARCHAR, 4000);
+ db.assertColumnDefinition("issues", "component_uuid", Types.VARCHAR, 50);
+ db.assertColumnDefinition("issues", "project_uuid", Types.VARCHAR, 50);
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/AddNewCharacteristicsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/AddNewCharacteristicsTest.java
new file mode 100644
index 00000000000..cfc43d2246d
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/AddNewCharacteristicsTest.java
@@ -0,0 +1,138 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.MessageException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static junit.framework.TestCase.fail;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AddNewCharacteristicsTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, AddNewCharacteristicsTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table characteristics");
+
+ when(system.now()).thenReturn(DateUtils.parseDate("2015-02-15").getTime());
+
+ migration = new AddNewCharacteristics(db.database(), system);
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "migrate-result.xml", "characteristics");
+ }
+
+ @Test
+ public void do_nothing_when_already_migrated() throws Exception {
+ db.prepareDbUnit(getClass(), "do_nothing_when_already_migrated.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "do_nothing_when_already_migrated.xml", "characteristics");
+ }
+
+ @Test
+ public void do_nothing_when_no_characteristics() throws Exception {
+ db.prepareDbUnit(getClass(), "empty.xml");
+ migration.execute();
+ assertThat(db.countRowsOfTable("characteristics")).isEqualTo(0);
+ }
+
+ @Test
+ public void insert_usability_at_the_top_if_security_does_exists() throws Exception {
+ db.prepareDbUnit(getClass(), "insert_usability_at_the_top_if_security_does_exists.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "insert_usability_at_the_top_if_security_does_exists-result.xml", "characteristics");
+ }
+
+ @Test
+ public void update_usability_order_if_already_exists() throws Exception {
+ db.prepareDbUnit(getClass(), "update_usability_if_already_exists.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "update_usability_if_already_exists-result.xml", "characteristics");
+ }
+
+ @Test
+ public void fail_if_usability_exists_as_sub_characteristic() {
+ db.prepareDbUnit(getClass(), "fail_if_usability_exists_as_sub_characteristic.xml");
+
+ try {
+ migration.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(MessageException.class).hasMessage(
+ "'Usability' must be a characteristic. Please restore your DB backup, start the previous version of SonarQube " +
+ "and update your SQALE model to fix this issue before trying again to run the migration.");
+ }
+ }
+
+ @Test
+ public void fail_if_compliance_already_exists_as_characteristic() {
+ db.prepareDbUnit(getClass(), "fail_if_compliance_already_exists_as_characteristic.xml");
+
+ try {
+ migration.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(MessageException.class).hasMessage(
+ "'Compliance' must be a sub-characteristic. Please restore your DB backup, start the previous version of SonarQube " +
+ "and update your SQALE model to fix this issue before trying again to run the migration.");
+ }
+ }
+
+ @Test
+ public void fail_if_compliance_already_exists_under_wrong_characteristic() {
+ db.prepareDbUnit(getClass(), "fail_if_compliance_already_exists_under_wrong_characteristic.xml");
+
+ try {
+ migration.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(MessageException.class).hasMessage(
+ "'Reusability Compliance' must be defined under 'Reusability'. Please restore your DB backup, start the previous version of SonarQube " +
+ "and update your SQALE model to fix this issue before trying again to run the migration.");
+ }
+ }
+
+ @Test
+ public void not_fail_if_some_deprecated_requirements_still_exists_in_db() throws Exception {
+ db.prepareDbUnit(getClass(), "not_fail_if_some_deprecated_requirements_still_exists_in_db.xml");
+ migration.execute();
+ db.assertDbUnit(getClass(), "not_fail_if_some_deprecated_requirements_still_exists_in_db.xml", "characteristics");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest.java
new file mode 100644
index 00000000000..c0345ad2862
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest.java
@@ -0,0 +1,102 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.user.UserDto;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class CopyScmAccountsFromAuthorsToUsersTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, CopyScmAccountsFromAuthorsToUsersTest.class, "schema.sql");
+
+ MigrationStep migration;
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table authors");
+ db.executeUpdateSql("truncate table users");
+ migration = new CopyScmAccountsFromAuthorsToUsers(db.database(), system);
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+ Long oldDate = 1500000000000L;
+ Long updatedDate = 2000000000000L;
+ when(system.now()).thenReturn(updatedDate);
+
+ migration.execute();
+
+ User simon = getUserByLogin("simon");
+ assertThat(simon.scmAccounts).isEqualTo(UserDto.SCM_ACCOUNTS_SEPARATOR + "Simon B" + UserDto.SCM_ACCOUNTS_SEPARATOR + "simon@codehaus.org" + UserDto.SCM_ACCOUNTS_SEPARATOR);
+ assertThat(simon.updatedAt).isEqualTo(updatedDate);
+
+ User fabrice = getUserByLogin("fabrice");
+ assertThat(fabrice.scmAccounts).isEqualTo(UserDto.SCM_ACCOUNTS_SEPARATOR + "fab" + UserDto.SCM_ACCOUNTS_SEPARATOR);
+ assertThat(fabrice.updatedAt).isEqualTo(updatedDate);
+
+ assertThat(getUserByLogin("julien").updatedAt).isEqualTo(oldDate);
+ assertThat(getUserByLogin("jb").updatedAt).isEqualTo(oldDate);
+ assertThat(getUserByLogin("disable").updatedAt).isEqualTo(oldDate);
+ assertThat(getUserByLogin("teryk").updatedAt).isEqualTo(oldDate);
+ assertThat(getUserByLogin("teryk2").updatedAt).isEqualTo(oldDate);
+ }
+
+ @Test
+ public void nothing_to_migrate_when_no_authors() throws Exception {
+ db.prepareDbUnit(getClass(), "no_authors.xml");
+ Long oldDate = 1500000000000L;
+ Long updatedDate = 2000000000000L;
+ when(system.now()).thenReturn(updatedDate);
+
+ migration.execute();
+
+ assertThat(db.countSql("SELECT count(*) FROM USERS WHERE updated_at=" + updatedDate)).isEqualTo(0);
+ assertThat(db.countSql("SELECT count(*) FROM USERS WHERE updated_at=" + oldDate)).isEqualTo(7);
+ }
+
+ private User getUserByLogin(String login) {
+ return new User(db.selectFirst("SELECT u.scm_Accounts as \"scmAccounts\", u.updated_at as \"updatedAt\" FROM users u WHERE u.login='" + login + "'"));
+ }
+
+ private static class User {
+ String scmAccounts;
+ Long updatedAt;
+
+ User(Map<String, Object> map) {
+ scmAccounts = (String) map.get("scmAccounts");
+ updatedAt = (Long) map.get("updatedAt");
+ }
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/DropIssuesColumnsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/DropIssuesColumnsTest.java
new file mode 100644
index 00000000000..3971e9c1838
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/DropIssuesColumnsTest.java
@@ -0,0 +1,52 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.db.Database;
+import org.sonar.db.dialect.PostgreSql;
+import org.sonar.db.version.v51.DropIssuesColumns;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DropIssuesColumnsTest {
+
+ DropIssuesColumns migration;
+
+ Database database;
+
+ @Before
+ public void setUp() {
+ database = mock(Database.class);
+ migration = new DropIssuesColumns(database);
+ }
+
+ @Test
+ public void generate_sql_on_postgresql() {
+ when(database.getDialect()).thenReturn(new PostgreSql());
+ assertThat(migration.generateSql()).isEqualTo(
+ "ALTER TABLE issues DROP COLUMN issue_creation_date, DROP COLUMN issue_update_date, DROP COLUMN issue_close_date, DROP COLUMN component_id, DROP COLUMN root_component_id"
+ );
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest.java
new file mode 100644
index 00000000000..c3e75f1fc75
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedAnalysisReportsLongDatesTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedAnalysisReportsLongDatesTest.class, "schema.sql");
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1500000000000L);
+ MigrationStep migration = new FeedAnalysisReportsLongDates(db.database(), system);
+ migration.execute();
+
+ int count = db.countSql("select count(*) from analysis_reports where created_at_ms is not null and updated_at_ms is not null");
+ assertThat(count).isEqualTo(3);
+
+ int countWithAllDateFieldsNull = db
+ .countSql("select count(*) from analysis_reports where created_at_ms is not null and updated_at_ms is not null and started_at_ms is not null and finished_at_ms is not null");
+ assertThat(countWithAllDateFieldsNull).isEqualTo(2);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedEventsLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedEventsLongDatesTest.java
new file mode 100644
index 00000000000..96104aee886
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedEventsLongDatesTest.java
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.DateUtils.parseDate;
+
+public class FeedEventsLongDatesTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedEventsLongDatesTest.class, "schema.sql");
+
+ @Before
+ public void before() {
+ db.prepareDbUnit(getClass(), "before.xml");
+ }
+
+ @Test
+ public void execute() throws Exception {
+ MigrationStep migration = newMigration(System2.INSTANCE);
+
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from events where " +
+ "created_at_ms is not null " +
+ "and event_date_ms is not null");
+ assertThat(count).isEqualTo(3);
+ }
+
+ @Test
+ public void take_now_if_date_in_the_future() throws Exception {
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1234L);
+
+ MigrationStep migration = newMigration(system);
+
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from events where " +
+ "created_at_ms = 1234");
+ assertThat(count).isEqualTo(2);
+ }
+
+ @Test
+ public void take_date_if_in_the_past() throws Exception {
+ MigrationStep migration = newMigration(System2.INSTANCE);
+
+ migration.execute();
+
+ long time = parseDate("2014-09-25").getTime();
+ int count = db
+ .countSql("select count(*) from events where " +
+ "created_at_ms=" + time);
+ assertThat(count).isEqualTo(1);
+ }
+
+ private MigrationStep newMigration(System2 system) {
+ return new FeedEventsLongDates(db.database(), system);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest.java
new file mode 100644
index 00000000000..2bf3bc23004
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest.java
@@ -0,0 +1,97 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.apache.commons.dbutils.DbUtils;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.source.FileSourceDto;
+import org.sonar.db.version.MigrationStep;
+import org.sonar.server.source.db.FileSourceDb;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FeedFileSourcesBinaryDataTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedFileSourcesBinaryDataTest.class, "schema.sql");
+
+ @Test
+ public void convert_csv_to_protobuf() throws Exception {
+ db.prepareDbUnit(getClass(), "data.xml");
+
+ MigrationStep migration = new FeedFileSourcesBinaryData(db.database());
+ migration.execute();
+
+ int count = db.countSql("select count(*) from file_sources where binary_data is not null");
+ assertThat(count).isEqualTo(3);
+
+ try (Connection connection = db.openConnection()) {
+ FileSourceDb.Data data = selectData(connection, 1L);
+ assertThat(data.getLinesCount()).isEqualTo(4);
+ assertThat(data.getLines(0).getScmRevision()).isEqualTo("aef12a");
+
+ data = selectData(connection, 2L);
+ assertThat(data.getLinesCount()).isEqualTo(4);
+ assertThat(data.getLines(0).hasScmRevision()).isFalse();
+
+ data = selectData(connection, 3L);
+ assertThat(data.getLinesCount()).isEqualTo(0);
+ }
+ }
+
+ @Test
+ public void fail_to_parse_csv() throws Exception {
+ db.prepareDbUnit(getClass(), "bad_data.xml");
+
+ MigrationStep migration = new FeedFileSourcesBinaryData(db.database());
+
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("Error during processing of row: [id=1,data=");
+
+ migration.execute();
+ }
+
+ private FileSourceDb.Data selectData(Connection connection, long fileSourceId) throws SQLException {
+ PreparedStatement pstmt = connection.prepareStatement("select binary_data from file_sources where id=?");
+ ResultSet rs = null;
+ try {
+ pstmt.setLong(1, fileSourceId);
+ rs = pstmt.executeQuery();
+ rs.next();
+ InputStream data = rs.getBinaryStream(1);
+ return FileSourceDto.decodeSourceData(data);
+ } finally {
+ DbUtils.closeQuietly(rs);
+ DbUtils.closeQuietly(pstmt);
+ }
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest.java
new file mode 100644
index 00000000000..01690ce52df
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedIssueChangesLongDatesTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedIssueChangesLongDatesTest.class, "schema.sql");
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1500000000000L);
+ MigrationStep migration = new FeedIssueChangesLongDates(db.database(), system);
+ migration.execute();
+
+ int count = db.countSql("select count(*) from issue_changes where created_at_ms is not null and updated_at_ms is not null");
+ assertThat(count).isEqualTo(3);
+
+ int countWithAllDateFieldsNull = db
+ .countSql("select count(*) from issue_changes where created_at_ms is not null and updated_at_ms is not null and issue_change_creation_date_ms is not null");
+ assertThat(countWithAllDateFieldsNull).isEqualTo(2);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueComponentUuidsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueComponentUuidsTest.java
new file mode 100644
index 00000000000..de5f1c94c05
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueComponentUuidsTest.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+public class FeedIssueComponentUuidsTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedIssueComponentUuidsTest.class, "schema.sql");
+
+ FeedIssueComponentUuids underTest;
+
+ @Before
+ public void setUp() {
+ db.truncateTables();
+
+ underTest = new FeedIssueComponentUuids(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ underTest.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "before.xml");
+ underTest.execute();
+ db.assertDbUnit(this.getClass(), "after-result.xml", "issues");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueTagsTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueTagsTest.java
new file mode 100644
index 00000000000..1e630545f94
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssueTagsTest.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import java.util.Date;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedIssueTagsTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedIssueTagsTest.class, "schema.sql");
+
+ FeedIssueTags migration;
+
+ System2 system;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table rules");
+ db.executeUpdateSql("truncate table issues");
+
+ system = mock(System2.class);
+ Date now = DateUtils.parseDateTime("2014-12-08T17:33:00+0100");
+ when(system.now()).thenReturn(now.getTime());
+ migration = new FeedIssueTags(db.database(), system);
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate_with_rule_tags() throws Exception {
+ db.prepareDbUnit(this.getClass(), "before.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "after-result.xml", "issues");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssuesLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssuesLongDatesTest.java
new file mode 100644
index 00000000000..b10df31cb5d
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedIssuesLongDatesTest.java
@@ -0,0 +1,81 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.DateUtils.parseDate;
+
+public class FeedIssuesLongDatesTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedIssuesLongDatesTest.class, "schema.sql");
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ MigrationStep migration = new FeedIssuesLongDates(db.database(), System2.INSTANCE);
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from issues where " +
+ "issue_creation_date_ms is not null " +
+ "and issue_update_date_ms is not null " +
+ "and issue_close_date_ms is not null");
+ assertThat(count).isEqualTo(2);
+ }
+
+ @Test
+ public void take_now_if_date_in_the_future() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+ System2 system2 = mock(System2.class);
+ when(system2.now()).thenReturn(0L);
+
+ MigrationStep migration = new FeedIssuesLongDates(db.database(), mock(System2.class));
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from issues where " +
+ "issue_creation_date_ms = 0");
+ assertThat(count).isEqualTo(1);
+ }
+
+ @Test
+ public void take_snapshot_date_if_in_the_past() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+ long snapshotTime = parseDate("2014-09-25").getTime();
+
+ MigrationStep migration = new FeedIssuesLongDates(db.database(), System2.INSTANCE);
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from issues where " +
+ "issue_creation_date_ms=" + snapshotTime);
+ assertThat(count).isEqualTo(1);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest.java
new file mode 100644
index 00000000000..2b4f3f5facb
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest.java
@@ -0,0 +1,88 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.DateUtils.parseDate;
+
+public class FeedManualMeasuresLongDatesTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedManualMeasuresLongDatesTest.class, "schema.sql");
+
+ @Before
+ public void before() {
+ db.prepareDbUnit(getClass(), "before.xml");
+ }
+
+ @Test
+ public void execute() throws Exception {
+ MigrationStep migration = newMigration(System2.INSTANCE);
+
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from manual_measures where " +
+ "created_at_ms is not null " +
+ "and updated_at_ms is not null");
+ assertThat(count).isEqualTo(2);
+ }
+
+ @Test
+ public void take_now_if_date_in_the_future() throws Exception {
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1234L);
+
+ MigrationStep migration = newMigration(system);
+
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from manual_measures where " +
+ "created_at_ms = 1234");
+ assertThat(count).isEqualTo(1);
+ }
+
+ @Test
+ public void take_manual_measure_date_if_in_the_past() throws Exception {
+ MigrationStep migration = newMigration(System2.INSTANCE);
+
+ migration.execute();
+
+ long snapshotTime = parseDate("2014-09-25").getTime();
+ int count = db
+ .countSql("select count(*) from manual_measures where " +
+ "created_at_ms=" + snapshotTime);
+ assertThat(count).isEqualTo(1);
+ }
+
+ private MigrationStep newMigration(System2 system) {
+ return new FeedManualMeasuresLongDates(db.database(), system);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest.java
new file mode 100644
index 00000000000..8eeb8bb588b
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest.java
@@ -0,0 +1,84 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.api.utils.DateUtils.parseDate;
+
+public class FeedSnapshotsLongDatesTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedSnapshotsLongDatesTest.class, "schema.sql");
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ MigrationStep migration = new FeedSnapshotsLongDates(db.database(), System2.INSTANCE);
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from snapshots where created_at_ms is not null " +
+ "and build_date_ms is not null " +
+ "and period1_date_ms is not null " +
+ "and period2_date_ms is not null " +
+ "and period3_date_ms is not null " +
+ "and period4_date_ms is not null " +
+ "and period5_date_ms is not null");
+ assertThat(count).isEqualTo(2);
+ }
+
+ @Test
+ public void take_now_if_date_in_the_future() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+ System2 system2 = mock(System2.class);
+ when(system2.now()).thenReturn(0L);
+
+ MigrationStep migration = new FeedSnapshotsLongDates(db.database(), mock(System2.class));
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from snapshots where " +
+ "created_at_ms = 0");
+ assertThat(count).isEqualTo(1);
+ }
+
+ @Test
+ public void take_snapshot_date_if_in_the_past() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+ long snapshotTime = parseDate("2014-09-25").getTime();
+
+ MigrationStep migration = new FeedSnapshotsLongDates(db.database(), System2.INSTANCE);
+ migration.execute();
+
+ int count = db
+ .countSql("select count(*) from snapshots where " +
+ "created_at_ms=" + snapshotTime);
+ assertThat(count).isEqualTo(1);
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/FeedUsersLongDatesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedUsersLongDatesTest.java
new file mode 100644
index 00000000000..88923fbcb6e
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/FeedUsersLongDatesTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedUsersLongDatesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedUsersLongDatesTest.class, "schema.sql");
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "before.xml");
+
+ System2 system = mock(System2.class);
+ when(system.now()).thenReturn(1500000000000L);
+ MigrationStep migration = new FeedUsersLongDates(db.database(), system);
+ migration.execute();
+
+ int count = db.countSql("select count(*) from users where created_at_ms is not null and updated_at_ms is not null");
+ assertThat(count).isEqualTo(3);
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest.java
new file mode 100644
index 00000000000..3b41af6846e
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest.java
@@ -0,0 +1,59 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class RemovePermissionsOnModulesMigrationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, RemovePermissionsOnModulesMigrationTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ migration = new RemovePermissionsOnModulesMigrationStep(db.database());
+ }
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "migrate.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "migrate-result.xml", "user_roles", "group_roles");
+ }
+
+ @Test
+ public void nothing_to_do_when_already_migrated() throws Exception {
+ db.prepareDbUnit(getClass(), "nothing_to_do_when_already_migrated.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "nothing_to_do_when_already_migrated.xml", "user_roles", "group_roles");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest.java
new file mode 100644
index 00000000000..b2ecde9fa88
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RenameComponentRelatedParamsInIssueFiltersMigrationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, RenameComponentRelatedParamsInIssueFiltersMigrationTest.class, "schema.sql");
+
+ MigrationStep migration;
+ System2 system = mock(System2.class);
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table issue_filters");
+ migration = new RenameComponentRelatedParamsInIssueFilters(db.database(), system);
+ when(system.now()).thenReturn(DateUtils.parseDate("2014-10-29").getTime());
+ }
+
+ @Test
+ public void execute() throws Exception {
+ db.prepareDbUnit(getClass(), "execute.xml");
+
+ migration.execute();
+
+ db.assertDbUnit(getClass(), "execute-result.xml", "issue_filters");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest.java b/sonar-db/src/test/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest.java
new file mode 100644
index 00000000000..89a8d7e6c7d
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest.java
@@ -0,0 +1,65 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v51;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class UpdateProjectsModuleUuidPathTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, UpdateProjectsModuleUuidPathTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ System2 system;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table projects");
+
+ migration = new UpdateProjectsModuleUuidPath(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate_components.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate_components-result.xml", "projects");
+ }
+
+ @Test
+ public void not_migrate_already_migrated_components() throws Exception {
+ db.prepareDbUnit(this.getClass(), "not_migrate_already_migrated_components.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "not_migrate_already_migrated_components.xml", "projects");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest.java
new file mode 100644
index 00000000000..07f48d9a144
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import java.sql.Types;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+public class AddManualMeasuresComponentUuidColumnTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, AddManualMeasuresComponentUuidColumnTest.class, "schema.sql");
+
+ AddManualMeasuresComponentUuidColumn underTest;
+
+ @Before
+ public void setUp() {
+ underTest = new AddManualMeasuresComponentUuidColumn(db.database());
+ }
+
+ @Test
+ public void update_columns() throws Exception {
+ underTest.execute();
+
+ db.assertColumnDefinition("manual_measures", "component_uuid", Types.VARCHAR, 50);
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/FeedEventsComponentUuidTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedEventsComponentUuidTest.java
new file mode 100644
index 00000000000..87f156d39be
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedEventsComponentUuidTest.java
@@ -0,0 +1,64 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class FeedEventsComponentUuidTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedEventsComponentUuidTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table events");
+ db.executeUpdateSql("truncate table projects");
+
+ migration = new FeedEventsComponentUuid(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "events");
+ }
+
+ @Test
+ public void not_migrate_already_migrated_data() throws Exception {
+ db.prepareDbUnit(this.getClass(), "not_migrate_already_migrated_data.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "not_migrate_already_migrated_data.xml", "events");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest.java
new file mode 100644
index 00000000000..615a41ba117
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class FeedFileSourcesDataTypeTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedFileSourcesDataTypeTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table file_sources");
+
+ migration = new FeedFileSourcesDataType(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "file_sources");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest.java
new file mode 100644
index 00000000000..cf76d6a4430
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+
+public class FeedManualMeasuresComponentUuidTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedManualMeasuresComponentUuidTest.class, "schema.sql");
+
+ FeedManualMeasuresComponentUuid underTest;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table manual_measures");
+ db.executeUpdateSql("truncate table projects");
+
+ underTest = new FeedManualMeasuresComponentUuid(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ underTest.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ underTest.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "manual_measures");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/FeedMetricsBooleansTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedMetricsBooleansTest.java
new file mode 100644
index 00000000000..b565f04dd34
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedMetricsBooleansTest.java
@@ -0,0 +1,54 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class FeedMetricsBooleansTest {
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedMetricsBooleansTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table metrics");
+
+ migration = new FeedMetricsBooleans(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "metrics");
+ }
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest.java
new file mode 100644
index 00000000000..4487d000614
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class FeedProjectLinksComponentUuidTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, FeedProjectLinksComponentUuidTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table project_links");
+
+ migration = new FeedProjectLinksComponentUuid(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "project_links");
+ }
+
+ @Test
+ public void not_migrate_already_migrated_data() throws Exception {
+ db.prepareDbUnit(this.getClass(), "not_migrate_already_migrated_data.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "not_migrate_already_migrated_data.xml", "project_links");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/MoveProjectProfileAssociationTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/MoveProjectProfileAssociationTest.java
new file mode 100644
index 00000000000..982859c34ae
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/MoveProjectProfileAssociationTest.java
@@ -0,0 +1,66 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class MoveProjectProfileAssociationTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, MoveProjectProfileAssociationTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table projects");
+ db.executeUpdateSql("truncate table project_qprofiles");
+ db.executeUpdateSql("truncate table properties");
+ db.executeUpdateSql("truncate table rules_profiles");
+
+ migration = new MoveProjectProfileAssociation(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "rules_profiles", "project_qprofiles");
+ }
+
+ @Test
+ public void not_migrate_already_migrated_data() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate-result.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "rules_profiles", "project_qprofiles");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveComponentLibrariesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveComponentLibrariesTest.java
new file mode 100644
index 00000000000..fa1d876f5af
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveComponentLibrariesTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class RemoveComponentLibrariesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, RemoveComponentLibrariesTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table projects");
+
+ migration = new RemoveComponentLibraries(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void remove_libraries() throws Exception {
+ db.prepareDbUnit(this.getClass(), "remove_libraries.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "remove_libraries-result.xml", "projects");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest.java
new file mode 100644
index 00000000000..79d5b1511ab
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest.java
@@ -0,0 +1,78 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class RemoveDuplicatedComponentKeysTest {
+
+ @ClassRule
+ public static DbTester db = DbTester.createForSchema(System2.INSTANCE, RemoveDuplicatedComponentKeysTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table projects");
+ db.executeUpdateSql("truncate table issues");
+
+ migration = new RemoveDuplicatedComponentKeys(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void migrate_components_and_issues() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "projects", "issues");
+ }
+
+ @Test
+ public void not_migrate_components_and_issues_already_migrated() throws Exception {
+ db.prepareDbUnit(this.getClass(), "migrate-result.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "migrate-result.xml", "projects", "issues");
+ }
+
+ @Test
+ public void keep_enable_component_when_enabled_component_exists() throws Exception {
+ db.prepareDbUnit(this.getClass(), "keep_enable_component.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "keep_enable_component-result.xml", "projects");
+ }
+
+ @Test
+ public void keep_last_component_when_no_enabled_components() throws Exception {
+ db.prepareDbUnit(this.getClass(), "keep_last_component.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "keep_last_component-result.xml", "projects");
+ }
+
+}
diff --git a/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest.java b/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest.java
new file mode 100644
index 00000000000..c9163a6c702
--- /dev/null
+++ b/sonar-db/src/test/java/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.db.version.v52;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.version.MigrationStep;
+
+public class RemoveSnapshotLibrariesTest {
+
+ @Rule
+ public DbTester db = DbTester.createForSchema(System2.INSTANCE, RemoveSnapshotLibrariesTest.class, "schema.sql");
+
+ MigrationStep migration;
+
+ @Before
+ public void setUp() {
+ db.executeUpdateSql("truncate table snapshots");
+
+ migration = new RemoveSnapshotLibraries(db.database());
+ }
+
+ @Test
+ public void migrate_empty_db() throws Exception {
+ migration.execute();
+ }
+
+ @Test
+ public void remove_libraries() throws Exception {
+ db.prepareDbUnit(this.getClass(), "remove_libraries.xml");
+ migration.execute();
+ db.assertDbUnit(this.getClass(), "remove_libraries-result.xml", "snapshots");
+ }
+
+}
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/batch-insert-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/batch-insert-result.xml
new file mode 100644
index 00000000000..96ecbf4062f
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/batch-insert-result.xml
@@ -0,0 +1,8 @@
+<dataset>
+ <persons id="1" login="barbara" age="56" enabled="[false]" coeff="1.5" updated_at="2014-01-25"/>
+ <persons id="2" login="emmerik" age="14" enabled="[true]" coeff="5.2" updated_at="2014-01-25"/>
+ <persons id="3" login="morgan" age="3" enabled="[true]" coeff="5.4" updated_at="2014-01-25"/>
+
+ <persons id="10" login="kurt" age="27" enabled="[true]" coeff="2.2" updated_at="[null]"/>
+ <persons id="11" login="courtney" age="25" enabled="[false]" coeff="2.3" updated_at="[null]"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/insert-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/insert-result.xml
new file mode 100644
index 00000000000..32b7ac03f06
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/insert-result.xml
@@ -0,0 +1,7 @@
+<dataset>
+ <persons id="1" login="barbara" age="56" enabled="[false]" coeff="1.5" updated_at="2014-01-25"/>
+ <persons id="2" login="emmerik" age="14" enabled="[true]" coeff="5.2" updated_at="2014-01-25"/>
+ <persons id="3" login="morgan" age="3" enabled="[true]" coeff="5.4" updated_at="2014-01-25"/>
+
+ <persons id="10" login="kurt" age="27" enabled="[true]" coeff="2.2" updated_at="[null]"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/mass-update-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/mass-update-result.xml
new file mode 100644
index 00000000000..9eb2317febf
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/mass-update-result.xml
@@ -0,0 +1,5 @@
+<dataset>
+ <persons id="1" login="barbara" age="56" enabled="[false]" coeff="1.5" updated_at="2014-01-25"/>
+ <persons id="2" login="login2" age="12" enabled="[true]" coeff="5.2" updated_at="2014-01-25"/>
+ <persons id="3" login="login3" age="13" enabled="[true]" coeff="5.4" updated_at="2014-01-25"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/persons.xml b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/persons.xml
new file mode 100644
index 00000000000..62c226d53b7
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/persons.xml
@@ -0,0 +1,5 @@
+<dataset>
+ <persons id="1" login="barbara" age="56" enabled="[false]" coeff="1.5" updated_at="2014-01-25"/>
+ <persons id="2" login="emmerik" age="14" enabled="[true]" coeff="5.2" updated_at="2014-01-25"/>
+ <persons id="3" login="morgan" age="3" enabled="[true]" coeff="5.4" updated_at="2014-01-25"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/schema.sql
new file mode 100644
index 00000000000..499b25b599d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/schema.sql
@@ -0,0 +1,8 @@
+CREATE TABLE "PERSONS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(50),
+ "AGE" INTEGER,
+ "ENABLED" BOOLEAN,
+ "UPDATED_AT" TIMESTAMP,
+ "COEFF" DOUBLE
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/scroll-and-update-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/scroll-and-update-result.xml
new file mode 100644
index 00000000000..0cf4e593d4d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/scroll-and-update-result.xml
@@ -0,0 +1,5 @@
+<dataset>
+ <persons id="1" login="login1" age="11" enabled="[false]" coeff="1.5" updated_at="2014-01-25"/>
+ <persons id="2" login="login2" age="12" enabled="[true]" coeff="5.2" updated_at="2014-01-25"/>
+ <persons id="3" login="login3" age="13" enabled="[true]" coeff="5.4" updated_at="2014-01-25"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/update-null-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/update-null-result.xml
new file mode 100644
index 00000000000..ec48c899b40
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/BaseDataChangeTest/update-null-result.xml
@@ -0,0 +1,5 @@
+<dataset>
+ <persons id="1" login="barbara" age="56" enabled="[false]" coeff="1.5" updated_at="2014-01-25"/>
+ <persons id="2" login="[null]" age="[null]" enabled="[null]" coeff="[null]" updated_at="[null]"/>
+ <persons id="3" login="morgan" age="3" enabled="[true]" coeff="5.4" updated_at="2014-01-25"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/after.xml b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/after.xml
new file mode 100644
index 00000000000..bb36c83c788
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/after.xml
@@ -0,0 +1,20 @@
+<dataset>
+ <!-- with default value -->
+ <rules_parameters id="1" rule_id="10" name="max" param_type="INT" default_value="10" description="[null]"/>
+
+ <!-- without default value, to be ignored -->
+ <rules_parameters id="2" rule_id="10" name="min" param_type="INT" default_value="[null]" description="[null]"/>
+
+ <!-- this active rule has all parameters -->
+ <active_rules id="100" profile_id="1000" rule_id="10" failure_level="3" inheritance="[null]"
+ created_at="2012-01-01" updated_at="2012-01-01"/>
+ <active_rule_parameters id="10000" active_rule_id="100" rules_parameter_id="1" rules_parameter_key="max" value="9"/>
+ <active_rule_parameters id="10001" active_rule_id="100" rules_parameter_id="2" rules_parameter_key="min" value="4"/>
+
+ <!-- this active rule does not have parameters. UPDATED_AT CHANGED -->
+ <active_rules id="101" profile_id="1000" rule_id="10" failure_level="3" inheritance="[null]"
+ created_at="2012-01-01" updated_at="2014-04-28"/>
+
+ <!-- newly created -->
+ <active_rule_parameters id="10002" active_rule_id="101" rules_parameter_id="1" rules_parameter_key="max" value="10"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/before.xml
new file mode 100644
index 00000000000..df5d8fa0bbd
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/before.xml
@@ -0,0 +1,17 @@
+<dataset>
+ <!-- with default value -->
+ <rules_parameters id="1" rule_id="10" name="max" param_type="INT" default_value="10" description="[null]"/>
+
+ <!-- without default value, to be ignored -->
+ <rules_parameters id="2" rule_id="10" name="min" param_type="INT" default_value="[null]" description="[null]"/>
+
+ <!-- this active rule has all parameters -->
+ <active_rules id="100" profile_id="1000" rule_id="10" failure_level="3" inheritance="[null]"
+ created_at="2012-01-01" updated_at="2012-01-01"/>
+ <active_rule_parameters id="10000" active_rule_id="100" rules_parameter_id="1" rules_parameter_key="max" value="9"/>
+ <active_rule_parameters id="10001" active_rule_id="100" rules_parameter_id="2" rules_parameter_key="min" value="4"/>
+
+ <!-- this active rule does not have parameters -->
+ <active_rules id="101" profile_id="1000" rule_id="10" failure_level="3" inheritance="[null]"
+ created_at="2012-01-01" updated_at="2012-01-01"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/no_changes.xml b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/no_changes.xml
new file mode 100644
index 00000000000..fdd7750955e
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/no_changes.xml
@@ -0,0 +1,11 @@
+<dataset>
+ <!-- with default value -->
+ <rules_parameters id="1" rule_id="10" name="max" param_type="INT" default_value="10" description="[null]"/>
+
+ <!-- this active rule has all parameters -->
+ <active_rules id="100" profile_id="1000" rule_id="10" failure_level="3" inheritance="[null]"
+ created_at="2012-01-01" updated_at="2012-01-01"/>
+ <active_rule_parameters id="10000" active_rule_id="100" rules_parameter_id="1" rules_parameter_key="max" value="9"/>
+ <active_rule_parameters id="10001" active_rule_id="100" rules_parameter_id="2" rules_parameter_key="min" value="4"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/schema.sql
new file mode 100644
index 00000000000..ebb574482c3
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v45/AddMissingRuleParameterDefaultValuesMigrationTest/schema.sql
@@ -0,0 +1,28 @@
+CREATE TABLE "RULES_PARAMETERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "RULE_ID" INTEGER NOT NULL,
+ "NAME" VARCHAR(128) NOT NULL,
+ "PARAM_TYPE" VARCHAR(512) NOT NULL,
+ "DEFAULT_VALUE" VARCHAR(4000),
+ "DESCRIPTION" VARCHAR(4000)
+);
+
+
+CREATE TABLE "ACTIVE_RULES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROFILE_ID" INTEGER NOT NULL,
+ "RULE_ID" INTEGER NOT NULL,
+ "FAILURE_LEVEL" INTEGER NOT NULL,
+ "INHERITANCE" VARCHAR(10),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+
+CREATE TABLE "ACTIVE_RULE_PARAMETERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ACTIVE_RULE_ID" INTEGER NOT NULL,
+ "RULES_PARAMETER_ID" INTEGER NOT NULL,
+ "RULES_PARAMETER_KEY" VARCHAR(128),
+ "VALUE" VARCHAR(4000)
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml
new file mode 100644
index 00000000000..14e559f4a12
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/before.xml
@@ -0,0 +1,34 @@
+<dataset>
+ <metrics id="1" name="quality_profiles" VAL_TYPE="DATA" DESCRIPTION="[null]" domain="[null]" short_name=""
+ enabled="[true]" worst_value="0" optimized_best_value="[true]" best_value="100" direction="1"
+ hidden="[false]" delete_historical_data="[null]"/>
+
+ <!-- old format, references a numeric value which is profile id -->
+ <project_measures id="1" VALUE="60" METRIC_ID="1" SNAPSHOT_ID="1001" alert_text="[null]" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]"
+ url="[null]"
+ variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]"
+ variation_value_4="[null]" variation_value_5="[null]"/>
+
+ <!-- new format, json data -->
+ <project_measures id="2" VALUE="[null]" text_value="{json}" METRIC_ID="1" SNAPSHOT_ID="1001" alert_text="[null]"
+ RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]" rule_priority="[null]" characteristic_id="[null]"
+ url="[null]"
+ variation_value_1="[null]" variation_value_2="[null]" variation_value_3="[null]"
+ variation_value_4="[null]" variation_value_5="[null]"/>
+
+ <!-- last snapshot -->
+ <snapshots purge_status="[null]" id="1001" project_id="1" parent_snapshot_id="[null]" root_project_id="1"
+ root_snapshot_id="[null]"
+ scope="PRJ" qualifier="TRK" created_at="2009-11-01 13:58:00.00" build_date="2009-11-01 13:58:00.00"
+ version="[null]" path=""
+ status="P" islast="[true]" depth="0"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]" period2_mode="[null]"
+ period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]" period4_mode="[null]"
+ period4_param="[null]" period4_date="[null]" period5_mode="[null]" period5_param="[null]"
+ period5_date="[null]"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql
new file mode 100644
index 00000000000..912fd080b0d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v45/DeleteMeasuresOnDeletedProfilesMigrationTest/schema.sql
@@ -0,0 +1,77 @@
+CREATE TABLE "PROJECT_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "VALUE" DOUBLE,
+ "METRIC_ID" INTEGER NOT NULL,
+ "SNAPSHOT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "RULES_CATEGORY_ID" INTEGER,
+ "TEXT_VALUE" VARCHAR(4000),
+ "TENDENCY" INTEGER,
+ "MEASURE_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER,
+ "ALERT_STATUS" VARCHAR(5),
+ "ALERT_TEXT" VARCHAR(4000),
+ "URL" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(4000),
+ "RULE_PRIORITY" INTEGER,
+ "CHARACTERISTIC_ID" INTEGER,
+ "PERSON_ID" INTEGER,
+ "VARIATION_VALUE_1" DOUBLE,
+ "VARIATION_VALUE_2" DOUBLE,
+ "VARIATION_VALUE_3" DOUBLE,
+ "VARIATION_VALUE_4" DOUBLE,
+ "VARIATION_VALUE_5" DOUBLE,
+ "MEASURE_DATA" BINARY(167772150)
+);
+
+CREATE TABLE "METRICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(64) NOT NULL,
+ "DESCRIPTION" VARCHAR(255),
+ "DIRECTION" INTEGER NOT NULL DEFAULT 0,
+ "DOMAIN" VARCHAR(64),
+ "SHORT_NAME" VARCHAR(64),
+ "QUALITATIVE" BOOLEAN NOT NULL DEFAULT FALSE,
+ "VAL_TYPE" VARCHAR(8),
+ "USER_MANAGED" BOOLEAN DEFAULT FALSE,
+ "ENABLED" BOOLEAN DEFAULT TRUE,
+ "ORIGIN" VARCHAR(3),
+ "WORST_VALUE" DOUBLE,
+ "BEST_VALUE" DOUBLE,
+ "OPTIMIZED_BEST_VALUE" BOOLEAN,
+ "HIDDEN" BOOLEAN,
+ "DELETE_HISTORICAL_DATA" BOOLEAN
+);
+
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "BUILD_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" TIMESTAMP,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" TIMESTAMP,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" TIMESTAMP,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" TIMESTAMP,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute-result.xml
new file mode 100644
index 00000000000..0e6a35407d8
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute-result.xml
@@ -0,0 +1,35 @@
+<dataset>
+
+ <!-- Template rule -->
+ <rules id="1" plugin_rule_key="ArchitecturalConstraint" plugin_name="xoo" name="Architectural constraint"
+ description="Architectural constraint" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[true]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="1" rule_id="1" name="max" param_type="INT" default_value="10" description="[null]"/>
+ <rules_parameters id="2" rule_id="1" name="format" param_type="STRING" default_value="txt" description="[null]"/>
+ <rules_parameters id="3" rule_id="1" name="type" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="4" rule_id="1" name="param" param_type="STRING" default_value="" description="[null]"/>
+
+ <!-- Custom rule, 2 parameters should be added -->
+ <rules id="2" plugin_rule_key="ArchitecturalConstraint_2" plugin_name="xoo" name="Architectural constraint 2"
+ description="Architectural constraint 2" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="1" created_at="2014-01-01" updated_at="2014-10-09"/>
+ <rules_parameters id="5" rule_id="2" name="max" param_type="INT" default_value="10" description="[null]"/>
+ <rules_parameters id="6" rule_id="2" name="format" param_type="STRING" default_value="csv" description="[null]"/>
+ <rules_parameters id="7" rule_id="2" name="type" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="8" rule_id="2" name="param" param_type="STRING" default_value="[null]" description="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute.xml
new file mode 100644
index 00000000000..a951abd43de
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute.xml
@@ -0,0 +1,33 @@
+<dataset>
+
+ <!-- Template rule -->
+ <rules id="1" plugin_rule_key="ArchitecturalConstraint" plugin_name="xoo" name="Architectural constraint"
+ description="Architectural constraint" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[true]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="1" rule_id="1" name="max" param_type="INT" default_value="10" description="[null]"/>
+ <rules_parameters id="2" rule_id="1" name="format" param_type="STRING" default_value="txt" description="[null]"/>
+ <rules_parameters id="3" rule_id="1" name="type" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="4" rule_id="1" name="param" param_type="STRING" default_value="" description="[null]"/>
+
+ <!-- Custom rule, 2 parameters are existing, 2 parameters should be added -->
+ <rules id="2" plugin_rule_key="ArchitecturalConstraint_2" plugin_name="xoo" name="Architectural constraint 2"
+ description="Architectural constraint 2" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="1" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="5" rule_id="2" name="max" param_type="INT" default_value="10" description="[null]"/>
+ <rules_parameters id="6" rule_id="2" name="format" param_type="STRING" default_value="csv" description="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter-result.xml
new file mode 100644
index 00000000000..ae74ca8fbe3
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter-result.xml
@@ -0,0 +1,35 @@
+<dataset>
+
+ <!-- Template rule -->
+ <rules id="1" plugin_rule_key="ArchitecturalConstraint" plugin_name="xoo" name="Architectural constraint"
+ description="Architectural constraint" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[true]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="1" rule_id="1" name="max" param_type="INT" default_value="10" description="[null]"/>
+ <rules_parameters id="2" rule_id="1" name="format" param_type="STRING" default_value="txt" description="[null]"/>
+ <rules_parameters id="3" rule_id="1" name="type" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="4" rule_id="1" name="param" param_type="STRING" default_value="" description="[null]"/>
+
+ <!-- Custom rule, 0 parameter are existing, 4 parameters should be added -->
+ <rules id="3" plugin_rule_key="ArchitecturalConstraint_3" plugin_name="xoo" name="Architectural constraint 3"
+ description="Architectural constraint 3" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="1" created_at="2014-01-01" updated_at="2014-10-09"/>
+ <rules_parameters id="5" rule_id="3" name="max" param_type="INT" default_value="[null]" description="[null]"/>
+ <rules_parameters id="6" rule_id="3" name="format" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="7" rule_id="3" name="type" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="8" rule_id="3" name="param" param_type="STRING" default_value="[null]" description="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter.xml
new file mode 100644
index 00000000000..ae7a8443d9a
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/execute_when_custom_rule_have_no_parameter.xml
@@ -0,0 +1,31 @@
+<dataset>
+
+ <!-- Template rule -->
+ <rules id="1" plugin_rule_key="ArchitecturalConstraint" plugin_name="xoo" name="Architectural constraint"
+ description="Architectural constraint" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[true]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="1" rule_id="1" name="max" param_type="INT" default_value="10" description="[null]"/>
+ <rules_parameters id="2" rule_id="1" name="format" param_type="STRING" default_value="txt" description="[null]"/>
+ <rules_parameters id="3" rule_id="1" name="type" param_type="STRING" default_value="[null]" description="[null]"/>
+ <rules_parameters id="4" rule_id="1" name="param" param_type="STRING" default_value="" description="[null]"/>
+
+ <!-- Custom rule, 0 parameter are existing, 4 parameters should be added -->
+ <rules id="3" plugin_rule_key="ArchitecturalConstraint_3" plugin_name="xoo" name="Architectural constraint 3"
+ description="Architectural constraint 3" status="READY" priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="1" created_at="2014-01-01" updated_at="2014-01-01"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/no_changes.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/no_changes.xml
new file mode 100644
index 00000000000..53ad673ec69
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/no_changes.xml
@@ -0,0 +1,29 @@
+<dataset>
+
+ <!-- Template rule -->
+ <rules id="10" plugin_rule_key="Rule2" plugin_name="xoo" name="Rule2" description="Rule2" status="READY" priority="1"
+ language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[true]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="10" rule_id="10" name="max" param_type="INT" default_value="10" description="[null]"/>
+
+ <!-- Custom rule, no parameter should be added -->
+ <rules id="11" plugin_rule_key="Rule2_2" plugin_name="xoo" name="Rule2_2" description="Rule2_2" status="READY"
+ priority="1" language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" tags="[null]" system_tags="[null]" plugin_config_key="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="10" created_at="2014-01-01" updated_at="2014-01-01"/>
+ <rules_parameters id="11" rule_id="11" name="max" param_type="INT" default_value="10" description="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/schema.sql
new file mode 100644
index 00000000000..0bf38617681
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/AddMissingCustomRuleParametersMigrationTest/schema.sql
@@ -0,0 +1,40 @@
+CREATE TABLE "RULES_PARAMETERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "RULE_ID" INTEGER NOT NULL,
+ "NAME" VARCHAR(128) NOT NULL,
+ "PARAM_TYPE" VARCHAR(512) NOT NULL,
+ "DEFAULT_VALUE" VARCHAR(4000),
+ "DESCRIPTION" VARCHAR(4000)
+);
+
+CREATE TABLE "RULES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+ "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+ "DESCRIPTION" VARCHAR(16777215),
+ "DESCRIPTION_FORMAT" VARCHAR(20),
+ "PRIORITY" INTEGER,
+ "IS_TEMPLATE" BOOLEAN DEFAULT FALSE,
+ "TEMPLATE_ID" INTEGER,
+ "PLUGIN_CONFIG_KEY" VARCHAR(500),
+ "NAME" VARCHAR(200),
+ "STATUS" VARCHAR(40),
+ "LANGUAGE" VARCHAR(20),
+ "NOTE_DATA" CLOB(2147483647),
+ "NOTE_USER_LOGIN" VARCHAR(255),
+ "NOTE_CREATED_AT" TIMESTAMP,
+ "NOTE_UPDATED_AT" TIMESTAMP,
+ "CHARACTERISTIC_ID" INTEGER,
+ "DEFAULT_CHARACTERISTIC_ID" INTEGER,
+ "REMEDIATION_FUNCTION" VARCHAR(20),
+ "DEFAULT_REMEDIATION_FUNCTION" VARCHAR(20),
+ "REMEDIATION_COEFF" VARCHAR(20),
+ "DEFAULT_REMEDIATION_COEFF" VARCHAR(20),
+ "REMEDIATION_OFFSET" VARCHAR(20),
+ "DEFAULT_REMEDIATION_OFFSET" VARCHAR(20),
+ "EFFORT_TO_FIX_DESCRIPTION" VARCHAR(4000),
+ "TAGS" VARCHAR(4000),
+ "SYSTEM_TAGS" VARCHAR(4000),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute-result.xml
new file mode 100644
index 00000000000..49462b9bbcd
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute-result.xml
@@ -0,0 +1,12 @@
+<dataset>
+
+ <!-- ok -->
+ <activities id="1" log_key="abcde" created_at="2014-01-10" user_login="[null]" log_type="QPROFILE"
+ log_action="ACTIVATED" log_message="[null]"
+ data_field="ruleKey=findbugs:PT_RELATIVE_PATH_TRAVERSAL;profileKey=java-findbugs-74105;severity=MAJOR"/>
+
+ <!-- ko - deleted -->
+ <!--<activities id="2" log_key="fghij" created_at="2014-01-10" user_login="[null]" log_type="QPROFILE"
+ log_action="ACTIVATED" log_message="[null]"
+ data_field="ruleKey=findbugs:PT_RELATIVE_PATH_TRAVERSAL;param_xpath=foo;bar;baz"/>-->
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute.xml b/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute.xml
new file mode 100644
index 00000000000..415484745fb
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/execute.xml
@@ -0,0 +1,12 @@
+<dataset>
+
+ <!-- ok -->
+ <activities id="1" log_key="abcde" created_at="2014-01-10" user_login="[null]" log_type="QPROFILE"
+ log_action="ACTIVATED" log_message="[null]"
+ data_field="ruleKey=findbugs:PT_RELATIVE_PATH_TRAVERSAL;profileKey=java-findbugs-74105;severity=MAJOR"/>
+
+ <!-- ko -->
+ <activities id="2" log_key="fghij" created_at="2014-01-10" user_login="[null]" log_type="QPROFILE"
+ log_action="ACTIVATED" log_message="[null]"
+ data_field="ruleKey=findbugs:PT_RELATIVE_PATH_TRAVERSAL;param_xpath=foo;bar;baz"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/schema.sql
new file mode 100644
index 00000000000..336e2fd7f01
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v451/DeleteUnescapedActivitiesTest/schema.sql
@@ -0,0 +1,10 @@
+CREATE TABLE "ACTIVITIES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOG_KEY" VARCHAR(250),
+ "CREATED_AT" TIMESTAMP,
+ "USER_LOGIN" VARCHAR(30),
+ "LOG_TYPE" VARCHAR(250),
+ "LOG_ACTION" VARCHAR(250),
+ "LOG_MESSAGE" VARCHAR(250),
+ "DATA_FIELD" CLOB(2147483647)
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-invalid-duplication.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-invalid-duplication.xml
new file mode 100644
index 00000000000..6810f0a5dfe
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-invalid-duplication.xml
@@ -0,0 +1,15 @@
+<dataset>
+
+ <file_sources id="1" project_uuid="uuid-MyProject" file_uuid="uuid-Migrated.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=""
+ line_hashes=""
+ data_hash=""/>
+
+ <file_sources id="2" project_uuid="uuid-MyProject" file_uuid="uuid-MyFile.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=",,,,,,,,,,,,,,,class Foo {&#13;&#10;,,,,,,,,,,,,,,, // Empty&#13;&#10;,,,,,,,,,,,,,,,}&#13;&#10;,,,,,,,,,,,,,,,&#13;&#10;"
+ line_hashes="6a19ce786467960a3a9b0d26383a464a&#10;aab2dbc5fdeaa80b050b1d049ede357c&#10;cbb184dd8e05c9709e5dcaedaa0495cf&#10;&#10;"
+ data_hash=""/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-scm.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-scm.xml
new file mode 100644
index 00000000000..fa4d6332c5b
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after-with-scm.xml
@@ -0,0 +1,15 @@
+<dataset>
+
+ <file_sources id="1" project_uuid="uuid-MyProject" file_uuid="uuid-Migrated.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=""
+ line_hashes=""
+ data_hash=""/>
+
+ <file_sources id="2" project_uuid="uuid-MyProject" file_uuid="uuid-MyFile.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data="aef12a,alice,2014-04-25T12:34:56+0100,1,4,2,2,5,3,3,6,4,,,1,class Foo {&#13;&#10;abe465,bob,2014-07-25T12:34:56+0100,,,,,,,,,,,,2, // Empty&#13;&#10;afb789,carol,2014-03-23T12:34:56+0100,0,,,0,,,0,,,,,,}&#13;&#10;afb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,,,,&#13;&#10;"
+ line_hashes="6a19ce786467960a3a9b0d26383a464a&#10;aab2dbc5fdeaa80b050b1d049ede357c&#10;cbb184dd8e05c9709e5dcaedaa0495cf&#10;&#10;"
+ data_hash=""/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after.xml
new file mode 100644
index 00000000000..6810f0a5dfe
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/after.xml
@@ -0,0 +1,15 @@
+<dataset>
+
+ <file_sources id="1" project_uuid="uuid-MyProject" file_uuid="uuid-Migrated.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=""
+ line_hashes=""
+ data_hash=""/>
+
+ <file_sources id="2" project_uuid="uuid-MyProject" file_uuid="uuid-MyFile.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=",,,,,,,,,,,,,,,class Foo {&#13;&#10;,,,,,,,,,,,,,,, // Empty&#13;&#10;,,,,,,,,,,,,,,,}&#13;&#10;,,,,,,,,,,,,,,,&#13;&#10;"
+ line_hashes="6a19ce786467960a3a9b0d26383a464a&#10;aab2dbc5fdeaa80b050b1d049ede357c&#10;cbb184dd8e05c9709e5dcaedaa0495cf&#10;&#10;"
+ data_hash=""/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/before.xml
new file mode 100644
index 00000000000..065480f088d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/before.xml
@@ -0,0 +1,134 @@
+<dataset>
+
+ <metrics id="1" name="revisions_by_line" description="[null]" direction="0" domain="SCM"
+ short_name="Revisions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="2" name="authors_by_line" description="[null]" direction="0" domain="SCM" short_name="Authors by line"
+ qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="3" name="last_commit_datetimes_by_line" description="[null]" direction="0" domain="SCM"
+ short_name="Last commit dates by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="4" name="coverage_line_hits_data" description="[null]" direction="0" domain="Test"
+ short_name="Coverage hits by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="5" name="conditions_by_line" description="[null]" direction="0" domain="Tests"
+ short_name="Conditions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="6" name="covered_conditions_by_line" description="[null]" direction="0" domain="Tests"
+ short_name="Covered conditions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="7" name="it_coverage_line_hits_data" description="[null]" direction="0" domain="Test"
+ short_name="Coverage hits by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="8" name="it_conditions_by_line" description="[null]" direction="0" domain="Tests"
+ short_name="Conditions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="9" name="it_covered_conditions_by_line" description="[null]" direction="0" domain="Tests"
+ short_name="Covered conditions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="10" name="overall_coverage_line_hits_data" description="[null]" direction="0" domain="Test"
+ short_name="Coverage hits by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="11" name="overall_conditions_by_line" description="[null]" direction="0" domain="Tests"
+ short_name="Conditions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="12" name="overall_covered_conditions_by_line" description="[null]" direction="0" domain="Tests"
+ short_name="Covered conditions by line" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+ <metrics id="13" name="duplications_data" description="[null]" direction="0" domain="Duplications"
+ short_name="Duplication data" qualitative="false" val_type="DATA"
+ user_managed="false" enabled="true" origin="JAV" worst_value="[null]" best_value="[null]"
+ optimized_best_value="[null]" hidden="[false]" delete_historical_data="false"/>
+
+ <projects id="1" uuid="uuid-MyProject" kee="MyProject" scope="PRJ" qualifier="TRK"/>
+ <projects id="2" uuid="uuid-prj" kee="MyProject:src/main/xoo/prj" scope="DIR" qualifier="DIR"/>
+ <projects id="3" uuid="uuid-MyFile.xoo" kee="MyProject:src/main/xoo/prj/MyFile.xoo" scope="FIL" qualifier="FIL"/>
+ <projects id="4" uuid="uuid-Migrated.xoo" kee="MyProject:src/main/xoo/prj/Migrated.xoo" scope="FIL" qualifier="FIL"/>
+
+ <snapshots id="1" project_id="1" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ depth="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02" build_date="2011-09-29"
+ version="2.1-SNAPSHOT" path="1.2."/>
+ <snapshots id="2" project_id="1" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ depth="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02" build_date="2011-09-29"
+ version="2.1-SNAPSHOT" path="1.2."/>
+
+ <snapshots id="3" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ depth="1" scope="DIR" qualifier="DIR" created_at="2008-12-02" build_date="2011-09-29"
+ version="2.1-SNAPSHOT" path="1.2."/>
+ <snapshots id="4" project_id="2" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="2"
+ status="P" islast="[true]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ depth="1" scope="DIR" qualifier="DIR" created_at="2008-12-02" build_date="2011-09-29"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ version="2.1-SNAPSHOT" path="1.2."/>
+
+ <snapshots id="5" project_id="3" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ depth="1" scope="DIR" qualifier="DIR" created_at="2008-12-02" build_date="2011-09-29"
+ version="2.1-SNAPSHOT" path="1.2."/>
+ <snapshots id="6" project_id="3" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="2"
+ status="P" islast="[true]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ depth="1" scope="FIL" qualifier="FIL" created_at="2008-12-02" build_date="2011-09-29"
+ version="2.1-SNAPSHOT" path="1.2."/>
+
+ <snapshots id="7" project_id="4" parent_snapshot_id="5" root_project_id="1" root_snapshot_id="2"
+ status="P" islast="[true]" purge_status="1"
+ period1_mode="days1" period1_param="30" period1_date="2011-09-24"
+ period2_mode="days2" period2_param="31" period2_date="2011-09-25"
+ period3_mode="days3" period3_param="32" period3_date="2011-09-26"
+ period4_mode="days4" period4_param="33" period4_date="2011-09-27"
+ period5_mode="days5" period5_param="34" period5_date="2011-09-28"
+ depth="1" scope="FIL" qualifier="FIL" created_at="2008-12-02" build_date="2011-09-29"
+ version="2.1-SNAPSHOT" path="1.2."/>
+
+ <file_sources id="1" project_uuid="uuid-MyProject" file_uuid="uuid-Migrated.xoo" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=""
+ line_hashes=""
+ data_hash=""/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/schema.sql
new file mode 100644
index 00000000000..481ea89ba0a
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedFileSourcesTest/schema.sql
@@ -0,0 +1,119 @@
+
+CREATE TABLE "METRICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(64) NOT NULL,
+ "DESCRIPTION" VARCHAR(255),
+ "DIRECTION" INTEGER NOT NULL DEFAULT 0,
+ "DOMAIN" VARCHAR(64),
+ "SHORT_NAME" VARCHAR(64),
+ "QUALITATIVE" BOOLEAN NOT NULL DEFAULT FALSE,
+ "VAL_TYPE" VARCHAR(8),
+ "USER_MANAGED" BOOLEAN DEFAULT FALSE,
+ "ENABLED" BOOLEAN DEFAULT TRUE,
+ "ORIGIN" VARCHAR(3),
+ "WORST_VALUE" DOUBLE,
+ "BEST_VALUE" DOUBLE,
+ "OPTIMIZED_BEST_VALUE" BOOLEAN,
+ "HIDDEN" BOOLEAN,
+ "DELETE_HISTORICAL_DATA" BOOLEAN
+);
+
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "BUILD_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" TIMESTAMP,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" TIMESTAMP,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" TIMESTAMP,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" TIMESTAMP,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" TIMESTAMP
+);
+
+CREATE TABLE "SNAPSHOT_SOURCES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "SNAPSHOT_ID" INTEGER NOT NULL,
+ "UPDATED_AT" TIMESTAMP,
+ "DATA" CLOB(2147483647)
+);
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "PROJECT_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "VALUE" DOUBLE,
+ "METRIC_ID" INTEGER NOT NULL,
+ "SNAPSHOT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "RULES_CATEGORY_ID" INTEGER,
+ "TEXT_VALUE" VARCHAR(4000),
+ "TENDENCY" INTEGER,
+ "MEASURE_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER,
+ "ALERT_STATUS" VARCHAR(5),
+ "ALERT_TEXT" VARCHAR(4000),
+ "URL" VARCHAR(2000),
+ "DESCRIPTION" VARCHAR(4000),
+ "RULE_PRIORITY" INTEGER,
+ "CHARACTERISTIC_ID" INTEGER,
+ "PERSON_ID" INTEGER,
+ "VARIATION_VALUE_1" DOUBLE,
+ "VARIATION_VALUE_2" DOUBLE,
+ "VARIATION_VALUE_3" DOUBLE,
+ "VARIATION_VALUE_4" DOUBLE,
+ "VARIATION_VALUE_5" DOUBLE,
+ "MEASURE_DATA" BINARY(167772150)
+);
+
+CREATE TABLE "FILE_SOURCES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "FILE_UUID" VARCHAR(50) NOT NULL,
+ "DATA" CLOB(2147483647),
+ "LINE_HASHES" CLOB(2147483647),
+ "DATA_HASH" VARCHAR(50) NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/before.xml
new file mode 100644
index 00000000000..7f2e3baa257
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/before.xml
@@ -0,0 +1,75 @@
+<dataset>
+
+ <issues id="1" kee="ABC" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="200"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="2014-05-12"
+ updated_at="2014-05-13"
+ CREATED_AT_MS="[null]"
+ UPDATED_AT_MS="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <issues id="2" kee="DEF" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="200"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+
+ created_at="2014-05-12"
+ updated_at="2014-05-13"
+ CREATED_AT_MS="1500000000000"
+ UPDATED_AT_MS="1500000000000"
+ />
+
+ <!-- NULL dates -->
+ <issues id="3" kee="MISSINGDATES" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="200"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ issue_creation_date="[null]"
+ issue_update_date="[null]"
+ issue_close_date="[null]"
+ created_at="[null]"
+ updated_at="[null]"
+ CREATED_AT_MS="[null]"
+ UPDATED_AT_MS="[null]"
+ />
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/schema.sql
new file mode 100644
index 00000000000..66c7d3a7a9f
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/FeedIssueLongDatesTest/schema.sql
@@ -0,0 +1,28 @@
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50) UNIQUE NOT NULL,
+ "COMPONENT_ID" INTEGER NOT NULL,
+ "ROOT_COMPONENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "EFFORT_TO_FIX" DOUBLE,
+ "TECHNICAL_DEBT" INTEGER,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "ISSUE_CREATION_DATE" TIMESTAMP,
+ "ISSUE_CLOSE_DATE" TIMESTAMP,
+ "ISSUE_UPDATE_DATE" TIMESTAMP,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "UPDATED_AT_MS" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/after.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/after.xml
new file mode 100644
index 00000000000..0b0dfa1d728
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/after.xml
@@ -0,0 +1,28 @@
+<dataset>
+
+ <projects id="1" kee="project" name="project" long_name="project" scope="PRJ" qualifier="TRK" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="123456789"/>
+
+ <projects id="2" kee="view" name="View" long_name="View" scope="PRJ" qualifier="VW" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="123456789"/>
+
+ <!-- File should not be updated -->
+ <projects id="3" kee="file" name="File" long_name="File" scope="FIL" qualifier="CLA" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="[null]"/>
+
+ <projects id="4" kee="disabled" name="Disabled" long_name="Disabled" scope="PRJ" qualifier="TRK" root_id="1"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/before.xml
new file mode 100644
index 00000000000..8cf83a39b03
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/before.xml
@@ -0,0 +1,28 @@
+<dataset>
+
+ <projects id="1" kee="project" name="project" long_name="project" scope="PRJ" qualifier="TRK" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="[null]"/>
+
+ <projects id="2" kee="view" name="View" long_name="View" scope="PRJ" qualifier="VW" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="[null]"/>
+
+ <!-- File should not be updated -->
+ <projects id="3" kee="file" name="File" long_name="File" scope="FIL" qualifier="CLA" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="[null]"/>
+
+ <projects id="4" kee="disabled" name="Disabled" long_name="Disabled" scope="PRJ" qualifier="TRK" root_id="1"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01" authorization_updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/schema.sql
new file mode 100644
index 00000000000..e10b82bd90e
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/InsertProjectsAuthorizationUpdatedAtMigrationTest/schema.sql
@@ -0,0 +1,18 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "KEE" VARCHAR(400),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "ROOT_ID" INTEGER,
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components.xml
new file mode 100644
index 00000000000..3ea725be763
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components.xml
@@ -0,0 +1,93 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+ <!-- sub module -->
+ <projects id="3" root_id="2" kee="org.struts:struts-db" name="Struts Db"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Db" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2."/>
+
+ <!-- directory -->
+ <projects long_name="org.struts" id="4" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="src/org/struts" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"
+ created_at="2014-06-18"/>
+ <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3."/>
+
+ <!-- file -->
+ <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="RequestContext.java" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java" created_at="2014-06-18"/>
+
+ <snapshots id="5" project_id="5" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3.4."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components_without_uuid.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components_without_uuid.xml
new file mode 100644
index 00000000000..e5f8a0223ed
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_components_without_uuid.xml
@@ -0,0 +1,58 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- module with a snapshot having no islast=true -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+ <!-- file linked on a no more existing project -->
+ <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="RequestContext.java" root_id="999"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java" created_at="2014-06-18"/>
+
+ <snapshots id="5" project_id="5" parent_snapshot_id="999" root_project_id="999" root_snapshot_id="999"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3.4."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_developer.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_developer.xml
new file mode 100644
index 00000000000..68496498454
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_developer.xml
@@ -0,0 +1,40 @@
+<dataset>
+
+ <!-- developer -->
+ <projects id="1" kee="DEV:developer@company.net" name="developer@company.net" long_name="Developer" scope="PRJ"
+ qualifier="DEV" root_id="[null]" description="[null]"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path=""
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="DEV" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- technical project -->
+ <projects id="2" root_id="1" scope="PRJ" qualifier="DEV_PRJ" kee="DEV:developer@company.net:org.struts:struts"
+ name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="10" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="1" scope="PRJ" qualifier="DEV_PRJ" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_disable_components.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_disable_components.xml
new file mode 100644
index 00000000000..47998eecac3
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_disable_components.xml
@@ -0,0 +1,93 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- removed module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" deprecated_kee="[null]"
+ description="[null]" enabled="[false]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+ <!--removed sub module -->
+ <projects id="3" root_id="2" kee="org.struts:struts-db" name="Struts Db"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Db" deprecated_kee="[null]"
+ description="[null]" enabled="[false]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2."/>
+
+ <!-- removed directory -->
+ <projects long_name="org.struts" id="4" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="src/org/struts" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[false]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"
+ created_at="2014-06-18"/>
+ <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3."/>
+
+ <!-- removed file -->
+ <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="RequestContext.java" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[false]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java" created_at="2014-06-18"/>
+
+ <snapshots id="5" project_id="5" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3.4."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_library.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_library.xml
new file mode 100644
index 00000000000..89709dca85d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_library.xml
@@ -0,0 +1,12 @@
+<dataset>
+
+ <!-- library -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="LIB" kee="org.hamcrest:hamcrest-library"
+ name="org.hamcrest:hamcrest-library"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="[null]" long_name="org.hamcrest:hamcrest-library"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_provisioned_project.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_provisioned_project.xml
new file mode 100644
index 00000000000..c485cc06664
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_provisioned_project.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <!-- provisioned project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_view.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_view.xml
new file mode 100644
index 00000000000..b3ae4868b4c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/migrate_view.xml
@@ -0,0 +1,57 @@
+<dataset>
+
+ <!-- view -->
+ <projects id="1" kee="view" name="View" long_name="View" scope="PRJ" qualifier="VW" root_id="[null]"
+ description="[null]"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="VW" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- sub view -->
+ <projects id="2" kee="subView" name="Sub View" long_name="Sub View" scope="PRJ" qualifier="SVW" root_id="1"
+ description="[null]"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-09-01"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="SVW" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+ <!-- technical project -->
+ <projects id="3" root_id="1" scope="FIL" qualifier="TRK" kee="vieworg.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="10" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="1" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_module_has_no_root_id.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_module_has_no_root_id.xml
new file mode 100644
index 00000000000..c0b7676303d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_module_has_no_root_id.xml
@@ -0,0 +1,38 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- module with null root id (probably a project that became a module) -->
+ <projects id="2" root_id="[null]" kee="org.struts:struts-core" name="Struts Core"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_project_has_two_active_snapshots.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_project_has_two_active_snapshots.xml
new file mode 100644
index 00000000000..e42d8158831
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_fail_when_project_has_two_active_snapshots.xml
@@ -0,0 +1,48 @@
+<dataset>
+
+ <!-- root project with 2 snapshots having islast to true-->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+ <snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- module linked on second active snapshot of the project -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="10" root_project_id="1" root_snapshot_id="10"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="10."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_migrate_already_migrated_components.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_migrate_already_migrated_components.xml
new file mode 100644
index 00000000000..76b63671f12
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/not_migrate_already_migrated_components.xml
@@ -0,0 +1,113 @@
+<dataset>
+
+ <!-- root project migrated -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path=""/>
+
+ <!-- module migrated -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core"
+ uuid="BCDE" project_uuid="ABCD" module_uuid="ABCD" module_uuid_path="ABCD"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1."/>
+
+ <!-- sub module not migrated -->
+ <projects id="3" root_id="2" kee="org.struts:struts-db" name="Struts Db"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ scope="PRJ" qualifier="BRC" long_name="Struts Db" deprecated_kee="[null]"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2014-06-18"/>
+ <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2."/>
+
+ <!-- directory not migrated -->
+ <projects long_name="org.struts" id="4" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="src/org/struts" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"
+ created_at="2014-06-18"/>
+ <snapshots id="4" project_id="4" parent_snapshot_id="3" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3."/>
+
+ <!-- file not migrated -->
+ <projects long_name="org.struts.RequestContext" id="5" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ name="RequestContext.java" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java" created_at="2014-06-18"/>
+
+ <snapshots id="5" project_id="5" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3.4."/>
+
+ <!-- removed file linked on module, migrated -->
+ <projects long_name="org.struts.RequestContext2" id="6" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext2.java"
+ uuid="DCBA" project_uuid="ABCD" module_uuid="BCDE" module_uuid_path="ABCD.BCDE"
+ name="RequestContext.java" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[false]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java" created_at="2014-06-18"/>
+
+ <snapshots id="6" project_id="6" parent_snapshot_id="4" root_project_id="1" root_snapshot_id="1"
+ status="P" islast="[false]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00"
+ build_date="2008-12-02 13:58:00.00"
+ version="[null]" path="1.2.3.4."/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/schema.sql
new file mode 100644
index 00000000000..c8fee3449dd
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/PopulateProjectsUuidColumnsMigrationTest/schema.sql
@@ -0,0 +1,54 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "BUILD_DATE" TIMESTAMP,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" TIMESTAMP,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" TIMESTAMP,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" TIMESTAMP,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" TIMESTAMP,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute-result.xml
new file mode 100644
index 00000000000..2be1be35633
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute-result.xml
@@ -0,0 +1,33 @@
+<dataset>
+
+ <issue_filters
+ id="1"
+ name="No sort field"
+ user_login="stephane"
+ shared="[true]"
+ description="no not touch"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="2"
+ name="Has sort field"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|projectUuids=ABC"
+ created_at="2013-06-10"
+ updated_at="2014-10-29 00:00:00.0"/>
+
+ <issue_filters
+ id="3"
+ name="corner-case"
+ user_login="michael"
+ shared="[true]"
+ description="do not touch"
+ data="statuses=CLOSED|resort=true"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute.xml
new file mode 100644
index 00000000000..e3fdb367e19
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/execute.xml
@@ -0,0 +1,33 @@
+<dataset>
+
+ <issue_filters
+ id="1"
+ name="No sort field"
+ user_login="stephane"
+ shared="[true]"
+ description="no not touch"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="2"
+ name="Has sort field"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|asc=true|projectUuids=ABC"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="3"
+ name="corner-case"
+ user_login="michael"
+ shared="[true]"
+ description="do not touch"
+ data="statuses=CLOSED|resort=true"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/schema.sql
new file mode 100644
index 00000000000..0627153a62d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/RemoveSortFieldFromIssueFiltersMigrationTest/schema.sql
@@ -0,0 +1,10 @@
+CREATE TABLE "ISSUE_FILTERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "SHARED" BOOLEAN NOT NULL DEFAULT FALSE,
+ "USER_LOGIN" VARCHAR(255),
+ "DESCRIPTION" VARCHAR(4000),
+ "DATA" CLOB(2147483647),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated-result.xml
new file mode 100644
index 00000000000..7f657ff712c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated-result.xml
@@ -0,0 +1,43 @@
+<dataset>
+
+ <issue_filters
+ id="1"
+ name="Struts Issues"
+ user_login="stephane"
+ shared="[true]"
+ description="All issues of Struts"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="2"
+ name="Open issues"
+ user_login="michael"
+ shared="[false]"
+ description="All open issues"
+ data="statuses=OPEN"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="3"
+ name="Sonar Open issues"
+ user_login="michael"
+ shared="[true]"
+ description="All open issues on Sonar"
+ data="statuses=CLOSED|projectUuids=ABCD|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="4"
+ name="Bad component roots fields"
+ user_login="michael"
+ shared="[true]"
+ description="All open issues on Sonar"
+ data="statuses=CLOSED|projectUuids=|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated.xml
new file mode 100644
index 00000000000..524a2bef4a7
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/do_not_execute_if_already_migrated.xml
@@ -0,0 +1,50 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="."
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+
+ <issue_filters
+ id="1"
+ name="Struts Issues"
+ user_login="stephane"
+ shared="[true]"
+ description="All issues of Struts"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="2"
+ name="Open issues"
+ user_login="michael"
+ shared="[false]"
+ description="All open issues"
+ data="statuses=OPEN"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="3"
+ name="Sonar Open issues"
+ user_login="michael"
+ shared="[true]"
+ description="All open issues on Sonar"
+ data="statuses=CLOSED|projectUuids=ABCD|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="4"
+ name="Bad component roots fields"
+ user_login="michael"
+ shared="[true]"
+ description="All open issues on Sonar"
+ data="statuses=CLOSED|projectUuids=|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute-result.xml
new file mode 100644
index 00000000000..3d2bb6937e5
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute-result.xml
@@ -0,0 +1,63 @@
+<dataset>
+
+ <issue_filters
+ id="1"
+ name="Struts Issues"
+ user_login="stephane"
+ shared="[true]"
+ description="All issues of Struts"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2014-10-29"/>
+
+ <issue_filters
+ id="2"
+ name="Open issues"
+ user_login="michael"
+ shared="[false]"
+ description="All open issues"
+ data="statuses=OPEN"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="3"
+ name="Sonar Open issues"
+ user_login="michael"
+ shared="[true]"
+ description="All open issues on Sonar"
+ data="statuses=CLOSED|projectUuids=ABCD|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2014-10-29"/>
+
+ <issue_filters
+ id="4"
+ name="Bad component roots fields"
+ user_login="michael"
+ shared="[true]"
+ description="Bad component roots fields"
+ data="statuses=CLOSED||resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2014-10-29"/>
+
+ <issue_filters
+ id="5"
+ name="Linked on not existing file"
+ user_login="michael"
+ shared="[true]"
+ description="Linked on not existing file"
+ data="statuses=CLOSED||resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2014-10-29"/>
+
+ <issue_filters
+ id="6"
+ name="Empty data"
+ user_login="michael"
+ shared="[true]"
+ description="Empty data"
+ data="[null]"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute.xml b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute.xml
new file mode 100644
index 00000000000..a952ef40542
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/execute.xml
@@ -0,0 +1,70 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="."
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+
+ <issue_filters
+ id="1"
+ name="Struts Issues"
+ user_login="stephane"
+ shared="[true]"
+ description="All issues of Struts"
+ data="componentRoots=org.struts:struts"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="2"
+ name="Open issues"
+ user_login="michael"
+ shared="[false]"
+ description="All open issues"
+ data="statuses=OPEN"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="3"
+ name="Sonar Open issues"
+ user_login="michael"
+ shared="[true]"
+ description="All open issues on Sonar"
+ data="statuses=CLOSED|componentRoots=org.struts:struts|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="4"
+ name="Bad component roots fields"
+ user_login="michael"
+ shared="[true]"
+ description="Bad component roots fields"
+ data="statuses=CLOSED|componentRoots=|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="5"
+ name="Linked on not existing file"
+ user_login="michael"
+ shared="[true]"
+ description="Linked on not existing file"
+ data="statuses=CLOSED|componentRoots=unknown|resolution=FIXED"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <issue_filters
+ id="6"
+ name="Empty data"
+ user_login="michael"
+ shared="[true]"
+ description="Empty data"
+ data="[null]"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/schema.sql
new file mode 100644
index 00000000000..a1182dd7d46
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v50/ReplaceIssueFiltersProjectKeyByUuidTest/schema.sql
@@ -0,0 +1,32 @@
+CREATE TABLE "ISSUE_FILTERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "SHARED" BOOLEAN NOT NULL DEFAULT FALSE,
+ "USER_LOGIN" VARCHAR(255),
+ "DESCRIPTION" VARCHAR(4000),
+ "DATA" CLOB(2147483647),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddIssuesColumnsTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddIssuesColumnsTest/schema.sql
new file mode 100644
index 00000000000..3e799c31508
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddIssuesColumnsTest/schema.sql
@@ -0,0 +1,26 @@
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50) UNIQUE NOT NULL,
+ "COMPONENT_ID" INTEGER NOT NULL,
+ "ROOT_COMPONENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "EFFORT_TO_FIX" DOUBLE,
+ "TECHNICAL_DEBT" INTEGER,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "ISSUE_CREATION_DATE" TIMESTAMP,
+ "ISSUE_CLOSE_DATE" TIMESTAMP,
+ "ISSUE_UPDATE_DATE" TIMESTAMP,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/do_nothing_when_already_migrated.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/do_nothing_when_already_migrated.xml
new file mode 100644
index 00000000000..5df0387b5f8
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/do_nothing_when_already_migrated.xml
@@ -0,0 +1,73 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="2" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="3" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="4" kee="PORTABILITY_COMPLIANCE" name="Portability Compliance" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="5" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="6" kee="MAINTAINABILITY_COMPLIANCE" name="Maintainability Compliance" parent_id="5"
+ rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="7" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="8" kee="SECURITY_COMPLIANCE" name="Security Compliance" parent_id="7" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+
+ <characteristics id="9" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="5"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="10" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="9" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="11" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="9" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="12" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="9" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="13" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]"
+ characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="14" kee="EFFICIENCY_COMPLIANCE" name="Efficiency Compliance" parent_id="13" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="15" kee="CHANGEABILITY" name="Changeability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="7" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="16" kee="CHANGEABILITY_COMPLIANCE" name="Changeability Compliance" parent_id="15"
+ rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="17" kee="RELIABILITY" name="Reliability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="8" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="18" kee="RELIABILITY_COMPLIANCE" name="Reliability Compliance" parent_id="17" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="19" kee="TESTABILITY" name="Testability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="9" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-20"/>
+ <characteristics id="20" kee="TESTABILITY_COMPLIANCE" name="Testability Compliance" parent_id="19" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/empty.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/empty.xml
new file mode 100644
index 00000000000..871dedcb5e9
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/empty.xml
@@ -0,0 +1,3 @@
+<dataset>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_as_characteristic.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_as_characteristic.xml
new file mode 100644
index 00000000000..72521ba290d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_as_characteristic.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="REUSABILITY_COMPLIANCE" name="Compliance" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml
new file mode 100644
index 00000000000..62a08ca6f7c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_compliance_already_exists_under_wrong_characteristic.xml
@@ -0,0 +1,16 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="3" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="2" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20"
+ updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_usability_exists_as_sub_characteristic.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_usability_exists_as_sub_characteristic.xml
new file mode 100644
index 00000000000..9db2b164d0d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/fail_if_usability_exists_as_sub_characteristic.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="USABILITY" name="Usability" parent_id="1" rule_id="[null]" characteristic_order="[null]"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists-result.xml
new file mode 100644
index 00000000000..62b66901fe8
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists-result.xml
@@ -0,0 +1,38 @@
+<dataset>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- New characteristic 'Usability' is on the top (order 1) -->
+ <characteristics id="3" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="1"
+ enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="4" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="5" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="6" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+
+ <!-- New sub characteristic 'Compliance' under Reusability -->
+ <characteristics id="7" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Portability -->
+ <characteristics id="8" kee="PORTABILITY_COMPLIANCE" name="Portability Compliance" parent_id="2" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists.xml
new file mode 100644
index 00000000000..a8394be84fa
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/insert_usability_at_the_top_if_security_does_exists.xml
@@ -0,0 +1,11 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate-result.xml
new file mode 100644
index 00000000000..b677f0f351a
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate-result.xml
@@ -0,0 +1,94 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="3" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="4" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="5" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]"
+ characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="6" kee="CHANGEABILITY" name="Changeability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="7" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="7" kee="RELIABILITY" name="Reliability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="8" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="8" kee="TESTABILITY" name="Testability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="9" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- New characteristic 'Usability' is after Security -->
+ <characteristics id="9" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="5"
+ enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <!-- New sub characteristics under Usability -->
+ <characteristics id="10" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="9" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="11" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="9" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="12" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="9" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Reusability -->
+ <characteristics id="13" kee="REUSABILITY_COMPLIANCE" name="Reusability Compliance" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Portability -->
+ <characteristics id="14" kee="PORTABILITY_COMPLIANCE" name="Portability Compliance" parent_id="2" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Maintainability -->
+ <characteristics id="15" kee="MAINTAINABILITY_COMPLIANCE" name="Maintainability Compliance" parent_id="3"
+ rule_id="[null]" characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Security -->
+ <characteristics id="16" kee="SECURITY_COMPLIANCE" name="Security Compliance" parent_id="4" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Efficiency -->
+ <characteristics id="17" kee="EFFICIENCY_COMPLIANCE" name="Efficiency Compliance" parent_id="5" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Changeability -->
+ <characteristics id="18" kee="CHANGEABILITY_COMPLIANCE" name="Changeability Compliance" parent_id="6" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Reliability -->
+ <characteristics id="19" kee="RELIABILITY_COMPLIANCE" name="Reliability Compliance" parent_id="7" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Testability -->
+ <characteristics id="20" kee="TESTABILITY_COMPLIANCE" name="Testability Compliance" parent_id="8" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate.xml
new file mode 100644
index 00000000000..52505623c3d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/migrate.xml
@@ -0,0 +1,35 @@
+<dataset>
+
+ <characteristics id="1" kee="REUSABILITY" name="Reusability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="1" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="PORTABILITY" name="Portability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="2" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="3" kee="MAINTAINABILITY" name="Maintainability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="3" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="4" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="5" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]"
+ characteristic_order="5" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="6" kee="CHANGEABILITY" name="Changeability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="7" kee="RELIABILITY" name="Reliability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="7" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="8" kee="TESTABILITY" name="Testability" parent_id="[null]" rule_id="[null]"
+ characteristic_order="8" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml
new file mode 100644
index 00000000000..81050d0ee0c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/not_fail_if_some_deprecated_requirements_still_exists_in_db.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <characteristics id="1" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="1"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+ <characteristics id="2" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="3" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2013-11-20"
+ updated_at="[null]"/>
+ <characteristics id="4" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="5" kee="[null]" name="[null]" parent_id="3" rule_id="3" characteristic_order="[null]"
+ enabled="[true]" created_at="2013-11-20" updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/schema.sql
new file mode 100644
index 00000000000..98c025def6b
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/schema.sql
@@ -0,0 +1,11 @@
+CREATE TABLE "CHARACTERISTICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(100),
+ "NAME" VARCHAR(100),
+ "PARENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "CHARACTERISTIC_ORDER" INTEGER,
+ "ENABLED" BOOLEAN,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists-result.xml
new file mode 100644
index 00000000000..86851f3d471
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists-result.xml
@@ -0,0 +1,38 @@
+<dataset>
+
+ <characteristics id="1" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <!-- Oder has changed : this characteristic is now one step lower -->
+ <characteristics id="2" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]"
+ characteristic_order="6" enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- Usability is moved after Security -->
+ <characteristics id="3" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="5"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2015-02-15"/>
+
+ <!-- New sub characteristics under Usability -->
+ <characteristics id="4" kee="USABILITY_ACCESSIBILITY" name="Accessibility" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="5" kee="USABILITY_EASE_OF_USE" name="Ease of Use" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+ <characteristics id="6" kee="USABILITY_COMPLIANCE" name="Usability Compliance" parent_id="3" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Security -->
+ <characteristics id="7" kee="SECURITY_COMPLIANCE" name="Security Compliance" parent_id="1" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]" created_at="2015-02-15"
+ updated_at="[null]"/>
+
+ <!-- New sub characteristic 'Compliance' under Efficiency -->
+ <characteristics id="8" kee="EFFICIENCY_COMPLIANCE" name="Efficiency Compliance" parent_id="2" rule_id="[null]"
+ characteristic_order="[null]" enabled="[true]"
+ created_at="2015-02-15" updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists.xml
new file mode 100644
index 00000000000..2e75b7324e7
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/AddNewCharacteristicsTest/update_usability_if_already_exists.xml
@@ -0,0 +1,16 @@
+<dataset>
+
+ <characteristics id="1" kee="SECURITY" name="Security" parent_id="[null]" rule_id="[null]" characteristic_order="4"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <characteristics id="2" kee="EFFICIENCY" name="Efficiency" parent_id="[null]" rule_id="[null]"
+ characteristic_order="5" enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+ <!-- Usability should be move after Security -->
+ <characteristics id="3" kee="USABILITY" name="Usability" parent_id="[null]" rule_id="[null]" characteristic_order="6"
+ enabled="[true]" created_at="2013-11-20"
+ updated_at="2013-11-22"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/before.xml
new file mode 100644
index 00000000000..0a7b5aa0cf1
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/before.xml
@@ -0,0 +1,49 @@
+<dataset>
+
+ <!-- Should contains Simon B, simon and simon@codehaus.org -->
+ <users id="1" login="simon" name="Simon" active="[true]" email="simon@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <!-- Authors contains login, email and fab -> SCM accounts should contains only fab -->
+ <users id="2" login="fabrice" name="Fabrice" active="[true]" email="fabrice@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <!-- Authors contains only login and email -> Nothing to do -->
+ <users id="3" login="jb" name="Jean Baptiste" active="[true]" email="jb@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <!-- Only one author row -> Nothing to do -->
+ <users id="4" login="julien" name="Julien" active="[true]" email="julien@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <!-- Disable user -> Nothing to do -->
+ <users id="5" login="disable" name="Disable" active="[false]" email="disable@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <!-- 2 users with the same email -> Nothing to do -->
+ <users id="6" login="teryk" name="Teryk" active="[true]" email="teryk@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+ <users id="7" login="teryk2" name="Teryk" active="[true]" email="teryk@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+
+ <authors id="1" person_id="1" login="Simon B" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="2" person_id="1" login="simon" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="3" person_id="1" login="simon@codehaus.org" created_at="2015-01-01" updated_at="2015-01-01"/>
+
+ <authors id="4" person_id="2" login="fabrice@email.com" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="5" person_id="2" login="fab" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="6" person_id="2" login="fabrice" created_at="2015-01-01" updated_at="2015-01-01"/>
+
+ <authors id="7" person_id="3" login="jb@email.com" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="8" person_id="3" login="jb" created_at="2015-01-01" updated_at="2015-01-01"/>
+
+ <authors id="9" person_id="4" login="julien" created_at="2015-01-01" updated_at="2015-01-01"/>
+
+ <authors id="10" person_id="5" login="disable" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="11" person_id="5" login="Disable user" created_at="2015-01-01" updated_at="2015-01-01"/>
+
+ <authors id="12" person_id="6" login="teryk@email.com" created_at="2015-01-01" updated_at="2015-01-01"/>
+ <authors id="13" person_id="6" login="teryk_b" created_at="2015-01-01" updated_at="2015-01-01"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/no_authors.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/no_authors.xml
new file mode 100644
index 00000000000..3f2da7a6af2
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/no_authors.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <users id="1" login="simon" name="Simon" active="[true]" email="simon@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <users id="2" login="fabrice" name="Fabrice" active="[true]" email="fabrice@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <users id="3" login="jb" name="Jean Baptiste" active="[true]" email="jb@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <users id="4" login="julien" name="Julien" active="[true]" email="julien@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <users id="5" login="disable" name="Disable" active="[false]" email="disable@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <users id="6" login="teryk" name="Teryk" active="[true]" email="teryk@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+ <users id="7" login="teryk2" name="Teryk" active="[true]" email="teryk@email.com" scm_accounts="[null]"
+ created_at="1500000000000" updated_at="1500000000000"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/schema.sql
new file mode 100644
index 00000000000..8dbd8b01ea9
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/CopyScmAccountsFromAuthorsToUsersTest/schema.sql
@@ -0,0 +1,22 @@
+CREATE TABLE "USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(255),
+ "NAME" VARCHAR(200),
+ "EMAIL" VARCHAR(100),
+ "CRYPTED_PASSWORD" VARCHAR(40),
+ "SALT" VARCHAR(40),
+ "REMEMBER_TOKEN" VARCHAR(500),
+ "REMEMBER_TOKEN_EXPIRES_AT" TIMESTAMP,
+ "ACTIVE" BOOLEAN DEFAULT TRUE,
+ "SCM_ACCOUNTS" VARCHAR(4000),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "AUTHORS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PERSON_ID" INTEGER,
+ "LOGIN" VARCHAR(100),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/before.xml
new file mode 100644
index 00000000000..c0d5b5e59ec
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/before.xml
@@ -0,0 +1,52 @@
+<dataset>
+ <!-- new migration -->
+ <analysis_reports
+ id="1"
+ project_key="123456789-987654321"
+ snapshot_id="123"
+ report_data="data-project"
+ report_status="WORKING"
+ created_at="2014-09-25"
+ updated_at="2014-09-24"
+ started_at="2014-09-23"
+ finished_at="2014-09-22"
+ created_at_ms="[null]"
+ updated_at_ms="[null]"
+ started_at_ms="[null]"
+ finished_at_ms="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <analysis_reports
+ id="2"
+ project_key="123456789-987654321"
+ snapshot_id="123"
+ report_data="data-project"
+ report_status="WORKING"
+ created_at="2014-09-25"
+ updated_at="2014-09-24"
+ started_at="2014-09-23"
+ finished_at="2014-09-22"
+ created_at_ms="1500000000000"
+ updated_at_ms="1500000000000"
+ started_at_ms="1500000000000"
+ finished_at_ms="1500000000000"
+ />
+
+ <!-- NULL dates -->
+ <analysis_reports
+ id="3"
+ project_key="123456789-987654321"
+ snapshot_id="123"
+ report_data="data-project"
+ report_status="WORKING"
+ created_at="[null]"
+ updated_at="[null]"
+ started_at="[null]"
+ finished_at="[null]"
+ created_at_ms="[null]"
+ updated_at_ms="[null]"
+ started_at_ms="[null]"
+ finished_at_ms="[null]"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/schema.sql
new file mode 100644
index 00000000000..c2187b6a747
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedAnalysisReportsLongDatesTest/schema.sql
@@ -0,0 +1,16 @@
+CREATE TABLE "ANALYSIS_REPORTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_KEY" VARCHAR(400) NOT NULL,
+ "PROJECT_NAME" VARCHAR(256) NULL,
+ "SNAPSHOT_ID" INTEGER NOT NULL,
+ "REPORT_STATUS" VARCHAR(20) NOT NULL,
+ "REPORT_DATA" BLOB(2147483647),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+ "STARTED_AT" TIMESTAMP,
+ "FINISHED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "UPDATED_AT_MS" BIGINT,
+ "STARTED_AT_MS" BIGINT,
+ "FINISHED_AT_MS" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/before.xml
new file mode 100644
index 00000000000..db9753d85be
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/before.xml
@@ -0,0 +1,28 @@
+<dataset>
+ <!-- new migration -->
+ <events
+ id="1"
+ created_at="2014-09-25"
+ created_at_ms="[null]"
+ event_date="2014-09-25"
+ event_date_ms="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the ones that are already fed with new dates -->
+ <events
+ id="2"
+ created_at="2014-09-25"
+ created_at_ms="1500000000"
+ event_date="2014-09-25"
+ event_date_ms="1500000000"
+ />
+
+ <!-- NULL dates -->
+ <events
+ id="3"
+ created_at="[null]"
+ created_at_ms="[null]"
+ event_date="[null]"
+ event_date_ms="[null]"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/schema.sql
new file mode 100644
index 00000000000..71ac42d40ef
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedEventsLongDatesTest/schema.sql
@@ -0,0 +1,7 @@
+CREATE TABLE "EVENTS" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "EVENT_DATE" TIMESTAMP,
+ "EVENT_DATE_MS" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/bad_data.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/bad_data.xml
new file mode 100644
index 00000000000..b4e95d8a3ea
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/bad_data.xml
@@ -0,0 +1,8 @@
+<dataset>
+ <file_sources id="1" project_uuid="PROJECT_UUID" file_uuid="FILE1_UUID" created_at="1416238020000"
+ updated_at="1414770242000"
+ data="&quot;missing_escape_end"
+ binary_data="[null]"
+ line_hashes=""
+ data_hash=""/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/data.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/data.xml
new file mode 100644
index 00000000000..df9b36ac5a2
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/data.xml
@@ -0,0 +1,25 @@
+<dataset>
+ <!-- has data -->
+ <file_sources id="1" project_uuid="PROJECT_UUID" file_uuid="FILE1_UUID" created_at="1416238020000"
+ updated_at="1414770242000"
+ data="aef12a,alice,2014-04-25T12:34:56+0100,1,4,2,2,5,3,3,6,4,syntax_highlighting,symbol_refs,22,class Foo {&#13;&#10;abe465,bob,2014-07-25T12:34:56+0100,,,,,,,,,,,,2, // Empty&#13;&#10;afb789,carol,2014-03-23T12:34:56+0100,0,,,0,,,0,,,,,,}&#13;&#10;afb789,carol,2014-03-23T12:34:56+0100,,,,,,,,,,,,,&#13;&#10;"
+ binary_data="[null]"
+ line_hashes=""
+ data_hash=""/>
+
+ <!-- empty fields in CSV -->
+ <file_sources id="2" project_uuid="PROJECT_UUID" file_uuid="FILE2_UUID" created_at="1416238020000"
+ updated_at="1414770242000"
+ data=",,,,,,,,,,,,,,,&#10;,,,,,,,,,,,,,,,&#10;,,,,,,,,,,,,,,,&#10;,,,,,,,,,,,,,,,&#10;"
+ binary_data="[null]"
+ line_hashes=""
+ data_hash=""/>
+
+ <!-- null CSV -->
+ <file_sources id="3" project_uuid="PROJECT_UUID" file_uuid="FILE2_UUID" created_at="1416238020000"
+ updated_at="1414770242000"
+ data="[null]"
+ binary_data="[null]"
+ line_hashes=""
+ data_hash=""/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/schema.sql
new file mode 100644
index 00000000000..5649f795864
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedFileSourcesBinaryDataTest/schema.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "FILE_SOURCES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "FILE_UUID" VARCHAR(50) NOT NULL,
+ "LINE_HASHES" CLOB(2147483647),
+ "DATA" CLOB(2147483647),
+ "DATA_HASH" VARCHAR(50) NOT NULL,
+ "SRC_HASH" VARCHAR(50) NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL,
+ "BINARY_DATA" BINARY(167772150),
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/before.xml
new file mode 100644
index 00000000000..e8e59f0d053
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/before.xml
@@ -0,0 +1,31 @@
+<dataset>
+
+ <!-- new migration -->
+ <issue_changes id="1" kee="ABC-DEF" issue_key="ABC" user_login="[null]" change_type="[null]" change_data="[null]"
+ created_at="2013-05-18"
+ updated_at="2013-05-18"
+ issue_change_creation_date="2013-05-18"
+ created_at_ms="[null]"
+ updated_at_ms="[null]"
+ issue_change_creation_date_ms="[null]"/>
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <issue_changes id="2" kee="FGH-DEF" issue_key="FGH" user_login="[null]" change_type="[null]" change_data="[null]"
+ created_at="2013-05-18"
+ updated_at="2013-05-18"
+ issue_change_creation_date="2013-05-18"
+ created_at_ms="1500000000000"
+ updated_at_ms="1500000000000"
+ issue_change_creation_date_ms="1500000000000"/>
+
+ <!-- NULL dates -->
+ <issue_changes id="3" kee="MISSING-DEF" issue_key="MISSING" user_login="[null]" change_type="[null]"
+ change_data="[null]"
+ created_at="[null]"
+ updated_at="[null]"
+ issue_change_creation_date="[null]"
+ created_at_ms="[null]"
+ updated_at_ms="[null]"
+ issue_change_creation_date_ms="[null]"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/schema.sql
new file mode 100644
index 00000000000..240e5cedf2d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueChangesLongDatesTest/schema.sql
@@ -0,0 +1,14 @@
+CREATE TABLE "ISSUE_CHANGES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50),
+ "ISSUE_KEY" VARCHAR(50) NOT NULL,
+ "USER_LOGIN" VARCHAR(255),
+ "CHANGE_TYPE" VARCHAR(40),
+ "CHANGE_DATA" VARCHAR(16777215),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+ "ISSUE_CHANGE_CREATION_DATE" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "UPDATED_AT_MS" BIGINT,
+ "ISSUE_CHANGE_CREATION_DATE_MS" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/after-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/after-result.xml
new file mode 100644
index 00000000000..0422433b393
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/after-result.xml
@@ -0,0 +1,22 @@
+<dataset>
+ <issues id="1" kee="ABC"
+ component_id="100" component_uuid="COMPONENTUUID" root_component_id="10" project_uuid="PROJECTUUID"
+ resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]" author_login="[null]" checksum="[null]" effort_to_fix="[null]" technical_debt="10"
+ message="[null]" line="5000" rule_id="10" reporter="emmerik" issue_attributes="foo=bar"
+ action_plan_key="[null]" tags="[null]"
+ issue_creation_date="2013-05-18" issue_update_date="2013-05-18" issue_close_date="2013-05-18"
+ created_at="1500000000000" updated_at="1500000000000"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with uuids -->
+ <issues id="2" kee="DEF"
+ component_id="101" component_uuid="ANOTHERUUID" root_component_id="11" project_uuid="ANOTHER2UUID"
+ resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]" author_login="[null]" checksum="[null]" effort_to_fix="[null]" technical_debt="10"
+ message="[null]" line="5000" rule_id="10" reporter="emmerik" issue_attributes="foo=bar"
+ action_plan_key="[null]" tags="[null]"
+ issue_creation_date="2013-05-18" issue_update_date="2013-05-18" issue_close_date="2013-05-18"
+ created_at="1500000000000" updated_at="1500000000000"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/before.xml
new file mode 100644
index 00000000000..fbcd05d6875
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/before.xml
@@ -0,0 +1,30 @@
+<dataset>
+ <projects long_name="org.struts.RequestContext" id="100" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="COMPONENTUUID" project_uuid="PROJECTUUID" module_uuid="[null]" module_uuid_path="[null]"
+ name="RequestContext.java" root_id="2"
+ description="[null]" deprecated_kee="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java" created_at="2014-06-18"/>
+
+ <issues id="1" kee="ABC"
+ component_id="100" component_uuid="[null]" root_component_id="10" project_uuid="[null]"
+ resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]" author_login="[null]" checksum="[null]" effort_to_fix="[null]" technical_debt="10"
+ message="[null]" line="5000" rule_id="10" reporter="emmerik" issue_attributes="foo=bar"
+ action_plan_key="[null]" tags="[null]"
+ issue_creation_date="2013-05-18" issue_update_date="2013-05-18" issue_close_date="2013-05-18"
+ created_at="1500000000000" updated_at="1500000000000"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with uuids -->
+ <issues id="2" kee="DEF"
+ component_id="101" component_uuid="ANOTHERUUID" root_component_id="11" project_uuid="ANOTHER2UUID"
+ resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]" author_login="[null]" checksum="[null]" effort_to_fix="[null]" technical_debt="10"
+ message="[null]" line="5000" rule_id="10" reporter="emmerik" issue_attributes="foo=bar"
+ action_plan_key="[null]" tags="[null]"
+ issue_creation_date="2013-05-18" issue_update_date="2013-05-18" issue_close_date="2013-05-18"
+ created_at="1500000000000" updated_at="1500000000000"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/schema.sql
new file mode 100644
index 00000000000..b7157762feb
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueComponentUuidsTest/schema.sql
@@ -0,0 +1,52 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50) UNIQUE NOT NULL,
+ "COMPONENT_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "ROOT_COMPONENT_ID" INTEGER,
+ "PROJECT_UUID" VARCHAR(50),
+ "RULE_ID" INTEGER,
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "EFFORT_TO_FIX" DOUBLE,
+ "TECHNICAL_DEBT" INTEGER,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "TAGS" VARCHAR(4000),
+ "ISSUE_CREATION_DATE" TIMESTAMP,
+ "ISSUE_CLOSE_DATE" TIMESTAMP,
+ "ISSUE_UPDATE_DATE" TIMESTAMP,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/after-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/after-result.xml
new file mode 100644
index 00000000000..329a3f58f48
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/after-result.xml
@@ -0,0 +1,135 @@
+<dataset>
+
+ <issues id="1" kee="ABC" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="10"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="tag1,tag2,tag3,tag4"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1418056380000"
+ />
+
+ <issues id="2" kee="DEF" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="10"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ tags="polop,palap"
+ action_plan_key="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <issues id="3" kee="GHI" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="20"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="tag3,tag4"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1418056380000"
+ />
+
+ <issues id="4" kee="JKL" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="30"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="tag1,tag2"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1418056380000"
+ />
+
+ <issues id="5" kee="MNO" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="40"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <issues id="6" kee="PQR" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="666"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/before.xml
new file mode 100644
index 00000000000..63b14662f3d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/before.xml
@@ -0,0 +1,185 @@
+<dataset>
+
+ <rules id="10" plugin_rule_key="Rule1" plugin_name="xoo" name="Rule1" description="Rule1" status="READY" priority="1"
+ language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" plugin_config_key="[null]"
+ tags="tag3,tag4" system_tags="tag1,tag2"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+
+ <rules id="20" plugin_rule_key="Rule2" plugin_name="xoo" name="Rule2" description="Rule2" status="READY" priority="1"
+ language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" plugin_config_key="[null]"
+ tags="tag3,tag4" system_tags=""
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+
+ <rules id="30" plugin_rule_key="Rule3" plugin_name="xoo" name="Rule3" description="Rule3" status="READY" priority="1"
+ language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" plugin_config_key="[null]"
+ tags="[null]" system_tags="tag1,tag2"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+
+ <rules id="40" plugin_rule_key="Rule4" plugin_name="xoo" name="Rule4" description="Rule4" status="READY" priority="1"
+ language="xoo"
+ note_data="[null]" note_user_login="[null]" note_created_at="[null]" note_updated_at="[null]"
+ description_format="HTML" plugin_config_key="[null]"
+ tags="[null]" system_tags="[null]"
+ characteristic_id="[null]" default_characteristic_id="[null]"
+ remediation_function="[null]" default_remediation_function="[null]"
+ remediation_coeff="[null]" default_remediation_coeff="[null]"
+ remediation_offset="[null]" default_remediation_offset="[null]"
+ effort_to_fix_description="[null]"
+ is_template="[false]" template_id="[null]" created_at="2014-01-01" updated_at="2014-01-01"/>
+
+
+ <issues id="1" kee="ABC" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="10"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <issues id="2" kee="DEF" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="10"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ tags="polop,palap"
+ action_plan_key="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <issues id="3" kee="GHI" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="20"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <issues id="4" kee="JKL" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="30"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <issues id="5" kee="MNO" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="40"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+ <issues id="6" kee="PQR" resolution="OPEN" status="OPEN" severity="BLOCKER" manual_severity="[false]"
+ assignee="[null]"
+ author_login="[null]"
+ checksum="[null]"
+ effort_to_fix="[null]"
+ technical_debt="10"
+ message="[null]"
+ line="5000"
+ component_id="100"
+ root_component_id="10"
+ rule_id="666"
+ reporter="emmerik"
+ issue_attributes="foo=bar"
+ action_plan_key="[null]"
+ tags="[null]"
+ issue_creation_date="2013-05-18"
+ issue_update_date="2013-05-18"
+ issue_close_date="2013-05-18"
+ created_at="1500000000000"
+ updated_at="1500000000000"
+ />
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/schema.sql
new file mode 100644
index 00000000000..a5ae2fca911
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssueTagsTest/schema.sql
@@ -0,0 +1,59 @@
+CREATE TABLE "RULES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+ "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+ "DESCRIPTION" VARCHAR(16777215),
+ "DESCRIPTION_FORMAT" VARCHAR(20),
+ "PRIORITY" INTEGER,
+ "IS_TEMPLATE" BOOLEAN DEFAULT FALSE,
+ "TEMPLATE_ID" INTEGER,
+ "PLUGIN_CONFIG_KEY" VARCHAR(500),
+ "NAME" VARCHAR(200),
+ "STATUS" VARCHAR(40),
+ "LANGUAGE" VARCHAR(20),
+ "NOTE_DATA" CLOB(2147483647),
+ "NOTE_USER_LOGIN" VARCHAR(255),
+ "NOTE_CREATED_AT" TIMESTAMP,
+ "NOTE_UPDATED_AT" TIMESTAMP,
+ "CHARACTERISTIC_ID" INTEGER,
+ "DEFAULT_CHARACTERISTIC_ID" INTEGER,
+ "REMEDIATION_FUNCTION" VARCHAR(20),
+ "DEFAULT_REMEDIATION_FUNCTION" VARCHAR(20),
+ "REMEDIATION_COEFF" VARCHAR(20),
+ "DEFAULT_REMEDIATION_COEFF" VARCHAR(20),
+ "REMEDIATION_OFFSET" VARCHAR(20),
+ "DEFAULT_REMEDIATION_OFFSET" VARCHAR(20),
+ "EFFORT_TO_FIX_DESCRIPTION" VARCHAR(4000),
+ "TAGS" VARCHAR(4000),
+ "SYSTEM_TAGS" VARCHAR(4000),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(50) UNIQUE NOT NULL,
+ "COMPONENT_ID" INTEGER NOT NULL,
+ "ROOT_COMPONENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "SEVERITY" VARCHAR(10),
+ "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+ "MESSAGE" VARCHAR(4000),
+ "LINE" INTEGER,
+ "EFFORT_TO_FIX" DOUBLE,
+ "TECHNICAL_DEBT" INTEGER,
+ "STATUS" VARCHAR(20),
+ "RESOLUTION" VARCHAR(20),
+ "CHECKSUM" VARCHAR(1000),
+ "REPORTER" VARCHAR(255),
+ "ASSIGNEE" VARCHAR(255),
+ "AUTHOR_LOGIN" VARCHAR(255),
+ "ACTION_PLAN_KEY" VARCHAR(50) NULL,
+ "ISSUE_ATTRIBUTES" VARCHAR(4000),
+ "TAGS" VARCHAR(4000),
+ "ISSUE_CREATION_DATE" TIMESTAMP,
+ "ISSUE_CLOSE_DATE" TIMESTAMP,
+ "ISSUE_UPDATE_DATE" TIMESTAMP,
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/before.xml
new file mode 100644
index 00000000000..be66fff5c0b
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/before.xml
@@ -0,0 +1,34 @@
+<dataset>
+ <!-- new migration -->
+ <issues
+ id="1"
+ issue_creation_date="2014-09-25"
+ issue_creation_date_ms="[null]"
+ issue_update_date="2014-09-25"
+ issue_update_date_ms="[null]"
+ issue_close_date="2014-09-25"
+ issue_close_date_ms="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <issues
+ id="2"
+ issue_creation_date="2014-09-25"
+ issue_creation_date_ms="1500000000"
+ issue_update_date="2014-09-25"
+ issue_update_date_ms="1500000000"
+ issue_close_date="2014-09-25"
+ issue_close_date_ms="1500000000"
+ />
+
+ <!-- NULL dates -->
+ <issues
+ id="3"
+ issue_creation_date="[null]"
+ issue_creation_date_ms="[null]"
+ issue_update_date="[null]"
+ issue_update_date_ms="[null]"
+ issue_close_date="[null]"
+ issue_close_date_ms="[null]"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/schema.sql
new file mode 100644
index 00000000000..ab749851864
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedIssuesLongDatesTest/schema.sql
@@ -0,0 +1,9 @@
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "ISSUE_CREATION_DATE" TIMESTAMP,
+ "ISSUE_CREATION_DATE_MS" BIGINT,
+ "ISSUE_CLOSE_DATE" TIMESTAMP,
+ "ISSUE_CLOSE_DATE_MS" BIGINT,
+ "ISSUE_UPDATE_DATE" TIMESTAMP,
+ "ISSUE_UPDATE_DATE_MS" BIGINT,
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/before.xml
new file mode 100644
index 00000000000..e90f89769b1
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/before.xml
@@ -0,0 +1,28 @@
+<dataset>
+ <!-- new migration -->
+ <manual_measures
+ id="1"
+ created_at="2014-09-25"
+ created_at_ms="[null]"
+ updated_at="2014-09-25"
+ updated_at_ms="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the ones that are already fed with new dates -->
+ <manual_measures
+ id="2"
+ created_at="2014-09-25"
+ created_at_ms="1500000000"
+ updated_at="2014-09-25"
+ updated_at_ms="1500000000"
+ />
+
+ <!-- NULL dates -->
+ <manual_measures
+ id="3"
+ created_at="[null]"
+ created_at_ms="[null]"
+ updated_at="[null]"
+ updated_at_ms="[null]"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/schema.sql
new file mode 100644
index 00000000000..c04fbbeb80e
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedManualMeasuresLongDatesTest/schema.sql
@@ -0,0 +1,7 @@
+CREATE TABLE "MANUAL_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "UPDATED_AT" TIMESTAMP,
+ "UPDATED_AT_MS" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/before.xml
new file mode 100644
index 00000000000..088fd0873f8
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/before.xml
@@ -0,0 +1,61 @@
+<dataset>
+ <!-- new migration -->
+ <snapshots
+ id="1"
+ project_id="1"
+ created_at="2014-09-25"
+ created_at_ms="[null]"
+ build_date="2014-09-25"
+ build_date_ms="[null]"
+ period1_date="2014-09-25"
+ period1_date_ms="[null]"
+ period2_date="2014-09-25"
+ period2_date_ms="[null]"
+ period3_date="2014-09-25"
+ period3_date_ms="[null]"
+ period4_date="2014-09-25"
+ period4_date_ms="[null]"
+ period5_date="2014-09-25"
+ period5_date_ms="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <snapshots
+ id="2"
+ project_id="1"
+ created_at="2014-09-25"
+ created_at_ms="1500000000"
+ build_date="2014-09-25"
+ build_date_ms="1500000000"
+ period1_date="2014-09-25"
+ period1_date_ms="1500000000"
+ period2_date="2014-09-25"
+ period2_date_ms="1500000000"
+ period3_date="2014-09-25"
+ period3_date_ms="1500000000"
+ period4_date="2014-09-25"
+ period4_date_ms="1500000000"
+ period5_date="2014-09-25"
+ period5_date_ms="1500000000"
+ />
+
+ <!-- NULL dates -->
+ <snapshots
+ id="3"
+ project_id="1"
+ created_at="[null]"
+ created_at_ms="[null]"
+ build_date="[null]"
+ build_date_ms="[null]"
+ period1_date="[null]"
+ period1_date_ms="[null]"
+ period2_date="[null]"
+ period2_date_ms="[null]"
+ period3_date="[null]"
+ period3_date_ms="[null]"
+ period4_date="[null]"
+ period4_date_ms="[null]"
+ period5_date="[null]"
+ period5_date_ms="[null]"
+ />
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/schema.sql
new file mode 100644
index 00000000000..318169c0787
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedSnapshotsLongDatesTest/schema.sql
@@ -0,0 +1,18 @@
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "BUILD_DATE" TIMESTAMP,
+ "BUILD_DATE_MS" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PERIOD1_DATE" TIMESTAMP,
+ "PERIOD1_DATE_MS" BIGINT,
+ "PERIOD2_DATE" TIMESTAMP,
+ "PERIOD2_DATE_MS" BIGINT,
+ "PERIOD3_DATE" TIMESTAMP,
+ "PERIOD3_DATE_MS" BIGINT,
+ "PERIOD4_DATE" TIMESTAMP,
+ "PERIOD4_DATE_MS" BIGINT,
+ "PERIOD5_DATE" TIMESTAMP,
+ "PERIOD5_DATE_MS" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/before.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/before.xml
new file mode 100644
index 00000000000..a4b16c1fcd2
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/before.xml
@@ -0,0 +1,26 @@
+<dataset>
+
+ <users id="1" login="user1" name="User1" active="[true]"
+ created_at="2014-05-12"
+ updated_at="2014-05-13"
+ CREATED_AT_MS="[null]"
+ UPDATED_AT_MS="[null]"
+ />
+
+ <!-- re-entrant migration - ignore the issues that are already fed with new dates -->
+ <users id="2" login="user2" name="User2" active="[true]"
+ created_at="2014-05-12"
+ updated_at="2014-05-13"
+ CREATED_AT_MS="1500000000000"
+ UPDATED_AT_MS="1500000000000"
+ />
+
+ <!-- NULL dates -->
+ <users id="3" login="user3" name="User3" active="[true]"
+ created_at="[null]"
+ updated_at="[null]"
+ CREATED_AT_MS="[null]"
+ UPDATED_AT_MS="[null]"
+ />
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/schema.sql
new file mode 100644
index 00000000000..ba24adf3a29
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/FeedUsersLongDatesTest/schema.sql
@@ -0,0 +1,15 @@
+CREATE TABLE "USERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "LOGIN" VARCHAR(255),
+ "NAME" VARCHAR(200),
+ "EMAIL" VARCHAR(100),
+ "CRYPTED_PASSWORD" VARCHAR(40),
+ "SALT" VARCHAR(40),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP,
+ "CREATED_AT_MS" BIGINT,
+ "UPDATED_AT_MS" BIGINT,
+ "REMEMBER_TOKEN" VARCHAR(500),
+ "REMEMBER_TOKEN_EXPIRES_AT" TIMESTAMP,
+ "ACTIVE" BOOLEAN DEFAULT TRUE
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate-result.xml
new file mode 100644
index 00000000000..66bbfaf90c1
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate-result.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <projects id="100" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ authorization_updated_at="123456789"/>
+
+ <projects id="101" root_id="[null]" scope="PRJ" qualifier="BRC" kee="org.struts:struts-server" name="Struts Server"
+ description="the description" long_name="Apache Struts Server"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ authorization_updated_at="123456789"/>
+
+ <!-- Permissions on project -->
+ <user_roles id="1" user_id="200" resource_id="100" role="user"/>
+ <user_roles id="2" user_id="200" resource_id="100" role="admin"/>
+ <group_roles id="1" group_id="100" resource_id="100" role="codeviewer"/>
+
+ <!-- No more permissions on module -->
+
+ <!-- Global permissions -->
+ <user_roles id="10" user_id="200" resource_id="[null]" role="admin"/>
+ <group_roles id="10" group_id="200" resource_id="[null]" role="admin"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate.xml
new file mode 100644
index 00000000000..a209fcf9025
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/migrate.xml
@@ -0,0 +1,29 @@
+<dataset>
+
+ <projects id="100" uuid="ABCD" module_uuid="[null]" project_uuid="ABCD" module_uuid_path=".ABCD." root_id="[null]"
+ scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts" description="the description"
+ long_name="Apache Struts"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ authorization_updated_at="123456789"/>
+
+ <projects id="101" uuid="BCDE" module_uuid="ABCD" project_uuid="ABCD" module_uuid_path=".ABCD.BCDE." root_id="100"
+ scope="PRJ" qualifier="BRC" kee="org.struts:struts-server" name="Struts Server"
+ description="the description" long_name="Apache Struts Server"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ authorization_updated_at="123456789"/>
+
+ <!-- Permissions on project -->
+ <user_roles id="1" user_id="200" resource_id="100" role="user"/>
+ <user_roles id="2" user_id="200" resource_id="100" role="admin"/>
+ <group_roles id="1" group_id="100" resource_id="100" role="codeviewer"/>
+
+ <!-- Permissions on module : should be deleted -->
+ <user_roles id="3" user_id="200" resource_id="101" role="user"/>
+ <user_roles id="4" user_id="200" resource_id="101" role="admin"/>
+ <group_roles id="2" group_id="100" resource_id="101" role="codeviewer"/>
+
+ <!-- Global permissions -->
+ <user_roles id="10" user_id="200" resource_id="[null]" role="admin"/>
+ <group_roles id="10" group_id="200" resource_id="[null]" role="admin"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/nothing_to_do_when_already_migrated.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/nothing_to_do_when_already_migrated.xml
new file mode 100644
index 00000000000..8a842e7a734
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/nothing_to_do_when_already_migrated.xml
@@ -0,0 +1,22 @@
+<dataset>
+
+ <projects id="100" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ authorization_updated_at="123456789"/>
+
+ <projects id="101" root_id="[null]" scope="PRJ" qualifier="BRC" kee="org.struts:struts-server" name="Struts Server"
+ description="the description" long_name="Apache Struts Server"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ authorization_updated_at="123456789"/>
+
+ <!-- Permissions on project -->
+ <user_roles id="1" user_id="200" resource_id="100" role="user"/>
+ <user_roles id="2" user_id="200" resource_id="100" role="admin"/>
+ <group_roles id="1" group_id="100" resource_id="100" role="codeviewer"/>
+
+ <!-- Global permissions -->
+ <user_roles id="10" user_id="200" resource_id="[null]" role="admin"/>
+ <group_roles id="10" group_id="200" resource_id="[null]" role="admin"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/schema.sql
new file mode 100644
index 00000000000..b291cf0beea
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RemovePermissionsOnModulesMigrationTest/schema.sql
@@ -0,0 +1,49 @@
+CREATE TABLE "USER_ROLES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_ID" INTEGER,
+ "RESOURCE_ID" INTEGER,
+ "ROLE" VARCHAR(64) NOT NULL
+);
+
+CREATE INDEX "USER_ROLES_RESOURCE" ON "USER_ROLES" ("RESOURCE_ID");
+
+CREATE INDEX "USER_ROLES_USER" ON "USER_ROLES" ("USER_ID");
+
+CREATE TABLE "GROUP_ROLES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "GROUP_ID" INTEGER,
+ "RESOURCE_ID" INTEGER,
+ "ROLE" VARCHAR(64) NOT NULL
+);
+
+CREATE INDEX "GROUP_ROLES_RESOURCE" ON "GROUP_ROLES" ("RESOURCE_ID");
+
+CREATE INDEX "GROUP_ROLES_GROUP" ON "GROUP_ROLES" ("GROUP_ID");
+
+CREATE INDEX "GROUP_ROLES_ROLE" ON "GROUP_ROLES" ("ROLE");
+
+CREATE UNIQUE INDEX "UNIQ_GROUP_ROLES" ON "GROUP_ROLES" ("GROUP_ID", "RESOURCE_ID", "ROLE");
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute-result.xml
new file mode 100644
index 00000000000..a3dc90896d3
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute-result.xml
@@ -0,0 +1,68 @@
+<dataset>
+
+ <!-- Not updated, no concerned field -->
+ <issue_filters
+ id="1"
+ name="No concerned field"
+ user_login="stephane"
+ shared="[true]"
+ description="no not touch"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <!-- Not updated, projectUuids is left as is -->
+ <issue_filters
+ id="2"
+ name="Has projects"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|asc=true|projectUuids=ABC"
+ created_at="2013-06-10"
+ updated_at="2013-06-10 00:00:00.0"/>
+
+ <!-- componentUuids replaced by fileUuids -->
+ <issue_filters
+ id="3"
+ name="Has components"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="fileUuids=ABC|statuses=OPEN|sort=SEVERITY|asc=true"
+ created_at="2013-06-10"
+ updated_at="2014-10-29 00:00:00.0"/>
+
+ <!-- componentRootUuids replaced by moduleUuids -->
+ <issue_filters
+ id="4"
+ name="Has componentRoots"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|asc=true|moduleUuids=ABC"
+ created_at="2013-06-10"
+ updated_at="2014-10-29 00:00:00.0"/>
+
+ <!-- componentRootUuids replaced by moduleUuids (in the middle of the string) -->
+ <issue_filters
+ id="5"
+ name="Has projects"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|moduleUuids=ABC|asc=true"
+ created_at="2013-06-10"
+ updated_at="2014-10-29 00:00:00.0"/>
+
+ <!-- componentUuidss replaced by fileUuids, componentRootUuids replaced by moduleUuids -->
+ <issue_filters
+ id="6"
+ name="Has all parameters"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|fileUuids=BCD|moduleUuids=ABC|projectUuids=CDE|asc=true"
+ created_at="2013-06-10"
+ updated_at="2014-10-29 00:00:00.0"/>
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute.xml
new file mode 100644
index 00000000000..971334c5066
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/execute.xml
@@ -0,0 +1,69 @@
+<dataset>
+
+ <!-- Not updated, no concerned field -->
+ <issue_filters
+ id="1"
+ name="No concerned field"
+ user_login="stephane"
+ shared="[true]"
+ description="no not touch"
+ data="projectUuids=ABCD"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <!-- Not updated, projectUuids is left as is -->
+ <issue_filters
+ id="2"
+ name="Has projects"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|asc=true|projectUuids=ABC"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <!-- componentUuids replaced by fileUuids -->
+ <issue_filters
+ id="3"
+ name="Has components"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="componentUuids=ABC|statuses=OPEN|sort=SEVERITY|asc=true"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <!-- componentRootUuids replaced by moduleUuids -->
+ <issue_filters
+ id="4"
+ name="Has componentRoots"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|asc=true|componentRootUuids=ABC"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <!-- componentRootUuids replaced by moduleUuids (in the middle of the string) -->
+ <issue_filters
+ id="5"
+ name="Has projects"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|componentRootUuids=ABC|asc=true"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+ <!-- componentUuidss replaced by fileUuids, componentRootUuids replaced by moduleUuids -->
+ <issue_filters
+ id="6"
+ name="Has all parameters"
+ user_login="michael"
+ shared="[false]"
+ description="to be updated"
+ data="statuses=OPEN|sort=SEVERITY|componentUuids=BCD|componentRootUuids=ABC|projectUuids=CDE|asc=true"
+ created_at="2013-06-10"
+ updated_at="2013-06-10"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/schema.sql
new file mode 100644
index 00000000000..0627153a62d
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/RenameComponentRelatedParamsInIssueFiltersMigrationTest/schema.sql
@@ -0,0 +1,10 @@
+CREATE TABLE "ISSUE_FILTERS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "SHARED" BOOLEAN NOT NULL DEFAULT FALSE,
+ "USER_LOGIN" VARCHAR(255),
+ "DESCRIPTION" VARCHAR(4000),
+ "DATA" CLOB(2147483647),
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml
new file mode 100644
index 00000000000..85f8eb4b928
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components-result.xml
@@ -0,0 +1,100 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD."
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" deprecated_kee="org.struts:struts-core"
+ uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD.EFGH."
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- sub module, already has dots: only itself appended -->
+ <projects id="3" root_id="1" kee="org.struts:struts-data" name="Struts Data" deprecated_kee="org.struts:struts-data"
+ uuid="FGHI" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH.FGHI."
+ scope="PRJ" qualifier="BRC" long_name="Struts Data"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- directory -->
+ <projects id="4" root_id="3" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ deprecated_kee="org.struts:struts-core:src/org/struts"
+ uuid="GHIJ" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path=".ABCD.EFGH.FGHI."
+ name="src/org/struts" long_name="org.struts"
+ description="[null]"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- file -->
+ <projects id="5" root_id="3" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ deprecated_kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="HIJK" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path=".ABCD.EFGH.FGHI."
+ name="RequestContext.java" long_name="org.struts.RequestContext"
+ description="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- view -->
+ <projects id="6" root_id="[null]" scope="PRJ" qualifier="VW" kee="Teams" name="Teams" deprecated_kee="Teams"
+ uuid="MEAT" project_uuid="MEAT" module_uuid="[null]" module_uuid_path=".MEAT."
+ description="the description" long_name="Teams"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- sub-view -->
+ <projects id="7" root_id="6" scope="PRJ" qualifier="SVW" kee="Platform_Team" name="Platform Team"
+ deprecated_kee="Platform_Team"
+ uuid="PLAT" project_uuid="MEAT" module_uuid="MEAT" module_uuid_path=".MEAT.PLAT."
+ description="the description" long_name="Platform Team"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- view technical project - unchanged -->
+ <projects id="8" root_id="6" scope="FIL" qualifier="TRK" kee="Platform_Team:sonarqube" name="SonarQube"
+ deprecated_kee="Platform_Team:sonarqube"
+ uuid="SNQB" project_uuid="PLAT" module_uuid="PLAT" module_uuid_path=".MEAT.PLAT."
+ description="the description" long_name="Platform Team"
+ enabled="[true]" language="[null]" copy_resource_id="42" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- root project already has dots, appending itself -->
+ <projects id="9" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.sonar:sample" name="Sample"
+ deprecated_kee="org.sonar:sample"
+ uuid="WDOT" project_uuid="WDOT" module_uuid="[null]" module_uuid_path=".WDOT."
+ description="the description" long_name="Sample"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- root project with module_uuid_path NULL -->
+ <projects id="10" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.sonar:sample" name="Sample"
+ deprecated_kee="org.sonar:sample"
+ uuid="DCBA" project_uuid="DCBA" module_uuid="[null]" module_uuid_path=".DCBA."
+ description="the description" long_name="Sample"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- developer -->
+ <projects id="11" root_id="[null]" scope="PRJ" qualifier="DEV" kee="DEV:anakin.skywalker" name="Anakin Skywalker"
+ deprecated_kee="DEV:anakin.skywalker"
+ uuid="VADR" project_uuid="VADR" module_uuid="[null]" module_uuid_path=".VADR."
+ description="the description" long_name="Anakin Skywalker"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="1" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- developer technical project, with dots - unchanged -->
+ <projects id="12" root_id="11" scope="PRJ" qualifier="DEV_PRJ" kee="DEV:anakin.skywalker:Executor"
+ name="Executor Star Dreadnaught" deprecated_kee="DEV:anakin.skywalker:Executor"
+ uuid="EXCT" project_uuid="VADR" module_uuid="VADR" module_uuid_path=".VADR."
+ description="the description" long_name="Executor Star Dreadnaught"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml
new file mode 100644
index 00000000000..ede863b4724
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/migrate_components.xml
@@ -0,0 +1,100 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" deprecated_kee="org.struts:struts-core"
+ uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="ABCD"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- sub module, already has dots: only itself appended -->
+ <projects id="3" root_id="1" kee="org.struts:struts-data" name="Struts Data" deprecated_kee="org.struts:struts-data"
+ uuid="FGHI" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH."
+ scope="PRJ" qualifier="BRC" long_name="Struts Data"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- directory -->
+ <projects id="4" root_id="3" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ deprecated_kee="org.struts:struts-core:src/org/struts"
+ uuid="GHIJ" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path="ABCD.EFGH.FGHI"
+ name="src/org/struts" long_name="org.struts"
+ description="[null]"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- file -->
+ <projects id="5" root_id="3" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ deprecated_kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="HIJK" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path="ABCD.EFGH.FGHI"
+ name="RequestContext.java" long_name="org.struts.RequestContext"
+ description="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- view -->
+ <projects id="6" root_id="[null]" scope="PRJ" qualifier="VW" kee="Teams" name="Teams" deprecated_kee="Teams"
+ uuid="MEAT" project_uuid="MEAT" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Teams"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- sub-view -->
+ <projects id="7" root_id="6" scope="PRJ" qualifier="SVW" kee="Platform_Team" name="Platform Team"
+ deprecated_kee="Platform_Team"
+ uuid="PLAT" project_uuid="MEAT" module_uuid="MEAT" module_uuid_path="MEAT"
+ description="the description" long_name="Platform Team"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- view technical project, already has dots - unchanged -->
+ <projects id="8" root_id="6" scope="FIL" qualifier="TRK" kee="Platform_Team:sonarqube" name="SonarQube"
+ deprecated_kee="Platform_Team:sonarqube"
+ uuid="SNQB" project_uuid="PLAT" module_uuid="PLAT" module_uuid_path=".MEAT.PLAT."
+ description="the description" long_name="Platform Team"
+ enabled="[true]" language="[null]" copy_resource_id="42" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- root project already has dots, appending itself -->
+ <projects id="9" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.sonar:sample" name="Sample"
+ deprecated_kee="org.sonar:sample"
+ uuid="WDOT" project_uuid="WDOT" module_uuid="[null]" module_uuid_path="."
+ description="the description" long_name="Sample"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- root project with module_uuid_path NULL -->
+ <projects id="10" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.sonar:sample" name="Sample"
+ deprecated_kee="org.sonar:sample"
+ uuid="DCBA" project_uuid="DCBA" module_uuid="[null]" module_uuid_path="[null]"
+ description="the description" long_name="Sample"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- developer -->
+ <projects id="11" root_id="[null]" scope="PRJ" qualifier="DEV" kee="DEV:anakin.skywalker" name="Anakin Skywalker"
+ deprecated_kee="DEV:anakin.skywalker"
+ uuid="VADR" project_uuid="VADR" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Anakin Skywalker"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="1" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- developer technical project, with dots - unchanged -->
+ <projects id="12" root_id="11" scope="PRJ" qualifier="DEV_PRJ" kee="DEV:anakin.skywalker:Executor"
+ name="Executor Star Dreadnaught" deprecated_kee="DEV:anakin.skywalker:Executor"
+ uuid="EXCT" project_uuid="VADR" module_uuid="VADR" module_uuid_path=".VADR."
+ description="the description" long_name="Executor Star Dreadnaught"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml
new file mode 100644
index 00000000000..547192c2bbf
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/not_migrate_already_migrated_components.xml
@@ -0,0 +1,100 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD."
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" deprecated_kee="org.struts:struts-core"
+ uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=".ABCD.EFGH."
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- sub module -->
+ <projects id="3" root_id="1" kee="org.struts:struts-data" name="Struts Data" deprecated_kee="org.struts:struts-data"
+ uuid="FGHI" project_uuid="ABCD" module_uuid="EFGH" module_uuid_path=".ABCD.EFGH.FGHI."
+ scope="PRJ" qualifier="BRC" long_name="Struts Data"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- directory -->
+ <projects id="4" root_id="3" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts"
+ deprecated_kee="org.struts:struts-core:src/org/struts"
+ uuid="GHIJ" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path=".ABCD.EFGH.FGHI."
+ name="src/org/struts" long_name="org.struts"
+ description="[null]"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- file -->
+ <projects id="5" root_id="3" scope="FIL" qualifier="FIL"
+ kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ deprecated_kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ uuid="HIJK" project_uuid="ABCD" module_uuid="FGHI" module_uuid_path=".ABCD.EFGH.FGHI."
+ name="RequestContext.java" long_name="org.struts.RequestContext"
+ description="[null]"
+ enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"
+ path="src/org/struts/RequestContext.java"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- view -->
+ <projects id="6" root_id="[null]" scope="PRJ" qualifier="VW" kee="Teams" name="Teams" deprecated_kee="Teams"
+ uuid="MEAT" project_uuid="MEAT" module_uuid="[null]" module_uuid_path=".MEAT."
+ description="the description" long_name="Teams"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- sub-view -->
+ <projects id="7" root_id="[null]" scope="PRJ" qualifier="SVW" kee="Platform_Team" name="Platform Team"
+ deprecated_kee="Platform_Team"
+ uuid="PLAT" project_uuid="MEAT" module_uuid="MEAT" module_uuid_path=".MEAT.PLAT."
+ description="the description" long_name="Platform Team"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- view technical project, already has dots - unchanged -->
+ <projects id="8" root_id="[null]" scope="FIL" qualifier="TRK" kee="Platform_Team:sonarqube" name="SonarQube"
+ deprecated_kee="Platform_Team:sonarqube"
+ uuid="SNQB" project_uuid="PLAT" module_uuid="PLAT" module_uuid_path=".MEAT.PLAT."
+ description="the description" long_name="Platform Team"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- root project already has dots, appending itself -->
+ <projects id="9" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.sonar:sample" name="Sample"
+ deprecated_kee="org.sonar:sample"
+ uuid="WDOT" project_uuid="WDOT" module_uuid="[null]" module_uuid_path=".WDOT."
+ description="the description" long_name="Sample"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- root project with module_uuid_path NULL -->
+ <projects id="10" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.sonar:sample" name="Sample"
+ deprecated_kee="org.sonar:sample"
+ uuid="DCBA" project_uuid="DCBA" module_uuid="[null]" module_uuid_path=".DCBA."
+ description="the description" long_name="Sample"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- developer -->
+ <projects id="11" root_id="[null]" scope="PRJ" qualifier="DEV" kee="DEV:anakin.skywalker" name="Anakin Skywalker"
+ deprecated_kee="DEV:anakin.skywalker"
+ uuid="VADR" project_uuid="VADR" module_uuid="[null]" module_uuid_path=".VADR."
+ description="the description" long_name="Anakin Skywalker"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="1" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- developer technical project, with dots - unchanged -->
+ <projects id="12" root_id="11" scope="PRJ" qualifier="DEV_PRJ" kee="DEV:anakin.skywalker:Executor"
+ name="Executor Star Dreadnaught" deprecated_kee="DEV:anakin.skywalker:Executor"
+ uuid="EXCT" project_uuid="VADR" module_uuid="VADR" module_uuid_path=".VADR."
+ description="the description" long_name="Executor Star Dreadnaught"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/schema.sql
new file mode 100644
index 00000000000..b7307a0902a
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v51/UpdateProjectsModuleUuidPathTest/schema.sql
@@ -0,0 +1,22 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest/schema.sql
new file mode 100644
index 00000000000..6fa4c32a970
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/AddManualMeasuresComponentUuidColumnTest/schema.sql
@@ -0,0 +1,11 @@
+CREATE TABLE "MANUAL_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "METRIC_ID" INTEGER NOT NULL,
+ "RESOURCE_ID" INTEGER,
+ "VALUE" DOUBLE,
+ "TEXT_VALUE" VARCHAR(4000),
+ "USER_LOGIN" VARCHAR(255),
+ "DESCRIPTION" VARCHAR(4000),
+ "CREATED_AT" BIGINT,
+ "UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate-result.xml
new file mode 100644
index 00000000000..2f6d90c92cb
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate-result.xml
@@ -0,0 +1,6 @@
+<dataset>
+
+ <events id="1" name="1.0" resource_id="1" component_uuid="ABCD" snapshot_id="1000" category="Version"
+ event_date="1225630680000" created_at="1225630680000" description="" event_data="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate.xml
new file mode 100644
index 00000000000..38cd3d20e3a
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/migrate.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <events id="1" name="1.0" resource_id="1" component_uuid="[null]" snapshot_id="1000" category="Version"
+ event_date="1225630680000" created_at="1225630680000" description="" event_data="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/not_migrate_already_migrated_data.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/not_migrate_already_migrated_data.xml
new file mode 100644
index 00000000000..afecdb23d15
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/not_migrate_already_migrated_data.xml
@@ -0,0 +1,13 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <events id="1" name="1.0" resource_id="1" component_uuid="ABCD" snapshot_id="1000" category="Version"
+ event_date="1225630680000" created_at="1225630680000" description="" event_data="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/schema.sql
new file mode 100644
index 00000000000..e4ca540f2f2
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedEventsComponentUuidTest/schema.sql
@@ -0,0 +1,35 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "EVENTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(400),
+ "RESOURCE_ID" INTEGER,
+ "COMPONENT_UUID" VARCHAR(50),
+ "SNAPSHOT_ID" INTEGER,
+ "CATEGORY" VARCHAR(50),
+ "EVENT_DATE" BIGINT NOT NULL,
+ "CREATED_AT" BIGINT NOT NULL,
+ "DESCRIPTION" VARCHAR(4000),
+ "EVENT_DATA" VARCHAR(4000)
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate-result.xml
new file mode 100644
index 00000000000..3788fbf38ef
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate-result.xml
@@ -0,0 +1,10 @@
+<dataset>
+
+ <file_sources id="1" project_uuid="project-1" file_uuid="file-1" data_type="SOURCE" created_at="123456789"
+ updated_at="456456456"/>
+ <file_sources id="2" project_uuid="project-2" file_uuid="file-2" data_type="SOURCE" created_at="123456789"
+ updated_at="456456456"/>
+ <file_sources id="3" project_uuid="project-3" file_uuid="file-3" data_type="SOURCE" created_at="123456789"
+ updated_at="456456456"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate.xml
new file mode 100644
index 00000000000..2282a9bb4c0
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/migrate.xml
@@ -0,0 +1,10 @@
+<dataset>
+
+ <file_sources id="1" project_uuid="project-1" file_uuid="file-1" data_type="[null]" created_at="123456789"
+ updated_at="456456456"/>
+ <file_sources id="2" project_uuid="project-2" file_uuid="file-2" data_type="SOURCE" created_at="123456789"
+ updated_at="456456456"/>
+ <file_sources id="3" project_uuid="project-3" file_uuid="file-3" data_type="[null]" created_at="123456789"
+ updated_at="456456456"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/schema.sql
new file mode 100644
index 00000000000..84eb7749817
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedFileSourcesDataTypeTest/schema.sql
@@ -0,0 +1,8 @@
+CREATE TABLE "FILE_SOURCES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "FILE_UUID" VARCHAR(50) NOT NULL,
+ "DATA_TYPE" VARCHAR(20),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate-result.xml
new file mode 100644
index 00000000000..2dd0252cb3c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate-result.xml
@@ -0,0 +1,6 @@
+<dataset>
+
+ <manual_measures id="1" resource_id="10" component_uuid="ABCD"/>
+ <manual_measures id="2" resource_id="20" component_uuid="EFGH"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate.xml
new file mode 100644
index 00000000000..2a36c383c69
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/migrate.xml
@@ -0,0 +1,9 @@
+<dataset>
+
+ <projects id="10" uuid="ABCD"/>
+ <projects id="20" uuid="EFGH"/>
+
+ <manual_measures id="1" resource_id="10" component_uuid="[null]"/>
+ <manual_measures id="2" resource_id="20" component_uuid="EFGH"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/schema.sql
new file mode 100644
index 00000000000..bc2c66dc238
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedManualMeasuresComponentUuidTest/schema.sql
@@ -0,0 +1,10 @@
+CREATE TABLE "MANUAL_MEASURES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "RESOURCE_ID" INTEGER,
+ "COMPONENT_UUID" VARCHAR(50),
+);
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "UUID" VARCHAR(50),
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate-result.xml
new file mode 100644
index 00000000000..dd63f7edb36
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate-result.xml
@@ -0,0 +1,9 @@
+<dataset>
+
+ <metrics id="1" user_managed="[true]" optimized_best_value="[false]" hidden="[false]"
+ delete_historical_data="[false]"/>
+ <metrics id="2" user_managed="[false]" optimized_best_value="[true]" hidden="[true]" delete_historical_data="[true]"/>
+ <metrics id="3" user_managed="[false]" optimized_best_value="[false]" hidden="[false]"
+ delete_historical_data="[false]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate.xml
new file mode 100644
index 00000000000..7fee0be9b77
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/migrate.xml
@@ -0,0 +1,7 @@
+<dataset>
+
+ <metrics id="1" user_managed="[true]" optimized_best_value="[null]" hidden="[null]" delete_historical_data="[null]"/>
+ <metrics id="2" user_managed="[false]" optimized_best_value="[true]" hidden="[true]" delete_historical_data="[true]"/>
+ <metrics id="3" user_managed="[false]" optimized_best_value="[null]" hidden="[null]" delete_historical_data="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/schema.sql
new file mode 100644
index 00000000000..d65487d7677
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedMetricsBooleansTest/schema.sql
@@ -0,0 +1,7 @@
+CREATE TABLE "METRICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "USER_MANAGED" BOOLEAN DEFAULT FALSE,
+ "OPTIMIZED_BEST_VALUE" BOOLEAN,
+ "HIDDEN" BOOLEAN,
+ "DELETE_HISTORICAL_DATA" BOOLEAN
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate-result.xml
new file mode 100644
index 00000000000..e46458a32e7
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate-result.xml
@@ -0,0 +1,28 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Both links now contains component uuid-->
+ <project_links id="1" project_id="1" component_uuid="ABCD" link_type="homepage" name="Home"
+ href="http://www.struts.org"/>
+ <project_links id="2" project_id="1" component_uuid="ABCD" link_type="scm" name="Sources"
+ href="https://github.com/Struts"/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" deprecated_kee="org.struts:struts-core"
+ uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="ABCD"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Link now contains component uuid-->
+ <project_links id="3" project_id="2" component_uuid="EFGH" link_type="scm" name="Sources"
+ href="https://github.com/Struts/struts-core"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate.xml
new file mode 100644
index 00000000000..a1de6ecdf02
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/migrate.xml
@@ -0,0 +1,26 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <project_links id="1" project_id="1" component_uuid="[null]" link_type="homepage" name="Home"
+ href="http://www.struts.org"/>
+ <project_links id="2" project_id="1" component_uuid="[null]" link_type="scm" name="Sources"
+ href="https://github.com/Struts"/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" deprecated_kee="org.struts:struts-core"
+ uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="ABCD"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <project_links id="3" project_id="2" component_uuid="[null]" link_type="scm" name="Sources"
+ href="https://github.com/Struts/struts-core"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/not_migrate_already_migrated_data.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/not_migrate_already_migrated_data.xml
new file mode 100644
index 00000000000..fc7f64f6e9c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/not_migrate_already_migrated_data.xml
@@ -0,0 +1,28 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Links already contains component uuid-->
+ <project_links id="1" project_id="1" component_uuid="ABCD" link_type="homepage" name="Home"
+ href="http://www.struts.org"/>
+ <project_links id="2" project_id="1" component_uuid="ABCD" link_type="scm" name="Sources"
+ href="https://github.com/Struts"/>
+
+ <!-- module -->
+ <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" deprecated_kee="org.struts:struts-core"
+ uuid="EFGH" project_uuid="ABCD" module_uuid="[null]" module_uuid_path="ABCD"
+ scope="PRJ" qualifier="BRC" long_name="Struts Core"
+ description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Link already contains component uuid-->
+ <project_links id="3" project_id="2" component_uuid="EFGH" link_type="scm" name="Sources"
+ href="https://github.com/Struts/struts-core"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/schema.sql
new file mode 100644
index 00000000000..f04590ffebd
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/FeedProjectLinksComponentUuidTest/schema.sql
@@ -0,0 +1,31 @@
+CREATE TABLE "PROJECT_LINKS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_ID" INTEGER NOT NULL,
+ "COMPONENT_UUID" VARCHAR(50),
+ "LINK_TYPE" VARCHAR(20),
+ "NAME" VARCHAR(128),
+ "HREF" VARCHAR(2048) NOT NULL
+);
+
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate-result.xml
new file mode 100644
index 00000000000..2a5da2677cc
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate-result.xml
@@ -0,0 +1,29 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Default Java profile, will be set as default -->
+ <rules_profiles id="1" name="Java One" language="java" parent_kee="[null]" kee="java-one" is_default="[true]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <rules_profiles id="2" name="Java Two" language="java" parent_kee="[null]" kee="java-two" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <rules_profiles id="3" name="Php One" language="php" parent_kee="[null]" kee="php-one" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <rules_profiles id="4" name="Cobol One" language="cbl" parent_kee="[null]" kee="cobol-one" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <!-- Unmodified property -->
+ <properties id="1" prop_key="polop.palap" text_value="Untouched" resource_id="[null]" user_id="[null]"/>
+
+ <!-- Project 'Struts' uses profile 'Java Two', moved to association table -->
+ <project_qprofiles id="1" project_uuid="ABCD" profile_key="java-two"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate.xml
new file mode 100644
index 00000000000..98849222aa9
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/migrate.xml
@@ -0,0 +1,41 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Default Java profile, will be set as default -->
+ <rules_profiles id="1" name="Java One" language="java" parent_kee="[null]" kee="java-one" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <rules_profiles id="2" name="Java Two" language="java" parent_kee="[null]" kee="java-two" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <rules_profiles id="3" name="Php One" language="php" parent_kee="[null]" kee="php-one" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <rules_profiles id="4" name="Cobol One" language="cbl" parent_kee="[null]" kee="cobol-one" is_default="[false]"
+ created_at="[null]" updated_at="[null]" rules_updated_at="[null]"/>
+
+ <!-- Unmodified property -->
+ <properties id="1" prop_key="polop.palap" text_value="Untouched" resource_id="[null]" user_id="[null]"/>
+
+ <!-- Default Java profile, will be set as default -->
+ <properties id="2" prop_key="sonar.profile.java" text_value="Java One" resource_id="[null]" user_id="[null]"/>
+
+ <!-- Project 'Struts' uses profile 'Java Two', will be moved to association table -->
+ <properties id="3" prop_key="sonar.profile.java" text_value="Java Two" resource_id="1" user_id="[null]"/>
+
+ <!-- Property on unknown language, will be ignored -->
+ <properties id="4" prop_key="sonar.profile.xoo" text_value="Xoo One" resource_id="[null]" user_id="[null]"/>
+
+ <!-- Property on unknown profile, will be ignored -->
+ <properties id="5" prop_key="sonar.profile.php" text_value="Php Two" resource_id="[null]" user_id="[null]"/>
+
+ <!-- Property on unknown project, will be ignored -->
+ <properties id="6" prop_key="sonar.profile.php" text_value="Php One" resource_id="2" user_id="[null]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/schema.sql
new file mode 100644
index 00000000000..004569aacfe
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/MoveProjectProfileAssociationTest/schema.sql
@@ -0,0 +1,48 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
+
+CREATE TABLE "RULES_PROFILES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "NAME" VARCHAR(100) NOT NULL,
+ "LANGUAGE" VARCHAR(20),
+ "KEE" VARCHAR(255) NOT NULL,
+ "PARENT_KEE" VARCHAR(255),
+ "RULES_UPDATED_AT" VARCHAR(100),
+ "IS_DEFAULT" BOOLEAN NOT NULL DEFAULT FALSE,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "PROPERTIES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROP_KEY" VARCHAR(512),
+ "RESOURCE_ID" INTEGER,
+ "TEXT_VALUE" CLOB(2147483647),
+ "USER_ID" INTEGER
+);
+
+CREATE TABLE "PROJECT_QPROFILES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "PROFILE_KEY" VARCHAR(255) NOT NULL
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries-result.xml
new file mode 100644
index 00000000000..8c9f5d38dfb
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries-result.xml
@@ -0,0 +1,17 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Removed -->
+ <!--<projects id="2" root_id="[null]" scope="PRJ" qualifier="LIB" kee="org.hamcrest:hamcrest-library" name="org.hamcrest:hamcrest-library"-->
+ <!--uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"-->
+ <!--description="[null]" long_name="org.hamcrest:hamcrest-library"-->
+ <!--enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]" deprecated_kee="[null]"-->
+ <!--created_at="2014-06-18" />-->
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries.xml
new file mode 100644
index 00000000000..59392a2b98c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/remove_libraries.xml
@@ -0,0 +1,19 @@
+<dataset>
+
+ <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts"
+ deprecated_kee="org.struts:struts"
+ uuid="ABCD" project_uuid="ABCD" module_uuid="[null]" module_uuid_path=""
+ description="the description" long_name="Apache Struts"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ created_at="2008-12-02 13:58:00.00" authorization_updated_at="[null]"/>
+
+ <!-- Should be removed -->
+ <projects id="2" root_id="[null]" scope="PRJ" qualifier="LIB" kee="org.hamcrest:hamcrest-library"
+ name="org.hamcrest:hamcrest-library"
+ uuid="[null]" project_uuid="[null]" module_uuid="[null]" module_uuid_path="[null]"
+ description="[null]" long_name="org.hamcrest:hamcrest-library"
+ enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"
+ deprecated_kee="[null]"
+ created_at="2014-06-18"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/schema.sql
new file mode 100644
index 00000000000..b7307a0902a
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveComponentLibrariesTest/schema.sql
@@ -0,0 +1,22 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "ROOT_ID" INTEGER,
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "MODULE_UUID" VARCHAR(50),
+ "MODULE_UUID_PATH" VARCHAR(4000),
+ "NAME" VARCHAR(256),
+ "DESCRIPTION" VARCHAR(2000),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "DEPRECATED_KEE" VARCHAR(400),
+ "PATH" VARCHAR(2000),
+ "LANGUAGE" VARCHAR(20),
+ "COPY_RESOURCE_ID" INTEGER,
+ "LONG_NAME" VARCHAR(256),
+ "PERSON_ID" INTEGER,
+ "CREATED_AT" TIMESTAMP,
+ "AUTHORIZATION_UPDATED_AT" BIGINT
+);
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component-result.xml
new file mode 100644
index 00000000000..a103099ddf4
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component-result.xml
@@ -0,0 +1,20 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" uuid="PROJECT" project_uuid="PROJECT" kee="org.struts:struts" enabled="[true]"/>
+
+ <!-- module -->
+ <projects id="10" uuid="MODULE" project_uuid="PROJECT" kee="org.struts:struts-core" enabled="[true]"/>
+
+ <!-- sub modules -->
+ <projects id="21" uuid="SUB_MODULE_2" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[true]"/>
+
+ <!-- directories -->
+ <projects id="31" uuid="DIRECTORY_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[true]"/>
+
+ <!-- files -->
+ <projects id="40" uuid="FILE_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[true]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component.xml
new file mode 100644
index 00000000000..893efe4fd15
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_enable_component.xml
@@ -0,0 +1,31 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" uuid="PROJECT" project_uuid="PROJECT" kee="org.struts:struts" enabled="[true]"/>
+
+ <!-- module -->
+ <projects id="10" uuid="MODULE" project_uuid="PROJECT" kee="org.struts:struts-core" enabled="[true]"/>
+
+ <!-- sub modules -->
+ <projects id="20" uuid="SUB_MODULE_1" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+ <!-- Only this component must be kept, as it's enabled -->
+ <projects id="21" uuid="SUB_MODULE_2" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[true]"/>
+ <projects id="22" uuid="SUB_MODULE_3" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+
+ <!-- directories -->
+ <projects id="30" uuid="DIRECTORY_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+ <!-- Only this component must be kept, as it's enabled -->
+ <projects id="31" uuid="DIRECTORY_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[true]"/>
+
+ <!-- files -->
+ <!-- Only this component must be kept, as it's enabled -->
+ <projects id="40" uuid="FILE_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[true]"/>
+ <projects id="41" uuid="FILE_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <projects id="42" uuid="FILE_3" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component-result.xml
new file mode 100644
index 00000000000..cd86088d452
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component-result.xml
@@ -0,0 +1,20 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" uuid="PROJECT" project_uuid="PROJECT" kee="org.struts:struts" enabled="[true]"/>
+
+ <!-- module -->
+ <projects id="10" uuid="MODULE" project_uuid="PROJECT" kee="org.struts:struts-core" enabled="[true]"/>
+
+ <!-- sub modules -->
+ <projects id="22" uuid="SUB_MODULE_3" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+
+ <!-- directories -->
+ <projects id="31" uuid="DIRECTORY_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+
+ <!-- files -->
+ <projects id="42" uuid="FILE_3" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component.xml
new file mode 100644
index 00000000000..72f1bea892e
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/keep_last_component.xml
@@ -0,0 +1,31 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" uuid="PROJECT" project_uuid="PROJECT" kee="org.struts:struts" enabled="[true]"/>
+
+ <!-- module -->
+ <projects id="10" uuid="MODULE" project_uuid="PROJECT" kee="org.struts:struts-core" enabled="[true]"/>
+
+ <!-- sub modules -->
+ <projects id="20" uuid="SUB_MODULE_1" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+ <projects id="21" uuid="SUB_MODULE_2" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+ <!-- Only this component must be kept, as it's the last one -->
+ <projects id="22" uuid="SUB_MODULE_3" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+
+ <!-- directories -->
+ <projects id="30" uuid="DIRECTORY_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+ <!-- Only this component must be kept, as it's the last one -->
+ <projects id="31" uuid="DIRECTORY_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+
+ <!-- files -->
+ <projects id="40" uuid="FILE_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <projects id="41" uuid="FILE_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <!-- Only this component must be kept, as it's the last one -->
+ <projects id="42" uuid="FILE_3" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate-result.xml
new file mode 100644
index 00000000000..75d7c73b2f6
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate-result.xml
@@ -0,0 +1,26 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" uuid="PROJECT" project_uuid="PROJECT" kee="org.struts:struts" enabled="[true]"/>
+ <issues id="1" component_uuid="PROJECT" project_uuid="PROJECT"/>
+
+ <!-- module -->
+ <projects id="10" uuid="MODULE" project_uuid="PROJECT" kee="org.struts:struts-core" enabled="[true]"/>
+
+ <!-- sub module -->
+ <projects id="21" uuid="SUB_MODULE_2" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[true]"/>
+ <issues id="20" component_uuid="SUB_MODULE_2" project_uuid="PROJECT"/>
+ <issues id="21" component_uuid="SUB_MODULE_2" project_uuid="PROJECT"/>
+ <issues id="22" component_uuid="SUB_MODULE_2" project_uuid="PROJECT"/>
+
+ <!-- removed directory -->
+ <projects id="31" uuid="DIRECTORY_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+
+ <!-- removed file -->
+ <projects id="42" uuid="FILE_3" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <issues id="40" component_uuid="FILE_3" project_uuid="PROJECT"/>
+ <issues id="41" component_uuid="FILE_3" project_uuid="PROJECT"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate.xml
new file mode 100644
index 00000000000..e4a4ea22046
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/migrate.xml
@@ -0,0 +1,35 @@
+<dataset>
+
+ <!-- root project -->
+ <projects id="1" uuid="PROJECT" project_uuid="PROJECT" kee="org.struts:struts" enabled="[true]"/>
+ <issues id="1" component_uuid="PROJECT" project_uuid="PROJECT"/>
+
+ <!-- module -->
+ <projects id="10" uuid="MODULE" project_uuid="PROJECT" kee="org.struts:struts-core" enabled="[true]"/>
+
+ <!-- disabled sub module -> should be removed and its issues should be attached to the enabled one -->
+ <projects id="20" uuid="SUB_MODULE_1" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[false]"/>
+ <issues id="20" component_uuid="SUB_MODULE_1" project_uuid="PROJECT"/>
+ <issues id="21" component_uuid="SUB_MODULE_1" project_uuid="PROJECT"/>
+
+ <!-- enabled sub module -> issues from disabled should be attached to it -->
+ <projects id="21" uuid="SUB_MODULE_2" project_uuid="PROJECT" kee="org.struts:struts-data" enabled="[true]"/>
+ <issues id="22" component_uuid="SUB_MODULE_2" project_uuid="PROJECT"/>
+
+ <!-- disabled directories, only one should be kept -->
+ <projects id="30" uuid="DIRECTORY_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+ <projects id="31" uuid="DIRECTORY_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts"
+ enabled="[false]"/>
+
+ <!-- disabled files -> only one should be kept and issue from removed one should be attached to the remaining one -->
+ <projects id="40" uuid="FILE_1" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <projects id="41" uuid="FILE_2" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <projects id="42" uuid="FILE_3" project_uuid="PROJECT" kee="org.struts:struts-core:src/org/struts/RequestContext.java"
+ enabled="[false]"/>
+ <issues id="40" component_uuid="FILE_1" project_uuid="PROJECT"/>
+ <issues id="41" component_uuid="FILE_3" project_uuid="PROJECT"/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/schema.sql
new file mode 100644
index 00000000000..19915dfd3b0
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveDuplicatedComponentKeysTest/schema.sql
@@ -0,0 +1,17 @@
+CREATE TABLE "PROJECTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "KEE" VARCHAR(400),
+ "UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50),
+ "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE
+);
+
+CREATE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE", "ENABLED");
+
+CREATE TABLE "ISSUES" (
+ "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "COMPONENT_UUID" VARCHAR(50),
+ "PROJECT_UUID" VARCHAR(50)
+);
+
+CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES" ("COMPONENT_UUID");
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries-result.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries-result.xml
new file mode 100644
index 00000000000..d9ebf91906c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries-result.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000"
+ version="[null]" path=""/>
+
+ <!-- Removed -->
+ <!--<snapshots id="10" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"-->
+ <!--status="P" islast="[false]" purge_status="[null]"-->
+ <!--period1_mode="[null]" period1_param="[null]" period1_date="[null]"-->
+ <!--period2_mode="[null]" period2_param="[null]" period2_date="[null]"-->
+ <!--period3_mode="[null]" period3_param="[null]" period3_date="[null]"-->
+ <!--period4_mode="[null]" period4_param="[null]" period4_date="[null]"-->
+ <!--period5_mode="[null]" period5_param="[null]" period5_date="[null]"-->
+ <!--depth="[null]" scope="PRJ" qualifier="LIB" created_at="1228136280000" build_date="1228136280000"-->
+ <!--version="[null]" path=""/>-->
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries.xml b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries.xml
new file mode 100644
index 00000000000..3cba1535e62
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/remove_libraries.xml
@@ -0,0 +1,24 @@
+<dataset>
+
+ <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="TRK" created_at="1228222680000" build_date="1228222680000"
+ version="[null]" path=""/>
+
+ <!-- Should be removed -->
+ <snapshots id="10" project_id="10" parent_snapshot_id="[null]" root_project_id="10" root_snapshot_id="[null]"
+ status="P" islast="[true]" purge_status="[null]"
+ period1_mode="[null]" period1_param="[null]" period1_date="[null]"
+ period2_mode="[null]" period2_param="[null]" period2_date="[null]"
+ period3_mode="[null]" period3_param="[null]" period3_date="[null]"
+ period4_mode="[null]" period4_param="[null]" period4_date="[null]"
+ period5_mode="[null]" period5_param="[null]" period5_date="[null]"
+ depth="[null]" scope="PRJ" qualifier="LIB" created_at="1228136280000" build_date="1228136280000"
+ version="[null]" path=""/>
+
+</dataset>
diff --git a/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/schema.sql b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/schema.sql
new file mode 100644
index 00000000000..832463b246c
--- /dev/null
+++ b/sonar-db/src/test/resources/org/sonar/db/version/v52/RemoveSnapshotLibrariesTest/schema.sql
@@ -0,0 +1,32 @@
+CREATE TABLE "SNAPSHOTS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "CREATED_AT" BIGINT,
+ "BUILD_DATE" BIGINT,
+ "PROJECT_ID" INTEGER NOT NULL,
+ "PARENT_SNAPSHOT_ID" INTEGER,
+ "STATUS" VARCHAR(4) NOT NULL DEFAULT 'U',
+ "PURGE_STATUS" INTEGER,
+ "ISLAST" BOOLEAN NOT NULL DEFAULT FALSE,
+ "SCOPE" VARCHAR(3),
+ "QUALIFIER" VARCHAR(10),
+ "ROOT_SNAPSHOT_ID" INTEGER,
+ "VERSION" VARCHAR(500),
+ "PATH" VARCHAR(500),
+ "DEPTH" INTEGER,
+ "ROOT_PROJECT_ID" INTEGER,
+ "PERIOD1_MODE" VARCHAR(100),
+ "PERIOD1_PARAM" VARCHAR(100),
+ "PERIOD1_DATE" BIGINT,
+ "PERIOD2_MODE" VARCHAR(100),
+ "PERIOD2_PARAM" VARCHAR(100),
+ "PERIOD2_DATE" BIGINT,
+ "PERIOD3_MODE" VARCHAR(100),
+ "PERIOD3_PARAM" VARCHAR(100),
+ "PERIOD3_DATE" BIGINT,
+ "PERIOD4_MODE" VARCHAR(100),
+ "PERIOD4_PARAM" VARCHAR(100),
+ "PERIOD4_DATE" BIGINT,
+ "PERIOD5_MODE" VARCHAR(100),
+ "PERIOD5_PARAM" VARCHAR(100),
+ "PERIOD5_DATE" BIGINT
+);