prependRegionStartKey ?()

in phoenix-core-client/src/main/java/org/apache/phoenix/index/IndexMaintainer.java [727:894]


                prependRegionStartKey ? (regionStartKey.length != 0 ? regionStartKey.length
                        : regionEndKey.length) : 0; 
        TrustedByteArrayOutputStream stream = new TrustedByteArrayOutputStream(estimatedIndexRowKeyBytes + (prependRegionStartKey ? prefixKeyLength : 0));
        DataOutput output = new DataOutputStream(stream);

        try {
            // For local indexes, we must prepend the row key with the start region key
            if (prependRegionStartKey) {
                if (regionStartKey.length == 0) {
                    output.write(new byte[prefixKeyLength]);
                } else {
                    output.write(regionStartKey);
                }
            }
            if (isIndexSalted) {
                output.write(0); // will be set at end to index salt byte
            }
            // The dataRowKeySchema includes the salt byte field,
            // so we must adjust for that here.
            int dataPosOffset = isDataTableSalted ? 1 : 0 ;
            BitSet viewConstantColumnBitSet = this.rowKeyMetaData.getViewConstantColumnBitSet();
            int nIndexedColumns = getIndexPkColumnCount() - getNumViewConstants();
            int[][] dataRowKeyLocator = new int[2][nIndexedColumns];
            // Skip data table salt byte
            int maxRowKeyOffset = rowKeyPtr.getOffset() + rowKeyPtr.getLength();
            dataRowKeySchema.iterator(rowKeyPtr, ptr, dataPosOffset);
            
            if (viewIndexId != null) {
                output.write(viewIndexId);
            }
            
            if (isMultiTenant) {
                dataRowKeySchema.next(ptr, dataPosOffset, maxRowKeyOffset);
                output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                if (!dataRowKeySchema.getField(dataPosOffset).getDataType().isFixedWidth()) {
                    output.write(SchemaUtil.getSeparatorBytes(
                        dataRowKeySchema.getField(dataPosOffset).getDataType(),
                        rowKeyOrderOptimizable,
                        ptr.getLength() == 0,
                        dataRowKeySchema.getField(dataPosOffset).getSortOrder()));
                }
                dataPosOffset++;
            }
            
            // Write index row key
            for (int i = dataPosOffset; i < indexDataColumnCount; i++) {
                Boolean hasValue=dataRowKeySchema.next(ptr, i, maxRowKeyOffset);
                // Ignore view constants from the data table, as these
                // don't need to appear in the index (as they're the
                // same for all rows in this index)
                if (!viewConstantColumnBitSet.get(i) || isIndexOnBaseTable()) {
                    int pos = rowKeyMetaData.getIndexPkPosition(i-dataPosOffset);
                    if (Boolean.TRUE.equals(hasValue)) {
                        dataRowKeyLocator[0][pos] = ptr.getOffset();
                        dataRowKeyLocator[1][pos] = ptr.getLength();
                    } else {
                        dataRowKeyLocator[0][pos] = 0;
                        dataRowKeyLocator[1][pos] = 0;
                    }
                } 
            }
            BitSet descIndexColumnBitSet = rowKeyMetaData.getDescIndexColumnBitSet();
            Iterator<Expression> expressionIterator = indexedExpressions.iterator();
            int trailingVariableWidthColumnNum = 0;
            PDataType[] indexedColumnDataTypes = new PDataType[nIndexedColumns];
            for (int i = 0; i < nIndexedColumns; i++) {
                PDataType dataColumnType;
                boolean isNullable;
                SortOrder dataSortOrder;
                if (dataPkPosition[i] == EXPRESSION_NOT_PRESENT) {
                	Expression expression = expressionIterator.next();
                	dataColumnType = expression.getDataType();
                	dataSortOrder = expression.getSortOrder();
                    isNullable = expression.isNullable();
                    if (expression instanceof PartitionIdFunction) {
                        if (i != 0) {
                            throw new DoNotRetryIOException("PARTITION_ID() has to be the prefix "
                            + "of the index row key!");
                        } else if (!isCDCIndex) {
                            throw new DoNotRetryIOException("PARTITION_ID() should be used only for"
                                    + " CDC Indexes!");
                        }
                        ptr.set(encodedRegionName);
                    } else {
                        expression.evaluate(new ValueGetterTuple(valueGetter, ts), ptr);
                    }
                }
                else {
                    Field field = dataRowKeySchema.getField(dataPkPosition[i]);
                    dataColumnType = field.getDataType();
                    ptr.set(rowKeyPtr.get(), dataRowKeyLocator[0][i], dataRowKeyLocator[1][i]);
                    dataSortOrder = field.getSortOrder();
                    isNullable = field.isNullable();
                }
                boolean isDataColumnInverted = dataSortOrder != SortOrder.ASC;
                PDataType indexColumnType = IndexUtil.getIndexColumnDataType(isNullable, dataColumnType);
                indexedColumnDataTypes[i] = indexColumnType;
                boolean isBytesComparable = dataColumnType.isBytesComparableWith(indexColumnType);
                boolean isIndexColumnDesc = descIndexColumnBitSet.get(i);
                if (isBytesComparable && isDataColumnInverted == isIndexColumnDesc) {
                    output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                } else {
                    if (!isBytesComparable)  {
                        indexColumnType.coerceBytes(ptr, dataColumnType, dataSortOrder, SortOrder.getDefault());
                    }
                    if (isDataColumnInverted != isIndexColumnDesc) {
                        writeInverted(ptr.get(), ptr.getOffset(), ptr.getLength(), output);
                    } else {
                        output.write(ptr.get(), ptr.getOffset(), ptr.getLength());
                    }
                }

                if (!indexColumnType.isFixedWidth()) {
                    output.write(
                        SchemaUtil.getSeparatorBytes(indexColumnType,
                            rowKeyOrderOptimizable,
                            ptr.getLength() == 0,
                            isIndexColumnDesc ? SortOrder.DESC : SortOrder.ASC));
                    trailingVariableWidthColumnNum++;
                } else {
                    trailingVariableWidthColumnNum = 0;
                }
            }
            byte[] indexRowKey = stream.getBuffer();
            // Remove trailing nulls
            int length = stream.size();
            int minLength = length - maxTrailingNulls;
            // The existing code does not eliminate the separator if the data type is not nullable. It not clear why.
            // The actual bug is in the calculation of maxTrailingNulls with view indexes. So, in order not to impact some other cases, we should keep minLength check here.
            int indexColumnIdx = nIndexedColumns - 1;
            while (trailingVariableWidthColumnNum > 0 && length > minLength) {
                if (indexColumnIdx < 0) {
                    break;
                }
                if (indexedColumnDataTypes[indexColumnIdx] != PVarbinaryEncoded.INSTANCE) {
                    if (indexRowKey[length - 1] == QueryConstants.SEPARATOR_BYTE) {
                        length--;
                    } else {
                        break;
                    }
                } else {
                    byte[] sepBytes = QueryConstants.VARBINARY_ENCODED_SEPARATOR_BYTES;
                    if (length >= 2 && indexRowKey[length - 1] == sepBytes[1]
                        && indexRowKey[length - 2] == sepBytes[0]) {
                        length -= 2;
                    } else {
                        break;
                    }
                }
                trailingVariableWidthColumnNum--;
                indexColumnIdx--;
            }

            if (isIndexSalted) {
                // Set salt byte
                byte saltByte = SaltingUtil.getSaltingByte(indexRowKey, SaltingUtil.NUM_SALTING_BYTES, length-SaltingUtil.NUM_SALTING_BYTES, nIndexSaltBuckets);
                indexRowKey[0] = saltByte;
            }
            return indexRowKey.length == length ? indexRowKey : Arrays.copyOf(indexRowKey, length);
        } catch (IOException e) {
            throw new RuntimeException(e); // Impossible
        } finally {
            try {
                stream.close();
            } catch (IOException e) {
                throw new RuntimeException(e); // Impossible
            }
        }