Repository: phoenix Updated Branches: refs/heads/4.x-HBase-0.98 c261ea3ac -> 9ab693258
PHOENIX-2160 Projection of specific array index does not work (Dumindu Buddhika) Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/9ab69325 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/9ab69325 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/9ab69325 Branch: refs/heads/4.x-HBase-0.98 Commit: 9ab69325830792efcba314ee4256505d5a7f46d9 Parents: c261ea3 Author: ramkrishna <ramkrishna.s.vasude...@gmail.com> Authored: Fri Sep 4 10:50:13 2015 +0530 Committer: ramkrishna <ramkrishna.s.vasude...@gmail.com> Committed: Fri Sep 4 10:50:13 2015 +0530 ---------------------------------------------------------------------- .../org/apache/phoenix/end2end/ArrayIT.java | 441 +++++++++++++++++++ .../phoenix/compile/ProjectionCompiler.java | 130 ++++-- .../apache/phoenix/compile/RowProjector.java | 4 +- .../coprocessor/BaseScannerRegionObserver.java | 15 +- .../expression/ProjectedColumnExpression.java | 7 +- .../visitor/CloneExpressionVisitor.java | 47 +- .../CloneNonDeterministicExpressionVisitor.java | 30 ++ .../ProjectedColumnExpressionVisitor.java | 25 ++ .../ReplaceArrayFunctionExpressionVisitor.java | 46 ++ .../apache/phoenix/iterate/ExplainTable.java | 3 + .../phoenix/compile/QueryCompilerTest.java | 77 ++++ .../expression/ArithmeticOperationTest.java | 8 +- 12 files changed, 761 insertions(+), 72 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java index 800a7b4..b7a2ad2 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayIT.java @@ -2238,4 +2238,445 @@ public class ArrayIT extends BaseClientManagedTimeIT { rs = stmt.executeQuery(); assertTrue(rs.next()); } + + @Test + public void testServerArrayElementProjection1() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1] FROM a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals("a", rs.getString(3)); + } + + @Test + public void testServerArrayElementProjection2() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY, arr3 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1], arr3[1] from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals("a", rs.getString(3)); + assertEquals(2, rs.getInt(4)); + } + + @Test + public void testServerArrayElementProjection3() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY, arr3 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1], arr3, arr3[1] from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals("a", rs.getString(3)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{2, 3}), rs.getArray(4)); + assertEquals(2, rs.getInt(5)); + } + + @Test + public void testServerArrayElementProjection4() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 VARCHAR ARRAY, arr3 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY['a', 'b'], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], arr2[1], ARRAY_APPEND(arr3, 4), arr3[1] from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals("a", rs.getString(3)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{2, 3, 4}), rs.getArray(4)); + assertEquals(2, rs.getInt(5)); + } + + @Test + public void testServerArrayElementProjection5() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr3 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_APPEND(arr1, arr3[1]) from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 2}), rs.getArray(3)); + } + + @Test + public void testServerArrayElementProjection6() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_APPEND(arr1, arr2[1]), p from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 2}), rs.getArray(3)); + assertEquals(1, rs.getInt(4)); + } + + @Test + public void testServerArrayElementProjection7() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_APPEND(ARRAY_APPEND(arr1, arr2[2]), arr2[1]), p from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 3, 2}), rs.getArray(3)); + assertEquals(1, rs.getInt(4)); + } + + @Test + public void testServerArrayElementProjection8() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals(1, rs.getInt(3)); + assertEquals(1, rs.getInt(4)); + assertEquals(3, rs.getInt(5)); + } + + @Test + public void testServerArrayElementProjection9() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER ARRAY PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (ARRAY[5, 6], ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1, arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2}), rs.getArray(1)); + assertEquals(1, rs.getInt(2)); + assertEquals(1, rs.getInt(3)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{5, 6}), rs.getArray(4)); + assertEquals(3, rs.getInt(5)); + } + + @Test + public void testServerArrayElementProjection10() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT arr1[1] + 5, arr2[1] FROM a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(6, rs.getInt(1)); + assertEquals(2, rs.getInt(2)); + } + + @Test + public void testServerArrayElementProjection11() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (2, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (3, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (4, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT CASE WHEN p = 1 THEN arr1[1] WHEN p = 2 THEN arr1[2] WHEN p = 3 THEN arr2[1] ELSE arr2[2] END FROM a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(3, rs.getInt(1)); + assertFalse(rs.next()); + } + + @Test + public void testServerArrayElementProjection12() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (2, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (3, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (4, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT CASE WHEN p = 1 THEN arr1[1] WHEN p = 2 THEN arr1[2] WHEN p = 3 THEN arr2[1] ELSE arr2[2] END, ARRAY_APPEND(arr1, arr1[1]) FROM a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2)); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2)); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2)); + assertTrue(rs.next()); + assertEquals(3, rs.getInt(1)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{1, 2, 1}), rs.getArray(2)); + assertFalse(rs.next()); + } + + @Test + public void testServerArrayElementProjection13() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (1, ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (2, ARRAY[3, 2], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (3, ARRAY[3, 5], ARRAY[2, 3])"); + stmt.execute(); + stmt = conn.prepareStatement("UPSERT INTO a VALUES (4, ARRAY[3, 5], ARRAY[6, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT CASE WHEN arr1[1] = 1 THEN 1 WHEN arr1[2] = 2 THEN 2 WHEN arr2[1] = 2 THEN 2 ELSE arr2[2] END FROM a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(3, rs.getInt(1)); + assertFalse(rs.next()); + } + + @Test + public void testServerArrayElementProjection14() throws SQLException { + long ts = nextTimestamp(); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 10)); + Connection conn = DriverManager.getConnection(getUrl(), props); + String ddl = "CREATE TABLE a (p INTEGER ARRAY PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"; + conn.createStatement().execute(ddl); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 30)); + conn = DriverManager.getConnection(getUrl(), props); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO a VALUES (ARRAY[5, 6], ARRAY[1, 2], ARRAY[2, 3])"); + stmt.execute(); + conn.commit(); + conn.close(); + + props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, Long.toString(ts + 40)); + conn = DriverManager.getConnection(getUrl(), props); + ResultSet rs; + stmt = conn.prepareStatement("SELECT ARRAY_ELEM(ARRAY_PREPEND(arr2[1], ARRAY_CAT(arr1, ARRAY[arr2[2],3])), 1), arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from a"); + rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals(2, rs.getInt(1)); + assertEquals(1, rs.getInt(2)); + assertEquals(1, rs.getInt(3)); + assertEquals(conn.createArrayOf("INTEGER", new Integer[]{5, 6}), rs.getArray(4)); + assertEquals(3, rs.getInt(5)); + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java index 915a55d..ad02961 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,12 +43,14 @@ import org.apache.phoenix.expression.CoerceExpression; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.expression.KeyValueColumnExpression; import org.apache.phoenix.expression.LiteralExpression; +import org.apache.phoenix.expression.ProjectedColumnExpression; import org.apache.phoenix.expression.aggregator.ClientAggregators; import org.apache.phoenix.expression.aggregator.ServerAggregators; import org.apache.phoenix.expression.function.ArrayIndexFunction; import org.apache.phoenix.expression.function.SingleAggregateFunction; import org.apache.phoenix.expression.visitor.ExpressionVisitor; -import org.apache.phoenix.expression.visitor.KeyValueExpressionVisitor; +import org.apache.phoenix.expression.visitor.ProjectedColumnExpressionVisitor; +import org.apache.phoenix.expression.visitor.ReplaceArrayFunctionExpressionVisitor; import org.apache.phoenix.expression.visitor.SingleAggregateFunctionVisitor; import org.apache.phoenix.jdbc.PhoenixConnection; import org.apache.phoenix.parse.AliasedNode; @@ -104,8 +107,6 @@ import com.google.common.collect.Sets; * @since 0.1 */ public class ProjectionCompiler { - private static ValueBitSet arrayIndexesBitSet; - private static KeyValueSchema arrayIndexesSchema; private ProjectionCompiler() { } @@ -339,10 +340,13 @@ public class ProjectionCompiler { */ public static RowProjector compile(StatementContext context, SelectStatement statement, GroupBy groupBy, List<? extends PDatum> targetColumns) throws SQLException { List<KeyValueColumnExpression> arrayKVRefs = new ArrayList<KeyValueColumnExpression>(); + List<ProjectedColumnExpression> arrayProjectedColumnRefs = new ArrayList<ProjectedColumnExpression>(); List<Expression> arrayKVFuncs = new ArrayList<Expression>(); + List<Expression> arrayOldFuncs = new ArrayList<Expression>(); + Map<Expression, Integer> arrayExpressionCounts = new HashMap<>(); List<AliasedNode> aliasedNodes = statement.getSelect(); // Setup projected columns in Scan - SelectClauseVisitor selectVisitor = new SelectClauseVisitor(context, groupBy, arrayKVRefs, arrayKVFuncs, statement); + SelectClauseVisitor selectVisitor = new SelectClauseVisitor(context, groupBy, arrayKVRefs, arrayKVFuncs, arrayExpressionCounts, arrayProjectedColumnRefs, arrayOldFuncs, statement); List<ExpressionProjector> projectedColumns = new ArrayList<ExpressionProjector>(); ColumnResolver resolver = context.getResolver(); TableRef tableRef = context.getCurrentTable(); @@ -409,24 +413,48 @@ public class ProjectionCompiler { String name = columnAlias == null ? expression.toString() : columnAlias; projectedColumns.add(new ExpressionProjector(name, tableRef.getTableAlias() == null ? (table.getName() == null ? "" : table.getName().getString()) : tableRef.getTableAlias(), expression, isCaseSensitive)); } - if(arrayKVFuncs.size() > 0 && arrayKVRefs.size() > 0) { - serailizeArrayIndexInformationAndSetInScan(context, arrayKVFuncs, arrayKVRefs); - KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(0); - for (Expression expression : arrayKVRefs) { - builder.addField(expression); - } - KeyValueSchema kvSchema = builder.build(); - arrayIndexesBitSet = ValueBitSet.newInstance(kvSchema); - builder = new KeyValueSchemaBuilder(0); - for (Expression expression : arrayKVFuncs) { - builder.addField(expression); - } - arrayIndexesSchema = builder.build(); - } + selectVisitor.reset(); index++; } + for (int i = arrayProjectedColumnRefs.size() - 1; i >= 0; i--) { + Expression expression = arrayProjectedColumnRefs.get(i); + Integer count = arrayExpressionCounts.get(expression); + if (count != 0) { + arrayKVRefs.remove(i); + arrayKVFuncs.remove(i); + arrayOldFuncs.remove(i); + } + } + + if (arrayKVFuncs.size() > 0 && arrayKVRefs.size() > 0) { + serailizeArrayIndexInformationAndSetInScan(context, arrayKVFuncs, arrayKVRefs); + KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(0); + for (Expression expression : arrayKVRefs) { + builder.addField(expression); + } + KeyValueSchema kvSchema = builder.build(); + ValueBitSet arrayIndexesBitSet = ValueBitSet.newInstance(kvSchema); + builder = new KeyValueSchemaBuilder(0); + for (Expression expression : arrayKVFuncs) { + builder.addField(expression); + } + KeyValueSchema arrayIndexesSchema = builder.build(); + + Map<Expression, Expression> replacementMap = new HashMap<>(); + for(int i = 0; i < arrayOldFuncs.size(); i++){ + Expression function =arrayKVFuncs.get(i); + replacementMap.put(arrayOldFuncs.get(i), new ArrayIndexExpression(i, function.getDataType(), arrayIndexesBitSet, arrayIndexesSchema)); + } + + ReplaceArrayFunctionExpressionVisitor visitor = new ReplaceArrayFunctionExpressionVisitor(replacementMap); + for (int i = 0; i < projectedColumns.size(); i++) { + ExpressionProjector projector = projectedColumns.get(i); + projectedColumns.set(i, new ExpressionProjector(projector.getName(), tableRef.getTableAlias() == null ? (table.getName() == null ? "" : table.getName().getString()) : tableRef.getTableAlias(), projector.getExpression().accept(visitor), projector.isCaseSensitive())); + } + } + // TODO make estimatedByteSize more accurate by counting the joined columns. int estimatedKeySize = table.getRowKeySchema().getEstimatedValueLength(); int estimatedByteSize = 0; @@ -473,17 +501,21 @@ public class ProjectionCompiler { static class ArrayIndexExpression extends BaseTerminalExpression { private final int position; private final PDataType type; + private final ValueBitSet arrayIndexesBitSet; + private final KeyValueSchema arrayIndexesSchema; - public ArrayIndexExpression(int position, PDataType type) { + public ArrayIndexExpression(int position, PDataType type, ValueBitSet arrayIndexesBitSet, KeyValueSchema arrayIndexesSchema) { this.position = position; this.type = type; + this.arrayIndexesBitSet = arrayIndexesBitSet; + this.arrayIndexesSchema = arrayIndexesSchema; } @Override public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (!tuple.getValue(QueryConstants.ARRAY_VALUE_COLUMN_FAMILY, QueryConstants.ARRAY_VALUE_COLUMN_QUALIFIER, ptr)) { - return false; + return false; } int maxOffset = ptr.getOffset() + ptr.getLength(); arrayIndexesBitSet.or(ptr); @@ -556,13 +588,19 @@ public class ProjectionCompiler { private int elementCount; private List<KeyValueColumnExpression> arrayKVRefs; private List<Expression> arrayKVFuncs; + private List<Expression> arrayOldFuncs; + private List<ProjectedColumnExpression> arrayProjectedColumnRefs; + private Map<Expression, Integer> arrayExpressionCounts; private SelectStatement statement; private SelectClauseVisitor(StatementContext context, GroupBy groupBy, - List<KeyValueColumnExpression> arrayKVRefs, List<Expression> arrayKVFuncs, SelectStatement statement) { + List<KeyValueColumnExpression> arrayKVRefs, List<Expression> arrayKVFuncs, Map<Expression, Integer> arrayExpressionCounts, List<ProjectedColumnExpression> arrayProjectedColumnRefs, List<Expression> arrayOldFuncs, SelectStatement statement) { super(context, groupBy); this.arrayKVRefs = arrayKVRefs; this.arrayKVFuncs = arrayKVFuncs; + this.arrayOldFuncs = arrayOldFuncs; + this.arrayExpressionCounts = arrayExpressionCounts; + this.arrayProjectedColumnRefs = arrayProjectedColumnRefs; this.statement = statement; reset(); } @@ -614,6 +652,16 @@ public class ProjectionCompiler { isCaseSensitive = isCaseSensitive && node.isCaseSensitive(); return ref; } + + @Override + public Expression visit(ColumnParseNode node) throws SQLException { + Expression expression = super.visit(node); + if (expression.getDataType().isArrayType()) { + Integer count = arrayExpressionCounts.get(expression); + arrayExpressionCounts.put(expression, count != null ? (count + 1) : 1); + } + return expression; + } @Override public void addElement(List<Expression> l, Expression element) { @@ -633,37 +681,43 @@ public class ProjectionCompiler { } @Override - public Expression visitLeave(FunctionParseNode node, List<Expression> children) throws SQLException { - Expression func = super.visitLeave(node,children); + public Expression visitLeave(FunctionParseNode node, final List<Expression> children) throws SQLException { + // this need not be done for group by clause with array. Hence the below check - if (!statement.isAggregate() && ArrayIndexFunction.NAME.equals(node.getName())) { + if (!statement.isAggregate() && ArrayIndexFunction.NAME.equals(node.getName()) && children.get(0) instanceof ProjectedColumnExpression) { final List<KeyValueColumnExpression> indexKVs = Lists.newArrayList(); + final List<ProjectedColumnExpression> indexProjectedColumns = Lists.newArrayList(); + final List<Expression> copyOfChildren = new ArrayList<>(children); // Create anon visitor to find reference to array in a generic way - children.get(0).accept(new KeyValueExpressionVisitor() { + children.get(0).accept(new ProjectedColumnExpressionVisitor() { @Override - public Void visit(KeyValueColumnExpression expression) { + public Void visit(ProjectedColumnExpression expression) { if (expression.getDataType().isArrayType()) { - indexKVs.add(expression); + indexProjectedColumns.add(expression); + KeyValueColumnExpression keyValueColumnExpression = new KeyValueColumnExpression(expression.getColumn()); + indexKVs.add(keyValueColumnExpression); + copyOfChildren.set(0, keyValueColumnExpression); + Integer count = arrayExpressionCounts.get(expression); + arrayExpressionCounts.put(expression, count != null ? (count - 1) : -1); } return null; } }); + + Expression func = super.visitLeave(node,children); // Add the keyvalues which is of type array if (!indexKVs.isEmpty()) { arrayKVRefs.addAll(indexKVs); - // Track the array index function also - arrayKVFuncs.add(func); - // Store the index of the array index function in the select query list - func = replaceArrayIndexFunction(func, arrayKVFuncs.size() - 1); - return func; + arrayProjectedColumnRefs.addAll(indexProjectedColumns); + Expression funcModified = super.visitLeave(node, copyOfChildren); + // Track the array index function also + arrayKVFuncs.add(funcModified); + arrayOldFuncs.add(func); } + return func; + } else { + return super.visitLeave(node,children); } - return func; - } - - public Expression replaceArrayIndexFunction(Expression func, int size) { - return new ArrayIndexExpression(size, func.getDataType()); } } - } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/compile/RowProjector.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/RowProjector.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/RowProjector.java index c60933e..99ab5d4 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/RowProjector.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/RowProjector.java @@ -24,7 +24,7 @@ import java.util.List; import org.apache.phoenix.expression.Determinism; import org.apache.phoenix.expression.Expression; -import org.apache.phoenix.expression.visitor.CloneExpressionVisitor; +import org.apache.phoenix.expression.visitor.CloneNonDeterministicExpressionVisitor; import org.apache.phoenix.schema.AmbiguousColumnException; import org.apache.phoenix.schema.ColumnNotFoundException; import org.apache.phoenix.util.SchemaUtil; @@ -118,7 +118,7 @@ public class RowProjector { ColumnProjector colProjector = columnProjectors.get(i); Expression expression = colProjector.getExpression(); if (expression.getDeterminism() == Determinism.PER_INVOCATION) { - CloneExpressionVisitor visitor = new CloneExpressionVisitor(); + CloneNonDeterministicExpressionVisitor visitor = new CloneNonDeterministicExpressionVisitor(); Expression clonedExpression = expression.accept(visitor); clonedColProjectors.add(new ExpressionProjector(colProjector.getName(), colProjector.getTableName(), http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java index f5468f5..8e94c78 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java @@ -302,11 +302,13 @@ abstract public class BaseScannerRegionObserver extends BaseRegionObserver { public boolean nextRaw(List<Cell> result) throws IOException { try { boolean next = s.nextRaw(result); + Cell arrayElementCell = null; if (result.size() == 0) { return next; } if (arrayFuncRefs != null && arrayFuncRefs.length > 0 && arrayKVRefs.size() > 0) { - replaceArrayIndexElement(arrayKVRefs, arrayFuncRefs, result); + int arrayElementCellPosition = replaceArrayIndexElement(arrayKVRefs, arrayFuncRefs, result); + arrayElementCell = result.get(arrayElementCellPosition); } if (ScanUtil.isLocalIndex(scan) && !ScanUtil.isAnalyzeTable(scan)) { IndexUtil.wrapResultUsingOffset(c, result, offset, dataColumns, @@ -316,6 +318,8 @@ abstract public class BaseScannerRegionObserver extends BaseRegionObserver { Tuple tuple = projector.projectResults(new ResultTuple(Result.create(result))); result.clear(); result.add(tuple.getValue(0)); + if(arrayElementCell != null) + result.add(arrayElementCell); } // There is a scanattribute set to retrieve the specific array element return next; @@ -329,11 +333,13 @@ abstract public class BaseScannerRegionObserver extends BaseRegionObserver { public boolean nextRaw(List<Cell> result, int limit) throws IOException { try { boolean next = s.nextRaw(result, limit); + Cell arrayElementCell = null; if (result.size() == 0) { return next; } if (arrayFuncRefs != null && arrayFuncRefs.length > 0 && arrayKVRefs.size() > 0) { - replaceArrayIndexElement(arrayKVRefs, arrayFuncRefs, result); + int arrayElementCellPosition = replaceArrayIndexElement(arrayKVRefs, arrayFuncRefs, result); + arrayElementCell = result.get(arrayElementCellPosition); } if ((offset > 0 || ScanUtil.isLocalIndex(scan)) && !ScanUtil.isAnalyzeTable(scan)) { IndexUtil.wrapResultUsingOffset(c, result, offset, dataColumns, @@ -343,6 +349,8 @@ abstract public class BaseScannerRegionObserver extends BaseRegionObserver { Tuple tuple = projector.projectResults(new ResultTuple(Result.create(result))); result.clear(); result.add(tuple.getValue(0)); + if(arrayElementCell != null) + result.add(arrayElementCell); } // There is a scanattribute set to retrieve the specific array element return next; @@ -352,7 +360,7 @@ abstract public class BaseScannerRegionObserver extends BaseRegionObserver { } } - private void replaceArrayIndexElement(final Set<KeyValueColumnExpression> arrayKVRefs, + private int replaceArrayIndexElement(final Set<KeyValueColumnExpression> arrayKVRefs, final Expression[] arrayFuncRefs, List<Cell> result) { // make a copy of the results array here, as we're modifying it below MultiKeyValueTuple tuple = new MultiKeyValueTuple(ImmutableList.copyOf(result)); @@ -383,6 +391,7 @@ abstract public class BaseScannerRegionObserver extends BaseRegionObserver { QueryConstants.ARRAY_VALUE_COLUMN_QUALIFIER, 0, QueryConstants.ARRAY_VALUE_COLUMN_QUALIFIER.length, HConstants.LATEST_TIMESTAMP, Type.codeToType(rowKv.getTypeByte()), value, 0, value.length)); + return result.size() - 1; } @Override http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/expression/ProjectedColumnExpression.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ProjectedColumnExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ProjectedColumnExpression.java index 97d1aff..89619bf 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ProjectedColumnExpression.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ProjectedColumnExpression.java @@ -40,6 +40,7 @@ public class ProjectedColumnExpression extends ColumnExpression { private int position; private String displayName; private final Collection<PColumn> columns; + private PColumn column; public ProjectedColumnExpression() { this.columns = Collections.emptyList(); @@ -48,9 +49,10 @@ public class ProjectedColumnExpression extends ColumnExpression { public ProjectedColumnExpression(PColumn column, PTable table, String displayName) { this(column, table.getColumns(), column.getPosition() - table.getPKColumns().size(), displayName); } - + public ProjectedColumnExpression(PColumn column, Collection<PColumn> columns, int position, String displayName) { super(column); + this.column = column; this.columns = columns; this.position = position; this.displayName = displayName; @@ -143,4 +145,7 @@ public class ProjectedColumnExpression extends ColumnExpression { return visitor.visit(this); } + public PColumn getColumn() { + return column; + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java index 55f227f..18b8795 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java @@ -26,7 +26,6 @@ import org.apache.phoenix.expression.ArrayConstructorExpression; import org.apache.phoenix.expression.CaseExpression; import org.apache.phoenix.expression.CoerceExpression; import org.apache.phoenix.expression.ComparisonExpression; -import org.apache.phoenix.expression.Determinism; import org.apache.phoenix.expression.DivideExpression; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.expression.InListExpression; @@ -49,7 +48,7 @@ import org.apache.phoenix.expression.function.ScalarFunction; import org.apache.phoenix.expression.function.SingleAggregateFunction; import org.apache.phoenix.expression.function.UDFExpression; -public class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Expression> { +public abstract class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Expression> { public CloneExpressionVisitor() { } @@ -88,17 +87,17 @@ public class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Express @Override public Expression visitLeave(AndExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new AndExpression(l); + return isCloneNode(node, l) ? new AndExpression(l) : node; } @Override public Expression visitLeave(OrExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new OrExpression(l); + return isCloneNode(node, l) ? new OrExpression(l) : node; } @Override public Expression visitLeave(ScalarFunction node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } public Expression visitLeave(UDFExpression node, List<Expression> l) { @@ -108,95 +107,95 @@ public class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Express @Override public Expression visitLeave(ComparisonExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(LikeExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node - .clone(l); + return isCloneNode(node, l) ? node.clone(l): node; } @Override public Expression visitLeave(SingleAggregateFunction node, List<Expression> l) { // Do not clone aggregate functions, as they're executed on the server side, // so any state for evaluation will live there. - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node; + return isCloneNode(node, l) ? node : node; } @Override public Expression visitLeave(CaseExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new CaseExpression(l); + return isCloneNode(node, l) ? new CaseExpression(l) : node; } @Override public Expression visitLeave(NotExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new NotExpression(l); + return isCloneNode(node, l) ? new NotExpression(l) : node; } @Override public Expression visitLeave(InListExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(IsNullExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(SubtractExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(MultiplyExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(AddExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(DivideExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(ModulusExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(CoerceExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(ArrayConstructorExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(StringConcatExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new StringConcatExpression(l); + return isCloneNode(node, l) ? new StringConcatExpression(l) : node; } @Override public Expression visitLeave(RowValueConstructorExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node.clone(l); + return isCloneNode(node, l) ? node.clone(l) : node; } @Override public Expression visitLeave(ArrayAnyComparisonExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new ArrayAnyComparisonExpression(l); + return isCloneNode(node, l) ? new ArrayAnyComparisonExpression(l) : node; } @Override public Expression visitLeave(ArrayElemRefExpression node, List<Expression> l) { - return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : new ArrayElemRefExpression(l); + return isCloneNode(node, l) ? new ArrayElemRefExpression(l) : node; } + public abstract boolean isCloneNode(Expression node, List<Expression> children); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneNonDeterministicExpressionVisitor.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneNonDeterministicExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneNonDeterministicExpressionVisitor.java new file mode 100644 index 0000000..1aeb9a9 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneNonDeterministicExpressionVisitor.java @@ -0,0 +1,30 @@ +/* + * 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.expression.visitor; + +import java.util.List; + +import org.apache.phoenix.expression.Determinism; +import org.apache.phoenix.expression.Expression; + +public class CloneNonDeterministicExpressionVisitor extends CloneExpressionVisitor { + + public boolean isCloneNode(Expression node, List<Expression> children) { + return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) <= 0; + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ProjectedColumnExpressionVisitor.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ProjectedColumnExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ProjectedColumnExpressionVisitor.java new file mode 100644 index 0000000..2380c6b --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ProjectedColumnExpressionVisitor.java @@ -0,0 +1,25 @@ +/* + * 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.expression.visitor; + +import org.apache.phoenix.expression.ProjectedColumnExpression; + +public abstract class ProjectedColumnExpressionVisitor extends StatelessTraverseAllExpressionVisitor<Void> { + @Override + abstract public Void visit(ProjectedColumnExpression node); +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ReplaceArrayFunctionExpressionVisitor.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ReplaceArrayFunctionExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ReplaceArrayFunctionExpressionVisitor.java new file mode 100644 index 0000000..2a460a4 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/ReplaceArrayFunctionExpressionVisitor.java @@ -0,0 +1,46 @@ +/* + * 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.expression.visitor; + +import java.util.List; +import java.util.Map; + +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.expression.function.ScalarFunction; + +public class ReplaceArrayFunctionExpressionVisitor extends CloneExpressionVisitor { + private Map<Expression, Expression> replacementMap; + + public ReplaceArrayFunctionExpressionVisitor(Map<Expression, Expression> replacementMap) { + this.replacementMap = replacementMap; + } + + @Override + public boolean isCloneNode(Expression node, List<Expression> children) { + return !children.equals(node.getChildren()); + } + + @Override + public Expression visitLeave(ScalarFunction node, List<Expression> l) { + Expression replacement = replacementMap.get(node); + if (replacement != null) { + return replacement; + } + return super.visitLeave(node, l); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java b/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java index 7b47543..264500b 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/iterate/ExplainTable.java @@ -152,6 +152,9 @@ public abstract class ExplainTable { groupByLimit = (Integer) PInteger.INSTANCE.toObject(groupByLimitBytes); } groupBy.explain(planSteps, groupByLimit); + if (scan.getAttribute(BaseScannerRegionObserver.SPECIFIC_ARRAY_INDEX) != null) { + planSteps.add(" SERVER ARRAY ELEMENT PROJECTION"); + } } private void appendPKColumnValue(StringBuilder buf, byte[] range, Boolean isNull, int slotIndex) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java index 98b130e..6225c6b 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/QueryCompilerTest.java @@ -1989,4 +1989,81 @@ public class QueryCompilerTest extends BaseConnectionlessQueryTest { } } + @Test + public void testServerArrayElementProjection1() throws SQLException { + Connection conn = DriverManager.getConnection(getUrl()); + try { + conn.createStatement().execute("CREATE TABLE t(a INTEGER PRIMARY KEY, arr INTEGER ARRAY)"); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT arr[1] from t"); + assertTrue(QueryUtil.getExplainPlan(rs).contains(" SERVER ARRAY ELEMENT PROJECTION")); + } finally { + conn.createStatement().execute("DROP TABLE IF EXISTS t"); + conn.close(); + } + } + + @Test + public void testServerArrayElementProjection2() throws SQLException { + Connection conn = DriverManager.getConnection(getUrl()); + try { + conn.createStatement().execute("CREATE TABLE t(a INTEGER PRIMARY KEY, arr INTEGER ARRAY)"); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT arr, arr[1] from t"); + assertFalse(QueryUtil.getExplainPlan(rs).contains(" SERVER ARRAY ELEMENT PROJECTION")); + } finally { + conn.createStatement().execute("DROP TABLE IF EXISTS t"); + conn.close(); + } + } + + @Test + public void testServerArrayElementProjection3() throws SQLException { + Connection conn = DriverManager.getConnection(getUrl()); + try { + conn.createStatement().execute("CREATE TABLE t(a INTEGER PRIMARY KEY, arr INTEGER ARRAY, arr2 VARCHAR ARRAY)"); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT arr, arr[1], arr2[1] from t"); + assertTrue(QueryUtil.getExplainPlan(rs).contains(" SERVER ARRAY ELEMENT PROJECTION")); + } finally { + conn.createStatement().execute("DROP TABLE IF EXISTS t"); + conn.close(); + } + } + + @Test + public void testServerArrayElementProjection4() throws SQLException { + Connection conn = DriverManager.getConnection(getUrl()); + try { + conn.createStatement().execute("CREATE TABLE t (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT arr1, arr1[1], ARRAY_APPEND(ARRAY_APPEND(arr1, arr2[2]), arr2[1]), p from t"); + assertTrue(QueryUtil.getExplainPlan(rs).contains(" SERVER ARRAY ELEMENT PROJECTION")); + } finally { + conn.createStatement().execute("DROP TABLE IF EXISTS t"); + conn.close(); + } + } + + @Test + public void testServerArrayElementProjection5() throws SQLException { + Connection conn = DriverManager.getConnection(getUrl()); + try { + conn.createStatement().execute("CREATE TABLE t (p INTEGER PRIMARY KEY, arr1 INTEGER ARRAY, arr2 INTEGER ARRAY)"); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT arr1, arr1[1], ARRAY_ELEM(ARRAY_APPEND(arr1, arr2[1]), 1), p, arr2[2] from t"); + assertTrue(QueryUtil.getExplainPlan(rs).contains(" SERVER ARRAY ELEMENT PROJECTION")); + } finally { + conn.createStatement().execute("DROP TABLE IF EXISTS t"); + conn.close(); + } + } + + @Test + public void testServerArrayElementProjectionWithArrayPrimaryKey() throws SQLException { + Connection conn = DriverManager.getConnection(getUrl()); + try { + conn.createStatement().execute("CREATE TABLE t(arr INTEGER ARRAY PRIMARY KEY)"); + ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT arr[1] from t"); + assertFalse(QueryUtil.getExplainPlan(rs).contains(" SERVER ARRAY ELEMENT PROJECTION")); + } finally { + conn.createStatement().execute("DROP TABLE IF EXISTS t"); + conn.close(); + } + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ab69325/phoenix-core/src/test/java/org/apache/phoenix/expression/ArithmeticOperationTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArithmeticOperationTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArithmeticOperationTest.java index 1b830f2..7876fce 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArithmeticOperationTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArithmeticOperationTest.java @@ -30,7 +30,7 @@ import java.util.List; import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.phoenix.exception.DataExceedsCapacityException; import org.apache.phoenix.expression.function.RandomFunction; -import org.apache.phoenix.expression.visitor.CloneExpressionVisitor; +import org.apache.phoenix.expression.visitor.CloneNonDeterministicExpressionVisitor; import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PDecimal; import org.apache.phoenix.schema.types.PInteger; @@ -273,7 +273,7 @@ public class ArithmeticOperationTest { e2 = new DoubleSubtractExpression(children); e3 = new DoubleAddExpression(Arrays.<Expression>asList(e1, e2)); e4 = new DoubleAddExpression(Arrays.<Expression>asList(new RandomFunction(Arrays.<Expression>asList(LiteralExpression.newConstant(null))), e3)); - CloneExpressionVisitor visitor = new CloneExpressionVisitor(); + CloneNonDeterministicExpressionVisitor visitor = new CloneNonDeterministicExpressionVisitor(); Expression clone = e4.accept(visitor); assertTrue(clone != e4); e4.evaluate(null, ptr1); @@ -281,7 +281,7 @@ public class ArithmeticOperationTest { assertNotEquals(ptr1, ptr2); e4 = new DoubleAddExpression(Arrays.<Expression>asList(new RandomFunction(Arrays.<Expression>asList(LiteralExpression.newConstant(1))), e3)); - visitor = new CloneExpressionVisitor(); + visitor = new CloneNonDeterministicExpressionVisitor(); clone = e4.accept(visitor); assertTrue(clone == e4); e4.evaluate(null, ptr1); @@ -294,7 +294,7 @@ public class ArithmeticOperationTest { boolean evaluated = e.evaluate(null, ptr); assertTrue(evaluated); assertEquals(value, type.toObject(ptr.get())); - CloneExpressionVisitor visitor = new CloneExpressionVisitor(); + CloneNonDeterministicExpressionVisitor visitor = new CloneNonDeterministicExpressionVisitor(); Expression clone = e.accept(visitor); evaluated = clone.evaluate(null, ptr); assertTrue(evaluated);