use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.
the class MetaDataProtoEditor method renameRecordType.
/**
* Rename a record type. This can be used to update any top-level record type defined within the
* meta-data's records descriptor, including {@code NESTED} records or the union descriptor. However,
* it cannot be used to rename nested messages (i.e., messages defined within other messages) or
* records defined in imported files. In addition to updating the file descriptor, if the record type
* is not {@code NESTED} or the union descriptor, update any other references to the record type
* within the meta-data (such as within index definitions).
*
* @param metaDataBuilder the meta-data builder
* @param recordTypeName the name of the existing top-level record type
* @param newRecordTypeName the new name to give to the record type
*/
public static void renameRecordType(@Nonnull RecordMetaDataProto.MetaData.Builder metaDataBuilder, @Nonnull String recordTypeName, @Nonnull String newRecordTypeName) {
// Create a copy of the records builder (rather than calling metaDataBuilder.getRecordsBuilder()) to avoid
// corrupting the records builder in metaDataBuilder before all validation has been done.
final DescriptorProtos.FileDescriptorProto records = metaDataBuilder.getRecords();
boolean found = false;
for (DescriptorProtos.DescriptorProto messageType : records.getMessageTypeList()) {
if (messageType.getName().equals(recordTypeName)) {
found = true;
} else if (messageType.getName().equals(newRecordTypeName)) {
throw new MetaDataException("Cannot rename record type to " + newRecordTypeName + " as it already exists");
}
}
if (!found) {
throw new MetaDataException("No record type found with name " + recordTypeName);
}
if (recordTypeName.equals(newRecordTypeName)) {
// From here on in, we can assume that recordTypeName != newRecordTypeName, which simplifies things.
return;
}
final DescriptorProtos.FileDescriptorProto.Builder recordsBuilder = records.toBuilder();
// Determine the usage of the original record type by looking through the union builder.
// If we find a field that matches, also update its name to be in the canonical form (i.e., "_" + recordTypeName)
DescriptorProtos.DescriptorProto.Builder unionBuilder = fetchUnionBuilder(recordsBuilder);
RecordMetaDataOptionsProto.RecordTypeOptions.Usage usage;
if (unionBuilder.getName().equals(recordTypeName)) {
usage = RecordMetaDataOptionsProto.RecordTypeOptions.Usage.UNION;
} else {
DescriptorProtos.FieldDescriptorProto.Builder unionFieldBuilder = fetchUnionFieldBuilder(recordsBuilder, unionBuilder, recordTypeName);
if (unionFieldBuilder == null) {
usage = RecordMetaDataOptionsProto.RecordTypeOptions.Usage.NESTED;
} else {
usage = RecordMetaDataOptionsProto.RecordTypeOptions.Usage.RECORD;
// Change the name to the "canonical" form unless that would cause a field name conflict
if (unionFieldBuilder.getName().equals("_" + recordTypeName)) {
String newFieldName = "_" + newRecordTypeName;
if (unionBuilder.getFieldBuilderList().stream().noneMatch(otherUnionField -> otherUnionField != unionFieldBuilder && otherUnionField.getName().equals(newFieldName))) {
unionFieldBuilder.setName(newFieldName);
}
}
}
}
// Do not allow renaming to the default union name unless the record type is already the union
if (newRecordTypeName.equals(RecordMetaDataBuilder.DEFAULT_UNION_NAME) && !RecordMetaDataOptionsProto.RecordTypeOptions.Usage.UNION.equals(usage)) {
throw new MetaDataException("Cannot rename record type to the default union name", LogMessageKeys.RECORD_TYPE, recordTypeName);
}
// Rename the record type within the file
renameRecordTypeUsages(recordsBuilder, recordTypeName, newRecordTypeName);
// If the record type is a top level record type, change its usage elsewhere in the meta-data
if (RecordMetaDataOptionsProto.RecordTypeOptions.Usage.RECORD.equals(usage)) {
renameTopLevelRecordType(metaDataBuilder, recordTypeName, newRecordTypeName);
}
// Update the file descriptor
metaDataBuilder.setRecords(recordsBuilder);
}
use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.
the class IndexingBase method validateSameMetadataOrThrow.
protected void validateSameMetadataOrThrow(FDBRecordStore store) {
final RecordMetaData metaData = store.getRecordMetaData();
final RecordMetaDataProvider recordMetaDataProvider = common.getRecordStoreBuilder().getMetaDataProvider();
if (recordMetaDataProvider == null || !metaData.equals(recordMetaDataProvider.getRecordMetaData())) {
throw new MetaDataException("Store does not have the same metadata");
}
}
use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.
the class FDBMetaDataStore method saveAndSetCurrent.
/**
* Build meta-data to use from Protobuf and save.
* @param metaDataProto the Protobuf form of the meta-data to save
* @return a future that completes when the save is done
*/
@Nonnull
public CompletableFuture<Void> saveAndSetCurrent(@Nonnull RecordMetaDataProto.MetaData metaDataProto) {
RecordMetaData validatedMetaData = buildMetaData(metaDataProto, true);
// Load even if not maintaining history so as to get compatibility upgrade before (over-)writing.
CompletableFuture<Void> future = loadCurrentSerialized().thenApply(oldSerialized -> {
if (oldSerialized != null) {
RecordMetaDataProto.MetaData oldProto = parseMetaDataProto(oldSerialized);
int oldVersion = oldProto.getVersion();
if (metaDataProto.getVersion() <= oldVersion) {
LOGGER.warn(KeyValueLogMessage.of("Meta-data version did not increase", subspaceProvider.logKey(), subspaceProvider.toString(context), LogMessageKeys.OLD, oldVersion, LogMessageKeys.NEW, metaDataProto.getVersion()));
throw new MetaDataException("meta-data version must increase");
}
// Build the meta-data, but don't use the local file descriptor as this should validate the original descriptors
// against each other.
RecordMetaData oldMetaData = buildMetaData(oldProto, true, false);
RecordMetaData newMetaData = buildMetaData(metaDataProto, true, false);
evolutionValidator.validate(oldMetaData, newMetaData);
if (maintainHistory) {
SplitHelper.saveWithSplit(context, getSubspace(), HISTORY_KEY_PREFIX.add(oldVersion), oldSerialized, null);
}
}
return null;
});
future = future.thenApply(vignore -> {
recordMetaData = validatedMetaData;
byte[] serialized = metaDataProto.toByteArray();
SplitHelper.saveWithSplit(context, getSubspace(), CURRENT_KEY, serialized, null);
if (cache != null) {
cache.setCurrentVersion(context, recordMetaData.getVersion());
addPendingCacheUpdate(recordMetaData);
addPendingCacheUpdate(serialized);
}
return null;
});
return instrument(FDBStoreTimer.Events.SAVE_META_DATA, future);
}
use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.
the class RecordMetaData method toProto.
/**
* Serializes the record meta-data to a <code>MetaData</code> proto message. This operates like
* {@link #toProto()} except that any dependency in the excluded list is not included in the
* serialized proto message. If the list is set to {@code null}, then all dependencies will be serialized
* to the proto message including those that are execluded by default.
*
* @param excludedDependencies a list of dependencies not to include in the serialized proto
* @return the serialized <code>MetaData</code> proto message
* @throws KeyExpression.SerializationException on any serialization failures
* @throws MetaDataException if this {@code RecordMetaData} was initialized with a
* {@linkplain RecordMetaDataBuilder#setLocalFileDescriptor(Descriptors.FileDescriptor) local file descriptor}
* @see #toProto()
*/
@Nonnull
@SuppressWarnings("deprecation")
public RecordMetaDataProto.MetaData toProto(@Nullable Descriptors.FileDescriptor[] excludedDependencies) throws KeyExpression.SerializationException {
if (usesLocalRecordsDescriptor) {
throw new MetaDataException("cannot serialize meta-data with a local records descriptor to proto");
}
RecordMetaDataProto.MetaData.Builder builder = RecordMetaDataProto.MetaData.newBuilder();
// Set the root records.
builder.setRecords(recordsDescriptor.toProto());
// Convert the exclusion list to a map
Map<String, Descriptors.FileDescriptor> excludeMap = null;
if (excludedDependencies != null) {
excludeMap = new HashMap<>(excludedDependencies.length);
for (Descriptors.FileDescriptor dependency : excludedDependencies) {
excludeMap.put(dependency.getName(), dependency);
}
}
// Add in the rest of dependencies.
Map<String, Descriptors.FileDescriptor> allDependencies = new TreeMap<>();
getDependencies(recordsDescriptor, allDependencies, excludeMap);
for (Descriptors.FileDescriptor dependency : allDependencies.values()) {
builder.addDependencies(dependency.toProto());
}
// Create builders for each index so that we can then add associated record types (etc.).
Map<String, RecordMetaDataProto.Index.Builder> indexBuilders = new TreeMap<>();
for (Map.Entry<String, Index> entry : indexes.entrySet()) {
indexBuilders.put(entry.getKey(), entry.getValue().toProto().toBuilder());
}
for (RecordType recordType : getRecordTypes().values()) {
// Add this record type to each appropriate index.
for (Index index : recordType.getIndexes()) {
indexBuilders.get(index.getName()).addRecordType(recordType.getName());
}
for (Index index : recordType.getMultiTypeIndexes()) {
indexBuilders.get(index.getName()).addRecordType(recordType.getName());
}
RecordMetaDataProto.RecordType.Builder typeBuilder = builder.addRecordTypesBuilder().setName(recordType.getName()).setPrimaryKey(recordType.getPrimaryKey().toKeyExpression());
if (recordType.getSinceVersion() != null) {
typeBuilder.setSinceVersion(recordType.getSinceVersion());
}
if (recordType.hasExplicitRecordTypeKey()) {
typeBuilder.setExplicitKey(LiteralKeyExpression.toProtoValue(recordType.getExplicitRecordTypeKey()));
}
}
indexBuilders.values().forEach(builder::addIndexes);
// Add in the former indexes.
for (FormerIndex formerIndex : getFormerIndexes()) {
builder.addFormerIndexes(formerIndex.toProto());
}
// Add in the final options.
builder.setSplitLongRecords(splitLongRecords);
builder.setStoreRecordVersions(storeRecordVersions);
builder.setVersion(version);
if (usesSubspaceKeyCounter()) {
builder.setSubspaceKeyCounter(subspaceKeyCounter);
builder.setUsesSubspaceKeyCounter(true);
}
if (recordCountKey != null) {
builder.setRecordCountKey(recordCountKey.toKeyExpression());
}
for (SyntheticRecordType<?> syntheticRecordType : syntheticRecordTypes.values()) {
if (syntheticRecordType instanceof JoinedRecordType) {
builder.addJoinedRecordTypes(((JoinedRecordType) syntheticRecordType).toProto());
}
}
return builder.build();
}
use of com.apple.foundationdb.record.metadata.MetaDataException in project fdb-record-layer by FoundationDB.
the class RecordMetaDataBuilder method removeIndex.
public void removeIndex(@Nonnull String name) {
Index index = indexes.remove(name);
if (index == null) {
throw new MetaDataException("No index named " + name + " defined");
}
for (RecordTypeBuilder recordType : recordTypes.values()) {
recordType.getIndexes().remove(index);
recordType.getMultiTypeIndexes().remove(index);
}
universalIndexes.remove(name);
formerIndexes.add(new FormerIndex(index.getSubspaceKey(), index.getAddedVersion(), ++version, name));
}
Aggregations