Search in sources :

Example 1 with ColumnDef

use of org.apache.phoenix.parse.ColumnDef in project phoenix by apache.

the class MetaDataClient method createTableInternal.

private PTable createTableInternal(CreateTableStatement statement, byte[][] splits, final PTable parent, String viewStatement, ViewType viewType, final byte[][] viewColumnConstants, final BitSet isViewColumnReferenced, boolean allocateIndexId, IndexType indexType, Date asyncCreatedDate, Map<String, Object> tableProps, Map<String, Object> commonFamilyProps) throws SQLException {
    final PTableType tableType = statement.getTableType();
    boolean wasAutoCommit = connection.getAutoCommit();
    connection.rollback();
    try {
        connection.setAutoCommit(false);
        List<Mutation> tableMetaData = Lists.newArrayListWithExpectedSize(statement.getColumnDefs().size() + 3);
        TableName tableNameNode = statement.getTableName();
        final String schemaName = connection.getSchema() != null && tableNameNode.getSchemaName() == null ? connection.getSchema() : tableNameNode.getSchemaName();
        final String tableName = tableNameNode.getTableName();
        String parentTableName = null;
        PName tenantId = connection.getTenantId();
        String tenantIdStr = tenantId == null ? null : tenantId.getString();
        Long scn = connection.getSCN();
        long clientTimeStamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn;
        boolean multiTenant = false;
        boolean storeNulls = false;
        boolean transactional = (parent != null) ? parent.isTransactional() : false;
        Integer saltBucketNum = null;
        String defaultFamilyName = null;
        boolean isImmutableRows = false;
        boolean isAppendOnlySchema = false;
        List<PName> physicalNames = Collections.emptyList();
        boolean addSaltColumn = false;
        boolean rowKeyOrderOptimizable = true;
        Long timestamp = null;
        boolean isNamespaceMapped = parent == null ? SchemaUtil.isNamespaceMappingEnabled(tableType, connection.getQueryServices().getProps()) : parent.isNamespaceMapped();
        boolean isLocalIndex = indexType == IndexType.LOCAL;
        QualifierEncodingScheme encodingScheme = NON_ENCODED_QUALIFIERS;
        ImmutableStorageScheme immutableStorageScheme = ONE_CELL_PER_COLUMN;
        if (parent != null && tableType == PTableType.INDEX) {
            timestamp = TransactionUtil.getTableTimestamp(connection, transactional);
            storeNulls = parent.getStoreNulls();
            isImmutableRows = parent.isImmutableRows();
            isAppendOnlySchema = parent.isAppendOnlySchema();
            // from the table to the index, though.
            if (isLocalIndex || (parent.getType() == PTableType.VIEW && parent.getViewType() != ViewType.MAPPED)) {
                PName physicalName = parent.getPhysicalName();
                saltBucketNum = parent.getBucketNum();
                addSaltColumn = (saltBucketNum != null && !isLocalIndex);
                defaultFamilyName = parent.getDefaultFamilyName() == null ? null : parent.getDefaultFamilyName().getString();
                if (isLocalIndex) {
                    defaultFamilyName = parent.getDefaultFamilyName() == null ? QueryConstants.DEFAULT_LOCAL_INDEX_COLUMN_FAMILY : IndexUtil.getLocalIndexColumnFamily(parent.getDefaultFamilyName().getString());
                    saltBucketNum = null;
                    // Set physical name of local index table
                    physicalNames = Collections.singletonList(PNameFactory.newName(physicalName.getBytes()));
                } else {
                    defaultFamilyName = parent.getDefaultFamilyName() == null ? QueryConstants.DEFAULT_COLUMN_FAMILY : parent.getDefaultFamilyName().getString();
                    // Set physical name of view index table
                    physicalNames = Collections.singletonList(PNameFactory.newName(MetaDataUtil.getViewIndexPhysicalName(physicalName.getBytes())));
                }
            }
            multiTenant = parent.isMultiTenant();
            storeNulls = parent.getStoreNulls();
            parentTableName = parent.getTableName().getString();
            // Pass through data table sequence number so we can check it hasn't changed
            PreparedStatement incrementStatement = connection.prepareStatement(INCREMENT_SEQ_NUM);
            incrementStatement.setString(1, tenantIdStr);
            incrementStatement.setString(2, schemaName);
            incrementStatement.setString(3, parentTableName);
            incrementStatement.setLong(4, parent.getSequenceNumber());
            incrementStatement.execute();
            // Get list of mutations and add to table meta data that will be passed to server
            // to guarantee order. This row will always end up last
            tableMetaData.addAll(connection.getMutationState().toMutations(timestamp).next().getSecond());
            connection.rollback();
            // Add row linking from data table row to index table row
            PreparedStatement linkStatement = connection.prepareStatement(CREATE_LINK);
            linkStatement.setString(1, tenantIdStr);
            linkStatement.setString(2, schemaName);
            linkStatement.setString(3, parentTableName);
            linkStatement.setString(4, tableName);
            linkStatement.setByte(5, LinkType.INDEX_TABLE.getSerializedValue());
            linkStatement.setLong(6, parent.getSequenceNumber());
            linkStatement.setString(7, PTableType.INDEX.getSerializedValue());
            linkStatement.execute();
        }
        PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint();
        String pkName = null;
        List<Pair<ColumnName, SortOrder>> pkColumnsNames = Collections.<Pair<ColumnName, SortOrder>>emptyList();
        Iterator<Pair<ColumnName, SortOrder>> pkColumnsIterator = Iterators.emptyIterator();
        if (pkConstraint != null) {
            pkColumnsNames = pkConstraint.getColumnNames();
            pkColumnsIterator = pkColumnsNames.iterator();
            pkName = pkConstraint.getName();
        }
        // This tells Phoenix that you're managing the index maintenance yourself.
        if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW || viewType == ViewType.MAPPED)) {
            // TODO remove TableProperty.IMMUTABLE_ROWS at the next major release
            Boolean isImmutableRowsProp = statement.immutableRows() != null ? statement.immutableRows() : (Boolean) TableProperty.IMMUTABLE_ROWS.getValue(tableProps);
            if (isImmutableRowsProp == null) {
                isImmutableRows = connection.getQueryServices().getProps().getBoolean(QueryServices.IMMUTABLE_ROWS_ATTRIB, QueryServicesOptions.DEFAULT_IMMUTABLE_ROWS);
            } else {
                isImmutableRows = isImmutableRowsProp;
            }
        }
        if (tableType == PTableType.TABLE) {
            Boolean isAppendOnlySchemaProp = (Boolean) TableProperty.APPEND_ONLY_SCHEMA.getValue(tableProps);
            isAppendOnlySchema = isAppendOnlySchemaProp != null ? isAppendOnlySchemaProp : false;
        }
        // Can't set any of these on views or shared indexes on views
        if (tableType != PTableType.VIEW && !allocateIndexId) {
            saltBucketNum = (Integer) TableProperty.SALT_BUCKETS.getValue(tableProps);
            if (saltBucketNum != null) {
                if (saltBucketNum < 0 || saltBucketNum > SaltingUtil.MAX_BUCKET_NUM) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_BUCKET_NUM).build().buildException();
                }
            }
            // Salt the index table if the data table is salted
            if (saltBucketNum == null) {
                if (parent != null) {
                    saltBucketNum = parent.getBucketNum();
                }
            } else if (saltBucketNum.intValue() == 0) {
                // Provides a way for an index to not be salted if its data table is salted
                saltBucketNum = null;
            }
            addSaltColumn = (saltBucketNum != null);
        }
        // Can't set MULTI_TENANT or DEFAULT_COLUMN_FAMILY_NAME on an INDEX or a non mapped VIEW
        if (tableType != PTableType.INDEX && (tableType != PTableType.VIEW || viewType == ViewType.MAPPED)) {
            Boolean multiTenantProp = (Boolean) tableProps.get(PhoenixDatabaseMetaData.MULTI_TENANT);
            multiTenant = Boolean.TRUE.equals(multiTenantProp);
            defaultFamilyName = (String) TableProperty.DEFAULT_COLUMN_FAMILY.getValue(tableProps);
        }
        boolean disableWAL = false;
        Boolean disableWALProp = (Boolean) TableProperty.DISABLE_WAL.getValue(tableProps);
        if (disableWALProp != null) {
            disableWAL = disableWALProp;
        }
        long updateCacheFrequency = connection.getQueryServices().getProps().getLong(QueryServices.DEFAULT_UPDATE_CACHE_FREQUENCY_ATRRIB, QueryServicesOptions.DEFAULT_UPDATE_CACHE_FREQUENCY);
        Long updateCacheFrequencyProp = (Long) TableProperty.UPDATE_CACHE_FREQUENCY.getValue(tableProps);
        if (updateCacheFrequencyProp != null) {
            updateCacheFrequency = updateCacheFrequencyProp;
        }
        String autoPartitionSeq = (String) TableProperty.AUTO_PARTITION_SEQ.getValue(tableProps);
        Long guidePostsWidth = (Long) TableProperty.GUIDE_POSTS_WIDTH.getValue(tableProps);
        Boolean storeNullsProp = (Boolean) TableProperty.STORE_NULLS.getValue(tableProps);
        if (storeNullsProp == null) {
            if (parent == null) {
                storeNulls = connection.getQueryServices().getProps().getBoolean(QueryServices.DEFAULT_STORE_NULLS_ATTRIB, QueryServicesOptions.DEFAULT_STORE_NULLS);
                tableProps.put(PhoenixDatabaseMetaData.STORE_NULLS, Boolean.valueOf(storeNulls));
            }
        } else {
            storeNulls = storeNullsProp;
        }
        Boolean transactionalProp = (Boolean) TableProperty.TRANSACTIONAL.getValue(tableProps);
        if (transactionalProp != null && parent != null) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.ONLY_TABLE_MAY_BE_DECLARED_TRANSACTIONAL).setSchemaName(schemaName).setTableName(tableName).build().buildException();
        }
        if (parent == null) {
            if (transactionalProp == null) {
                transactional = connection.getQueryServices().getProps().getBoolean(QueryServices.DEFAULT_TABLE_ISTRANSACTIONAL_ATTRIB, QueryServicesOptions.DEFAULT_TABLE_ISTRANSACTIONAL);
            } else {
                transactional = transactionalProp;
            }
        }
        boolean transactionsEnabled = connection.getQueryServices().getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED);
        // can't create a transactional table if transactions are not enabled
        if (!transactionsEnabled && transactional) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_CREATE_TXN_TABLE_IF_TXNS_DISABLED).setSchemaName(schemaName).setTableName(tableName).build().buildException();
        }
        // can't create a transactional table if it has a row timestamp column
        if (pkConstraint.getNumColumnsWithRowTimestamp() > 0 && transactional) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_CREATE_TXN_TABLE_WITH_ROW_TIMESTAMP).setSchemaName(schemaName).setTableName(tableName).build().buildException();
        }
        // Put potentially inferred value into tableProps as it's used by the createTable call below
        // to determine which coprocessors to install on the new table.
        tableProps.put(PhoenixDatabaseMetaData.TRANSACTIONAL, transactional);
        if (transactional) {
            // If TTL set, use Tephra TTL property name instead
            Object ttl = commonFamilyProps.remove(HColumnDescriptor.TTL);
            if (ttl != null) {
                commonFamilyProps.put(TxConstants.PROPERTY_TTL, ttl);
            }
        }
        boolean useStatsForParallelization = true;
        Boolean useStatsForParallelizationProp = (Boolean) TableProperty.USE_STATS_FOR_PARALLELIZATION.getValue(tableProps);
        if (useStatsForParallelizationProp != null) {
            useStatsForParallelization = useStatsForParallelizationProp;
        } else {
            useStatsForParallelization = connection.getQueryServices().getProps().getBoolean(QueryServices.USE_STATS_FOR_PARALLELIZATION, QueryServicesOptions.DEFAULT_USE_STATS_FOR_PARALLELIZATION);
        }
        boolean sharedTable = statement.getTableType() == PTableType.VIEW || allocateIndexId;
        if (transactional) {
            // maintenance code being able to see the prior state to update the rows correctly.
            if (Boolean.FALSE.equals(storeNullsProp)) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.STORE_NULLS_MUST_BE_TRUE_FOR_TRANSACTIONAL).setSchemaName(schemaName).setTableName(tableName).build().buildException();
            }
            // Force STORE_NULLS to true when transactional as Tephra cannot deal with column deletes
            storeNulls = true;
            tableProps.put(PhoenixDatabaseMetaData.STORE_NULLS, Boolean.TRUE);
            if (!sharedTable) {
                Integer maxVersionsProp = (Integer) commonFamilyProps.get(HConstants.VERSIONS);
                if (maxVersionsProp == null) {
                    if (parent != null) {
                        HTableDescriptor desc = connection.getQueryServices().getTableDescriptor(parent.getPhysicalName().getBytes());
                        if (desc != null) {
                            maxVersionsProp = desc.getFamily(SchemaUtil.getEmptyColumnFamily(parent)).getMaxVersions();
                        }
                    }
                    if (maxVersionsProp == null) {
                        maxVersionsProp = connection.getQueryServices().getProps().getInt(QueryServices.MAX_VERSIONS_TRANSACTIONAL_ATTRIB, QueryServicesOptions.DEFAULT_MAX_VERSIONS_TRANSACTIONAL);
                    }
                    commonFamilyProps.put(HConstants.VERSIONS, maxVersionsProp);
                }
            }
        }
        timestamp = timestamp == null ? TransactionUtil.getTableTimestamp(connection, transactional) : timestamp;
        // Delay this check as it is supported to have IMMUTABLE_ROWS and SALT_BUCKETS defined on views
        if (sharedTable) {
            if (tableProps.get(PhoenixDatabaseMetaData.DEFAULT_COLUMN_FAMILY_NAME) != null) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.DEFAULT_COLUMN_FAMILY_ON_SHARED_TABLE).setSchemaName(schemaName).setTableName(tableName).build().buildException();
            }
            if (SchemaUtil.hasHTableDescriptorProps(tableProps)) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES).build().buildException();
            }
        }
        List<ColumnDef> colDefs = statement.getColumnDefs();
        LinkedHashMap<PColumn, PColumn> columns;
        LinkedHashSet<PColumn> pkColumns;
        if (tenantId != null && !sharedTable) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_CREATE_TENANT_SPECIFIC_TABLE).setSchemaName(schemaName).setTableName(tableName).build().buildException();
        }
        if (autoPartitionSeq != null) {
            int autoPartitionColIndex = multiTenant ? 1 : 0;
            PDataType dataType = colDefs.get(autoPartitionColIndex).getDataType();
            if (!PLong.INSTANCE.isCastableTo(dataType)) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.SEQUENCE_NOT_CASTABLE_TO_AUTO_PARTITION_ID_COLUMN).setSchemaName(schemaName).setTableName(tableName).build().buildException();
            }
        }
        if (tableType == PTableType.VIEW) {
            physicalNames = Collections.singletonList(PNameFactory.newName(parent.getPhysicalName().getString()));
            if (viewType == ViewType.MAPPED) {
                columns = Maps.newLinkedHashMap();
                pkColumns = newLinkedHashSetWithExpectedSize(colDefs.size());
            } else {
                // Propagate property values to VIEW.
                // TODO: formalize the known set of these properties
                // Manually transfer the ROW_KEY_ORDER_OPTIMIZABLE_BYTES from parent as we don't
                // want to add this hacky flag to the schema (see PHOENIX-2067).
                rowKeyOrderOptimizable = parent.rowKeyOrderOptimizable();
                if (rowKeyOrderOptimizable) {
                    UpgradeUtil.addRowKeyOrderOptimizableCell(tableMetaData, SchemaUtil.getTableKey(tenantIdStr, schemaName, tableName), clientTimeStamp);
                }
                multiTenant = parent.isMultiTenant();
                saltBucketNum = parent.getBucketNum();
                isAppendOnlySchema = parent.isAppendOnlySchema();
                isImmutableRows = parent.isImmutableRows();
                if (updateCacheFrequencyProp == null) {
                    // set to the parent value if the property is not set on the view
                    updateCacheFrequency = parent.getUpdateCacheFrequency();
                }
                disableWAL = (disableWALProp == null ? parent.isWALDisabled() : disableWALProp);
                defaultFamilyName = parent.getDefaultFamilyName() == null ? null : parent.getDefaultFamilyName().getString();
                List<PColumn> allColumns = parent.getColumns();
                if (saltBucketNum != null) {
                    // Don't include salt column in columns, as it should not have it when created
                    allColumns = allColumns.subList(1, allColumns.size());
                }
                columns = new LinkedHashMap<PColumn, PColumn>(allColumns.size() + colDefs.size());
                for (PColumn column : allColumns) {
                    columns.put(column, column);
                }
                pkColumns = newLinkedHashSet(parent.getPKColumns());
                // Add row linking view to its parent 
                PreparedStatement linkStatement = connection.prepareStatement(CREATE_VIEW_LINK);
                linkStatement.setString(1, tenantIdStr);
                linkStatement.setString(2, schemaName);
                linkStatement.setString(3, tableName);
                linkStatement.setString(4, parent.getName().getString());
                linkStatement.setByte(5, LinkType.PARENT_TABLE.getSerializedValue());
                linkStatement.setString(6, parent.getTenantId() == null ? null : parent.getTenantId().getString());
                linkStatement.execute();
                // Add row linking parent to view
                linkStatement = connection.prepareStatement(CREATE_CHILD_LINK);
                linkStatement.setString(1, parent.getTenantId() == null ? null : parent.getTenantId().getString());
                linkStatement.setString(2, parent.getSchemaName() == null ? null : parent.getSchemaName().getString());
                linkStatement.setString(3, parent.getTableName().getString());
                linkStatement.setString(4, tenantIdStr);
                linkStatement.setString(5, SchemaUtil.getTableName(schemaName, tableName));
                linkStatement.setByte(6, LinkType.CHILD_TABLE.getSerializedValue());
                linkStatement.execute();
            }
        } else {
            columns = new LinkedHashMap<PColumn, PColumn>(colDefs.size());
            // in case salted
            pkColumns = newLinkedHashSetWithExpectedSize(colDefs.size() + 1);
        }
        // fail because it looks like there's always a view associated with it.
        if (!physicalNames.isEmpty()) {
            // Otherwise, we end up with a self-referencing link and then cannot ever drop the view.
            if (viewType != ViewType.MAPPED || !physicalNames.get(0).getString().equals(SchemaUtil.getTableName(schemaName, tableName))) {
                // Add row linking from data table row to physical table row
                PreparedStatement linkStatement = connection.prepareStatement(CREATE_LINK);
                for (PName physicalName : physicalNames) {
                    linkStatement.setString(1, tenantIdStr);
                    linkStatement.setString(2, schemaName);
                    linkStatement.setString(3, tableName);
                    linkStatement.setString(4, physicalName.getString());
                    linkStatement.setByte(5, LinkType.PHYSICAL_TABLE.getSerializedValue());
                    if (tableType == PTableType.VIEW) {
                        PTable physicalTable = connection.getTable(new PTableKey(null, physicalName.getString().replace(QueryConstants.NAMESPACE_SEPARATOR, QueryConstants.NAME_SEPARATOR)));
                        linkStatement.setLong(6, physicalTable.getSequenceNumber());
                        linkStatement.setString(7, null);
                    } else {
                        linkStatement.setLong(6, parent.getSequenceNumber());
                        linkStatement.setString(7, PTableType.INDEX.getSerializedValue());
                    }
                    linkStatement.execute();
                }
                tableMetaData.addAll(connection.getMutationState().toMutations(timestamp).next().getSecond());
                connection.rollback();
            }
        }
        Map<String, PName> familyNames = Maps.newLinkedHashMap();
        boolean isPK = false;
        boolean rowTimeStampColumnAlreadyFound = false;
        int positionOffset = columns.size();
        if (saltBucketNum != null) {
            positionOffset++;
            if (addSaltColumn) {
                pkColumns.add(SaltingUtil.SALTING_COLUMN);
            }
        }
        int pkPositionOffset = pkColumns.size();
        int position = positionOffset;
        EncodedCQCounter cqCounter = NULL_COUNTER;
        PTable viewPhysicalTable = null;
        if (tableType == PTableType.VIEW) {
            /*
                 * We can't control what column qualifiers are used in HTable mapped to Phoenix views. So we are not
                 * able to encode column names.
                 */
            if (viewType != MAPPED) {
                /*
                     * For regular phoenix views, use the storage scheme of the physical table since they all share the
                     * the same HTable. Views always use the base table's column qualifier counter for doling out
                     * encoded column qualifier.
                     */
                viewPhysicalTable = PhoenixRuntime.getTable(connection, physicalNames.get(0).getString());
                immutableStorageScheme = viewPhysicalTable.getImmutableStorageScheme();
                encodingScheme = viewPhysicalTable.getEncodingScheme();
                if (EncodedColumnsUtil.usesEncodedColumnNames(viewPhysicalTable)) {
                    cqCounter = viewPhysicalTable.getEncodedCQCounter();
                }
            }
        } else // System tables have hard-coded column qualifiers. So we can't use column encoding for them.
        if (!SchemaUtil.isSystemTable(Bytes.toBytes(SchemaUtil.getTableName(schemaName, tableName)))) {
            /*
                 * Indexes inherit the storage scheme of the parent data tables. Otherwise, we always attempt to 
                 * create tables with encoded column names. 
                 * 
                 * Also of note is the case with shared indexes i.e. local indexes and view indexes. In these cases, 
                 * column qualifiers for covered columns don't have to be unique because rows of the logical indexes are 
                 * partitioned by the virtue of indexId present in the row key. As such, different shared indexes can use
                 * potentially overlapping column qualifiers.
                 * 
                 * If the hbase table already exists, then possibly encoded or non-encoded column qualifiers were used. 
                 * In this case we pursue ahead with non-encoded column qualifier scheme. If the phoenix metadata for this table already exists 
                 * then we rely on the PTable, with appropriate storage scheme, returned in the MetadataMutationResult to be updated 
                 * in the client cache. If the phoenix table metadata already doesn't exist then the non-encoded column qualifier scheme works
                 * because we cannot control the column qualifiers that were used when populating the hbase table.
                 */
            byte[] tableNameBytes = SchemaUtil.getTableNameAsBytes(schemaName, tableName);
            boolean tableExists = true;
            try {
                HTableDescriptor tableDescriptor = connection.getQueryServices().getTableDescriptor(tableNameBytes);
                if (tableDescriptor == null) {
                    // for connectionless
                    tableExists = false;
                }
            } catch (org.apache.phoenix.schema.TableNotFoundException e) {
                tableExists = false;
            }
            if (tableExists) {
                encodingScheme = NON_ENCODED_QUALIFIERS;
                immutableStorageScheme = ONE_CELL_PER_COLUMN;
            } else if (parent != null) {
                encodingScheme = parent.getEncodingScheme();
                immutableStorageScheme = parent.getImmutableStorageScheme();
            } else {
                Byte encodingSchemeSerializedByte = (Byte) TableProperty.COLUMN_ENCODED_BYTES.getValue(tableProps);
                if (encodingSchemeSerializedByte == null) {
                    encodingSchemeSerializedByte = (byte) connection.getQueryServices().getProps().getInt(QueryServices.DEFAULT_COLUMN_ENCODED_BYTES_ATRRIB, QueryServicesOptions.DEFAULT_COLUMN_ENCODED_BYTES);
                }
                encodingScheme = QualifierEncodingScheme.fromSerializedValue(encodingSchemeSerializedByte);
                if (isImmutableRows) {
                    immutableStorageScheme = (ImmutableStorageScheme) TableProperty.IMMUTABLE_STORAGE_SCHEME.getValue(tableProps);
                    if (immutableStorageScheme == null) {
                        if (multiTenant) {
                            immutableStorageScheme = ImmutableStorageScheme.valueOf(connection.getQueryServices().getProps().get(QueryServices.DEFAULT_MULTITENANT_IMMUTABLE_STORAGE_SCHEME_ATTRIB, QueryServicesOptions.DEFAULT_MULTITENANT_IMMUTABLE_STORAGE_SCHEME));
                        } else {
                            immutableStorageScheme = ImmutableStorageScheme.valueOf(connection.getQueryServices().getProps().get(QueryServices.DEFAULT_IMMUTABLE_STORAGE_SCHEME_ATTRIB, QueryServicesOptions.DEFAULT_IMMUTABLE_STORAGE_SCHEME));
                        }
                    }
                    if (immutableStorageScheme != ONE_CELL_PER_COLUMN && encodingScheme == NON_ENCODED_QUALIFIERS) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_IMMUTABLE_STORAGE_SCHEME_AND_COLUMN_QUALIFIER_BYTES).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                }
            }
            cqCounter = encodingScheme != NON_ENCODED_QUALIFIERS ? new EncodedCQCounter() : NULL_COUNTER;
        }
        Map<String, Integer> changedCqCounters = new HashMap<>(colDefs.size());
        for (ColumnDef colDef : colDefs) {
            rowTimeStampColumnAlreadyFound = checkAndValidateRowTimestampCol(colDef, pkConstraint, rowTimeStampColumnAlreadyFound, tableType);
            if (colDef.isPK()) {
                // i.e. the column is declared as CREATE TABLE COLNAME DATATYPE PRIMARY KEY...
                if (isPK) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_ALREADY_EXISTS).setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException();
                }
                isPK = true;
            } else {
                // do not allow setting NOT-NULL constraint on non-primary columns.
                if (Boolean.FALSE.equals(colDef.isNull()) && (isPK || (pkConstraint != null && !pkConstraint.contains(colDef.getColumnDefName())))) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_NOT_NULL_CONSTRAINT).setSchemaName(schemaName).setTableName(tableName).setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException();
                }
            }
            ColumnName columnDefName = colDef.getColumnDefName();
            String colDefFamily = columnDefName.getFamilyName();
            boolean isPkColumn = isPkColumn(pkConstraint, colDef, columnDefName);
            String cqCounterFamily = null;
            if (!isPkColumn) {
                if (immutableStorageScheme == SINGLE_CELL_ARRAY_WITH_OFFSETS && encodingScheme != NON_ENCODED_QUALIFIERS) {
                    // For this scheme we track column qualifier counters at the column family level.
                    cqCounterFamily = colDefFamily != null ? colDefFamily : (defaultFamilyName != null ? defaultFamilyName : DEFAULT_COLUMN_FAMILY);
                } else {
                    // For other schemes, column qualifier counters are tracked using the default column family.
                    cqCounterFamily = defaultFamilyName != null ? defaultFamilyName : DEFAULT_COLUMN_FAMILY;
                }
            }
            Integer encodedCQ = isPkColumn ? null : cqCounter.getNextQualifier(cqCounterFamily);
            byte[] columnQualifierBytes = null;
            try {
                columnQualifierBytes = EncodedColumnsUtil.getColumnQualifierBytes(columnDefName.getColumnName(), encodedCQ, encodingScheme, isPkColumn);
            } catch (QualifierOutOfRangeException e) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.MAX_COLUMNS_EXCEEDED).setSchemaName(schemaName).setTableName(tableName).build().buildException();
            }
            PColumn column = newColumn(position++, colDef, pkConstraint, defaultFamilyName, false, columnQualifierBytes);
            if (cqCounter.increment(cqCounterFamily)) {
                changedCqCounters.put(cqCounterFamily, cqCounter.getNextQualifier(cqCounterFamily));
            }
            if (SchemaUtil.isPKColumn(column)) {
                // TODO: remove this constraint?
                if (pkColumnsIterator.hasNext() && !column.getName().getString().equals(pkColumnsIterator.next().getFirst().getColumnName())) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_OUT_OF_ORDER).setSchemaName(schemaName).setTableName(tableName).setColumnName(column.getName().getString()).build().buildException();
                }
                if (tableType == PTableType.VIEW && viewType != ViewType.MAPPED) {
                    throwIfLastPKOfParentIsFixedLength(parent, schemaName, tableName, colDef);
                }
                if (!pkColumns.add(column)) {
                    throw new ColumnAlreadyExistsException(schemaName, tableName, column.getName().getString());
                }
            }
            if (columns.put(column, column) != null) {
                throw new ColumnAlreadyExistsException(schemaName, tableName, column.getName().getString());
            }
            if ((colDef.getDataType() == PVarbinary.INSTANCE || colDef.getDataType().isArrayType()) && SchemaUtil.isPKColumn(column) && pkColumnsIterator.hasNext()) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.VARBINARY_IN_ROW_KEY).setSchemaName(schemaName).setTableName(tableName).setColumnName(column.getName().getString()).build().buildException();
            }
            if (column.getFamilyName() != null) {
                familyNames.put(IndexUtil.getActualColumnFamilyName(column.getFamilyName().getString()), column.getFamilyName());
            }
        }
        // We need a PK definition for a TABLE or mapped VIEW
        if (!isPK && pkColumnsNames.isEmpty() && tableType != PTableType.VIEW && viewType != ViewType.MAPPED) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_MISSING).setSchemaName(schemaName).setTableName(tableName).build().buildException();
        }
        if (!pkColumnsNames.isEmpty() && pkColumnsNames.size() != pkColumns.size() - pkPositionOffset) {
            // Then a column name in the primary key constraint wasn't resolved
            Iterator<Pair<ColumnName, SortOrder>> pkColumnNamesIterator = pkColumnsNames.iterator();
            while (pkColumnNamesIterator.hasNext()) {
                ColumnName colName = pkColumnNamesIterator.next().getFirst();
                ColumnDef colDef = findColumnDefOrNull(colDefs, colName);
                if (colDef == null) {
                    throw new ColumnNotFoundException(schemaName, tableName, null, colName.getColumnName());
                }
                if (colDef.getColumnDefName().getFamilyName() != null) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_WITH_FAMILY_NAME).setSchemaName(schemaName).setTableName(tableName).setColumnName(colDef.getColumnDefName().getColumnName()).setFamilyName(colDef.getColumnDefName().getFamilyName()).build().buildException();
                }
            }
            // The above should actually find the specific one, but just in case...
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_PRIMARY_KEY_CONSTRAINT).setSchemaName(schemaName).setTableName(tableName).build().buildException();
        }
        List<Pair<byte[], Map<String, Object>>> familyPropList = Lists.newArrayListWithExpectedSize(familyNames.size());
        if (!statement.getProps().isEmpty()) {
            for (String familyName : statement.getProps().keySet()) {
                if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
                    if (familyNames.get(familyName) == null) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.PROPERTIES_FOR_FAMILY).setFamilyName(familyName).build().buildException();
                    } else if (statement.getTableType() == PTableType.VIEW) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES).build().buildException();
                    }
                }
            }
        }
        throwIfInsufficientColumns(schemaName, tableName, pkColumns, saltBucketNum != null, multiTenant);
        for (PName familyName : familyNames.values()) {
            String fam = familyName.getString();
            Collection<Pair<String, Object>> props = statement.getProps().get(IndexUtil.getActualColumnFamilyName(fam));
            if (props.isEmpty()) {
                familyPropList.add(new Pair<byte[], Map<String, Object>>(familyName.getBytes(), commonFamilyProps));
            } else {
                Map<String, Object> combinedFamilyProps = Maps.newHashMapWithExpectedSize(props.size() + commonFamilyProps.size());
                combinedFamilyProps.putAll(commonFamilyProps);
                for (Pair<String, Object> prop : props) {
                    // i.e. it can't be column family specific.
                    if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY) && prop.getFirst().equals(TTL)) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL).build().buildException();
                    }
                    combinedFamilyProps.put(prop.getFirst(), prop.getSecond());
                }
                familyPropList.add(new Pair<byte[], Map<String, Object>>(familyName.getBytes(), combinedFamilyProps));
            }
        }
        if (familyNames.isEmpty()) {
            //if there are no family names, use the default column family name. This also takes care of the case when
            //the table ddl has only PK cols present (which means familyNames is empty).
            byte[] cf = defaultFamilyName == null ? (!isLocalIndex ? QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES : QueryConstants.DEFAULT_LOCAL_INDEX_COLUMN_FAMILY_BYTES) : Bytes.toBytes(defaultFamilyName);
            familyPropList.add(new Pair<byte[], Map<String, Object>>(cf, commonFamilyProps));
        }
        // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists
        if (SchemaUtil.isMetaTable(schemaName, tableName)) {
            // TODO: what about stats for system catalog?
            PName newSchemaName = PNameFactory.newName(schemaName);
            // Column names and qualifiers and hardcoded for system tables.
            PTable table = PTableImpl.makePTable(tenantId, newSchemaName, PNameFactory.newName(tableName), tableType, null, MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM, PNameFactory.newName(QueryConstants.SYSTEM_TABLE_PK_NAME), null, columns.values(), null, null, Collections.<PTable>emptyList(), isImmutableRows, Collections.<PName>emptyList(), defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), null, Boolean.TRUE.equals(disableWAL), false, false, null, null, indexType, true, false, 0, 0L, isNamespaceMapped, autoPartitionSeq, isAppendOnlySchema, ONE_CELL_PER_COLUMN, NON_ENCODED_QUALIFIERS, PTable.EncodedCQCounter.NULL_COUNTER, true);
            connection.addTable(table, MetaDataProtocol.MIN_TABLE_TIMESTAMP);
        }
        // Update column qualifier counters
        if (EncodedColumnsUtil.usesEncodedColumnNames(encodingScheme)) {
            // Store the encoded column counter for phoenix entities that have their own hbase
            // tables i.e. base tables and indexes.
            String schemaNameToUse = tableType == VIEW ? viewPhysicalTable.getSchemaName().getString() : schemaName;
            String tableNameToUse = tableType == VIEW ? viewPhysicalTable.getTableName().getString() : tableName;
            boolean sharedIndex = tableType == PTableType.INDEX && (indexType == IndexType.LOCAL || parent.getType() == PTableType.VIEW);
            // For local indexes and indexes on views, pass on the the tenant id since all their meta-data rows have
            // tenant ids in there.
            String tenantIdToUse = connection.getTenantId() != null && sharedIndex ? connection.getTenantId().getString() : null;
            // too since we want clients to get the latest PTable of the base table.
            for (Entry<String, Integer> entry : changedCqCounters.entrySet()) {
                try (PreparedStatement linkStatement = connection.prepareStatement(UPDATE_ENCODED_COLUMN_COUNTER)) {
                    linkStatement.setString(1, tenantIdToUse);
                    linkStatement.setString(2, schemaNameToUse);
                    linkStatement.setString(3, tableNameToUse);
                    linkStatement.setString(4, entry.getKey());
                    linkStatement.setInt(5, entry.getValue());
                    linkStatement.execute();
                }
            }
            if (tableType == VIEW && !changedCqCounters.isEmpty()) {
                PreparedStatement incrementStatement = connection.prepareStatement(INCREMENT_SEQ_NUM);
                incrementStatement.setString(1, null);
                incrementStatement.setString(2, viewPhysicalTable.getSchemaName().getString());
                incrementStatement.setString(3, viewPhysicalTable.getTableName().getString());
                incrementStatement.setLong(4, viewPhysicalTable.getSequenceNumber() + 1);
                incrementStatement.execute();
            }
            if (connection.getMutationState().toMutations(timestamp).hasNext()) {
                tableMetaData.addAll(connection.getMutationState().toMutations(timestamp).next().getSecond());
                connection.rollback();
            }
        }
        short nextKeySeq = 0;
        List<Mutation> columnMetadata = Lists.newArrayListWithExpectedSize(columns.size());
        try (PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN_CREATE_TABLE)) {
            for (Map.Entry<PColumn, PColumn> entry : columns.entrySet()) {
                PColumn column = entry.getValue();
                final int columnPosition = column.getPosition();
                // set the autoPartition column attributes
                if (parent != null && parent.getAutoPartitionSeqName() != null && parent.getPKColumns().get(MetaDataUtil.getAutoPartitionColIndex(parent)).equals(column)) {
                    entry.setValue(column = new DelegateColumn(column) {

                        @Override
                        public byte[] getViewConstant() {
                            // will be set correctly on the server
                            return QueryConstants.EMPTY_COLUMN_VALUE_BYTES;
                        }

                        @Override
                        public boolean isViewReferenced() {
                            return true;
                        }
                    });
                } else if (isViewColumnReferenced != null) {
                    if (viewColumnConstants != null && columnPosition < viewColumnConstants.length) {
                        entry.setValue(column = new DelegateColumn(column) {

                            @Override
                            public byte[] getViewConstant() {
                                return viewColumnConstants[columnPosition];
                            }

                            @Override
                            public boolean isViewReferenced() {
                                return isViewColumnReferenced.get(columnPosition);
                            }
                        });
                    } else {
                        entry.setValue(column = new DelegateColumn(column) {

                            @Override
                            public boolean isViewReferenced() {
                                return isViewColumnReferenced.get(columnPosition);
                            }
                        });
                    }
                }
                Short keySeq = SchemaUtil.isPKColumn(column) ? ++nextKeySeq : null;
                addColumnMutation(schemaName, tableName, column, colUpsert, parentTableName, pkName, keySeq, saltBucketNum != null);
                columnMetadata.addAll(connection.getMutationState().toMutations(timestamp).next().getSecond());
                connection.rollback();
            }
        }
        // add the columns in reverse order since we reverse the list later
        Collections.reverse(columnMetadata);
        tableMetaData.addAll(columnMetadata);
        String dataTableName = parent == null || tableType == PTableType.VIEW ? null : parent.getTableName().getString();
        PIndexState indexState = parent == null || tableType == PTableType.VIEW ? null : PIndexState.BUILDING;
        PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE);
        tableUpsert.setString(1, tenantIdStr);
        tableUpsert.setString(2, schemaName);
        tableUpsert.setString(3, tableName);
        tableUpsert.setString(4, tableType.getSerializedValue());
        tableUpsert.setLong(5, PTable.INITIAL_SEQ_NUM);
        tableUpsert.setInt(6, position);
        if (saltBucketNum != null) {
            tableUpsert.setInt(7, saltBucketNum);
        } else {
            tableUpsert.setNull(7, Types.INTEGER);
        }
        tableUpsert.setString(8, pkName);
        tableUpsert.setString(9, dataTableName);
        tableUpsert.setString(10, indexState == null ? null : indexState.getSerializedValue());
        tableUpsert.setBoolean(11, isImmutableRows);
        tableUpsert.setString(12, defaultFamilyName);
        if (parent != null && parent.getAutoPartitionSeqName() != null && viewStatement == null) {
            // set to non-null value so that we will generate a Put that
            // will be set correctly on the server
            tableUpsert.setString(13, QueryConstants.EMPTY_COLUMN_VALUE);
        } else {
            tableUpsert.setString(13, viewStatement);
        }
        tableUpsert.setBoolean(14, disableWAL);
        tableUpsert.setBoolean(15, multiTenant);
        if (viewType == null) {
            tableUpsert.setNull(16, Types.TINYINT);
        } else {
            tableUpsert.setByte(16, viewType.getSerializedValue());
        }
        if (indexType == null) {
            tableUpsert.setNull(17, Types.TINYINT);
        } else {
            tableUpsert.setByte(17, indexType.getSerializedValue());
        }
        tableUpsert.setBoolean(18, storeNulls);
        if (parent != null && tableType == PTableType.VIEW) {
            tableUpsert.setInt(19, parent.getColumns().size());
        } else {
            tableUpsert.setInt(19, BASE_TABLE_BASE_COLUMN_COUNT);
        }
        tableUpsert.setBoolean(20, transactional);
        tableUpsert.setLong(21, updateCacheFrequency);
        tableUpsert.setBoolean(22, isNamespaceMapped);
        if (autoPartitionSeq == null) {
            tableUpsert.setNull(23, Types.VARCHAR);
        } else {
            tableUpsert.setString(23, autoPartitionSeq);
        }
        tableUpsert.setBoolean(24, isAppendOnlySchema);
        if (guidePostsWidth == null) {
            tableUpsert.setNull(25, Types.BIGINT);
        } else {
            tableUpsert.setLong(25, guidePostsWidth);
        }
        tableUpsert.setByte(26, immutableStorageScheme.getSerializedMetadataValue());
        tableUpsert.setByte(27, encodingScheme.getSerializedMetadataValue());
        tableUpsert.setBoolean(28, useStatsForParallelization);
        tableUpsert.execute();
        if (asyncCreatedDate != null) {
            PreparedStatement setAsync = connection.prepareStatement(SET_ASYNC_CREATED_DATE);
            setAsync.setString(1, tenantIdStr);
            setAsync.setString(2, schemaName);
            setAsync.setString(3, tableName);
            setAsync.setDate(4, asyncCreatedDate);
            setAsync.execute();
        }
        tableMetaData.addAll(connection.getMutationState().toMutations(timestamp).next().getSecond());
        connection.rollback();
        /*
             * The table metadata must be in the following order:
             * 1) table header row
             * 2) ordered column rows
             * 3) parent table header row
             */
        Collections.reverse(tableMetaData);
        if (indexType != IndexType.LOCAL) {
            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);
        MutationCode code = result.getMutationCode();
        switch(code) {
            case TABLE_ALREADY_EXISTS:
                if (result.getTable() != null) {
                    // Can happen for transactional table that already exists as HBase table
                    addTableToCache(result);
                }
                if (!statement.ifNotExists()) {
                    throw new TableAlreadyExistsException(schemaName, tableName, result.getTable());
                }
                return null;
            case PARENT_TABLE_NOT_FOUND:
                throw new TableNotFoundException(schemaName, parent.getName().getString());
            case NEWER_TABLE_FOUND:
                // it to this connection as we can't see it.
                if (!statement.ifNotExists()) {
                    throw new NewerTableAlreadyExistsException(schemaName, tableName, result.getTable());
                }
            case UNALLOWED_TABLE_MUTATION:
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE).setSchemaName(schemaName).setTableName(tableName).build().buildException();
            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();
            case CANNOT_COERCE_AUTO_PARTITION_ID:
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_COERCE_AUTO_PARTITION_ID).setSchemaName(schemaName).setTableName(tableName).build().buildException();
            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();
            default:
                // 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 = PTableImpl.makePTable(tenantId, newSchemaName, PNameFactory.newName(tableName), tableType, indexState, timestamp != null ? timestamp : result.getMutationTime(), PTable.INITIAL_SEQ_NUM, pkName == null ? null : PNameFactory.newName(pkName), saltBucketNum, columns.values(), parent == null ? null : parent.getSchemaName(), parent == null ? null : parent.getTableName(), Collections.<PTable>emptyList(), isImmutableRows, physicalNames, defaultFamilyName == null ? null : PNameFactory.newName(defaultFamilyName), viewStatement, Boolean.TRUE.equals(disableWAL), multiTenant, storeNulls, viewType, result.getViewIndexId(), indexType, rowKeyOrderOptimizable, transactional, updateCacheFrequency, 0L, isNamespaceMapped, autoPartitionSeq, isAppendOnlySchema, immutableStorageScheme, encodingScheme, cqCounterToBe, useStatsForParallelization);
                result = new MetaDataMutationResult(code, result.getMutationTime(), table, true);
                addTableToCache(result);
                return table;
        }
    } finally {
        connection.setAutoCommit(wasAutoCommit);
    }
}
Also used : LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) PDataType(org.apache.phoenix.schema.types.PDataType) SQLExceptionInfo(org.apache.phoenix.exception.SQLExceptionInfo) ColumnDef(org.apache.phoenix.parse.ColumnDef) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) TableName(org.apache.phoenix.parse.TableName) PUnsignedLong(org.apache.phoenix.schema.types.PUnsignedLong) PLong(org.apache.phoenix.schema.types.PLong) Mutation(org.apache.hadoop.hbase.client.Mutation) Map(java.util.Map) LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) QualifierOutOfRangeException(org.apache.phoenix.schema.PTable.QualifierEncodingScheme.QualifierOutOfRangeException) QualifierEncodingScheme(org.apache.phoenix.schema.PTable.QualifierEncodingScheme) ImmutableStorageScheme(org.apache.phoenix.schema.PTable.ImmutableStorageScheme) MetaDataMutationResult(org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult) Pair(org.apache.hadoop.hbase.util.Pair) PreparedStatement(java.sql.PreparedStatement) IndexKeyConstraint(org.apache.phoenix.parse.IndexKeyConstraint) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) ColumnDefInPkConstraint(org.apache.phoenix.parse.ColumnDefInPkConstraint) MutationCode(org.apache.phoenix.coprocessor.MetaDataProtocol.MutationCode) HTableDescriptor(org.apache.hadoop.hbase.HTableDescriptor) PInteger(org.apache.phoenix.schema.types.PInteger) ColumnName(org.apache.phoenix.parse.ColumnName) EncodedCQCounter(org.apache.phoenix.schema.PTable.EncodedCQCounter)

Example 2 with ColumnDef

use of org.apache.phoenix.parse.ColumnDef in project phoenix by apache.

the class MetaDataClient method createIndex.

/**
     * Create an index table by morphing the CreateIndexStatement into a CreateTableStatement and calling
     * MetaDataClient.createTable. In doing so, we perform the following translations:
     * 1) Change the type of any columns being indexed to types that support null if the column is nullable.
     *    For example, a BIGINT type would be coerced to a DECIMAL type, since a DECIMAL type supports null
     *    when it's in the row key while a BIGINT does not.
     * 2) Append any row key column from the data table that is not in the indexed column list. Our indexes
     *    rely on having a 1:1 correspondence between the index and data rows.
     * 3) Change the name of the columns to include the column family. For example, if you have a column
     *    named "B" in a column family named "A", the indexed column name will be "A:B". This makes it easy
     *    to translate the column references in a query to the correct column references in an index table
     *    regardless of whether the column reference is prefixed with the column family name or not. It also
     *    has the side benefit of allowing the same named column in different column families to both be
     *    listed as an index column.
     * @param statement
     * @param splits
     * @return MutationState from population of index table from data table
     * @throws SQLException
     */
public MutationState createIndex(CreateIndexStatement statement, byte[][] splits) throws SQLException {
    IndexKeyConstraint ik = statement.getIndexConstraint();
    TableName indexTableName = statement.getIndexTableName();
    Map<String, Object> tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size());
    Map<String, Object> commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size() + 1);
    populatePropertyMaps(statement.getProps(), tableProps, commonFamilyProps);
    List<Pair<ParseNode, SortOrder>> indexParseNodeAndSortOrderList = ik.getParseNodeAndSortOrderList();
    List<ColumnName> includedColumns = statement.getIncludeColumns();
    TableRef tableRef = null;
    PTable table = null;
    int numRetries = 0;
    boolean allocateIndexId = false;
    boolean isLocalIndex = statement.getIndexType() == IndexType.LOCAL;
    int hbaseVersion = connection.getQueryServices().getLowestClusterHBaseVersion();
    if (isLocalIndex) {
        if (!connection.getQueryServices().getProps().getBoolean(QueryServices.ALLOW_LOCAL_INDEX_ATTRIB, QueryServicesOptions.DEFAULT_ALLOW_LOCAL_INDEX)) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNALLOWED_LOCAL_INDEXES).setTableName(indexTableName.getTableName()).build().buildException();
        }
        if (!connection.getQueryServices().supportsFeature(Feature.LOCAL_INDEX)) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_LOCAL_INDEXES).setTableName(indexTableName.getTableName()).build().buildException();
        }
    }
    while (true) {
        try {
            ColumnResolver resolver = FromCompiler.getResolver(statement, connection, statement.getUdfParseNodes());
            tableRef = resolver.getTables().get(0);
            Date asyncCreatedDate = null;
            if (statement.isAsync()) {
                asyncCreatedDate = new Date(tableRef.getTimeStamp());
            }
            PTable dataTable = tableRef.getTable();
            boolean isTenantConnection = connection.getTenantId() != null;
            if (isTenantConnection) {
                if (dataTable.getType() != PTableType.VIEW) {
                    throw new SQLFeatureNotSupportedException("An index may only be created for a VIEW through a tenant-specific connection");
                }
            }
            if (!dataTable.isImmutableRows()) {
                if (hbaseVersion < PhoenixDatabaseMetaData.MUTABLE_SI_VERSION_THRESHOLD) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_MUTABLE_INDEXES).setTableName(indexTableName.getTableName()).build().buildException();
                }
                if (!connection.getQueryServices().hasIndexWALCodec() && !dataTable.isTransactional()) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_MUTABLE_INDEX_CONFIG).setTableName(indexTableName.getTableName()).build().buildException();
                }
            }
            int posOffset = 0;
            List<PColumn> pkColumns = dataTable.getPKColumns();
            Set<RowKeyColumnExpression> unusedPkColumns;
            if (dataTable.getBucketNum() != null) {
                // Ignore SALT column
                unusedPkColumns = Sets.newLinkedHashSetWithExpectedSize(pkColumns.size() - 1);
                posOffset++;
            } else {
                unusedPkColumns = Sets.newLinkedHashSetWithExpectedSize(pkColumns.size());
            }
            for (int i = posOffset; i < pkColumns.size(); i++) {
                PColumn column = pkColumns.get(i);
                unusedPkColumns.add(new RowKeyColumnExpression(column, new RowKeyValueAccessor(pkColumns, i), "\"" + column.getName().getString() + "\""));
            }
            List<ColumnDefInPkConstraint> allPkColumns = Lists.newArrayListWithExpectedSize(unusedPkColumns.size());
            List<ColumnDef> columnDefs = Lists.newArrayListWithExpectedSize(includedColumns.size() + indexParseNodeAndSortOrderList.size());
            /*
                 * Allocate an index ID in two circumstances:
                 * 1) for a local index, as all local indexes will reside in the same HBase table
                 * 2) for a view on an index.
                 */
            if (isLocalIndex || (dataTable.getType() == PTableType.VIEW && dataTable.getViewType() != ViewType.MAPPED)) {
                allocateIndexId = true;
                PDataType dataType = MetaDataUtil.getViewIndexIdDataType();
                ColumnName colName = ColumnName.caseSensitiveColumnName(MetaDataUtil.getViewIndexIdColumnName());
                allPkColumns.add(new ColumnDefInPkConstraint(colName, SortOrder.getDefault(), false));
                columnDefs.add(FACTORY.columnDef(colName, dataType.getSqlTypeName(), false, null, null, false, SortOrder.getDefault(), null, false));
            }
            if (dataTable.isMultiTenant()) {
                PColumn col = dataTable.getPKColumns().get(posOffset);
                RowKeyColumnExpression columnExpression = new RowKeyColumnExpression(col, new RowKeyValueAccessor(pkColumns, posOffset), col.getName().getString());
                unusedPkColumns.remove(columnExpression);
                PDataType dataType = IndexUtil.getIndexColumnDataType(col);
                ColumnName colName = ColumnName.caseSensitiveColumnName(IndexUtil.getIndexColumnName(col));
                allPkColumns.add(new ColumnDefInPkConstraint(colName, col.getSortOrder(), false));
                columnDefs.add(FACTORY.columnDef(colName, dataType.getSqlTypeName(), col.isNullable(), col.getMaxLength(), col.getScale(), false, SortOrder.getDefault(), col.getName().getString(), col.isRowTimestamp()));
            }
            PhoenixStatement phoenixStatment = new PhoenixStatement(connection);
            StatementContext context = new StatementContext(phoenixStatment, resolver);
            IndexExpressionCompiler expressionIndexCompiler = new IndexExpressionCompiler(context);
            Set<ColumnName> indexedColumnNames = Sets.newHashSetWithExpectedSize(indexParseNodeAndSortOrderList.size());
            for (Pair<ParseNode, SortOrder> pair : indexParseNodeAndSortOrderList) {
                ParseNode parseNode = pair.getFirst();
                // normalize the parse node
                parseNode = StatementNormalizer.normalize(parseNode, resolver);
                // compile the parseNode to get an expression
                expressionIndexCompiler.reset();
                Expression expression = parseNode.accept(expressionIndexCompiler);
                if (expressionIndexCompiler.isAggregate()) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.AGGREGATE_EXPRESSION_NOT_ALLOWED_IN_INDEX).build().buildException();
                }
                if (expression.getDeterminism() != Determinism.ALWAYS) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.NON_DETERMINISTIC_EXPRESSION_NOT_ALLOWED_IN_INDEX).build().buildException();
                }
                if (expression.isStateless()) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.STATELESS_EXPRESSION_NOT_ALLOWED_IN_INDEX).build().buildException();
                }
                unusedPkColumns.remove(expression);
                // Go through parse node to get string as otherwise we
                // can lose information during compilation
                StringBuilder buf = new StringBuilder();
                parseNode.toSQL(resolver, buf);
                // need to escape backslash as this expression will be re-parsed later
                String expressionStr = StringUtil.escapeBackslash(buf.toString());
                ColumnName colName = null;
                ColumnRef colRef = expressionIndexCompiler.getColumnRef();
                boolean isRowTimestamp = false;
                if (colRef != null) {
                    // if this is a regular column
                    PColumn column = colRef.getColumn();
                    String columnFamilyName = column.getFamilyName() != null ? column.getFamilyName().getString() : null;
                    colName = ColumnName.caseSensitiveColumnName(IndexUtil.getIndexColumnName(columnFamilyName, column.getName().getString()));
                    isRowTimestamp = column.isRowTimestamp();
                    if (colRef.getColumn().getExpressionStr() != null) {
                        expressionStr = colRef.getColumn().getExpressionStr();
                    }
                } else {
                    // if this is an expression
                    // TODO column names cannot have double quotes, remove this once this PHOENIX-1621 is fixed
                    String name = expressionStr.replaceAll("\"", "'");
                    colName = ColumnName.caseSensitiveColumnName(IndexUtil.getIndexColumnName(null, name));
                }
                indexedColumnNames.add(colName);
                PDataType dataType = IndexUtil.getIndexColumnDataType(expression.isNullable(), expression.getDataType());
                allPkColumns.add(new ColumnDefInPkConstraint(colName, pair.getSecond(), isRowTimestamp));
                columnDefs.add(FACTORY.columnDef(colName, dataType.getSqlTypeName(), expression.isNullable(), expression.getMaxLength(), expression.getScale(), false, pair.getSecond(), expressionStr, isRowTimestamp));
            }
            // Next all the PK columns from the data table that aren't indexed
            if (!unusedPkColumns.isEmpty()) {
                for (RowKeyColumnExpression colExpression : unusedPkColumns) {
                    PColumn col = dataTable.getPKColumns().get(colExpression.getPosition());
                    // we don't need these in the index
                    if (col.getViewConstant() == null) {
                        ColumnName colName = ColumnName.caseSensitiveColumnName(IndexUtil.getIndexColumnName(col));
                        allPkColumns.add(new ColumnDefInPkConstraint(colName, colExpression.getSortOrder(), col.isRowTimestamp()));
                        PDataType dataType = IndexUtil.getIndexColumnDataType(colExpression.isNullable(), colExpression.getDataType());
                        columnDefs.add(FACTORY.columnDef(colName, dataType.getSqlTypeName(), colExpression.isNullable(), colExpression.getMaxLength(), colExpression.getScale(), false, colExpression.getSortOrder(), colExpression.toString(), col.isRowTimestamp()));
                    }
                }
            }
            // Last all the included columns (minus any PK columns)
            for (ColumnName colName : includedColumns) {
                PColumn col = resolver.resolveColumn(null, colName.getFamilyName(), colName.getColumnName()).getColumn();
                colName = ColumnName.caseSensitiveColumnName(IndexUtil.getIndexColumnName(col));
                // Check for duplicates between indexed and included columns
                if (indexedColumnNames.contains(colName)) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.COLUMN_EXIST_IN_DEF).build().buildException();
                }
                if (!SchemaUtil.isPKColumn(col) && col.getViewConstant() == null) {
                    // Need to re-create ColumnName, since the above one won't have the column family name
                    colName = ColumnName.caseSensitiveColumnName(isLocalIndex ? IndexUtil.getLocalIndexColumnFamily(col.getFamilyName().getString()) : col.getFamilyName().getString(), IndexUtil.getIndexColumnName(col));
                    columnDefs.add(FACTORY.columnDef(colName, col.getDataType().getSqlTypeName(), col.isNullable(), col.getMaxLength(), col.getScale(), false, col.getSortOrder(), col.getExpressionStr(), col.isRowTimestamp()));
                }
            }
            // We need this in the props so that the correct column family is created
            if (dataTable.getDefaultFamilyName() != null && dataTable.getType() != PTableType.VIEW && !allocateIndexId) {
                statement.getProps().put("", new Pair<String, Object>(DEFAULT_COLUMN_FAMILY_NAME, dataTable.getDefaultFamilyName().getString()));
            }
            PrimaryKeyConstraint pk = FACTORY.primaryKey(null, allPkColumns);
            tableProps.put(MetaDataUtil.DATA_TABLE_NAME_PROP_NAME, dataTable.getName().getString());
            CreateTableStatement tableStatement = FACTORY.createTable(indexTableName, statement.getProps(), columnDefs, pk, statement.getSplitNodes(), PTableType.INDEX, statement.ifNotExists(), null, null, statement.getBindCount(), null);
            table = createTableInternal(tableStatement, splits, dataTable, null, null, null, null, allocateIndexId, statement.getIndexType(), asyncCreatedDate, tableProps, commonFamilyProps);
            break;
        } catch (ConcurrentTableMutationException e) {
            // Can happen if parent data table changes while above is in progress
            if (numRetries < 5) {
                numRetries++;
                continue;
            }
            throw e;
        }
    }
    if (table == null) {
        return new MutationState(0, 0, connection);
    }
    if (logger.isInfoEnabled())
        logger.info("Created index " + table.getName().getString() + " at " + table.getTimeStamp());
    boolean asyncIndexBuildEnabled = connection.getQueryServices().getProps().getBoolean(QueryServices.INDEX_ASYNC_BUILD_ENABLED, QueryServicesOptions.DEFAULT_INDEX_ASYNC_BUILD_ENABLED);
    // In async process, we return immediately as the MR job needs to be triggered .
    if (statement.isAsync() && asyncIndexBuildEnabled) {
        return new MutationState(0, 0, connection);
    }
    // connection so that our new index table is visible.
    if (connection.getSCN() != null) {
        return buildIndexAtTimeStamp(table, statement.getTable());
    }
    return buildIndex(table, tableRef);
}
Also used : SQLFeatureNotSupportedException(java.sql.SQLFeatureNotSupportedException) PhoenixStatement(org.apache.phoenix.jdbc.PhoenixStatement) StatementContext(org.apache.phoenix.compile.StatementContext) PDataType(org.apache.phoenix.schema.types.PDataType) ParseNode(org.apache.phoenix.parse.ParseNode) SQLExceptionInfo(org.apache.phoenix.exception.SQLExceptionInfo) ColumnResolver(org.apache.phoenix.compile.ColumnResolver) Pair(org.apache.hadoop.hbase.util.Pair) CreateTableStatement(org.apache.phoenix.parse.CreateTableStatement) IndexKeyConstraint(org.apache.phoenix.parse.IndexKeyConstraint) ColumnDef(org.apache.phoenix.parse.ColumnDef) ColumnDefInPkConstraint(org.apache.phoenix.parse.ColumnDefInPkConstraint) RowKeyColumnExpression(org.apache.phoenix.expression.RowKeyColumnExpression) IndexKeyConstraint(org.apache.phoenix.parse.IndexKeyConstraint) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) ColumnDefInPkConstraint(org.apache.phoenix.parse.ColumnDefInPkConstraint) PDate(org.apache.phoenix.schema.types.PDate) Date(java.sql.Date) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) TableName(org.apache.phoenix.parse.TableName) ColumnName(org.apache.phoenix.parse.ColumnName) MutationState(org.apache.phoenix.execute.MutationState) RowKeyColumnExpression(org.apache.phoenix.expression.RowKeyColumnExpression) Expression(org.apache.phoenix.expression.Expression) IndexExpressionCompiler(org.apache.phoenix.compile.IndexExpressionCompiler)

Example 3 with ColumnDef

use of org.apache.phoenix.parse.ColumnDef in project phoenix by apache.

the class MetaDataClient method createTable.

public MutationState createTable(CreateTableStatement statement, byte[][] splits, PTable parent, String viewStatement, ViewType viewType, byte[][] viewColumnConstants, BitSet isViewColumnReferenced) throws SQLException {
    TableName tableName = statement.getTableName();
    Map<String, Object> tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size());
    Map<String, Object> commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size() + 1);
    populatePropertyMaps(statement.getProps(), tableProps, commonFamilyProps);
    boolean isAppendOnlySchema = false;
    long updateCacheFrequency = connection.getQueryServices().getProps().getLong(QueryServices.DEFAULT_UPDATE_CACHE_FREQUENCY_ATRRIB, QueryServicesOptions.DEFAULT_UPDATE_CACHE_FREQUENCY);
    if (parent == null) {
        Boolean appendOnlySchemaProp = (Boolean) TableProperty.APPEND_ONLY_SCHEMA.getValue(tableProps);
        if (appendOnlySchemaProp != null) {
            isAppendOnlySchema = appendOnlySchemaProp;
        }
        Long updateCacheFrequencyProp = (Long) TableProperty.UPDATE_CACHE_FREQUENCY.getValue(tableProps);
        if (updateCacheFrequencyProp != null) {
            updateCacheFrequency = updateCacheFrequencyProp;
        }
    } else {
        isAppendOnlySchema = parent.isAppendOnlySchema();
        updateCacheFrequency = parent.getUpdateCacheFrequency();
    }
    // updateCacheFrequency cannot be set to ALWAYS if isAppendOnlySchema is true
    if (isAppendOnlySchema && updateCacheFrequency == 0) {
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.UPDATE_CACHE_FREQUENCY_INVALID).setSchemaName(tableName.getSchemaName()).setTableName(tableName.getTableName()).build().buildException();
    }
    Boolean immutableProp = (Boolean) TableProperty.IMMUTABLE_ROWS.getValue(tableProps);
    if (statement.immutableRows() != null && immutableProp != null) {
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.IMMUTABLE_TABLE_PROPERTY_INVALID).setSchemaName(tableName.getSchemaName()).setTableName(tableName.getTableName()).build().buildException();
    }
    PTable table = null;
    // if it is add columns that are not already present
    if (isAppendOnlySchema) {
        // look up the table in the cache
        MetaDataMutationResult result = updateCache(tableName.getSchemaName(), tableName.getTableName());
        if (result.getMutationCode() == MutationCode.TABLE_ALREADY_EXISTS) {
            table = result.getTable();
            if (!statement.ifNotExists()) {
                throw new NewerTableAlreadyExistsException(tableName.getSchemaName(), tableName.getTableName(), table);
            }
            List<ColumnDef> columnDefs = statement.getColumnDefs();
            PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint();
            // get the list of columns to add
            for (ColumnDef columnDef : columnDefs) {
                if (pkConstraint.contains(columnDef.getColumnDefName())) {
                    columnDef.setIsPK(true);
                }
            }
            // if there are new columns to add
            return addColumn(table, columnDefs, statement.getProps(), statement.ifNotExists(), true, NamedTableNode.create(statement.getTableName()), statement.getTableType());
        }
    }
    table = createTableInternal(statement, splits, parent, viewStatement, viewType, viewColumnConstants, isViewColumnReferenced, false, null, null, tableProps, commonFamilyProps);
    if (table == null || table.getType() == PTableType.VIEW) /*|| table.isTransactional()*/
    {
        return new MutationState(0, 0, connection);
    }
    // Hack to get around the case when an SCN is specified on the connection.
    // In this case, we won't see the table we just created yet, so we hack
    // around it by forcing the compiler to not resolve anything.
    PostDDLCompiler compiler = new PostDDLCompiler(connection);
    //connection.setAutoCommit(true);
    // Execute any necessary data updates
    Long scn = connection.getSCN();
    long ts = (scn == null ? table.getTimeStamp() : scn);
    // Getting the schema through the current connection doesn't work when the connection has an scn specified
    // Since the table won't be added to the current connection.
    TableRef tableRef = new TableRef(null, table, ts, false);
    byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table);
    MutationPlan plan = compiler.compile(Collections.singletonList(tableRef), emptyCF, null, null, tableRef.getTimeStamp());
    return connection.getQueryServices().updateData(plan);
}
Also used : ColumnDef(org.apache.phoenix.parse.ColumnDef) PostDDLCompiler(org.apache.phoenix.compile.PostDDLCompiler) MutationPlan(org.apache.phoenix.compile.MutationPlan) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) TableName(org.apache.phoenix.parse.TableName) MutationState(org.apache.phoenix.execute.MutationState) PUnsignedLong(org.apache.phoenix.schema.types.PUnsignedLong) PLong(org.apache.phoenix.schema.types.PLong) MetaDataMutationResult(org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult)

Example 4 with ColumnDef

use of org.apache.phoenix.parse.ColumnDef in project phoenix by apache.

the class MetaDataClient method addColumn.

public MutationState addColumn(PTable table, List<ColumnDef> origColumnDefs, ListMultimap<String, Pair<String, Object>> stmtProperties, boolean ifNotExists, boolean removeTableProps, NamedTableNode namedTableNode, PTableType tableType) throws SQLException {
    connection.rollback();
    boolean wasAutoCommit = connection.getAutoCommit();
    try {
        connection.setAutoCommit(false);
        PName tenantId = connection.getTenantId();
        String schemaName = table.getSchemaName().getString();
        String tableName = table.getTableName().getString();
        Boolean isImmutableRowsProp = null;
        Boolean multiTenantProp = null;
        Boolean disableWALProp = null;
        Boolean storeNullsProp = null;
        Boolean isTransactionalProp = null;
        Long updateCacheFrequencyProp = null;
        Boolean appendOnlySchemaProp = null;
        Long guidePostWidth = -1L;
        ImmutableStorageScheme immutableStorageSchemeProp = null;
        Boolean useStatsForParallelizationProp = null;
        Map<String, List<Pair<String, Object>>> properties = new HashMap<>(stmtProperties.size());
        List<ColumnDef> columnDefs = null;
        if (table.isAppendOnlySchema()) {
            // only make the rpc if we are adding new columns
            columnDefs = Lists.newArrayList();
            for (ColumnDef columnDef : origColumnDefs) {
                String familyName = columnDef.getColumnDefName().getFamilyName();
                String columnName = columnDef.getColumnDefName().getColumnName();
                if (familyName != null) {
                    try {
                        PColumnFamily columnFamily = table.getColumnFamily(familyName);
                        columnFamily.getPColumnForColumnName(columnName);
                        if (!ifNotExists) {
                            throw new ColumnAlreadyExistsException(schemaName, tableName, columnName);
                        }
                    } catch (ColumnFamilyNotFoundException | ColumnNotFoundException e) {
                        columnDefs.add(columnDef);
                    }
                } else {
                    try {
                        table.getColumnForColumnName(columnName);
                        if (!ifNotExists) {
                            throw new ColumnAlreadyExistsException(schemaName, tableName, columnName);
                        }
                    } catch (ColumnNotFoundException e) {
                        columnDefs.add(columnDef);
                    }
                }
            }
        } else {
            columnDefs = origColumnDefs == null ? Collections.<ColumnDef>emptyList() : origColumnDefs;
        }
        for (String family : stmtProperties.keySet()) {
            List<Pair<String, Object>> origPropsList = stmtProperties.get(family);
            List<Pair<String, Object>> propsList = Lists.newArrayListWithExpectedSize(origPropsList.size());
            for (Pair<String, Object> prop : origPropsList) {
                String propName = prop.getFirst();
                if (TableProperty.isPhoenixTableProperty(propName)) {
                    TableProperty tableProp = TableProperty.valueOf(propName);
                    tableProp.validate(true, !family.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY), table.getType());
                    Object value = tableProp.getValue(prop.getSecond());
                    if (propName.equals(PTable.IS_IMMUTABLE_ROWS_PROP_NAME)) {
                        isImmutableRowsProp = (Boolean) value;
                    } else if (propName.equals(PhoenixDatabaseMetaData.MULTI_TENANT)) {
                        multiTenantProp = (Boolean) value;
                    } else if (propName.equals(DISABLE_WAL)) {
                        disableWALProp = (Boolean) value;
                    } else if (propName.equals(STORE_NULLS)) {
                        storeNullsProp = (Boolean) value;
                    } else if (propName.equals(TRANSACTIONAL)) {
                        isTransactionalProp = (Boolean) value;
                    } else if (propName.equals(UPDATE_CACHE_FREQUENCY)) {
                        updateCacheFrequencyProp = (Long) value;
                    } else if (propName.equals(GUIDE_POSTS_WIDTH)) {
                        guidePostWidth = (Long) value;
                    } else if (propName.equals(APPEND_ONLY_SCHEMA)) {
                        appendOnlySchemaProp = (Boolean) value;
                    } else if (propName.equalsIgnoreCase(IMMUTABLE_STORAGE_SCHEME)) {
                        immutableStorageSchemeProp = (ImmutableStorageScheme) value;
                    } else if (propName.equalsIgnoreCase(USE_STATS_FOR_PARALLELIZATION)) {
                        useStatsForParallelizationProp = (Boolean) value;
                    }
                }
                // if removeTableProps is true only add the property if it is not a HTable or Phoenix Table property
                if (!removeTableProps || (!TableProperty.isPhoenixTableProperty(propName) && !MetaDataUtil.isHTableProperty(propName))) {
                    propsList.add(prop);
                }
            }
            properties.put(family, propsList);
        }
        boolean retried = false;
        boolean changingPhoenixTableProperty = false;
        boolean nonTxToTx = false;
        while (true) {
            ColumnResolver resolver = FromCompiler.getResolver(namedTableNode, connection);
            table = resolver.getTables().get(0).getTable();
            int nIndexes = table.getIndexes().size();
            int numCols = columnDefs.size();
            int nNewColumns = numCols;
            List<Mutation> tableMetaData = Lists.newArrayListWithExpectedSize((1 + nNewColumns) * (nIndexes + 1));
            List<Mutation> columnMetaData = Lists.newArrayListWithExpectedSize(nNewColumns * (nIndexes + 1));
            if (logger.isDebugEnabled()) {
                logger.debug(LogUtil.addCustomAnnotations("Resolved table to " + table.getName().getString() + " with seqNum " + table.getSequenceNumber() + " at timestamp " + table.getTimeStamp() + " with " + table.getColumns().size() + " columns: " + table.getColumns(), connection));
            }
            int position = table.getColumns().size();
            List<PColumn> currentPKs = table.getPKColumns();
            PColumn lastPK = currentPKs.get(currentPKs.size() - 1);
            // Disallow adding columns if the last column is VARBIANRY.
            if (lastPK.getDataType() == PVarbinary.INSTANCE || lastPK.getDataType().isArrayType()) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.VARBINARY_LAST_PK).setColumnName(lastPK.getName().getString()).build().buildException();
            }
            // Disallow adding columns if last column is fixed width and nullable.
            if (lastPK.isNullable() && lastPK.getDataType().isFixedWidth()) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.NULLABLE_FIXED_WIDTH_LAST_PK).setColumnName(lastPK.getName().getString()).build().buildException();
            }
            Boolean isImmutableRows = null;
            if (isImmutableRowsProp != null) {
                if (isImmutableRowsProp.booleanValue() != table.isImmutableRows()) {
                    if (table.getImmutableStorageScheme() != ImmutableStorageScheme.ONE_CELL_PER_COLUMN) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ALTER_IMMUTABLE_ROWS_PROPERTY).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                    isImmutableRows = isImmutableRowsProp;
                    changingPhoenixTableProperty = true;
                }
            }
            Boolean multiTenant = null;
            if (multiTenantProp != null) {
                if (multiTenantProp.booleanValue() != table.isMultiTenant()) {
                    multiTenant = multiTenantProp;
                    changingPhoenixTableProperty = true;
                }
            }
            Boolean disableWAL = null;
            if (disableWALProp != null) {
                if (disableWALProp.booleanValue() != table.isWALDisabled()) {
                    disableWAL = disableWALProp;
                    changingPhoenixTableProperty = true;
                }
            }
            Long updateCacheFrequency = null;
            if (updateCacheFrequencyProp != null) {
                if (updateCacheFrequencyProp.longValue() != table.getUpdateCacheFrequency()) {
                    updateCacheFrequency = updateCacheFrequencyProp;
                    changingPhoenixTableProperty = true;
                }
            }
            Boolean appendOnlySchema = null;
            if (appendOnlySchemaProp != null) {
                if (appendOnlySchemaProp != table.isAppendOnlySchema()) {
                    appendOnlySchema = appendOnlySchemaProp;
                    changingPhoenixTableProperty = true;
                }
            }
            ImmutableStorageScheme immutableStorageScheme = null;
            if (immutableStorageSchemeProp != null) {
                if (table.getImmutableStorageScheme() == ONE_CELL_PER_COLUMN || immutableStorageSchemeProp == ONE_CELL_PER_COLUMN) {
                    throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_IMMUTABLE_STORAGE_SCHEME_CHANGE).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                } else if (immutableStorageSchemeProp != table.getImmutableStorageScheme()) {
                    immutableStorageScheme = immutableStorageSchemeProp;
                    changingPhoenixTableProperty = true;
                }
            }
            if (guidePostWidth == null || guidePostWidth >= 0) {
                changingPhoenixTableProperty = true;
            }
            Boolean storeNulls = null;
            if (storeNullsProp != null) {
                if (storeNullsProp.booleanValue() != table.getStoreNulls()) {
                    storeNulls = storeNullsProp;
                    changingPhoenixTableProperty = true;
                }
            }
            Boolean useStatsForParallelization = null;
            if (useStatsForParallelizationProp != null) {
                if (useStatsForParallelizationProp.booleanValue() != table.useStatsForParallelization()) {
                    useStatsForParallelization = useStatsForParallelizationProp;
                    changingPhoenixTableProperty = true;
                }
            }
            Boolean isTransactional = null;
            if (isTransactionalProp != null) {
                if (isTransactionalProp.booleanValue() != table.isTransactional()) {
                    isTransactional = isTransactionalProp;
                    // delete markers.
                    if (!isTransactional) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.TX_MAY_NOT_SWITCH_TO_NON_TX).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                    // cannot create a transactional table if transactions are disabled
                    boolean transactionsEnabled = connection.getQueryServices().getProps().getBoolean(QueryServices.TRANSACTIONS_ENABLED, QueryServicesOptions.DEFAULT_TRANSACTIONS_ENABLED);
                    if (!transactionsEnabled) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ALTER_TO_BE_TXN_IF_TXNS_DISABLED).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                    // cannot make a table transactional if it has a row timestamp column
                    if (SchemaUtil.hasRowTimestampColumn(table)) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ALTER_TO_BE_TXN_WITH_ROW_TIMESTAMP).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                    changingPhoenixTableProperty = true;
                    nonTxToTx = true;
                }
            }
            Long timeStamp = TransactionUtil.getTableTimestamp(connection, table.isTransactional() || nonTxToTx);
            int numPkColumnsAdded = 0;
            List<PColumn> columns = Lists.newArrayListWithExpectedSize(numCols);
            Set<String> colFamiliesForPColumnsToBeAdded = new LinkedHashSet<>();
            Set<String> families = new LinkedHashSet<>();
            PTable tableForCQCounters = tableType == PTableType.VIEW ? PhoenixRuntime.getTable(connection, table.getPhysicalName().getString()) : table;
            ;
            EncodedCQCounter cqCounterToUse = tableForCQCounters.getEncodedCQCounter();
            Map<String, Integer> changedCqCounters = new HashMap<>(numCols);
            if (numCols > 0) {
                StatementContext context = new StatementContext(new PhoenixStatement(connection), resolver);
                String addColumnSqlToUse = connection.isRunningUpgrade() && tableName.equals(PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE) && schemaName.equals(PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA) ? ALTER_SYSCATALOG_TABLE_UPGRADE : INSERT_COLUMN_ALTER_TABLE;
                try (PreparedStatement colUpsert = connection.prepareStatement(addColumnSqlToUse)) {
                    short nextKeySeq = SchemaUtil.getMaxKeySeq(table);
                    for (ColumnDef colDef : columnDefs) {
                        if (colDef != null && !colDef.isNull()) {
                            if (colDef.isPK()) {
                                throw new SQLExceptionInfo.Builder(SQLExceptionCode.NOT_NULLABLE_COLUMN_IN_ROW_KEY).setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException();
                            } else {
                                throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ADD_NOT_NULLABLE_COLUMN).setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException();
                            }
                        }
                        if (colDef != null && colDef.isPK() && table.getType() == VIEW && table.getViewType() != MAPPED) {
                            throwIfLastPKOfParentIsFixedLength(getParentOfView(table), schemaName, tableName, colDef);
                        }
                        if (colDef != null && colDef.isRowTimestamp()) {
                            throw new SQLExceptionInfo.Builder(SQLExceptionCode.ROWTIMESTAMP_CREATE_ONLY).setColumnName(colDef.getColumnDefName().getColumnName()).build().buildException();
                        }
                        if (!colDef.validateDefault(context, null)) {
                            // Remove DEFAULT as it's not necessary
                            colDef = new ColumnDef(colDef, null);
                        }
                        Integer encodedCQ = null;
                        if (!colDef.isPK()) {
                            String colDefFamily = colDef.getColumnDefName().getFamilyName();
                            String familyName = null;
                            ImmutableStorageScheme storageScheme = table.getImmutableStorageScheme();
                            String defaultColumnFamily = tableForCQCounters.getDefaultFamilyName() != null && !Strings.isNullOrEmpty(tableForCQCounters.getDefaultFamilyName().getString()) ? tableForCQCounters.getDefaultFamilyName().getString() : DEFAULT_COLUMN_FAMILY;
                            if (table.getType() == PTableType.INDEX && table.getIndexType() == IndexType.LOCAL) {
                                defaultColumnFamily = QueryConstants.LOCAL_INDEX_COLUMN_FAMILY_PREFIX + defaultColumnFamily;
                            }
                            if (storageScheme == SINGLE_CELL_ARRAY_WITH_OFFSETS) {
                                familyName = colDefFamily != null ? colDefFamily : defaultColumnFamily;
                            } else {
                                familyName = defaultColumnFamily;
                            }
                            encodedCQ = cqCounterToUse.getNextQualifier(familyName);
                            if (cqCounterToUse.increment(familyName)) {
                                changedCqCounters.put(familyName, cqCounterToUse.getNextQualifier(familyName));
                            }
                        }
                        byte[] columnQualifierBytes = null;
                        try {
                            columnQualifierBytes = EncodedColumnsUtil.getColumnQualifierBytes(colDef.getColumnDefName().getColumnName(), encodedCQ, table, colDef.isPK());
                        } catch (QualifierOutOfRangeException e) {
                            throw new SQLExceptionInfo.Builder(SQLExceptionCode.MAX_COLUMNS_EXCEEDED).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                        }
                        PColumn column = newColumn(position++, colDef, PrimaryKeyConstraint.EMPTY, table.getDefaultFamilyName() == null ? null : table.getDefaultFamilyName().getString(), true, columnQualifierBytes);
                        columns.add(column);
                        String pkName = null;
                        Short keySeq = null;
                        // TODO: support setting properties on other families?
                        if (column.getFamilyName() == null) {
                            ++numPkColumnsAdded;
                            pkName = table.getPKName() == null ? null : table.getPKName().getString();
                            keySeq = ++nextKeySeq;
                        } else {
                            families.add(column.getFamilyName().getString());
                        }
                        colFamiliesForPColumnsToBeAdded.add(column.getFamilyName() == null ? null : column.getFamilyName().getString());
                        addColumnMutation(schemaName, tableName, column, colUpsert, null, pkName, keySeq, table.getBucketNum() != null);
                    }
                    // Add any new PK columns to end of index PK
                    if (numPkColumnsAdded > 0) {
                        // create PK column list that includes the newly created columns
                        List<PColumn> pkColumns = Lists.newArrayListWithExpectedSize(table.getPKColumns().size() + numPkColumnsAdded);
                        pkColumns.addAll(table.getPKColumns());
                        for (int i = 0; i < numCols; ++i) {
                            if (columnDefs.get(i).isPK()) {
                                pkColumns.add(columns.get(i));
                            }
                        }
                        int pkSlotPosition = table.getPKColumns().size() - 1;
                        for (PTable index : table.getIndexes()) {
                            short nextIndexKeySeq = SchemaUtil.getMaxKeySeq(index);
                            int indexPosition = index.getColumns().size();
                            for (int i = 0; i < numCols; ++i) {
                                ColumnDef colDef = columnDefs.get(i);
                                if (colDef.isPK()) {
                                    PDataType indexColDataType = IndexUtil.getIndexColumnDataType(colDef.isNull(), colDef.getDataType());
                                    ColumnName indexColName = ColumnName.caseSensitiveColumnName(IndexUtil.getIndexColumnName(null, colDef.getColumnDefName().getColumnName()));
                                    Expression expression = new RowKeyColumnExpression(columns.get(i), new RowKeyValueAccessor(pkColumns, ++pkSlotPosition));
                                    ColumnDef indexColDef = FACTORY.columnDef(indexColName, indexColDataType.getSqlTypeName(), colDef.isNull(), colDef.getMaxLength(), colDef.getScale(), true, colDef.getSortOrder(), expression.toString(), colDef.isRowTimestamp());
                                    PColumn indexColumn = newColumn(indexPosition++, indexColDef, PrimaryKeyConstraint.EMPTY, null, true, null);
                                    addColumnMutation(schemaName, index.getTableName().getString(), indexColumn, colUpsert, index.getParentTableName().getString(), index.getPKName() == null ? null : index.getPKName().getString(), ++nextIndexKeySeq, index.getBucketNum() != null);
                                }
                            }
                        }
                    }
                    columnMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                    connection.rollback();
                }
            } else {
                // have existing indexes.
                if (Boolean.FALSE.equals(isImmutableRows) && !table.getIndexes().isEmpty()) {
                    int hbaseVersion = connection.getQueryServices().getLowestClusterHBaseVersion();
                    if (hbaseVersion < PhoenixDatabaseMetaData.MUTABLE_SI_VERSION_THRESHOLD) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_MUTABLE_INDEXES).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                    if (!connection.getQueryServices().hasIndexWALCodec() && !table.isTransactional()) {
                        throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_MUTABLE_INDEX_CONFIG).setSchemaName(schemaName).setTableName(tableName).build().buildException();
                    }
                }
                if (Boolean.TRUE.equals(multiTenant)) {
                    throwIfInsufficientColumns(schemaName, tableName, table.getPKColumns(), table.getBucketNum() != null, multiTenant);
                }
            }
            if (!table.getIndexes().isEmpty() && (numPkColumnsAdded > 0 || nonTxToTx)) {
                for (PTable index : table.getIndexes()) {
                    incrementTableSeqNum(index, index.getType(), numPkColumnsAdded, nonTxToTx ? Boolean.TRUE : null, updateCacheFrequency);
                }
                tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                connection.rollback();
            }
            if (changingPhoenixTableProperty || columnDefs.size() > 0) {
                incrementTableSeqNum(table, tableType, columnDefs.size(), isTransactional, updateCacheFrequency, isImmutableRows, disableWAL, multiTenant, storeNulls, guidePostWidth, appendOnlySchema, immutableStorageScheme, useStatsForParallelization);
                tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                connection.rollback();
            }
            // Force the table header row to be first
            Collections.reverse(tableMetaData);
            // Add column metadata afterwards, maintaining the order so columns have more predictable ordinal position
            tableMetaData.addAll(columnMetaData);
            boolean sharedIndex = tableType == PTableType.INDEX && (table.getIndexType() == IndexType.LOCAL || table.getViewIndexId() != null);
            String tenantIdToUse = connection.getTenantId() != null && sharedIndex ? connection.getTenantId().getString() : null;
            if (!changedCqCounters.isEmpty()) {
                PreparedStatement linkStatement;
                linkStatement = connection.prepareStatement(UPDATE_ENCODED_COLUMN_COUNTER);
                for (Entry<String, Integer> entry : changedCqCounters.entrySet()) {
                    linkStatement.setString(1, tenantIdToUse);
                    linkStatement.setString(2, tableForCQCounters.getSchemaName().getString());
                    linkStatement.setString(3, tableForCQCounters.getTableName().getString());
                    linkStatement.setString(4, entry.getKey());
                    linkStatement.setInt(5, entry.getValue());
                    linkStatement.execute();
                }
                // too since we want clients to get the latest PTable of the base table.
                if (tableType == VIEW) {
                    PreparedStatement incrementStatement = connection.prepareStatement(INCREMENT_SEQ_NUM);
                    incrementStatement.setString(1, null);
                    incrementStatement.setString(2, tableForCQCounters.getSchemaName().getString());
                    incrementStatement.setString(3, tableForCQCounters.getTableName().getString());
                    incrementStatement.setLong(4, tableForCQCounters.getSequenceNumber() + 1);
                    incrementStatement.execute();
                }
                tableMetaData.addAll(connection.getMutationState().toMutations(timeStamp).next().getSecond());
                connection.rollback();
            }
            byte[] family = families.size() > 0 ? families.iterator().next().getBytes() : null;
            // Figure out if the empty column family is changing as a result of adding the new column
            byte[] emptyCF = null;
            byte[] projectCF = null;
            if (table.getType() != PTableType.VIEW && family != null) {
                if (table.getColumnFamilies().isEmpty()) {
                    emptyCF = family;
                } else {
                    try {
                        table.getColumnFamily(family);
                    } catch (ColumnFamilyNotFoundException e) {
                        projectCF = family;
                        emptyCF = SchemaUtil.getEmptyColumnFamily(table);
                    }
                }
            }
            MetaDataMutationResult result = connection.getQueryServices().addColumn(tableMetaData, table, properties, colFamiliesForPColumnsToBeAdded, columns);
            try {
                MutationCode code = processMutationResult(schemaName, tableName, result);
                if (code == MutationCode.COLUMN_ALREADY_EXISTS) {
                    addTableToCache(result);
                    if (!ifNotExists) {
                        throw new ColumnAlreadyExistsException(schemaName, tableName, SchemaUtil.findExistingColumn(result.getTable(), columns));
                    }
                    return new MutationState(0, 0, connection);
                }
                // Only update client side cache if we aren't adding a PK column to a table with indexes or
                // transitioning a table from non transactional to transactional.
                // We could update the cache manually then too, it'd just be a pain.
                String fullTableName = SchemaUtil.getTableName(schemaName, tableName);
                long resolvedTimeStamp = TransactionUtil.getResolvedTime(connection, result);
                if (table.getIndexes().isEmpty() || (numPkColumnsAdded == 0 && !nonTxToTx)) {
                    connection.addTable(result.getTable(), resolvedTimeStamp);
                    table = result.getTable();
                } else if (updateCacheFrequency != null) {
                    // Force removal from cache as the update cache frequency has changed
                    // Note that clients outside this JVM won't be affected.
                    connection.removeTable(tenantId, fullTableName, null, resolvedTimeStamp);
                }
                // We only need to do this if the multiTenant transitioned to false
                if (table.getType() == PTableType.TABLE && Boolean.FALSE.equals(multiTenant) && MetaDataUtil.hasViewIndexTable(connection, table.getPhysicalName())) {
                    connection.setAutoCommit(true);
                    MetaDataUtil.deleteViewIndexSequences(connection, table.getPhysicalName(), table.isNamespaceMapped());
                    // commands are run would remove all rows already.
                    if (!connection.getQueryServices().getProps().getBoolean(DROP_METADATA_ATTRIB, DEFAULT_DROP_METADATA)) {
                        Long scn = connection.getSCN();
                        long ts = (scn == null ? result.getMutationTime() : scn);
                        byte[] viewIndexPhysicalName = MetaDataUtil.getViewIndexPhysicalName(table.getPhysicalName().getBytes());
                        PTable viewIndexTable = new PTableImpl(null, SchemaUtil.getSchemaNameFromFullName(viewIndexPhysicalName), SchemaUtil.getTableNameFromFullName(viewIndexPhysicalName), ts, table.getColumnFamilies(), table.isNamespaceMapped(), table.getImmutableStorageScheme(), table.getEncodingScheme(), table.useStatsForParallelization());
                        List<TableRef> tableRefs = Collections.singletonList(new TableRef(null, viewIndexTable, ts, false));
                        MutationPlan plan = new PostDDLCompiler(connection).compile(tableRefs, null, null, Collections.<PColumn>emptyList(), ts);
                        connection.getQueryServices().updateData(plan);
                    }
                }
                if (emptyCF != null) {
                    Long scn = connection.getSCN();
                    connection.setAutoCommit(true);
                    // Delete everything in the column. You'll still be able to do queries at earlier timestamps
                    long ts = (scn == null ? result.getMutationTime() : scn);
                    MutationPlan plan = new PostDDLCompiler(connection).compile(Collections.singletonList(new TableRef(null, table, ts, false)), emptyCF, projectCF == null ? null : Collections.singletonList(projectCF), null, ts);
                    return connection.getQueryServices().updateData(plan);
                }
                return new MutationState(0, 0, connection);
            } catch (ConcurrentTableMutationException e) {
                if (retried) {
                    throw e;
                }
                if (logger.isDebugEnabled()) {
                    logger.debug(LogUtil.addCustomAnnotations("Caught ConcurrentTableMutationException for table " + SchemaUtil.getTableName(schemaName, tableName) + ". Will try again...", connection));
                }
                retried = true;
            }
        }
    } finally {
        connection.setAutoCommit(wasAutoCommit);
    }
}
Also used : LinkedHashSet(java.util.LinkedHashSet) Sets.newLinkedHashSet(com.google.common.collect.Sets.newLinkedHashSet) LinkedHashMap(java.util.LinkedHashMap) HashMap(java.util.HashMap) PostDDLCompiler(org.apache.phoenix.compile.PostDDLCompiler) PhoenixStatement(org.apache.phoenix.jdbc.PhoenixStatement) StatementContext(org.apache.phoenix.compile.StatementContext) PDataType(org.apache.phoenix.schema.types.PDataType) ArrayList(java.util.ArrayList) List(java.util.List) SQLExceptionInfo(org.apache.phoenix.exception.SQLExceptionInfo) ColumnResolver(org.apache.phoenix.compile.ColumnResolver) ColumnDef(org.apache.phoenix.parse.ColumnDef) MutationPlan(org.apache.phoenix.compile.MutationPlan) PUnsignedLong(org.apache.phoenix.schema.types.PUnsignedLong) PLong(org.apache.phoenix.schema.types.PLong) Mutation(org.apache.hadoop.hbase.client.Mutation) QualifierOutOfRangeException(org.apache.phoenix.schema.PTable.QualifierEncodingScheme.QualifierOutOfRangeException) ImmutableStorageScheme(org.apache.phoenix.schema.PTable.ImmutableStorageScheme) MetaDataMutationResult(org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult) Pair(org.apache.hadoop.hbase.util.Pair) PreparedStatement(java.sql.PreparedStatement) RowKeyColumnExpression(org.apache.phoenix.expression.RowKeyColumnExpression) IndexKeyConstraint(org.apache.phoenix.parse.IndexKeyConstraint) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) ColumnDefInPkConstraint(org.apache.phoenix.parse.ColumnDefInPkConstraint) MutationCode(org.apache.phoenix.coprocessor.MetaDataProtocol.MutationCode) PInteger(org.apache.phoenix.schema.types.PInteger) ColumnName(org.apache.phoenix.parse.ColumnName) EncodedCQCounter(org.apache.phoenix.schema.PTable.EncodedCQCounter) MutationState(org.apache.phoenix.execute.MutationState) RowKeyColumnExpression(org.apache.phoenix.expression.RowKeyColumnExpression) Expression(org.apache.phoenix.expression.Expression)

Example 5 with ColumnDef

use of org.apache.phoenix.parse.ColumnDef in project phoenix by apache.

the class CreateTableCompiler method compile.

public MutationPlan compile(CreateTableStatement create) throws SQLException {
    final PhoenixConnection connection = statement.getConnection();
    ColumnResolver resolver = FromCompiler.getResolverForCreation(create, connection);
    PTableType type = create.getTableType();
    PhoenixConnection connectionToBe = connection;
    PTable parentToBe = null;
    ViewType viewTypeToBe = null;
    Scan scan = new Scan();
    final StatementContext context = new StatementContext(statement, resolver, scan, new SequenceManager(statement));
    // TODO: support any statement for a VIEW instead of just a WHERE clause
    ParseNode whereNode = create.getWhereClause();
    String viewStatementToBe = null;
    byte[][] viewColumnConstantsToBe = null;
    BitSet isViewColumnReferencedToBe = null;
    // Check whether column families having local index column family suffix or not if present
    // don't allow creating table.
    // Also validate the default values expressions.
    List<ColumnDef> columnDefs = create.getColumnDefs();
    List<ColumnDef> overideColumnDefs = null;
    PrimaryKeyConstraint pkConstraint = create.getPrimaryKeyConstraint();
    for (int i = 0; i < columnDefs.size(); i++) {
        ColumnDef columnDef = columnDefs.get(i);
        if (columnDef.getColumnDefName().getFamilyName() != null && columnDef.getColumnDefName().getFamilyName().contains(QueryConstants.LOCAL_INDEX_COLUMN_FAMILY_PREFIX)) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNALLOWED_COLUMN_FAMILY).build().buildException();
        }
        // False means we do not need the default (because it evaluated to null)
        if (!columnDef.validateDefault(context, pkConstraint)) {
            if (overideColumnDefs == null) {
                overideColumnDefs = new ArrayList<>(columnDefs);
            }
            overideColumnDefs.set(i, new ColumnDef(columnDef, null));
        }
    }
    if (overideColumnDefs != null) {
        create = new CreateTableStatement(create, overideColumnDefs);
    }
    final CreateTableStatement finalCreate = create;
    if (type == PTableType.VIEW) {
        TableRef tableRef = resolver.getTables().get(0);
        int nColumns = tableRef.getTable().getColumns().size();
        isViewColumnReferencedToBe = new BitSet(nColumns);
        // Used to track column references in a view
        ExpressionCompiler expressionCompiler = new ColumnTrackingExpressionCompiler(context, isViewColumnReferencedToBe);
        parentToBe = tableRef.getTable();
        viewTypeToBe = parentToBe.getViewType() == ViewType.MAPPED ? ViewType.MAPPED : ViewType.UPDATABLE;
        if (whereNode == null) {
            viewStatementToBe = parentToBe.getViewStatement();
        } else {
            whereNode = StatementNormalizer.normalize(whereNode, resolver);
            if (whereNode.isStateless()) {
                throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WHERE_IS_CONSTANT).build().buildException();
            }
            // If our parent has a VIEW statement, combine it with this one
            if (parentToBe.getViewStatement() != null) {
                SelectStatement select = new SQLParser(parentToBe.getViewStatement()).parseQuery().combine(whereNode);
                whereNode = select.getWhere();
            }
            Expression where = whereNode.accept(expressionCompiler);
            if (where != null && !LiteralExpression.isTrue(where)) {
                TableName baseTableName = create.getBaseTableName();
                StringBuilder buf = new StringBuilder();
                whereNode.toSQL(resolver, buf);
                viewStatementToBe = QueryUtil.getViewStatement(baseTableName.getSchemaName(), baseTableName.getTableName(), buf.toString());
            }
            if (viewTypeToBe != ViewType.MAPPED) {
                Long scn = connection.getSCN();
                connectionToBe = (scn != null || tableRef.getTable().isTransactional()) ? connection : // clocks being in sync.
                new PhoenixConnection(// on our connection.
                new DelegateConnectionQueryServices(connection.getQueryServices()) {

                    @Override
                    public void addTable(PTable table, long resolvedTime) throws SQLException {
                        connection.addTable(table, resolvedTime);
                    }
                }, connection, tableRef.getTimeStamp() + 1);
                viewColumnConstantsToBe = new byte[nColumns][];
                ViewWhereExpressionVisitor visitor = new ViewWhereExpressionVisitor(parentToBe, viewColumnConstantsToBe);
                where.accept(visitor);
                // If view is not updatable, viewColumnConstants should be empty. We will still
                // inherit our parent viewConstants, but we have no additional ones.
                viewTypeToBe = visitor.isUpdatable() ? ViewType.UPDATABLE : ViewType.READ_ONLY;
                if (viewTypeToBe != ViewType.UPDATABLE) {
                    viewColumnConstantsToBe = null;
                }
            }
        }
    }
    final ViewType viewType = viewTypeToBe;
    final String viewStatement = viewStatementToBe;
    final byte[][] viewColumnConstants = viewColumnConstantsToBe;
    final BitSet isViewColumnReferenced = isViewColumnReferencedToBe;
    List<ParseNode> splitNodes = create.getSplitNodes();
    final byte[][] splits = new byte[splitNodes.size()][];
    ImmutableBytesWritable ptr = context.getTempPtr();
    ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
    for (int i = 0; i < splits.length; i++) {
        ParseNode node = splitNodes.get(i);
        if (node instanceof BindParseNode) {
            context.getBindManager().addParamMetaData((BindParseNode) node, VARBINARY_DATUM);
        }
        if (node.isStateless()) {
            Expression expression = node.accept(expressionCompiler);
            if (expression.evaluate(null, ptr)) {
                ;
                splits[i] = ByteUtil.copyKeyBytesIfNecessary(ptr);
                continue;
            }
        }
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.SPLIT_POINT_NOT_CONSTANT).setMessage("Node: " + node).build().buildException();
    }
    final MetaDataClient client = new MetaDataClient(connectionToBe);
    final PTable parent = parentToBe;
    return new BaseMutationPlan(context, operation) {

        @Override
        public MutationState execute() throws SQLException {
            try {
                return client.createTable(finalCreate, splits, parent, viewStatement, viewType, viewColumnConstants, isViewColumnReferenced);
            } finally {
                if (client.getConnection() != connection) {
                    client.getConnection().close();
                }
            }
        }

        @Override
        public ExplainPlan getExplainPlan() throws SQLException {
            return new ExplainPlan(Collections.singletonList("CREATE TABLE"));
        }
    };
}
Also used : PhoenixConnection(org.apache.phoenix.jdbc.PhoenixConnection) SQLException(java.sql.SQLException) DelegateConnectionQueryServices(org.apache.phoenix.query.DelegateConnectionQueryServices) PTable(org.apache.phoenix.schema.PTable) SelectStatement(org.apache.phoenix.parse.SelectStatement) BindParseNode(org.apache.phoenix.parse.BindParseNode) ColumnParseNode(org.apache.phoenix.parse.ColumnParseNode) ParseNode(org.apache.phoenix.parse.ParseNode) SQLExceptionInfo(org.apache.phoenix.exception.SQLExceptionInfo) MetaDataClient(org.apache.phoenix.schema.MetaDataClient) ImmutableBytesWritable(org.apache.hadoop.hbase.io.ImmutableBytesWritable) PTableType(org.apache.phoenix.schema.PTableType) CreateTableStatement(org.apache.phoenix.parse.CreateTableStatement) BitSet(java.util.BitSet) ColumnDef(org.apache.phoenix.parse.ColumnDef) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) PrimaryKeyConstraint(org.apache.phoenix.parse.PrimaryKeyConstraint) TableName(org.apache.phoenix.parse.TableName) SQLParser(org.apache.phoenix.parse.SQLParser) KeyValueColumnExpression(org.apache.phoenix.expression.KeyValueColumnExpression) Expression(org.apache.phoenix.expression.Expression) SingleCellColumnExpression(org.apache.phoenix.expression.SingleCellColumnExpression) LiteralExpression(org.apache.phoenix.expression.LiteralExpression) RowKeyColumnExpression(org.apache.phoenix.expression.RowKeyColumnExpression) IsNullExpression(org.apache.phoenix.expression.IsNullExpression) AndExpression(org.apache.phoenix.expression.AndExpression) ComparisonExpression(org.apache.phoenix.expression.ComparisonExpression) Scan(org.apache.hadoop.hbase.client.Scan) ViewType(org.apache.phoenix.schema.PTable.ViewType) TableRef(org.apache.phoenix.schema.TableRef) BindParseNode(org.apache.phoenix.parse.BindParseNode)

Aggregations

ColumnDef (org.apache.phoenix.parse.ColumnDef)5 PrimaryKeyConstraint (org.apache.phoenix.parse.PrimaryKeyConstraint)5 SQLExceptionInfo (org.apache.phoenix.exception.SQLExceptionInfo)4 TableName (org.apache.phoenix.parse.TableName)4 Pair (org.apache.hadoop.hbase.util.Pair)3 MetaDataMutationResult (org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult)3 MutationState (org.apache.phoenix.execute.MutationState)3 Expression (org.apache.phoenix.expression.Expression)3 RowKeyColumnExpression (org.apache.phoenix.expression.RowKeyColumnExpression)3 ColumnDefInPkConstraint (org.apache.phoenix.parse.ColumnDefInPkConstraint)3 ColumnName (org.apache.phoenix.parse.ColumnName)3 IndexKeyConstraint (org.apache.phoenix.parse.IndexKeyConstraint)3 PDataType (org.apache.phoenix.schema.types.PDataType)3 PLong (org.apache.phoenix.schema.types.PLong)3 PUnsignedLong (org.apache.phoenix.schema.types.PUnsignedLong)3 PreparedStatement (java.sql.PreparedStatement)2 HashMap (java.util.HashMap)2 LinkedHashMap (java.util.LinkedHashMap)2 Mutation (org.apache.hadoop.hbase.client.Mutation)2 ColumnResolver (org.apache.phoenix.compile.ColumnResolver)2