public MetaDataMutationResult validateWithChildViews()

in phoenix-core-server/src/main/java/org/apache/phoenix/coprocessor/AddColumnMutator.java [96:267]


    public MetaDataMutationResult validateWithChildViews(PTable table, List<PTable> childViews,
                                                         List<Mutation> tableMetadata,
                                                         byte[] schemaName, byte[] tableName)
            throws SQLException {
        // Disallow if trying to switch tenancy of a table that has views
        if (!childViews.isEmpty() && switchAttribute(table.isMultiTenant(),
                tableMetadata, MULTI_TENANT_BYTES)) {
            return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION
                    , EnvironmentEdgeManager.currentTimeMillis(), null);
        }

        List<Put> columnPutsForBaseTable =
                Lists.newArrayListWithExpectedSize(tableMetadata.size());
        boolean salted = table.getBucketNum()!=null;
        // Isolate the puts relevant to adding columns
        for (Mutation m : tableMetadata) {
            if (m instanceof Put) {
                byte[][] rkmd = new byte[5][];
                int pkCount = getVarChars(m.getRow(), rkmd);
                // check if this put is for adding a column
                if (pkCount > COLUMN_NAME_INDEX && rkmd[COLUMN_NAME_INDEX] != null
                        && rkmd[COLUMN_NAME_INDEX].length > 0
                        && Bytes.compareTo(schemaName, rkmd[SCHEMA_NAME_INDEX]) == 0
                        && Bytes.compareTo(tableName, rkmd[TABLE_NAME_INDEX]) == 0) {
                    columnPutsForBaseTable.add((Put)m);
                }
            }
        }
        for (PTable view : childViews) {
            /*
             * Disallow adding columns to a base table with APPEND_ONLY_SCHEMA since this
             * creates a gap in the column positions for every view (PHOENIX-4737).
             */
            if (!columnPutsForBaseTable.isEmpty() && view.isAppendOnlySchema()) {
                return new MetaDataMutationResult(
                        MutationCode.UNALLOWED_TABLE_MUTATION,
                        EnvironmentEdgeManager.currentTimeMillis(), null);
            }

            // add the new columns to the child view
            List<PColumn> viewPkCols = new ArrayList<>(view.getPKColumns());
            // remove salted column
            if (salted) {
                viewPkCols.remove(0);
            }
            // remove pk columns that are present in the parent
            viewPkCols.removeAll(table.getPKColumns());
            boolean addedPkColumn = false;
            for (Put columnToBeAdded : columnPutsForBaseTable) {
                PColumn existingViewColumn = null;
                byte[][] rkmd = new byte[5][];
                getVarChars(columnToBeAdded.getRow(), rkmd);
                String columnName = Bytes.toString(rkmd[COLUMN_NAME_INDEX]);
                String columnFamily =
                        rkmd[FAMILY_NAME_INDEX] == null ? null
                                : Bytes.toString(rkmd[FAMILY_NAME_INDEX]);
                try {
                    existingViewColumn =
                            columnFamily == null ? view.getColumnForColumnName(columnName)
                                    : view.getColumnFamily(columnFamily)
                                    .getPColumnForColumnName(columnName);
                } catch (ColumnFamilyNotFoundException e) {
                    // ignore since it means that the column family is not present for the column to
                    // be added.
                } catch (ColumnNotFoundException e) {
                    // ignore since it means the column is not present in the view
                }

                boolean isCurrColumnToBeAddPkCol = columnFamily == null;
                addedPkColumn |= isCurrColumnToBeAddPkCol;
                if (existingViewColumn != null) {
                    if (EncodedColumnsUtil.usesEncodedColumnNames(table)
                            && !SchemaUtil.isPKColumn(existingViewColumn)) {
                        /*
                         * If the column already exists in a view, then we cannot add the column to
                         * the base table. The reason is subtle and is as follows: consider the case
                         * where a table has two views where both the views have the same key value
                         * column KV. Now, we dole out encoded column qualifiers for key value
                         * columns in views by using the counters stored in the base physical table.
                         * So the KV column can have different column qualifiers for the two views.
                         * For example, 11 for VIEW1 and 12 for VIEW2. This naturally extends to
                         * rows being inserted using the two views having different column
                         * qualifiers for the column named KV. Now, when an attempt is made to add
                         * column KV to the base table, we cannot decide which column qualifier
                         * should that column be assigned. It cannot be a number different than 11
                         * or 12 since a query like SELECT KV FROM BASETABLE would return null for
                         * KV which is incorrect since column KV is present in rows inserted from
                         * the two views. We cannot use 11 or 12 either because we will then
                         * incorrectly return value of KV column inserted using only one view.
                         */
                        return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                                EnvironmentEdgeManager.currentTimeMillis(), table);
                    }
                    // Validate data type is same
                    int baseColumnDataType =
                            getInteger(columnToBeAdded, TABLE_FAMILY_BYTES, DATA_TYPE_BYTES);
                    if (baseColumnDataType != existingViewColumn.getDataType().getSqlType()) {
                        return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                                EnvironmentEdgeManager.currentTimeMillis(), table);
                    }

                    // Validate max length is same
                    int maxLength =
                            getInteger(columnToBeAdded, TABLE_FAMILY_BYTES, COLUMN_SIZE_BYTES);
                    int existingMaxLength =
                            existingViewColumn.getMaxLength() == null ? 0
                                    : existingViewColumn.getMaxLength();
                    if (maxLength != existingMaxLength) {
                        return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                                EnvironmentEdgeManager.currentTimeMillis(), table);
                    }

                    // Validate scale is same
                    int scale =
                            getInteger(columnToBeAdded, TABLE_FAMILY_BYTES, DECIMAL_DIGITS_BYTES);
                    int existingScale =
                            existingViewColumn.getScale() == null ? 0
                                    : existingViewColumn.getScale();
                    if (scale != existingScale) {
                        return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                                EnvironmentEdgeManager.currentTimeMillis(), table);
                    }

                    // Validate sort order is same
                    int sortOrder =
                            getInteger(columnToBeAdded, TABLE_FAMILY_BYTES, SORT_ORDER_BYTES);
                    if (sortOrder != existingViewColumn.getSortOrder().getSystemValue()) {
                        return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                                EnvironmentEdgeManager.currentTimeMillis(), table);
                    }

                    // if the column to be added to the base table is a pk column, then we need to
                    // validate that the key slot position is the same
                    if (isCurrColumnToBeAddPkCol) {
                        List<Cell> keySeqCells =
                                columnToBeAdded.get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
                                        PhoenixDatabaseMetaData.KEY_SEQ_BYTES);
                        if (keySeqCells != null && keySeqCells.size() > 0) {
                            Cell cell = keySeqCells.get(0);
                            int keySeq =
                                    PSmallint.INSTANCE.getCodec().decodeInt(cell.getValueArray(),
                                            cell.getValueOffset(), SortOrder.getDefault());
                            // we need to take into account the columns inherited from the base table
                            // if the table is salted we don't include the salted column (which is
                            // present in getPKColumns())
                            int pkPosition = SchemaUtil.getPKPosition(view, existingViewColumn)
                                    + 1 - (salted ? 1 : 0);
                            if (pkPosition != keySeq) {
                                return new MetaDataMutationResult(
                                        MutationCode.UNALLOWED_TABLE_MUTATION,
                                        EnvironmentEdgeManager.currentTimeMillis(),
                                        table);
                            }
                        }
                    }
                }
                if (existingViewColumn!=null && isCurrColumnToBeAddPkCol) {
                    viewPkCols.remove(existingViewColumn);
                }
            }
            /*
             * Allow adding a pk columns to base table : 1. if all the view pk columns are exactly
             * the same as the base table pk columns 2. if we are adding all the existing view pk
             * columns to the base table
             */
            if (addedPkColumn && !viewPkCols.isEmpty()) {
                return new MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
                        EnvironmentEdgeManager.currentTimeMillis(), table);
            }
        }
        return null;
    }