in phoenix-core-client/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java [1697:1935]
private TableDescriptor ensureTableCreated(byte[] physicalTableName, byte[] parentPhysicalTableName, PTableType tableType, Map<String, Object> props,
List<Pair<byte[], Map<String, Object>>> families, byte[][] splits, boolean modifyExistingMetaData,
boolean isNamespaceMapped, boolean isDoNotUpgradePropSet) throws SQLException {
SQLException sqlE = null;
TableDescriptor existingDesc = null;
boolean isMetaTable = SchemaUtil.isMetaTable(physicalTableName);
boolean tableExist = true;
try (Admin admin = getAdmin()) {
final String quorum = ZKConfig.getZKQuorumServersString(config);
final String znode = this.getProps().get(HConstants.ZOOKEEPER_ZNODE_PARENT);
boolean createdNamespace = false;
LOGGER.debug("Found quorum: " + quorum + ":" + znode);
if (isMetaTable) {
if (SchemaUtil.isNamespaceMappingEnabled(PTableType.SYSTEM, this.getProps())) {
try {
// SYSTEM namespace needs to be created via HBase APIs because "CREATE SCHEMA" statement tries to write
// its metadata in SYSTEM:CATALOG table. Without SYSTEM namespace, SYSTEM:CATALOG table cannot be created
createdNamespace = ensureNamespaceCreated(QueryConstants.SYSTEM_SCHEMA_NAME);
} catch (PhoenixIOException e) {
// We could either:
// 1) Not access the NS descriptor. The NS may or may not exist at this point
// 2) We could not create the NS
// Regardless of the case 1 or 2, if we eventually try to migrate SYSTEM tables to the SYSTEM
// namespace using the {@link ensureSystemTablesMigratedToSystemNamespace(ReadOnlyProps)} method,
// if the NS does not exist, we will error as expected, or
// if the NS does exist and tables are already mapped, the check will exit gracefully
}
if (AdminUtilWithFallback.tableExists(admin,
SchemaUtil.getPhysicalTableName(SYSTEM_CATALOG_NAME_BYTES, false))) {
// SYSTEM.CATALOG exists, so at this point, we have 3 cases:
// 1) If server-side namespace mapping is disabled, drop the SYSTEM namespace if it was created
// above and throw Inconsistent namespace mapping exception
// 2) If server-side namespace mapping is enabled and SYSTEM.CATALOG needs to be upgraded,
// upgrade SYSTEM.CATALOG and also migrate SYSTEM tables to the SYSTEM namespace
// 3. If server-side namespace mapping is enabled and SYSTEM.CATALOG doesn't need to be
// upgraded, we still need to migrate SYSTEM tables to the SYSTEM namespace using the
// {@link ensureSystemTablesMigratedToSystemNamespace(ReadOnlyProps)} method (as part of
// {@link upgradeSystemTables(String, Properties)})
try {
checkClientServerCompatibility(SYSTEM_CATALOG_NAME_BYTES);
} catch (SQLException possibleCompatException) {
// Handles Case 1: Drop the SYSTEM namespace in case it was created above
if (createdNamespace && possibleCompatException.getErrorCode() ==
SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode()) {
ensureNamespaceDropped(QueryConstants.SYSTEM_SCHEMA_NAME);
}
// rethrow the SQLException
throw possibleCompatException;
}
// Thrown so we can force an upgrade which will just migrate SYSTEM tables to the SYSTEM namespace
throw new UpgradeRequiredException(MIN_SYSTEM_TABLE_TIMESTAMP);
}
} else if (AdminUtilWithFallback.tableExists(admin,
SchemaUtil.getPhysicalTableName(SYSTEM_CATALOG_NAME_BYTES, true))) {
// If SYSTEM:CATALOG exists, but client-side namespace mapping for SYSTEM tables is disabled, throw an exception
throw new SQLExceptionInfo.Builder(
SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES)
.setMessage("Cannot initiate connection as "
+ SchemaUtil.getPhysicalTableName(SYSTEM_CATALOG_NAME_BYTES, true)
+ " is found but client does not have "
+ IS_NAMESPACE_MAPPING_ENABLED + " enabled")
.build().buildException();
}
// If DoNotUpgrade config is set only check namespace mapping and
// Client-server compatibility for system tables.
if (isDoNotUpgradePropSet) {
try {
checkClientServerCompatibility(SchemaUtil.getPhysicalName(
SYSTEM_CATALOG_NAME_BYTES, this.getProps()).getName());
} catch (SQLException possibleCompatException) {
if (possibleCompatException.getCause()
instanceof org.apache.hadoop.hbase.TableNotFoundException) {
throw new UpgradeRequiredException(MIN_SYSTEM_TABLE_TIMESTAMP);
}
throw possibleCompatException;
}
return null;
}
}
try {
existingDesc = admin.getDescriptor(TableName.valueOf(physicalTableName));
} catch (org.apache.hadoop.hbase.TableNotFoundException e) {
tableExist = false;
if (tableType == PTableType.VIEW) {
String fullTableName = Bytes.toString(physicalTableName);
throw new ReadOnlyTableException(
"An HBase table for a VIEW must already exist",
SchemaUtil.getSchemaNameFromFullName(fullTableName),
SchemaUtil.getTableNameFromFullName(fullTableName));
}
}
TableDescriptorBuilder newDesc = generateTableDescriptor(physicalTableName, parentPhysicalTableName, existingDesc, tableType, props, families,
splits, isNamespaceMapped);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("ensureTableCreated " +
"physicalTableName = {}, " +
"parentPhysicalTableName = {}, " +
"isUpgradeRequired = {}, " +
"isAutoUpgradeEnabled = {}, " +
"isDoNotUpgradePropSet = {}, " +
"isNamespaceMapped = {}, " +
"createdNamespace = {}",
Bytes.toString(physicalTableName),
Bytes.toString(parentPhysicalTableName),
isUpgradeRequired(),
isAutoUpgradeEnabled,
isDoNotUpgradePropSet,
isNamespaceMapped,
createdNamespace);
}
if (!tableExist) {
if (SchemaUtil.isSystemTable(physicalTableName) && (tableType == PTableType.TABLE || tableType == PTableType.SYSTEM) && !isUpgradeRequired() && (!isAutoUpgradeEnabled || isDoNotUpgradePropSet)) {
// Disallow creating the SYSTEM.CATALOG or SYSTEM:CATALOG HBase table
throw new UpgradeRequiredException();
}
if (newDesc.build().getValue(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_BYTES) != null && Boolean.TRUE.equals(
PBoolean.INSTANCE.toObject(newDesc.build().getValue(MetaDataUtil.IS_LOCAL_INDEX_TABLE_PROP_BYTES)))) {
newDesc.setRegionSplitPolicyClassName(QueryConstants.INDEX_REGION_SPLIT_POLICY_CLASSNAME);
}
try {
if (splits == null) {
admin.createTable(newDesc.build());
} else {
admin.createTable(newDesc.build(), splits);
}
} catch (TableExistsException e) {
// We can ignore this, as it just means that another client beat us
// to creating the HBase metadata.
if (isMetaTable && !isUpgradeRequired()) {
checkClientServerCompatibility(SchemaUtil.getPhysicalName(SYSTEM_CATALOG_NAME_BYTES, this.getProps()).getName());
}
return null;
}
if (isMetaTable && !isUpgradeRequired()) {
try {
checkClientServerCompatibility(SchemaUtil.getPhysicalName(SYSTEM_CATALOG_NAME_BYTES,
this.getProps()).getName());
} catch (SQLException possibleCompatException) {
if (possibleCompatException.getErrorCode() ==
SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode()) {
try {
// In case we wrongly created SYSTEM.CATALOG or SYSTEM:CATALOG, we should drop it
disableTable(admin, TableName.valueOf(physicalTableName));
admin.deleteTable(TableName.valueOf(physicalTableName));
} catch (org.apache.hadoop.hbase.TableNotFoundException ignored) {
// Ignore this since it just means that another client with a similar set of
// incompatible configs and conditions beat us to dropping the SYSCAT HBase table
}
if (createdNamespace &&
SchemaUtil.isNamespaceMappingEnabled(PTableType.SYSTEM, this.getProps())) {
// We should drop the SYSTEM namespace which we just created, since
// server-side namespace mapping is disabled
ensureNamespaceDropped(QueryConstants.SYSTEM_SCHEMA_NAME);
}
}
// rethrow the SQLException
throw possibleCompatException;
}
}
return null;
} else {
if (isMetaTable && !isUpgradeRequired()) {
checkClientServerCompatibility(SchemaUtil.getPhysicalName(SYSTEM_CATALOG_NAME_BYTES, this.getProps()).getName());
} else {
for (Pair<byte[],Map<String,Object>> family: families) {
if ((Bytes.toString(family.getFirst())
.startsWith(QueryConstants.LOCAL_INDEX_COLUMN_FAMILY_PREFIX))) {
newDesc.setRegionSplitPolicyClassName(QueryConstants.INDEX_REGION_SPLIT_POLICY_CLASSNAME);
break;
}
}
}
if (!modifyExistingMetaData) {
return existingDesc; // Caller already knows that no metadata was changed
}
TransactionFactory.Provider provider = getTransactionProvider(props);
boolean willBeTx = provider != null;
// If mapping an existing table as transactional, set property so that existing
// data is correctly read.
if (willBeTx) {
if (!equalTxCoprocessor(provider, existingDesc, newDesc.build())) {
// Cannot switch between different providers
if (hasTxCoprocessor(existingDesc)) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_SWITCH_TXN_PROVIDERS)
.setSchemaName(SchemaUtil.getSchemaNameFromFullName(physicalTableName))
.setTableName(SchemaUtil.getTableNameFromFullName(physicalTableName)).build().buildException();
}
if (provider.getTransactionProvider().isUnsupported(PhoenixTransactionProvider.Feature.ALTER_NONTX_TO_TX)) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ALTER_TABLE_FROM_NON_TXN_TO_TXNL)
.setMessage(provider.name())
.setSchemaName(SchemaUtil.getSchemaNameFromFullName(physicalTableName))
.setTableName(SchemaUtil.getTableNameFromFullName(physicalTableName)).build().buildException();
}
newDesc.setValue(PhoenixTransactionContext.READ_NON_TX_DATA, Boolean.TRUE.toString());
}
} else {
// If we think we're creating a non transactional table when it's already
// transactional, don't allow.
if (hasTxCoprocessor(existingDesc)) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.TX_MAY_NOT_SWITCH_TO_NON_TX)
.setSchemaName(SchemaUtil.getSchemaNameFromFullName(physicalTableName))
.setTableName(SchemaUtil.getTableNameFromFullName(physicalTableName)).build().buildException();
}
newDesc.removeValue(Bytes.toBytes(PhoenixTransactionContext.READ_NON_TX_DATA));
}
TableDescriptor result = newDesc.build();
if (existingDesc.equals(result)) {
return null; // Indicate that no metadata was changed
}
// Do not call modifyTable for SYSTEM tables
if (tableType != PTableType.SYSTEM) {
modifyTable(physicalTableName, newDesc.build(), true);
}
return result;
}
} catch (IOException e) {
sqlE = ClientUtil.parseServerException(e);
} catch (InterruptedException e) {
// restore the interrupt status
Thread.currentThread().interrupt();
sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.INTERRUPTED_EXCEPTION).setRootCause(e).build().buildException();
} catch (TimeoutException e) {
sqlE = new SQLExceptionInfo.Builder(SQLExceptionCode.OPERATION_TIMED_OUT).setRootCause(e.getCause() != null ? e.getCause() : e).build().buildException();
} finally {
if (sqlE != null) {
throw sqlE;
}
}
return null; // will never make it here
}