PHOENIX-1875 implement ARRAY_PREPEND built in function (Dumindu)
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/b5ef25c9 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/b5ef25c9 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/b5ef25c9 Branch: refs/heads/json Commit: b5ef25c942fb0f4ab9a6fec66e821c5c3473ea46 Parents: 978b232 Author: ramkrishna <ramkrishna.s.vasude...@gmail.com> Authored: Wed May 13 10:46:19 2015 +0530 Committer: ramkrishna <ramkrishna.s.vasude...@gmail.com> Committed: Wed May 13 10:46:19 2015 +0530 ---------------------------------------------------------------------- .../phoenix/end2end/ArrayPrependFunctionIT.java | 652 +++++++++++++++++++ .../phoenix/expression/ExpressionType.java | 4 +- .../function/ArrayAppendFunction.java | 35 +- .../function/ArrayModifierFunction.java | 75 +++ .../function/ArrayPrependFunction.java | 96 +++ .../phoenix/schema/types/PArrayDataType.java | 161 ++++- .../expression/ArrayPrependFunctionTest.java | 552 ++++++++++++++++ 7 files changed, 1541 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayPrependFunctionIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayPrependFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayPrependFunctionIT.java new file mode 100644 index 0000000..3145d95 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayPrependFunctionIT.java @@ -0,0 +1,652 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.phoenix.end2end; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.sql.*; + +import org.apache.phoenix.schema.TypeMismatchException; +import org.apache.phoenix.schema.types.PhoenixArray; +import org.junit.Test; + +public class ArrayPrependFunctionIT extends BaseHBaseManagedTimeIT { + + private void initTableWithVarArray(Connection conn, String type, Object[] objectArray, String value) throws SQLException { + conn.createStatement().execute("CREATE TABLE t ( k VARCHAR PRIMARY KEY, a " + type + "[],b " + type + ")"); + conn.commit(); + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO t VALUES(?,?," + value + ")"); + PhoenixArray array = (PhoenixArray) conn.createArrayOf(type, objectArray); + stmt.setString(1, "a"); + stmt.setArray(2, array); + stmt.execute(); + conn.commit(); + + } + + private void initTables(Connection conn) throws Exception { + String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchars VARCHAR[],integers INTEGER[],doubles DOUBLE[],bigints BIGINT[],chars CHAR(15)[],double1 DOUBLE,char1 CHAR(17),nullcheck INTEGER,chars2 CHAR(15)[])"; + conn.createStatement().execute(ddl); + String dml = "UPSERT INTO regions(region_name,varchars,integers,doubles,bigints,chars,double1,char1,nullcheck,chars2) VALUES('SF Bay Area'," + + "ARRAY['2345','46345','23234']," + + "ARRAY[2345,46345,23234,456]," + + "ARRAY[23.45,46.345,23.234,45.6,5.78]," + + "ARRAY[12,34,56,78,910]," + + "ARRAY['a','bbbb','c','ddd','e']," + + "23.45," + + "'wert'," + + "NULL," + + "ARRAY['foo','a','bbbb','c','ddd','e']" + + ")"; + PreparedStatement stmt = conn.prepareStatement(dml); + stmt.execute(); + conn.commit(); + } + + private void initTablesDesc(Connection conn, String type, String val) throws Exception { + String ddl = "CREATE TABLE regions (pk " + type + " PRIMARY KEY DESC,varchars VARCHAR[],integers INTEGER[],doubles DOUBLE[],bigints BIGINT[],chars CHAR(15)[],chars2 CHAR(15)[], bools BOOLEAN[])"; + conn.createStatement().execute(ddl); + String dml = "UPSERT INTO regions(pk,varchars,integers,doubles,bigints,chars,chars2,bools) VALUES(" + val + "," + + "ARRAY['2345','46345','23234']," + + "ARRAY[2345,46345,23234,456]," + + "ARRAY[23.45,46.345,23.234,45.6,5.78]," + + "ARRAY[12,34,56,78,910]," + + "ARRAY['a','bbbb','c','ddd','e']," + + "ARRAY['a','bbbb','c','ddd','e','foo']," + + "ARRAY[true,false]" + + ")"; + PreparedStatement stmt = conn.prepareStatement(dml); + stmt.execute(); + conn.commit(); + } + + @Test + public void testArrayPrependFunctionInteger() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(1234,integers) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Integer[] integers = new Integer[]{1234, 2345, 46345, 23234, 456}; + + Array array = conn.createArrayOf("INTEGER", integers); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionVarchar() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND('34567',varchars) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + String[] strings = new String[]{"34567", "2345", "46345", "23234"}; + + Array array = conn.createArrayOf("VARCHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionNulls1() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + String[] s = new String[]{null, null, "1", "2"}; + initTableWithVarArray(conn, "VARCHAR", s, null); + String[] s2 = new String[]{null, null, null, "1", "2"}; + PhoenixArray array2 = (PhoenixArray) conn.createArrayOf("VARCHAR", s2); + conn = DriverManager.getConnection(getUrl()); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(b,a) FROM t WHERE k = 'a'"); + assertTrue(rs.next()); + assertEquals(array2, rs.getArray(1)); + } + + @Test + public void testArrayPrependFunctionNulls2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + String[] s = new String[]{"1", "2"}; + initTableWithVarArray(conn, "VARCHAR", s, null); + String[] s2 = new String[]{null, "1", "2"}; + PhoenixArray array2 = (PhoenixArray) conn.createArrayOf("VARCHAR", s2); + conn = DriverManager.getConnection(getUrl()); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(b,a) FROM t WHERE k = 'a'"); + assertTrue(rs.next()); + assertEquals(array2, rs.getArray(1)); + } + + @Test + public void testArrayPrependFunctionNulls3() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + String[] s = new String[]{"176", null, "212"}; + initTableWithVarArray(conn, "VARCHAR", s, null); + String[] s2 = new String[]{null, "176", null, "212"}; + PhoenixArray array2 = (PhoenixArray) conn.createArrayOf("VARCHAR", s2); + conn = DriverManager.getConnection(getUrl()); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(b,a) FROM t WHERE k = 'a'"); + assertTrue(rs.next()); + assertEquals(array2, rs.getArray(1)); + } + + @Test + public void testArrayPrependFunctionNulls4() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + String[] s = new String[]{"176", null, "212"}; + initTableWithVarArray(conn, "VARCHAR", s, "'foo'"); + String[] s2 = new String[]{"foo", "176", null, "212"}; + PhoenixArray array2 = (PhoenixArray) conn.createArrayOf("VARCHAR", s2); + conn = DriverManager.getConnection(getUrl()); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(b,a) FROM t WHERE k = 'a'"); + assertTrue(rs.next()); + assertEquals(array2, rs.getArray(1)); + } + + @Test + public void testArrayPrependFunctionDouble() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(double1,doubles) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Double[] doubles = new Double[]{23.45, 23.45, 46.345, 23.234, 45.6, 5.78}; + + Array array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionDouble2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(23,doubles) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Double[] doubles = new Double[]{new Double(23), 23.45, 46.345, 23.234, 45.6, 5.78}; + + Array array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionBigint() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(1112,bigints) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Long[] longs = new Long[]{1112l, 12l, 34l, 56l, 78l, 910l}; + + Array array = conn.createArrayOf("BIGINT", longs); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionChar() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND('fac',chars) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + String[] strings = new String[]{"fac", "a", "bbbb", "c", "ddd", "e"}; + + Array array = conn.createArrayOf("CHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test(expected = TypeMismatchException.class) + public void testArrayPrependFunctionIntToCharArray() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(234,varchars) FROM regions WHERE region_name = 'SF Bay Area'"); + } + + @Test(expected = TypeMismatchException.class) + public void testArrayPrependFunctionVarcharToIntegerArray() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND('234',integers) FROM regions WHERE region_name = 'SF Bay Area'"); + + } + + @Test(expected = SQLException.class) + public void testArrayPrependFunctionChar2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND('facfacfacfacfacfacfac',chars) FROM regions WHERE region_name = 'SF Bay Area'"); + rs.next(); + rs.getArray(1); + } + + @Test + public void testArrayPrependFunctionIntegerToDoubleArray() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(45,doubles) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Double[] doubles = new Double[]{45.0, 23.45, 46.345, 23.234, 45.6, 5.78}; + + Array array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithNestedFunctions1() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(integers[1],ARRAY[23,45]) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Integer[] integers = new Integer[]{2345, 23, 45}; + + Array array = conn.createArrayOf("INTEGER", integers); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithNestedFunctions2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(ARRAY_ELEM(ARRAY[2,4],1),integers) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Integer[] integers = new Integer[]{2, 2345, 46345, 23234, 456}; + + Array array = conn.createArrayOf("INTEGER", integers); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithNestedFunctions3() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(ARRAY_ELEM(doubles,2),doubles) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Double[] doubles = new Double[]{46.345, 23.45, 46.345, 23.234, 45.6, 5.78}; + + Array array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithUpsert1() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + + String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,varchars VARCHAR[])"; + conn.createStatement().execute(ddl); + + String dml = "UPSERT INTO regions(region_name,varchars) VALUES('SF Bay Area',ARRAY_PREPEND(':-)',ARRAY['hello','world']))"; + conn.createStatement().execute(dml); + conn.commit(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT varchars FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + String[] strings = new String[]{":-)", "hello", "world"}; + + Array array = conn.createArrayOf("VARCHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithUpsert2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + + String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,integers INTEGER[])"; + conn.createStatement().execute(ddl); + + String dml = "UPSERT INTO regions(region_name,integers) VALUES('SF Bay Area',ARRAY_PREPEND(6,ARRAY[4,5]))"; + conn.createStatement().execute(dml); + conn.commit(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT integers FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Integer[] integers = new Integer[]{6, 4, 5}; + + Array array = conn.createArrayOf("INTEGER", integers); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithUpsert3() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + + String ddl = "CREATE TABLE regions (region_name VARCHAR PRIMARY KEY,doubles DOUBLE[])"; + conn.createStatement().execute(ddl); + + String dml = "UPSERT INTO regions(region_name,doubles) VALUES('SF Bay Area',ARRAY_PREPEND(9.0,ARRAY[5.67,7.87]))"; + conn.createStatement().execute(dml); + conn.commit(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT doubles FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + Double[] doubles = new Double[]{new Double(9), 5.67, 7.87}; + + Array array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithUpsertSelect1() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + + String ddl = "CREATE TABLE source (region_name VARCHAR PRIMARY KEY,doubles DOUBLE[])"; + conn.createStatement().execute(ddl); + + ddl = "CREATE TABLE target (region_name VARCHAR PRIMARY KEY,doubles DOUBLE[])"; + conn.createStatement().execute(ddl); + + String dml = "UPSERT INTO source(region_name,doubles) VALUES('SF Bay Area',ARRAY_PREPEND(9.0,ARRAY[5.67,7.87]))"; + conn.createStatement().execute(dml); + + dml = "UPSERT INTO source(region_name,doubles) VALUES('SF Bay Area2',ARRAY_PREPEND(9.2,ARRAY[56.7,7.87]))"; + conn.createStatement().execute(dml); + conn.commit(); + + dml = "UPSERT INTO target(region_name, doubles) SELECT region_name, ARRAY_PREPEND(5,doubles) FROM source"; + conn.createStatement().execute(dml); + conn.commit(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT doubles FROM target"); + assertTrue(rs.next()); + + Double[] doubles = new Double[]{new Double(5), new Double(9), 5.67, 7.87}; + Array array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertTrue(rs.next()); + + doubles = new Double[]{new Double(5), new Double(9.2), 56.7, 7.87}; + array = conn.createArrayOf("DOUBLE", doubles); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionWithUpsertSelect2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + + String ddl = "CREATE TABLE source (region_name VARCHAR PRIMARY KEY,varchars VARCHAR[])"; + conn.createStatement().execute(ddl); + + ddl = "CREATE TABLE target (region_name VARCHAR PRIMARY KEY,varchars VARCHAR[])"; + conn.createStatement().execute(ddl); + + String dml = "UPSERT INTO source(region_name,varchars) VALUES('SF Bay Area',ARRAY_PREPEND('c',ARRAY['abcd','b']))"; + conn.createStatement().execute(dml); + + dml = "UPSERT INTO source(region_name,varchars) VALUES('SF Bay Area2',ARRAY_PREPEND('something',ARRAY['d','fgh']))"; + conn.createStatement().execute(dml); + conn.commit(); + + dml = "UPSERT INTO target(region_name, varchars) SELECT region_name, ARRAY_PREPEND('stu',varchars) FROM source"; + conn.createStatement().execute(dml); + conn.commit(); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT varchars FROM target"); + assertTrue(rs.next()); + + String[] strings = new String[]{"stu", "c", "abcd", "b"}; + Array array = conn.createArrayOf("VARCHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertTrue(rs.next()); + + strings = new String[]{"stu", "something", "d", "fgh"}; + array = conn.createArrayOf("VARCHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere1() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY[123,2345,46345,23234,456]=ARRAY_PREPEND(123,integers)"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere2() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE varchars[1]=ANY(ARRAY_PREPEND('1234',ARRAY['2345','46345','23234']))"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere3() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY['1234','2345','46345','23234']=ARRAY_PREPEND('1234',ARRAY['2345','46345','23234'])"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere4() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY[123.4,23.45,4634.5,2.3234]=ARRAY_PREPEND(123.4,ARRAY[23.45,4634.5,2.3234])"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere5() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY['foo','2345','46345','23234']=ARRAY_PREPEND('foo',varchars)"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere6() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE chars2=ARRAY_PREPEND('foo',chars)"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionInWhere7() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY[4,2,3]=ARRAY_PREPEND(4,ARRAY[2,3])"); + assertTrue(rs.next()); + + assertEquals("SF Bay Area", rs.getString(1)); + assertFalse(rs.next()); + } + + @Test(expected = SQLException.class) + public void testArrayPrependFunctionCharLimitCheck() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTables(conn); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(char1,chars) FROM regions WHERE region_name = 'SF Bay Area'"); + assertTrue(rs.next()); + + String[] strings = new String[]{"wert", "a", "bbbb", "c", "ddd", "e"}; + + Array array = conn.createArrayOf("CHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionIntegerDesc() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTablesDesc(conn, "INTEGER", "23"); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(pk,integers) FROM regions"); + assertTrue(rs.next()); + + Integer[] integers = new Integer[]{23, 2345, 46345, 23234, 456}; + + Array array = conn.createArrayOf("INTEGER", integers); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + + } + + @Test + public void testArrayPrependFunctionVarcharDesc() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTablesDesc(conn, "VARCHAR", "'e'"); + + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(pk,varchars) FROM regions"); + assertTrue(rs.next()); + + String[] strings = new String[]{"e", "2345", "46345", "23234"}; + + Array array = conn.createArrayOf("VARCHAR", strings); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionBigIntDesc() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTablesDesc(conn, "BIGINT", "1112"); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(pk,bigints) FROM regions"); + assertTrue(rs.next()); + + Long[] longs = new Long[]{1112l, 12l, 34l, 56l, 78l, 910l}; + + Array array = conn.createArrayOf("BIGINT", longs); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } + + @Test + public void testArrayPrependFunctionBooleanDesc() throws Exception { + Connection conn = DriverManager.getConnection(getUrl()); + initTablesDesc(conn, "BOOLEAN", "false"); + ResultSet rs; + rs = conn.createStatement().executeQuery("SELECT ARRAY_PREPEND(pk,bools) FROM regions"); + assertTrue(rs.next()); + + Boolean[] booleans = new Boolean[]{false, true, false}; + + Array array = conn.createArrayOf("BOOLEAN", booleans); + + assertEquals(array, rs.getArray(1)); + assertFalse(rs.next()); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java index 71f0521..d7142e7 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java @@ -25,6 +25,7 @@ import org.apache.phoenix.expression.function.ArrayAppendFunction; import org.apache.phoenix.expression.function.ArrayElemRefExpression; import org.apache.phoenix.expression.function.ArrayIndexFunction; import org.apache.phoenix.expression.function.ArrayLengthFunction; +import org.apache.phoenix.expression.function.ArrayPrependFunction; import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction; import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction; import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction; @@ -229,7 +230,8 @@ public enum ExpressionType { MinuteFunction(MinuteFunction.class), DayOfMonthFunction(DayOfMonthFunction.class), ArrayAppendFunction(ArrayAppendFunction.class), - UDFExpression(UDFExpression.class) + UDFExpression(UDFExpression.class), + ArrayPrependFunction(ArrayPrependFunction.class) ; ExpressionType(Class<? extends Expression> clazz) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayAppendFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayAppendFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayAppendFunction.java index db92d61..bf6c29f 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayAppendFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayAppendFunction.java @@ -33,7 +33,7 @@ import org.apache.phoenix.schema.tuple.Tuple; @FunctionParseNode.Argument(allowedTypes = {PBinaryArray.class, PVarbinaryArray.class}), @FunctionParseNode.Argument(allowedTypes = {PVarbinary.class}, defaultValue = "null")}) -public class ArrayAppendFunction extends ScalarFunction { +public class ArrayAppendFunction extends ArrayModifierFunction { public static final String NAME = "ARRAY_APPEND"; @@ -42,21 +42,6 @@ public class ArrayAppendFunction extends ScalarFunction { public ArrayAppendFunction(List<Expression> children) throws TypeMismatchException { super(children); - - if (getDataType() != null && !(getElementExpr() instanceof LiteralExpression && getElementExpr().isNullable()) && !getElementDataType().isCoercibleTo(getBaseType())) { - throw TypeMismatchException.newException(getBaseType(), getElementDataType()); - } - - // If the base type of an element is fixed width, make sure the element being appended will fit - if (getDataType() != null && getElementExpr().getDataType().getByteSize() == null && getElementDataType() != null && getBaseType().isFixedWidth() && getElementDataType().isFixedWidth() && getArrayExpr().getMaxLength() != null && - getElementExpr().getMaxLength() != null && getElementExpr().getMaxLength() > getArrayExpr().getMaxLength()) { - throw new DataExceedsCapacityException(""); - } - // If the base type has a scale, make sure the element being appended has a scale less than or equal to it - if (getDataType() != null && getArrayExpr().getScale() != null && getElementExpr().getScale() != null && - getElementExpr().getScale() > getArrayExpr().getScale()) { - throw new DataExceedsCapacityException(getBaseType(), getArrayExpr().getMaxLength(), getArrayExpr().getScale()); - } } @Override @@ -78,12 +63,8 @@ public class ArrayAppendFunction extends ScalarFunction { return true; } - if (!getBaseType().isSizeCompatible(ptr, null, getElementDataType(), getElementExpr().getMaxLength(), getElementExpr().getScale(), getArrayExpr().getMaxLength(), getArrayExpr().getScale())) { - throw new DataExceedsCapacityException(""); - } - - getBaseType().coerceBytes(ptr, null, getElementDataType(), getElementExpr().getMaxLength(), getElementExpr().getScale(), getElementExpr().getSortOrder(), getArrayExpr().getMaxLength(), getArrayExpr().getScale(), getArrayExpr().getSortOrder()); - + checkSizeCompatibility(ptr); + coerceBytes(ptr); return PArrayDataType.appendItemToArray(ptr, length, offset, arrayBytes, getBaseType(), arrayLength, getMaxLength(), getArrayExpr().getSortOrder()); } @@ -114,14 +95,4 @@ public class ArrayAppendFunction extends ScalarFunction { public Expression getElementExpr() { return getChildren().get(1); } - - public PDataType getBaseType() { - return PDataType.arrayBaseType(getArrayExpr().getDataType()); - } - - public PDataType getElementDataType() { - return getElementExpr().getDataType(); - } - - } http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java new file mode 100644 index 0000000..afd10e5 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayModifierFunction.java @@ -0,0 +1,75 @@ +/* + * 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.function; + +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.exception.DataExceedsCapacityException; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.expression.LiteralExpression; +import org.apache.phoenix.schema.TypeMismatchException; +import org.apache.phoenix.schema.types.*; + +public abstract class ArrayModifierFunction extends ScalarFunction { + + public ArrayModifierFunction() { + } + + public ArrayModifierFunction(List<Expression> children) throws TypeMismatchException { + super(children); + + if (getDataType() != null && !(getElementExpr() instanceof LiteralExpression && getElementExpr().isNullable()) && !getElementDataType().isCoercibleTo(getBaseType())) { + throw TypeMismatchException.newException(getBaseType(), getElementDataType()); + } + + // If the base type of an element is fixed width, make sure the element being appended will fit + if (getDataType() != null && getElementExpr().getDataType().getByteSize() == null && getElementDataType() != null && getBaseType().isFixedWidth() && getElementDataType().isFixedWidth() && getArrayExpr().getMaxLength() != null && + getElementExpr().getMaxLength() != null && getElementExpr().getMaxLength() > getArrayExpr().getMaxLength()) { + throw new DataExceedsCapacityException(""); + } + // If the base type has a scale, make sure the element being appended has a scale less than or equal to it + if (getDataType() != null && getArrayExpr().getScale() != null && getElementExpr().getScale() != null && + getElementExpr().getScale() > getArrayExpr().getScale()) { + throw new DataExceedsCapacityException(getBaseType(), getArrayExpr().getMaxLength(), getArrayExpr().getScale()); + } + } + + protected void checkSizeCompatibility(ImmutableBytesWritable ptr) { + if (!getBaseType().isSizeCompatible(ptr, null, getElementDataType(), getElementExpr().getMaxLength(), getElementExpr().getScale(), getArrayExpr().getMaxLength(), getArrayExpr().getScale())) { + throw new DataExceedsCapacityException(""); + } + } + + protected void coerceBytes(ImmutableBytesWritable ptr) { + getBaseType().coerceBytes(ptr, null, getElementDataType(), getElementExpr().getMaxLength(), getElementExpr().getScale(), getElementExpr().getSortOrder(), getArrayExpr().getMaxLength(), getArrayExpr().getScale(), getArrayExpr().getSortOrder()); + } + + public abstract Expression getArrayExpr(); + + public abstract Expression getElementExpr(); + + public PDataType getBaseType() { + return PDataType.arrayBaseType(getArrayExpr().getDataType()); + } + + public PDataType getElementDataType() { + return getElementExpr().getDataType(); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayPrependFunction.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayPrependFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayPrependFunction.java new file mode 100644 index 0000000..3cea4df --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayPrependFunction.java @@ -0,0 +1,96 @@ +/* + * 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.function; + +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode; +import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.TypeMismatchException; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; + +@FunctionParseNode.BuiltInFunction(name = ArrayPrependFunction.NAME, args = { + @FunctionParseNode.Argument(allowedTypes = {PVarbinary.class}), + @FunctionParseNode.Argument(allowedTypes = {PBinaryArray.class, + PVarbinaryArray.class})}) +public class ArrayPrependFunction extends ArrayModifierFunction { + + public static final String NAME = "ARRAY_PREPEND"; + + public ArrayPrependFunction() { + } + + public ArrayPrependFunction(List<Expression> children) throws TypeMismatchException { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + if (!getArrayExpr().evaluate(tuple, ptr)) { + return false; + } else if (ptr.getLength() == 0) { + return true; + } + int arrayLength = PArrayDataType.getArrayLength(ptr, getBaseType(), getArrayExpr().getMaxLength()); + + int length = ptr.getLength(); + int offset = ptr.getOffset(); + byte[] arrayBytes = ptr.get(); + + getElementExpr().evaluate(tuple, ptr); + + checkSizeCompatibility(ptr); + coerceBytes(ptr); + return PArrayDataType.prependItemToArray(ptr, length, offset, arrayBytes, getBaseType(), arrayLength, getMaxLength(), getArrayExpr().getSortOrder()); + } + + @Override + public PDataType getDataType() { + return children.get(1).getDataType(); + } + + @Override + public Integer getMaxLength() { + return this.children.get(1).getMaxLength(); + } + + @Override + public SortOrder getSortOrder() { + return getChildren().get(1).getSortOrder(); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public Expression getArrayExpr() { + return getChildren().get(1); + } + + @Override + public Expression getElementExpr() { + return getChildren().get(0); + } +} http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java index c6861f7..86f22f7 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/types/PArrayDataType.java @@ -21,6 +21,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.text.Format; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -512,7 +513,7 @@ public abstract class PArrayDataType<T> extends PDataType<T> { } else { int off = newOffsetArrayPosition; for (int arrayIndex = 0; arrayIndex < Math.abs(arrayLength) - 1; arrayIndex++) { - Bytes.putInt(newArray, off, getOffset(arrayBytes, arrayIndex, true, offsetArrayPosition)); + Bytes.putInt(newArray, off, getOffset(arrayBytes, arrayIndex, true, offsetArrayPosition + offset)); off += Bytes.SIZEOF_INT; } @@ -543,6 +544,164 @@ public abstract class PArrayDataType<T> extends PDataType<T> { Bytes.putByte(array, newOffsetArrayPosition + offsetArrayLength + byteSize + 2 * Bytes.SIZEOF_INT, header); } + public static boolean prependItemToArray(ImmutableBytesWritable ptr, int length, int offset, byte[] arrayBytes, PDataType baseType, int arrayLength, Integer maxLength, SortOrder sortOrder) { + int elementLength = maxLength == null ? ptr.getLength() : maxLength; + if (ptr.getLength() == 0) { + elementLength = 0; + } + //padding + if (elementLength > ptr.getLength()) { + baseType.pad(ptr, elementLength, sortOrder); + } + int elementOffset = ptr.getOffset(); + byte[] elementBytes = ptr.get(); + + byte[] newArray; + if (!baseType.isFixedWidth()) { + int offsetArrayPosition = Bytes.toInt(arrayBytes, offset + length - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE, Bytes.SIZEOF_INT); + int offsetArrayLength = length - offsetArrayPosition - Bytes.SIZEOF_INT - Bytes.SIZEOF_INT - Bytes.SIZEOF_BYTE; + arrayLength = Math.abs(arrayLength); + + //checks whether offset array consists of shorts or integers + boolean useInt = offsetArrayLength / arrayLength == Bytes.SIZEOF_INT; + boolean convertToInt = false; + int endElementPosition = getOffset(arrayBytes, arrayLength - 1, !useInt, offsetArrayPosition + offset) + elementLength + Bytes.SIZEOF_BYTE; + int newOffsetArrayPosition; + int lengthIncrease; + int firstNonNullElementPosition = 0; + int currentPosition = 0; + //handle the case where prepended element is null + if (elementLength == 0) { + int nulls = 1; + //counts the number of nulls which are already at the beginning of the array + for (int index = 0; index < arrayLength; index++) { + int currOffset = getOffset(arrayBytes, index, !useInt, offsetArrayPosition + offset); + if (arrayBytes[offset + currOffset] == QueryConstants.SEPARATOR_BYTE) { + nulls++; + } else { + //gets the offset of the first element after nulls at the beginning + firstNonNullElementPosition = currOffset; + break; + } + } + + int nMultiplesOver255 = nulls / 255; + int nRemainingNulls = nulls % 255; + + //Calculates the increase in length due to prepending the null + //There is a length increase only when nRemainingNulls == 1 + //nRemainingNulls == 1 and nMultiplesOver255 == 0 means there were no nulls at the beginning previously. + //At that case we need to increase the length by two bytes, one for separator byte and one for null count. + //ex: initial array - 65 0 66 0 0 0 after prepending null - 0 1(inverted) 65 0 66 0 0 0 + //nRemainingNulls == 1 and nMultiplesOver255 != 0 means there were null at the beginning previously. + //In this case due to prepending nMultiplesOver255 is increased by 1. + //We need to increase the length by one byte to store increased that. + //ex: initial array - 0 1 65 0 66 0 0 0 after prepending null - 0 1 1(inverted) 65 0 66 0 0 0 + //nRemainingNulls == 0 case. + //ex: initial array - 0 254(inverted) 65 0 66 0 0 0 after prepending null - 0 1 65 0 66 0 0 0 + //nRemainingNulls > 1 case. + //ex: initial array - 0 45(inverted) 65 0 66 0 0 0 after prepending null - 0 46(inverted) 65 0 66 0 0 0 + lengthIncrease = nRemainingNulls == 1 ? (nMultiplesOver255 == 0 ? 2 * Bytes.SIZEOF_BYTE : Bytes.SIZEOF_BYTE) : 0; + endElementPosition = getOffset(arrayBytes, arrayLength - 1, !useInt, offsetArrayPosition + offset) + lengthIncrease; + if (!useInt) { + if (PArrayDataType.useShortForOffsetArray(endElementPosition)) { + newArray = new byte[length + Bytes.SIZEOF_SHORT + lengthIncrease]; + } else { + newArray = new byte[length + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT + lengthIncrease]; + convertToInt = true; + } + } else { + newArray = new byte[length + Bytes.SIZEOF_INT + lengthIncrease]; + } + newArray[currentPosition] = QueryConstants.SEPARATOR_BYTE; + currentPosition++; + + newOffsetArrayPosition = offsetArrayPosition + lengthIncrease; + while (nMultiplesOver255-- > 0) { + newArray[currentPosition] = (byte) 1; + currentPosition++; + } + // Write a byte for the remaining null elements + if (nRemainingNulls > 0) { + byte nNullByte = SortOrder.invert((byte) (nRemainingNulls - 1)); + newArray[currentPosition] = nNullByte; // Single byte for repeating nulls + currentPosition++; + } + } else { + if (!useInt) { + if (PArrayDataType.useShortForOffsetArray(endElementPosition)) { + newArray = new byte[length + elementLength + Bytes.SIZEOF_SHORT + Bytes.SIZEOF_BYTE]; + } else { + newArray = new byte[length + elementLength + arrayLength * Bytes.SIZEOF_SHORT + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE]; + convertToInt = true; + } + } else { + newArray = new byte[length + elementLength + Bytes.SIZEOF_INT + Bytes.SIZEOF_BYTE]; + } + newOffsetArrayPosition = offsetArrayPosition + Bytes.SIZEOF_BYTE + elementLength; + + lengthIncrease = elementLength + Bytes.SIZEOF_BYTE; + System.arraycopy(elementBytes, elementOffset, newArray, 0, elementLength); + currentPosition += elementLength + Bytes.SIZEOF_BYTE; + } + + System.arraycopy(arrayBytes, firstNonNullElementPosition + offset, newArray, currentPosition, offsetArrayPosition); + + arrayLength = arrayLength + 1; + //writes the new offset and changes the previous offsets + if (useInt || convertToInt) { + writeNewOffsets(arrayBytes, newArray, false, !useInt, newOffsetArrayPosition, arrayLength, offsetArrayPosition, offset, lengthIncrease, length); + } else { + writeNewOffsets(arrayBytes, newArray, true, true, newOffsetArrayPosition, arrayLength, offsetArrayPosition, offset, lengthIncrease, length); + } + } else { + newArray = new byte[length + elementLength]; + + System.arraycopy(elementBytes, elementOffset, newArray, 0, elementLength); + System.arraycopy(arrayBytes, offset, newArray, elementLength, length); + } + + ptr.set(newArray); + return true; + } + + private static void writeNewOffsets(byte[] arrayBytes, byte[] newArray, boolean useShortNew, boolean useShortPrevious, int newOffsetArrayPosition, int arrayLength, int offsetArrayPosition, int offset, int offsetShift, int length) { + int currentPosition = newOffsetArrayPosition; + int offsetArrayElementSize = useShortNew ? Bytes.SIZEOF_SHORT : Bytes.SIZEOF_INT; + if (useShortNew) { + Bytes.putShort(newArray, currentPosition, (short) (0 - Short.MAX_VALUE)); + } else { + Bytes.putInt(newArray, currentPosition, 0); + } + + currentPosition += offsetArrayElementSize; + boolean nullsAtBeginning = true; + for (int arrayIndex = 0; arrayIndex < arrayLength - 1; arrayIndex++) { + int oldOffset = getOffset(arrayBytes, arrayIndex, useShortPrevious, offsetArrayPosition + offset); + if (arrayBytes[offset + oldOffset] == QueryConstants.SEPARATOR_BYTE && nullsAtBeginning) { + if (useShortNew) { + Bytes.putShort(newArray, currentPosition, (short) (oldOffset - Short.MAX_VALUE)); + } else { + Bytes.putInt(newArray, currentPosition, oldOffset); + } + } else { + if (useShortNew) { + Bytes.putShort(newArray, currentPosition, (short) (oldOffset + offsetShift - Short.MAX_VALUE)); + } else { + Bytes.putInt(newArray, currentPosition, oldOffset + offsetShift); + } + nullsAtBeginning = false; + } + currentPosition += offsetArrayElementSize; + } + + Bytes.putInt(newArray, currentPosition, newOffsetArrayPosition); + currentPosition += Bytes.SIZEOF_INT; + Bytes.putInt(newArray, currentPosition, useShortNew ? arrayLength : -arrayLength); + currentPosition += Bytes.SIZEOF_INT; + Bytes.putByte(newArray, currentPosition, arrayBytes[offset + length - 1]); + } + public static int serailizeOffsetArrayIntoStream(DataOutputStream oStream, TrustedByteArrayOutputStream byteStream, int noOfElements, int maxOffset, int[] offsetPos) throws IOException { int offsetPosition = (byteStream.size()); http://git-wip-us.apache.org/repos/asf/phoenix/blob/b5ef25c9/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java new file mode 100644 index 0000000..4d2f960 --- /dev/null +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayPrependFunctionTest.java @@ -0,0 +1,552 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.sql.SQLException; +import java.util.Calendar; +import java.util.List; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.function.ArrayPrependFunction; +import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.types.*; +import org.junit.Test; + +import com.google.common.collect.Lists; + +public class ArrayPrependFunctionTest { + + private static void testExpression(LiteralExpression array, LiteralExpression element, PhoenixArray expected) + throws SQLException { + List<Expression> expressions = Lists.newArrayList((Expression) element); + expressions.add(array); + + Expression arrayPrependFunction = new ArrayPrependFunction(expressions); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + arrayPrependFunction.evaluate(null, ptr); + PhoenixArray result = (PhoenixArray) arrayPrependFunction.getDataType().toObject(ptr, expressions.get(1).getSortOrder(), array.getMaxLength(), array.getScale()); + assertEquals(result, expected); + } + + private static void test(PhoenixArray array, Object element, PDataType arrayDataType, Integer arrMaxLen, Integer arrScale, PDataType elementDataType, Integer elemMaxLen, Integer elemScale, PhoenixArray expected, SortOrder arraySortOrder, SortOrder elementSortOrder) throws SQLException { + LiteralExpression arrayLiteral, elementLiteral; + arrayLiteral = LiteralExpression.newConstant(array, arrayDataType, arrMaxLen, arrScale, arraySortOrder, Determinism.ALWAYS); + elementLiteral = LiteralExpression.newConstant(element, elementDataType, elemMaxLen, elemScale, elementSortOrder, Determinism.ALWAYS); + testExpression(arrayLiteral, elementLiteral, expected); + } + + @Test + public void testArrayPrependFunction1() throws Exception { + Object[] o = new Object[]{1, 2, -3, 4}; + Object[] o2 = new Object[]{5, 1, 2, -3, 4}; + Object element = 5; + PDataType baseType = PInteger.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction2() throws Exception { + Object[] o = new Object[]{"1", "2", "3", "4"}; + Object[] o2 = new Object[]{"56", "1", "2", "3", "4"}; + Object element = "56"; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction3() throws Exception { + //offset array short to int transition + Object[] o = new Object[Short.MAX_VALUE + 1]; + for (int i = 0; i < o.length; i++) { + o[i] = "a"; + } + Object[] o2 = new Object[Short.MAX_VALUE + 2]; + for (int i = 1; i < o2.length; i++) { + o2[i] = "a"; + } + Object element = "b"; + o2[0] = element; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction4() throws Exception { + //offset array int + Object[] o = new Object[Short.MAX_VALUE + 7]; + for (int i = 0; i < o.length; i++) { + o[i] = "a"; + } + Object[] o2 = new Object[Short.MAX_VALUE + 8]; + for (int i = 1; i < o2.length; i++) { + o2[i] = "a"; + } + Object element = "b"; + o2[0] = element; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunctionBoolean() throws Exception { + Boolean[] o = new Boolean[]{true, false, false, true}; + Boolean[] o2 = new Boolean[]{false, true, false, false, true}; + Boolean element = false; + PDataType baseType = PBoolean.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveBooleanPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveBooleanPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), + null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction6() throws Exception { + Object[] o = new Object[]{new Float(2.3), new Float(7.9), new Float(-9.6), new Float(2.3)}; + Object[] o2 = new Object[]{new Float(8.9), new Float(2.3), new Float(7.9), new Float(-9.6), new Float(2.3)}; + Object element = 8.9; + PDataType baseType = PFloat.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveFloatPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveFloatPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction7() throws Exception { + Object[] o = new Object[]{4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE}; + Object[] o2 = new Object[]{12.67, 4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE}; + Object element = 12.67; + PDataType baseType = PDouble.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveDoublePhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveDoublePhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction8() throws Exception { + Object[] o = new Object[]{123l, 677l, 98789l, -78989l, 66787l}; + Object[] o2 = new Object[]{543l, 123l, 677l, 98789l, -78989l, 66787l}; + Object element = 543l; + PDataType baseType = PLong.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveLongPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveLongPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction9() throws Exception { + Object[] o = new Object[]{(short) 34, (short) -23, (short) -89, (short) 999, (short) 34}; + Object[] o2 = new Object[]{(short) 7, (short) 34, (short) -23, (short) -89, (short) 999, (short) 34}; + Object element = (short) 7; + PDataType baseType = PSmallint.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveShortPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveShortPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction10() throws Exception { + Object[] o = new Object[]{(byte) 4, (byte) 8, (byte) 9}; + Object[] o2 = new Object[]{(byte) 6, (byte) 4, (byte) 8, (byte) 9}; + Object element = (byte) 6; + PDataType baseType = PTinyint.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveBytePhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveBytePhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction11() throws Exception { + Object[] o = new Object[]{BigDecimal.valueOf(2345), BigDecimal.valueOf(-23.45), BigDecimal.valueOf(785)}; + Object[] o2 = new Object[]{BigDecimal.valueOf(-19), BigDecimal.valueOf(2345), BigDecimal.valueOf(-23.45), BigDecimal.valueOf(785)}; + Object element = BigDecimal.valueOf(-19); + PDataType baseType = PDecimal.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction12() throws Exception { + Calendar calendar = Calendar.getInstance(); + java.util.Date currentDate = calendar.getTime(); + java.sql.Date date = new java.sql.Date(currentDate.getTime()); + + Object[] o = new Object[]{date, date, date}; + Object[] o2 = new Object[]{date, date, date, date}; + PDataType baseType = PDate.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, date, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction13() throws Exception { + Calendar calendar = Calendar.getInstance(); + java.util.Date currentDate = calendar.getTime(); + java.sql.Time time = new java.sql.Time(currentDate.getTime()); + + Object[] o = new Object[]{time, time, time}; + Object[] o2 = new Object[]{time, time, time, time}; + PDataType baseType = PTime.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, time, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction14() throws Exception { + Calendar calendar = Calendar.getInstance(); + java.util.Date currentDate = calendar.getTime(); + java.sql.Timestamp timestamp = new java.sql.Timestamp(currentDate.getTime()); + + Object[] o = new Object[]{timestamp, timestamp, timestamp}; + Object[] o2 = new Object[]{timestamp, timestamp, timestamp, timestamp}; + PDataType baseType = PTimestamp.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, timestamp, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction15() throws Exception { + Object[] o = new Object[]{1, 2, -3, 4}; + Object[] o2 = new Object[]{5, 1, 2, -3, 4}; + Object element = 5; + PDataType baseType = PInteger.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.DESC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction16() throws Exception { + Object[] o = new Object[]{1, 2, -3, 4}; + Object[] o2 = new Object[]{5, 1, 2, -3, 4}; + Object element = 5; + PDataType baseType = PInteger.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.DESC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction17() throws Exception { + Object[] o = new Object[]{1, 2, -3, 4}; + Object[] o2 = new Object[]{5, 1, 2, -3, 4}; + Object element = 5; + PDataType baseType = PInteger.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveIntPhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction18() throws Exception { + Object[] o = new Object[]{"1 ", "2 ", "3 ", "4 "}; + Object[] o2 = new Object[]{"5", "1", "2", "3", "4"}; + Object element = "5"; + PDataType baseType = PChar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction19() throws Exception { + Object[] o = new Object[]{"1 ", "2 ", "3 ", "4 "}; + Object[] o2 = new Object[]{"5", "1", "2", "3", "4"}; + Object element = "5"; + PDataType baseType = PChar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.DESC, SortOrder.ASC); + } + + @Test + public void testArrayPrependFunction20() throws Exception { + Object[] o = new Object[]{"1 ", "2 ", "3 ", "4 "}; + Object[] o2 = new Object[]{"5", "1", "2", "3", "4"}; + Object element = "5"; + PDataType baseType = PChar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.DESC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction21() throws Exception { + Object[] o = new Object[]{4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE}; + Object[] o2 = new Object[]{12.67, 4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE}; + Object element = 12.67; + PDataType baseType = PDouble.INSTANCE; + + PhoenixArray arr = new PhoenixArray.PrimitiveDoublePhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray.PrimitiveDoublePhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, null, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction22() throws Exception { + byte[][] o = new byte[][]{new byte[]{2, 0, 3}, new byte[]{42, 3}, new byte[]{5, 3}, new byte[]{6, 3}, new byte[]{2, 5}}; + byte[][] o2 = new byte[][]{new byte[]{5, 6}, new byte[]{2, 0, 3}, new byte[]{42, 3}, new byte[]{5, 3}, new byte[]{6, 3}, new byte[]{2, 5}}; + byte[] element = new byte[]{5, 6}; + PDataType baseType = PVarbinary.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction23() throws Exception { + byte[][] o = new byte[][]{new byte[]{2, 3}, new byte[]{42, 3}, new byte[]{5, 3}, new byte[]{6, 3}, new byte[]{2, 5}}; + byte[][] o2 = new byte[][]{new byte[]{5, 6}, new byte[]{2, 3}, new byte[]{42, 3}, new byte[]{5, 3}, new byte[]{6, 3}, new byte[]{2, 5}}; + byte[] element = new byte[]{5, 6}; + PDataType baseType = PBinary.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 2, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testArrayPrependFunction24() throws Exception { + byte[][] o = new byte[][]{new byte[]{2, 0}, new byte[]{13, 3}, new byte[]{5, 3}, new byte[]{6, 3}, new byte[]{2, 5}}; + byte[][] o2 = new byte[][]{new byte[]{5, 6}, new byte[]{2, 0}, new byte[]{13, 3}, new byte[]{5, 3}, new byte[]{6, 3}, new byte[]{2, 5}}; + byte[] element = new byte[]{5, 6}; + PDataType baseType = PBinary.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 3, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWithNoNullsAtBeginning() throws Exception { + Object[] o = new Object[]{"1 ", "2 ", "3 ", "4 "}; + Object[] o2 = new Object[]{"1", "2", "3", "4"}; + Object element = null; + PDataType baseType = PChar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsAllNulls() throws Exception { + Object element = null; + PDataType baseType = PChar.INSTANCE; + + PhoenixArray arr = null; + PhoenixArray expected = null; + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith268NullsAtBeginning() throws Exception { + //268 nulls at the beginning + Object[] o = new Object[270]; + for (int i = 0; i < o.length - 2; i++) + o[i] = null; + + o[o.length - 2] = "1"; + o[o.length - 1] = "2"; + + Object[] o2 = new Object[271]; + for (int i = 0; i < o2.length - 2; i++) + o2[i] = null; + + o2[o2.length - 2] = "1"; + o2[o2.length - 1] = "2"; + + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith241NullsAtBeginning() throws Exception { + //241 nulls at the beginning + Object[] o = new Object[243]; + for (int i = 0; i < o.length - 2; i++) + o[i] = null; + + o[o.length - 2] = "1"; + o[o.length - 1] = "2"; + + Object[] o2 = new Object[244]; + for (int i = 0; i < o2.length - 2; i++) + o2[i] = null; + + o2[o2.length - 2] = "1"; + o2[o2.length - 1] = "2"; + + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith254NullsAtBeginning() throws Exception { + //254 nulls at the beginning + Object[] o = new Object[256]; + for (int i = 0; i < o.length - 2; i++) + o[i] = null; + + o[o.length - 2] = "1"; + o[o.length - 1] = "2"; + + Object[] o2 = new Object[257]; + for (int i = 0; i < o2.length - 2; i++) + o2[i] = null; + + o2[o2.length - 2] = "1"; + o2[o2.length - 1] = "2"; + + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith510NullsAtBeginning() throws Exception { + //510 nulls at the beginning + Object[] o = new Object[512]; + for (int i = 0; i < o.length - 2; i++) + o[i] = null; + + o[o.length - 2] = "1"; + o[o.length - 1] = "2"; + + Object[] o2 = new Object[513]; + for (int i = 0; i < o2.length - 2; i++) + o2[i] = null; + + o2[o2.length - 2] = "1"; + o2[o2.length - 1] = "2"; + + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith509NullsAtBeginning() throws Exception { + //509 nulls at the beginning + Object[] o = new Object[511]; + for (int i = 0; i < o.length - 2; i++) + o[i] = null; + + o[o.length - 2] = "1"; + o[o.length - 1] = "2"; + + Object[] o2 = new Object[512]; + for (int i = 0; i < o2.length - 2; i++) + o2[i] = null; + + o2[o2.length - 2] = "1"; + o2[o2.length - 1] = "2"; + + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith1NullAtBeginning() throws Exception { + Object[] o = new Object[]{"1 ", "2 ", "3 ", "4 "}; + Object[] o2 = new Object[]{null, "1 ", "2 ", "3 ", "4 "}; + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWith2NullsAtBeginning() throws Exception { + Object[] o = new Object[]{null, "1 ", "2 ", "3 ", "4 "}; + Object[] o2 = new Object[]{null, null, "1 ", "2 ", "3 ", "4 "}; + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), 4, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } + + @Test + public void testForNullsWithNullsInMiddle() throws Exception { + Object[] o = new Object[]{"1 ", "2 ", null, "3 ", "4 "}; + Object[] o2 = new Object[]{null, "1 ", "2 ", null, "3 ", "4 "}; + Object element = null; + PDataType baseType = PVarchar.INSTANCE; + + PhoenixArray arr = new PhoenixArray(baseType, o); + PhoenixArray expected = new PhoenixArray(baseType, o2); + test(arr, element, PDataType.fromTypeId(baseType.getSqlType() + PDataType.ARRAY_TYPE_BASE), null, null, baseType, 1, null, expected, SortOrder.ASC, SortOrder.DESC); + } +}