You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ya...@apache.org on 2020/07/29 21:58:43 UTC
[phoenix] branch master updated: PHOENIX-6022 RVC Offset does not
handle trailing nulls properly
This is an automated email from the ASF dual-hosted git repository.
yanxinyi 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 ce0de92 PHOENIX-6022 RVC Offset does not handle trailing nulls properly
ce0de92 is described below
commit ce0de92029efecbfe3be2bd47dfc57e1da7cf6a0
Author: Daniel Wong <da...@salesforce.com>
AuthorDate: Wed Jul 29 13:02:34 2020 -0700
PHOENIX-6022 RVC Offset does not handle trailing nulls properly
Signed-off-by: Xinyi Yan <ya...@apache.org>
---
.../end2end/RowValueConstructorOffsetIT.java | 83 ++++++++++++++++++--
.../apache/phoenix/compile/RVCOffsetCompiler.java | 88 ++++++++++++++++------
.../phoenix/compile/RVCOffsetCompilerTest.java | 33 ++++----
3 files changed, 159 insertions(+), 45 deletions(-)
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorOffsetIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorOffsetIT.java
index 841e4aa..0981757 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorOffsetIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorOffsetIT.java
@@ -21,6 +21,7 @@ import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -1116,19 +1117,22 @@ public class RowValueConstructorOffsetIT extends ParallelStatsDisabledIT {
@Test
public void rvcOffsetTrailingVariableLengthKeyTest() throws Exception {
- String TEST_DDL = "CREATE TABLE IF NOT EXISTS TEST_SCHEMA (\n"
+
+ String tableName = generateUniqueName();
+ String ddl = String.format("CREATE TABLE IF NOT EXISTS %s (\n"
+ " ORGANIZATION_ID VARCHAR(15), \n" + " TEST_ID VARCHAR(15), \n"
+ " CREATED_DATE DATE, \n" + " LAST_UPDATE DATE\n"
- + " CONSTRAINT TEST_SCHEMA_PK PRIMARY KEY (ORGANIZATION_ID, TEST_ID) \n" + ")";
+ + " CONSTRAINT TEST_SCHEMA_PK PRIMARY KEY (ORGANIZATION_ID, TEST_ID) \n" + ")",tableName);
try (Statement statement = conn.createStatement()) {
- statement.execute(TEST_DDL);
+ statement.execute(ddl);
}
//setup
+ String upsert = "UPSERT INTO %s(ORGANIZATION_ID,TEST_ID) VALUES (%s,%s)";
List<String> upserts = new ArrayList<>();
- upserts.add("UPSERT INTO TEST_SCHEMA(ORGANIZATION_ID,TEST_ID) VALUES ('1','1')");
- upserts.add("UPSERT INTO TEST_SCHEMA(ORGANIZATION_ID,TEST_ID) VALUES ('1','10')");
- upserts.add("UPSERT INTO TEST_SCHEMA(ORGANIZATION_ID,TEST_ID) VALUES ('2','2')");
+ upserts.add(String.format(upsert,tableName,"'1'","'1'"));
+ upserts.add(String.format(upsert,tableName,"'1'","'10'"));
+ upserts.add(String.format(upsert,tableName,"'2'","'2'"));
for(String sql : upserts) {
try (Statement statement = conn.createStatement()) {
@@ -1137,7 +1141,7 @@ public class RowValueConstructorOffsetIT extends ParallelStatsDisabledIT {
}
conn.commit();
- String query = "SELECT * FROM TEST_SCHEMA OFFSET (ORGANIZATION_ID,TEST_ID) = ('1','1')";
+ String query = String.format("SELECT * FROM %s OFFSET (ORGANIZATION_ID,TEST_ID) = ('1','1')",tableName);
try (Statement statement = conn.createStatement() ; ResultSet rs2 = statement.executeQuery(query) ) {
assertTrue(rs2.next());
@@ -1150,4 +1154,69 @@ public class RowValueConstructorOffsetIT extends ParallelStatsDisabledIT {
}
}
+ //Note that phoenix does not suport a rowkey with a null inserted so there is no single key version of this.
+ @Test
+ public void rvcOffsetTrailingNullKeyTest() throws Exception {
+ String tableName = generateUniqueName();
+ String ddl = String.format("CREATE TABLE IF NOT EXISTS %s (\n"
+ + " ORGANIZATION_ID VARCHAR(15), \n" + " TEST_ID VARCHAR(15), \n"
+ + " CREATED_DATE DATE, \n" + " LAST_UPDATE DATE\n"
+ + " CONSTRAINT TEST_SCHEMA_PK PRIMARY KEY (ORGANIZATION_ID, TEST_ID) \n" + ")",tableName);
+
+ try (Statement statement = conn.createStatement()) {
+ statement.execute(ddl);
+ }
+ //setup
+ String upsert = "UPSERT INTO %s(ORGANIZATION_ID,TEST_ID) VALUES (%s,%s)";
+ List<String> upserts = new ArrayList<>();
+ upserts.add(String.format(upsert,tableName,"'0'","null"));
+ upserts.add(String.format(upsert,tableName,"'1'","null"));
+ upserts.add(String.format(upsert,tableName,"'1'","'1'"));
+ upserts.add(String.format(upsert,tableName,"'1'","'10'"));
+ upserts.add(String.format(upsert,tableName,"'2'","null"));
+
+ for(String sql : upserts) {
+ try (Statement statement = conn.createStatement()) {
+ statement.execute(sql);
+ }
+ }
+ conn.commit();
+
+ String query = String.format("SELECT * FROM %s OFFSET (ORGANIZATION_ID,TEST_ID) = ('1',null)",tableName);
+
+ try (Statement statement = conn.createStatement() ; ResultSet rs2 = statement.executeQuery(query) ) {
+ assertTrue(rs2.next());
+ assertEquals("1",rs2.getString(1));
+ assertEquals("1",rs2.getString(2));
+ assertTrue(rs2.next());
+ assertEquals("1",rs2.getString(1));
+ assertEquals("10",rs2.getString(2));
+ assertTrue(rs2.next());
+ assertEquals("2",rs2.getString(1));
+ assertNull(rs2.getString(2));
+ assertFalse(rs2.next());
+ }
+
+ String preparedQuery = String.format("SELECT * FROM %s OFFSET (ORGANIZATION_ID,TEST_ID) = (?,?)",tableName);
+
+ try (PreparedStatement statement = conn.prepareStatement(preparedQuery) ) {
+
+ statement.setString(1,"1");
+ statement.setString(2,null);
+
+ try(ResultSet rs2 = statement.executeQuery()) {
+ assertTrue(rs2.next());
+ assertEquals("1", rs2.getString(1));
+ assertEquals("1", rs2.getString(2));
+ assertTrue(rs2.next());
+ assertEquals("1", rs2.getString(1));
+ assertEquals("10", rs2.getString(2));
+ assertTrue(rs2.next());
+ assertEquals("2", rs2.getString(1));
+ assertNull(rs2.getString(2));
+ assertFalse(rs2.next());
+ }
+ }
+ }
+
}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/RVCOffsetCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/RVCOffsetCompiler.java
index bf6514d..afcd00e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/RVCOffsetCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/RVCOffsetCompiler.java
@@ -195,9 +195,12 @@ public class RVCOffsetCompiler {
}
// Now that columns etc have been resolved lets check to make sure they match the pk order
- List<RowKeyColumnExpression>
- rowKeyColumnExpressionList =
+ RowKeyColumnExpressionOutput rowKeyColumnExpressionOutput =
buildListOfRowKeyColumnExpressions(whereExpression, isIndex);
+
+ List<RowKeyColumnExpression>
+ rowKeyColumnExpressionList = rowKeyColumnExpressionOutput.getRowKeyColumnExpressions();
+
if (rowKeyColumnExpressionList.size() != numUserColumns) {
LOGGER.warn("Unexpected error while compiling RVC Offset, expected " + numUserColumns
+ " found " + rowKeyColumnExpressionList.size());
@@ -240,34 +243,41 @@ public class RVCOffsetCompiler {
}
}
+ byte[] key;
+
// check to see if this was a single key expression
ScanRanges scanRanges = context.getScanRanges();
+ //We do not generate a point lookup today in phoenix if the rowkey has a trailing null, we generate a range scan.
if (!scanRanges.isPointLookup()) {
- throw new RowValueConstructorOffsetNotCoercibleException(
- "RVC Offset must be a point lookup.");
- }
-
-
- RowKeySchema.RowKeySchemaBuilder builder = new RowKeySchema.RowKeySchemaBuilder(columns.size());
+ //Since we use a range scan to guarantee we get only the null value and the upper bound is unset this suffices
+ //sanity check
+ if (!rowKeyColumnExpressionOutput.isTrailingNull()) {
+ throw new RowValueConstructorOffsetNotCoercibleException(
+ "RVC Offset must be a point lookup.");
+ }
+ key = scanRanges.getScanRange().getUpperRange();
+ } else {
+ RowKeySchema.RowKeySchemaBuilder builder = new RowKeySchema.RowKeySchemaBuilder(columns.size());
- for(PColumn column : columns) {
- builder.addField(column, column.isNullable(), column.getSortOrder());
- }
+ for (PColumn column : columns) {
+ builder.addField(column, column.isNullable(), column.getSortOrder());
+ }
- RowKeySchema rowKeySchema = builder.build();
+ RowKeySchema rowKeySchema = builder.build();
- //we make a ScanRange with 1 keyslots that cover the entire PK to reuse the code
- KeyRange pointKeyRange = scanRanges.getScanRange();
- KeyRange keyRange = KeyRange.getKeyRange(pointKeyRange.getLowerRange(), false, KeyRange.UNBOUND, true);
- List<KeyRange> myRangeList = Lists.newArrayList(keyRange);
- List<List<KeyRange>> slots = new ArrayList<>();
- slots.add(myRangeList);
- int[] slotSpan = new int[1];
+ //we make a ScanRange with 1 keyslots that cover the entire PK to reuse the code
+ KeyRange pointKeyRange = scanRanges.getScanRange();
+ KeyRange keyRange = KeyRange.getKeyRange(pointKeyRange.getLowerRange(), false, KeyRange.UNBOUND, true);
+ List<KeyRange> myRangeList = Lists.newArrayList(keyRange);
+ List<List<KeyRange>> slots = new ArrayList<>();
+ slots.add(myRangeList);
+ int[] slotSpan = new int[1];
- //subtract 1 see ScanUtil.SINGLE_COLUMN_SLOT_SPAN is 0
- slotSpan[0] = columns.size() - 1;
- byte[] key = ScanUtil.getMinKey(rowKeySchema, slots, slotSpan);
+ //subtract 1 see ScanUtil.SINGLE_COLUMN_SLOT_SPAN is 0
+ slotSpan[0] = columns.size() - 1;
+ key = ScanUtil.getMinKey(rowKeySchema, slots, slotSpan);
+ }
// Note the use of ByteUtil.nextKey() to generate exclusive offset
CompiledOffset
@@ -279,10 +289,30 @@ public class RVCOffsetCompiler {
}
@VisibleForTesting
- List<RowKeyColumnExpression> buildListOfRowKeyColumnExpressions (
+ static class RowKeyColumnExpressionOutput {
+ public RowKeyColumnExpressionOutput(List<RowKeyColumnExpression> rowKeyColumnExpressions, boolean trailingNull) {
+ this.rowKeyColumnExpressions = rowKeyColumnExpressions;
+ this.trailingNull = trailingNull;
+ }
+
+ private final List<RowKeyColumnExpression> rowKeyColumnExpressions;
+ private final boolean trailingNull;
+
+ public List<RowKeyColumnExpression> getRowKeyColumnExpressions() {
+ return rowKeyColumnExpressions;
+ }
+
+ public boolean isTrailingNull() {
+ return trailingNull;
+ }
+ }
+
+ @VisibleForTesting
+ RowKeyColumnExpressionOutput buildListOfRowKeyColumnExpressions (
Expression whereExpression, boolean isIndex)
throws RowValueConstructorOffsetNotCoercibleException, RowValueConstructorOffsetInternalErrorException {
+ boolean trailingNull = false;
List<Expression> expressions;
if((whereExpression instanceof AndExpression)) {
expressions = whereExpression.getChildren();
@@ -298,13 +328,21 @@ public class RVCOffsetCompiler {
List<RowKeyColumnExpression>
rowKeyColumnExpressionList =
new ArrayList<RowKeyColumnExpression>();
- for (Expression child : expressions) {
+ for (int i = 0; i < expressions.size(); i++) {
+ Expression child = expressions.get(i);
if (!(child instanceof ComparisonExpression || child instanceof IsNullExpression)) {
LOGGER.warn("Unexpected error while compiling RVC Offset");
throw new RowValueConstructorOffsetNotCoercibleException(
"RVC Offset must specify the tables PKs.");
}
+ //if this is the last position
+ if(i == expressions.size() - 1) {
+ if(child instanceof IsNullExpression) {
+ trailingNull = true;
+ }
+ }
+
//For either case comparison/isNull the first child should be the rowkey
Expression possibleRowKeyColumnExpression = child.getChildren().get(0);
@@ -325,7 +363,7 @@ public class RVCOffsetCompiler {
}
rowKeyColumnExpressionList.add((RowKeyColumnExpression) possibleRowKeyColumnExpression);
}
- return rowKeyColumnExpressionList;
+ return new RowKeyColumnExpressionOutput(rowKeyColumnExpressionList,trailingNull);
}
@VisibleForTesting List<ColumnParseNode> buildListOfColumnParseNodes(
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/RVCOffsetCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/RVCOffsetCompilerTest.java
index a1c62b4..93c2c48 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/RVCOffsetCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/RVCOffsetCompilerTest.java
@@ -18,6 +18,7 @@
package org.apache.phoenix.compile;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import java.util.ArrayList;
@@ -119,9 +120,10 @@ public class RVCOffsetCompilerTest {
AndExpression expression = mock(AndExpression.class);
Mockito.when(expression.getChildren()).thenReturn(expressions);
+ RVCOffsetCompiler.RowKeyColumnExpressionOutput
+ output = offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
List<RowKeyColumnExpression>
- result =
- offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
+ result = output.getRowKeyColumnExpressions();
assertEquals(2,result.size());
assertEquals(rvc1,result.get(0));
@@ -156,9 +158,10 @@ public class RVCOffsetCompilerTest {
AndExpression expression = mock(AndExpression.class);
Mockito.when(expression.getChildren()).thenReturn(expressions);
+ RVCOffsetCompiler.RowKeyColumnExpressionOutput
+ output = offsetCompiler.buildListOfRowKeyColumnExpressions(expression, true);
List<RowKeyColumnExpression>
- result =
- offsetCompiler.buildListOfRowKeyColumnExpressions(expression, true);
+ result = output.getRowKeyColumnExpressions();
assertEquals(2,result.size());
assertEquals(rvc1,result.get(0));
@@ -175,9 +178,10 @@ public class RVCOffsetCompilerTest {
Mockito.when(expression.getChildren()).thenReturn(Lists.<Expression>newArrayList(rvc));
+ RVCOffsetCompiler.RowKeyColumnExpressionOutput
+ output = offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
List<RowKeyColumnExpression>
- result =
- offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
+ result = output.getRowKeyColumnExpressions();
assertEquals(1,result.size());
assertEquals(rvc,result.get(0));
@@ -193,12 +197,14 @@ public class RVCOffsetCompilerTest {
Mockito.when(expression.getChildren()).thenReturn(Lists.<Expression>newArrayList(rvc));
- List<RowKeyColumnExpression>
- result =
- offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
+ RVCOffsetCompiler.RowKeyColumnExpressionOutput output = offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
+
+ List<RowKeyColumnExpression> result = output.getRowKeyColumnExpressions();
assertEquals(1,result.size());
assertEquals(rvc,result.get(0));
+
+ assertTrue(output.isTrailingNull());
}
@Test
@@ -220,13 +226,14 @@ public class RVCOffsetCompilerTest {
AndExpression expression = mock(AndExpression.class);
Mockito.when(expression.getChildren()).thenReturn(expressions);
- List<RowKeyColumnExpression>
- result =
- offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
+ RVCOffsetCompiler.RowKeyColumnExpressionOutput output = offsetCompiler.buildListOfRowKeyColumnExpressions(expression, false);
+
+ List<RowKeyColumnExpression> result = output.getRowKeyColumnExpressions();
assertEquals(2,result.size());
assertEquals(rvc1,result.get(0));
-
assertEquals(rvc2,result.get(1));
+
+ assertTrue(output.isTrailingNull());
}
}