abhishek-chouhan commented on a change in pull request #699: PHOENIX-5496
Ensure that we handle all server-side mutation codes on the client
URL: https://github.com/apache/phoenix/pull/699#discussion_r384175122
##########
File path:
phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
##########
@@ -2957,149 +2958,187 @@ public boolean isViewReferenced() {
splits = SchemaUtil.processSplits(splits, pkColumns,
saltBucketNum, connection.getQueryServices().getProps().getBoolean(
QueryServices.FORCE_ROW_KEY_ORDER_ATTRIB,
QueryServicesOptions.DEFAULT_FORCE_ROW_KEY_ORDER));
}
- MetaDataMutationResult result =
connection.getQueryServices().createTable(
- tableMetaData,
- viewType == ViewType.MAPPED || allocateIndexId ?
physicalNames.get(0).getBytes() : null,
- tableType, tableProps, familyPropList, splits,
isNamespaceMapped, allocateIndexId,
- UpgradeUtil.isNoUpgradeSet(connection.getClientInfo()),
parent);
+
+ // Modularized this code for unit testing
+ MetaDataMutationResult result =
connection.getQueryServices().createTable(tableMetaData,
+ viewType == ViewType.MAPPED || allocateIndexId ?
physicalNames.get(0).
+ getBytes() : null, tableType, tableProps,
familyPropList, splits,
+ isNamespaceMapped, allocateIndexId,
UpgradeUtil.isNoUpgradeSet(
+ connection.getClientInfo()), parent);
MutationCode code = result.getMutationCode();
- switch(code) {
+ if (code != MutationCode.TABLE_NOT_FOUND) {
+ boolean tableAlreadyExists =
handleCreateTableMutationCode(result, code, statement,
+ schemaName, tableName, parent);
+ if(tableAlreadyExists) {
+ return null;
+ }
+ }
+ // If the parent table of the view has the auto partition sequence
name attribute,
+ // set the view statement and relevant partition column attributes
correctly
+ if (parent!=null && parent.getAutoPartitionSeqName()!=null) {
+ final PColumn autoPartitionCol =
parent.getPKColumns().get(MetaDataUtil
+ .getAutoPartitionColIndex(parent));
+ final Long autoPartitionNum =
Long.valueOf(result.getAutoPartitionNum());
+ columns.put(autoPartitionCol, new
DelegateColumn(autoPartitionCol) {
+ @Override
+ public byte[] getViewConstant() {
+ PDataType dataType = autoPartitionCol.getDataType();
+ Object val = dataType.toObject(autoPartitionNum,
PLong.INSTANCE);
+ byte[] bytes = new byte [dataType.getByteSize() + 1];
+ dataType.toBytes(val, bytes, 0);
+ return bytes;
+ }
+ @Override
+ public boolean isViewReferenced() {
+ return true;
+ }
+ });
+ String viewPartitionClause =
QueryUtil.getViewPartitionClause(MetaDataUtil
+ .getAutoPartitionColumnName(parent), autoPartitionNum);
+ if (viewStatement!=null) {
+ viewStatement = viewStatement + " AND " +
viewPartitionClause;
+ }
+ else {
+ viewStatement =
QueryUtil.getViewStatement(parent.getSchemaName().getString(),
+ parent.getTableName().getString(),
viewPartitionClause);
+ }
+ }
+ PName newSchemaName = PNameFactory.newName(schemaName);
+ /*
+ * It doesn't hurt for the PTable of views to have the cqCounter.
However, views always
+ * rely on the parent table's counter to dole out encoded column
qualifiers. So setting
+ * the counter as NULL_COUNTER for extra safety.
+ */
+ EncodedCQCounter cqCounterToBe = tableType == PTableType.VIEW ?
NULL_COUNTER :
+ cqCounter;
+ PTable table = new PTableImpl.Builder()
+ .setType(tableType)
+ .setState(indexState)
+ .setTimeStamp(timestamp != null ? timestamp :
result.getMutationTime())
+ .setIndexDisableTimestamp(0L)
+ .setSequenceNumber(PTable.INITIAL_SEQ_NUM)
+ .setImmutableRows(isImmutableRows)
+ .setViewStatement(viewStatement)
+ .setDisableWAL(Boolean.TRUE.equals(disableWAL))
+ .setMultiTenant(multiTenant)
+ .setStoreNulls(storeNulls)
+ .setViewType(viewType)
+ .setViewIndexIdType(viewIndexIdType)
+ .setViewIndexId(result.getViewIndexId())
+ .setIndexType(indexType)
+ .setTransactionProvider(transactionProvider)
+ .setUpdateCacheFrequency(updateCacheFrequency)
+ .setNamespaceMapped(isNamespaceMapped)
+ .setAutoPartitionSeqName(autoPartitionSeq)
+ .setAppendOnlySchema(isAppendOnlySchema)
+ .setImmutableStorageScheme(immutableStorageScheme == null ?
+ ImmutableStorageScheme.ONE_CELL_PER_COLUMN :
immutableStorageScheme)
+ .setQualifierEncodingScheme(encodingScheme == null ?
+ QualifierEncodingScheme.NON_ENCODED_QUALIFIERS :
encodingScheme)
+ .setBaseColumnCount(baseTableColumnCount)
+ .setEncodedCQCounter(cqCounterToBe)
+
.setUseStatsForParallelization(useStatsForParallelizationProp)
+ .setExcludedColumns(ImmutableList.of())
+ .setTenantId(tenantId)
+ .setSchemaName(newSchemaName)
+ .setTableName(PNameFactory.newName(tableName))
+ .setPkName(pkName == null ? null :
PNameFactory.newName(pkName))
+ .setDefaultFamilyName(defaultFamilyName == null ?
+ null : PNameFactory.newName(defaultFamilyName))
+ .setRowKeyOrderOptimizable(rowKeyOrderOptimizable)
+ .setBucketNum(saltBucketNum)
+ .setIndexes(Collections.emptyList())
+ .setParentSchemaName((parent == null) ? null :
parent.getSchemaName())
+ .setParentTableName((parent == null) ? null :
parent.getTableName())
+ .setPhysicalNames(physicalNames == null ?
+ ImmutableList.of() :
ImmutableList.copyOf(physicalNames))
+ .setColumns(columns.values())
+ .setViewTTL(viewTTL == null ? VIEW_TTL_NOT_DEFINED :
viewTTL)
+ .setViewTTLHighWaterMark(viewTTLHighWaterMark == null ?
MIN_VIEW_TTL_HWM :
+ viewTTLHighWaterMark)
+ .setViewModifiedUpdateCacheFrequency(tableType ==
PTableType.VIEW &&
+ parent != null &&
+ parent.getUpdateCacheFrequency() !=
updateCacheFrequency)
+ .setViewModifiedUseStatsForParallelization(tableType ==
PTableType.VIEW &&
+ parent != null &&
+ parent.useStatsForParallelization()
+ != useStatsForParallelizationProp)
+ .setViewModifiedViewTTL(tableType == PTableType.VIEW &&
+ parent != null && viewTTL != null &&
+ parent.getViewTTL() != viewTTL)
+ .build();
+ result = new MetaDataMutationResult(code,
result.getMutationTime(), table, true);
+ addTableToCache(result);
+ return table;
+ } finally {
+ connection.setAutoCommit(wasAutoCommit);
+ deleteMutexCells(parentPhysicalSchemaName, parentPhysicalTableName,
+ acquiredColumnMutexSet);
+ }
+ }
+
+ /* This method handles mutation codes sent by phoenix server, except for
TABLE_NOT_FOUND which
+ * is considered to be a success code. If TABLE_ALREADY_EXISTS in hbase, we
don't need to add
+ * it in ConnectionQueryServices and we return result as true. However if
code is
+ * NEWER_TABLE_FOUND and it does not exists in statement then we return
false because we need to
+ * add it ConnectionQueryServices. For other mutation codes it throws
related SQLException.
+ * If server is throwing new mutation code which is not being handled by
client then it throws
+ * SQLException stating the server side Mutation code.
+ */
+ @VisibleForTesting
+ public boolean handleCreateTableMutationCode(MetaDataMutationResult
result, MutationCode code,
+ CreateTableStatement statement, String schemaName, String
tableName,
+ PTable parent) throws SQLException {
+ switch(code) {
case TABLE_ALREADY_EXISTS:
- if (result.getTable() != null) { // Can happen for
transactional table that already exists as HBase table
+ if(result.getTable() != null) {
addTableToCache(result);
}
- if (!statement.ifNotExists()) {
+ if(!statement.ifNotExists()) {
throw new TableAlreadyExistsException(schemaName,
tableName, result.getTable());
}
- return null;
+ return true;
case NEWER_TABLE_FOUND:
// Add table to ConnectionQueryServices so it's cached, but
don't add
// it to this connection as we can't see it.
if (!statement.ifNotExists()) {
- throw new NewerTableAlreadyExistsException(schemaName,
tableName, result.getTable());
+ throw new NewerTableAlreadyExistsException(schemaName,
tableName,
+ result.getTable());
}
+ return false;
case UNALLOWED_TABLE_MUTATION:
- throw new
SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
-
.setSchemaName(schemaName).setTableName(tableName).build().buildException();
+
throwsSQLExceptionUtil("CANNOT_MUTATE_TABLE",schemaName,tableName);
case CONCURRENT_TABLE_MUTATION:
addTableToCache(result);
throw new ConcurrentTableMutationException(schemaName,
tableName);
case AUTO_PARTITION_SEQUENCE_NOT_FOUND:
throw new
SQLExceptionInfo.Builder(SQLExceptionCode.AUTO_PARTITION_SEQUENCE_UNDEFINED)
-
.setSchemaName(schemaName).setTableName(tableName).build().buildException();
+
.setSchemaName(schemaName).setTableName(tableName).build().buildException();
case CANNOT_COERCE_AUTO_PARTITION_ID:
- throw new
SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_COERCE_AUTO_PARTITION_ID)
-
.setSchemaName(schemaName).setTableName(tableName).build().buildException();
+ case UNABLE_TO_CREATE_CHILD_LINK:
+ case PARENT_TABLE_NOT_FOUND:
+ case TABLE_NOT_IN_REGION:
+ throwsSQLExceptionUtil(String.valueOf(code), schemaName,
tableName);
case TOO_MANY_INDEXES:
- throw new
SQLExceptionInfo.Builder(SQLExceptionCode.TOO_MANY_INDEXES)
-
.setSchemaName(SchemaUtil.getSchemaNameFromFullName(parent.getPhysicalName().getString()))
-
.setTableName(SchemaUtil.getTableNameFromFullName(parent.getPhysicalName().getString())).build()
- .buildException();
case UNABLE_TO_UPDATE_PARENT_TABLE:
- throw new
SQLExceptionInfo.Builder(SQLExceptionCode.UNABLE_TO_UPDATE_PARENT_TABLE)
-
.setSchemaName(SchemaUtil.getSchemaNameFromFullName(parent.getPhysicalName().getString()))
-
.setTableName(SchemaUtil.getTableNameFromFullName(parent.getPhysicalName().getString())).build()
- .buildException();
+ throwsSQLExceptionUtil(String.valueOf(code),
SchemaUtil.getSchemaNameFromFullName(
+
parent.getPhysicalName().getString()),SchemaUtil.getTableNameFromFullName(
+ parent.getPhysicalName().getString()));
default:
- // If the parent table of the view has the auto partition
sequence name attribute,
- // set the view statement and relevant partition column
attributes correctly
- if (parent!=null && parent.getAutoPartitionSeqName()!=null) {
- final PColumn autoPartitionCol =
parent.getPKColumns().get(MetaDataUtil.getAutoPartitionColIndex(parent));
- final Long autoPartitionNum =
Long.valueOf(result.getAutoPartitionNum());
- columns.put(autoPartitionCol, new
DelegateColumn(autoPartitionCol) {
- @Override
- public byte[] getViewConstant() {
- PDataType dataType =
autoPartitionCol.getDataType();
- Object val = dataType.toObject(autoPartitionNum,
PLong.INSTANCE);
- byte[] bytes = new byte [dataType.getByteSize() +
1];
- dataType.toBytes(val, bytes, 0);
- return bytes;
- }
- @Override
- public boolean isViewReferenced() {
- return true;
- }
- });
- String viewPartitionClause =
QueryUtil.getViewPartitionClause(MetaDataUtil.getAutoPartitionColumnName(parent),
autoPartitionNum);
- if (viewStatement!=null) {
- viewStatement = viewStatement + " AND " +
viewPartitionClause;
- }
- else {
- viewStatement =
QueryUtil.getViewStatement(parent.getSchemaName().getString(),
parent.getTableName().getString(), viewPartitionClause);
- }
- }
- PName newSchemaName = PNameFactory.newName(schemaName);
- /*
- * It doesn't hurt for the PTable of views to have the
cqCounter. However, views always rely on the
- * parent table's counter to dole out encoded column
qualifiers. So setting the counter as NULL_COUNTER
- * for extra safety.
- */
- EncodedCQCounter cqCounterToBe = tableType == PTableType.VIEW
? NULL_COUNTER : cqCounter;
- PTable table = new PTableImpl.Builder()
- .setType(tableType)
- .setState(indexState)
- .setTimeStamp(timestamp != null ? timestamp :
result.getMutationTime())
- .setIndexDisableTimestamp(0L)
- .setSequenceNumber(PTable.INITIAL_SEQ_NUM)
- .setImmutableRows(isImmutableRows)
- .setViewStatement(viewStatement)
- .setDisableWAL(Boolean.TRUE.equals(disableWAL))
- .setMultiTenant(multiTenant)
- .setStoreNulls(storeNulls)
- .setViewType(viewType)
- .setViewIndexIdType(viewIndexIdType)
- .setViewIndexId(result.getViewIndexId())
- .setIndexType(indexType)
- .setTransactionProvider(transactionProvider)
- .setUpdateCacheFrequency(updateCacheFrequency)
- .setNamespaceMapped(isNamespaceMapped)
- .setAutoPartitionSeqName(autoPartitionSeq)
- .setAppendOnlySchema(isAppendOnlySchema)
- .setImmutableStorageScheme(immutableStorageScheme ==
null ?
- ImmutableStorageScheme.ONE_CELL_PER_COLUMN :
immutableStorageScheme)
- .setQualifierEncodingScheme(encodingScheme == null ?
- QualifierEncodingScheme.NON_ENCODED_QUALIFIERS
: encodingScheme)
- .setBaseColumnCount(baseTableColumnCount)
- .setEncodedCQCounter(cqCounterToBe)
-
.setUseStatsForParallelization(useStatsForParallelizationProp)
- .setExcludedColumns(ImmutableList.of())
- .setTenantId(tenantId)
- .setSchemaName(newSchemaName)
- .setTableName(PNameFactory.newName(tableName))
- .setPkName(pkName == null ? null :
PNameFactory.newName(pkName))
- .setDefaultFamilyName(defaultFamilyName == null ?
- null : PNameFactory.newName(defaultFamilyName))
- .setRowKeyOrderOptimizable(rowKeyOrderOptimizable)
- .setBucketNum(saltBucketNum)
- .setIndexes(Collections.emptyList())
- .setParentSchemaName((parent == null) ? null :
parent.getSchemaName())
- .setParentTableName((parent == null) ? null :
parent.getTableName())
- .setPhysicalNames(physicalNames == null ?
- ImmutableList.of() :
ImmutableList.copyOf(physicalNames))
- .setColumns(columns.values())
- .setViewTTL(viewTTL == null ? VIEW_TTL_NOT_DEFINED :
viewTTL)
- .setViewTTLHighWaterMark(viewTTLHighWaterMark == null
? MIN_VIEW_TTL_HWM : viewTTLHighWaterMark)
- .setViewModifiedUpdateCacheFrequency(tableType ==
PTableType.VIEW &&
- parent != null &&
- parent.getUpdateCacheFrequency() !=
updateCacheFrequency)
- .setViewModifiedUseStatsForParallelization(tableType
== PTableType.VIEW &&
- parent != null &&
- parent.useStatsForParallelization()
- != useStatsForParallelizationProp)
- .setViewModifiedViewTTL(tableType == PTableType.VIEW
&&
- parent != null && viewTTL != null &&
- parent.getViewTTL() != viewTTL)
- .build();
- result = new MetaDataMutationResult(code,
result.getMutationTime(), table, true);
- addTableToCache(result);
- return table;
- }
- } finally {
- connection.setAutoCommit(wasAutoCommit);
- deleteMutexCells(parentPhysicalSchemaName,
parentPhysicalTableName, acquiredColumnMutexSet);
+ // Cannot use SQLExecptionInfo here since not all mutation
codes have their
+ // corresponding codes in the enum SQLExceptionCode
+ throw new
SQLExceptionInfo.Builder(SQLExceptionCode.UNEXPECTED_MUTATION_CODE)
Review comment:
Would be good to tighten the test and assert on this specific exception
being thrown.
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services