PHOENIX-514 Support functional indexes (Thomas D'Silva) Conflicts:
phoenix-core/src/main/java/org/apache/phoenix/hbase/index/util/IndexManagementUtil.java phoenix-core/src/main/java/org/apache/phoenix/schema/DelegateColumn.java Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/5acebb38 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/5acebb38 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/5acebb38 Branch: refs/heads/4.0 Commit: 5acebb38bb23005a67d14979af45f0a89efc35c9 Parents: 40762a5 Author: James Taylor <jamestay...@apache.org> Authored: Wed Feb 4 12:30:51 2015 -0800 Committer: James Taylor <jamestay...@apache.org> Committed: Wed Feb 4 12:41:54 2015 -0800 ---------------------------------------------------------------------- .../org/apache/phoenix/end2end/HashJoinIT.java | 12 +- .../apache/phoenix/end2end/PercentileIT.java | 7 +- .../java/org/apache/phoenix/end2end/ViewIT.java | 2 +- .../phoenix/end2end/index/ImmutableIndexIT.java | 76 +- .../end2end/index/IndexExpressionIT.java | 866 +++++++++++++++++++ .../phoenix/end2end/index/IndexMetadataIT.java | 20 +- phoenix-core/src/main/antlr3/PhoenixSQL.g | 32 +- .../IndexHalfStoreFileReaderGenerator.java | 4 +- .../apache/phoenix/compile/DeleteCompiler.java | 4 +- .../phoenix/compile/ExpressionCompiler.java | 9 +- .../apache/phoenix/compile/FromCompiler.java | 6 +- .../apache/phoenix/compile/HavingCompiler.java | 4 +- .../compile/IndexExpressionCompiler.java | 53 ++ .../apache/phoenix/compile/JoinCompiler.java | 8 +- .../phoenix/compile/PostIndexDDLCompiler.java | 47 +- .../apache/phoenix/compile/UpsertCompiler.java | 2 +- .../apache/phoenix/compile/WhereCompiler.java | 4 +- .../coprocessor/MetaDataEndpointImpl.java | 44 +- .../UngroupedAggregateRegionObserver.java | 3 +- .../coprocessor/generated/PTableProtos.java | 215 ++++- .../phoenix/exception/SQLExceptionCode.java | 6 + .../apache/phoenix/execute/BaseQueryPlan.java | 2 +- .../apache/phoenix/execute/MutationState.java | 4 +- .../phoenix/expression/CoerceExpression.java | 10 +- .../expression/RowKeyColumnExpression.java | 6 +- .../apache/phoenix/hbase/index/ValueGetter.java | 2 + .../index/covered/data/LazyValueGetter.java | 5 + .../hbase/index/util/IndexManagementUtil.java | 23 - .../apache/phoenix/index/IndexMaintainer.java | 333 ++++--- .../apache/phoenix/index/PhoenixIndexCodec.java | 2 +- .../index/PhoenixIndexFailurePolicy.java | 21 +- .../phoenix/jdbc/PhoenixDatabaseMetaData.java | 1 + .../apache/phoenix/jdbc/PhoenixStatement.java | 9 +- .../apache/phoenix/optimize/QueryOptimizer.java | 12 +- .../apache/phoenix/parse/BetweenParseNode.java | 22 + .../org/apache/phoenix/parse/BindParseNode.java | 22 + .../org/apache/phoenix/parse/CastParseNode.java | 38 + .../org/apache/phoenix/parse/ColumnDef.java | 14 +- .../org/apache/phoenix/parse/ColumnName.java | 2 +- .../apache/phoenix/parse/ColumnParseNode.java | 1 + .../apache/phoenix/parse/CompoundParseNode.java | 32 +- .../phoenix/parse/CreateIndexStatement.java | 10 +- .../apache/phoenix/parse/ExistsParseNode.java | 22 + .../phoenix/parse/FamilyWildcardParseNode.java | 22 + .../apache/phoenix/parse/FunctionParseNode.java | 31 + .../apache/phoenix/parse/InListParseNode.java | 22 + .../org/apache/phoenix/parse/InParseNode.java | 25 + .../parse/IndexExpressionParseNodeRewriter.java | 104 +++ .../phoenix/parse/IndexKeyConstraint.java | 12 +- .../apache/phoenix/parse/IsNullParseNode.java | 22 + .../org/apache/phoenix/parse/LikeParseNode.java | 26 + .../apache/phoenix/parse/LiteralParseNode.java | 21 + .../org/apache/phoenix/parse/NamedNode.java | 2 +- .../apache/phoenix/parse/NamedParseNode.java | 30 + .../apache/phoenix/parse/ParseNodeFactory.java | 34 +- .../phoenix/parse/SequenceValueParseNode.java | 29 + .../apache/phoenix/parse/SubqueryParseNode.java | 28 + .../org/apache/phoenix/parse/TableName.java | 3 +- .../phoenix/parse/TableWildcardParseNode.java | 29 + .../apache/phoenix/parse/WildcardParseNode.java | 24 +- .../apache/phoenix/schema/DelegateColumn.java | 6 + .../apache/phoenix/schema/DelegateTable.java | 9 +- .../apache/phoenix/schema/MetaDataClient.java | 173 ++-- .../java/org/apache/phoenix/schema/PColumn.java | 3 + .../org/apache/phoenix/schema/PColumnImpl.java | 26 +- .../apache/phoenix/schema/PMetaDataImpl.java | 2 +- .../java/org/apache/phoenix/schema/PTable.java | 7 +- .../org/apache/phoenix/schema/PTableImpl.java | 27 +- .../org/apache/phoenix/schema/SaltingUtil.java | 2 +- .../phoenix/schema/tuple/ValueGetterTuple.java | 93 ++ .../java/org/apache/phoenix/util/IndexUtil.java | 15 +- .../expression/ColumnExpressionTest.java | 8 +- .../phoenix/index/IndexMaintainerTest.java | 14 +- .../iterate/AggregateResultScannerTest.java | 5 + .../java/org/apache/phoenix/query/BaseTest.java | 26 +- phoenix-protocol/src/main/PTable.proto | 1 + 76 files changed, 2475 insertions(+), 423 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java index 781bfea..76eab22 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java @@ -289,7 +289,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT { "CLIENT MERGE SORT\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" + - " SERVER FILTER BY order_id != '000000000000003'\n" + + " SERVER FILTER BY \"order_id\" != '000000000000003'\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" + " SERVER FILTER BY NAME != 'T3'\n" + @@ -380,7 +380,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT { "CLIENT MERGE SORT\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" + - " SERVER FILTER BY order_id != '000000000000003'\n" + + " SERVER FILTER BY \"order_id\" != '000000000000003'\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" + " SERVER FILTER BY NAME != 'T3'\n" + @@ -655,7 +655,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT { "CLIENT MERGE SORT\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" + - " SERVER FILTER BY order_id != '000000000000003'\n" + + " SERVER FILTER BY \"order_id\" != '000000000000003'\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" + " SERVER FILTER BY NAME != 'T3'\n" + @@ -747,7 +747,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT { "CLIENT MERGE SORT\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" + - " SERVER FILTER BY order_id != '000000000000003'\n" + + " SERVER FILTER BY \"order_id\" != '000000000000003'\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" + " SERVER FILTER BY NAME != 'T3'\n" + @@ -1039,7 +1039,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT { "CLIENT MERGE SORT\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" + - " SERVER FILTER BY order_id != '000000000000003'\n" + + " SERVER FILTER BY \"order_id\" != '000000000000003'\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+ JOIN_ITEM_TABLE_DISPLAY_NAME +" [-32768]\n" + " SERVER FILTER BY NAME != 'T3'\n" + @@ -1134,7 +1134,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT { "CLIENT MERGE SORT\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" + - " SERVER FILTER BY order_id != '000000000000003'\n" + + " SERVER FILTER BY \"order_id\" != '000000000000003'\n" + " PARALLEL INNER-JOIN TABLE 0\n" + " CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + MetaDataUtil.LOCAL_INDEX_TABLE_PREFIX +""+JOIN_ITEM_TABLE_DISPLAY_NAME + " [-32768]\n" + " SERVER FILTER BY NAME != 'T3'\n" + http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java index 685daeb..8109694 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/PercentileIT.java @@ -50,6 +50,7 @@ import java.sql.Types; import java.util.Properties; import org.apache.phoenix.query.QueryConstants; +import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; import org.junit.Test; @@ -516,15 +517,17 @@ public class PercentileIT extends BaseClientManagedTimeIT { private static void populateINDEX_DATA_TABLETable() throws SQLException { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); + Date date = DateUtil.parseDate("2015-01-01 00:00:00"); try { String upsert = "UPSERT INTO " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE - + " VALUES(?, ?, ?, ?, ?)"; + + " VALUES(?, ?, ?, ?, ?, ?)"; PreparedStatement stmt = conn.prepareStatement(upsert); stmt.setString(1, "varchar1"); stmt.setString(2, "char1"); stmt.setInt(3, 1); stmt.setLong(4, 1L); stmt.setBigDecimal(5, new BigDecimal(1.0)); + stmt.setDate(6, date); stmt.executeUpdate(); stmt.setString(1, "varchar2"); @@ -532,6 +535,7 @@ public class PercentileIT extends BaseClientManagedTimeIT { stmt.setInt(3, 2); stmt.setLong(4, 2L); stmt.setBigDecimal(5, new BigDecimal(2.0)); + stmt.setDate(6, date); stmt.executeUpdate(); stmt.setString(1, "varchar3"); @@ -539,6 +543,7 @@ public class PercentileIT extends BaseClientManagedTimeIT { stmt.setInt(3, 3); stmt.setLong(4, 3L); stmt.setBigDecimal(5, new BigDecimal(3.0)); + stmt.setDate(6, date); stmt.executeUpdate(); conn.commit(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java ---------------------------------------------------------------------- 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 aa26f9b..9a89531 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 @@ -424,6 +424,6 @@ public class ViewIT extends BaseViewIT { String queryPlan = QueryUtil.getExplainPlan(rs); assertEquals( "CLIENT PARALLEL 1-WAY SKIP SCAN ON 4 KEYS OVER I1 [1,100] - [2,109]\n" + - " SERVER FILTER BY (S2 = 'bas' AND S1 = 'foo')", queryPlan); + " SERVER FILTER BY (S2 = 'bas' AND \"S1\" = 'foo')", queryPlan); } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java index 55b38a5..9eb9a57 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ImmutableIndexIT.java @@ -27,6 +27,7 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; import java.sql.Connection; +import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -38,6 +39,7 @@ import org.apache.phoenix.exception.SQLExceptionCode; import org.apache.phoenix.jdbc.PhoenixConnection; import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.schema.PTableKey; +import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; import org.junit.Test; @@ -50,23 +52,27 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT { Connection conn = DriverManager.getConnection(getUrl(), props); try { String upsert = "UPSERT INTO " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + INDEX_DATA_TABLE - + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; PreparedStatement stmt = conn.prepareStatement(upsert); stmt.setString(1, "varchar1"); stmt.setString(2, "char1"); stmt.setInt(3, 1); stmt.setLong(4, 1L); stmt.setBigDecimal(5, new BigDecimal(1.0)); - stmt.setString(6, "varchar_a"); - stmt.setString(7, "chara"); - stmt.setInt(8, 2); - stmt.setLong(9, 2L); - stmt.setBigDecimal(10, new BigDecimal(2.0)); - stmt.setString(11, "varchar_b"); - stmt.setString(12, "charb"); - stmt.setInt(13, 3); - stmt.setLong(14, 3L); - stmt.setBigDecimal(15, new BigDecimal(3.0)); + Date date = DateUtil.parseDate("2015-01-01 00:00:00"); + stmt.setDate(6, date); + stmt.setString(7, "varchar_a"); + stmt.setString(8, "chara"); + stmt.setInt(9, 2); + stmt.setLong(10, 2L); + stmt.setBigDecimal(11, new BigDecimal(2.0)); + stmt.setDate(12, date); + stmt.setString(13, "varchar_b"); + stmt.setString(14, "charb"); + stmt.setInt(15, 3); + stmt.setLong(16, 3L); + stmt.setBigDecimal(17, new BigDecimal(3.0)); + stmt.setDate(18, date); stmt.executeUpdate(); stmt.setString(1, "varchar2"); @@ -74,16 +80,20 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT { stmt.setInt(3, 2); stmt.setLong(4, 2L); stmt.setBigDecimal(5, new BigDecimal(2.0)); - stmt.setString(6, "varchar_a"); - stmt.setString(7, "chara"); - stmt.setInt(8, 3); - stmt.setLong(9, 3L); - stmt.setBigDecimal(10, new BigDecimal(3.0)); - stmt.setString(11, "varchar_b"); - stmt.setString(12, "charb"); - stmt.setInt(13, 4); - stmt.setLong(14, 4L); - stmt.setBigDecimal(15, new BigDecimal(4.0)); + date = DateUtil.parseDate("2015-01-02 00:00:00"); + stmt.setDate(6, date); + stmt.setString(7, "varchar_a"); + stmt.setString(8, "chara"); + stmt.setInt(9, 3); + stmt.setLong(10, 3L); + stmt.setBigDecimal(11, new BigDecimal(3.0)); + stmt.setDate(12, date); + stmt.setString(13, "varchar_b"); + stmt.setString(14, "charb"); + stmt.setInt(15, 4); + stmt.setLong(16, 4L); + stmt.setBigDecimal(17, new BigDecimal(4.0)); + stmt.setDate(18, date); stmt.executeUpdate(); stmt.setString(1, "varchar3"); @@ -91,16 +101,20 @@ public class ImmutableIndexIT extends BaseHBaseManagedTimeIT { stmt.setInt(3, 3); stmt.setLong(4, 3L); stmt.setBigDecimal(5, new BigDecimal(3.0)); - stmt.setString(6, "varchar_a"); - stmt.setString(7, "chara"); - stmt.setInt(8, 4); - stmt.setLong(9, 4L); - stmt.setBigDecimal(10, new BigDecimal(4.0)); - stmt.setString(11, "varchar_b"); - stmt.setString(12, "charb"); - stmt.setInt(13, 5); - stmt.setLong(14, 5L); - stmt.setBigDecimal(15, new BigDecimal(5.0)); + date = DateUtil.parseDate("2015-01-03 00:00:00"); + stmt.setDate(6, date); + stmt.setString(7, "varchar_a"); + stmt.setString(8, "chara"); + stmt.setInt(9, 4); + stmt.setLong(10, 4L); + stmt.setBigDecimal(11, new BigDecimal(4.0)); + stmt.setDate(12, date); + stmt.setString(13, "varchar_b"); + stmt.setString(14, "charb"); + stmt.setInt(15, 5); + stmt.setLong(16, 5L); + stmt.setBigDecimal(17, new BigDecimal(5.0)); + stmt.setDate(18, date); stmt.executeUpdate(); conn.commit(); http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java new file mode 100644 index 0000000..28124b6 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexExpressionIT.java @@ -0,0 +1,866 @@ +/* + * 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.index; + +import static org.apache.phoenix.util.TestUtil.INDEX_DATA_SCHEMA; +import static org.apache.phoenix.util.TestUtil.INDEX_DATA_TABLE; +import static org.apache.phoenix.util.TestUtil.MUTABLE_INDEX_DATA_TABLE; +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.math.BigDecimal; +import java.sql.Connection; +import java.sql.Date; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Properties; + +import org.apache.commons.lang.StringUtils; +import org.apache.phoenix.end2end.BaseHBaseManagedTimeIT; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.query.QueryConstants; +import org.apache.phoenix.util.DateUtil; +import org.apache.phoenix.util.PropertiesUtil; +import org.apache.phoenix.util.QueryUtil; +import org.junit.Test; + +public class IndexExpressionIT extends BaseHBaseManagedTimeIT { + + private static final int NUM_MILLIS_IN_DAY = 86400000; + + @Test + public void testImmutableIndexCreationAndUpdate() throws Exception { + helpTestCreateAndUpdate(false, false); + } + + @Test + public void testImmutableLocalIndexCreationAndUpdate() throws Exception { + helpTestCreateAndUpdate(false, true); + } + + @Test + public void testMutableIndexCreationAndUpdate() throws Exception { + helpTestCreateAndUpdate(true, false); + } + + @Test + public void testMutableLocalIndexCreationAndUpdate() throws Exception { + helpTestCreateAndUpdate(true, true); + } + + /** + * Adds a row to the index data table + * + * @param i + * row number + */ + private void insertRow(PreparedStatement stmt, int i) throws SQLException { + // insert row + stmt.setString(1, "varchar" + String.valueOf(i)); + stmt.setString(2, "char" + String.valueOf(i)); + stmt.setInt(3, i); + stmt.setLong(4, i); + stmt.setBigDecimal(5, new BigDecimal(Double.valueOf(i))); + Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i - 1) * NUM_MILLIS_IN_DAY); + stmt.setDate(6, date); + stmt.setString(7, "a.varchar" + String.valueOf(i)); + stmt.setString(8, "a.char" + String.valueOf(i)); + stmt.setInt(9, i); + stmt.setLong(10, i); + stmt.setBigDecimal(11, new BigDecimal((double)i)); + stmt.setDate(12, date); + stmt.setString(13, "b.varchar" + String.valueOf(i)); + stmt.setString(14, "b.char" + String.valueOf(i)); + stmt.setInt(15, i); + stmt.setLong(16, i); + stmt.setBigDecimal(17, new BigDecimal((double)i)); + stmt.setDate(18, date); + stmt.executeUpdate(); + } + + private void verifyResult(ResultSet rs, int i) throws SQLException { + assertTrue(rs.next()); + assertEquals("VARCHAR" + String.valueOf(i) + "_" + StringUtils.rightPad("CHAR" + String.valueOf(i), 6, ' ') + + "_A.VARCHAR" + String.valueOf(i) + "_" + StringUtils.rightPad("B.CHAR" + String.valueOf(i), 10, ' '), + rs.getString(1)); + assertEquals(i * 4, rs.getInt(2)); + Date date = new Date(DateUtil.parseDate("2015-01-01 00:00:00").getTime() + (i) * NUM_MILLIS_IN_DAY); + assertEquals(date, rs.getDate(3)); + assertEquals(date, rs.getDate(4)); + assertEquals(date, rs.getDate(5)); + assertEquals("varchar" + String.valueOf(i), rs.getString(6)); + assertEquals("char" + String.valueOf(i), rs.getString(7)); + assertEquals(i, rs.getInt(8)); + assertEquals(i, rs.getLong(9)); + assertEquals(i, rs.getDouble(10), 0.000001); + assertEquals(i, rs.getLong(11)); + assertEquals(i, rs.getLong(12)); + } + + protected void helpTestCreateAndUpdate(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + populateDataTable(conn, dataTableName); + + // create an expression index + String ddl = "CREATE " + + (localIndex ? "LOCAL" : "") + + " INDEX IDX ON " + + fullDataTableName + + " ((UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(char_col2))," + + " (decimal_pk+int_pk+decimal_col2+int_col1)," + " date_pk+1, date1+1, date2+1 )" + + " INCLUDE (long_col1, long_col2)"; + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + + // run select query with expression in WHERE clause + String whereSql = "SELECT long_col1, long_col2 from " + + fullDataTableName + + " WHERE UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(char_col2) = ?" + + " AND decimal_pk+int_pk+decimal_col2+int_col1=?" + // since a.date1 and b.date2 are NULLABLE and date is fixed width, these expressions are stored as + // DECIMAL in the index (which is not fixed width) + + " AND date_pk+1=? AND date1+1=? AND date2+1=?"; + stmt = conn.prepareStatement(whereSql); + stmt.setString(1, "VARCHAR1_CHAR1 _A.VARCHAR1_B.CHAR1 "); + stmt.setInt(2, 4); + Date date = DateUtil.parseDate("2015-01-02 00:00:00"); + stmt.setDate(3, date); + stmt.setDate(4, date); + stmt.setDate(5, date); + + // verify that the query does a range scan on the index table + ResultSet rs = stmt.executeQuery("EXPLAIN " + whereSql); + assertEquals( + localIndex ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_INDEX_TEST." + + dataTableName + + " [-32768,'VARCHAR1_CHAR1 _A.VARCHAR1_B.CHAR1 ',4,'2015-01-02 00:00:00.000',1,420,156,800,000,1,420,156,800,000]\nCLIENT MERGE SORT" + : "CLIENT PARALLEL 1-WAY RANGE SCAN OVER INDEX_TEST.IDX ['VARCHAR1_CHAR1 _A.VARCHAR1_B.CHAR1 ',4,'2015-01-02 00:00:00.000',1,420,156,800,000,1,420,156,800,000]", + QueryUtil.getExplainPlan(rs)); + + // verify that the correct results are returned + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(1, rs.getInt(2)); + assertFalse(rs.next()); + + // verify all rows in data table are present in index table + String indexSelectSql = "SELECT UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(char_col2), " + + "decimal_pk+int_pk+decimal_col2+int_col1, " + + "date_pk+1, date1+1, date2+1, " + + "varchar_pk, char_pk, int_pk, long_pk, decimal_pk, " + + "long_col1, long_col2 " + + "from " + + fullDataTableName; + rs = conn.createStatement().executeQuery("EXPLAIN " + indexSelectSql); + assertEquals(localIndex ? "CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_" + fullDataTableName + + " [-32768]\nCLIENT MERGE SORT" : "CLIENT PARALLEL 1-WAY FULL SCAN OVER INDEX_TEST.IDX", + QueryUtil.getExplainPlan(rs)); + rs = conn.createStatement().executeQuery(indexSelectSql); + verifyResult(rs, 1); + verifyResult(rs, 2); + + // Insert two more rows to the index data table + String upsert = "UPSERT INTO " + fullDataTableName + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + stmt = conn.prepareStatement(upsert); + insertRow(stmt, 3); + insertRow(stmt, 4); + conn.commit(); + + rs = conn.createStatement().executeQuery(indexSelectSql); + verifyResult(rs, 1); + verifyResult(rs, 2); + // verify that two rows added after index was created were also added to + // the index table + verifyResult(rs, 3); + verifyResult(rs, 4); + + // update the first row + upsert = "UPSERT INTO " + + fullDataTableName + + "(varchar_pk, char_pk, int_pk, long_pk, decimal_pk, date_pk, a.varchar_col1) VALUES(?, ?, ?, ?, ?, ?, ?)"; + + stmt = conn.prepareStatement(upsert); + stmt.setString(1, "varchar1"); + stmt.setString(2, "char1"); + stmt.setInt(3, 1); + stmt.setLong(4, 1l); + stmt.setBigDecimal(5, new BigDecimal(1.0)); + stmt.setDate(6, DateUtil.parseDate("2015-01-01 00:00:00")); + stmt.setString(7, "a.varchar_updated"); + stmt.executeUpdate(); + conn.commit(); + + // verify only one row was updated in the data table + String selectSql = "UPPER(varchar_pk) || '_' || UPPER(char_pk) || '_' || UPPER(varchar_col1) || '_' || UPPER(char_col2) from " + + fullDataTableName; + rs = conn.createStatement().executeQuery("SELECT /*+ NO_INDEX */ " + selectSql); + assertTrue(rs.next()); + assertEquals("VARCHAR1_CHAR1 _A.VARCHAR_UPDATED_B.CHAR1 ", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("VARCHAR2_CHAR2 _A.VARCHAR2_B.CHAR2 ", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("VARCHAR3_CHAR3 _A.VARCHAR3_B.CHAR3 ", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("VARCHAR4_CHAR4 _A.VARCHAR4_B.CHAR4 ", rs.getString(1)); + assertFalse(rs.next()); + + // verify that the rows in the index table are also updated + rs = conn.createStatement().executeQuery("SELECT " + selectSql); + assertTrue(rs.next()); + // if the data table is immutable, the index table will have one more + // row + if (!mutable) { + assertEquals("VARCHAR1_CHAR1 _A.VARCHAR1_B.CHAR1 ", rs.getString(1)); + assertTrue(rs.next()); + } + assertEquals("VARCHAR1_CHAR1 _A.VARCHAR_UPDATED_" + (mutable ? "B.CHAR1 " : ""), rs.getString(1)); + assertTrue(rs.next()); + assertEquals("VARCHAR2_CHAR2 _A.VARCHAR2_B.CHAR2 ", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("VARCHAR3_CHAR3 _A.VARCHAR3_B.CHAR3 ", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("VARCHAR4_CHAR4 _A.VARCHAR4_B.CHAR4 ", rs.getString(1)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + private void populateDataTable(Connection conn, String dataTable) throws SQLException { + ensureTableCreated(getUrl(), dataTable); + String upsert = "UPSERT INTO " + INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTable + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + PreparedStatement stmt1 = conn.prepareStatement(upsert); + // insert two rows + insertRow(stmt1, 1); + insertRow(stmt1, 2); + conn.commit(); + } + + @Test + public void testDeleteIndexedExpressionImmutableIndex() throws Exception { + helpTestDeleteIndexedExpression(false, false); + } + + @Test + public void testDeleteIndexedExpressionImmutableLocalIndex() throws Exception { + helpTestDeleteIndexedExpression(false, true); + } + + @Test + public void testDeleteIndexedExpressionMutableIndex() throws Exception { + helpTestDeleteIndexedExpression(true, false); + } + + @Test + public void testDeleteIndexedExpressionMutableLocalIndex() throws Exception { + helpTestDeleteIndexedExpression(true, true); + } + + protected void helpTestDeleteIndexedExpression(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + String fullIndexTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + "IDX"; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + ensureTableCreated(getUrl(), dataTableName); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (2*long_col2)"; + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullDataTableName); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + + conn.setAutoCommit(true); + String dml = "DELETE from " + fullDataTableName + " WHERE long_col2 = 2"; + try { + conn.createStatement().execute(dml); + if (!mutable) { + fail(); + } + } catch (SQLException e) { + if (!mutable) { + assertEquals(SQLExceptionCode.INVALID_FILTER_ON_IMMUTABLE_ROWS.getErrorCode(), e.getErrorCode()); + } + } + + if (!mutable) { + dml = "DELETE from " + fullDataTableName + " WHERE 2*long_col2 = 4"; + conn.createStatement().execute(dml); + } + + rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullDataTableName); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testDeleteCoveredColImmutableIndex() throws Exception { + helpTestDeleteCoveredCol(false, false); + } + + @Test + public void testDeleteCoveredColImmutableLocalIndex() throws Exception { + helpTestDeleteCoveredCol(false, true); + } + + @Test + public void testDeleteCoveredColMutableIndex() throws Exception { + helpTestDeleteCoveredCol(true, false); + } + + @Test + public void testDeleteCoveredColMutableLocalIndex() throws Exception { + helpTestDeleteCoveredCol(true, true); + } + + protected void helpTestDeleteCoveredCol(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + String fullIndexTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + "IDX"; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + ensureTableCreated(getUrl(), dataTableName); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (long_pk, varchar_pk, 1+long_pk, UPPER(varchar_pk) )" + " INCLUDE (long_col1, long_col2)"; + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullDataTableName); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + rs = conn.createStatement().executeQuery("SELECT COUNT(*) FROM " + fullIndexTableName); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + + String dml = "DELETE from " + fullDataTableName + " WHERE long_col2 = 2"; + assertEquals(1, conn.createStatement().executeUpdate(dml)); + conn.commit(); + + String query = "SELECT /*+ NO_INDEX */ long_pk, varchar_pk, 1+long_pk, UPPER(varchar_pk) FROM " + + fullDataTableName; + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals(1L, rs.getLong(1)); + assertEquals("varchar1", rs.getString(2)); + assertEquals(2L, rs.getLong(3)); + assertEquals("VARCHAR1", rs.getString(4)); + assertFalse(rs.next()); + + query = "SELECT long_pk, varchar_pk, 1+long_pk, UPPER(varchar_pk) FROM " + fullDataTableName; + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals(1L, rs.getLong(1)); + assertEquals("varchar1", rs.getString(2)); + assertEquals(2L, rs.getLong(3)); + assertEquals("VARCHAR1", rs.getString(4)); + assertFalse(rs.next()); + + query = "SELECT * FROM " + fullIndexTableName; + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + + assertEquals(1L, rs.getLong(1)); + assertEquals("varchar1", rs.getString(2)); + assertEquals(2L, rs.getLong(3)); + assertEquals("VARCHAR1", rs.getString(4)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testGroupByCountImmutableIndex() throws Exception { + helpTestGroupByCount(false, false); + } + + @Test + public void testGroupByCountImmutableLocalIndex() throws Exception { + helpTestGroupByCount(false, true); + } + + @Test + public void testGroupByCountMutableIndex() throws Exception { + helpTestGroupByCount(true, false); + } + + @Test + public void testGroupByCountMutableLocalIndex() throws Exception { + helpTestGroupByCount(true, true); + } + + protected void helpTestGroupByCount(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (int_col1+int_col2)"; + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + + String groupBySql = "SELECT (int_col1+int_col2), COUNT(*) FROM " + fullDataTableName + + " GROUP BY (int_col1+int_col2)"; + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + groupBySql); + String expectedPlan = "CLIENT PARALLEL 1-WAY " + + (localIndex ? "RANGE SCAN OVER _LOCAL_IDX_" + fullDataTableName + " [-32768]" + : "FULL SCAN OVER INDEX_TEST.IDX") + + "\n SERVER FILTER BY FIRST KEY ONLY\n SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [TO_BIGINT((A.INT_COL1 + B.INT_COL2))]\nCLIENT MERGE SORT"; + assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs)); + rs = conn.createStatement().executeQuery(groupBySql); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(2)); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(2)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testSelectDistinctImmutableIndex() throws Exception { + helpTestSelectDistinct(false, false); + } + + @Test + public void testSelectDistinctImmutableIndexLocal() throws Exception { + helpTestSelectDistinct(false, true); + } + + @Test + public void testSelectDistinctMutableIndex() throws Exception { + helpTestSelectDistinct(true, false); + } + + @Test + public void testSelectDistinctMutableLocalIndex() throws Exception { + helpTestSelectDistinct(true, true); + } + + protected void helpTestSelectDistinct(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (int_col1+1)"; + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + String sql = "SELECT distinct int_col1+1 FROM " + fullDataTableName + " where int_col1+1 > 0"; + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); + String expectedPlan = "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + + (localIndex ? "_LOCAL_IDX_" + fullDataTableName + " [-32768,0] - [-32768,*]" + : "INDEX_TEST.IDX [0] - [*]") + + "\n SERVER FILTER BY FIRST KEY ONLY\n SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [TO_BIGINT((A.INT_COL1 + 1))]\nCLIENT MERGE SORT"; + assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs)); + rs = conn.createStatement().executeQuery(sql); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(3, rs.getInt(1)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testInClauseWithImmutableIndex() throws Exception { + helpTestInClauseWithIndex(false, false); + } + + @Test + public void testInClauseWithImmutableLocalIndex() throws Exception { + helpTestInClauseWithIndex(false, true); + } + + @Test + public void testInClauseWithMutableIndex() throws Exception { + helpTestInClauseWithIndex(true, false); + } + + @Test + public void testInClauseWithMutableLocalIndex() throws Exception { + helpTestInClauseWithIndex(true, false); + } + + protected void helpTestInClauseWithIndex(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (int_col1+1)"; + + conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + String sql = "SELECT int_col1+1 FROM " + fullDataTableName + " where int_col1+1 IN (2)"; + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); + assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + + (localIndex ? "_LOCAL_IDX_" + fullDataTableName + " [-32768,2]\n SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" + : "INDEX_TEST.IDX [2]\n SERVER FILTER BY FIRST KEY ONLY"), QueryUtil.getExplainPlan(rs)); + rs = conn.createStatement().executeQuery(sql); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testOrderByWithImmutableIndex() throws Exception { + helpTestOrderByWithIndex(false, false); + } + + @Test + public void testOrderByWithImmutableLocalIndex() throws Exception { + helpTestOrderByWithIndex(false, true); + } + + @Test + public void testOrderByWithMutableIndex() throws Exception { + helpTestOrderByWithIndex(true, false); + } + + @Test + public void testOrderByWithMutableLocalIndex() throws Exception { + helpTestOrderByWithIndex(true, false); + } + + protected void helpTestOrderByWithIndex(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (int_col1+1)"; + + conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + String sql = "SELECT int_col1+1 FROM " + fullDataTableName + " ORDER BY int_col1+1"; + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); + assertEquals("CLIENT PARALLEL 1-WAY " + + (localIndex ? "RANGE SCAN OVER _LOCAL_IDX_" + fullDataTableName + + " [-32768]\n SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" + : "FULL SCAN OVER INDEX_TEST.IDX\n SERVER FILTER BY FIRST KEY ONLY"), + QueryUtil.getExplainPlan(rs)); + rs = conn.createStatement().executeQuery(sql); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(3, rs.getInt(1)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testSelectColOnlyInDataTableImmutableIndex() throws Exception { + helpTestSelectColOnlyInDataTable(false, false); + } + + @Test + public void testSelectColOnlyInDataTableImmutableLocalIndex() throws Exception { + helpTestSelectColOnlyInDataTable(false, true); + } + + @Test + public void testSelectColOnlyInDataTableMutableIndex() throws Exception { + helpTestSelectColOnlyInDataTable(true, false); + } + + @Test + public void testSelectColOnlyInDataTableMutableLocalIndex() throws Exception { + helpTestSelectColOnlyInDataTable(true, false); + } + + protected void helpTestSelectColOnlyInDataTable(boolean mutable, boolean localIndex) throws Exception { + String dataTableName = mutable ? MUTABLE_INDEX_DATA_TABLE : INDEX_DATA_TABLE; + String fullDataTableName = INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR + dataTableName; + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.setAutoCommit(false); + populateDataTable(conn, dataTableName); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX IDX ON " + fullDataTableName + + " (int_col1+1)"; + + conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + PreparedStatement stmt = conn.prepareStatement(ddl); + stmt.execute(); + String sql = "SELECT int_col1+1, int_col2 FROM " + fullDataTableName + " WHERE int_col1+1=2"; + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); + assertEquals("CLIENT PARALLEL 1-WAY " + + (localIndex ? "RANGE SCAN OVER _LOCAL_IDX_" + fullDataTableName + + " [-32768,2]\n SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" : "FULL SCAN OVER " + + fullDataTableName + "\n SERVER FILTER BY (A.INT_COL1 + 1) = 2"), + QueryUtil.getExplainPlan(rs)); + rs = conn.createStatement().executeQuery(sql); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertEquals(1, rs.getInt(2)); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX IDX ON " + fullDataTableName); + } finally { + conn.close(); + } + } + + @Test + public void testImmutableIndexWithCaseSensitiveCols() throws Exception { + helpTestIndexWithCaseSensitiveCols(false, false); + } + + @Test + public void testImmutableLocalIndexWithCaseSensitiveCols() throws Exception { + helpTestIndexWithCaseSensitiveCols(true, false); + } + + @Test + public void testMutableIndexWithCaseSensitiveCols() throws Exception { + helpTestIndexWithCaseSensitiveCols(true, false); + } + + @Test + public void testMutableLocalIndexWithCaseSensitiveCols() throws Exception { + helpTestIndexWithCaseSensitiveCols(true, false); + } + + protected void helpTestIndexWithCaseSensitiveCols(boolean mutable, boolean localIndex) throws Exception { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + try { + conn.createStatement().execute("CREATE TABLE cs (k VARCHAR NOT NULL PRIMARY KEY, \"V1\" VARCHAR, \"v2\" VARCHAR) "+ (mutable ? "IMMUTABLE_ROWS=true" : "")); + String query = "SELECT * FROM cs"; + ResultSet rs = conn.createStatement().executeQuery(query); + assertFalse(rs.next()); + if (localIndex) { + conn.createStatement().execute("CREATE LOCAL INDEX ics ON cs (\"v2\" || '_modified') INCLUDE (\"V1\",\"v2\")"); + } else { + conn.createStatement().execute("CREATE INDEX ics ON cs (\"V1\" || '_' || \"v2\") INCLUDE (\"V1\",\"v2\")"); + } + query = "SELECT * FROM ics"; + rs = conn.createStatement().executeQuery(query); + assertFalse(rs.next()); + + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO cs VALUES(?,?,?)"); + stmt.setString(1,"a"); + stmt.setString(2, "x"); + stmt.setString(3, "1"); + stmt.execute(); + stmt.setString(1,"b"); + stmt.setString(2, "y"); + stmt.setString(3, "2"); + stmt.execute(); + conn.commit(); + + //TODO FIX THIS change this to * + query = "SELECT (\"V1\" || '_' || \"v2\"), k, \"V1\", \"v2\" FROM cs WHERE (\"V1\" || '_' || \"v2\") = 'x_1'"; + rs = conn.createStatement().executeQuery("EXPLAIN " + query); + if(localIndex){ + assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_CS [-32768,'x_1']\n" + + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + } else { + assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER ICS ['x_1']", QueryUtil.getExplainPlan(rs)); + } + + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals("x_1",rs.getString(1)); + assertEquals("a",rs.getString(2)); + assertEquals("x",rs.getString(3)); + assertEquals("1",rs.getString(4)); + //TODO figure out why this " " is needed + assertEquals("x_1",rs.getString("\"('V1' || '_' || 'v2')\"")); + assertEquals("a",rs.getString("k")); + assertEquals("x",rs.getString("V1")); + assertEquals("1",rs.getString("v2")); + assertFalse(rs.next()); + + query = "SELECT \"V1\", \"V1\" as foo1, (\"V1\" || '_' || \"v2\") as foo, (\"V1\" || '_' || \"v2\") as \"Foo1\", (\"V1\" || '_' || \"v2\") FROM cs ORDER BY foo"; + rs = conn.createStatement().executeQuery("EXPLAIN " + query); + if(localIndex){ + assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _LOCAL_IDX_CS [-32768]\nCLIENT MERGE SORT", + QueryUtil.getExplainPlan(rs)); + } else { + assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER ICS", QueryUtil.getExplainPlan(rs)); + } + + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals("x",rs.getString(1)); + assertEquals("x",rs.getString("V1")); + assertEquals("x",rs.getString(2)); + assertEquals("x",rs.getString("foo1")); + assertEquals("x_1",rs.getString(3)); + assertEquals("x_1",rs.getString("Foo")); + assertEquals("x_1",rs.getString(4)); + assertEquals("x_1",rs.getString("Foo1")); + assertEquals("x_1",rs.getString(5)); + assertEquals("x_1",rs.getString("\"('V1' || '_' || 'v2')\"")); + assertTrue(rs.next()); + assertEquals("y",rs.getString(1)); + assertEquals("y",rs.getString("V1")); + assertEquals("y",rs.getString(2)); + assertEquals("y",rs.getString("foo1")); + assertEquals("y_2",rs.getString(3)); + assertEquals("y_2",rs.getString("Foo")); + assertEquals("y_2",rs.getString(4)); + assertEquals("y_2",rs.getString("Foo1")); + assertEquals("y_2",rs.getString(5)); + assertEquals("y_2",rs.getString("\"('V1' || '_' || 'v2')\"")); + assertFalse(rs.next()); + conn.createStatement().execute("DROP INDEX ICS ON CS"); + } finally { + conn.close(); + } + } + + @Test + public void testImmutableIndexDropIndexedColumn() throws Exception { + helpTestDropIndexedColumn(false, false); + } + + @Test + public void testImmutableLocalIndexDropIndexedColumn() throws Exception { + helpTestDropIndexedColumn(false, true); + } + + @Test + public void testMutableIndexDropIndexedColumn() throws Exception { + helpTestDropIndexedColumn(true, false); + } + + @Test + public void testMutableLocalIndexDropIndexedColumn() throws Exception { + helpTestDropIndexedColumn(true, true); + } + + public void helpTestDropIndexedColumn(boolean mutable, boolean local) 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 t (k VARCHAR NOT NULL PRIMARY KEY, v1 VARCHAR, v2 VARCHAR)"); + query = "SELECT * FROM t" ; + rs = conn.createStatement().executeQuery(query); + assertFalse(rs.next()); + String indexName = "it_" + (mutable ? "m" : "im") + "_" + (local ? "l" : "h"); + conn.createStatement().execute("CREATE " + ( local ? "LOCAL" : "") + " INDEX " + indexName + " ON t (v1 || '_' || v2)"); + + query = "SELECT * FROM t"; + rs = conn.createStatement().executeQuery(query); + assertFalse(rs.next()); + + // load some data into the table + stmt = conn.prepareStatement("UPSERT INTO t VALUES(?,?,?)"); + stmt.setString(1, "a"); + stmt.setString(2, "x"); + stmt.setString(3, "1"); + stmt.execute(); + conn.commit(); + + assertIndexExists(conn,true); + conn.createStatement().execute("ALTER TABLE t DROP COLUMN v1"); + assertIndexExists(conn,false); + + query = "SELECT * FROM t"; + 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 t VALUES(?,?)"); + stmt.setString(1, "a"); + stmt.setString(2, "2"); + stmt.execute(); + conn.commit(); + + query = "SELECT * FROM t"; + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals("a",rs.getString(1)); + assertEquals("2",rs.getString(2)); + assertFalse(rs.next()); + } + + private static void assertIndexExists(Connection conn, boolean exists) throws SQLException { + ResultSet rs = conn.getMetaData().getIndexInfo(null, null, "T", false, false); + assertEquals(exists, rs.next()); + } + +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java index 88e7340..d6ced3c 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexMetadataIT.java @@ -100,7 +100,7 @@ public class IndexMetadataIT extends BaseHBaseManagedTimeIT { String fullTableName = SchemaUtil.getTableName(schemaName, tableName); conn.createStatement().executeQuery("SELECT count(*) FROM " + fullTableName).next(); // client side cache will update PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class); - pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).getIndexMaintainers(ptr); + pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).getIndexMaintainers(ptr, pconn); assertTrue(ptr.getLength() > 0); } @@ -109,7 +109,7 @@ public class IndexMetadataIT extends BaseHBaseManagedTimeIT { String fullTableName = SchemaUtil.getTableName(schemaName, tableName); conn.createStatement().executeQuery("SELECT count(*) FROM " + fullTableName).next(); // client side cache will update PhoenixConnection pconn = conn.unwrap(PhoenixConnection.class); - pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).getIndexMaintainers(ptr); + pconn.getMetaDataCache().getTable(new PTableKey(pconn.getTenantId(), fullTableName)).getIndexMaintainers(ptr, pconn); assertTrue(ptr.getLength() == 0); } @@ -135,8 +135,9 @@ public class IndexMetadataIT extends BaseHBaseManagedTimeIT { assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 5, ":CHAR_PK", Order.ASC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 6, ":LONG_PK", Order.DESC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 7, ":DECIMAL_PK", Order.ASC); - assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 8, "A:INT_COL1", null); - assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 9, "B:INT_COL2", null); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 8, ":DATE_PK", Order.ASC); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 9, "A:INT_COL1", null); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX", 10, "B:INT_COL2", null); assertFalse(rs.next()); rs = conn.getMetaData().getTables(null, StringUtil.escapeLike(INDEX_DATA_SCHEMA), StringUtil.escapeLike("IDX"), new String[] {PTableType.INDEX.getValue().getString() }); @@ -245,8 +246,9 @@ public class IndexMetadataIT extends BaseHBaseManagedTimeIT { assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 5, ":CHAR_PK", Order.ASC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 6, ":LONG_PK", Order.DESC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 7, ":DECIMAL_PK", Order.ASC); - assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 8, "A:INT_COL1", null); - assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 9, "B:INT_COL2", null); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 8, ":DATE_PK", Order.ASC); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 9, "A:INT_COL1", null); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX1", 10, "B:INT_COL2", null); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 1, "A:VARCHAR_COL1", Order.ASC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 2, "B:VARCHAR_COL2", Order.ASC); @@ -255,7 +257,8 @@ public class IndexMetadataIT extends BaseHBaseManagedTimeIT { assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 5, ":CHAR_PK", Order.ASC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 6, ":LONG_PK", Order.DESC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 7, ":DECIMAL_PK", Order.ASC); - assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 8, "B:INT_COL2", null); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 8, ":DATE_PK", Order.ASC); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, MUTABLE_INDEX_DATA_TABLE, "IDX2", 9, "B:INT_COL2", null); assertFalse(rs.next()); // Create another table in the same schema @@ -307,7 +310,8 @@ public class IndexMetadataIT extends BaseHBaseManagedTimeIT { assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 6, ":INT_PK", Order.ASC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 7, ":LONG_PK", Order.DESC); assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 8, ":DECIMAL_PK", Order.ASC); - assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 9, "A:INT_COL1", null); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 9, ":DATE_PK", Order.ASC); + assertIndexInfoMetadata(rs, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX", 10, "A:INT_COL1", null); assertFalse(rs.next()); rs = IndexTestUtil.readDataTableIndexRow(conn, INDEX_DATA_SCHEMA, INDEX_DATA_TABLE, "IDX"); http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/antlr3/PhoenixSQL.g ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g index bcf26be..fad5fb3 100644 --- a/phoenix-core/src/main/antlr3/PhoenixSQL.g +++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g @@ -393,11 +393,11 @@ create_view_node returns [CreateTableStatement ret] // Parse a create index statement. create_index_node returns [CreateIndexStatement ret] : CREATE l=LOCAL? INDEX (IF NOT ex=EXISTS)? i=index_name ON t=from_table_name - (LPAREN pk=index_pk_constraint RPAREN) + (LPAREN ik=ik_constraint RPAREN) (INCLUDE (LPAREN icrefs=column_names RPAREN))? (p=fam_properties)? (SPLIT ON v=value_expression_list)? - {ret = factory.createIndex(i, factory.namedTable(null,t), pk, icrefs, v, p, ex!=null, l==null ? IndexType.getDefault() : IndexType.LOCAL, getBindCount()); } + {ret = factory.createIndex(i, factory.namedTable(null,t), ik, icrefs, v, p, ex!=null, l==null ? IndexType.getDefault() : IndexType.LOCAL, getBindCount()); } ; // Parse a create sequence statement. @@ -436,17 +436,17 @@ col_name_with_sort_order returns [Pair<ColumnName, SortOrder> ret] : f=identifier (order=ASC|order=DESC)? {$ret = Pair.newPair(factory.columnName(f), order == null ? SortOrder.getDefault() : SortOrder.fromDDLValue(order.getText()));} ; -index_pk_constraint returns [PrimaryKeyConstraint ret] - : cols = col_def_name_with_sort_order_list {$ret = factory.primaryKey(null, cols); } - ; +ik_constraint returns [IndexKeyConstraint ret] + : x = expression_with_sort_order_list {$ret = factory.indexKey(x); } +; -col_def_name_with_sort_order_list returns [List<Pair<ColumnName, SortOrder>> ret] -@init{ret = new ArrayList<Pair<ColumnName, SortOrder>>(); } - : p=col_def_name_with_sort_order {$ret.add(p);} (COMMA p = col_def_name_with_sort_order {$ret.add(p);} )* +expression_with_sort_order_list returns [List<Pair<ParseNode, SortOrder>> ret] +@init{ret = new ArrayList<Pair<ParseNode, SortOrder>>(); } + : p=expression_with_sort_order {$ret.add(p);} (COMMA p = expression_with_sort_order {$ret.add(p);} )* ; -col_def_name_with_sort_order returns [Pair<ColumnName, SortOrder> ret] - : c=column_name (order=ASC|order=DESC)? {$ret = Pair.newPair(c, order == null ? SortOrder.getDefault() : SortOrder.fromDDLValue(order.getText()));} +expression_with_sort_order returns [Pair<ParseNode, SortOrder> ret] + : (x=expression) (order=ASC|order=DESC)? {$ret = Pair.newPair(x, order == null ? SortOrder.getDefault() : SortOrder.fromDDLValue(order.getText()));} ; fam_properties returns [ListMultimap<String,Pair<String,Object>> ret] @@ -780,7 +780,9 @@ term returns [ParseNode ret] | field=identifier LPAREN l=zero_or_more_expressions RPAREN wg=(WITHIN GROUP LPAREN ORDER BY l2=one_or_more_expressions (a=ASC | DESC) RPAREN)? { FunctionParseNode f = wg==null ? factory.function(field, l) : factory.function(field,l,l2,a!=null); - contextStack.peek().setAggregate(f.isAggregate()); + if (!contextStack.isEmpty()) { + contextStack.peek().setAggregate(f.isAggregate()); + } $ret = f; } | field=identifier LPAREN t=ASTERISK RPAREN @@ -789,13 +791,17 @@ term returns [ParseNode ret] throwRecognitionException(t); } FunctionParseNode f = factory.function(field, LiteralParseNode.STAR); - contextStack.peek().setAggregate(f.isAggregate()); + if (!contextStack.isEmpty()) { + contextStack.peek().setAggregate(f.isAggregate()); + } $ret = f; } | field=identifier LPAREN t=DISTINCT l=zero_or_more_expressions RPAREN { FunctionParseNode f = factory.functionDistinct(field, l); - contextStack.peek().setAggregate(f.isAggregate()); + if (!contextStack.isEmpty()) { + contextStack.peek().setAggregate(f.isAggregate()); + } $ret = f; } | e=case_statement { $ret = e; } http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/java/org/apache/hadoop/hbase/regionserver/IndexHalfStoreFileReaderGenerator.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/hadoop/hbase/regionserver/IndexHalfStoreFileReaderGenerator.java b/phoenix-core/src/main/java/org/apache/hadoop/hbase/regionserver/IndexHalfStoreFileReaderGenerator.java index f213d2d..718f820 100644 --- a/phoenix-core/src/main/java/org/apache/hadoop/hbase/regionserver/IndexHalfStoreFileReaderGenerator.java +++ b/phoenix-core/src/main/java/org/apache/hadoop/hbase/regionserver/IndexHalfStoreFileReaderGenerator.java @@ -57,8 +57,8 @@ import org.apache.phoenix.schema.MetaDataClient; import org.apache.phoenix.schema.PColumn; import org.apache.phoenix.schema.PIndexState; import org.apache.phoenix.schema.PTable; -import org.apache.phoenix.schema.PTableType; import org.apache.phoenix.schema.PTable.IndexType; +import org.apache.phoenix.schema.PTableType; import org.apache.phoenix.util.ByteUtil; import org.apache.phoenix.util.IndexUtil; import org.apache.phoenix.util.MetaDataUtil; @@ -144,7 +144,7 @@ public class IndexHalfStoreFileReaderGenerator extends BaseRegionObserver { new HashMap<ImmutableBytesWritable, IndexMaintainer>(); for (PTable index : indexes) { if (index.getIndexType() == IndexType.LOCAL) { - IndexMaintainer indexMaintainer = index.getIndexMaintainer(dataTable); + IndexMaintainer indexMaintainer = index.getIndexMaintainer(dataTable, conn); indexMaintainers.put(new ImmutableBytesWritable(MetaDataUtil .getViewIndexIdDataType().toBytes(index.getViewIndexId())), indexMaintainer); http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java index 0a2ee38..322d24a 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java @@ -65,7 +65,6 @@ import org.apache.phoenix.schema.MetaDataClient; import org.apache.phoenix.schema.MetaDataEntityNotFoundException; import org.apache.phoenix.schema.PColumn; import org.apache.phoenix.schema.PIndexState; -import org.apache.phoenix.schema.types.PLong; import org.apache.phoenix.schema.PName; import org.apache.phoenix.schema.PRow; import org.apache.phoenix.schema.PTable; @@ -75,6 +74,7 @@ import org.apache.phoenix.schema.ReadOnlyTableException; import org.apache.phoenix.schema.SortOrder; import org.apache.phoenix.schema.TableRef; import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.PLong; import org.apache.phoenix.util.MetaDataUtil; import org.apache.phoenix.util.ScanUtil; @@ -483,7 +483,7 @@ public class DeleteCompiler { public MutationState execute() throws SQLException { // TODO: share this block of code with UPSERT SELECT ImmutableBytesWritable ptr = context.getTempPtr(); - tableRef.getTable().getIndexMaintainers(ptr); + tableRef.getTable().getIndexMaintainers(ptr, context.getConnection()); ServerCache cache = null; try { if (ptr.getLength() > 0) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java index 95e145c..97818e6 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java @@ -135,6 +135,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio protected final StatementContext context; protected final GroupBy groupBy; private int nodeCount; + private int totalNodeCount; private final boolean resolveViewConstants; public ExpressionCompiler(StatementContext context) { @@ -166,6 +167,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio public void reset() { this.isAggregate = false; this.nodeCount = 0; + this.totalNodeCount = 0; } @Override @@ -420,6 +422,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio @Override public void addElement(List<Expression> l, Expression element) { nodeCount--; + totalNodeCount++; l.add(element); } @@ -553,7 +556,7 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio expr = CastParseNode.convertToRoundExpressionIfNeeded(fromDataType, targetDataType, children); } } - return CoerceExpression.create(expr, targetDataType, SortOrder.getDefault(), expr.getMaxLength()); + return wrapGroupByExpression(CoerceExpression.create(expr, targetDataType, SortOrder.getDefault(), expr.getMaxLength())); } @Override @@ -1254,4 +1257,8 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio Object result = context.getSubqueryResult(node.getSelectNode()); return LiteralExpression.newConstant(result); } + + public int getTotalNodeCount() { + return totalNodeCount; + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/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 0163082..1a605b7 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 @@ -184,7 +184,7 @@ public class FromCompiler { 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()); + column.getPosition(), sourceExpression.getSortOrder(), column.getArraySize(), column.getViewConstant(), column.isViewReferenced(), column.getExpressionStr()); projectedColumns.add(projectedColumn); sourceExpressions.add(sourceExpression); } @@ -391,7 +391,7 @@ public class FromCompiler { familyName = PNameFactory.newName(family); } allcolumns.add(new PColumnImpl(name, familyName, dynColumn.getDataType(), dynColumn.getMaxLength(), - dynColumn.getScale(), dynColumn.isNull(), position, dynColumn.getSortOrder(), dynColumn.getArraySize(), null, false)); + dynColumn.getScale(), dynColumn.isNull(), position, dynColumn.getSortOrder(), dynColumn.getArraySize(), null, false, dynColumn.getExpression())); position++; } theTable = PTableImpl.makePTable(theTable, allcolumns); @@ -469,7 +469,7 @@ public class FromCompiler { } PColumnImpl column = new PColumnImpl(PNameFactory.newName(alias), PNameFactory.newName(QueryConstants.DEFAULT_COLUMN_FAMILY), - null, 0, 0, true, position++, SortOrder.ASC, null, null, false); + null, 0, 0, true, position++, SortOrder.ASC, null, null, false, null); columns.add(column); } PTable t = PTableImpl.makePTable(null, PName.EMPTY_NAME, PName.EMPTY_NAME, http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/java/org/apache/phoenix/compile/HavingCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/HavingCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/HavingCompiler.java index 0cd6ecf..224a9b4 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/HavingCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/HavingCompiler.java @@ -35,8 +35,8 @@ import org.apache.phoenix.parse.ParseNode; import org.apache.phoenix.parse.SelectStatement; import org.apache.phoenix.parse.SelectStatementRewriter; import org.apache.phoenix.schema.ColumnRef; -import org.apache.phoenix.schema.types.PBoolean; import org.apache.phoenix.schema.TypeMismatchException; +import org.apache.phoenix.schema.types.PBoolean; public class HavingCompiler { @@ -171,7 +171,7 @@ public class HavingCompiler { @Override public Void visit(ColumnParseNode node) throws SQLException { ColumnRef ref = context.getResolver().resolveColumn(node.getSchemaName(), node.getTableName(), node.getName()); - boolean isAggregateColumn = groupBy.getExpressions().indexOf(ref.newColumnExpression()) >= 0; + boolean isAggregateColumn = groupBy.getExpressions().indexOf(ref.newColumnExpression(node.isTableNameCaseSensitive(), node.isCaseSensitive())) >= 0; if (hasOnlyAggregateColumns == null) { hasOnlyAggregateColumns = isAggregateColumn; } else { http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/java/org/apache/phoenix/compile/IndexExpressionCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/IndexExpressionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/IndexExpressionCompiler.java new file mode 100644 index 0000000..b4a4168 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/IndexExpressionCompiler.java @@ -0,0 +1,53 @@ +/* + * 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.compile; + +import java.sql.SQLException; + +import org.apache.phoenix.expression.ColumnExpression; +import org.apache.phoenix.parse.ColumnParseNode; +import org.apache.phoenix.schema.ColumnRef; + +/** + * Used to check if the expression being compiled is a {@link ColumnExpression} + */ +public class IndexExpressionCompiler extends ExpressionCompiler { + + // + private ColumnRef columnRef; + + public IndexExpressionCompiler(StatementContext context) { + super(context); + this.columnRef = null; + } + + @Override + public void reset() { + super.reset(); + this.columnRef = null; + } + + @Override + protected ColumnRef resolveColumn(ColumnParseNode node) throws SQLException { + ColumnRef columnRef = super.resolveColumn(node); + if (isTopLevel()) { + this.columnRef = columnRef; + } + return columnRef; + } + + /** + * @return if the expression being compiled is a regular column the column ref, else is null + */ + public ColumnRef getColumnRef() { + return columnRef; + } + +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/5acebb38/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java index 445edd8..c29ea23 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java @@ -788,7 +788,7 @@ public class JoinCompiler { PName name = PNameFactory.newName(aliasedName); PColumnImpl column = new PColumnImpl(name, familyName, sourceColumn.getDataType(), sourceColumn.getMaxLength(), sourceColumn.getScale(), sourceColumn.isNullable(), - position, sourceColumn.getSortOrder(), sourceColumn.getArraySize(), sourceColumn.getViewConstant(), sourceColumn.isViewReferenced()); + position, sourceColumn.getSortOrder(), sourceColumn.getArraySize(), sourceColumn.getViewConstant(), sourceColumn.isViewReferenced(), sourceColumn.getExpressionStr()); Expression sourceExpression = isLocalIndexColumnRef ? NODE_FACTORY.column(TableName.create(schemaName, tableName), "\"" + colName + "\"", null).accept(new ExpressionCompiler(context)) : new ColumnRef(tableRef, sourceColumn.getPosition()).newColumnExpression(); @@ -807,7 +807,7 @@ public class JoinCompiler { Expression sourceExpression = rowProjector.getColumnProjector(column.getPosition()).getExpression(); PColumnImpl projectedColumn = new PColumnImpl(PNameFactory.newName(colName), PNameFactory.newName(TupleProjector.VALUE_COLUMN_FAMILY), sourceExpression.getDataType(), sourceExpression.getMaxLength(), sourceExpression.getScale(), sourceExpression.isNullable(), - column.getPosition(), sourceExpression.getSortOrder(), column.getArraySize(), column.getViewConstant(), column.isViewReferenced()); + column.getPosition(), sourceExpression.getSortOrder(), column.getArraySize(), column.getViewConstant(), column.isViewReferenced(), column.getExpressionStr()); projectedColumns.add(projectedColumn); sourceExpressions.add(sourceExpression); } @@ -1367,7 +1367,7 @@ public class JoinCompiler { } else { PColumnImpl column = new PColumnImpl(c.getName(), c.getFamilyName(), c.getDataType(), c.getMaxLength(), c.getScale(), true, c.getPosition(), - c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced()); + c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced(), c.getExpressionStr()); merged.add(column); } } @@ -1378,7 +1378,7 @@ public class JoinCompiler { PColumnImpl column = new PColumnImpl(c.getName(), PNameFactory.newName(TupleProjector.VALUE_COLUMN_FAMILY), c.getDataType(), c.getMaxLength(), c.getScale(), type == JoinType.Inner ? c.isNullable() : true, position++, - c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced()); + c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced(), c.getExpressionStr()); merged.add(column); } }