in phoenix-core-client/src/main/java/org/apache/phoenix/index/IndexMaintainer.java [469:704]
private IndexMaintainer(final PTable dataTable, final PTable cdcTable, final PTable index,
PhoenixConnection connection) throws SQLException {
this(dataTable.getRowKeySchema(), dataTable.getBucketNum() != null);
this.rowKeyOrderOptimizable = index.rowKeyOrderOptimizable();
this.isMultiTenant = dataTable.isMultiTenant();
this.viewIndexId = index.getViewIndexId() == null ? null : index.getviewIndexIdType().toBytes(index.getViewIndexId());
this.viewIndexIdType = index.getviewIndexIdType();
this.isLocalIndex = index.getIndexType() == IndexType.LOCAL;
this.isUncovered = index.getIndexType() == IndexType.UNCOVERED_GLOBAL;
this.encodingScheme = index.getEncodingScheme();
this.isCDCIndex = CDCUtil.isCDCIndex(index);
// null check for b/w compatibility
this.encodingScheme = index.getEncodingScheme() == null ? QualifierEncodingScheme.NON_ENCODED_QUALIFIERS : index.getEncodingScheme();
this.immutableStorageScheme = index.getImmutableStorageScheme() == null ? ImmutableStorageScheme.ONE_CELL_PER_COLUMN : index.getImmutableStorageScheme();
this.dataEncodingScheme = dataTable.getEncodingScheme() == null ? QualifierEncodingScheme.NON_ENCODED_QUALIFIERS : dataTable.getEncodingScheme();
this.dataImmutableStorageScheme = dataTable.getImmutableStorageScheme() == null ? ImmutableStorageScheme.ONE_CELL_PER_COLUMN : dataTable.getImmutableStorageScheme();
this.nDataTableSaltBuckets = isDataTableSalted ? dataTable.getBucketNum() : PTable.NO_SALTING;
byte[] indexTableName = index.getPhysicalName().getBytes();
// Use this for the nDataSaltBuckets as we need this for local indexes
// TODO: persist nDataSaltBuckets separately, but maintain b/w compat.
Integer nIndexSaltBuckets = isLocalIndex ? dataTable.getBucketNum() : index.getBucketNum();
boolean indexWALDisabled = index.isWALDisabled();
int indexPosOffset = (index.getBucketNum() == null ? 0 : 1) + (this.isMultiTenant ? 1 : 0) + (this.viewIndexId == null ? 0 : 1);
int nIndexColumns = index.getColumns().size() - indexPosOffset;
int nIndexPKColumns = index.getPKColumns().size() - indexPosOffset;
// number of expressions that are indexed that are not present in the row key of the data table
int indexedExpressionCount = 0;
for (int i = indexPosOffset; i<index.getPKColumns().size();i++) {
PColumn indexColumn = index.getPKColumns().get(i);
String indexColumnName = indexColumn.getName().getString();
String dataFamilyName = IndexUtil.getDataColumnFamilyName(indexColumnName);
String dataColumnName = IndexUtil.getDataColumnName(indexColumnName);
try {
PColumn dataColumn = dataFamilyName.equals("") ? dataTable.getColumnForColumnName(dataColumnName) : dataTable.getColumnFamily(dataFamilyName).getPColumnForColumnName(dataColumnName);
if (SchemaUtil.isPKColumn(dataColumn))
continue;
} catch (ColumnNotFoundException e) {
// This column must be an expression
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
indexedExpressionCount++;
}
int dataPosOffset = (isDataTableSalted ? 1 : 0) + (this.isMultiTenant ? 1 : 0);
// For indexes on views, we need to remember which data columns are "constants"
// These are the values in a VIEW where clause. For these, we don't put them in the
// index, as they're the same for every row in the index. The data table can be
// either a VIEW or PROJECTED
List<PColumn>dataPKColumns = dataTable.getPKColumns();
this.indexDataColumnCount = dataPKColumns.size();
PTable parentTable = dataTable;
// We need to get the PK column for the table on which the index is created
if (!dataTable.getName().equals(cdcTable != null
? cdcTable.getParentName() : index.getParentName())) {
try {
String tenantId = (index.getTenantId() != null) ?
index.getTenantId().getString() : null;
parentTable = connection.getTable(tenantId, index.getParentName().getString());
this.indexDataColumnCount = parentTable.getPKColumns().size();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
this.parentTableType = parentTable.getType();
int indexPkColumnCount = this.indexDataColumnCount +
indexedExpressionCount - (this.isDataTableSalted ? 1 : 0) - (this.isMultiTenant ? 1 : 0);
this.rowKeyMetaData = newRowKeyMetaData(indexPkColumnCount);
BitSet bitSet = this.rowKeyMetaData.getViewConstantColumnBitSet();
int nDataPKColumns = this.indexDataColumnCount - dataPosOffset;
for (int i = dataPosOffset; i < dataPKColumns.size(); i++) {
PColumn dataPKColumn = dataPKColumns.get(i);
if (dataPKColumn.getViewConstant() != null) {
bitSet.set(i);
nDataPKColumns--;
}
}
this.indexTableName = indexTableName;
this.indexedColumnTypes = Lists.<PDataType>newArrayListWithExpectedSize(nIndexPKColumns-nDataPKColumns);
this.indexedExpressions = Lists.newArrayListWithExpectedSize(nIndexPKColumns-nDataPKColumns);
this.coveredColumnsMap = Maps.newHashMapWithExpectedSize(nIndexColumns - nIndexPKColumns);
this.nIndexSaltBuckets = nIndexSaltBuckets == null ? PTable.NO_SALTING : nIndexSaltBuckets;
this.dataEmptyKeyValueCF = SchemaUtil.getEmptyColumnFamily(dataTable);
this.emptyKeyValueCFPtr = SchemaUtil.getEmptyColumnFamilyPtr(index);
this.nDataCFs = dataTable.getColumnFamilies().size();
this.indexWALDisabled = indexWALDisabled;
// TODO: check whether index is immutable or not. Currently it's always false so checking
// data table is with immutable rows or not.
this.immutableRows = dataTable.isImmutableRows();
int indexColByteSize = 0;
ColumnResolver resolver = null;
List<ParseNode> parseNodes = new ArrayList<ParseNode>(1);
UDFParseNodeVisitor visitor = new UDFParseNodeVisitor();
for (int i = indexPosOffset; i < index.getPKColumns().size(); i++) {
PColumn indexColumn = index.getPKColumns().get(i);
String expressionStr = IndexUtil.getIndexColumnExpressionStr(indexColumn);
try {
ParseNode parseNode = SQLParser.parseCondition(expressionStr);
parseNode.accept(visitor);
parseNodes.add(parseNode);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
try {
resolver = FromCompiler.getResolver(connection, new TableRef(dataTable), visitor.getUdfParseNodes());
} catch (SQLException e) {
throw new RuntimeException(e); // Impossible
}
StatementContext context = new StatementContext(new PhoenixStatement(connection), resolver);
this.indexedColumnsInfo = Sets.newHashSetWithExpectedSize(nIndexColumns - nIndexPKColumns);
IndexExpressionCompiler expressionIndexCompiler = new IndexExpressionCompiler(context);
for (int i = indexPosOffset; i < index.getPKColumns().size(); i++) {
PColumn indexColumn = index.getPKColumns().get(i);
int indexPos = i - indexPosOffset;
Expression expression = null;
try {
expressionIndexCompiler.reset();
expression = parseNodes.get(indexPos).accept(expressionIndexCompiler);
} catch (SQLException e) {
throw new RuntimeException(e); // Impossible
}
if ( expressionIndexCompiler.getColumnRef()!=null ) {
// get the column of the data column that corresponds to this index column
PColumn column = IndexUtil.getDataColumn(dataTable, indexColumn.getName().getString());
boolean isPKColumn = SchemaUtil.isPKColumn(column);
if (isPKColumn) {
int dataPkPos = dataTable.getPKColumns().indexOf(column) - (dataTable.getBucketNum() == null ? 0 : 1) - (this.isMultiTenant ? 1 : 0);
this.rowKeyMetaData.setIndexPkPosition(dataPkPos, indexPos);
indexedColumnsInfo.add(new Pair<>((String)null, column.getName().getString()));
} else {
indexColByteSize += column.getDataType().isFixedWidth() ? SchemaUtil.getFixedByteSize(column) : ValueSchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
try {
// Surround constant with cast so that we can still know the original type. Otherwise, if we lose the type,
// (for example when VARCHAR becomes CHAR), it can lead to problems in the type translation we do between data tables and indexes.
if (column.isNullable() && ExpressionUtil.isConstant(expression)) {
expression = CoerceExpression.create(expression, indexColumn.getDataType());
}
this.indexedExpressions.add(expression);
indexedColumnsInfo.add(new Pair<>(column.getFamilyName().getString(), column.getName().getString()));
} catch (SQLException e) {
throw new RuntimeException(e); // Impossible
}
}
}
else {
indexColByteSize += expression.getDataType().isFixedWidth() ? SchemaUtil.getFixedByteSize(expression) : ValueSchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
this.indexedExpressions.add(expression);
KeyValueExpressionVisitor kvVisitor = new KeyValueExpressionVisitor() {
@Override
public Void visit(KeyValueColumnExpression colExpression) {
return addDataColInfo(dataTable, colExpression);
}
@Override
public Void visit(SingleCellColumnExpression expression) {
return addDataColInfo(dataTable, expression);
}
private Void addDataColInfo(final PTable dataTable, Expression expression) {
Preconditions.checkArgument(expression instanceof SingleCellColumnExpression
|| expression instanceof KeyValueColumnExpression);
KeyValueColumnExpression colExpression = null;
if (expression instanceof SingleCellColumnExpression) {
colExpression =
((SingleCellColumnExpression) expression).getKeyValueExpression();
} else {
colExpression = ((KeyValueColumnExpression) expression);
}
byte[] cf = colExpression.getColumnFamily();
byte[] cq = colExpression.getColumnQualifier();
try {
PColumn dataColumn =
cf == null ? dataTable.getColumnForColumnQualifier(null, cq)
: dataTable.getColumnFamily(cf)
.getPColumnForColumnQualifier(cq);
if (dataColumn == null) {
if (Bytes.compareTo(cf, dataEmptyKeyValueCF) == 0
&& Bytes.compareTo(cq, EncodedColumnsUtil.getEmptyKeyValueInfo(dataEncodingScheme).getFirst()) == 0) {
return null;
} else {
throw new ColumnNotFoundException(dataTable.getSchemaName().getString(),
dataTable.getTableName().getString(), Bytes.toString(cf), Bytes.toString(cq));
}
} else {
indexedColumnsInfo.add(new Pair<>(dataColumn.getFamilyName()
.getString(), dataColumn.getName().getString()));
}
} catch (ColumnNotFoundException | ColumnFamilyNotFoundException
| AmbiguousColumnException e) {
if (dataTable.hasOnlyPkColumns()) {
return null;
}
throw new RuntimeException(e);
}
return null;
}
};
expression.accept(kvVisitor);
}
// set the sort order of the expression correctly
if (indexColumn.getSortOrder() == SortOrder.DESC) {
this.rowKeyMetaData.getDescIndexColumnBitSet().set(indexPos);
}
}
this.estimatedExpressionSize = expressionIndexCompiler.getTotalNodeCount() * ESTIMATED_EXPRESSION_SIZE;
for (int i = 0; i < index.getColumnFamilies().size(); i++) {
PColumnFamily family = index.getColumnFamilies().get(i);
for (PColumn indexColumn : family.getColumns()) {
PColumn dataColumn = IndexUtil.getDataColumnOrNull(dataTable, indexColumn.getName().getString());
// This can happen during deletion where we don't need covered columns
if (dataColumn != null) {
byte[] dataColumnCq = dataColumn.getColumnQualifierBytes();
byte[] indexColumnCq = indexColumn.getColumnQualifierBytes();
this.coveredColumnsMap.put(new ColumnReference(dataColumn.getFamilyName().getBytes(), dataColumnCq),
new ColumnReference(indexColumn.getFamilyName().getBytes(), indexColumnCq));
}
}
}
this.estimatedIndexRowKeyBytes = estimateIndexRowKeyByteSize(indexColByteSize);
this.logicalIndexName = index.getName().getString();
if (index.getIndexWhere() != null) {
this.indexWhere = index.getIndexWhereExpression(connection);
this.indexWhereColumns = index.getIndexWhereColumns(connection);
}
initCachedState();
}