private TableDescriptor ensureTableCreated()

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
    }