|
31 | 31 | import com.apple.foundationdb.record.TestRecordsEnumProto; |
32 | 32 | import com.apple.foundationdb.record.TestRecordsIdenticalTypesProto; |
33 | 33 | import com.apple.foundationdb.record.TestRecordsMultidimensionalProto; |
| 34 | +import com.apple.foundationdb.record.TestRecordsSwapProto; |
34 | 35 | import com.apple.foundationdb.record.TestRecordsWithHeaderProto; |
35 | 36 | import com.apple.foundationdb.record.evolution.TestHeaderAsGroupProto; |
36 | 37 | import com.apple.foundationdb.record.evolution.TestMergedNestedTypesProto; |
@@ -1123,6 +1124,124 @@ void renameFieldInPrimaryKey(boolean deprecated) { |
1123 | 1124 | fieldRenameChecker.assertValidRenaming(deprecated, metaData1, metaData3); |
1124 | 1125 | } |
1125 | 1126 |
|
| 1127 | + @ParameterizedTest |
| 1128 | + @MethodSource("deprecatedArgs") |
| 1129 | + void renameFieldOnReplacedTypeWithIndexes(boolean deprecated) { |
| 1130 | + final RecordMetaData metaData1 = RecordMetaData.build(TestRecords1Proto.getDescriptor()); |
| 1131 | + |
| 1132 | + // Mutate the original file change the following: |
| 1133 | + // 1. The MySimpleRecord type is renamed MySimpleRecord__Old |
| 1134 | + // 2. Two fields, num_value_3_indexed and num_value_2, are renamed on MySimpleRecord__Old |
| 1135 | + // 3. A new type, also named MySimpleRecord, is added with all the fields from the original MySimpleRecord |
| 1136 | + // 4. Update the RecordTypeUnion to account for the type name and new record type |
| 1137 | + final FileDescriptor mutatedFile = mutateFile(fileBuilder -> { |
| 1138 | + DescriptorProtos.DescriptorProto.Builder newType = DescriptorProtos.DescriptorProto.newBuilder() |
| 1139 | + .setName("MySimpleRecord"); |
| 1140 | + |
| 1141 | + for (DescriptorProtos.DescriptorProto.Builder descriptorProto : fileBuilder.getMessageTypeBuilderList()) { |
| 1142 | + if (descriptorProto.getName().equals("MySimpleRecord")) { |
| 1143 | + descriptorProto.setName("MySimpleRecord__Old"); |
| 1144 | + descriptorProto.getFieldList().forEach(newType::addField); |
| 1145 | + for (DescriptorProtos.FieldDescriptorProto.Builder field : descriptorProto.getFieldBuilderList()) { |
| 1146 | + if (field.getName().equals("num_value_3_indexed") || field.getName().equals("num_value_2")) { |
| 1147 | + field.setName(field.getName() + "__old"); |
| 1148 | + if (deprecated) { |
| 1149 | + field.getOptionsBuilder().setDeprecated(true); |
| 1150 | + } |
| 1151 | + } |
| 1152 | + } |
| 1153 | + } else if (descriptorProto.getName().equals(RecordMetaDataBuilder.DEFAULT_UNION_NAME)) { |
| 1154 | + for (DescriptorProtos.FieldDescriptorProto.Builder fieldProto : descriptorProto.getFieldBuilderList()) { |
| 1155 | + if (fieldProto.getTypeName().endsWith("MySimpleRecord")) { |
| 1156 | + fieldProto.setTypeName("MySimpleRecord__Old"); |
| 1157 | + fieldProto.setName(fieldProto.getName() + "__Old"); |
| 1158 | + } |
| 1159 | + } |
| 1160 | + addField(descriptorProto) |
| 1161 | + .setLabel(DescriptorProtos.FieldDescriptorProto.Label.LABEL_OPTIONAL) |
| 1162 | + .setType(DescriptorProtos.FieldDescriptorProto.Type.TYPE_MESSAGE) |
| 1163 | + .setTypeName("MySimpleRecord") |
| 1164 | + .setName("_MySimpleRecord"); |
| 1165 | + } |
| 1166 | + } |
| 1167 | + |
| 1168 | + fileBuilder.addMessageType(newType); |
| 1169 | + }); |
| 1170 | + // Construct a meta-data object to match the new type |
| 1171 | + final RecordMetaData metaData2 = replaceRecordsDescriptor(metaData1, mutatedFile, metaData -> { |
| 1172 | + for (RecordMetaDataProto.RecordType.Builder recordType : metaData.getRecordTypesBuilderList()) { |
| 1173 | + if (recordType.getName().equals("MySimpleRecord")) { |
| 1174 | + recordType.setName("MySimpleRecord__Old"); |
| 1175 | + } |
| 1176 | + } |
| 1177 | + metaData.addRecordTypesBuilder() |
| 1178 | + .setName("MySimpleRecord") |
| 1179 | + .setPrimaryKey(Key.Expressions.field("rec_no").toKeyExpression()) |
| 1180 | + .setSinceVersion(metaData.getVersion()); |
| 1181 | + }); |
| 1182 | + |
| 1183 | + // Should be invalid. The indexes that were previously on the old record type have been moved over to the new |
| 1184 | + // type |
| 1185 | + fieldRenameChecker.assertInvalidRenaming("new index removes record type", deprecated, metaData1, metaData2); |
| 1186 | + |
| 1187 | + // Rename the record type in the references held by each index on the original MySimpleRecord. |
| 1188 | + // Note that the MySimpleRecord$num_value_3_indexed index also needs to update its field. While doing that, |
| 1189 | + // change the field name incorrectly, and validate that we catch that. |
| 1190 | + RecordMetaData metaData3 = replaceIndex(metaData2, "MySimpleRecord$str_value_indexed", index -> |
| 1191 | + index.toBuilder().clearRecordType().addRecordType("MySimpleRecord__Old").build()); |
| 1192 | + metaData3 = replaceIndex(metaData3, "MySimpleRecord$num_value_unique", index -> |
| 1193 | + index.toBuilder().clearRecordType().addRecordType("MySimpleRecord__Old").build()); |
| 1194 | + metaData3 = replaceIndex(metaData3, "MySimpleRecord$num_value_3_indexed", index -> |
| 1195 | + index.toBuilder().clearRecordType().addRecordType("MySimpleRecord__Old").setRootExpression(Key.Expressions.field("num_value_2__old").toKeyExpression()).build()); |
| 1196 | + |
| 1197 | + fieldRenameChecker.assertInvalidRenaming("index key expression does not match required", deprecated, metaData1, metaData3); |
| 1198 | + |
| 1199 | + // Correct the field renaming on that final index |
| 1200 | + final RecordMetaData metaData4 = replaceIndex(metaData3, "MySimpleRecord$num_value_3_indexed", index -> |
| 1201 | + index.toBuilder().setRootExpression(Key.Expressions.field("num_value_3_indexed__old").toKeyExpression()).build()); |
| 1202 | + fieldRenameChecker.assertValidRenaming(deprecated, metaData1, metaData4); |
| 1203 | + } |
| 1204 | + |
| 1205 | + @Test |
| 1206 | + void swapTypesWithIndexesRequiresRenamingFields() { |
| 1207 | + final RecordMetaData metaData1 = RecordMetaData.build(TestRecordsSwapProto.getDescriptor()); |
| 1208 | + |
| 1209 | + // The two types, TypeAlpha and TypeBeta, have the same field names, but in different orders. |
| 1210 | + // Swapping the types should therefore be okay, but it will require renaming the referenced |
| 1211 | + // fields within the indexes. |
| 1212 | + |
| 1213 | + final FileDescriptor mutatedDescriptor = mutateMessageType("Union", TestRecordsSwapProto.getDescriptor(), unionTypeBuilder -> { |
| 1214 | + for (DescriptorProtos.FieldDescriptorProto.Builder field : unionTypeBuilder.getFieldBuilderList()) { |
| 1215 | + // Swap the union descriptor fields for TypeAlpha and TypeBeta |
| 1216 | + if (field.getNumber() == TestRecordsSwapProto.Union._TYPEALPHA_FIELD_NUMBER) { |
| 1217 | + field.setNumber(TestRecordsSwapProto.Union._TYPEBETA_FIELD_NUMBER); |
| 1218 | + } else if (field.getNumber() == TestRecordsSwapProto.Union._TYPEBETA_FIELD_NUMBER) { |
| 1219 | + field.setNumber(TestRecordsSwapProto.Union._TYPEALPHA_FIELD_NUMBER); |
| 1220 | + } |
| 1221 | + } |
| 1222 | + }); |
| 1223 | + |
| 1224 | + // Initial evolution validation fails because the indexes all swap types (because their |
| 1225 | + // referenced type names still point to the old types) |
| 1226 | + final RecordMetaData metaData2 = replaceRecordsDescriptor(metaData1, mutatedDescriptor); |
| 1227 | + fieldRenameChecker.assertInvalidRenaming("new index removes record type", false, metaData1, metaData2); |
| 1228 | + |
| 1229 | + // Fix the types on each index. This still fails to validate as the index root expressions will |
| 1230 | + // still use the incorrect names |
| 1231 | + RecordMetaData metaData3 = replaceIndex(metaData2, "TypeAlpha$bar", index -> |
| 1232 | + index.toBuilder().clearRecordType().addRecordType("TypeBeta").build()); |
| 1233 | + metaData3 = replaceIndex(metaData3, "TypeBeta$bar", index -> |
| 1234 | + index.toBuilder().clearRecordType().addRecordType("TypeAlpha").build()); |
| 1235 | + fieldRenameChecker.assertInvalidRenaming("index key expression does not match required", false, metaData1, metaData3); |
| 1236 | + |
| 1237 | + // Rename the fields referenced in the index. After that, the evolution should be allowed |
| 1238 | + RecordMetaData metaData4 = replaceIndex(metaData3, "TypeAlpha$bar", index -> |
| 1239 | + index.toBuilder().setRootExpression(Key.Expressions.field("baz").toKeyExpression()).build()); |
| 1240 | + metaData4 = replaceIndex(metaData4, "TypeBeta$bar", index -> |
| 1241 | + index.toBuilder().setRootExpression(Key.Expressions.field("foo").toKeyExpression()).build()); |
| 1242 | + fieldRenameChecker.assertValidRenaming(false, metaData1, metaData4); |
| 1243 | + } |
| 1244 | + |
1126 | 1245 | @Test |
1127 | 1246 | void deprecateField() { |
1128 | 1247 | FileDescriptor deprecatedFile = mutateField("MySimpleRecord", "str_value_indexed", |
|
0 commit comments