PHOENIX-1578 Introduce STORE_NULLS table option Add the STORE_NULLS table option. This causes null values to be store explicitly as empty byte arrays, instead of changing null upserts into HBase deletes.
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/9cd7e86b Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/9cd7e86b Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/9cd7e86b Branch: refs/heads/4.0 Commit: 9cd7e86b0d87e7f5090f2654bf179a04fb7adccc Parents: 3a92435 Author: Gabriel Reid <gabri...@ngdata.com> Authored: Mon Jan 12 16:30:57 2015 +0100 Committer: Gabriel Reid <gabri...@ngdata.com> Committed: Wed Jan 14 08:22:39 2015 +0100 ---------------------------------------------------------------------- .../apache/phoenix/end2end/AlterTableIT.java | 367 +++++++++--------- .../apache/phoenix/end2end/StoreNullsIT.java | 207 ++++++++++ .../apache/phoenix/compile/FromCompiler.java | 59 +-- .../apache/phoenix/compile/JoinCompiler.java | 364 +++++++++--------- .../coprocessor/MetaDataEndpointImpl.java | 113 +++--- .../coprocessor/generated/PTableProtos.java | 111 +++++- .../phoenix/jdbc/PhoenixDatabaseMetaData.java | 52 +-- .../apache/phoenix/query/QueryConstants.java | 48 +-- .../apache/phoenix/schema/DelegateTable.java | 7 +- .../apache/phoenix/schema/MetaDataClient.java | 377 ++++++++++--------- .../java/org/apache/phoenix/schema/PTable.java | 51 +-- .../org/apache/phoenix/schema/PTableImpl.java | 141 +++---- .../apache/phoenix/schema/TableProperty.java | 13 +- phoenix-protocol/src/main/PTable.proto | 3 +- 14 files changed, 1147 insertions(+), 766 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java index 9a262cb..dcb6237 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java @@ -33,6 +33,7 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.Collections; import java.util.Map; import java.util.Properties; @@ -57,14 +58,14 @@ import org.junit.BeforeClass; import org.junit.Test; /** - * + * * A lot of tests in this class test HBase level properties. As a result, - * tests need to have non-overlapping table names. The option of + * tests need to have non-overlapping table names. The option of * disabling and dropping underlying HBase tables at the end of each test - * to avoid the overlap makes the test class really slow. By having the + * to avoid the overlap makes the test class really slow. By having the * test class run in its own cluster and having non overlapping table names * we don't need to worry about dropping the tables between each test - * or at the end of test class. + * or at the end of test class. * */ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { @@ -75,25 +76,25 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { public static final String DATA_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "T"); public static final String INDEX_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "I"); public static final String LOCAL_INDEX_TABLE_FULL_NAME = SchemaUtil.getTableName(SCHEMA_NAME, "LI"); - + @BeforeClass public static void doSetup() throws Exception { Map<String, String> props = Collections.emptyMap(); setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator())); } - + @Test public void testAlterTableWithVarBinaryKey() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + try { String ddl = "CREATE TABLE test_table " + " (a_string varchar not null, a_binary varbinary not null, col1 integer" + " CONSTRAINT pk PRIMARY KEY (a_string, a_binary))\n"; createTestTable(getUrl(), ddl); - + ddl = "ALTER TABLE test_table ADD b_string VARCHAR NULL PRIMARY KEY"; PreparedStatement stmt = conn.prepareStatement(ddl); stmt.execute(); @@ -111,9 +112,9 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP + 1)); Connection conn = DriverManager.getConnection(getUrl(), props); - + try{ - conn.createStatement().executeUpdate("ALTER TABLE " + PhoenixDatabaseMetaData.SYSTEM_CATALOG + + conn.createStatement().executeUpdate("ALTER TABLE " + PhoenixDatabaseMetaData.SYSTEM_CATALOG + " ADD IF NOT EXISTS testNewColumn integer"); String query = "SELECT testNewColumn FROM " + PhoenixDatabaseMetaData.SYSTEM_CATALOG; try { @@ -149,13 +150,13 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + try { String ddl = "CREATE TABLE test_table " + " (a_string varchar not null, col1 integer" + " CONSTRAINT pk PRIMARY KEY (a_string))\n"; conn.createStatement().execute(ddl); - + String dml = "UPSERT INTO test_table VALUES(?)"; PreparedStatement stmt = conn.prepareStatement(dml); stmt.setString(1, "b"); @@ -163,7 +164,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(1, "a"); stmt.execute(); conn.commit(); - + String query = "SELECT * FROM test_table"; ResultSet rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -171,49 +172,49 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertTrue(rs.next()); assertEquals("b",rs.getString(1)); assertFalse(rs.next()); - + ddl = "ALTER TABLE test_table ADD b_string VARCHAR NULL PRIMARY KEY "; conn.createStatement().execute(ddl); - + query = "SELECT * FROM test_table WHERE a_string = 'a' AND b_string IS NULL"; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("a",rs.getString(1)); assertFalse(rs.next()); - + dml = "UPSERT INTO test_table VALUES(?)"; stmt = conn.prepareStatement(dml); stmt.setString(1, "c"); stmt.execute(); conn.commit(); - + query = "SELECT * FROM test_table WHERE a_string = 'c' AND b_string IS NULL"; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("c",rs.getString(1)); assertFalse(rs.next()); - + dml = "UPSERT INTO test_table(a_string,col1) VALUES(?,?)"; stmt = conn.prepareStatement(dml); stmt.setString(1, "a"); stmt.setInt(2, 5); stmt.execute(); conn.commit(); - + query = "SELECT a_string,col1 FROM test_table WHERE a_string = 'a' AND b_string IS NULL"; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("a",rs.getString(1)); assertEquals(5,rs.getInt(2)); // TODO: figure out why this flaps assertFalse(rs.next()); - + } finally { conn.close(); } } - - + + @Test public void testSetPropertyAndAddColumnForNewColumnFamily() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -236,22 +237,22 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + private static void assertIndexExists(Connection conn, boolean exists) throws SQLException { ResultSet rs = conn.getMetaData().getIndexInfo(null, SCHEMA_NAME, DATA_TABLE_NAME, false, false); assertEquals(exists, rs.next()); } - + @Test public void testDropIndexedColumn() throws Exception { String query; ResultSet rs; PreparedStatement stmt; - + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + // make sure that the tables are empty, but reachable conn.createStatement().execute( "CREATE TABLE " + DATA_TABLE_FULL_NAME @@ -259,7 +260,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - + conn.createStatement().execute( "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1, v2)"); conn.createStatement().execute( @@ -268,7 +269,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - + // load some data into the table stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?,?)"); stmt.setString(1, "a"); @@ -276,25 +277,25 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(3, "1"); stmt.execute(); conn.commit(); - + assertIndexExists(conn,true); conn.createStatement().execute("ALTER TABLE " + DATA_TABLE_FULL_NAME + " DROP COLUMN v1"); assertIndexExists(conn,false); - + query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("a",rs.getString(1)); assertEquals("1",rs.getString(2)); assertFalse(rs.next()); - + // load some data into the table stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?)"); stmt.setString(1, "a"); stmt.setString(2, "2"); stmt.execute(); conn.commit(); - + query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -302,17 +303,17 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals("2",rs.getString(2)); assertFalse(rs.next()); } - + @Test public void testDropCoveredColumn() throws Exception { String query; ResultSet rs; PreparedStatement stmt; - + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + // make sure that the tables are empty, but reachable conn.createStatement().execute( "CREATE TABLE " + DATA_TABLE_FULL_NAME @@ -320,7 +321,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - + conn.createStatement().execute( "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) include (v2, v3)"); conn.createStatement().execute( @@ -331,7 +332,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { query = "SELECT * FROM " + LOCAL_INDEX_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - + // load some data into the table stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?,?,?)"); stmt.setString(1, "a"); @@ -340,12 +341,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(4, "j"); stmt.execute(); conn.commit(); - + assertIndexExists(conn,true); conn.createStatement().execute("ALTER TABLE " + DATA_TABLE_FULL_NAME + " DROP COLUMN v2"); // TODO: verify meta data that we get back to confirm our column was dropped assertIndexExists(conn,true); - + query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -353,7 +354,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals("x",rs.getString(2)); assertEquals("j",rs.getString(3)); assertFalse(rs.next()); - + // load some data into the table stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?,?)"); stmt.setString(1, "a"); @@ -361,7 +362,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(3, "k"); stmt.execute(); conn.commit(); - + query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -370,17 +371,17 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals("k",rs.getString(3)); assertFalse(rs.next()); } - + @Test public void testAddPKColumnToTableWithIndex() throws Exception { String query; ResultSet rs; PreparedStatement stmt; - + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + // make sure that the tables are empty, but reachable conn.createStatement().execute( "CREATE TABLE " + DATA_TABLE_FULL_NAME @@ -388,13 +389,13 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - + conn.createStatement().execute( "CREATE INDEX " + INDEX_TABLE_NAME + " ON " + DATA_TABLE_FULL_NAME + " (v1) include (v2)"); query = "SELECT * FROM " + INDEX_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - + // load some data into the table stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + " VALUES(?,?,?)"); stmt.setString(1, "a"); @@ -402,7 +403,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(3, "1"); stmt.execute(); conn.commit(); - + assertIndexExists(conn,true); conn.createStatement().execute("ALTER TABLE " + DATA_TABLE_FULL_NAME + " ADD v3 VARCHAR, k2 DECIMAL PRIMARY KEY"); rs = conn.getMetaData().getPrimaryKeys("", SCHEMA_NAME, DATA_TABLE_NAME); @@ -423,9 +424,9 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertTrue(rs.next()); assertEquals(IndexUtil.INDEX_COLUMN_NAME_SEP + "K2",rs.getString("COLUMN_NAME")); assertEquals(3, rs.getShort("KEY_SEQ")); - + assertIndexExists(conn,true); - + query = "SELECT * FROM " + DATA_TABLE_FULL_NAME; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -434,7 +435,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals("1",rs.getString(3)); assertNull(rs.getBigDecimal(4)); assertFalse(rs.next()); - + // load some data into the table stmt = conn.prepareStatement("UPSERT INTO " + DATA_TABLE_FULL_NAME + "(K,K2,V1,V2) VALUES(?,?,?,?)"); stmt.setString(1, "b"); @@ -443,7 +444,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(4, "2"); stmt.execute(); conn.commit(); - + query = "SELECT k,k2 FROM " + DATA_TABLE_FULL_NAME + " WHERE v1='y'"; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -451,87 +452,87 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(BigDecimal.valueOf(2),rs.getBigDecimal(2)); assertFalse(rs.next()); } - + @Test public void testSetSaltedTableAsImmutable() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + try { - String ddl = "CREATE TABLE MESSAGES (\n" + - " SENDER_ID UNSIGNED_LONG NOT NULL,\n" + - " RECIPIENT_ID UNSIGNED_LONG NOT NULL,\n" + - " M_TIMESTAMP DATE NOT NULL,\n" + - " ROW_ID UNSIGNED_LONG NOT NULL,\n" + - " IS_READ TINYINT,\n" + - " IS_DELETED TINYINT,\n" + - " VISIBILITY TINYINT,\n" + - " B.SENDER_IP VARCHAR,\n" + - " B.JSON VARCHAR,\n" + - " B.M_TEXT VARCHAR\n" + - " CONSTRAINT ROWKEY PRIMARY KEY\n" + - "(SENDER_ID,RECIPIENT_ID,M_TIMESTAMP DESC,ROW_ID))\n" + + String ddl = "CREATE TABLE MESSAGES (\n" + + " SENDER_ID UNSIGNED_LONG NOT NULL,\n" + + " RECIPIENT_ID UNSIGNED_LONG NOT NULL,\n" + + " M_TIMESTAMP DATE NOT NULL,\n" + + " ROW_ID UNSIGNED_LONG NOT NULL,\n" + + " IS_READ TINYINT,\n" + + " IS_DELETED TINYINT,\n" + + " VISIBILITY TINYINT,\n" + + " B.SENDER_IP VARCHAR,\n" + + " B.JSON VARCHAR,\n" + + " B.M_TEXT VARCHAR\n" + + " CONSTRAINT ROWKEY PRIMARY KEY\n" + + "(SENDER_ID,RECIPIENT_ID,M_TIMESTAMP DESC,ROW_ID))\n" + "SALT_BUCKETS=4"; conn.createStatement().execute(ddl); - + ddl = "ALTER TABLE MESSAGES SET IMMUTABLE_ROWS=true"; conn.createStatement().execute(ddl); - + conn.createStatement().executeQuery("select count(*) from messages").next(); - + } finally { conn.close(); } } - - + + @Test public void testDropColumnFromSaltedTable() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + try { - String ddl = "CREATE TABLE MESSAGES (\n" + - " SENDER_ID UNSIGNED_LONG NOT NULL,\n" + - " RECIPIENT_ID UNSIGNED_LONG NOT NULL,\n" + - " M_TIMESTAMP DATE NOT NULL,\n" + - " ROW_ID UNSIGNED_LONG NOT NULL,\n" + - " IS_READ TINYINT,\n" + - " IS_DELETED TINYINT,\n" + - " VISIBILITY TINYINT,\n" + - " B.SENDER_IP VARCHAR,\n" + - " B.JSON VARCHAR,\n" + - " B.M_TEXT VARCHAR\n" + - " CONSTRAINT ROWKEY PRIMARY KEY\n" + - "(SENDER_ID,RECIPIENT_ID,M_TIMESTAMP DESC,ROW_ID))\n" + + String ddl = "CREATE TABLE MESSAGES (\n" + + " SENDER_ID UNSIGNED_LONG NOT NULL,\n" + + " RECIPIENT_ID UNSIGNED_LONG NOT NULL,\n" + + " M_TIMESTAMP DATE NOT NULL,\n" + + " ROW_ID UNSIGNED_LONG NOT NULL,\n" + + " IS_READ TINYINT,\n" + + " IS_DELETED TINYINT,\n" + + " VISIBILITY TINYINT,\n" + + " B.SENDER_IP VARCHAR,\n" + + " B.JSON VARCHAR,\n" + + " B.M_TEXT VARCHAR\n" + + " CONSTRAINT ROWKEY PRIMARY KEY\n" + + "(SENDER_ID,RECIPIENT_ID,M_TIMESTAMP DESC,ROW_ID))\n" + "SALT_BUCKETS=4"; conn.createStatement().execute(ddl); - + ddl = "ALTER TABLE MESSAGES DROP COLUMN B.JSON"; conn.createStatement().execute(ddl); - + conn.createStatement().executeQuery("select count(*) from messages").next(); } finally { conn.close(); } } - - + + @Test public void testAddVarCols() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); - + try { String ddl = "CREATE TABLE test_table " + " (a_string varchar not null, col1 integer" + " CONSTRAINT pk PRIMARY KEY (a_string))\n"; conn.createStatement().execute(ddl); - + String dml = "UPSERT INTO test_table VALUES(?)"; PreparedStatement stmt = conn.prepareStatement(dml); stmt.setString(1, "b"); @@ -539,7 +540,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setString(1, "a"); stmt.execute(); conn.commit(); - + String query = "SELECT * FROM test_table"; ResultSet rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -547,16 +548,16 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertTrue(rs.next()); assertEquals("b",rs.getString(1)); assertFalse(rs.next()); - - + + query = "SELECT * FROM test_table WHERE a_string = 'a' "; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("a",rs.getString(1)); - + ddl = "ALTER TABLE test_table ADD c1.col2 VARCHAR , c1.col3 integer , c2.col4 integer"; conn.createStatement().execute(ddl); - + ddl = "ALTER TABLE test_table ADD col5 integer , c1.col2 VARCHAR"; try { conn.createStatement().execute(ddl); @@ -564,18 +565,18 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { } catch (SQLException e) { assertEquals(SQLExceptionCode.COLUMN_EXIST_IN_DEF.getErrorCode(), e.getErrorCode()); } - + query = "SELECT col5 FROM test_table"; try { conn.createStatement().executeQuery(query); - fail(); + fail(); } catch(SQLException e) { assertTrue(e.getMessage(), e.getMessage().contains("ERROR 504 (42703): Undefined column.")); } - + ddl = "ALTER TABLE test_table ADD IF NOT EXISTS col5 integer , c1.col2 VARCHAR"; conn.createStatement().execute(ddl); - + dml = "UPSERT INTO test_table VALUES(?,?,?,?,?)"; stmt = conn.prepareStatement(dml); stmt.setString(1, "c"); @@ -585,7 +586,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setInt(5, 102); stmt.execute(); conn.commit(); - + query = "SELECT * FROM test_table WHERE a_string = 'c' "; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -595,18 +596,18 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(101,rs.getInt(4)); assertEquals(102,rs.getInt(5)); assertFalse(rs.next()); - + ddl = "ALTER TABLE test_table ADD col5 integer"; conn.createStatement().execute(ddl); - + query = "SELECT c1.* FROM test_table WHERE a_string = 'c' "; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("d",rs.getString(1)); assertEquals(101,rs.getInt(2)); assertFalse(rs.next()); - - + + dml = "UPSERT INTO test_table(a_string,col1,col5) VALUES(?,?,?)"; stmt = conn.prepareStatement(dml); stmt.setString(1, "e"); @@ -614,8 +615,8 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { stmt.setInt(3, 201); stmt.execute(); conn.commit(); - - + + query = "SELECT a_string,col1,col5 FROM test_table WHERE a_string = 'e' "; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -623,7 +624,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(200,rs.getInt(2)); assertEquals(201,rs.getInt(3)); assertFalse(rs.next()); - + } finally { conn.close(); } @@ -648,7 +649,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testDisallowAddingNotNullableColumnNotPartOfPkForExistingTable() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -683,7 +684,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class); assertEquals(expectedValue, pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).isWALDisabled()); } - + @Test public void testDisableWAL() throws Exception { String fullTableName = "TEST_TABLE"; @@ -714,7 +715,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertFalse(rs.next()); conn2.close(); asssertIsWALDisabled(conn,fullIndexName, false); - + conn.createStatement().execute("DROP TABLE test_table"); } finally { conn.close(); @@ -773,7 +774,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertFalse(rs.next()); conn2.close(); asssertIsWALDisabled(conn,fullIndexName, false); - + } finally { conn.close(); } @@ -828,7 +829,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { String ddl = "ALTER TABLE test_table DROP COLUMN IF EXISTS col2,col3"; conn.createStatement().execute(ddl); - + ddl = "ALTER TABLE test_table DROP COLUMN a_string,col1"; try{ conn.createStatement().execute(ddl); @@ -836,7 +837,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { } catch (SQLException e) { assertEquals(SQLExceptionCode.CANNOT_DROP_PK.getErrorCode(), e.getErrorCode()); } - + ddl = "ALTER TABLE test_table DROP COLUMN col4,col5"; try { conn.createStatement().execute(ddl); @@ -844,17 +845,17 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { } catch (SQLException e) { assertEquals(SQLExceptionCode.COLUMN_NOT_FOUND.getErrorCode(), e.getErrorCode()); assertTrue(e.getMessage(), e.getMessage().contains("ERROR 504 (42703): Undefined column. columnName=COL5")); - } + } ddl = "ALTER TABLE test_table DROP COLUMN IF EXISTS col1"; conn.createStatement().execute(ddl); - + query = "SELECT * FROM i"; try { rs = conn.createStatement().executeQuery(query); fail(); } catch (TableNotFoundException e) {} - + query = "select col4 FROM test_table"; rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -867,12 +868,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { } catch (SQLException e) { assertEquals(SQLExceptionCode.COLUMN_NOT_FOUND.getErrorCode(), e.getErrorCode()); } - + } finally { conn.close(); } } - + @Test public void alterTableFromDifferentClient() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -891,7 +892,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { // Do the alter through a separate client. conn3.createStatement().execute("alter table test_simpletable add field2 BIGINT"); - + //Connection conn1 = DriverManager.getConnection(getUrl(), props); PreparedStatement pstmt2 = conn1.prepareStatement("upsert into test_simpletable (id, field1, field2) values ( ?, ?, ?)"); pstmt2.setString(1, "key2"); @@ -902,7 +903,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { pstmt2.close(); conn1.close(); } - + @Test public void testAddColumnsUsingNewConnection() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -923,7 +924,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn1.createStatement().execute(ddl); conn1.close(); } - + @Test public void testAddColumnForNewColumnFamily() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -939,7 +940,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { ddl = "ALTER TABLE T ADD CF.STRING VARCHAR"; conn1.createStatement().execute(ddl); } - + @Test public void testSetHColumnProperties() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -961,7 +962,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1, columnFamilies[0].getScope()); } } - + @Test public void testSetHTableProperties() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -983,7 +984,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED)); } } - + @Test public void testSetHTableAndHColumnProperties() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1007,7 +1008,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(false, tableDesc.isCompactionEnabled()); } } - + @Test public void testSetHTableHColumnAndPhoenixTableProperties() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1026,28 +1027,28 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { ddl = "ALTER TABLE T3 SET COMPACTION_ENABLED = FALSE, CF1.MIN_VERSIONS = 1, CF2.MIN_VERSIONS = 3, MIN_VERSIONS = 8, IMMUTABLE_ROWS=false"; conn.createStatement().execute(ddl); assertImmutableRows(conn, "T3", false); - + try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) { HTableDescriptor tableDesc = admin.getTableDescriptor(Bytes.toBytes("T3")); HColumnDescriptor[] columnFamilies = tableDesc.getColumnFamilies(); assertEquals(3, columnFamilies.length); - + assertEquals("0", columnFamilies[0].getNameAsString()); assertEquals(8, columnFamilies[0].getMinVersions()); assertEquals(10, columnFamilies[0].getMaxVersions()); - + assertEquals("CF1", columnFamilies[1].getNameAsString()); assertEquals(1, columnFamilies[1].getMinVersions()); assertEquals(10, columnFamilies[1].getMaxVersions()); - + assertEquals("CF2", columnFamilies[2].getNameAsString()); assertEquals(3, columnFamilies[2].getMinVersions()); assertEquals(10, columnFamilies[2].getMaxVersions()); - + assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED)); } } - + @Test public void testSpecifyingColumnFamilyForHTablePropertyFails() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1067,7 +1068,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSpecifyingColumnFamilyForPhoenixTablePropertyFails() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1087,7 +1088,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_TABLE_PROPERTY.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSpecifyingColumnFamilyForTTLFails() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1108,7 +1109,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_ALLOWED_FOR_TTL.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSetPropertyNeedsColumnFamilyToExist() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1128,7 +1129,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.COLUMN_FAMILY_NOT_FOUND.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSetDefaultColumnFamilyNotAllowed() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1148,7 +1149,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.DEFAULT_COLUMN_FAMILY_ONLY_ON_CREATE_TABLE.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSetHColumnOrHTablePropertiesOnViewsNotAllowed() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1176,7 +1177,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSetForSomePhoenixTablePropertiesOnViewsAllowed() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1204,7 +1205,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(SQLExceptionCode.VIEW_WITH_PROPERTIES.getErrorCode(), e.getErrorCode()); } } - + @Test public void testSettingPropertiesWhenTableHasDefaultColFamilySpecified() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1234,7 +1235,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(Boolean.toString(false), tableDesc.getValue(HTableDescriptor.COMPACTION_ENABLED)); } } - + @Test public void testNewColumnFamilyInheritsTTLOfEmptyCF() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1259,12 +1260,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1000, columnFamilies[1].getTimeToLive()); } } - + private static void assertImmutableRows(Connection conn, String fullTableName, boolean expectedValue) throws SQLException { PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class); assertEquals(expectedValue, pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).isImmutableRows()); } - + @Test public void testSetPropertyAndAddColumnForExistingColumnFamily() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1287,7 +1288,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetPropertyAndAddColumnForNewAndExistingColumnFamily() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1315,7 +1316,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetPropertyAndAddColumnWhenTableHasExplicitDefaultColumnFamily() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1343,7 +1344,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetPropertyAndAddColumnFailsForColumnFamilyNotPresentInAddCol() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1363,7 +1364,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetPropertyAndAddColumnForDifferentColumnFamilies() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1394,7 +1395,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetPropertyAndAddColumnUsingDefaultColumnFamilySpecifier() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1420,7 +1421,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetPropertyAndAddColumnForDefaultColumnFamily() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1442,7 +1443,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testAddNewColumnFamilyProperties() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1474,7 +1475,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertTrue(columnFamilies[3].isInMemory()); assertEquals("CF4", columnFamilies[4].getNameAsString()); assertTrue(columnFamilies[4].isInMemory()); - } + } } finally { conn.close(); } @@ -1546,7 +1547,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { fail(); } catch (SQLException e) { assertEquals(SQLExceptionCode.CANNOT_SET_TABLE_PROPERTY_ADD_COLUMN.getErrorCode(), e.getErrorCode()); - } + } try { String ddl = "ALTER TABLE ttl_test add col1 varchar a.ttl=30"; conn.createStatement().execute(ddl); @@ -1557,7 +1558,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSetTTLForTableWithOnlyPKCols() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1577,7 +1578,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1, columnFamilies.length); assertEquals("XYZ", columnFamilies[0].getNameAsString()); assertEquals(86400, columnFamilies[0].getTimeToLive()); - } + } ddl = "ALTER TABLE ttl_test2 SET TTL=30"; conn.createStatement().execute(ddl); conn.commit(); @@ -1587,12 +1588,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1, columnFamilies.length); assertEquals(30, columnFamilies[0].getTimeToLive()); assertEquals("XYZ", columnFamilies[0].getNameAsString()); - } + } } finally { conn.close(); } } - + @Test public void testSetHColumnPropertyForTableWithOnlyPKCols() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1615,11 +1616,11 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1, columnFamilies.length); assertEquals(true, columnFamilies[0].isInMemory()); assertEquals("XYZ", columnFamilies[0].getNameAsString()); - } + } } finally { conn.close(); } - + try { String ddl = "create table IF NOT EXISTS SETHCPROPPKONLY2 (" + " id char(1) NOT NULL," @@ -1637,12 +1638,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1, columnFamilies.length); assertEquals(true, columnFamilies[0].isInMemory()); assertEquals("0", columnFamilies[0].getNameAsString()); - } + } } finally { conn.close(); } } - + @Test public void testSetHColumnPropertyAndAddColumnForDefaultCFForTableWithOnlyPKCols() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1665,12 +1666,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(1, columnFamilies.length); assertEquals(true, columnFamilies[0].isInMemory()); assertEquals("XYZ", columnFamilies[0].getNameAsString()); - } + } } finally { conn.close(); } } - + @Test public void testSetHColumnPropertyAndAddColumnForNewCFForTableWithOnlyPKCols() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1695,12 +1696,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(true, columnFamilies[0].isInMemory()); assertEquals("XYZ", columnFamilies[1].getNameAsString()); assertEquals(false, columnFamilies[1].isInMemory()); - } + } } finally { conn.close(); } } - + @Test public void testTTLAssignmentForNewEmptyCF() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); @@ -1728,7 +1729,7 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(false, columnFamilies[1].isInMemory()); assertEquals(86400, columnFamilies[1].getTimeToLive()); } - + ddl = "ALTER TABLE NEWEMPTYCFTABLE SET TTL=1000"; conn.createStatement().execute(ddl); conn.commit(); @@ -1743,11 +1744,11 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { assertEquals(false, columnFamilies[1].isInMemory()); assertEquals(86400, columnFamilies[1].getTimeToLive()); } - - // the new column will be assigned to the column family XYZ. With the a KV column getting added for XYZ, + + // the new column will be assigned to the column family XYZ. With the a KV column getting added for XYZ, // the column family will start showing up in PTable.getColumnFamilies() after the column is added. Thus // being a new column family for the PTable, it will end up inheriting the TTL of the emptyCF (NEWCF). - ddl = "ALTER TABLE NEWEMPTYCFTABLE ADD COL3 INTEGER"; + ddl = "ALTER TABLE NEWEMPTYCFTABLE ADD COL3 INTEGER"; conn.createStatement().execute(ddl); conn.commit(); try (HBaseAdmin admin = conn.unwrap(PhoenixConnection.class).getQueryServices().getAdmin()) { @@ -1765,12 +1766,12 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + @Test public void testSettingNotHColumnNorPhoenixPropertyEndsUpAsHTableProperty() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); - try { + try { String ddl = "create table IF NOT EXISTS RANDMONPROPTABLE (" + " id char(1) NOT NULL," + " col1 integer NOT NULL," @@ -1794,5 +1795,31 @@ public class AlterTableIT extends BaseOwnClusterHBaseManagedTimeIT { conn.close(); } } - + + @Test + public void testAlterStoreNulls() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + + Statement stmt = conn.createStatement(); + stmt.execute("CREATE TABLE WITH_NULLS (id SMALLINT PRIMARY KEY, name VARCHAR)"); + + ResultSet rs = stmt.executeQuery("SELECT STORE_NULLS FROM SYSTEM.CATALOG " + + "WHERE table_name = 'WITH_NULLS' AND STORE_NULLS IS NOT NULL"); + assertTrue(rs.next()); + assertFalse(rs.getBoolean(1)); + assertFalse(rs.next()); + rs.close(); + + stmt.execute("ALTER TABLE WITH_NULLS SET STORE_NULLS = true"); + + rs = stmt.executeQuery("SELECT STORE_NULLS FROM SYSTEM.CATALOG " + + "WHERE table_name = 'WITH_NULLS' AND STORE_NULLS IS NOT NULL"); + assertTrue(rs.next()); + assertTrue(rs.getBoolean(1)); + assertFalse(rs.next()); + rs.close(); + stmt.close(); + + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/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 new file mode 100644 index 0000000..c834ade --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/StoreNullsIT.java @@ -0,0 +1,207 @@ +/* + * 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 com.google.common.collect.Lists; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.client.Delete; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.ResultScanner; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.query.QueryConstants; +import org.apache.phoenix.util.PhoenixRuntime; +import org.apache.phoenix.util.SchemaUtil; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests to demonstrate and verify the STORE_NULLS option on a table, + * which allows explicitly storing null values (as opposed to using HBase Deletes) for nulls. This + * functionality allows having row-level versioning (similar to how KEEP_DELETED_CELLS works), but + * also allows permanently deleting a row. + */ +public class StoreNullsIT extends BaseHBaseManagedTimeIT { + + private static final Log LOG = LogFactory.getLog(StoreNullsIT.class); + + private Connection conn; + private Statement stmt; + + @Before + public void setUp() throws SQLException { + conn = DriverManager.getConnection(getUrl()); + conn.setAutoCommit(true); + + stmt = conn.createStatement(); + stmt.execute("CREATE TABLE with_nulls (" + + "id SMALLINT NOT NULL PRIMARY KEY, " + + "name VARCHAR) " + + "STORE_NULLS = true, VERSIONS = 1000, KEEP_DELETED_CELLS = false"); + stmt.execute("CREATE TABLE without_nulls (" + + "id SMALLINT NOT NULL PRIMARY KEY, " + + "name VARCHAR) " + + "VERSIONS = 1000, KEEP_DELETED_CELLS = false"); + } + + @After + public void tearDown() throws SQLException { + stmt.close(); + conn.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')"); + + Thread.sleep(10L); + long afterFirstInsert = System.currentTimeMillis(); + Thread.sleep(10L); + + stmt.executeUpdate("UPSERT INTO with_nulls VALUES (1, null)"); + stmt.executeUpdate("UPSERT INTO without_nulls VALUES (1, null)"); + Thread.sleep(10L); + + doMajorCompaction("with_nulls"); + doMajorCompaction("without_nulls"); + + Properties historicalProps = new Properties(); + historicalProps.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, + Long.toString(afterFirstInsert)); + Connection historicalConn = DriverManager.getConnection(getUrl(), historicalProps); + Statement historicalStmt = historicalConn.createStatement(); + + ResultSet rs = historicalStmt.executeQuery("SELECT name FROM with_nulls WHERE id = 1"); + assertTrue(rs.next()); + assertEquals("v1", rs.getString(1)); + rs.close(); + + // The single null wipes out all history for a field if STORE_NULLS is not enabled + rs = historicalStmt.executeQuery("SELECT name FROM without_nulls WHERE id = 1"); + assertTrue(rs.next()); + assertNull(rs.getString(1)); + rs.close(); + + historicalStmt.close(); + historicalConn.close(); + } + + // Row deletes should work in the same way regardless of what STORE_NULLS is set to + @Test + public void testDeletes() throws SQLException, InterruptedException, IOException { + stmt.executeUpdate("UPSERT INTO with_nulls VALUES (1, 'v1')"); + stmt.executeUpdate("UPSERT INTO without_nulls VALUES (1, 'v1')"); + + Thread.sleep(10L); + long afterFirstInsert = System.currentTimeMillis(); + Thread.sleep(10L); + + stmt.executeUpdate("DELETE FROM with_nulls WHERE id = 1"); + stmt.executeUpdate("DELETE FROM without_nulls WHERE id = 1"); + Thread.sleep(10L); + + doMajorCompaction("with_nulls"); + doMajorCompaction("without_nulls"); + + Properties historicalProps = new Properties(); + historicalProps.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, + Long.toString(afterFirstInsert)); + Connection historicalConn = DriverManager.getConnection(getUrl(), historicalProps); + Statement historicalStmt = historicalConn.createStatement(); + + // The row should be completely gone for both tables now + + ResultSet rs = historicalStmt.executeQuery("SELECT name FROM with_nulls WHERE id = 1"); + assertFalse(rs.next()); + rs.close(); + + rs = historicalStmt.executeQuery("SELECT name FROM without_nulls WHERE id = 1"); + assertFalse(rs.next()); + rs.close(); + } + + /** + * Runs a major compaction, and then waits until the compaction is complete before returning. + * + * @param tableName name of the table to be compacted + */ + private void doMajorCompaction(String tableName) throws IOException, InterruptedException { + + tableName = SchemaUtil.normalizeIdentifier(tableName); + + // We simply write a marker row, request a major compaction, and then wait until the marker + // row is gone + HTable htable = new HTable(getUtility().getConfiguration(), tableName); + byte[] markerRowKey = Bytes.toBytes("TO_DELETE"); + + + Put put = new Put(markerRowKey); + put.add(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES, HConstants.EMPTY_BYTE_ARRAY, + HConstants.EMPTY_BYTE_ARRAY); + htable.put(put); + htable.delete(new Delete(markerRowKey)); + htable.close(); + + HBaseAdmin hbaseAdmin = new HBaseAdmin(getUtility().getConfiguration()); + hbaseAdmin.flush(tableName); + hbaseAdmin.majorCompact(tableName); + hbaseAdmin.close(); + + boolean compactionDone = false; + while (!compactionDone) { + Thread.sleep(2000L); + htable = new HTable(getUtility().getConfiguration(), tableName); + Scan scan = new Scan(); + scan.setStartRow(markerRowKey); + scan.setStopRow(Bytes.add(markerRowKey, new byte[] { 0 })); + scan.setRaw(true); + + ResultScanner scanner = htable.getScanner(scan); + List<Result> results = Lists.newArrayList(scanner); + LOG.info("Results: " + results); + compactionDone = results.isEmpty(); + scanner.close(); + + LOG.info("Compaction done: " + compactionDone); + } + + htable.close(); + + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/9cd7e86b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java index fb3183a..0163082 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java @@ -81,8 +81,8 @@ import com.google.common.collect.Lists; /** * Validates FROM clause and builds a ColumnResolver for resolving column references - * - * + * + * * @since 0.1 */ public class FromCompiler { @@ -143,7 +143,7 @@ public class FromCompiler { /** * Iterate through the nodes in the FROM clause to build a column resolver used to lookup a column given the name * and alias. - * + * * @param statement * the select statement * @return the column resolver @@ -158,7 +158,7 @@ public class FromCompiler { TableNode fromNode = statement.getFrom(); if (fromNode instanceof NamedTableNode) return new SingleTableColumnResolver(connection, (NamedTableNode) fromNode, true, 1); - + MultiTableColumnResolver visitor = new MultiTableColumnResolver(connection, 1); fromNode.accept(visitor); return visitor; @@ -168,23 +168,23 @@ public class FromCompiler { SingleTableColumnResolver visitor = new SingleTableColumnResolver(connection, tableNode, true); return visitor; } - + public static ColumnResolver getResolver(SingleTableStatement statement, PhoenixConnection connection) throws SQLException { SingleTableColumnResolver visitor = new SingleTableColumnResolver(connection, statement.getTable(), true); return visitor; } - - public static ColumnResolver getResolverForCompiledDerivedTable(PhoenixConnection connection, TableRef tableRef, RowProjector projector) + + public static ColumnResolver getResolverForCompiledDerivedTable(PhoenixConnection connection, TableRef tableRef, RowProjector projector) throws SQLException { List<PColumn> projectedColumns = new ArrayList<PColumn>(); List<Expression> sourceExpressions = new ArrayList<Expression>(); PTable table = tableRef.getTable(); for (PColumn column : table.getColumns()) { Expression sourceExpression = projector.getColumnProjector(column.getPosition()).getExpression(); - PColumnImpl projectedColumn = new PColumnImpl(column.getName(), column.getFamilyName(), - sourceExpression.getDataType(), sourceExpression.getMaxLength(), sourceExpression.getScale(), sourceExpression.isNullable(), - column.getPosition(), sourceExpression.getSortOrder(), column.getArraySize(), column.getViewConstant(), column.isViewReferenced()); + PColumnImpl projectedColumn = new PColumnImpl(column.getName(), column.getFamilyName(), + sourceExpression.getDataType(), sourceExpression.getMaxLength(), sourceExpression.getScale(), sourceExpression.isNullable(), + column.getPosition(), sourceExpression.getSortOrder(), column.getArraySize(), column.getViewConstant(), column.isViewReferenced()); projectedColumns.add(projectedColumn); sourceExpressions.add(sourceExpression); } @@ -207,11 +207,11 @@ public class FromCompiler { SingleTableColumnResolver visitor = new SingleTableColumnResolver(connection, statement.getTable(), false); return visitor; } - + private static class SingleTableColumnResolver extends BaseColumnResolver { private final List<TableRef> tableRefs; private final String alias; - + public SingleTableColumnResolver(PhoenixConnection connection, NamedTableNode table, long timeStamp) throws SQLException { super(connection, 0); List<PColumnFamily> families = Lists.newArrayListWithExpectedSize(table.getDynamicColumns().size()); @@ -226,7 +226,7 @@ public class FromCompiler { alias = null; tableRefs = ImmutableList.of(new TableRef(alias, theTable, timeStamp, !table.getDynamicColumns().isEmpty())); } - + public SingleTableColumnResolver(PhoenixConnection connection, NamedTableNode tableNode, boolean updateCacheImmediately) throws SQLException { this(connection, tableNode, updateCacheImmediately, 0); } @@ -237,7 +237,7 @@ public class FromCompiler { TableRef tableRef = createTableRef(tableNode, updateCacheImmediately); tableRefs = ImmutableList.of(tableRef); } - + public SingleTableColumnResolver(PhoenixConnection connection, TableRef tableRef) { super(connection, 0); alias = tableRef.getTableAlias(); @@ -269,7 +269,7 @@ public class FromCompiler { String resolvedSchemaName = tableRef.getTable().getSchemaName().getString(); if (schemaName != null && tableName != null) { if ( ! ( schemaName.equals(resolvedSchemaName) && - tableName.equals(resolvedTableName) ) && + tableName.equals(resolvedTableName) ) && ! schemaName.equals(alias) ) { throw new TableNotFoundException(schemaName, tableName); } @@ -298,7 +298,7 @@ public class FromCompiler { resolveCF = true; } } - + } PColumn column = resolveCF ? tableRef.getTable().getColumnFamily(tableName).getColumn(colName) @@ -315,7 +315,7 @@ public class FromCompiler { // on Windows because the millis timestamp granularity is so bad we sometimes won't // get the data back that we just upsert. private final int tsAddition; - + private BaseColumnResolver(PhoenixConnection connection, int tsAddition) { this.connection = connection; this.client = connection == null ? null : new MetaDataClient(connection); @@ -371,7 +371,7 @@ public class FromCompiler { } return tableRef; } - + protected PTable addDynamicColumns(List<ColumnDef> dynColumns, PTable theTable) throws SQLException { if (!dynColumns.isEmpty()) { @@ -399,7 +399,7 @@ public class FromCompiler { return theTable; } } - + private static class MultiTableColumnResolver extends BaseColumnResolver implements TableNodeVisitor<Void> { private final ListMultimap<String, TableRef> tableMap; private final List<TableRef> tables; @@ -455,28 +455,29 @@ public class FromCompiler { String alias = aliasedNode.getAlias(); if (alias == null) { ParseNode node = aliasedNode.getNode(); - if (node instanceof WildcardParseNode + if (node instanceof WildcardParseNode || node instanceof TableWildcardParseNode || node instanceof FamilyWildcardParseNode) throw new SQLException("Encountered wildcard in subqueries."); - + alias = SchemaUtil.normalizeIdentifier(node.getAlias()); } if (alias == null) { - // Use position as column name for anonymous columns, which can be + // Use position as column name for anonymous columns, which can be // referenced by an outer wild-card select. alias = String.valueOf(position); } - PColumnImpl column = new PColumnImpl(PNameFactory.newName(alias), - PNameFactory.newName(QueryConstants.DEFAULT_COLUMN_FAMILY), + PColumnImpl column = new PColumnImpl(PNameFactory.newName(alias), + PNameFactory.newName(QueryConstants.DEFAULT_COLUMN_FAMILY), null, 0, 0, true, position++, SortOrder.ASC, null, null, false); columns.add(column); } - PTable t = PTableImpl.makePTable(null, PName.EMPTY_NAME, PName.EMPTY_NAME, - PTableType.SUBQUERY, null, MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM, - null, null, columns, null, null, Collections.<PTable>emptyList(), - false, Collections.<PName>emptyList(), null, null, false, false, null, null, null); - + PTable t = PTableImpl.makePTable(null, PName.EMPTY_NAME, PName.EMPTY_NAME, + PTableType.SUBQUERY, null, MetaDataProtocol.MIN_TABLE_TIMESTAMP, PTable.INITIAL_SEQ_NUM, + null, null, columns, null, null, Collections.<PTable>emptyList(), + false, Collections.<PName>emptyList(), null, null, false, false, false, null, + null, null); + String alias = subselectNode.getAlias(); TableRef tableRef = new TableRef(alias, t, MetaDataProtocol.MIN_TABLE_TIMESTAMP, false); tableMap.put(alias, tableRef);