You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by st...@apache.org on 2023/07/20 16:08:29 UTC
[phoenix] branch master updated: PHOENIX-6961 Using a covered field in hinted non-covered indexed query fails
This is an automated email from the ASF dual-hosted git repository.
stoty pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push:
new 5f2eb21610 PHOENIX-6961 Using a covered field in hinted non-covered indexed query fails
5f2eb21610 is described below
commit 5f2eb21610b54c27395266f29b5ebea98ff7af93
Author: Istvan Toth <st...@apache.org>
AuthorDate: Tue Jun 20 07:53:23 2023 +0200
PHOENIX-6961 Using a covered field in hinted non-covered indexed query fails
---
.../apache/phoenix/end2end/index/BaseIndexIT.java | 109 +++++++++++++++++++++
.../end2end/index/GlobalMutableNonTxIndexIT.java | 1 -
.../apache/phoenix/compile/ExpressionCompiler.java | 4 +-
.../org/apache/phoenix/compile/JoinCompiler.java | 6 +-
.../apache/phoenix/compile/ProjectionCompiler.java | 99 ++++++++++++-------
.../phoenix/compile/TupleProjectionCompiler.java | 12 +--
...mnRef.java => IndexUncoveredDataColumnRef.java} | 10 +-
.../java/org/apache/phoenix/schema/PTableImpl.java | 29 ++++--
.../java/org/apache/phoenix/util/ScanUtil.java | 4 +-
.../apache/phoenix/compile/QueryCompilerTest.java | 17 ++++
.../java/org/apache/phoenix/query/BaseTest.java | 35 +++----
11 files changed, 246 insertions(+), 80 deletions(-)
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
index 8bf84296f3..e4d759e218 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
import java.io.IOException;
import java.math.BigDecimal;
@@ -37,6 +38,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
@@ -81,6 +83,7 @@ import org.apache.phoenix.util.DateUtil;
import org.apache.phoenix.util.EnvironmentEdgeManager;
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.PropertiesUtil;
+import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.SchemaUtil;
import org.apache.phoenix.util.TestUtil;
import org.apache.phoenix.util.TransactionUtil;
@@ -1521,4 +1524,110 @@ public abstract class BaseIndexIT extends ParallelStatsDisabledIT {
CreateTableIT.verifyLastDDLTimestamp(fullIndexName, activeIndexLastDDLTimestamp + 1, conn);
}
}
+
+ @Test
+ public void testSelectUncoveredWithCoveredFieldSimple() throws Exception {
+ assumeFalse(localIndex);
+ assumeFalse(uncovered);
+ Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+ String tableName = "TBL_" + generateUniqueName();
+ String indexName = "IND_" + generateUniqueName();
+ String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+ try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+ conn.setAutoCommit(false);
+ String ddl =
+ "CREATE TABLE " + fullTableName
+ + " (v1 integer, k integer primary key, v2 integer, v3 integer, v4 integer) "
+ + tableDDLOptions;
+ String upsert = "UPSERT INTO " + fullTableName + " values (1, 2, 3, 4, 5)";
+ Statement stmt = conn.createStatement();
+ stmt.execute(ddl);
+ stmt.execute(upsert);
+ conn.commit();
+ ddl =
+ "CREATE " + (uncovered ? "UNCOVERED" : "") + " INDEX " + indexName + " ON "
+ + fullTableName + " (v2) include(v3)";
+ conn.createStatement().execute(ddl);
+ ResultSet rs =
+ conn.createStatement().executeQuery("SELECT /*+ INDEX(" + fullTableName + " "
+ + indexName + ")*/ * FROM " + fullTableName + " where v2=3 and v3=4");
+ // We're only testing the projector
+ assertTrue(rs.next());
+ assertEquals("1", rs.getString("v1"));
+ assertEquals("2", rs.getString("k"));
+ assertEquals("3", rs.getString("v2"));
+ assertEquals("4", rs.getString("v3"));
+ assertEquals("5", rs.getString("v4"));
+ assertFalse(rs.next());
+ }
+ }
+
+ @Test
+ public void testSelectUncoveredWithCoveredField() throws Exception {
+ assumeFalse(localIndex);
+ assumeFalse(uncovered);
+ Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+ String tableName = "TBL_" + generateUniqueName();
+ String indexName = "IND_" + generateUniqueName();
+ String fullTableName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, tableName);
+ String fullIndexName = SchemaUtil.getTableName(TestUtil.DEFAULT_SCHEMA_NAME, indexName);
+
+
+ try (Connection conn = DriverManager.getConnection(getUrl(), props);
+ Statement stmt = conn.createStatement()) {
+ conn.setAutoCommit(false);
+ String ddl =
+ "CREATE TABLE " + fullTableName + TestUtil.TEST_TABLE_SCHEMA + tableDDLOptions;
+ stmt.execute(ddl);
+ BaseTest.populateTestTable(fullTableName);
+ conn.commit();
+ ddl =
+ "CREATE " + (uncovered ? "UNCOVERED" : "") + " INDEX " + indexName + " ON "
+ + fullTableName + " ( int_col1 ASC)"
+ + " INCLUDE (long_col1, long_col2)"
+ ;
+ stmt.execute(ddl);
+
+ String query;
+ ResultSet rs;
+
+ for (String columns : Arrays.asList(new String[] { "*",
+ "varchar_pk,char_pk,int_pk,long_pk,decimal_pk,date_pk,a.varchar_col1,a.char_col1,a.int_col1,a.long_col1,a.decimal_col1,a.date1,b.varchar_col2,b.char_col2,b.int_col2,b.long_col2,b.decimal_col2,b.date2",
+ "varchar_pk,char_pk,int_pk,long_pk,decimal_pk,date_pk,varchar_col1,char_col1,int_col1,long_col1,decimal_col1,date1,varchar_col2,char_col2,int_col2,long_col2,decimal_col2,date2", })) {
+
+ query =
+ "SELECT /*+ INDEX(" + fullTableName + " " + indexName + ")*/ " + columns
+ + " from " + fullTableName + " where int_col1=2 and long_col1=2";
+ rs = stmt.executeQuery("Explain " + query);
+ String explainPlan = QueryUtil.getExplainPlan(rs);
+ assertEquals("bad plan with columns:" + columns,
+ "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullIndexName + " [2]\n"
+ + " SERVER MERGE [A.VARCHAR_COL1, A.CHAR_COL1, A.DECIMAL_COL1, A.DATE1, B.VARCHAR_COL2, B.CHAR_COL2, B.INT_COL2, B.DECIMAL_COL2, B.DATE2]\n"
+ + " SERVER FILTER BY A.\"LONG_COL1\" = 2",
+ explainPlan);
+ rs = stmt.executeQuery(query);
+ assertTrue(rs.next());
+ // Test the projector thoroughly
+ assertEquals("varchar1", rs.getString("varchar_pk"));
+ assertEquals("char1", rs.getString("char_pk"));
+ assertEquals("1", rs.getString("int_pk"));
+ assertEquals("1", rs.getString("long_pk"));
+ assertEquals("1", rs.getString("decimal_pk"));
+ assertEquals("2015-01-01 00:00:00.000", rs.getString("date_pk"));
+ assertEquals("varchar_a", rs.getString("varchar_col1"));
+ assertEquals("chara", rs.getString("char_col1"));
+ assertEquals("2", rs.getString("int_col1"));
+ assertEquals("2", rs.getString("long_col1"));
+ assertEquals("2", rs.getString("decimal_col1"));
+ assertEquals("2015-01-01 00:00:00.000", rs.getString("date1"));
+ assertEquals("varchar_b", rs.getString("varchar_col2"));
+ assertEquals("charb", rs.getString("char_col2"));
+ assertEquals("3", rs.getString("int_col2"));
+ assertEquals("3", rs.getString("long_col2"));
+ assertEquals("3", rs.getString("decimal_col2"));
+ assertEquals("2015-01-01 00:00:00.000", rs.getString("date2"));
+ assertFalse(rs.next());
+ }
+ }
+ }
}
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
index 28da77564e..fbea096280 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/GlobalMutableNonTxIndexIT.java
@@ -21,7 +21,6 @@ import java.util.Arrays;
import java.util.Collection;
import org.apache.phoenix.end2end.ParallelStatsDisabledTest;
-import org.apache.phoenix.hbase.index.IndexRegionObserver;
import org.junit.experimental.categories.Category;
import org.junit.runners.Parameterized.Parameters;
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
index 09a6adfbe4..1cd167ee52 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
@@ -112,7 +112,7 @@ import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ColumnRef;
import org.apache.phoenix.schema.DelegateDatum;
-import org.apache.phoenix.schema.IndexDataColumnRef;
+import org.apache.phoenix.schema.IndexUncoveredDataColumnRef;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PDatum;
import org.apache.phoenix.schema.PTable;
@@ -373,7 +373,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
if (IndexUtil.shouldIndexBeUsedForUncoveredQuery(context.getCurrentTable())) {
try {
context.setUncoveredIndex(true);
- return new IndexDataColumnRef(context, context.getCurrentTable(),
+ return new IndexUncoveredDataColumnRef(context, context.getCurrentTable(),
node.getName());
} catch (ColumnFamilyNotFoundException c) {
throw e;
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
index 4093436b3b..04dd4ffaa5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
@@ -75,7 +75,7 @@ import org.apache.phoenix.parse.TableNodeVisitor;
import org.apache.phoenix.parse.TableWildcardParseNode;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ColumnRef;
-import org.apache.phoenix.schema.IndexDataColumnRef;
+import org.apache.phoenix.schema.IndexUncoveredDataColumnRef;
import org.apache.phoenix.schema.MetaDataEntityNotFoundException;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PNameFactory;
@@ -1127,7 +1127,7 @@ public class JoinCompiler {
if (columnRef.getTableRef().equals(tableRef)
&& (!retainPKColumns || !SchemaUtil.isPKColumn(columnRef.getColumn()))) {
if (columnRef instanceof LocalIndexColumnRef) {
- sourceColumns.add(new IndexDataColumnRef(context, tableRef,
+ sourceColumns.add(new IndexUncoveredDataColumnRef(context, tableRef,
IndexUtil.getIndexColumnName(columnRef.getColumn())));
} else {
sourceColumns.add(columnRef);
@@ -1393,7 +1393,7 @@ public class JoinCompiler {
try {
columnRef = resolver.resolveColumn(node.getSchemaName(), node.getTableName(), node.getName());
} catch (ColumnNotFoundException e) {
- // This could be an IndexDataColumnRef. If so, the table name must have
+ // This could be an IndexUncoveredDataColumnRef. If so, the table name must have
// been appended by the IndexStatementRewriter, and we can convert it into.
TableRef tableRef = resolver.resolveTable(node.getSchemaName(), node.getTableName());
if (IndexUtil.shouldIndexBeUsedForUncoveredQuery(tableRef)) {
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
index 80c2156bdf..1fc74297b2 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
@@ -69,7 +69,7 @@ import org.apache.phoenix.schema.ArgumentTypeMismatchException;
import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ColumnRef;
-import org.apache.phoenix.schema.IndexDataColumnRef;
+import org.apache.phoenix.schema.IndexUncoveredDataColumnRef;
import org.apache.phoenix.schema.KeyValueSchema;
import org.apache.phoenix.schema.KeyValueSchema.KeyValueSchemaBuilder;
import org.apache.phoenix.schema.PColumn;
@@ -78,7 +78,6 @@ import org.apache.phoenix.schema.PDatum;
import org.apache.phoenix.schema.PName;
import org.apache.phoenix.schema.PTable;
import org.apache.phoenix.schema.PTable.ImmutableStorageScheme;
-import org.apache.phoenix.schema.PTable.IndexType;
import org.apache.phoenix.schema.PTableKey;
import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.ProjectedColumn;
@@ -191,17 +190,17 @@ public class ProjectionCompiler {
int projectedOffset = projectedExpressions.size();
PhoenixConnection conn = context.getConnection();
PName tenantId = conn.getTenantId();
- String tableName = index.getParentName().getString();
+ String dataTableName = index.getParentName().getString();
PTable dataTable = null;
try {
- dataTable = conn.getTable(new PTableKey(tenantId, tableName));
+ dataTable = conn.getTable(new PTableKey(tenantId, dataTableName));
} catch (TableNotFoundException e) {
if (tenantId != null) {
// Check with null tenantId
- dataTable = conn.getTable(new PTableKey(null, tableName));
+ dataTable = conn.getTable(new PTableKey(null, dataTableName));
}
else {
- throw e;
+ throw e;
}
}
int tableOffset = dataTable.getBucketNum() == null ? 0 : 1;
@@ -215,6 +214,13 @@ public class ProjectionCompiler {
throw new ColumnNotFoundException(schemaNameStr, tableNameStr,null, WildcardParseNode.INSTANCE.toString());
}
}
+ // At this point, the index table is either fully covered, or we are projecting uncovered
+ // columns
+ // The easy thing would be to just call projectAllTableColumns on the projected table,
+ // but its columns are not in the same order as the data column, so we have to map them to
+ // the data column order
+ TableRef projectedTableRef =
+ new TableRef(resolver.getTables().get(0), tableRef.getTableAlias());
for (int i = tableOffset, j = tableOffset; i < dataTable.getColumns().size(); i++) {
PColumn column = dataTable.getColumns().get(i);
// Skip tenant ID column (which may not be the first column, but is the first PK column)
@@ -222,29 +228,34 @@ public class ProjectionCompiler {
tableOffset++;
continue;
}
- PColumn tableColumn = dataTable.getColumns().get(i);
- String indexColName = IndexUtil.getIndexColumnName(tableColumn);
+ PColumn dataTableColumn = dataTable.getColumns().get(i);
+ String indexColName = IndexUtil.getIndexColumnName(dataTableColumn);
PColumn indexColumn = null;
ColumnRef ref = null;
try {
indexColumn = index.getColumnForColumnName(indexColName);
- ref = new ColumnRef(tableRef, indexColumn.getPosition());
+ //TODO could should we do this more efficiently than catching the expcetion ?
} catch (ColumnNotFoundException e) {
if (IndexUtil.shouldIndexBeUsedForUncoveredQuery(tableRef)) {
- try {
- context.setUncoveredIndex(true);
- ref = new IndexDataColumnRef(context, tableRef, indexColName);
- indexColumn = ref.getColumn();
- } catch (ColumnFamilyNotFoundException c) {
- throw e;
- }
+ //Projected columns have the same name as in the data table
+ String familyName =
+ dataTableColumn.getFamilyName() == null ? null
+ : dataTableColumn.getFamilyName().getString();
+ ref =
+ resolver.resolveColumn(familyName,
+ tableRef.getTableAlias() == null
+ ? tableRef.getTable().getName().getString()
+ : tableRef.getTableAlias(),
+ indexColName);
+ indexColumn = ref.getColumn();
} else {
throw e;
}
}
- String colName = tableColumn.getName().getString();
+ ref = new ColumnRef(projectedTableRef, indexColumn.getPosition());
+ String colName = dataTableColumn.getName().getString();
String tableAlias = tableRef.getTableAlias();
- if (resolveColumn && !(ref instanceof IndexDataColumnRef)) {
+ if (resolveColumn) {
try {
if (tableAlias != null) {
ref = resolver.resolveColumn(null, tableAlias, indexColName);
@@ -289,11 +300,15 @@ public class ProjectionCompiler {
}
private static void projectIndexColumnFamily(StatementContext context, String cfName, TableRef tableRef, boolean resolveColumn, List<Expression> projectedExpressions, List<ExpressionProjector> projectedColumns) throws SQLException {
+ ColumnResolver resolver = context.getResolver();
PTable index = tableRef.getTable();
PhoenixConnection conn = context.getConnection();
- String tableName = index.getParentName().getString();
- PTable table = conn.getTable(new PTableKey(conn.getTenantId(), tableName));
- PColumnFamily pfamily = table.getColumnFamily(cfName);
+ String dataTableName = index.getParentName().getString();
+ PTable dataTable = conn.getTable(new PTableKey(conn.getTenantId(), dataTableName));
+ PColumnFamily pfamily = dataTable.getColumnFamily(cfName);
+ TableRef projectedTableRef =
+ new TableRef(resolver.getTables().get(0), tableRef.getTableAlias());
+ PTable projectedIndex = projectedTableRef.getTable();
for (PColumn column : pfamily.getColumns()) {
String indexColName = IndexUtil.getIndexColumnName(column);
PColumn indexColumn = null;
@@ -301,20 +316,24 @@ public class ProjectionCompiler {
String indexColumnFamily = null;
try {
indexColumn = index.getColumnForColumnName(indexColName);
- ref = new ColumnRef(tableRef, indexColumn.getPosition());
- indexColumnFamily = indexColumn.getFamilyName() == null ? null : indexColumn.getFamilyName().getString();
+ ref = new ColumnRef(projectedTableRef, indexColumn.getPosition());
+ indexColumnFamily =
+ indexColumn.getFamilyName() == null ? null
+ : indexColumn.getFamilyName().getString();
} catch (ColumnNotFoundException e) {
if (IndexUtil.shouldIndexBeUsedForUncoveredQuery(tableRef)) {
try {
- context.setUncoveredIndex(true);
- ref = new IndexDataColumnRef(context, tableRef, indexColName);
- indexColumn = ref.getColumn();
- indexColumnFamily =
- indexColumn.getFamilyName() == null ? null
- : (index.getIndexType() == IndexType.LOCAL ? IndexUtil
- .getLocalIndexColumnFamily(indexColumn
- .getFamilyName().getString()) : indexColumn
- .getFamilyName().getString());
+ //Projected columns have the same name as in the data table
+ String colName = column.getName().getString();
+ String familyName =
+ column.getFamilyName() == null ? null
+ : column.getFamilyName().getString();
+ resolver.resolveColumn(familyName,
+ tableRef.getTableAlias() == null
+ ? tableRef.getTable().getName().getString()
+ : tableRef.getTableAlias(),
+ indexColName);
+ indexColumn = projectedIndex.getColumnForColumnName(colName);
} catch (ColumnFamilyNotFoundException c) {
throw e;
}
@@ -323,14 +342,18 @@ public class ProjectionCompiler {
}
}
if (resolveColumn) {
- ref = context.getResolver().resolveColumn(index.getTableName().getString(), indexColumnFamily, indexColName);
+ ref =
+ resolver.resolveColumn(index.getTableName().getString(), indexColumnFamily,
+ indexColName);
}
Expression expression = ref.newColumnExpression();
projectedExpressions.add(expression);
String colName = column.getName().toString();
boolean isCaseSensitive = !SchemaUtil.normalizeIdentifier(colName).equals(colName);
- projectedColumns.add(new ExpressionProjector(colName,
- tableRef.getTableAlias() == null ? table.getName().getString() : tableRef.getTableAlias(), expression, isCaseSensitive));
+ projectedColumns.add(new ExpressionProjector(colName,
+ tableRef.getTableAlias() == null ? dataTable.getName().getString()
+ : tableRef.getTableAlias(),
+ expression, isCaseSensitive));
}
}
@@ -397,7 +420,7 @@ public class ProjectionCompiler {
}
isWildcard = true;
if (tableRef.getTable().getType() == PTableType.INDEX && ((WildcardParseNode)node).isRewrite()) {
- projectAllIndexColumns(context, tableRef, resolveColumn, projectedExpressions, projectedColumns, targetColumns);
+ projectAllIndexColumns(context, tableRef, resolveColumn, projectedExpressions, projectedColumns, targetColumns);
} else {
projectAllTableColumns(context, tableRef, resolveColumn, projectedExpressions, projectedColumns, targetColumns);
}
@@ -411,7 +434,7 @@ public class ProjectionCompiler {
projectAllIndexColumns(context, tRef, true, projectedExpressions, projectedColumns, targetColumns);
} else {
projectAllTableColumns(context, tRef, true, projectedExpressions, projectedColumns, targetColumns);
- }
+ }
} else if (node instanceof FamilyWildcardParseNode) {
if (tableRef == TableRef.EMPTY_TABLE_REF) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.NO_TABLE_SPECIFIED_FOR_WILDCARD_SELECT).build().buildException();
@@ -712,7 +735,7 @@ public class ProjectionCompiler {
PColumn col = expression.getColumn();
// hack'ish... For covered columns with local indexes we defer to the server.
if (col instanceof ProjectedColumn && ((ProjectedColumn) col)
- .getSourceColumnRef() instanceof IndexDataColumnRef) {
+ .getSourceColumnRef() instanceof IndexUncoveredDataColumnRef) {
return null;
}
PTable table = context.getCurrentTable().getTable();
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
index 0c4c514122..99392f6d29 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
@@ -41,7 +41,7 @@ import org.apache.phoenix.parse.WildcardParseNode;
import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ColumnRef;
-import org.apache.phoenix.schema.IndexDataColumnRef;
+import org.apache.phoenix.schema.IndexUncoveredDataColumnRef;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PName;
import org.apache.phoenix.schema.PNameFactory;
@@ -159,9 +159,9 @@ public class TupleProjectionCompiler {
EncodedColumnsUtil.setColumns(column, table, context.getScan());
}
}
- // add IndexDataColumnRef
+ // add IndexUncoveredDataColumnRef
position = projectedColumns.size() + (hasSaltingColumn ? 1 : 0);
- for (IndexDataColumnRef sourceColumnRef : visitor.indexColumnRefSet) {
+ for (IndexUncoveredDataColumnRef sourceColumnRef : visitor.indexColumnRefSet) {
PColumn column = new ProjectedColumn(sourceColumnRef.getColumn().getName(),
sourceColumnRef.getColumn().getFamilyName(), position++,
sourceColumnRef.getColumn().isNullable(), sourceColumnRef, sourceColumnRef.getColumn().getColumnQualifierBytes());
@@ -239,12 +239,12 @@ public class TupleProjectionCompiler {
private static class ColumnRefVisitor extends StatelessTraverseAllParseNodeVisitor {
private final StatementContext context;
private final LinkedHashSet<ColumnRef> nonPkColumnRefSet;
- private final LinkedHashSet<IndexDataColumnRef> indexColumnRefSet;
+ private final LinkedHashSet<IndexUncoveredDataColumnRef> indexColumnRefSet;
private ColumnRefVisitor(StatementContext context) {
this.context = context;
this.nonPkColumnRefSet = new LinkedHashSet<ColumnRef>();
- this.indexColumnRefSet = new LinkedHashSet<IndexDataColumnRef>();
+ this.indexColumnRefSet = new LinkedHashSet<IndexUncoveredDataColumnRef>();
}
@Override
@@ -259,7 +259,7 @@ public class TupleProjectionCompiler {
if (IndexUtil.shouldIndexBeUsedForUncoveredQuery(context.getCurrentTable())) {
try {
context.setUncoveredIndex(true);
- indexColumnRefSet.add(new IndexDataColumnRef(context,
+ indexColumnRefSet.add(new IndexUncoveredDataColumnRef(context,
context.getCurrentTable(), node.getName()));
} catch (ColumnFamilyNotFoundException c) {
throw e;
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/IndexDataColumnRef.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/IndexUncoveredDataColumnRef.java
similarity index 89%
rename from phoenix-core/src/main/java/org/apache/phoenix/schema/IndexDataColumnRef.java
rename to phoenix-core/src/main/java/org/apache/phoenix/schema/IndexUncoveredDataColumnRef.java
index 6b8c0e2952..2bffdbb5c3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/IndexDataColumnRef.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/IndexUncoveredDataColumnRef.java
@@ -33,13 +33,13 @@ import org.apache.phoenix.util.IndexUtil;
* use index in the query plan and fetch the missing columns from the data table rows on the
* server side. This class is used to keep track of such data columns.
*/
-public class IndexDataColumnRef extends ColumnRef {
+public class IndexUncoveredDataColumnRef extends ColumnRef {
final private int position;
// Despite the final keyword, columns IS mutable, and must not be used for equality/hashCode
final private Set<PColumn> columns;
private static final ParseNodeFactory FACTORY = new ParseNodeFactory();
- public IndexDataColumnRef(StatementContext context, TableRef tRef, String indexColumnName)
+ public IndexUncoveredDataColumnRef(StatementContext context, TableRef tRef, String indexColumnName)
throws MetaDataEntityNotFoundException, SQLException {
super(FromCompiler.getResolver(
FACTORY.namedTable(
@@ -54,7 +54,7 @@ public class IndexDataColumnRef extends ColumnRef {
columns = context.getDataColumns();
}
- protected IndexDataColumnRef(IndexDataColumnRef indexDataColumnRef, long timestamp) {
+ protected IndexUncoveredDataColumnRef(IndexUncoveredDataColumnRef indexDataColumnRef, long timestamp) {
super(indexDataColumnRef, timestamp);
this.position = indexDataColumnRef.position;
this.columns = indexDataColumnRef.columns;
@@ -62,7 +62,7 @@ public class IndexDataColumnRef extends ColumnRef {
@Override
public ColumnRef cloneAtTimestamp(long timestamp) {
- return new IndexDataColumnRef(this, timestamp);
+ return new IndexUncoveredDataColumnRef(this, timestamp);
}
@Override
@@ -84,7 +84,7 @@ public class IndexDataColumnRef extends ColumnRef {
if (!super.equals(o)) {
return false;
}
- IndexDataColumnRef that = (IndexDataColumnRef) o;
+ IndexUncoveredDataColumnRef that = (IndexUncoveredDataColumnRef) o;
return position == that.position;
}
}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index 1d55ea8b21..fb23a381c3 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -744,14 +744,25 @@ public class PTableImpl implements PTable {
}
});
+ // With the new uncovered index code, we pass the data table columns to the index
+ // PTable. This wreaks havoc with the code used disambiguate column qualifiers.
+ // localColumns only holds the actual columns of the table, and not external references
+ List<PColumn> localColumns = new ArrayList<>(this.columns.size());
+
+ //TODO should we just pass the global indexref columns separately instead ?
+ for (PColumn column : sortedColumns) {
+ if (!(column instanceof ProjectedColumn && ((ProjectedColumn) column)
+ .getSourceColumnRef() instanceof IndexUncoveredDataColumnRef)) {
+ localColumns.add(column);
+ }
+ }
+
int position = 0;
if (this.bucketNum != null) {
position = 1;
}
ListMultimap<String, PColumn> populateColumnsByName =
ArrayListMultimap.create(this.columns.size(), 1);
- Map<KVColumnFamilyQualifier, PColumn> populateKvColumnsByQualifiers =
- Maps.newHashMapWithExpectedSize(this.columns.size());
for (PColumn column : sortedColumns) {
allColumns[position] = column;
position++;
@@ -772,6 +783,10 @@ public class PTableImpl implements PTable {
}
}
}
+ }
+ Map<KVColumnFamilyQualifier, PColumn> populateKvColumnsByQualifiers =
+ Maps.newHashMapWithExpectedSize(localColumns.size());
+ for (PColumn column : localColumns) {
byte[] cq = column.getColumnQualifierBytes();
String cf = column.getFamilyName() != null ?
column.getFamilyName().getString() : null;
@@ -779,7 +794,7 @@ public class PTableImpl implements PTable {
KVColumnFamilyQualifier info = new KVColumnFamilyQualifier(cf, cq);
if (populateKvColumnsByQualifiers.get(info) != null) {
throw new ColumnAlreadyExistsException(this.schemaName.getString(),
- fullName.getString(), columnName);
+ fullName.getString(), column.getName().getString());
}
populateKvColumnsByQualifiers.put(info, column);
}
@@ -815,11 +830,13 @@ public class PTableImpl implements PTable {
if (column.isRowTimestamp()) {
rowTimestampCol = column;
}
- }
- if (familyName == null) {
estimatedSize += column.getEstimatedSize(); // PK columns
builder.addField(column, column.isNullable(), column.getSortOrder());
- } else {
+ }
+ }
+ for (PColumn column : localColumns) {
+ PName familyName = column.getFamilyName();
+ if (familyName != null) {
List<PColumn> columnsInFamily = familyMap.get(familyName);
if (columnsInFamily == null) {
columnsInFamily = Lists.newArrayListWithExpectedSize(maxExpectedSize);
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
index 3928142f95..c02acd9379 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ScanUtil.java
@@ -1194,10 +1194,10 @@ public class ScanUtil {
scan.setAttribute(BaseScannerRegionObserver.EMPTY_COLUMN_QUALIFIER_NAME, emptyCQ);
scan.setAttribute(BaseScannerRegionObserver.READ_REPAIR_TRANSFORMING_TABLE, TRUE_BYTES);
} else {
- if (table.isTransactional() || table.getType() != PTableType.INDEX) {
+ if (table.getType() != PTableType.INDEX || !IndexUtil.isGlobalIndex(indexTable)) {
return;
}
- if (!IndexUtil.isGlobalIndex(indexTable)) {
+ if (table.isTransactional() && table.getIndexType() == IndexType.UNCOVERED_GLOBAL) {
return;
}
PTable dataTable = ScanUtil.getDataTable(indexTable, phoenixConnection);
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
index df0ae054cf..084a87ee4f 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java
@@ -7082,4 +7082,21 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest {
explainPlan);
}
}
+
+ @Test
+ public void testUncoveredPhoenix6961() throws Exception {
+ try (Connection conn = DriverManager.getConnection(getUrl());
+ Statement stmt = conn.createStatement();) {
+ stmt.execute(
+ "create table d (k integer primary key, v1 integer, v2 integer, v3 integer, v4 integer)");
+ stmt.execute("create index i on d(v2) include (v3)");
+ String query = "select /*+ index(d i) */ * from d where v2=1 and v3=1";
+ ResultSet rs = stmt.executeQuery("EXPLAIN " + query);
+ String explainPlan = QueryUtil.getExplainPlan(rs);
+ assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER I [1]\n"
+ + " SERVER MERGE [0.V1, 0.V4]\n"
+ + " SERVER FILTER BY \"V3\" = 1",
+ explainPlan);
+ }
+ }
}
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
index 30f73f77c8..fd8f0903b0 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/BaseTest.java
@@ -1766,25 +1766,26 @@ public abstract class BaseTest {
String upsert = "UPSERT INTO " + fullTableName
+ " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
PreparedStatement stmt = conn.prepareStatement(upsert);
+ //varchar_pk
stmt.setString(1, firstRowInBatch ? "firstRowInBatch_" : "" + "varchar"+index);
- stmt.setString(2, "char"+index);
- stmt.setInt(3, index);
- stmt.setLong(4, index);
- stmt.setBigDecimal(5, new BigDecimal(index));
+ stmt.setString(2, "char"+index); // char_pk
+ stmt.setInt(3, index); // int_pk
+ stmt.setLong(4, index); // long_pk
+ stmt.setBigDecimal(5, new BigDecimal(index)); // decimal_pk
Date date = DateUtil.parseDate("2015-01-01 00:00:00");
- stmt.setDate(6, date);
- stmt.setString(7, "varchar_a");
- stmt.setString(8, "chara");
- stmt.setInt(9, index+1);
- stmt.setLong(10, index+1);
- stmt.setBigDecimal(11, new BigDecimal(index+1));
- stmt.setDate(12, date);
- stmt.setString(13, "varchar_b");
- stmt.setString(14, "charb");
- stmt.setInt(15, index+2);
- stmt.setLong(16, index+2);
- stmt.setBigDecimal(17, new BigDecimal(index+2));
- stmt.setDate(18, date);
+ stmt.setDate(6, date); // date_pk
+ stmt.setString(7, "varchar_a"); // a.varchar_col1
+ stmt.setString(8, "chara"); // a.char_col1
+ stmt.setInt(9, index+1); // a.int_col1
+ stmt.setLong(10, index+1); // a.long_col1
+ stmt.setBigDecimal(11, new BigDecimal(index+1)); // a.decimal_col1
+ stmt.setDate(12, date); // a.date1
+ stmt.setString(13, "varchar_b"); // b.varchar_col2
+ stmt.setString(14, "charb"); // b.char_col2
+ stmt.setInt(15, index+2); // b.int_col2
+ stmt.setLong(16, index+2); // b.long_col2
+ stmt.setBigDecimal(17, new BigDecimal(index+2)); // b.decimal_col2
+ stmt.setDate(18, date); // b.date2
stmt.executeUpdate();
}