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 11f8e3c200 PHOENIX-6764 Implement Binary and Hexadecimal literals
11f8e3c200 is described below
commit 11f8e3c20068b3e62482f6b3cdd0214915767636
Author: Istvan Toth <[email protected]>
AuthorDate: Fri Aug 19 13:00:55 2022 +0200
PHOENIX-6764 Implement Binary and Hexadecimal literals
also changes to how binary types are converted to String in ResultSets and
explain results
---
.../end2end/BaseTenantSpecificViewIndexIT.java | 6 +-
.../phoenix/end2end/BinaryStringLiteralIT.java | 142 +++++++++++++++++++++
.../phoenix/end2end/RowValueConstructorIT.java | 2 +-
.../phoenix/end2end/SortMergeJoinMoreIT.java | 8 +-
.../org/apache/phoenix/end2end/UpsertSelectIT.java | 2 +-
.../it/java/org/apache/phoenix/end2end/ViewIT.java | 11 +-
.../phoenix/end2end/index/SaltedIndexIT.java | 13 +-
.../phoenix/end2end/join/HashJoinMoreIT.java | 8 +-
phoenix-core/src/main/antlr3/PhoenixSQL.g | 77 ++++++++++-
.../org/apache/phoenix/jdbc/PhoenixConnection.java | 3 +
.../org/apache/phoenix/parse/ParseNodeFactory.java | 44 ++++++-
.../phoenix/schema/types/PArrayDataType.java | 2 +-
.../org/apache/phoenix/schema/types/PBinary.java | 3 -
.../apache/phoenix/schema/types/PVarbinary.java | 10 +-
.../java/org/apache/phoenix/util/ByteUtil.java | 50 +++++++-
.../apache/phoenix/compile/WhereOptimizerTest.java | 13 +-
.../org/apache/phoenix/parse/QueryParserTest.java | 54 ++++++++
.../org/apache/phoenix/query/QueryPlanTest.java | 4 +-
18 files changed, 402 insertions(+), 50 deletions(-)
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
index fdce1c4b87..92eef8ef3d 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BaseTenantSpecificViewIndexIT.java
@@ -33,6 +33,7 @@ import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.compile.ExplainPlan;
import org.apache.phoenix.compile.ExplainPlanAttributes;
import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
+import org.apache.phoenix.schema.types.PVarbinary;
import org.apache.phoenix.util.PhoenixRuntime;
import org.apache.phoenix.util.SchemaUtil;
@@ -169,9 +170,10 @@ public abstract class BaseTenantSpecificViewIndexIT
extends SplitSystemCatalogIT
} else {
iteratorTypeAndScanSize = "PARALLEL 3-WAY";
clientSortAlgo = "CLIENT MERGE SORT";
- keyRanges = " [0," + (Short.MIN_VALUE + expectedIndexIdOffset)
+ keyRanges = " [X'00'," + (Short.MIN_VALUE +
expectedIndexIdOffset)
+ ",'" + tenantId + "','" + valuePrefix + "v2-1'] - ["
- + (saltBuckets - 1) + "," + (Short.MIN_VALUE +
expectedIndexIdOffset)
+ + PVarbinary.INSTANCE.toStringLiteral(new byte[]
{(byte)(saltBuckets - 1)})
+ + "," + (Short.MIN_VALUE + expectedIndexIdOffset)
+ ",'" + tenantId + "','" + valuePrefix + "v2-1']";
}
expectedTableName = "_IDX_" + tableName;
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BinaryStringLiteralIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BinaryStringLiteralIT.java
new file mode 100644
index 0000000000..a6632166d8
--- /dev/null
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BinaryStringLiteralIT.java
@@ -0,0 +1,142 @@
+/*
+ * 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.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+/**
+ * End to end tests for Hexadecimal and Binary literals
+ */
+@Category(ParallelStatsDisabledTest.class)
+public class BinaryStringLiteralIT extends ParallelStatsDisabledIT {
+
+ private static String EMPTY = "";
+ private static String THREE_HEX = "0001AA";
+ private static String NINE_HEX = "0102030405607080F0";
+
+ private static String THREE_BIN = "00000000" + "00000001" + "10101010";
+ private static String NINE_BIN =
+ "00000001" + "00000010" + "00000011" + "00000100" + "00000101" +
"01100000" + "01110000"
+ + "10000000" + "11110000";
+
+ private String PARSER_STRESS = "x'0 12 ' --comment \n /* comment */ ' 34
567' \n \n 'aA'";
+
+ private String toHex(String s) {
+ return "X'" + s + "'";
+ }
+
+ private String toBin(String s) {
+ return "B'" + s + "'";
+ }
+
+ private void insertRow(Statement stmt, String tableName, int id, String s)
throws SQLException {
+ stmt.executeUpdate("UPSERT INTO " + tableName + " VALUES (" + id + ","
+ s + "," + s + ")");
+ }
+
+ @Test
+ public void testBinary() throws Exception {
+ String tableName = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(getUrl());
+ Statement stmt = conn.createStatement();) {
+ String ddl =
+ "CREATE TABLE " + tableName
+ + " (id INTEGER NOT NULL PRIMARY KEY, b
BINARY(10), vb VARBINARY)";
+ stmt.execute(ddl);
+ conn.commit();
+
+ insertRow(stmt, tableName, 1, toHex(EMPTY));
+ insertRow(stmt, tableName, 3, toHex(THREE_HEX));
+ insertRow(stmt, tableName, 9, toHex(NINE_HEX));
+ insertRow(stmt, tableName, 10, PARSER_STRESS);
+
+ insertRow(stmt, tableName, 101, toBin(EMPTY));
+ insertRow(stmt, tableName, 103, toBin(THREE_BIN));
+ insertRow(stmt, tableName, 109, toBin(NINE_BIN));
+
+ conn.commit();
+ ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName + "
ORDER BY ID ASC");
+ assertTrue(rs.next());
+ assertEquals(1, rs.getInt(1));
+ assertEquals(null, rs.getString(2));
+ assertEquals(null, rs.getString(3));
+
+ assertTrue(rs.next());
+ assertEquals(3, rs.getInt(1));
+ assertEquals("0001aa00000000000000", rs.getString(2));
+ assertEquals("0001aa", rs.getString(3));
+
+ assertTrue(rs.next());
+ assertEquals(9, rs.getInt(1));
+ assertEquals("0102030405607080f000", rs.getString(2));
+ assertEquals("0102030405607080f0", rs.getString(3));
+
+ assertTrue(rs.next());
+ assertEquals(10, rs.getInt(1));
+ assertEquals("01234567aa0000000000", rs.getString(2));
+ assertEquals("01234567aa", rs.getString(3));
+
+ assertTrue(rs.next());
+ assertEquals(101, rs.getInt(1));
+ assertEquals(null, rs.getString(2));
+ assertEquals(null, rs.getString(3));
+
+ assertTrue(rs.next());
+ assertEquals(103, rs.getInt(1));
+ assertEquals("0001aa00000000000000", rs.getString(2));
+ assertEquals("0001aa", rs.getString(3));
+
+ assertTrue(rs.next());
+ assertEquals(109, rs.getInt(1));
+ assertEquals("0102030405607080f000", rs.getString(2));
+ assertEquals("0102030405607080f0", rs.getString(3));
+
+ assertFalse(rs.next());
+ }
+ }
+
+ @Test
+ public void testBinaryArray() throws Exception {
+ String tableName = generateUniqueName();
+
+ try (Connection conn = DriverManager.getConnection(getUrl());
+ Statement stmt = conn.createStatement();) {
+ String ddl =
+ "CREATE TABLE " + tableName
+ + " (id INTEGER NOT NULL PRIMARY KEY, b
BINARY(10), a BINARY(10)[])";
+ stmt.execute(ddl);
+ conn.commit();
+
+ stmt.executeUpdate("UPSERT INTO " + tableName + " VALUES (" + 3 +
"," + toHex(THREE_HEX) + ", ARRAY[" + toHex(THREE_HEX)+", "+toHex(THREE_HEX)+",
"+toHex(THREE_HEX)+"])");
+
+ conn.commit();
+ ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName + "
ORDER BY ID ASC");
+ assertTrue(rs.next());
+ assertEquals("0001aa00000000000000", rs.getString(2));
+ //FIXME we're using a different string representation here than
for the scalar values
+ assertEquals("[X'0001aa00000000000000', X'0001aa00000000000000',
X'0001aa00000000000000']", rs.getString(3));
+ assertFalse(rs.next());
+ }
+ }
+
+}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
index 47d63272f7..dee1792c90 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java
@@ -1238,7 +1238,7 @@ public class RowValueConstructorIT extends
ParallelStatsDisabledIT {
explainPlanAttributes.getExplainScanType());
assertEquals(tempTableWithCompositePK,
explainPlanAttributes.getTableName());
- assertEquals(" [0,2] - [3,4]",
explainPlanAttributes.getKeyRanges());
+ assertEquals(" [X'00',2] - [X'03',4]",
explainPlanAttributes.getKeyRanges());
assertEquals("CLIENT MERGE SORT",
explainPlanAttributes.getClientSortAlgo());
} finally {
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
index f1e6547833..b7e52859bb 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SortMergeJoinMoreIT.java
@@ -430,14 +430,14 @@ public class SortMergeJoinMoreIT extends
ParallelStatsDisabledIT {
String p = i == 0 ?
"SORT-MERGE-JOIN (INNER) TABLES\n" +
- " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + eventCountTableName + " [0,'5SEC',~1462993520000000000,'Tr/Bal'] -
[1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+ " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + eventCountTableName + " [X'00','5SEC',~1462993520000000000,'Tr/Bal'] -
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
" SERVER DISTINCT PREFIX FILTER OVER [BUCKET,
TIMESTAMP, LOCATION]\n" +
" SERVER AGGREGATE INTO ORDERED DISTINCT ROWS
BY [BUCKET, TIMESTAMP, LOCATION]\n" +
" CLIENT MERGE SORT\n" +
" CLIENT SORTED BY [BUCKET, TIMESTAMP]\n" +
"AND (SKIP MERGE)\n" +
- " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + t[i] + " [0,'5SEC',~1462993520000000000,'Tr/Bal'] -
[1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+ " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + t[i] + " [X'00','5SEC',~1462993520000000000,'Tr/Bal'] -
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY AND
SRC_LOCATION = DST_LOCATION\n" +
" SERVER DISTINCT PREFIX FILTER OVER [BUCKET,
\"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
" SERVER AGGREGATE INTO ORDERED DISTINCT ROWS
BY [BUCKET, \"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
@@ -446,14 +446,14 @@ public class SortMergeJoinMoreIT extends
ParallelStatsDisabledIT {
"CLIENT AGGREGATE INTO DISTINCT ROWS BY [E.BUCKET,
E.TIMESTAMP]"
:
"SORT-MERGE-JOIN (INNER) TABLES\n" +
- " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + eventCountTableName + " [0,'5SEC',~1462993520000000000,'Tr/Bal'] -
[1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+ " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + eventCountTableName + " [X'00','5SEC',~1462993520000000000,'Tr/Bal'] -
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
" SERVER DISTINCT PREFIX FILTER OVER [BUCKET,
TIMESTAMP, LOCATION]\n" +
" SERVER AGGREGATE INTO ORDERED DISTINCT ROWS
BY [BUCKET, TIMESTAMP, LOCATION]\n" +
" CLIENT MERGE SORT\n" +
" CLIENT SORTED BY [BUCKET, TIMESTAMP]\n" +
"AND (SKIP MERGE)\n" +
- " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + t[i] + " [0,'5SEC',1462993420000000001,'Tr/Bal'] -
[1,'5SEC',1462993520000000000,'Tr/Bal']\n" +
+ " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
" + t[i] + " [X'00','5SEC',1462993420000000001,'Tr/Bal'] -
[X'01','5SEC',1462993520000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY AND
SRC_LOCATION = DST_LOCATION\n" +
" SERVER DISTINCT PREFIX FILTER OVER [BUCKET,
\"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
" SERVER AGGREGATE INTO ORDERED DISTINCT ROWS
BY [BUCKET, \"TIMESTAMP\", SRC_LOCATION, DST_LOCATION]\n" +
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertSelectIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertSelectIT.java
index 0febfe687d..a99ac5aab6 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertSelectIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpsertSelectIT.java
@@ -1640,7 +1640,7 @@ public class UpsertSelectIT extends
ParallelStatsDisabledIT {
rs = stmt.executeQuery("select * from " + t2);
assertTrue(rs.next());
assertEquals(2, rs.getLong(1));
- assertEquals("[[128,0,0,54], [128,0,4,0]]",
rs.getArray(2).toString());
+ assertEquals("[X'80000036', X'80000400']",
rs.getArray(2).toString());
}
}
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
index 8bbc37d484..6f3dcb7f12 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java
@@ -63,6 +63,7 @@ import
org.apache.phoenix.schema.export.DefaultSchemaRegistryRepository;
import org.apache.phoenix.schema.export.DefaultSchemaWriter;
import org.apache.phoenix.schema.export.SchemaRegistryRepository;
import org.apache.phoenix.schema.export.SchemaRegistryRepositoryFactory;
+import org.apache.phoenix.schema.types.PVarbinary;
import org.apache.phoenix.thirdparty.com.google.common.collect.Lists;
import org.apache.phoenix.transaction.PhoenixTransactionProvider.Feature;
import org.apache.phoenix.transaction.TransactionFactory;
@@ -1083,8 +1084,9 @@ public class ViewIT extends SplitSystemCatalogIT {
} else {
assertEquals("PARALLEL " + saltBuckets + "-WAY",
explainPlanAttributes.getIteratorTypeAndScanSize());
- assertEquals(" [0," + Short.MIN_VALUE + ",51] - ["
- + (saltBuckets - 1) + "," + Short.MIN_VALUE + ",51]",
+ assertEquals(" [X'00'," + Short.MIN_VALUE + ",51] - ["
+ + PVarbinary.INSTANCE.toStringLiteral(new byte[]
{(byte)(saltBuckets - 1)})
+ + "," + Short.MIN_VALUE + ",51]",
explainPlanAttributes.getKeyRanges());
assertEquals("CLIENT MERGE SORT",
explainPlanAttributes.getClientSortAlgo());
@@ -1149,8 +1151,9 @@ public class ViewIT extends SplitSystemCatalogIT {
} else {
assertEquals("PARALLEL " + saltBuckets + "-WAY",
explainPlanAttributes.getIteratorTypeAndScanSize());
- assertEquals(" [0," + (Short.MIN_VALUE + 1) + ",'foo'] - ["
- + (saltBuckets - 1) + "," + (Short.MIN_VALUE + 1)
+ assertEquals(" [X'00'," + (Short.MIN_VALUE + 1) + ",'foo']
- ["
+ + PVarbinary.INSTANCE.toStringLiteral(new byte[]
{(byte)(saltBuckets - 1)})
+ + "," + (Short.MIN_VALUE + 1)
+ ",'foo']", explainPlanAttributes.getKeyRanges());
assertEquals("CLIENT MERGE SORT",
explainPlanAttributes.getClientSortAlgo());
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java
index b1ed74e67f..a3ee7f7124 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/SaltedIndexIT.java
@@ -34,6 +34,7 @@ import org.apache.phoenix.end2end.ParallelStatsDisabledTest;
import org.apache.phoenix.jdbc.PhoenixConnection;
import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.schema.types.PVarbinary;
import org.apache.phoenix.util.PropertiesUtil;
import org.apache.phoenix.util.QueryUtil;
import org.apache.phoenix.util.SchemaUtil;
@@ -148,8 +149,9 @@ public class SaltedIndexIT extends ParallelStatsDisabledIT {
expectedPlan = indexSaltBuckets == null ?
"CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexTableFullName + "
[~'y']\n" +
" SERVER FILTER BY FIRST KEY ONLY" :
- ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + indexTableFullName + "
[0,~'y'] - ["+(indexSaltBuckets.intValue()-1)+",~'y']\n" +
-
+ ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + indexTableFullName + "
[X'00',~'y'] - [" +
+ PVarbinary.INSTANCE.toStringLiteral(new byte[]
{(byte)(indexSaltBuckets - 1)}) +
+ ",~'y']\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
"CLIENT MERGE SORT");
assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs));
@@ -170,9 +172,10 @@ public class SaltedIndexIT extends ParallelStatsDisabledIT
{
expectedPlan = indexSaltBuckets == null ?
"CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexTableFullName + "
[*] - [~'x']\n"
+ " SERVER FILTER BY FIRST KEY ONLY" :
- ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + indexTableFullName + "
[0,*] - ["+(indexSaltBuckets.intValue()-1)+",~'x']\n"
-
- + " SERVER FILTER BY FIRST KEY ONLY\n" +
+ ("CLIENT PARALLEL 4-WAY RANGE SCAN OVER " + indexTableFullName + "
[X'00',*] - ["
+ + PVarbinary.INSTANCE.toStringLiteral(new byte[]
{(byte)(indexSaltBuckets - 1)})
+ + ",~'x']\n"
+ + " SERVER FILTER BY FIRST KEY ONLY\n" +
"CLIENT MERGE SORT");
assertEquals(expectedPlan,QueryUtil.getExplainPlan(rs));
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
index 226e0e3ae1..065f230bbf 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/join/HashJoinMoreIT.java
@@ -839,21 +839,21 @@ public class HashJoinMoreIT extends
ParallelStatsDisabledIT {
" GROUP BY C.BUCKET, C.\"TIMESTAMP\"";
String p = i == 0 ?
- "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
EVENT_COUNT [0,'5SEC',~1462993520000000000,'Tr/Bal'] -
[1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+ "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
EVENT_COUNT [X'00','5SEC',~1462993520000000000,'Tr/Bal'] -
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
" SERVER AGGREGATE INTO DISTINCT ROWS BY
[\"E.TIMESTAMP\", E.BUCKET]\n" +
"CLIENT MERGE SORT\n" +
" PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
- " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES
OVER " + t[i] + " [0,'5SEC',~1462993520000000000,'Tr/Bal'] -
[1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+ " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES
OVER " + t[i] + " [X'00','5SEC',~1462993520000000000,'Tr/Bal'] -
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY AND
SRC_LOCATION = DST_LOCATION\n" +
" CLIENT MERGE SORT"
:
- "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
EVENT_COUNT [0,'5SEC',~1462993520000000000,'Tr/Bal'] -
[1,'5SEC',~1462993420000000000,'Tr/Bal']\n" +
+ "CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES OVER
EVENT_COUNT [X'00','5SEC',~1462993520000000000,'Tr/Bal'] -
[X'01','5SEC',~1462993420000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
" SERVER AGGREGATE INTO DISTINCT ROWS BY
[\"E.TIMESTAMP\", E.BUCKET]\n" +
"CLIENT MERGE SORT\n" +
" PARALLEL INNER-JOIN TABLE 0 (SKIP MERGE)\n" +
- " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES
OVER " + t[i] + " [0,'5SEC',1462993420000000001,'Tr/Bal'] -
[1,'5SEC',1462993520000000000,'Tr/Bal']\n" +
+ " CLIENT PARALLEL 2-WAY SKIP SCAN ON 2 RANGES
OVER " + t[i] + " [X'00','5SEC',1462993420000000001,'Tr/Bal'] -
[X'01','5SEC',1462993520000000000,'Tr/Bal']\n" +
" SERVER FILTER BY FIRST KEY ONLY AND
SRC_LOCATION = DST_LOCATION\n" +
" CLIENT MERGE SORT";
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g
b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index b0dc1898aa..f988acfbe9 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -110,7 +110,7 @@ tokens
CYCLE='cycle';
CASCADE='cascade';
UPDATE='update';
- STATISTICS='statistics';
+ STATISTICS='statistics';
COLUMNS='columns';
TRACE='trace';
ASYNC='async';
@@ -1152,7 +1152,10 @@ literal_or_bind returns [ParseNode ret]
// Get a string, integer, double, date, boolean, or NULL value.
literal returns [LiteralParseNode ret]
- : s=STRING_LITERAL {
+ :
+ h=hex_literal { ret = h; }
+ | b=bin_literal { ret = b; }
+ | s=STRING_LITERAL {
ret = factory.literal(s.getText());
}
| n=NUMBER {
@@ -1163,7 +1166,7 @@ literal returns [LiteralParseNode ret]
}
| dbl=DOUBLE {
ret = factory.literal(Double.valueOf(dbl.getText()));
- }
+ }
| NULL {ret = factory.literal(null);}
| TRUE {ret = factory.literal(Boolean.TRUE);}
| FALSE {ret = factory.literal(Boolean.FALSE);}
@@ -1175,13 +1178,33 @@ literal returns [LiteralParseNode ret]
}
}
;
-
+
int_or_long_literal returns [LiteralParseNode ret]
: n=NUMBER {
ret = factory.intOrLong(n.getText());
}
;
+hex_literal returns [LiteralParseNode ret]
+@init {StringBuilder sb = new StringBuilder();}
+ :
+ (
+ h=HEX_LITERAL { sb.append(h.getText()); }
+ (s=STRING_LITERAL { sb.append(factory.stringToHexLiteral(s.getText())); }
)*
+ )
+ { ret = factory.hexLiteral(sb.toString()); }
+ ;
+
+bin_literal returns [LiteralParseNode ret]
+@init {StringBuilder sb = new StringBuilder();}
+ :
+ (
+ b=BIN_LITERAL { sb.append(b.getText()); }
+ (s=STRING_LITERAL { sb.append(factory.stringToBinLiteral(s.getText())); }
)*
+ )
+ { ret = factory.binLiteral(sb.toString()); }
+ ;
+
// Bind names are a colon followed by 1+ letter/digits/underscores, or '?'
(unclear how Oracle acutally deals with this, but we'll just treat it as a
special bind)
bind_name returns [String ret]
: n=BIND_NAME { String bind = n.getText().substring(1);
updateBind(bind); $ret = bind; }
@@ -1217,6 +1240,28 @@ BIND_NAME
: COLON (DIGIT)+
;
+HEX_LITERAL
+@init{ StringBuilder sb = new StringBuilder();}
+ :
+ X { $type = NAME;}
+ (
+ (FIELDCHAR) => FIELDCHAR+
+ | ('\'') => '\'' ' '* ( d=HEX_DIGIT { sb.append(d.getText()); } ' '* )*
'\'' { $type=HEX_LITERAL; }
+ )?
+ { if ($type == HEX_LITERAL) { setText(sb.toString()); } }
+ ;
+
+BIN_LITERAL
+@init{ StringBuilder sb = new StringBuilder();}
+ :
+ B { $type = NAME;}
+ (
+ (FIELDCHAR) => FIELDCHAR+
+ | ('\'') => '\'' ' '* ( d=BIN_DIGIT { sb.append(d.getText()); } ' '* )*
'\'' { $type=BIN_LITERAL; }
+ )?
+ { if ($type == BIN_LITERAL) { setText(sb.toString()); } }
+ ;
+
NAME
: LETTER (FIELDCHAR)*
| '\"' (DBL_QUOTE_CHAR)* '\"'
@@ -1371,6 +1416,16 @@ DIGIT
: '0'..'9'
;
+fragment
+HEX_DIGIT
+ : ('0'..'9' | 'a'..'f' | 'A'..'F')
+ ;
+
+fragment
+BIN_DIGIT
+ : ('0' | '1')
+ ;
+
// string literals
STRING_LITERAL
@init{ StringBuilder sb = new StringBuilder(); }
@@ -1390,6 +1445,16 @@ DBL_QUOTE_CHAR
: ( ~('\"') )+
;
+fragment
+X
+ : ( 'X' | 'x' )
+ ;
+
+fragment
+B
+ : ( 'B' | 'b' )
+ ;
+
// escape sequence inside a string literal
fragment
CHAR_ESC
@@ -1413,7 +1478,7 @@ CHAR_ESC
WS
: ( ' ' | '\t' | '\u2002' ) { $channel=HIDDEN; }
;
-
+
EOL
: ('\r' | '\n')
{ skip(); }
@@ -1439,7 +1504,7 @@ SL_COMMENT
DOT
: '.'
;
-
+
OTHER
: . { if (true) // to prevent compile error
throw new RuntimeException("Unexpected char: '" + $text + "'");
}
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
index 9d5cb28d18..774b0ce07d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixConnection.java
@@ -102,6 +102,7 @@ import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.SchemaNotFoundException;
import org.apache.phoenix.schema.TableNotFoundException;
import org.apache.phoenix.schema.types.PArrayDataType;
+import org.apache.phoenix.schema.types.PBinary;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PDate;
import org.apache.phoenix.schema.types.PDecimal;
@@ -361,6 +362,8 @@ public class PhoenixConnection implements MetaDataMutated,
SQLCloseable, Phoenix
formatters.put(PDecimal.INSTANCE,
FunctionArgumentType.NUMERIC.getFormatter(numberPattern));
formatters.put(PVarbinary.INSTANCE, VarBinaryFormatter.INSTANCE);
+ formatters.put(PBinary.INSTANCE, VarBinaryFormatter.INSTANCE);
+
// We do not limit the metaData on a connection less than the
global
// one,
// as there's not much that will be cached here.
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 2bf1bf93f1..f4210c5365 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import
org.apache.phoenix.thirdparty.com.google.common.annotations.VisibleForTesting;
import
org.apache.phoenix.thirdparty.com.google.common.collect.ArrayListMultimap;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
+import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.ExpressionType;
@@ -49,9 +50,11 @@ import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.TypeMismatchException;
import org.apache.phoenix.schema.stats.StatisticsCollectionScope;
+import org.apache.phoenix.schema.types.PBinary;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PLong;
import org.apache.phoenix.schema.types.PTimestamp;
+import org.apache.phoenix.util.ByteUtil;
import org.apache.phoenix.util.SchemaUtil;
import org.apache.phoenix.thirdparty.com.google.common.collect.ListMultimap;
@@ -79,7 +82,6 @@ public class ParseNodeFactory {
private static final Multimap<String, BuiltInFunctionInfo>
BUILT_IN_FUNCTION_MULTIMAP = ArrayListMultimap.create();
private static final BigDecimal MAX_LONG =
BigDecimal.valueOf(Long.MAX_VALUE);
-
/**
*
* Key used to look up a built-in function using the combination of
@@ -555,7 +557,7 @@ public class ParseNodeFactory {
public LiteralParseNode realNumber(String text) {
return new LiteralParseNode(new BigDecimal(text,
PDataType.DEFAULT_MATH_CONTEXT));
}
-
+
public LiteralParseNode wholeNumber(String text) {
int length = text.length();
// We know it'll fit into long, might still fit into int
@@ -585,6 +587,44 @@ public class ParseNodeFactory {
return new LiteralParseNode(l);
}
+ public LiteralParseNode hexLiteral(String text) {
+ // The lexer has already removed everything but the digits
+ int length = text.length();
+ if (length % 2 != 0) {
+ throw new IllegalArgumentException("Hex literals must have an even
number of digits");
+ }
+ byte[] bytes = Bytes.fromHex(text);
+ return new LiteralParseNode(bytes, PBinary.INSTANCE);
+ }
+
+ public String stringToHexLiteral(String in) {
+ String noSpace = in.replaceAll(" ", "");
+ if (!noSpace.matches("^[0-9a-fA-F]+$")) {
+ throw new IllegalArgumentException(
+ "Hex literal continuation line has non hex digit characters");
+ }
+ return noSpace;
+ }
+
+ public LiteralParseNode binLiteral(String text) {
+ // The lexer has already removed everything but the digits
+ int length = text.length();
+ if (length % 8 != 0) {
+ throw new IllegalArgumentException("Binary literals must have a
multiple of 8 digits");
+ }
+ byte[] bytes = ByteUtil.fromAscii(text.toCharArray());
+ return new LiteralParseNode(bytes, PBinary.INSTANCE);
+ }
+
+ public String stringToBinLiteral(String in) {
+ String noSpace = in.replaceAll(" ", "");
+ if (!noSpace.matches("^[0-1]+$")) {
+ throw new IllegalArgumentException(
+ "Binary literal continuation line has non binary digit
characters");
+ }
+ return noSpace;
+ }
+
public CastParseNode cast(ParseNode expression, String dataType, Integer
maxLength, Integer scale) {
return new CastParseNode(expression, dataType, maxLength, scale,
false);
}
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
index 497c90fdae..4ae1f23f8b 100644
---
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
+++
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java
@@ -1144,7 +1144,7 @@ public abstract class PArrayDataType<T> extends
PDataType<T> {
}
public static int estimateSize(int size, PDataType baseType) {
- if (baseType.isFixedWidth()) {
+ if (baseType.isFixedWidth() && baseType.getByteSize() != null) {
return baseType.getByteSize() * size;
} else {
return size * ValueSchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java
index 539bc87ad6..ec5a685222 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PBinary.java
@@ -187,9 +187,6 @@ public class PBinary extends PBinaryBase {
@Override
public String toStringLiteral(byte[] b, int offset, int length, Format
formatter) {
- if (length == 1) {
- return Integer.toString(0xFF & b[offset]);
- }
return PVarbinary.INSTANCE.toStringLiteral(b, offset, length,
formatter);
}
diff --git
a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java
b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java
index 1af460a306..579931344f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PVarbinary.java
@@ -140,15 +140,11 @@ public class PVarbinary extends PBinaryBase {
@Override
public String toStringLiteral(byte[] b, int o, int length, Format
formatter) {
StringBuilder buf = new StringBuilder();
- buf.append('[');
+ buf.append("X'");
if (length > 0) {
- for (int i = o; i < o + length; i++) {
- buf.append(0xFF & b[i]);
- buf.append(',');
- }
- buf.setLength(buf.length()-1);
+ buf.append(Bytes.toHex(b, o, length));
}
- buf.append(']');
+ buf.append("'");
return buf.toString();
}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
b/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
index 0cc9066b6c..f021d7bad5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
@@ -56,7 +56,34 @@ public class ByteUtil {
EMPTY_BYTE_ARRAY);
public static final ImmutableBytesWritable EMPTY_IMMUTABLE_BYTE_ARRAY =
new ImmutableBytesWritable(
EMPTY_BYTE_ARRAY);
-
+
+
+ /** Mask for bit 0 of a byte. */
+ private static final int BIT_0 = 0x01;
+
+ /** Mask for bit 1 of a byte. */
+ private static final int BIT_1 = 0x02;
+
+ /** Mask for bit 2 of a byte. */
+ private static final int BIT_2 = 0x04;
+
+ /** Mask for bit 3 of a byte. */
+ private static final int BIT_3 = 0x08;
+
+ /** Mask for bit 4 of a byte. */
+ private static final int BIT_4 = 0x10;
+
+ /** Mask for bit 5 of a byte. */
+ private static final int BIT_5 = 0x20;
+
+ /** Mask for bit 6 of a byte. */
+ private static final int BIT_6 = 0x40;
+
+ /** Mask for bit 7 of a byte. */
+ private static final int BIT_7 = 0x80;
+
+ private static final int[] BITS = {BIT_7, BIT_6, BIT_5, BIT_4, BIT_3,
BIT_2, BIT_1, BIT_0};
+
public static final Comparator<ImmutableBytesPtr> BYTES_PTR_COMPARATOR =
new Comparator<ImmutableBytesPtr>() {
@Override
@@ -652,4 +679,25 @@ public class ByteUtil {
}
return dst;
}
+
+ // Adapted from the Commons Codec BinaryCodec, but treat the input as a
byte sequence, without
+ // the endinanness reversion in the original code
+ public static byte[] fromAscii(final char[] ascii) {
+ if (ascii == null || ascii.length == 0) {
+ return EMPTY_BYTE_ARRAY;
+ }
+ final int asciiLength = ascii.length;
+ // get length/8 times bytes with 3 bit shifts to the right of the
length
+ final byte[] l_raw = new byte[asciiLength >> 3];
+ // We incr index jj by 8 as we go along to not recompute indices using
multiplication every
+ // time inside the loop.
+ for (int ii = 0, jj = 0; ii < l_raw.length; ii++, jj += 8) {
+ for (int bits = 0; bits < BITS.length; ++bits) {
+ if (ascii[jj + bits] == '1') {
+ l_raw[ii] |= BITS[bits];
+ }
+ }
+ }
+ return l_raw;
+ }
}
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
index a451afedcb..26a674835a 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
@@ -2735,8 +2735,8 @@ public class WhereOptimizerTest extends
BaseConnectionlessQueryTest {
assertTrue(filterList.getFilters().get(0) instanceof
SkipScanFilter);
assertTrue(filterList.getFilters().get(1) instanceof
RowKeyComparisonFilter);
RowKeyComparisonFilter rowKeyComparisonFilter
=(RowKeyComparisonFilter) filterList.getFilters().get(1);
- assertTrue(rowKeyComparisonFilter.toString().equals(
- "(OBJECT_ID, OBJECT_VERSION) IN
([111,98,106,49,0,205,205,205,205],[111,98,106,50,0,206,206,206,206],[111,98,106,51,0,206,206,206,206])"));
+ assertEquals(rowKeyComparisonFilter.toString(),
+ "(OBJECT_ID, OBJECT_VERSION) IN
(X'6f626a3100cdcdcdcd',X'6f626a3200cececece',X'6f626a3300cececece')");
assertTrue(queryPlan.getContext().getScanRanges().isPointLookup());
assertArrayEquals(startKey, scan.getStartRow());
@@ -2780,8 +2780,8 @@ public class WhereOptimizerTest extends
BaseConnectionlessQueryTest {
scan = queryPlan.getContext().getScan();
assertTrue(scan.getFilter() instanceof RowKeyComparisonFilter);
rowKeyComparisonFilter = (RowKeyComparisonFilter)scan.getFilter();
- assertTrue(rowKeyComparisonFilter.toString().equals(
- "((PK1, PK2) IN
([128,0,0,2,128,0,0,3],[128,0,0,2,128,0,0,4]) AND PK3 = 5)"));
+ assertEquals(rowKeyComparisonFilter.toString(),
+ "((PK1, PK2) IN (X'8000000280000003',X'8000000280000004')
AND PK3 = 5)");
assertArrayEquals(
scan.getStartRow(),
ByteUtil.concat(
@@ -2961,9 +2961,8 @@ public class WhereOptimizerTest extends
BaseConnectionlessQueryTest {
assertTrue(filterList.getFilters().get(1) instanceof
RowKeyComparisonFilter);
rowKeyComparisonFilter =(RowKeyComparisonFilter)
filterList.getFilters().get(1);
- assertTrue(rowKeyComparisonFilter.toString().equals(
- "((PK3, PK4) IN
([127,255,255,251,128,0,0,5],[127,255,255,252,128,0,0,4])"+
- " AND (PK5, PK6, PK7) IN
([128,0,0,5,127,255,255,249,128,0,0,7],[128,0,0,6,127,255,255,248,128,0,0,8]))"));
+ assertEquals(rowKeyComparisonFilter.toString(),
+ "((PK3, PK4) IN (X'7ffffffb80000005',X'7ffffffc80000004')
AND (PK5, PK6, PK7) IN
(X'800000057ffffff980000007',X'800000067ffffff880000008'))");
/**
* RVC is not singleKey
*/
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
index 8ad3a4fb61..305ec84610 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
@@ -66,6 +66,15 @@ public class QueryParserTest {
}
}
+ private void parseQueryThatShouldFailWithSQLException(String sql) throws
Exception {
+ try {
+ parseQuery(sql);
+ fail("Query should throw a PhoenixParserException \n " + sql);
+ }
+ catch (SQLException e){
+ }
+ }
+
@Test
public void testParseGrantQuery() throws Exception {
@@ -925,6 +934,51 @@ public class QueryParserTest {
// Expected failures.
parseQueryThatShouldFail("SHOW CREATE VIEW foo");
parseQueryThatShouldFail("SHOW CREATE TABLE 'foo'");
+ }
+ @Test
+ public void testBinaryLiteral() throws Exception {
+ // As per ISO/IEC 9075-2:2011(E) 5.3 <literal> page 163
+ // The literal syntax is:
+ //
+ // <binary string literal> ::=
+ // X <quote> [ <space>... ] [ { <hexit> [ <space>... ] <hexit> [
<space>... ] }... ] <quote>
+ // [ { <separator> <quote> [ <space>... ] [ { <hexit> [ <space>... ]
+ // <hexit> [ <space>... ] }... ] <quote> }... ]
+ //
+ // With the current grammar we only approximate the multi-line syntax,
but
+ // we're close enough for most practical purposes
+ // (We do not enforce a new line before continuation lines)
+
+ // Happy paths
+ parseQuery("SELECT b, x from x WHERE x = x'00'");
+ parseQuery("SELECT b, x from x WHERE x = "
+ + "x'0 12 ' --comment \n /* comment */ '34 567' \n \n 'aA'");
+ parseQuery("SELECT b, x from x WHERE x = "
+ + "b'0 10 ' --comment \n /* comment */ '10 101' \n \n
'00000000'");
+
+ // Expected failures.
+ // Space after 'x'
+ parseQueryThatShouldFailWithSQLException("SELECT b, x from x WHERE x =
x '00'");
+ parseQueryThatShouldFailWithSQLException("SELECT b, x from x WHERE b =
b '00'");
+
+ // Illegal digit character in first line
+ parseQueryThatShouldFail("SELECT b, x from x WHERE x = "
+ + "x'X0 12 ' --comment \n /* comment */ '34 5670' \n \n 'aA'");
+ parseQueryThatShouldFail("SELECT b, x from b WHERE b = "
+ + "b'B0 10 ' --comment \n /* comment */ '10 101' \n \n
'000000000'");
+
+ // Illegal digit character in continuation line
+ parseQueryThatShouldFail("SELECT b, x from x WHERE x = "
+ + "x'0 12 ' --comment \n /* comment */ '34 5670' \n \n 'aA_'");
+ parseQueryThatShouldFail("SELECT b, x from x WHERE x = "
+ + "b'0 10 ' --comment \n /* comment */ '00 0000' \n \n '00_'");
+
+ // No digit between quotes in continuation line
+ parseQueryThatShouldFail("SELECT b, x from x WHERE x = "
+ + "x'0 12 ' --comment \n /* comment */ '34 5670' \n \n ''");
+ parseQueryThatShouldFail("SELECT b, x from x WHERE x = "
+ + "b'0 10 ' --comment \n /* comment */ '00 000' \n \n ''");
}
+
}
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java
index d87989c93e..cb7c261eec 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/query/QueryPlanTest.java
@@ -248,7 +248,7 @@ public class QueryPlanTest extends
BaseConnectionlessQueryTest {
ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " +
query);
String queryPlan = QueryUtil.getExplainPlan(rs);
assertEquals(
- "CLIENT PARALLEL 20-WAY RANGE SCAN OVER FOO
[0,'a',~'2016-01-28 23:59:59.999'] - [19,'a',~'2016-01-28 00:00:00.000']\n" +
+ "CLIENT PARALLEL 20-WAY RANGE SCAN OVER FOO
[X'00','a',~'2016-01-28 23:59:59.999'] - [X'13','a',~'2016-01-28
00:00:00.000']\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
"CLIENT MERGE SORT", queryPlan);
} finally {
@@ -274,7 +274,7 @@ public class QueryPlanTest extends
BaseConnectionlessQueryTest {
ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " +
query);
String queryPlan = QueryUtil.getExplainPlan(rs);
assertEquals(
- "CLIENT PARALLEL 20-WAY ROUND ROBIN RANGE SCAN OVER " +
tableName + " [0,'a',~'2016-01-28 23:59:59.999'] - [19,'a',~'2016-01-28
00:00:00.000']\n" +
+ "CLIENT PARALLEL 20-WAY ROUND ROBIN RANGE SCAN OVER " +
tableName + " [X'00','a',~'2016-01-28 23:59:59.999'] - [X'13','a',~'2016-01-28
00:00:00.000']\n" +
" SERVER FILTER BY FIRST KEY ONLY", queryPlan);
} finally {
conn.close();