Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PHOENIX-7107 Add support for indexing on SYSTEM.CATALOG table #2048

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package org.apache.phoenix.compile;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellComparator;
import org.apache.hadoop.hbase.client.Scan;
Expand All @@ -32,6 +33,7 @@
import org.apache.phoenix.jdbc.PhoenixStatement;
import org.apache.phoenix.jdbc.PhoenixStatement.Operation;
import org.apache.phoenix.parse.CreateIndexStatement;
import org.apache.phoenix.parse.NamedTableNode;
import org.apache.phoenix.parse.ParseNode;
import org.apache.phoenix.parse.StatelessTraverseAllParseNodeVisitor;
import org.apache.phoenix.parse.SubqueryParseNode;
Expand Down Expand Up @@ -73,6 +75,10 @@
import java.util.Iterator;
import java.util.List;

import static org.apache.phoenix.query.QueryServices.DEFAULT_SYSTEM_KEEP_DELETED_CELLS_ATTRIB;
import static org.apache.phoenix.query.QueryServices.SYSTEM_CATALOG_INDEXES_ENABLED;
import static org.apache.phoenix.query.QueryServicesOptions.DEFAULT_SYSTEM_CATALOG_INDEXES_ENABLED;

public class CreateIndexCompiler {
private final PhoenixStatement statement;
private final Operation operation;
Expand Down Expand Up @@ -230,6 +236,7 @@ private void verifyIndexWhere(ParseNode indexWhere, StatementContext context,
}
public MutationPlan compile(final CreateIndexStatement create) throws SQLException {
final PhoenixConnection connection = statement.getConnection();
verifyDataTable(connection, create.getTable());
final ColumnResolver resolver
= FromCompiler.getResolverForCreateIndex(
create, connection, create.getUdfParseNodes());
Expand Down Expand Up @@ -278,4 +285,35 @@ public ExplainPlan getExplainPlan() throws SQLException {

};
}

/**
* Helper method to validate CREATE INDEX statements on SYSTEM tables.
* 1. Pass if scheme name not provided, assumption is - it not a SYSTEM table.
* 2. Fail if SYSTEM_CATALOG_INDEXES_ENABLED not enabled
* 3. Fail if table other than SYSTEM.CATALOG
*
* @param connection
* @param table
* @throws SQLException
*/
private void verifyDataTable(PhoenixConnection connection, NamedTableNode table) throws SQLException {
Configuration conf = connection.getQueryServices().getConfiguration();
boolean catalogIndexesEnabled = conf.getBoolean(SYSTEM_CATALOG_INDEXES_ENABLED, DEFAULT_SYSTEM_CATALOG_INDEXES_ENABLED);

TableName tableName = table.getName();
if (tableName.getSchemaName() == null) {
return;
}
if (tableName.getSchemaName().equalsIgnoreCase(PhoenixDatabaseMetaData.SYSTEM_SCHEMA_NAME) &&
!catalogIndexesEnabled) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.SYSTEM_TABLE_INDEXES_NOT_ENABLED).
build().buildException();
}

if (tableName.getSchemaName().equalsIgnoreCase(PhoenixDatabaseMetaData.SYSTEM_SCHEMA_NAME) &&
!tableName.getTableName().equalsIgnoreCase(PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE)) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_INDEX_SYSTEM_TABLE).
build().buildException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ public SQLException newException(SQLExceptionInfo info) {
" Index where clause cannot include a subquery."),
CANNOT_EVALUATE_INDEX_WHERE(304, "23102",
"Invalid index where clause. It cannot be evaluated on a data table row."),
SYSTEM_TABLE_INDEXES_NOT_ENABLED(305, "23103",
"Invalid index on table. Indexes on SYSTEM tables are not enabled."),
CANNOT_INDEX_SYSTEM_TABLE(306, "23104",
"Invalid index on table. SYSTEM Indexes can only be on SYSTEM.CATALOG table."),
/**
* Invalid Cursor State (errorcode 04, sqlstate 24)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,8 @@ public enum ExpressionType {
BsonValueFunction(BsonValueFunction.class),
PartitionIdFunction(PartitionIdFunction.class),
DecodeBinaryFunction(DecodeBinaryFunction.class),
EncodeBinaryFunction(EncodeBinaryFunction.class);
EncodeBinaryFunction(EncodeBinaryFunction.class),
DecodeViewIdFunction(DecodeViewIndexIdFunction.class);

ExpressionType(Class<? extends Expression> clazz) {
this.clazz = clazz;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.phoenix.expression.function;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
import org.apache.phoenix.expression.Determinism;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.KeyValueColumnExpression;
import org.apache.phoenix.parse.FunctionParseNode;
import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
import org.apache.phoenix.parse.DecodeViewIndexIdParseNode;
import org.apache.phoenix.parse.PhoenixRowTimestampParseNode;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.tuple.Tuple;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PInteger;
import org.apache.phoenix.schema.types.PLong;
import org.apache.phoenix.schema.types.PSmallint;

import java.sql.Types;
import java.util.List;

import static org.apache.phoenix.util.ViewIndexIdRetrieveUtil.NULL_DATA_TYPE_VALUE;
import static org.apache.phoenix.util.ViewIndexIdRetrieveUtil.VIEW_INDEX_ID_BIGINT_TYPE_PTR_LEN;

/**
Copy link
Contributor

@kadirozde kadirozde Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments here are for the PHOENIX_ROW_TIMESTAMP() function. Let's update them.

* Function to return the ViewIndexId value based on the ViewIndexIDDataType field.
* Can also be used in sql predicates.
* THe ViewIndexId field value needs to be interpreted based on the type specified in the
* ViewIndexIdDataType field
This is how the various client created view index id's look like:
client VIEW_INDEX_ID(Cell number of bytes) VIEW_INDEX_ID_DATA_TYPE
pre-4.15 2 bytes NULL
post-4.15[config smallint] 2 bytes 5(smallint)
post-4.15[config bigint] 8 bytes -5(bigint)

VIEW_INDEX_ID_DATA_TYPE, VIEW_INDEX_ID(Cell representation of the data)
NULL, SMALLINT -> RETRIEVE AND CONVERT TO BIGINT
SMALLINT, SMALLINT -> RETRIEVE AND CONVERT TO BIGINT
BIGINT, BIGINT -> DO NOT CONVERT

*/
@BuiltInFunction(name = DecodeViewIndexIdFunction.NAME,
nodeClass= DecodeViewIndexIdParseNode.class,
args = {@FunctionParseNode.Argument(allowedTypes = { PLong.class}),
@FunctionParseNode.Argument(allowedTypes = { PInteger.class})
})
public class DecodeViewIndexIdFunction extends ScalarFunction {

public static final String NAME = "DECODE_VIEW_INDEX_ID";

public DecodeViewIndexIdFunction() {
}

/**
* @param children VIEW_INDEX_ID and VIEW_INDEX_ID_DATA_TYPE expressions
*/
public DecodeViewIndexIdFunction(List<Expression> children) {
super(children);

// It takes 2 parameters - VIEW_INDEX_ID, VIEW_INDEX_ID_DATA_TYPE.
if ((children.size() != 2) || !children.get(0).getClass().isAssignableFrom(
KeyValueColumnExpression.class) || !children.get(1).getClass().isAssignableFrom(
KeyValueColumnExpression.class)) {
throw new IllegalArgumentException(
"DecodeViewIndexIdFunction should only have a "
+ "VIEW_INDEX_ID and a VIEW_INDEX_ID_DATA_TYPE key value expression."
);
}
if (!(children.get(0).getDataType().equals(PLong.INSTANCE))) {
throw new IllegalArgumentException(
"DecodeViewIndexIdFunction should have an "
+ "VIEW_INDEX_ID key value expression of type PLong"
);
}

if (!(children.get(1).getDataType().equals(PInteger.INSTANCE))) {
throw new IllegalArgumentException(
"DecodeViewIndexIdFunction should have an "
+ "VIEW_INDEX_ID_DATA_TYPE key value expression of type PLong"
);
}
}

@Override
public String getName() {
return NAME;
}

@Override
public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
if (tuple == null) {
return false;
}

byte[] viewIndexIdCF = ((KeyValueColumnExpression) children.get(0)).getColumnFamily();
byte[] viewIndexIdCQ = ((KeyValueColumnExpression) children.get(0)).getColumnQualifier();
byte[] viewIndexIdTypeCF = ((KeyValueColumnExpression) children.get(1)).getColumnFamily();
byte[] viewIndexIdTypeCQ = ((KeyValueColumnExpression) children.get(1)).getColumnQualifier();

Cell viewIndexIdCell = tuple.getValue(viewIndexIdCF, viewIndexIdCQ);
Cell viewIndexIdDataTypeCell = tuple.getValue(viewIndexIdTypeCF, viewIndexIdTypeCQ);


/*
This is combination of diff client created view index looks like:
client VIEW_INDEX_ID(Cell number of bytes) VIEW_INDEX_ID_DATA_TYPE
pre-4.15 2 bytes NULL
post-4.15[config smallint] 2 bytes 5(smallint)
post-4.15[config bigint] 8 bytes -5(bigint)

VIEW_INDEX_ID_DATA_TYPE, VIEW_INDEX_ID(Cell representation of the data)
NULL, SMALLINT -> RETRIEVE AND CONVERT TO BIGINT
SMALLINT, SMALLINT -> RETRIEVE AND CONVERT TO BIGINT
BIGINT, BIGINT -> DO NOT CONVERT

*/

if (viewIndexIdCell != null) {
int type = NULL_DATA_TYPE_VALUE;
if (viewIndexIdDataTypeCell != null) {
type = (Integer) PInteger.INSTANCE.toObject(
viewIndexIdDataTypeCell.getValueArray(),
viewIndexIdDataTypeCell.getValueOffset(),
viewIndexIdDataTypeCell.getValueLength(),
PInteger.INSTANCE,
SortOrder.ASC);
}

ImmutableBytesWritable columnValue =
new ImmutableBytesWritable(CellUtil.cloneValue(viewIndexIdCell));
if ((type == NULL_DATA_TYPE_VALUE || type == Types.SMALLINT) && (viewIndexIdCell.getValueLength() <
VIEW_INDEX_ID_BIGINT_TYPE_PTR_LEN)) {
byte[] newBytes = PLong.INSTANCE.toBytes(PSmallint.INSTANCE.toObject(columnValue.get()));
ptr.set(newBytes, 0, newBytes.length);
} else {
ptr.set(columnValue.get(), columnValue.getOffset(), columnValue.getLength());
}
}
return true;
}

@Override
public PDataType getDataType() {
return PLong.INSTANCE;
}

@Override
public boolean isStateless() {
return false;
}

@Override
public Determinism getDeterminism() {
return Determinism.PER_ROW;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1074,6 +1074,9 @@ public boolean shouldPrepareIndexMutations(Put dataRowState) {
return true;
}
List<Cell> cols = IndexUtil.readColumnsFromRow(dataRowState, getIndexWhereColumns());
if (cols.isEmpty()) {
return false;
}
// Cells should be sorted as they are searched using a binary search during expression
// evaluation
Collections.sort(cols, CellComparator.getInstance());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.ReadOnlyProps;
import org.apache.phoenix.util.ScanUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class IndexMetaDataCacheClient {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,12 @@ protected CreateTableStatement(TableName tableName, ListMultimap<String,Pair<Str
Map<String, Integer> familyCounters, boolean noVerify) {
this.tableName = tableName;
this.props = props == null ? ImmutableListMultimap.<String,Pair<String,Object>>of() : props;
this.tableType = PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA.equals(tableName.getSchemaName()) ? PTableType.SYSTEM : tableType;
this.tableType =
(PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA.equals(
tableName.getSchemaName()) &&
(tableType == PTableType.TABLE || tableType == PTableType.SYSTEM) ?
PTableType.SYSTEM :
tableType);
this.columns = columns == null ? ImmutableList.<ColumnDef>of() : ImmutableList.<ColumnDef>copyOf(columns);
this.pkConstraint = pkConstraint == null ? PrimaryKeyConstraint.EMPTY : pkConstraint;
this.splitNodes = splitNodes == null ? Collections.<ParseNode>emptyList() : ImmutableList.copyOf(splitNodes);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.phoenix.parse;

import org.apache.phoenix.compile.StatementContext;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.function.DecodeViewIndexIdFunction;
import org.apache.phoenix.expression.function.FunctionExpression;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.util.IndexUtil;

import java.sql.SQLException;
import java.util.List;

import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID;
import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID_DATA_TYPE;

public class DecodeViewIndexIdParseNode extends FunctionParseNode {

DecodeViewIndexIdParseNode(String name, List<ParseNode> children,
BuiltInFunctionInfo info) {
super(name, children, info);
// It takes 2 parameters - VIEW_INDEX_ID, VIEW_INDEX_ID_DATA_TYPE.
if (children.size() != 2) {
throw new IllegalArgumentException(
"DecodeViewIndexIdParseNode should only have "
+ "VIEW_INDEX_ID and VIEW_INDEX_ID_DATA_TYPE parse nodes."
);
}
if (children.get(0).getClass().isAssignableFrom(ColumnParseNode.class)
&& children.get(1).getClass().isAssignableFrom(ColumnParseNode.class)
&& (!(((ColumnParseNode) children.get(0)).getName().equals(VIEW_INDEX_ID))
|| !(((ColumnParseNode) children.get(1)).getName().equals(VIEW_INDEX_ID_DATA_TYPE)))
) {
throw new IllegalArgumentException(
"DecodeViewIndexIdParseNode should only have "
+ "VIEW_INDEX_ID and VIEW_INDEX_ID_DATA_TYPE parse nodes."
);
}

// CastPastNode is generated during IndexStatement rewriting
if (children.get(0).getClass().isAssignableFrom(CastParseNode.class)
&& children.get(1).getClass().isAssignableFrom(CastParseNode.class)
&& (!((ColumnParseNode) (((CastParseNode) children.get(0)).getChildren().get(0))).getName().equals(
IndexUtil.getIndexColumnName(QueryConstants.DEFAULT_COLUMN_FAMILY, VIEW_INDEX_ID))
|| !((ColumnParseNode) (((CastParseNode) children.get(1)).getChildren().get(0))).getName().equals(
IndexUtil.getIndexColumnName(QueryConstants.DEFAULT_COLUMN_FAMILY, VIEW_INDEX_ID_DATA_TYPE)))
) {
throw new IllegalArgumentException(
"DecodeViewIndexIdParseNode should only have "
+ "VIEW_INDEX_ID and VIEW_INDEX_ID_DATA_TYPE parse nodes."
);
}

}

@Override
public FunctionExpression create(List<Expression> children, StatementContext context)
throws SQLException {
return new DecodeViewIndexIdFunction(children);
}

}
Loading