/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.ais.protobuf;
import com.foundationdb.ais.model.*;
import com.foundationdb.ais.util.TableChange;
import com.foundationdb.server.error.ProtobufReadException;
import com.foundationdb.server.spatial.Spatial;
import com.foundationdb.server.store.format.StorageFormatRegistry;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.service.TypesRegistry;
import com.google.protobuf.AbstractMessage;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.Descriptors;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
public class ProtobufReader {
private final TypesRegistry typesRegistry;
private final StorageFormatRegistry storageFormatRegistry;
private final AkibanInformationSchema destAIS;
private final AISProtobuf.AkibanInformationSchema.Builder pbAISBuilder;
private final NameGenerator nameGenerator = new DefaultNameGenerator();
public ProtobufReader(TypesRegistry typesRegistry, StorageFormatRegistry storageFormatRegistry) {
this(typesRegistry, storageFormatRegistry, new AkibanInformationSchema());
}
public ProtobufReader(TypesRegistry typesRegistry, StorageFormatRegistry storageFormatRegistry, AkibanInformationSchema destAIS) {
this(typesRegistry, storageFormatRegistry, destAIS, AISProtobuf.AkibanInformationSchema.newBuilder());
}
public ProtobufReader(TypesRegistry typesRegistry, StorageFormatRegistry storageFormatRegistry, AkibanInformationSchema destAIS, AISProtobuf.AkibanInformationSchema.Builder pbAISBuilder) {
this.typesRegistry = typesRegistry;
this.storageFormatRegistry = storageFormatRegistry;
this.destAIS = destAIS;
this.pbAISBuilder = pbAISBuilder;
}
public AkibanInformationSchema getAIS() {
return destAIS;
}
public ProtobufReader loadAIS() {
// AIS has two fields (types, schemas) and both are optional
AISProtobuf.AkibanInformationSchema pbAIS = pbAISBuilder.clone().build();
loadSchemas(pbAIS.getSchemasList());
return this;
}
public ProtobufReader loadBuffer(ByteBuffer buffer) {
loadFromBuffer(buffer);
return this;
}
public AkibanInformationSchema loadAndGetAIS(ByteBuffer buffer) {
loadBuffer(buffer);
loadAIS();
return getAIS();
}
private void loadFromBuffer(ByteBuffer buffer) {
final String MESSAGE_NAME = AISProtobuf.AkibanInformationSchema.getDescriptor().getFullName();
checkBuffer(buffer);
final int serializedSize = buffer.getInt();
final int initialPos = buffer.position();
final int bufferSize = buffer.limit() - initialPos;
if(bufferSize < serializedSize) {
throw new ProtobufReadException(MESSAGE_NAME, "Buffer corrupt, serialized size greater than remaining");
}
CodedInputStream codedInput = CodedInputStream.newInstance(buffer.array(), buffer.position(), Math.min(serializedSize, bufferSize));
try {
pbAISBuilder.mergeFrom(codedInput, storageFormatRegistry.getExtensionRegistry());
// Successfully consumed, update byte buffer
buffer.position(initialPos + serializedSize);
} catch(IOException e) {
// CodedInputStream really only throws InvalidProtocolBufferException, but declares IOE
throw new ProtobufReadException(MESSAGE_NAME, e.getMessage());
}
}
private void loadSchemas(Collection<AISProtobuf.Schema> pbSchemas) {
List<List<NewGroupInfo>> allNewGroups = new ArrayList<>();
for(AISProtobuf.Schema pbSchema : pbSchemas) {
hasRequiredFields(pbSchema);
loadSequences (pbSchema.getSchemaName(), pbSchema.getSequencesList());
List<NewGroupInfo> newGroups = loadGroups(pbSchema.getSchemaName(), pbSchema.getGroupsList());
allNewGroups.add(newGroups);
// Requires no tables, does not load indexes
loadTables(pbSchema.getSchemaName(), pbSchema.getTablesList());
loadViews(pbSchema.getSchemaName(), pbSchema.getViewsList());
loadRoutines(pbSchema.getSchemaName(), pbSchema.getRoutinesList());
loadSQLJJars(pbSchema.getSchemaName(), pbSchema.getSqljJarsList());
}
// Assume no ordering of schemas or tables, load joins and view refs second
for(AISProtobuf.Schema pbSchema : pbSchemas) {
loadTableJoins(pbSchema.getSchemaName(), pbSchema.getTablesList());
loadExternalRoutines(pbSchema.getSchemaName(), pbSchema.getRoutinesList());
}
// Hook up groups, create group tables and indexes after all in place
for(List<NewGroupInfo> newGroups : allNewGroups) {
hookUpGroupAndCreateGroupIndexes(newGroups);
}
// Likewise full text indexes and foreign keys.
for(AISProtobuf.Schema pbSchema : pbSchemas) {
loadFullTextIndexes(pbSchema.getSchemaName(), pbSchema.getTablesList());
loadForeignKeys(pbSchema.getSchemaName(), pbSchema.getTablesList());
}
}
private List<NewGroupInfo> loadGroups(String schema, Collection<AISProtobuf.Group> pbGroups) {
List<NewGroupInfo> newGroups = new ArrayList<>();
for(AISProtobuf.Group pbGroup : pbGroups) {
hasRequiredFields(pbGroup);
String rootTableName = pbGroup.getRootTableName();
Group group = Group.create(destAIS, schema, rootTableName);
StorageDescription storage = null;
if (pbGroup.hasStorage()) {
storage = storageFormatRegistry.readProtobuf(pbGroup.getStorage(), group);
}
group.setStorageDescription(storage);
newGroups.add(new NewGroupInfo(schema, group, pbGroup));
}
return newGroups;
}
private void hookUpGroupAndCreateGroupIndexes(List<NewGroupInfo> newGroups) {
List<Join> joinsNeedingGroup = new ArrayList<>();
for(NewGroupInfo newGroupInfo : newGroups) {
String rootTableName = newGroupInfo.pbGroup.getRootTableName();
Table rootTable = destAIS.getTable(newGroupInfo.schema, rootTableName);
rootTable.setGroup(newGroupInfo.group);
joinsNeedingGroup.addAll(rootTable.getCandidateChildJoins());
newGroupInfo.group.setRootTable(rootTable);
}
for(int i = 0; i < joinsNeedingGroup.size(); ++i) {
Join join = joinsNeedingGroup.get(i);
Group group = join.getParent().getGroup();
join.setGroup(group);
join.getChild().setGroup(group);
joinsNeedingGroup.addAll(join.getChild().getCandidateChildJoins());
}
// Final pass (GI creation requires everything else be completed)
for(NewGroupInfo newGroupInfo : newGroups) {
loadGroupIndexes(newGroupInfo.group, newGroupInfo.pbGroup.getIndexesList());
}
}
private void loadTables(String schema, Collection<AISProtobuf.Table> pbTables) {
int generatedId = 1;
for(AISProtobuf.Table pbTable : pbTables) {
hasRequiredFields(pbTable);
Table table = Table.create(
destAIS,
schema,
pbTable.getTableName(),
pbTable.hasTableId() ? pbTable.getTableId() : generatedId++
);
if(pbTable.hasOrdinal()) {
table.setOrdinal(pbTable.getOrdinal());
}
if (pbTable.hasUuid()) {
table.setUuid(convertUUID(pbTable.getUuid()));
}
if(pbTable.hasCharColl()) {
AISProtobuf.CharCollation pbCharAndCol = pbTable.getCharColl();
hasRequiredFields(pbCharAndCol);
table.setCharsetAndCollation(pbCharAndCol.getCharacterSetName(),
pbCharAndCol.getCollationOrderName());
}
if(pbTable.hasVersion()) {
table.setVersion(pbTable.getVersion());
}
loadColumns(table, pbTable.getColumnsList());
loadTableIndexes(table, pbTable.getIndexesList());
if (pbTable.hasPendingOSC()) {
table.setPendingOSC(loadPendingOSC(pbTable.getPendingOSC()));
}
}
}
private void loadSequences (String schema, Collection<AISProtobuf.Sequence> pbSequences) {
for (AISProtobuf.Sequence pbSequence : pbSequences) {
hasRequiredFields(pbSequence);
Sequence sequence = Sequence.create(
destAIS,
schema,
pbSequence.getSequenceName(),
pbSequence.getStart(),
pbSequence.getIncrement(),
pbSequence.getMinValue(),
pbSequence.getMaxValue(),
pbSequence.getIsCycle());
StorageDescription storage = null;
if (pbSequence.hasStorage()) {
storage = storageFormatRegistry.readProtobuf(pbSequence.getStorage(), sequence);
}
sequence.setStorageDescription(storage);
}
}
private void loadTableJoins(String schema, Collection<AISProtobuf.Table> pbTables) {
for(AISProtobuf.Table pbTable : pbTables) {
if(pbTable.hasParentTable()) {
AISProtobuf.Join pbJoin = pbTable.getParentTable();
hasRequiredFields(pbJoin);
AISProtobuf.TableName pbParentName = pbJoin.getParentTable();
hasRequiredFields(pbParentName);
Table childTable = destAIS.getTable(schema, pbTable.getTableName());
Table parentTable = destAIS.getTable(pbParentName.getSchemaName(), pbParentName.getTableName());
if(parentTable == null) {
throw new ProtobufReadException(
pbTable.getDescriptorForType().getFullName(),
String.format("%s has unknown parentTable %s.%s", childTable.getName(),
pbParentName.getSchemaName(), pbParentName.getTableName())
);
}
List<String> parentColNames = new ArrayList<>();
List<String> childColNames = new ArrayList<>();
for(AISProtobuf.JoinColumn pbJoinColumn : pbJoin.getColumnsList()) {
hasRequiredFields(pbJoinColumn);
parentColNames.add(pbJoinColumn.getParentColumn());
childColNames.add(pbJoinColumn.getChildColumn());
}
Join join = Join.create(destAIS, pbJoin.getConstraintName().getTableName(), parentTable, childTable);
for(AISProtobuf.JoinColumn pbJoinColumn : pbJoin.getColumnsList()) {
JoinColumn.create(
join,
parentTable.getColumn(pbJoinColumn.getParentColumn()),
childTable.getColumn(pbJoinColumn.getChildColumn())
);
}
}
}
}
private void loadViews(String schema, Collection<AISProtobuf.View> pbViews) {
for(AISProtobuf.View pbView : pbViews) {
hasRequiredFields(pbView);
Map<TableName,Collection<String>> refs =
new HashMap<>();
for(AISProtobuf.ColumnReference pbReference : pbView.getReferencesList()) {
hasRequiredFields(pbReference);
AISProtobuf.TableName pbTableName = pbReference.getTable();
hasRequiredFields(pbTableName);
TableName tableName = TableName.create(pbTableName.getSchemaName(), pbTableName.getTableName());
Collection<String> columns = new HashSet<>();
Collection<String> old = refs.put(tableName, columns);
assert (old == null);
for(String colname : pbReference.getColumnsList()) {
boolean added = columns.add(colname);
assert added;
}
}
View view = View.create(
destAIS,
schema,
pbView.getViewName(),
pbView.getDefinition(),
loadProperties(pbView.getDefinitionPropertiesList()),
refs
);
loadColumns(view, pbView.getColumnsList());
}
}
private Properties loadProperties(Collection<AISProtobuf.Property> pbProperties) {
Properties properties = new Properties();
for(AISProtobuf.Property pbProperty : pbProperties) {
hasRequiredFields(pbProperty);
properties.put(pbProperty.getKey(), pbProperty.getValue());
}
return properties;
}
private void loadColumns(Columnar columnar, Collection<AISProtobuf.Column> pbColumns) {
for(AISProtobuf.Column pbColumn : pbColumns) {
hasRequiredFields(pbColumn);
Long maxStorageSize = pbColumn.hasMaxStorageSize() ? pbColumn.getMaxStorageSize() : null;
Integer prefixSize = pbColumn.hasPrefixSize() ? pbColumn.getPrefixSize() : null;
String charset = null, collation = null;
if (pbColumn.hasCharColl()) {
AISProtobuf.CharCollation pbCharAndCol = pbColumn.getCharColl();
hasRequiredFields(pbCharAndCol);
charset = pbCharAndCol.getCharacterSetName();
collation = pbCharAndCol.getCollationOrderName();
}
TInstance type = typesRegistry.getType(
convertUUID(pbColumn.getTypeBundleUUID()),
pbColumn.getTypeName(),
pbColumn.getTypeVersion(),
pbColumn.hasTypeParam1() ? pbColumn.getTypeParam1() : null,
pbColumn.hasTypeParam2() ? pbColumn.getTypeParam2() : null,
charset, collation,
pbColumn.getIsNullable(),
columnar.getName().getSchemaName(), columnar.getName().getTableName(),
pbColumn.getColumnName()
);
Column column = Column.create(
columnar,
pbColumn.getColumnName(),
pbColumn.getPosition(),
type,
pbColumn.hasInitAutoInc() ? pbColumn.getInitAutoInc() : null,
maxStorageSize,
prefixSize
);
if (pbColumn.hasUuid()) {
if (pbColumn.hasUuid()) {
column.setUuid(convertUUID(pbColumn.getUuid()));
}
}
if (pbColumn.hasDefaultIdentity()) {
column.setDefaultIdentity(pbColumn.getDefaultIdentity());
}
if (pbColumn.hasSequence()) {
TableName sequenceName = new TableName (pbColumn.getSequence().getSchemaName(), pbColumn.getSequence().getTableName());
Sequence identityGenerator = getAIS().getSequence(sequenceName);
column.setIdentityGenerator(identityGenerator);
}
if (pbColumn.hasDefaultValue()) {
column.setDefaultValue(pbColumn.getDefaultValue());
}
if (pbColumn.hasDefaultFunction()) {
column.setDefaultFunction(pbColumn.getDefaultFunction());
}
}
}
private void loadTableIndexes(Table table, Collection<AISProtobuf.Index> pbIndexes) {
for(AISProtobuf.Index pbIndex : pbIndexes) {
hasRequiredFields(pbIndex);
TableIndex tableIndex = TableIndex.create(
destAIS,
table,
pbIndex.getIndexName(),
pbIndex.getIndexId(),
pbIndex.getIsUnique(),
pbIndex.getIsPK(),
getConstraintName(pbIndex)
);
handleStorage(tableIndex, pbIndex);
handleSpatial(tableIndex, pbIndex);
loadIndexColumns(table, tableIndex, pbIndex.getColumnsList());
}
}
private void loadGroupIndexes(Group group, Collection<AISProtobuf.Index> pbIndexes) {
for(AISProtobuf.Index pbIndex : pbIndexes) {
hasRequiredFieldsGI(pbIndex);
GroupIndex groupIndex = GroupIndex.create(
destAIS,
group,
pbIndex.getIndexName(),
pbIndex.getIndexId(),
pbIndex.getIsUnique(),
pbIndex.getIsPK(),
getConstraintName(pbIndex),
convertJoinTypeOrNull(pbIndex.hasJoinType(), pbIndex.getJoinType())
);
handleStorage(groupIndex, pbIndex);
handleSpatial(groupIndex, pbIndex);
loadIndexColumns(null, groupIndex, pbIndex.getColumnsList());
}
}
private void loadFullTextIndexes(String schema, Collection<AISProtobuf.Table> pbTables) {
for(AISProtobuf.Table pbTable : pbTables) {
for(AISProtobuf.Index pbIndex : pbTable.getFullTextIndexesList()) {
hasRequiredFields(pbIndex);
Table table = destAIS.getTable(schema, pbTable.getTableName());
FullTextIndex textIndex = FullTextIndex.create(
destAIS,
table,
pbIndex.getIndexName(),
pbIndex.getIndexId(),
getConstraintName(pbIndex)
);
handleStorage(textIndex, pbIndex);
handleSpatial(textIndex, pbIndex);
loadIndexColumns(table, textIndex, pbIndex.getColumnsList());
}
}
}
private TableName getConstraintName(AISProtobuf.Index pbIndex){
TableName constraintName = null;
if (pbIndex.hasConstraintName()) {
constraintName = new TableName(pbIndex.getConstraintName().getSchemaName(), pbIndex.getConstraintName().getTableName());
}
return constraintName;
}
private void loadForeignKeys(String schema, Collection<AISProtobuf.Table> pbTables) {
for(AISProtobuf.Table pbTable : pbTables) {
for(AISProtobuf.ForeignKey pbFK : pbTable.getForeignKeysList()) {
hasRequiredFields(pbFK);
Table referencingTable = destAIS.getTable(schema, pbTable.getTableName());
Table referencedTable = destAIS.getTable(convertTableNameOrNull(true, pbFK.getReferencedTable()));
List<Column> referencingColumns = getForeignKeyColumns(referencingTable, pbFK.getReferencingColumnsList());
List<Column> referencedColumns = getForeignKeyColumns(referencedTable, pbFK.getReferencedColumnsList());
ForeignKey.create(destAIS,
pbFK.getConstraintName(),
referencingTable,
referencingColumns,
referencedTable,
referencedColumns,
convertForeignKeyAction(pbFK.getOnDelete()),
convertForeignKeyAction(pbFK.getOnUpdate()),
pbFK.getDeferrable(),
pbFK.getInitiallyDeferred());
}
}
}
private List<Column> getForeignKeyColumns(Table table, List<String> names) {
List<Column> result = new ArrayList<>();
for(String name : names) {
result.add(table.getColumn(name));
}
return result;
}
private static ForeignKey.Action convertForeignKeyAction(AISProtobuf.ForeignKeyAction action) {
switch (action) {
case NO_ACTION:
default:
return ForeignKey.Action.NO_ACTION;
case RESTRICT:
return ForeignKey.Action.RESTRICT;
case CASCADE:
return ForeignKey.Action.CASCADE;
case SET_NULL:
return ForeignKey.Action.SET_NULL;
case SET_DEFAULT:
return ForeignKey.Action.SET_DEFAULT;
}
}
private void handleStorage(Index index, AISProtobuf.Index pbIndex) {
StorageDescription storage = null;
if (pbIndex.hasStorage()) {
storage = storageFormatRegistry.readProtobuf(pbIndex.getStorage(), index);
}
index.setStorageDescription(storage);
}
private void handleSpatial(Index index, AISProtobuf.Index pbIndex) {
if (pbIndex.hasIndexMethod()) {
switch (pbIndex.getIndexMethod()) {
case Z_ORDER_LAT_LON:
assert pbIndex.hasFirstSpatialArg() == pbIndex.hasDimensions();
int firstSpatialArg = 0;
int lastSpatialArg = 0;
int dimensions = Spatial.LAT_LON_DIMENSIONS;
if (pbIndex.hasFirstSpatialArg()) {
firstSpatialArg = pbIndex.getFirstSpatialArg();
dimensions = pbIndex.getDimensions();
if (pbIndex.hasLastSpatialArg()) {
lastSpatialArg = pbIndex.getLastSpatialArg();
} else {
// Schema created before spatial object support, when spatial meant just lat/lon.
lastSpatialArg = firstSpatialArg + dimensions - 1;
}
}
index.markSpatial(firstSpatialArg, lastSpatialArg - firstSpatialArg + 1);
break;
}
}
}
private void loadIndexColumns(Table table, Index index, Collection<AISProtobuf.IndexColumn> pbIndexColumns) {
for(AISProtobuf.IndexColumn pbIndexColumn : pbIndexColumns) {
hasRequiredFields(pbIndexColumn);
if(pbIndexColumn.hasTableName()) {
hasRequiredFields(pbIndexColumn.getTableName());
table = destAIS.getTable(convertTableNameOrNull(true, pbIndexColumn.getTableName()));
}
Integer indexedLength = null;
if(pbIndexColumn.hasIndexedLength()) {
indexedLength = pbIndexColumn.getIndexedLength();
}
IndexColumn.create(
index,
table != null ? table.getColumn(pbIndexColumn.getColumnName()) : null,
pbIndexColumn.getPosition(),
pbIndexColumn.getIsAscending(),
indexedLength
);
}
}
private void loadRoutines(String schema, Collection<AISProtobuf.Routine> pbRoutines) {
for (AISProtobuf.Routine pbRoutine : pbRoutines) {
hasRequiredFields(pbRoutine);
Routine routine = Routine.create(
destAIS,
schema,
pbRoutine.getRoutineName(),
pbRoutine.getLanguage(),
convertRoutineCallingConvention(pbRoutine.getCallingConvention())
);
loadParameters(routine, pbRoutine.getParametersList());
if (pbRoutine.hasDefinition())
routine.setDefinition(pbRoutine.getDefinition());
if (pbRoutine.hasSqlAllowed())
routine.setSQLAllowed(convertRoutineSQLAllowed(pbRoutine.getSqlAllowed()));
if (pbRoutine.hasDynamicResultSets())
routine.setDynamicResultSets(pbRoutine.getDynamicResultSets());
if (pbRoutine.hasDeterministic())
routine.setDeterministic(pbRoutine.getDeterministic());
if (pbRoutine.hasCalledOnNullInput())
routine.setCalledOnNullInput(pbRoutine.getCalledOnNullInput());
if (pbRoutine.hasVersion()) {
routine.setVersion(pbRoutine.getVersion());
}
}
}
private void loadParameters(Routine routine, Collection<AISProtobuf.Parameter> pbParameters) {
for (AISProtobuf.Parameter pbParameter : pbParameters) {
hasRequiredFields(pbParameter);
TInstance type = typesRegistry.getType(
convertUUID(pbParameter.getTypeBundleUUID()),
pbParameter.getTypeName(),
pbParameter.getTypeVersion(),
pbParameter.hasTypeParam1() ? pbParameter.getTypeParam1() : null,
pbParameter.hasTypeParam2() ? pbParameter.getTypeParam2() : null,
true,
routine.getName().getSchemaName(), routine.getName().getTableName(),
pbParameter.getParameterName()
);
Parameter parameter = Parameter.create(
routine,
pbParameter.getParameterName(),
convertParameterDirection(pbParameter.getDirection()),
type
);
}
}
private static Routine.CallingConvention convertRoutineCallingConvention(AISProtobuf.RoutineCallingConvention callingConvention) {
switch (callingConvention) {
case JAVA:
default:
return Routine.CallingConvention.JAVA;
case LOADABLE_PLAN:
return Routine.CallingConvention.LOADABLE_PLAN;
case SQL_ROW:
return Routine.CallingConvention.SQL_ROW;
case SCRIPT_FUNCTION_JAVA:
return Routine.CallingConvention.SCRIPT_FUNCTION_JAVA;
case SCRIPT_BINDINGS:
return Routine.CallingConvention.SCRIPT_BINDINGS;
case SCRIPT_FUNCTION_JSON:
return Routine.CallingConvention.SCRIPT_FUNCTION_JSON;
case SCRIPT_BINDINGS_JSON:
return Routine.CallingConvention.SCRIPT_BINDINGS_JSON;
case SCRIPT_LIBRARY:
return Routine.CallingConvention.SCRIPT_LIBRARY;
}
}
private static Routine.SQLAllowed convertRoutineSQLAllowed(AISProtobuf.RoutineSQLAllowed sqlAllowed) {
switch (sqlAllowed) {
case MODIFIES_SQL_DATA:
default:
return Routine.SQLAllowed.MODIFIES_SQL_DATA;
case READS_SQL_DATA:
return Routine.SQLAllowed.READS_SQL_DATA;
case CONTAINS_SQL:
return Routine.SQLAllowed.CONTAINS_SQL;
case NO_SQL:
return Routine.SQLAllowed.NO_SQL;
}
}
private static Parameter.Direction convertParameterDirection(AISProtobuf.ParameterDirection parameterDirection) {
switch (parameterDirection) {
case IN:
default:
return Parameter.Direction.IN;
case OUT:
return Parameter.Direction.OUT;
case INOUT:
return Parameter.Direction.INOUT;
case RETURN:
return Parameter.Direction.RETURN;
}
}
private void loadSQLJJars(String schema, Collection<AISProtobuf.SQLJJar> pbJars) {
for (AISProtobuf.SQLJJar pbJar : pbJars) {
hasRequiredFields(pbJar);
try {
SQLJJar sqljJar = SQLJJar.create(destAIS,
schema,
pbJar.getJarName(),
new URL(pbJar.getUrl()));
if (pbJar.hasVersion()) {
sqljJar.setVersion(pbJar.getVersion());
}
}
catch (MalformedURLException ex) {
throw new ProtobufReadException(
pbJar.getDescriptorForType().getFullName(),
ex.toString()
);
}
}
}
private void loadExternalRoutines(String schema, Collection<AISProtobuf.Routine> pbRoutines) {
for (AISProtobuf.Routine pbRoutine : pbRoutines) {
if (pbRoutine.hasClassName() || pbRoutine.hasMethodName()) {
Routine routine = destAIS.getRoutine(schema, pbRoutine.getRoutineName());
if (routine == null) {
throw new ProtobufReadException(
pbRoutine.getDescriptorForType().getFullName(),
String.format("%s not found", pbRoutine.getRoutineName())
);
}
SQLJJar sqljJar = null;
String className = null;
String methodName = null;
if (pbRoutine.hasJarName()) {
sqljJar = destAIS.getSQLJJar(pbRoutine.getJarName().getSchemaName(),
pbRoutine.getJarName().getTableName());
if (sqljJar == null) {
throw new ProtobufReadException(
pbRoutine.getDescriptorForType().getFullName(),
String.format("%s references JAR %s", pbRoutine.getRoutineName(), pbRoutine.getJarName())
);
}
}
if (pbRoutine.hasClassName())
className = pbRoutine.getClassName();
if (pbRoutine.hasMethodName())
methodName = pbRoutine.getMethodName();
routine.setExternalName(sqljJar, className, methodName);
}
}
}
private PendingOSC loadPendingOSC(AISProtobuf.PendingOSC pbPendingOSC) {
hasRequiredFields(pbPendingOSC);
List<TableChange> columnChanges = new ArrayList<>();
loadPendingOSChanges(columnChanges, pbPendingOSC.getColumnChangesList());
List<TableChange> indexChanges = new ArrayList<>();
loadPendingOSChanges(indexChanges, pbPendingOSC.getIndexChangesList());
PendingOSC osc = new PendingOSC(pbPendingOSC.getOriginalName(), columnChanges, indexChanges);
if (pbPendingOSC.hasCurrentName())
osc.setCurrentName(pbPendingOSC.getCurrentName());
return osc;
}
private void loadPendingOSChanges(Collection<TableChange> changes, Collection<AISProtobuf.PendingOSChange> pbChanges) {
for (AISProtobuf.PendingOSChange pbChange : pbChanges) {
hasRequiredFields(pbChange);
switch (pbChange.getType()) {
case ADD:
changes.add(TableChange.createAdd(pbChange.getNewName()));
break;
case DROP:
changes.add(TableChange.createDrop(pbChange.getOldName()));
break;
case MODIFY:
changes.add(TableChange.createModify(pbChange.getOldName(), pbChange.getNewName()));
break;
default:
throw new ProtobufReadException(AISProtobuf.PendingOSChange.getDescriptor().getFullName(),
"Unknown change type " + pbChange);
}
}
}
private static Index.JoinType convertJoinTypeOrNull(boolean isValid, AISProtobuf.JoinType joinType) {
if(isValid) {
switch(joinType) {
case LEFT_OUTER_JOIN: return Index.JoinType.LEFT;
case RIGHT_OUTER_JOIN: return Index.JoinType.RIGHT;
}
throw new ProtobufReadException(AISProtobuf.JoinType.getDescriptor().getFullName(),
"Unsupported join type: " + joinType.name());
}
return null;
}
private static UUID convertUUID(AISProtobuf.UUID pbUuid) {
return new UUID(pbUuid.getMostSignificantBits(),
pbUuid.getLeastSignificantBits());
}
private static TableName convertTableNameOrNull(boolean isValid, AISProtobuf.TableName tableName) {
if(isValid) {
hasRequiredFields(tableName);
return new TableName(tableName.getSchemaName(), tableName.getTableName());
}
return null;
}
/**
* Check that a given message instance has all (application) required fields.
* By default, this is all declared fields. See overloads for specific types.
* @param message Message to check
*/
private static void hasRequiredFields(AbstractMessage message) {
requireAllFieldsExcept(message);
}
private static void hasRequiredFields(AISProtobuf.Group pbGroup) {
requireAllFieldsExcept(
pbGroup,
AISProtobuf.Group.INDEXES_FIELD_NUMBER,
AISProtobuf.Group.STORAGE_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Join pbJoin) {
requireAllFieldsExcept(
pbJoin,
AISProtobuf.Join.CONSTRAINTNAME_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Schema pbSchema) {
requireAllFieldsExcept(
pbSchema,
AISProtobuf.Schema.TABLES_FIELD_NUMBER,
AISProtobuf.Schema.GROUPS_FIELD_NUMBER,
AISProtobuf.Schema.SEQUENCES_FIELD_NUMBER,
AISProtobuf.Schema.VIEWS_FIELD_NUMBER,
AISProtobuf.Schema.ROUTINES_FIELD_NUMBER,
AISProtobuf.Schema.SQLJJARS_FIELD_NUMBER,
AISProtobuf.Schema.CHARCOLL_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Table pbTable) {
requireAllFieldsExcept(
pbTable,
AISProtobuf.Table.TABLEID_FIELD_NUMBER,
AISProtobuf.Table.ORDINAL_FIELD_NUMBER,
AISProtobuf.Table.CHARCOLL_FIELD_NUMBER,
AISProtobuf.Table.INDEXES_FIELD_NUMBER,
AISProtobuf.Table.PARENTTABLE_FIELD_NUMBER,
AISProtobuf.Table.DESCRIPTION_FIELD_NUMBER,
AISProtobuf.Table.PROTECTED_FIELD_NUMBER,
AISProtobuf.Table.VERSION_FIELD_NUMBER,
AISProtobuf.Table.PENDINGOSC_FIELD_NUMBER,
AISProtobuf.Table.UUID_FIELD_NUMBER,
AISProtobuf.Table.FULLTEXTINDEXES_FIELD_NUMBER,
AISProtobuf.Table.ORDINAL_FIELD_NUMBER,
AISProtobuf.Table.FOREIGNKEYS_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.View pbView) {
requireAllFieldsExcept(
pbView,
AISProtobuf.View.COLUMNS_FIELD_NUMBER,
AISProtobuf.View.DEFINITIONPROPERTIES_FIELD_NUMBER,
AISProtobuf.View.REFERENCES_FIELD_NUMBER,
AISProtobuf.View.DESCRIPTION_FIELD_NUMBER,
AISProtobuf.View.PROTECTED_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Column pbColumn) {
requireAllFieldsExcept(
pbColumn,
AISProtobuf.Column.TYPEPARAM1_FIELD_NUMBER,
AISProtobuf.Column.TYPEPARAM2_FIELD_NUMBER,
AISProtobuf.Column.INITAUTOINC_FIELD_NUMBER,
AISProtobuf.Column.CHARCOLL_FIELD_NUMBER,
AISProtobuf.Column.DESCRIPTION_FIELD_NUMBER,
AISProtobuf.Column.DEFAULTIDENTITY_FIELD_NUMBER,
AISProtobuf.Column.SEQUENCE_FIELD_NUMBER,
AISProtobuf.Column.MAXSTORAGESIZE_FIELD_NUMBER,
AISProtobuf.Column.PREFIXSIZE_FIELD_NUMBER,
AISProtobuf.Column.DEFAULTVALUE_FIELD_NUMBER,
AISProtobuf.Column.UUID_FIELD_NUMBER,
AISProtobuf.Column.DEFAULTFUNCTION_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Index pbIndex) {
requireAllFieldsExcept(
pbIndex,
AISProtobuf.Index.DESCRIPTION_FIELD_NUMBER,
AISProtobuf.Index.JOINTYPE_FIELD_NUMBER,
AISProtobuf.Index.INDEXMETHOD_FIELD_NUMBER,
AISProtobuf.Index.FIRSTSPATIALARG_FIELD_NUMBER,
AISProtobuf.Index.LASTSPATIALARG_FIELD_NUMBER,
AISProtobuf.Index.DIMENSIONS_FIELD_NUMBER,
AISProtobuf.Index.STORAGE_FIELD_NUMBER,
AISProtobuf.Index.CONSTRAINTNAME_FIELD_NUMBER
);
}
private static void hasRequiredFieldsGI(AISProtobuf.Index pbIndex) {
requireAllFieldsExcept(
pbIndex,
AISProtobuf.Index.DESCRIPTION_FIELD_NUMBER,
AISProtobuf.Index.INDEXMETHOD_FIELD_NUMBER,
AISProtobuf.Index.FIRSTSPATIALARG_FIELD_NUMBER,
AISProtobuf.Index.LASTSPATIALARG_FIELD_NUMBER,
AISProtobuf.Index.DIMENSIONS_FIELD_NUMBER,
AISProtobuf.Index.STORAGE_FIELD_NUMBER,
AISProtobuf.Index.CONSTRAINTNAME_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.IndexColumn pbIndexColumn) {
requireAllFieldsExcept(
pbIndexColumn,
AISProtobuf.IndexColumn.TABLENAME_FIELD_NUMBER,
AISProtobuf.IndexColumn.INDEXEDLENGTH_FIELD_NUMBER
);
}
private static void hasRequiredFields (AISProtobuf.Sequence pbSequence) {
requireAllFieldsExcept(
pbSequence,
AISProtobuf.Sequence.ACCUMULATOR_FIELD_NUMBER,
AISProtobuf.Sequence.STORAGE_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Property pbProperty) {
requireAllFieldsExcept(
pbProperty
);
}
private static void hasRequiredFields(AISProtobuf.ColumnReference pbReference) {
requireAllFieldsExcept(
pbReference,
AISProtobuf.ColumnReference.COLUMNS_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Routine pbRoutine) {
requireAllFieldsExcept(
pbRoutine,
AISProtobuf.Routine.PARAMETERS_FIELD_NUMBER,
AISProtobuf.Routine.JARNAME_FIELD_NUMBER,
AISProtobuf.Routine.CLASSNAME_FIELD_NUMBER,
AISProtobuf.Routine.METHODNAME_FIELD_NUMBER,
AISProtobuf.Routine.DEFINITION_FIELD_NUMBER,
AISProtobuf.Routine.DESCRIPTION_FIELD_NUMBER,
AISProtobuf.Routine.PROTECTED_FIELD_NUMBER,
AISProtobuf.Routine.SQLALLOWED_FIELD_NUMBER,
AISProtobuf.Routine.DYNAMICRESULTSETS_FIELD_NUMBER,
AISProtobuf.Routine.DETERMINISTIC_FIELD_NUMBER,
AISProtobuf.Routine.CALLEDONNULLINPUT_FIELD_NUMBER,
AISProtobuf.Routine.VERSION_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.Parameter pbParameter) {
requireAllFieldsExcept(
pbParameter,
AISProtobuf.Parameter.PARAMETERNAME_FIELD_NUMBER,
AISProtobuf.Parameter.TYPEPARAM1_FIELD_NUMBER,
AISProtobuf.Parameter.TYPEPARAM2_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.SQLJJar pbJar) {
requireAllFieldsExcept(
pbJar,
AISProtobuf.SQLJJar.VERSION_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.PendingOSC pbPendingOSC) {
requireAllFieldsExcept(
pbPendingOSC,
AISProtobuf.PendingOSC.COLUMNCHANGES_FIELD_NUMBER,
AISProtobuf.PendingOSC.INDEXCHANGES_FIELD_NUMBER,
AISProtobuf.PendingOSC.CURRENTNAME_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.PendingOSChange pbChange) {
requireAllFieldsExcept(
pbChange,
AISProtobuf.PendingOSChange.OLDNAME_FIELD_NUMBER,
AISProtobuf.PendingOSChange.NEWNAME_FIELD_NUMBER
);
}
private static void hasRequiredFields(AISProtobuf.ForeignKey pbFK) {
requireAllFieldsExcept(
pbFK,
AISProtobuf.ForeignKey.CONSTRAINTNAME_FIELD_NUMBER,
AISProtobuf.ForeignKey.ONDELETE_FIELD_NUMBER,
AISProtobuf.ForeignKey.ONUPDATE_FIELD_NUMBER,
AISProtobuf.ForeignKey.DEFERRABLE_FIELD_NUMBER,
AISProtobuf.ForeignKey.INITIALLYDEFERRED_FIELD_NUMBER
);
}
private static void requireAllFieldsExcept(AbstractMessage message, int... fieldNumbersNotRequired) {
Collection<Descriptors.FieldDescriptor> required = new ArrayList<>(message.getDescriptorForType().getFields());
Collection<Descriptors.FieldDescriptor> actual = message.getAllFields().keySet();
required.removeAll(actual);
if(fieldNumbersNotRequired != null) {
for(int fieldNumber : fieldNumbersNotRequired) {
required.remove(message.getDescriptorForType().findFieldByNumber(fieldNumber));
}
}
if(!required.isEmpty()) {
Collection<String> names = new ArrayList<>(required.size());
for(Descriptors.FieldDescriptor desc : required) {
names.add(desc.getName());
}
throw new ProtobufReadException(message.getDescriptorForType().getFullName(),
"Missing required fields: " + names.toString());
}
}
private static void checkBuffer(ByteBuffer buffer) {
assert buffer != null;
assert buffer.hasArray() : "Array backed buffer required: " + buffer;
}
private static class NewGroupInfo {
final String schema;
final Group group;
final AISProtobuf.Group pbGroup;
public NewGroupInfo(String schema, Group group, AISProtobuf.Group pbGroup) {
this.schema = schema;
this.group = group;
this.pbGroup = pbGroup;
}
}
}