PHOENIX-2785 Do not store NULLs for immutable tables.
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/3a8724ee Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/3a8724ee Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/3a8724ee Branch: refs/heads/calcite Commit: 3a8724eee05aaf477bf6085415e781856990e1c0 Parents: b65e385 Author: Lars Hofhansl <la...@apache.org> Authored: Wed Sep 7 14:03:56 2016 -0700 Committer: Lars Hofhansl <la...@apache.org> Committed: Wed Sep 7 14:11:03 2016 -0700 ---------------------------------------------------------------------- .../apache/phoenix/end2end/StoreNullsIT.java | 40 +++++++++++++++++++- .../org/apache/phoenix/schema/PTableImpl.java | 13 ++++--- 2 files changed, 47 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/3a8724ee/phoenix-core/src/it/java/org/apache/phoenix/end2end/StoreNullsIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StoreNullsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StoreNullsIT.java index cbce02e..836418c 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/StoreNullsIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StoreNullsIT.java @@ -34,7 +34,6 @@ import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.SchemaUtil; import org.junit.After; -import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -63,6 +62,8 @@ public class StoreNullsIT extends BaseHBaseManagedTimeTableReuseIT { private static final Log LOG = LogFactory.getLog(StoreNullsIT.class); private static final String WITH_NULLS = generateRandomString(); private static final String WITHOUT_NULLS = generateRandomString(); + private static final String IMMUTABLE_WITH_NULLS = generateRandomString(); + private static final String IMMUTABLE_WITHOUT_NULLS = generateRandomString(); private static Connection conn; private static Statement stmt; @@ -81,6 +82,12 @@ public class StoreNullsIT extends BaseHBaseManagedTimeTableReuseIT { "id SMALLINT NOT NULL PRIMARY KEY, " + "name VARCHAR) " + "VERSIONS = 1000, KEEP_DELETED_CELLS = false"); + stmt.execute("CREATE TABLE " + IMMUTABLE_WITH_NULLS + " (" + + "id SMALLINT NOT NULL PRIMARY KEY, name VARCHAR) " + + "STORE_NULLS = true, VERSIONS = 1, KEEP_DELETED_CELLS = false, IMMUTABLE_ROWS=true"); + stmt.execute("CREATE TABLE " + IMMUTABLE_WITHOUT_NULLS + " (" + + "id SMALLINT NOT NULL PRIMARY KEY, name VARCHAR) " + + "VERSIONS = 1, KEEP_DELETED_CELLS = false, IMMUTABLE_ROWS=true"); } @After @@ -90,6 +97,37 @@ public class StoreNullsIT extends BaseHBaseManagedTimeTableReuseIT { } @Test + public void testStoringNulls() throws SQLException, InterruptedException, IOException { + stmt.executeUpdate("UPSERT INTO " + IMMUTABLE_WITH_NULLS + " VALUES (1, 'v1')"); + stmt.executeUpdate("UPSERT INTO " + IMMUTABLE_WITHOUT_NULLS + " VALUES (1, 'v1')"); + stmt.executeUpdate("UPSERT INTO " + IMMUTABLE_WITH_NULLS + " VALUES (2, null)"); + stmt.executeUpdate("UPSERT INTO " + IMMUTABLE_WITHOUT_NULLS + " VALUES (2, null)"); + + ensureNullsNotStored(IMMUTABLE_WITH_NULLS); + ensureNullsNotStored(IMMUTABLE_WITHOUT_NULLS); + } + + private void ensureNullsNotStored(String tableName) throws IOException { + tableName = SchemaUtil.normalizeIdentifier(tableName); + HTable htable = new HTable(getUtility().getConfiguration(), tableName); + Scan s = new Scan(); + s.setRaw(true); + ResultScanner scanner = htable.getScanner(s); + // first row has a value for name + Result rs = scanner.next(); + assertTrue(rs.containsColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, Bytes.toBytes("NAME"))); + assertTrue(rs.size() == 2); + // 2nd row has not + rs = scanner.next(); + assertFalse(rs.containsColumn(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, Bytes.toBytes("NAME"))); + // and no delete marker either + assertTrue(rs.size() == 1); + assertNull(scanner.next()); + scanner.close(); + htable.close(); + } + + @Test public void testQueryingHistory() throws SQLException, InterruptedException, IOException { stmt.executeUpdate("UPSERT INTO " + WITH_NULLS + " VALUES (1, 'v1')"); stmt.executeUpdate("UPSERT INTO " + WITHOUT_NULLS + " VALUES (1, 'v1')"); http://git-wip-us.apache.org/repos/asf/phoenix/blob/3a8724ee/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java ---------------------------------------------------------------------- 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 c485a30..b9cec02 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 @@ -782,6 +782,7 @@ public class PTableImpl implements PTable { if (Bytes.compareTo(kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength(), qualifier, 0, qualifier.length) == 0) { iterator.remove(); + break; } } } @@ -798,13 +799,15 @@ public class PTableImpl implements PTable { deleteRow = null; byte[] family = column.getFamilyName().getBytes(); byte[] qualifier = column.getName().getBytes(); - PDataType type = column.getDataType(); + PDataType<?> type = column.getDataType(); // Check null, since some types have no byte representation for null boolean isNull = type.isNull(byteValue); - if (isNull && !getStoreNulls()) { - if (!column.isNullable()) { - throw new ConstraintViolationException(name.getString() + "." + column.getName().getString() + " may not be null"); - } + if (isNull && !column.isNullable()) { + throw new ConstraintViolationException(name.getString() + "." + column.getName().getString() + " may not be null"); + } else if (isNull && PTableImpl.this.isImmutableRows()) { + removeIfPresent(setValues, family, qualifier); + removeIfPresent(unsetValues, family, qualifier); + } else if (isNull && !getStoreNulls()) { removeIfPresent(setValues, family, qualifier); deleteQuietly(unsetValues, kvBuilder, kvBuilder.buildDeleteColumns(keyPtr, column .getFamilyName().getBytesPtr(), column.getName().getBytesPtr(), ts));