PHOENIX-4643 Implement ARRAY_REMOVE built in function (Xavier Jodoin)

Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/cdb77d46
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/cdb77d46
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/cdb77d46

Branch: refs/heads/4.x-cdh5.11.2
Commit: cdb77d468bc5bc8bbba8eef11935ff79620a3106
Parents: 6c1d6c2
Author: James Taylor <jtay...@salesforce.com>
Authored: Fri Mar 23 12:00:45 2018 -0700
Committer: James Taylor <jtay...@salesforce.com>
Committed: Fri Mar 23 12:31:22 2018 -0700

----------------------------------------------------------------------
 .../phoenix/end2end/ArrayRemoveFunctionIT.java  | 383 +++++++++++++++++++
 .../phoenix/expression/ExpressionType.java      |  10 +-
 .../function/ArrayRemoveFunction.java           |  88 +++++
 .../expression/ArrayRemoveFunctionTest.java     | 285 ++++++++++++++
 4 files changed, 762 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/cdb77d46/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayRemoveFunctionIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayRemoveFunctionIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayRemoveFunctionIT.java
new file mode 100644
index 0000000..b5a468c
--- /dev/null
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayRemoveFunctionIT.java
@@ -0,0 +1,383 @@
+/*
+ * 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.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+
+import org.apache.phoenix.schema.TypeMismatchException;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ArrayRemoveFunctionIT extends ParallelStatsDisabledIT {
+
+       private Connection conn;
+       private String tableName;
+
+       @Before
+       public void setup() throws Exception {
+               conn = DriverManager.getConnection(getUrl());
+               tableName = initTables(conn);
+       }
+
+       private String initTables(Connection conn) throws Exception {
+               String tableName = generateUniqueName();
+               String ddl = "CREATE TABLE " + tableName
+                               + " (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)[], nullVarchar VARCHAR[], nullBigInt 
BIGINT[],double2 DOUBLE,integer1 INTEGER,oneItem VARCHAR[],char2 
char(15),varchar1 VARCHAR)";
+               conn.createStatement().execute(ddl);
+               String dml = "UPSERT INTO " + tableName
+                               + 
"(region_name,varchars,integers,doubles,bigints,chars,double1,char1,nullcheck,chars2,double2,integer1,oneItem,char2,varchar1)
 VALUES('SF Bay Area',"
+                               + "ARRAY['2345','46345','23234']," + 
"ARRAY[2345,46345,23234,456],"
+                               + "ARRAY[10.0,23.45,46.345,23.234,45.6,5.78]," 
+ "ARRAY[12,34,56,78,910],"
+                               + "ARRAY['a','bbbb','c','ddd','e','c']," + 
"23.45," + "'wert'," + "NULL,"
+                               + "ARRAY['a','bbbb','c','ddd','e','foo']," + 
"12," + "12," + "ARRAY['alone'],'2345','a')";
+               PreparedStatement stmt = conn.prepareStatement(dml);
+               stmt.execute();
+               conn.commit();
+               return tableName;
+       }
+
+       @Test
+       public void testEmptyArrayModification() throws Exception {
+               ResultSet rs = conn.createStatement()
+                               .executeQuery("SELECT 
ARRAY_REMOVE(nullVarChar,'34567') FROM " + tableName + " LIMIT 1");
+               assertTrue(rs.next());
+
+               assertNull(rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionVarchar() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,'23234') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"2345", "46345" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionInteger() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(integers,456) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("INTEGER", new Integer[] { 
2345, 46345, 23234 }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionDouble() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(doubles,double1) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("DOUBLE", new Double[] { 10.0, 
46.345, 23.234, 45.6, 5.78 }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionDoubleWithInt() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(doubles,10),doubles FROM " 
+ tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("DOUBLE", new Double[] { 23.45, 
46.345, 23.234, 45.6, 5.78 }), rs.getArray(1));
+               assertEquals(conn.createArrayOf("DOUBLE", new Double[] { 10.0, 
23.45, 46.345, 23.234, 45.6, 5.78 }),
+                               rs.getArray(2));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionBigint() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(bigints,56),bigints FROM " 
+ tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("BIGINT", new Long[] { 12l, 
34l, 78l, 910l }), rs.getArray(1));
+               assertEquals(conn.createArrayOf("BIGINT", new Long[] { 12l, 
34l, 56l, 78l, 910l }), rs.getArray(2));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionBigintWithInteger() throws Exception 
{
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(bigints,integer1) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("BIGINT", new Long[] { 34l, 
56l, 78l, 910l }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test(expected = TypeMismatchException.class)
+       public void testArrayRemoveFunctionBigintWithDouble() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(bigints,double2) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("BIGINT", new Long[] { 34l, 
56l, 78l, 910l }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionChar() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(chars,'ddd') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("CHAR", new String[] { "a", 
"bbbb", "c", "e", "c" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test(expected = TypeMismatchException.class)
+       public void testArrayRemoveFunctionIntToCharArray() throws Exception {
+               conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,234) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+       }
+
+       @Test(expected = TypeMismatchException.class)
+       public void testArrayRemoveFunctionVarcharToIntegerArray() throws 
Exception {
+               conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(integers,'234') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+       }
+
+       @Test
+       public void testArrayRemoveFunctionWithNestedFunctions1() throws 
Exception {
+               ResultSet rs = conn.createStatement().executeQuery("SELECT 
ARRAY_REMOVE(ARRAY[23,2345],integers[1]) FROM "
+                               + tableName + " WHERE region_name = 'SF Bay 
Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("INTEGER", new Integer[] { 23 
}), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionWithNestedFunctions2() throws 
Exception {
+               ResultSet rs = conn.createStatement()
+                               .executeQuery("SELECT 
ARRAY_REMOVE(integers,ARRAY_ELEM(ARRAY[2345,4],1)) FROM " + tableName
+                                               + " WHERE region_name = 'SF Bay 
Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("INTEGER", new Integer[] { 
46345, 23234, 456 }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionWithUpsert1() throws Exception {
+               String uniqueName = generateUniqueName();
+               String ddl = "CREATE TABLE " + uniqueName + " (region_name 
VARCHAR PRIMARY KEY,varchars VARCHAR[])";
+               conn.createStatement().execute(ddl);
+
+               String dml = "UPSERT INTO " + uniqueName
+                               + "(region_name,varchars) VALUES('SF Bay 
Area',ARRAY_REMOVE(ARRAY['hello','world'],'world'))";
+               conn.createStatement().execute(dml);
+               conn.commit();
+
+               ResultSet rs = conn.createStatement()
+                               .executeQuery("SELECT varchars FROM " + 
uniqueName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"hello" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionWithUpsert2() throws Exception {
+               String tableName = generateUniqueName();
+               String ddl = "CREATE TABLE " + tableName + " (region_name 
VARCHAR PRIMARY KEY,integers INTEGER[])";
+               conn.createStatement().execute(ddl);
+
+               String dml = "UPSERT INTO " + tableName
+                               + "(region_name,integers) VALUES('SF Bay 
Area',ARRAY_REMOVE(ARRAY[4,5],5))";
+               conn.createStatement().execute(dml);
+               conn.commit();
+
+               ResultSet rs = conn.createStatement()
+                               .executeQuery("SELECT integers FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("INTEGER", new Integer[] { 4 
}), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionWithUpsertSelect1() throws Exception 
{
+               String sourceTableName = generateUniqueName();
+               String targetTableName = generateUniqueName();
+
+               String ddl = "CREATE TABLE " + sourceTableName + " (region_name 
VARCHAR PRIMARY KEY,doubles DOUBLE[])";
+               conn.createStatement().execute(ddl);
+
+               ddl = "CREATE TABLE " + targetTableName + " (region_name 
VARCHAR PRIMARY KEY,doubles DOUBLE[])";
+               conn.createStatement().execute(ddl);
+
+               String dml = "UPSERT INTO " + sourceTableName
+                               + "(region_name,doubles) VALUES('SF Bay 
Area',ARRAY_APPEND(ARRAY[5.67,7.87],9))";
+               conn.createStatement().execute(dml);
+
+               dml = "UPSERT INTO " + sourceTableName
+                               + "(region_name,doubles) VALUES('SF Bay 
Area2',ARRAY_APPEND(ARRAY[56.7,7.87],9))";
+               conn.createStatement().execute(dml);
+               conn.commit();
+
+               dml = "UPSERT INTO " + targetTableName
+                               + "(region_name, doubles) SELECT region_name, 
ARRAY_REMOVE(doubles,9) FROM " + sourceTableName;
+               conn.createStatement().execute(dml);
+               conn.commit();
+
+               ResultSet rs = conn.createStatement().executeQuery("SELECT 
doubles FROM " + targetTableName);
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("DOUBLE", new Double[] { 5.67, 
7.87 }), rs.getArray(1));
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("DOUBLE", new Double[] { 56.7, 
7.87 }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionInWhere1() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT region_name FROM " + tableName + " 
WHERE ARRAY[2345,46345,23234]=ARRAY_REMOVE(integers,456)");
+               assertTrue(rs.next());
+
+               assertEquals("SF Bay Area", rs.getString(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionVarcharWithNull() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,NULL) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"2345", "46345", "23234" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionDoublesWithNull() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(doubles,NULL) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("DOUBLE", new Double[] { 10.0, 
23.45, 46.345, 23.234, 45.6, 5.78 }),
+                               rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionCharsWithNull() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(chars,NULL) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("CHAR", new String[] { "a", 
"bbbb", "c", "ddd", "e", "c" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionWithNull() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(integers,nullcheck) FROM " 
+ tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("INTEGER", new Integer[] { 
2345, 46345, 23234, 456 }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionFirstElement() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,'2345') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"46345", "23234" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionMiddleElement() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,'46345') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"2345", "23234" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionLastElement() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,'23234') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"2345", "46345" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionOneElement() throws Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(oneItem,'alone') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[0]), 
rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionRepeatingElements() throws Exception 
{
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(chars,'c') FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("CHAR", new String[] { "a", 
"bbbb", "ddd", "e" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionCharFromVarcharArray() throws 
Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(varchars,char2) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("VARCHAR", new String[] { 
"46345", "23234" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+       @Test
+       public void testArrayRemoveFunctionVarcharFromCharArray() throws 
Exception {
+               ResultSet rs = conn.createStatement().executeQuery(
+                               "SELECT ARRAY_REMOVE(chars,varchar1) FROM " + 
tableName + " WHERE region_name = 'SF Bay Area'");
+               assertTrue(rs.next());
+
+               assertEquals(conn.createArrayOf("CHAR", new String[] { "bbbb", 
"c", "ddd", "e", "c" }), rs.getArray(1));
+               assertFalse(rs.next());
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/cdb77d46/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 9a53eb1..c5065e0 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
@@ -29,6 +29,7 @@ import 
org.apache.phoenix.expression.function.ArrayFillFunction;
 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.ArrayRemoveFunction;
 import org.apache.phoenix.expression.function.ArrayToStringFunction;
 import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction;
 import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction;
@@ -46,9 +47,12 @@ import 
org.apache.phoenix.expression.function.CollationKeyFunction;
 import org.apache.phoenix.expression.function.ConvertTimezoneFunction;
 import org.apache.phoenix.expression.function.CountAggregateFunction;
 import org.apache.phoenix.expression.function.DayOfMonthFunction;
+import org.apache.phoenix.expression.function.DayOfWeekFunction;
+import org.apache.phoenix.expression.function.DayOfYearFunction;
 import org.apache.phoenix.expression.function.DecodeFunction;
 import org.apache.phoenix.expression.function.DefaultValueExpression;
 import org.apache.phoenix.expression.function.DistinctCountAggregateFunction;
+import 
org.apache.phoenix.expression.function.DistinctCountHyperLogLogAggregateFunction;
 import org.apache.phoenix.expression.function.DistinctValueAggregateFunction;
 import org.apache.phoenix.expression.function.EncodeFunction;
 import org.apache.phoenix.expression.function.ExpFunction;
@@ -129,9 +133,6 @@ import org.apache.phoenix.expression.function.UDFExpression;
 import org.apache.phoenix.expression.function.UpperFunction;
 import org.apache.phoenix.expression.function.WeekFunction;
 import org.apache.phoenix.expression.function.YearFunction;
-import org.apache.phoenix.expression.function.DayOfWeekFunction;
-import org.apache.phoenix.expression.function.DayOfYearFunction;
-import 
org.apache.phoenix.expression.function.DistinctCountHyperLogLogAggregateFunction;
 
 import com.google.common.collect.Maps;
 
@@ -296,7 +297,8 @@ public enum ExpressionType {
     FirstValuesFunction(FirstValuesFunction.class),
     LastValuesFunction(LastValuesFunction.class),
     
DistinctCountHyperLogLogAggregateFunction(DistinctCountHyperLogLogAggregateFunction.class),
-    CollationKeyFunction(CollationKeyFunction.class);
+    CollationKeyFunction(CollationKeyFunction.class),
+    ArrayRemoveFunction(ArrayRemoveFunction.class);
 
     ExpressionType(Class<? extends Expression> clazz) {
         this.clazz = clazz;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/cdb77d46/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayRemoveFunction.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayRemoveFunction.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayRemoveFunction.java
new file mode 100644
index 0000000..d71cc23
--- /dev/null
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayRemoveFunction.java
@@ -0,0 +1,88 @@
+/*
+ * 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.ArrayModifierParseNode;
+import org.apache.phoenix.parse.FunctionParseNode;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.TypeMismatchException;
+import org.apache.phoenix.schema.types.PArrayDataTypeDecoder;
+import org.apache.phoenix.schema.types.PArrayDataTypeEncoder;
+import org.apache.phoenix.schema.types.PBinaryArray;
+import org.apache.phoenix.schema.types.PChar;
+import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PVarbinary;
+import org.apache.phoenix.schema.types.PVarbinaryArray;
+import org.apache.phoenix.util.StringUtil;
+
+@FunctionParseNode.BuiltInFunction(name = ArrayRemoveFunction.NAME, nodeClass 
= ArrayModifierParseNode.class, args = {
+               @FunctionParseNode.Argument(allowedTypes = { 
PBinaryArray.class, PVarbinaryArray.class }),
+               @FunctionParseNode.Argument(allowedTypes = { PVarbinary.class 
}) })
+public class ArrayRemoveFunction extends ArrayModifierFunction {
+
+       public static final String NAME = "ARRAY_REMOVE";
+
+       public ArrayRemoveFunction() {
+       }
+
+       public ArrayRemoveFunction(List<Expression> children) throws 
TypeMismatchException {
+               super(children);
+       }
+
+       @Override
+       protected boolean modifierFunction(ImmutableBytesWritable ptr, int 
length, int offset, byte[] arrayBytes,
+                       PDataType baseType, int arrayLength, Integer maxLength, 
Expression arrayExp) {
+               SortOrder sortOrder = arrayExp.getSortOrder();
+
+               if (ptr.getLength() == 0 || arrayBytes.length == 0) {
+                       ptr.set(arrayBytes, offset, length);
+                       return true;
+               }
+
+               PArrayDataTypeEncoder arrayDataTypeEncoder = new 
PArrayDataTypeEncoder(baseType, sortOrder);
+
+               if (getRHSBaseType().equals(PChar.INSTANCE)) {
+                       int unpaddedCharLength = 
StringUtil.getUnpaddedCharLength(ptr.get(), ptr.getOffset(), ptr.getLength(),
+                                       sortOrder);
+                       ptr.set(ptr.get(), offset, unpaddedCharLength);
+               }
+
+               for (int arrayIndex = 0; arrayIndex < arrayLength; 
arrayIndex++) {
+                       ImmutableBytesWritable ptr2 = new 
ImmutableBytesWritable(arrayBytes, offset, length);
+                       PArrayDataTypeDecoder.positionAtArrayElement(ptr2, 
arrayIndex, baseType, maxLength);
+                       if (baseType.compareTo(ptr2, sortOrder, ptr, sortOrder, 
baseType) != 0) {
+                               arrayDataTypeEncoder.appendValue(ptr2.get(), 
ptr2.getOffset(), ptr2.getLength());
+                       }
+               }
+
+               ptr.set(arrayDataTypeEncoder.encode());
+
+               return true;
+       }
+
+       @Override
+       public String getName() {
+               return NAME;
+       }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/cdb77d46/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayRemoveFunctionTest.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayRemoveFunctionTest.java
 
b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayRemoveFunctionTest.java
new file mode 100644
index 0000000..eb45eac
--- /dev/null
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayRemoveFunctionTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.Date;
+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.ArrayRemoveFunction;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PBoolean;
+import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PDate;
+import org.apache.phoenix.schema.types.PDecimal;
+import org.apache.phoenix.schema.types.PDouble;
+import org.apache.phoenix.schema.types.PFloat;
+import org.apache.phoenix.schema.types.PInteger;
+import org.apache.phoenix.schema.types.PLong;
+import org.apache.phoenix.schema.types.PSmallint;
+import org.apache.phoenix.schema.types.PTime;
+import org.apache.phoenix.schema.types.PTimestamp;
+import org.apache.phoenix.schema.types.PTinyint;
+import org.apache.phoenix.schema.types.PVarbinary;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.schema.types.PhoenixArray;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class ArrayRemoveFunctionTest {
+
+       private static void testExpression(LiteralExpression array, 
LiteralExpression element, PhoenixArray expected)
+                       throws SQLException {
+               List<Expression> expressions = Lists.newArrayList((Expression) 
element);
+               expressions.add(array);
+
+               Expression arrayRemoveFunction = new 
ArrayRemoveFunction(expressions);
+               ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+               arrayRemoveFunction.evaluate(null, ptr);
+               PhoenixArray result = (PhoenixArray) 
arrayRemoveFunction.getDataType().toObject(ptr,
+                               expressions.get(1).getSortOrder(), 
array.getMaxLength(), array.getScale());
+               assertEquals(expected, result);
+       }
+
+       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 testArrayRemoveFunction1() throws Exception {
+               Object[] o = new Object[] { 1, 2, -3, 4 };
+               Object[] o2 = new Object[] { 1, 2, -3 };
+               Object element = 4;
+               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 testArrayRemoveFunction2() throws Exception {
+               Object[] o = new Object[] { "1", "2", "3", "4" };
+               Object[] o2 = new Object[] { "1", "3", "4" };
+               Object element = "2";
+               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 testArrayRemoveFunction3() throws Exception {
+               Object[] o = new Object[] { "1", "2", "2", "4" };
+               Object[] o2 = new Object[] { "1", "4" };
+               Object element = "2";
+               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 testArrayRemoveFunction4() throws Exception {
+               Object[] o = new Object[] { "1", "2", "2", "4" };
+               Object[] o2 = new Object[] { "1", "2", "2", "4" };
+               Object element = "5";
+               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 testArrayRemoveFunctionBoolean() throws Exception {
+               Boolean[] o = new Boolean[] { true, false, false, true };
+               Boolean[] o2 = new Boolean[] { true, 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 testArrayRemoveFunction6() 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(7.9), new Float(-9.6) };
+               Object element = 2.3;
+               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 testArrayRemoveFunction7() throws Exception {
+               Object[] o = new Object[] { 4.78, 9.54, 2.34, -9.675, 
Double.MAX_VALUE };
+               Object[] o2 = new Object[] { 9.54, 2.34, -9.675, 
Double.MAX_VALUE };
+               Object element = 4.78;
+               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 testArrayRemoveFunction8() throws Exception {
+               Object[] o = new Object[] { 123l, 677l, 98789l, -78989l, 66787l 
};
+               Object[] o2 = new Object[] { 123l, 677l, -78989l, 66787l };
+               Object element = 98789l;
+               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 testArrayRemoveFunction9() throws Exception {
+               Object[] o = new Object[] { (short) 34, (short) -89, (short) 
999, (short) 34 };
+               Object[] o2 = new Object[] { (short) 34, (short) -89, (short) 
999, (short) 34 };
+               Object element = (short) -23;
+               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 testArrayRemoveFunction10() throws Exception {
+               Object[] o = new Object[] { (byte) 4, (byte) 8, (byte) 9 };
+               Object[] o2 = new Object[] { (byte) 8, (byte) 9 };
+               Object element = (byte) 4;
+               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 testArrayRemoveFunction11() throws Exception {
+               Object[] o = new Object[] { BigDecimal.valueOf(2345), 
BigDecimal.valueOf(-23.45), BigDecimal.valueOf(785) };
+               Object[] o2 = new Object[] { BigDecimal.valueOf(-23.45), 
BigDecimal.valueOf(785) };
+               Object element = BigDecimal.valueOf(2345);
+               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 testArrayRemoveFunction12() throws Exception {
+               Calendar calendar = Calendar.getInstance();
+               java.util.Date currentDate = calendar.getTime();
+               java.sql.Date date = new java.sql.Date(currentDate.getTime());
+               Date date2 = new Date(new java.util.Date().getTime() + 1000);
+
+               Object[] o = new Object[] { date, date, date, date2 };
+               Object[] o2 = new Object[] { date2 };
+               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 testArrayRemoveFunction13() throws Exception {
+               Calendar calendar = Calendar.getInstance();
+               java.util.Date currentDate = calendar.getTime();
+               java.sql.Time time = new java.sql.Time(currentDate.getTime());
+               java.sql.Time time2 = new java.sql.Time(new 
java.util.Date().getTime() + 1000);
+
+               Object[] o = new Object[] { time, time, time, time2 };
+               Object[] o2 = new Object[] { time2 };
+               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 testArrayRemoveFunction14() throws Exception {
+               Calendar calendar = Calendar.getInstance();
+               java.util.Date currentDate = calendar.getTime();
+               java.sql.Timestamp timestamp = new 
java.sql.Timestamp(currentDate.getTime());
+               java.sql.Timestamp timestamp2 = new java.sql.Timestamp(new 
java.util.Date().getTime() + 1000);
+
+               Object[] o = new Object[] { timestamp, timestamp2, timestamp, 
timestamp };
+               Object[] o2 = new Object[] { timestamp2 };
+               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 testArrayRemoveFunction15() 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[] { 42, 3 }, new byte[] { 
5, 3 }, new byte[] { 6, 3 },
+                               new byte[] { 2, 5 } };
+               byte[] element = new byte[] { 2, 0, 3 };
+               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);
+       }
+
+}

Reply via email to