PHOENIX-2691 Exception while unpacking resultset containing VARCHAR ARRAY of unspecified length
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/cac03056 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/cac03056 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/cac03056 Branch: refs/heads/calcite Commit: cac03056578170a82ba812aa4648e0e5b1a1bbb6 Parents: 45a9d67 Author: James Taylor <jtay...@salesforce.com> Authored: Thu Feb 18 14:59:29 2016 -0800 Committer: James Taylor <jtay...@salesforce.com> Committed: Thu Feb 18 15:14:48 2016 -0800 ---------------------------------------------------------------------- .../apache/phoenix/end2end/GroupByCaseIT.java | 35 +++++++++ .../apache/phoenix/compile/GroupByCompiler.java | 74 ++++++++++++-------- .../phoenix/exception/SQLExceptionCode.java | 5 +- .../java/org/apache/phoenix/util/IndexUtil.java | 6 +- .../phoenix/compile/QueryCompilerTest.java | 36 ++++++++++ 5 files changed, 122 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/cac03056/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByCaseIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByCaseIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByCaseIT.java index 0f1568c..172f9f7 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByCaseIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/GroupByCaseIT.java @@ -226,4 +226,39 @@ public class GroupByCaseIT extends BaseHBaseManagedTimeIT { conn.close(); } + + @Test + public void testGroupByArray() throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.createStatement().execute("CREATE TABLE test1(\n" + + " a VARCHAR NOT NULL,\n" + + " b VARCHAR,\n" + + " c INTEGER,\n" + + " d VARCHAR,\n" + + " e VARCHAR ARRAY,\n" + + " f BIGINT,\n" + + " g BIGINT,\n" + + " CONSTRAINT pk PRIMARY KEY(a)\n" + + ")"); + conn.createStatement().execute("UPSERT INTO test1 VALUES('1', 'val', 100, 'a', ARRAY ['b'], 1, 2)"); + conn.createStatement().execute("UPSERT INTO test1 VALUES('2', 'val', 100, 'a', ARRAY ['b'], 3, 4)"); + conn.createStatement().execute("UPSERT INTO test1 VALUES('3', 'val', 100, 'a', ARRAY ['b','c'], 5, 6)"); + conn.commit(); + + ResultSet rs = conn.createStatement().executeQuery("SELECT c, SUM(f + g) AS sumone, d, e\n" + + "FROM test1\n" + + "WHERE b = 'val'\n" + + " AND a IN ('1','2','3')\n" + + "GROUP BY c, d, e\n" + + "ORDER BY sumone DESC"); + assertTrue(rs.next()); + assertEquals(100, rs.getInt(1)); + assertEquals(11, rs.getLong(2)); + assertTrue(rs.next()); + assertEquals(100, rs.getInt(1)); + assertEquals(10, rs.getLong(2)); + assertFalse(rs.next()); + conn.close(); + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/cac03056/phoenix-core/src/main/java/org/apache/phoenix/compile/GroupByCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/GroupByCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/GroupByCompiler.java index 7d9df02..85478bf 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/GroupByCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/GroupByCompiler.java @@ -38,8 +38,8 @@ import org.apache.phoenix.parse.SelectStatement; import org.apache.phoenix.schema.AmbiguousColumnException; import org.apache.phoenix.schema.ColumnNotFoundException; import org.apache.phoenix.schema.types.PDataType; -import org.apache.phoenix.schema.types.PDecimal; -import org.apache.phoenix.schema.types.PVarchar; +import org.apache.phoenix.schema.types.PVarbinary; +import org.apache.phoenix.util.IndexUtil; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; @@ -217,34 +217,53 @@ public class GroupByCompiler { public int compare(Pair<Integer,Expression> gb1, Pair<Integer,Expression> gb2) { Expression e1 = gb1.getSecond(); Expression e2 = gb2.getSecond(); - boolean isFixed1 = e1.getDataType().isFixedWidth(); - boolean isFixed2 = e2.getDataType().isFixedWidth(); + PDataType t1 = e1.getDataType(); + PDataType t2 = e2.getDataType(); + boolean isFixed1 = t1.isFixedWidth(); + boolean isFixed2 = t2.isFixedWidth(); boolean isFixedNullable1 = e1.isNullable() &&isFixed1; boolean isFixedNullable2 = e2.isNullable() && isFixed2; - if (isFixedNullable1 == isFixedNullable2) { - if (isFixed1 == isFixed2) { - // Not strictly necessary, but forces the order to match the schema - // column order (with PK columns before value columns). - //return o1.getColumnPosition() - o2.getColumnPosition(); - return gb1.getFirst() - gb2.getFirst(); - } else if (isFixed1) { - return -1; - } else { + boolean oae1 = onlyAtEndType(e1); + boolean oae2 = onlyAtEndType(e2); + if (oae1 == oae2) { + if (isFixedNullable1 == isFixedNullable2) { + if (isFixed1 == isFixed2) { + // Not strictly necessary, but forces the order to match the schema + // column order (with PK columns before value columns). + //return o1.getColumnPosition() - o2.getColumnPosition(); + return gb1.getFirst() - gb2.getFirst(); + } else if (isFixed1) { + return -1; + } else { + return 1; + } + } else if (isFixedNullable1) { return 1; + } else { + return -1; } - } else if (isFixedNullable1) { + } else if (oae1) { return 1; } else { return -1; } } }); + boolean foundOnlyAtEndType = false; for (Pair<Integer,Expression> groupBy : groupBys) { - expressions.add(groupBy.getSecond()); + Expression e = groupBy.getSecond(); + if (onlyAtEndType(e)) { + if (foundOnlyAtEndType) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.UNSUPPORTED_GROUP_BY_EXPRESSIONS) + .setMessage(e.toString()).build().buildException(); + } + foundOnlyAtEndType = true; + } + expressions.add(e); } for (int i = expressions.size()-2; i >= 0; i--) { Expression expression = expressions.get(i); - PDataType keyType = getKeyType(expression); + PDataType keyType = getGroupByDataType(expression); if (keyType == expression.getDataType()) { continue; } @@ -263,19 +282,16 @@ public class GroupByCompiler { return groupBy; } - private static PDataType getKeyType(Expression expression) { - PDataType type = expression.getDataType(); - if (!expression.isNullable() || !type.isFixedWidth()) { - return type; - } - if (type.isCastableTo(PDecimal.INSTANCE)) { - return PDecimal.INSTANCE; - } - if (type.isCastableTo(PVarchar.INSTANCE)) { - return PVarchar.INSTANCE; - } - // This might happen if someone tries to group by an array - throw new IllegalStateException("Multiple occurrences of type " + type + " may not occur in a GROUP BY clause"); + private static boolean onlyAtEndType(Expression expression) { + // Due to the encoding schema of these types, they may only be + // used once in a group by and are located at the end of the + // group by row key. + PDataType type = getGroupByDataType(expression); + return type.isArrayType() || type == PVarbinary.INSTANCE; + } + + private static PDataType getGroupByDataType(Expression expression) { + return IndexUtil.getIndexColumnDataType(expression.isNullable(), expression.getDataType()); } private GroupByCompiler() { http://git-wip-us.apache.org/repos/asf/phoenix/blob/cac03056/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java index 7ddd14c..65cb6db 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java @@ -229,7 +229,6 @@ public enum SQLExceptionCode { AGGREGATE_WITH_NOT_GROUP_BY_COLUMN(1018, "42Y27", "Aggregate may not contain columns not in GROUP BY."), ONLY_AGGREGATE_IN_HAVING_CLAUSE(1019, "42Y26", "Only aggregate maybe used in the HAVING clause."), UPSERT_COLUMN_NUMBERS_MISMATCH(1020, "42Y60", "Number of columns upserting must match number of values."), - NO_TABLE_SPECIFIED_FOR_WILDCARD_SELECT(1057, "42Y10", "No table specified for wildcard select."), // Table properties exception. INVALID_BUCKET_NUM(1021, "42Y80", "Salt bucket numbers should be with 1 and 256."), NO_SPLITS_ON_SALTED_TABLE(1022, "42Y81", "Should not specify split points on salted table with default row key order."), @@ -263,8 +262,10 @@ public enum SQLExceptionCode { UNALLOWED_LOCAL_INDEXES(1055, "43A12", "Local secondary indexes are configured to not be allowed."), DESC_VARBINARY_NOT_SUPPORTED(1056, "43A13", "Descending VARBINARY columns not supported."), + NO_TABLE_SPECIFIED_FOR_WILDCARD_SELECT(1057, "42Y10", "No table specified for wildcard select."), + UNSUPPORTED_GROUP_BY_EXPRESSIONS(1058, "43A14", "Only a single VARBINARY, ARRAY, or nullable BINARY type may be referenced in a GROUP BY."), - DEFAULT_COLUMN_FAMILY_ON_SHARED_TABLE(1069, "43A13", "Default column family not allowed on VIEW or shared INDEX."), + DEFAULT_COLUMN_FAMILY_ON_SHARED_TABLE(1069, "43A69", "Default column family not allowed on VIEW or shared INDEX."), ONLY_TABLE_MAY_BE_DECLARED_TRANSACTIONAL(1070, "44A01", "Only tables may be declared as transactional."), TX_MAY_NOT_SWITCH_TO_NON_TX(1071, "44A02", "A transactional table may not be switched to non transactional."), STORE_NULLS_MUST_BE_TRUE_FOR_TRANSACTIONAL(1072, "44A03", "Store nulls must be true when a table is transactional."), http://git-wip-us.apache.org/repos/asf/phoenix/blob/cac03056/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java index 98b88f4..8fc0c7e 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/util/IndexUtil.java @@ -87,10 +87,10 @@ import org.apache.phoenix.schema.types.PDecimal; import org.apache.phoenix.schema.types.PVarbinary; import org.apache.phoenix.schema.types.PVarchar; -import co.cask.tephra.TxConstants; - import com.google.common.collect.Lists; +import co.cask.tephra.TxConstants; + public class IndexUtil { public static final String INDEX_COLUMN_NAME_SEP = ":"; public static final byte[] INDEX_COLUMN_NAME_SEP_BYTES = Bytes.toBytes(INDEX_COLUMN_NAME_SEP); @@ -129,7 +129,7 @@ public class IndexUtil { if (PBinary.INSTANCE.equals(dataType)) { return PVarbinary.INSTANCE; } - throw new IllegalArgumentException("Unsupported non nullable index type " + dataType); + throw new IllegalArgumentException("Unsupported non nullable type " + dataType); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/cac03056/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java ---------------------------------------------------------------------- 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 b3baa73..cfec967 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 @@ -2136,6 +2136,42 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest { } @Test + public void testGroupByVarbinaryOrArray() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + conn.createStatement().execute("CREATE TABLE T1 (PK VARCHAR PRIMARY KEY, c1 VARCHAR, c2 VARBINARY, C3 VARCHAR ARRAY, c4 VARBINARY, C5 VARCHAR ARRAY, C6 BINARY(10)) "); + try { + conn.createStatement().executeQuery("SELECT c1 FROM t1 GROUP BY c1,c2,c3"); + fail(); + } catch(SQLException e) { + assertEquals(SQLExceptionCode.UNSUPPORTED_GROUP_BY_EXPRESSIONS.getErrorCode(), e.getErrorCode()); + } + try { + conn.createStatement().executeQuery("SELECT c1 FROM t1 GROUP BY c1,c3,c2"); + fail(); + } catch(SQLException e) { + assertEquals(SQLExceptionCode.UNSUPPORTED_GROUP_BY_EXPRESSIONS.getErrorCode(), e.getErrorCode()); + } + try { + conn.createStatement().executeQuery("SELECT c1 FROM t1 GROUP BY c1,c2,c4"); + fail(); + } catch(SQLException e) { + assertEquals(SQLExceptionCode.UNSUPPORTED_GROUP_BY_EXPRESSIONS.getErrorCode(), e.getErrorCode()); + } + try { + conn.createStatement().executeQuery("SELECT c1 FROM t1 GROUP BY c1,c3,c5"); + fail(); + } catch(SQLException e) { + assertEquals(SQLExceptionCode.UNSUPPORTED_GROUP_BY_EXPRESSIONS.getErrorCode(), e.getErrorCode()); + } + try { + conn.createStatement().executeQuery("SELECT c1 FROM t1 GROUP BY c1,c6,c5"); + fail(); + } catch(SQLException e) { + assertEquals(SQLExceptionCode.UNSUPPORTED_GROUP_BY_EXPRESSIONS.getErrorCode(), e.getErrorCode()); + } + } + + @Test public void testQueryWithSCN() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); props.put(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(1000));