You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ja...@apache.org on 2015/04/14 21:09:38 UTC

[1/3] phoenix git commit: PHOENIX-1705 implement ARRAY_APPEND built in function (Dumindu Buddhika)

Repository: phoenix
Updated Branches:
  refs/heads/master 986080f3f -> 3f6b25947


PHOENIX-1705 implement ARRAY_APPEND built in function (Dumindu Buddhika)


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

Branch: refs/heads/master
Commit: 7ef1718127ffaa99adbc4c25ec3715d9472464f9
Parents: 986080f
Author: James Taylor <jt...@salesforce.com>
Authored: Tue Apr 14 12:07:29 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Tue Apr 14 12:07:29 2015 -0700

----------------------------------------------------------------------
 .../phoenix/end2end/ArrayAppendFunctionIT.java  | 667 +++++++++++++++++++
 .../function/ArrayAppendFunction.java           | 127 ++++
 .../expression/ArrayAppendFunctionTest.java     | 345 ++++++++++
 3 files changed, 1139 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/7ef17181/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayAppendFunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayAppendFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayAppendFunctionIT.java
new file mode 100644
index 0000000..1957b3a
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayAppendFunctionIT.java
@@ -0,0 +1,667 @@
+/*
+ * 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.junit.Test;
+
+public class ArrayAppendFunctionIT extends BaseHBaseManagedTimeIT {
+    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['a','bbbb','c','ddd','e','foo']" +
+                ")";
+        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 testArrayAppendFunctionVarchar() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(varchars,'34567') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String[] strings = new String[]{"2345", "46345", "23234", "34567"};
+
+        Array array = conn.createArrayOf("VARCHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInteger() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(integers,1234) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Integer[] integers = new Integer[]{2345, 46345, 23234, 456, 1234};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionDouble() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(doubles,double1) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Double[] doubles = new Double[]{23.45, 46.345, 23.234, 45.6, 5.78, 23.45};
+
+        Array array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionDouble2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(doubles,23) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Double[] doubles = new Double[]{23.45, 46.345, 23.234, 45.6, 5.78, new Double(23)};
+
+        Array array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionBigint() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(bigints,1112) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Long[] longs = new Long[]{12l, 34l, 56l, 78l, 910l, 1112l};
+
+        Array array = conn.createArrayOf("BIGINT", longs);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionChar() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(chars,'fac') FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String[] strings = new String[]{"a", "bbbb", "c", "ddd", "e", "fac"};
+
+        Array array = conn.createArrayOf("CHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test(expected = TypeMismatchException.class)
+    public void testArrayAppendFunctionIntToCharArray() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(varchars,234) FROM regions WHERE region_name = 'SF Bay Area'");
+    }
+
+    @Test(expected = TypeMismatchException.class)
+    public void testArrayAppendFunctionVarcharToIntegerArray() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(integers,'234') FROM regions WHERE region_name = 'SF Bay Area'");
+
+    }
+
+    @Test(expected = SQLException.class)
+    public void testArrayAppendFunctionChar2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(chars,'facfacfacfacfacfacfac') FROM regions WHERE region_name = 'SF Bay Area'");
+        rs.next();
+        rs.getArray(1);
+    }
+
+    @Test
+    public void testArrayAppendFunctionIntegerToDoubleArray() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(doubles,45) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Double[] doubles = new Double[]{23.45, 46.345, 23.234, 45.6, 5.78, 45.0};
+
+        Array array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithNestedFunctions1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(ARRAY[23,45],integers[1]) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Integer[] integers = new Integer[]{23, 45, 2345};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithNestedFunctions2() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(integers,ARRAY_ELEM(ARRAY[2,4],1)) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Integer[] integers = new Integer[]{2345, 46345, 23234, 456, 2};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithNestedFunctions3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(doubles,ARRAY_ELEM(doubles,2)) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Double[] doubles = new Double[]{23.45, 46.345, 23.234, 45.6, 5.78, 46.345};
+
+        Array array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithUpsert1() 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_APPEND(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 testArrayAppendFunctionWithUpsert2() 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_APPEND(ARRAY[4,5],6))";
+        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[]{4, 5, 6};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithUpsert3() 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_APPEND(ARRAY[5.67,7.87],9.0))";
+        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[]{5.67, 7.87, new Double(9)};
+
+        Array array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithUpsertSelect1() 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_APPEND(ARRAY[5.67,7.87],9.0))";
+        conn.createStatement().execute(dml);
+
+        dml = "UPSERT INTO source(region_name,doubles) VALUES('SF Bay Area2',ARRAY_APPEND(ARRAY[56.7,7.87],9.2))";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        dml = "UPSERT INTO target(region_name, doubles) SELECT region_name, ARRAY_APPEND(doubles,5) 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[]{5.67, 7.87, new Double(9), new Double(5)};
+        Array array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertTrue(rs.next());
+
+        doubles = new Double[]{56.7, 7.87, new Double(9.2), new Double(5)};
+        array = conn.createArrayOf("DOUBLE", doubles);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithUpsertSelect2() 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_APPEND(ARRAY['abcd','b'],'c'))";
+        conn.createStatement().execute(dml);
+
+        dml = "UPSERT INTO source(region_name,varchars) VALUES('SF Bay Area2',ARRAY_APPEND(ARRAY['d','fgh'],'something'))";
+        conn.createStatement().execute(dml);
+        conn.commit();
+
+        dml = "UPSERT INTO target(region_name, varchars) SELECT region_name, ARRAY_APPEND(varchars,'stu') 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[]{"abcd", "b", "c", "stu"};
+        Array array = conn.createArrayOf("VARCHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertTrue(rs.next());
+
+        strings = new String[]{"d", "fgh", "something", "stu"};
+        array = conn.createArrayOf("VARCHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere1() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY[2345,46345,23234,456,123]=ARRAY_APPEND(integers,123)");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere2() 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_APPEND(ARRAY['2345','46345','23234'],'1234'))");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere3() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY['2345','46345','23234','1234']=ARRAY_APPEND(ARRAY['2345','46345','23234'],'1234')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere4() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY[23.45,4634.5,2.3234,123.4]=ARRAY_APPEND(ARRAY[23.45,4634.5,2.3234],123.4)");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere5() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY['2345','46345','23234','foo']=ARRAY_APPEND(varchars,'foo')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere6() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE chars2=ARRAY_APPEND(chars,'foo')");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionInWhere7() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT region_name FROM regions WHERE ARRAY[2,3,4]=ARRAY_APPEND(ARRAY[2,3],4)");
+        assertTrue(rs.next());
+
+        assertEquals("SF Bay Area", rs.getString(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionIntegerWithNull() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(NULL,NULL) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Integer[] integers = new Integer[]{2345, 46345, 23234, 456};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(null, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionVarcharWithNull() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(varchars,NULL) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String[] strings = new String[]{"2345", "46345", "23234"};
+
+        Array array = conn.createArrayOf("VARCHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionDoublesWithNull() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(doubles,NULL) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Double[] doubles = new Double[]{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 testArrayAppendFunctionCharsWithNull() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(chars,NULL) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String[] strings = new String[]{"a", "bbbb", "c", "ddd", "e"};
+
+        Array array = conn.createArrayOf("CHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionWithNull() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(integers,nullcheck) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        Integer[] integers = new Integer[]{2345, 46345, 23234, 456};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test(expected = SQLException.class)
+    public void testArrayAppendFunctionCharLimitCheck() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTables(conn);
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(chars,char1) FROM regions WHERE region_name = 'SF Bay Area'");
+        assertTrue(rs.next());
+
+        String[] strings = new String[]{"a", "bbbb", "c", "ddd", "e", "wert"};
+
+        Array array = conn.createArrayOf("CHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionIntegerDesc() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTablesDesc(conn, "INTEGER", "23");
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(integers,pk) FROM regions");
+        assertTrue(rs.next());
+
+        Integer[] integers = new Integer[]{2345, 46345, 23234, 456, 23};
+
+        Array array = conn.createArrayOf("INTEGER", integers);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+
+    }
+
+    @Test
+    public void testArrayAppendFunctionVarcharDesc() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTablesDesc(conn, "VARCHAR", "'e'");
+
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(varchars,pk) FROM regions");
+        assertTrue(rs.next());
+
+        String[] strings = new String[]{"2345", "46345", "23234", "e"};
+
+        Array array = conn.createArrayOf("VARCHAR", strings);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionBigIntDesc() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTablesDesc(conn, "BIGINT", "1112");
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(bigints,pk) FROM regions");
+        assertTrue(rs.next());
+
+        Long[] longs = new Long[]{12l, 34l, 56l, 78l, 910l, 1112l};
+
+        Array array = conn.createArrayOf("BIGINT", longs);
+
+        assertEquals(array, rs.getArray(1));
+        assertFalse(rs.next());
+    }
+
+    @Test
+    public void testArrayAppendFunctionBooleanDesc() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        initTablesDesc(conn, "BOOLEAN", "false");
+        ResultSet rs;
+        rs = conn.createStatement().executeQuery("SELECT ARRAY_APPEND(bools,pk) FROM regions");
+        assertTrue(rs.next());
+
+        Boolean[] booleans = new Boolean[]{true, false, 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/7ef17181/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
new file mode 100644
index 0000000..db92d61
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayAppendFunction.java
@@ -0,0 +1,127 @@
+/*
+ * 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.parse.FunctionParseNode;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.TypeMismatchException;
+import org.apache.phoenix.schema.types.*;
+import org.apache.phoenix.schema.tuple.Tuple;
+
+@FunctionParseNode.BuiltInFunction(name = ArrayAppendFunction.NAME, args = {
+        @FunctionParseNode.Argument(allowedTypes = {PBinaryArray.class,
+                PVarbinaryArray.class}),
+        @FunctionParseNode.Argument(allowedTypes = {PVarbinary.class}, defaultValue = "null")})
+public class ArrayAppendFunction extends ScalarFunction {
+
+    public static final String NAME = "ARRAY_APPEND";
+
+    public ArrayAppendFunction() {
+    }
+
+    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
+    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();
+
+        if (!getElementExpr().evaluate(tuple, ptr) || ptr.getLength() == 0) {
+            ptr.set(arrayBytes, offset, length);
+            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());
+
+        return PArrayDataType.appendItemToArray(ptr, length, offset, arrayBytes, getBaseType(), arrayLength, getMaxLength(), getArrayExpr().getSortOrder());
+    }
+
+    @Override
+    public PDataType getDataType() {
+        return children.get(0).getDataType();
+    }
+
+    @Override
+    public Integer getMaxLength() {
+        return this.children.get(0).getMaxLength();
+    }
+
+    @Override
+    public SortOrder getSortOrder() {
+        return getChildren().get(0).getSortOrder();
+    }
+
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+    public Expression getArrayExpr() {
+        return getChildren().get(0);
+    }
+
+    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/7ef17181/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
new file mode 100644
index 0000000..2b4cb84
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ArrayAppendFunctionTest.java
@@ -0,0 +1,345 @@
+/*
+ * 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.assertTrue;
+
+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.ArrayAppendFunction;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.*;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class ArrayAppendFunctionTest {
+
+    private static void testExpression(LiteralExpression array, LiteralExpression element, PhoenixArray expected)
+            throws SQLException {
+        List<Expression> expressions = Lists.newArrayList((Expression) array);
+        expressions.add(element);
+
+        Expression arrayAppendFunction = new ArrayAppendFunction(expressions);
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        arrayAppendFunction.evaluate(null, ptr);
+        PhoenixArray result = (PhoenixArray) arrayAppendFunction.getDataType().toObject(ptr, expressions.get(0).getSortOrder(), array.getMaxLength(), array.getScale());
+        assertTrue(result.equals(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 testArrayAppendFunction1() throws Exception {
+        Object[] o = new Object[]{1, 2, -3, 4};
+        Object[] o2 = new Object[]{1, 2, -3, 4, 5};
+        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 testArrayAppendFunction2() throws Exception {
+        Object[] o = new Object[]{"1", "2", "3", "4"};
+        Object[] o2 = new Object[]{"1", "2", "3", "4", "56"};
+        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 testArrayAppendFunction3() 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 = 0; i < o2.length - 1; i++) {
+            o2[i] = "a";
+        }
+        Object element = "b";
+        o2[o2.length - 1] = 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 testArrayAppendFunction4() 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 = 0; i < o2.length - 1; i++) {
+            o2[i] = "a";
+        }
+        Object element = "b";
+        o2[o2.length - 1] = 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 testArrayAppendFunction5() throws Exception {
+        Boolean[] o = new Boolean[]{true, false, false, true};
+        Boolean[] o2 = new Boolean[]{true, false, false, true, false};
+        Boolean element = false;
+        PDataType baseType = PBoolean.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 testArrayAppendFunction6() 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(2.3), new Float(7.9), new Float(-9.6), new Float(2.3), new Float(8.9)};
+        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 testArrayAppendFunction7() throws Exception {
+        Object[] o = new Object[]{4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE};
+        Object[] o2 = new Object[]{4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE, 12.67};
+        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 testArrayAppendFunction8() throws Exception {
+        Object[] o = new Object[]{123l, 677l, 98789l, -78989l, 66787l};
+        Object[] o2 = new Object[]{123l, 677l, 98789l, -78989l, 66787l, 543l};
+        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 testArrayAppendFunction9() throws Exception {
+        Object[] o = new Object[]{(short) 34, (short) -23, (short) -89, (short) 999, (short) 34};
+        Object[] o2 = new Object[]{(short) 34, (short) -23, (short) -89, (short) 999, (short) 34, (short) 7};
+        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 testArrayAppendFunction10() throws Exception {
+        Object[] o = new Object[]{(byte) 4, (byte) 8, (byte) 9};
+        Object[] o2 = new Object[]{(byte) 4, (byte) 8, (byte) 9, (byte) 6};
+        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 testArrayAppendFunction11() throws Exception {
+        Object[] o = new Object[]{BigDecimal.valueOf(2345), BigDecimal.valueOf(-23.45), BigDecimal.valueOf(785)};
+        Object[] o2 = new Object[]{BigDecimal.valueOf(2345), BigDecimal.valueOf(-23.45), BigDecimal.valueOf(785), BigDecimal.valueOf(-19)};
+        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 testArrayAppendFunction12() 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 testArrayAppendFunction13() 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 testArrayAppendFunction14() 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 testArrayAppendFunction15() throws Exception {
+        Object[] o = new Object[]{1, 2, -3, 4};
+        Object[] o2 = new Object[]{1, 2, -3, 4, 5};
+        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 testArrayAppendFunction16() throws Exception {
+        Object[] o = new Object[]{1, 2, -3, 4};
+        Object[] o2 = new Object[]{1, 2, -3, 4, 5};
+        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 testArrayAppendFunction17() throws Exception {
+        Object[] o = new Object[]{1, 2, -3, 4};
+        Object[] o2 = new Object[]{1, 2, -3, 4, 5};
+        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 testArrayAppendFunction18() throws Exception {
+        Object[] o = new Object[]{"1   ", "2   ", "3   ", "4   "};
+        Object[] o2 = new Object[]{"1", "2", "3", "4", "5"};
+        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 testArrayAppendFunction19() throws Exception {
+        Object[] o = new Object[]{"1   ", "2   ", "3   ", "4   "};
+        Object[] o2 = new Object[]{"1", "2", "3", "4", "5"};
+        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 testArrayAppendFunction20() throws Exception {
+        Object[] o = new Object[]{"1   ", "2   ", "3   ", "4   "};
+        Object[] o2 = new Object[]{"1", "2", "3", "4", "5"};
+        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 testArrayAppendFunction21() throws Exception {
+        Object[] o = new Object[]{4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE};
+        Object[] o2 = new Object[]{4.78, 9.54, 2.34, -9.675, Double.MAX_VALUE, 12.67};
+        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 testArrayAppendFunction22() 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);
+    }
+
+}


[2/3] phoenix git commit: PHOENIX-1287 Use the joni byte[] regex engine in place of j.u.regex (Shuxiong Ye)

Posted by ja...@apache.org.
http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSubstrParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSubstrParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSubstrParseNode.java
new file mode 100644
index 0000000..a975550
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSubstrParseNode.java
@@ -0,0 +1,55 @@
+/*
+ * 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.parse;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction;
+import org.apache.phoenix.expression.function.RegexpSubstrFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSubstrFunction;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
+
+/**
+ * Parse node corresponding to {@link RegexpSubstrFunction}. It also acts as a factory for creating
+ * the right kind of RegexpSubstrFunction according to setting in
+ * QueryServices.USE_BYTE_BASED_REGEX_ATTRIB
+ */
+public class RegexpSubstrParseNode extends FunctionParseNode {
+
+    RegexpSubstrParseNode(String name, List<ParseNode> children, BuiltInFunctionInfo info) {
+        super(name, children, info);
+    }
+
+    @Override
+    public Expression create(List<Expression> children, StatementContext context)
+            throws SQLException {
+        QueryServices services = context.getConnection().getQueryServices();
+        boolean useByteBasedRegex =
+                services.getProps().getBoolean(QueryServices.USE_BYTE_BASED_REGEX_ATTRIB,
+                    QueryServicesOptions.DEFAULT_USE_BYTE_BASED_REGEX);
+        if (useByteBasedRegex) {
+            return new ByteBasedRegexpSubstrFunction(children);
+        } else {
+            return new StringBasedRegexpSubstrFunction(children);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
index adf146d..3c6a6c1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -50,6 +50,8 @@ public interface QueryServices extends SQLCloseable {
     public static final String AUTO_COMMIT_ATTRIB = "phoenix.connection.autoCommit";
     // consistency configuration setting
     public static final String CONSISTENCY_ATTRIB = "phoenix.connection.consistency";
+    // joni byte regex engine setting
+    public static final String USE_BYTE_BASED_REGEX_ATTRIB = "phoenix.regex.byteBased";
 
     /**
 	 * max size to spool the the result into

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index 884b820..5cc4fa7 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -61,6 +61,7 @@ import static org.apache.phoenix.query.QueryServices.STATS_UPDATE_FREQ_MS_ATTRIB
 import static org.apache.phoenix.query.QueryServices.STATS_USE_CURRENT_TIME_ATTRIB;
 import static org.apache.phoenix.query.QueryServices.THREAD_POOL_SIZE_ATTRIB;
 import static org.apache.phoenix.query.QueryServices.THREAD_TIMEOUT_MS_ATTRIB;
+import static org.apache.phoenix.query.QueryServices.USE_BYTE_BASED_REGEX_ATTRIB;
 import static org.apache.phoenix.query.QueryServices.USE_INDEXES_ATTRIB;
 
 import java.util.Map.Entry;
@@ -68,10 +69,8 @@ import java.util.Map.Entry;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.Coprocessor;
 import org.apache.hadoop.hbase.client.Consistency;
-import org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory;
 import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
 import org.apache.hadoop.hbase.ipc.controller.ClientRpcControllerFactory;
-import org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory;
 import org.apache.hadoop.hbase.regionserver.wal.WALCellCodec;
 import org.apache.phoenix.schema.SaltingUtil;
 import org.apache.phoenix.trace.util.Tracing;
@@ -194,6 +193,8 @@ public class QueryServicesOptions {
     
     public static final String DEFAULT_CONSISTENCY_LEVEL = Consistency.STRONG.toString();
 
+    public static final boolean DEFAULT_USE_BYTE_BASED_REGEX = true;
+
     private final Configuration config;
 
     private QueryServicesOptions(Configuration config) {
@@ -247,6 +248,7 @@ public class QueryServicesOptions {
             .setIfUnset(DELAY_FOR_SCHEMA_UPDATE_CHECK, DEFAULT_DELAY_FOR_SCHEMA_UPDATE_CHECK)
             .setIfUnset(METRICS_ENABLED, DEFAULT_IS_METRICS_ENABLED)
             .setIfUnset(RpcControllerFactory.CUSTOM_CONTROLLER_CONF_KEY, DEFAULT_CLIENT_RPC_CONTROLLER_FACTORY)
+            .setIfUnset(USE_BYTE_BASED_REGEX_ATTRIB, DEFAULT_USE_BYTE_BASED_REGEX)
             ;
         // HBase sets this to 1, so we reset it to something more appropriate.
         // Hopefully HBase will change this, because we can't know if a user set
@@ -450,6 +452,10 @@ public class QueryServicesOptions {
         return config.getBoolean(METRICS_ENABLED, DEFAULT_IS_METRICS_ENABLED);
     }
     
+    public boolean isUseByteBasedRegex() {
+        return config.getBoolean(USE_BYTE_BASED_REGEX_ATTRIB, DEFAULT_USE_BYTE_BASED_REGEX);
+    }
+
     public QueryServicesOptions setMaxServerCacheTTLMs(int ttl) {
         return set(MAX_SERVER_CACHE_TIME_TO_LIVE_MS_ATTRIB, ttl);
     }
@@ -524,4 +530,8 @@ public class QueryServicesOptions {
         return this;
     }
 
+    public QueryServicesOptions setUseByteBasedRegex(boolean flag) {
+        config.setBoolean(USE_BYTE_BASED_REGEX_ATTRIB, flag);
+        return this;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/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 b6dce34..c6861f7 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,8 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.text.Format;
+import java.util.LinkedList;
+import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
@@ -755,4 +757,93 @@ public abstract class PArrayDataType<T> extends PDataType<T> {
         buf.append(']');
         return buf.toString();
     }
+
+    static public class PArrayDataTypeBytesArrayBuilder<T> {
+        static private final int BYTE_ARRAY_DEFAULT_SIZE = 128;
+
+        private PDataType baseType;
+        private SortOrder sortOrder;
+        private List<Integer> offsetPos;
+        private TrustedByteArrayOutputStream byteStream;
+        private DataOutputStream oStream;
+        private int nulls;
+
+        public PArrayDataTypeBytesArrayBuilder(PDataType baseType, SortOrder sortOrder) {
+            this.baseType = baseType;
+            this.sortOrder = sortOrder;
+            offsetPos = new LinkedList<Integer>();
+            byteStream = new TrustedByteArrayOutputStream(BYTE_ARRAY_DEFAULT_SIZE);
+            oStream = new DataOutputStream(byteStream);
+            nulls = 0;
+        }
+
+        private void close() {
+            try {
+                if (byteStream != null) byteStream.close();
+                if (oStream != null) oStream.close();
+                byteStream = null;
+                oStream = null;
+            } catch (IOException ioe) {
+            }
+        }
+
+        public boolean appendElem(byte[] bytes) {
+            return appendElem(bytes, 0, bytes.length);
+        }
+
+        public boolean appendElem(byte[] bytes, int offset, int len) {
+            if (oStream == null || byteStream == null) return false;
+            try {
+                if (!baseType.isFixedWidth()) {
+                    if (len == 0) {
+                        offsetPos.add(byteStream.size());
+                        nulls++;
+                    } else {
+                        nulls = serializeNulls(oStream, nulls);
+                        offsetPos.add(byteStream.size());
+                        if (sortOrder == SortOrder.DESC) {
+                            SortOrder.invert(bytes, offset, bytes, offset, len);
+                        }
+                        oStream.write(bytes, offset, len);
+                        oStream.write(QueryConstants.SEPARATOR_BYTE);
+                    }
+                } else {
+                    if (sortOrder == SortOrder.DESC) {
+                        SortOrder.invert(bytes, offset, bytes, offset, len);
+                    }
+                    oStream.write(bytes, offset, len);
+                }
+                return true;
+            } catch (IOException e) {
+            }
+            return false;
+        }
+
+        public byte[] getBytesAndClose() {
+            try {
+                if (!baseType.isFixedWidth()) {
+                    int noOfElements = offsetPos.size();
+                    int[] offsetPosArray = new int[noOfElements];
+                    int index = 0;
+                    for (Integer i : offsetPos) {
+                        offsetPosArray[index] = i;
+                        ++index;
+                    }
+                    PArrayDataType.writeEndSeperatorForVarLengthArray(oStream);
+                    noOfElements =
+                            PArrayDataType.serailizeOffsetArrayIntoStream(oStream, byteStream,
+                                noOfElements, offsetPosArray[offsetPosArray.length - 1],
+                                offsetPosArray);
+                    serializeHeaderInfoIntoStream(oStream, noOfElements);
+                }
+                ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+                ptr.set(byteStream.getBuffer(), 0, byteStream.size());
+                return ByteUtil.copyKeyBytesIfNecessary(ptr);
+            } catch (IOException e) {
+            } finally {
+                close();
+            }
+            return null;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/util/StringUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/StringUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/StringUtil.java
index 4a7ae38..89ae43b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/StringUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/StringUtil.java
@@ -115,7 +115,13 @@ public class StringUtil {
     }
 
     public static int getBytesInChar(byte b, SortOrder sortOrder) {
-    	Preconditions.checkNotNull(sortOrder);
+        int ret = getBytesInCharNoException(b, sortOrder);
+        if (ret == -1) throw new UndecodableByteException(b);
+        return ret;
+    }
+
+    private static int getBytesInCharNoException(byte b, SortOrder sortOrder) {
+        Preconditions.checkNotNull(sortOrder);
         if (sortOrder == SortOrder.DESC) {
             b = SortOrder.invert(b);
         }
@@ -128,8 +134,7 @@ public class StringUtil {
             return 3;
         if ((c & BYTES_4_MASK) == 0xF0)
             return 4;
-        // Any thing else in the first byte is invalid
-        throw new UndecodableByteException(b);
+        return -1;
     }
 
     public static int calculateUTF8Length(byte[] bytes, int offset, int length, SortOrder sortOrder) {
@@ -143,6 +148,63 @@ public class StringUtil {
         return length;
     }
 
+    // given an array of bytes containing utf-8 encoded strings, starting from curPos, ending before
+    // range, and return the next character offset, -1 if no next character available or
+    // UndecodableByteException
+    private static int calculateNextCharOffset(byte[] bytes, int curPos, int range,
+            SortOrder sortOrder) {
+        int ret = getBytesInCharNoException(bytes[curPos], sortOrder);
+        if (ret == -1) return -1;
+        ret += curPos;
+        if (ret >= range) return -1;
+        return ret;
+    }
+
+    // given an array of bytes containing utf-8 encoded strings, starting from offset, and return
+    // the previous character offset , -1 if UndecodableByteException. curPos points to current
+    // character starting offset.
+    private static int calculatePreCharOffset(byte[] bytes, int curPos, int offset,
+            SortOrder sortOrder) {
+        --curPos;
+        for (int i = 1, pos = curPos - i + 1; i <= 4 && offset <= pos; ++i, --pos) {
+            int ret = getBytesInCharNoException(bytes[pos], sortOrder);
+            if (ret == i) return pos;
+        }
+        return -1;
+    }
+
+    // return actural offsetInBytes corresponding to offsetInStr in utf-8 encoded strings bytes
+    // containing
+    // @param bytes an array of bytes containing utf-8 encoded strings
+    // @param offset
+    // @param length
+    // @param sortOrder
+    // @param offsetInStr offset for utf-8 encoded strings bytes array containing. Can be negative
+    // meaning counting from the end of encoded strings
+    // @return actural offsetInBytes corresponding to offsetInStr. -1 if offsetInStr is out of index
+    public static int calculateUTF8Offset(byte[] bytes, int offset, int length,
+            SortOrder sortOrder, int offsetInStr) {
+        if (offsetInStr == 0) return offset;
+        int ret, range = offset + length;
+        if (offsetInStr > 0) {
+            ret = offset;
+            while (offsetInStr > 0) {
+                ret = calculateNextCharOffset(bytes, ret, range, sortOrder);
+                if (ret == -1) return -1;
+                --offsetInStr;
+            }
+        } else {
+            ret = offset + length;
+            while (offsetInStr < 0) {
+                ret = calculatePreCharOffset(bytes, ret, offset, sortOrder);
+                // if calculateCurCharOffset returns -1, ret must be smaller than offset
+                if (ret < offset) return -1;
+                ++offsetInStr;
+            }
+        }
+        return ret;
+    }
+
     // Given an array of bytes containing encoding utf-8 encoded strings, the offset and a length
     // parameter, return the actual index into the byte array which would represent a substring
     // of <length> starting from the character at <offset>. We assume the <offset> is the start

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
index 94b25d0..f40afc3 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java
@@ -731,7 +731,8 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
         assertEquals(
                 rowKeyFilter(like(
                     ENTITY_ID,
-                    likeArg)),
+                    likeArg,
+                    context)),
                 filter);
 
         byte[] startRow = ByteUtil.concat(
@@ -757,7 +758,8 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
         assertEquals(
                 rowKeyFilter(like(
                     ENTITY_ID,
-                    likeArg)),
+                    likeArg,
+                    context)),
                 filter);
 
         byte[] startRow = ByteUtil.concat(
@@ -783,7 +785,8 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
         assertEquals(
                 rowKeyFilter(like(
                     substr(ENTITY_ID,1,10),
-                    likeArg)),
+                    likeArg,
+                    context)),
                 filter);
 
         byte[] startRow = ByteUtil.concat(
@@ -809,7 +812,8 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
         assertEquals(
                 rowKeyFilter(like(
                     substr(ENTITY_ID,4,10),
-                    likeArg)),
+                    likeArg,
+                    context)),
                 filter);
 
         byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
@@ -832,7 +836,8 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
         assertEquals(
                 rowKeyFilter(like(
                     ENTITY_ID,
-                    likeArg)),
+                    likeArg,
+                    context)),
                 filter);
 
         byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);
@@ -855,7 +860,8 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest {
         assertEquals(
                 rowKeyFilter(not(like(
                     ENTITY_ID,
-                    likeArg))),
+                    likeArg,
+                    context))),
                 filter);
 
         byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/ILikeExpressionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/ILikeExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/ILikeExpressionTest.java
index 3033edf..e66ad13 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/ILikeExpressionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/ILikeExpressionTest.java
@@ -20,24 +20,40 @@ package org.apache.phoenix.expression;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.sql.SQLException;
 import java.util.Arrays;
 import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.parse.LikeParseNode.LikeType;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PVarchar;
 import org.junit.Test;
 
 public class ILikeExpressionTest {
-    public boolean testExpression (String value, String expression) {
-      LiteralExpression v = LiteralExpression.newConstant(value);
-      LiteralExpression p = LiteralExpression.newConstant(expression);
+    private boolean testExpression (String value, String expression, SortOrder sortorder)
+            throws SQLException {
+      LiteralExpression v = LiteralExpression.newConstant(value, PVarchar.INSTANCE, sortorder);
+      LiteralExpression p = LiteralExpression.newConstant(expression, PVarchar.INSTANCE, sortorder);
       List<Expression> children = Arrays.<Expression>asList(v,p);
-      LikeExpression e = LikeExpression.create(children, LikeType.CASE_INSENSITIVE);
+      LikeExpression e1 = ByteBasedLikeExpression.create(children, LikeType.CASE_INSENSITIVE);
+      LikeExpression e2 = StringBasedLikeExpression.create(children, LikeType.CASE_INSENSITIVE);
       ImmutableBytesWritable ptr = new ImmutableBytesWritable();
-      boolean evaluated = e.evaluate(null, ptr);
-      Boolean result = (Boolean)e.getDataType().toObject(ptr);
-      assertTrue(evaluated);
-      return result;
+      boolean evaluated1 = e1.evaluate(null, ptr);
+      Boolean result1 = (Boolean)e1.getDataType().toObject(ptr);
+      assertTrue(evaluated1);
+      boolean evaluated2 = e2.evaluate(null, ptr);
+      Boolean result2 = (Boolean)e2.getDataType().toObject(ptr);
+      assertTrue(evaluated2);
+      assertEquals(result1, result2);
+      return result1;
+    }
+
+    private boolean testExpression(String value, String expression) throws SQLException {
+        boolean result1 = testExpression(value, expression, SortOrder.ASC);
+        boolean result2 = testExpression(value, expression, SortOrder.DESC);
+        assertEquals(result1, result2);
+        return result1;
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/LikeExpressionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/LikeExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/LikeExpressionTest.java
index 27e6547..0bf8b06 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/LikeExpressionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/LikeExpressionTest.java
@@ -20,25 +20,42 @@ package org.apache.phoenix.expression;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.sql.SQLException;
 import java.util.Arrays;
 import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.parse.LikeParseNode.LikeType;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PVarchar;
 import org.junit.Test;
 
 public class LikeExpressionTest {
-    public boolean testExpression (String value, String expression) {
-      LiteralExpression v = LiteralExpression.newConstant(value);
-      LiteralExpression p = LiteralExpression.newConstant(expression);
+    private boolean testExpression(String value, String expression, SortOrder sortorder)
+            throws SQLException {
+      LiteralExpression v = LiteralExpression.newConstant(value, PVarchar.INSTANCE, sortorder);
+      LiteralExpression p = LiteralExpression.newConstant(expression, PVarchar.INSTANCE, sortorder);
       List<Expression> children = Arrays.<Expression>asList(v,p);
-      LikeExpression e = LikeExpression.create(children, LikeType.CASE_SENSITIVE);
+      LikeExpression e1 = ByteBasedLikeExpression.create(children, LikeType.CASE_SENSITIVE);
+      LikeExpression e2 = StringBasedLikeExpression.create(children, LikeType.CASE_SENSITIVE);
       ImmutableBytesWritable ptr = new ImmutableBytesWritable();
-      boolean evaluated = e.evaluate(null, ptr);
-      Boolean result = (Boolean)e.getDataType().toObject(ptr);
-      assertTrue(evaluated);
-      return result;
+      boolean evaluated1 = e1.evaluate(null, ptr);
+      Boolean result1 = (Boolean)e1.getDataType().toObject(ptr);
+      assertTrue(evaluated1);
+      boolean evaluated2 = e2.evaluate(null, ptr);
+      Boolean result2 = (Boolean)e2.getDataType().toObject(ptr);
+      assertTrue(evaluated2);
+      assertEquals(result1, result2);
+      return result1;
     }
+
+    private boolean testExpression(String value, String expression) throws SQLException {
+        boolean result1 = testExpression(value, expression, SortOrder.ASC);
+        boolean result2 = testExpression(value, expression, SortOrder.DESC);
+        assertEquals(result1, result2);
+        return result1;
+    }
+
     @Test
     public void testStartWildcard() throws Exception {
         assertEquals(Boolean.FALSE, testExpression ("149na7-app1-2-", "%-w"));
@@ -58,4 +75,10 @@ public class LikeExpressionTest {
         assertEquals(Boolean.TRUE, testExpression ("test", "%s%"));
         assertEquals(Boolean.FALSE, testExpression ("test", "%S%"));
     }
+
+    @Test
+    public void testEmptySourceStr() throws Exception {
+        assertEquals(Boolean.TRUE, testExpression ("", "%"));
+        assertEquals(Boolean.FALSE, testExpression ("", "_"));
+    }
  }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpReplaceFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpReplaceFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpReplaceFunctionTest.java
new file mode 100644
index 0000000..ad11c1b
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpReplaceFunctionTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpReplaceFunction;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class RegexpReplaceFunctionTest {
+    private final static PVarchar TYPE = PVarchar.INSTANCE;
+
+    private String evalExp(Expression exp) {
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        boolean eval = exp.evaluate(null, ptr);
+        assertTrue(eval);
+        String res = (String) exp.getDataType().toObject(ptr);
+        return res;
+    }
+
+    private String testExpression(String srcStr, String patternStr, String replaceStr,
+            SortOrder sortOrder) throws SQLException {
+        Expression srcExp, patternExp, replaceExp;
+        srcExp = LiteralExpression.newConstant(srcStr, TYPE, sortOrder);
+        patternExp = LiteralExpression.newConstant(patternStr, TYPE, sortOrder);
+        replaceExp = LiteralExpression.newConstant(replaceStr, TYPE, sortOrder);
+        List<Expression> expressions = Lists.newArrayList(srcExp, patternExp, replaceExp);
+        String res1, res2;
+        res1 = evalExp(new ByteBasedRegexpReplaceFunction(expressions));
+        res2 = evalExp(new StringBasedRegexpReplaceFunction(expressions));
+        assertEquals(res1, res2);
+        return res1;
+    }
+
+    private String testExpression(String srcStr, String patternStr, String replaceStr)
+            throws SQLException {
+        String result1 = testExpression(srcStr, patternStr, replaceStr, SortOrder.ASC);
+        String result2 = testExpression(srcStr, patternStr, replaceStr, SortOrder.DESC);
+        assertEquals(result1, result2);
+        return result1;
+    }
+
+    private void testExpression(String srcStr, String patternStr, String replaceStr,
+            String expectedStr) throws SQLException {
+        String result = testExpression(srcStr, patternStr, replaceStr);
+        assertEquals(expectedStr, result);
+    }
+
+    @Test
+    public void test() throws Exception {
+        testExpression("aa11bb22cc33dd44ee", "[0-9]+", "*", "aa*bb*cc*dd*ee");
+        testExpression("aa11bb22cc33dd44ee", "[0-9]+", "", "aabbccddee");
+        testExpression("aa11bb22cc33dd44ee", "[a-z][0-9]", "", "a1b2c3d4ee");
+        testExpression("aa11bb22cc33dd44ee", "[a-z0-9]+", "", (String) null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSplitFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSplitFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSplitFunctionTest.java
new file mode 100644
index 0000000..6157ce0
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSplitFunctionTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSplitFunction;
+import org.apache.phoenix.schema.SortOrder;
+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 RegexpSplitFunctionTest {
+    private final static PVarchar TYPE = PVarchar.INSTANCE;
+
+    private String[] evalExp(Expression exp) throws SQLException {
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        boolean eval = exp.evaluate(null, ptr);
+        assertTrue(eval);
+        PhoenixArray evalRes = (PhoenixArray) exp.getDataType().toObject(ptr);
+        String[] res = (String[]) evalRes.getArray();
+        return res;
+    }
+
+    private String[] testExpression(String srcStr, String patternStr, SortOrder sortOrder)
+            throws SQLException {
+        Expression srcExp, patternExp;
+        srcExp = LiteralExpression.newConstant(srcStr, TYPE, sortOrder);
+        patternExp = LiteralExpression.newConstant(patternStr, TYPE, sortOrder);
+        List<Expression> expressions = Lists.newArrayList(srcExp, patternExp);
+        String[] res1, res2;
+        res1 = evalExp(new ByteBasedRegexpSplitFunction(expressions));
+        res2 = evalExp(new StringBasedRegexpSplitFunction(expressions));
+        testEqual(res2, res1);
+        return res1;
+    }
+
+    private String[] testExpression(String srcStr, String patternStr) throws SQLException {
+        String[] result1 = testExpression(srcStr, patternStr, SortOrder.ASC);
+        String[] result2 = testExpression(srcStr, patternStr, SortOrder.DESC);
+        testEqual(result1, result2);
+        return result1;
+    }
+
+    private void testEqual(String[] expectedStr, String[] result) {
+        if (result == null ^ expectedStr == null) return;
+        if (expectedStr == null) return;
+        assertEquals(expectedStr.length, result.length);
+        for (int i = 0; i < expectedStr.length; ++i)
+            assertEquals(expectedStr[i], result[i]);
+    }
+
+    private void testExpression(String srcStr, String patternStr, String[] expectedStr)
+            throws SQLException {
+        String[] result = testExpression(srcStr, patternStr);
+        testEqual(expectedStr, result);
+    }
+
+    @Test
+    public void test() throws Exception {
+        String[] res = new String[] { "ONE", "TWO", "THREE" };
+        testExpression("ONE:TWO:THREE", ":", res);
+        testExpression("ONE,TWO,THREE", ",", res);
+        testExpression("12ONE34TWO56THREE78", "[0-9]+", new String[] { null, "ONE", "TWO", "THREE",
+                null });
+        testExpression("ONE34TWO56THREE78", "[0-9]+", new String[] { "ONE", "TWO", "THREE", null });
+        testExpression("123ONE34TWO56THREE", "[0-9]+", new String[] { null, "ONE", "TWO", "THREE" });
+        testExpression("123", "[0-9]+", new String[] { null, null });
+        testExpression("ONE", "[0-9]+", new String[] { "ONE" });
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSubstrFunctionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSubstrFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSubstrFunctionTest.java
new file mode 100644
index 0000000..c2889b3
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/RegexpSubstrFunctionTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSubstrFunction;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PInteger;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.junit.Test;
+
+import com.google.common.collect.Lists;
+
+public class RegexpSubstrFunctionTest {
+    private final static PVarchar TYPE = PVarchar.INSTANCE;
+
+    private String evalExp(Expression exp) {
+        ImmutableBytesWritable ptr = new ImmutableBytesWritable();
+        boolean eval = exp.evaluate(null, ptr);
+        assertTrue(eval);
+        String res = (String) exp.getDataType().toObject(ptr);
+        return res;
+    }
+
+    private String testExpression(String srcStr, String patternStr, int offset, SortOrder sortOrder) throws SQLException {
+        Expression srcExp, patternExp, offsetExp;
+        srcExp = LiteralExpression.newConstant(srcStr, TYPE, sortOrder);
+        patternExp = LiteralExpression.newConstant(patternStr, TYPE, sortOrder);
+        offsetExp = LiteralExpression.newConstant(offset, PInteger.INSTANCE, sortOrder);
+        List<Expression> expressions = Lists.newArrayList(srcExp, patternExp, offsetExp);
+        String res1, res2;
+        res1 = evalExp(new ByteBasedRegexpSubstrFunction(expressions));
+        res2 = evalExp(new StringBasedRegexpSubstrFunction(expressions));
+        assertEquals(res1, res2);
+        return res1;
+    }
+
+    private String testExpression(String srcStr, String patternStr, int offset) throws SQLException {
+        String result1 = testExpression(srcStr, patternStr, offset, SortOrder.ASC);
+        String result2 = testExpression(srcStr, patternStr, offset, SortOrder.DESC);
+        assertEquals(result1, result2);
+        return result1;
+    }
+
+    private void testExpression(String srcStr, String patternStr, int offset, String expectedStr)
+            throws SQLException {
+        String result = testExpression(srcStr, patternStr, offset);
+        assertEquals(expectedStr, result);
+    }
+
+    @Test
+    public void test() throws Exception {
+        testExpression("Report1?1", "[^\\\\?]+", 1, "Report1");
+        testExpression("Report1?2", "[^\\\\?]+", 1, "Report1");
+        testExpression("Report2?1", "[^\\\\?]+", 1, "Report2");
+        testExpression("Report3?2", "[^\\\\?]+", 1, "Report3");
+        testExpression("Report3?2", "[4-9]+", 0, (String) null);
+        testExpression("Report3?2", "[^\\\\?]+", 2, "eport3");
+        testExpression("Report3?2", "[^\\\\?]+", -5, "rt3");
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
index 8fb1a6c..b9ee0eb 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/SortOrderExpressionTest.java
@@ -30,16 +30,18 @@ import java.util.TimeZone;
 
 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction;
 import org.apache.phoenix.expression.function.FunctionArgumentType;
 import org.apache.phoenix.expression.function.LTrimFunction;
 import org.apache.phoenix.expression.function.LengthFunction;
 import org.apache.phoenix.expression.function.LowerFunction;
 import org.apache.phoenix.expression.function.LpadFunction;
 import org.apache.phoenix.expression.function.RTrimFunction;
-import org.apache.phoenix.expression.function.RegexpReplaceFunction;
-import org.apache.phoenix.expression.function.RegexpSubstrFunction;
 import org.apache.phoenix.expression.function.RoundDateExpression;
 import org.apache.phoenix.expression.function.SqlTypeNameFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpReplaceFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSubstrFunction;
 import org.apache.phoenix.expression.function.SubstrFunction;
 import org.apache.phoenix.expression.function.ToCharFunction;
 import org.apache.phoenix.expression.function.ToDateFunction;
@@ -80,13 +82,15 @@ public class SortOrderExpressionTest {
     @Test
     public void regexpSubstr() throws Exception {
         List<Expression> args = Lists.newArrayList(getInvertedLiteral("blah", PChar.INSTANCE), getLiteral("l.h"), getLiteral(2));
-        evaluateAndAssertResult(new RegexpSubstrFunction(args), "lah");
+        evaluateAndAssertResult(new StringBasedRegexpSubstrFunction(args), "lah");
+        evaluateAndAssertResult(new ByteBasedRegexpSubstrFunction(args), "lah");
     }
     
     @Test
     public void regexpReplace() throws Exception {
         List<Expression> args = Lists.newArrayList(getInvertedLiteral("blah", PChar.INSTANCE), getLiteral("l.h"), getLiteral("foo"));
-        evaluateAndAssertResult(new RegexpReplaceFunction(args), "bfoo");
+        evaluateAndAssertResult(new ByteBasedRegexpReplaceFunction(args), "bfoo");
+        evaluateAndAssertResult(new StringBasedRegexpReplaceFunction(args), "bfoo");
     }
     
     @Test

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/expression/util/regex/PatternPerformanceTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/util/regex/PatternPerformanceTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/util/regex/PatternPerformanceTest.java
new file mode 100644
index 0000000..908c662
--- /dev/null
+++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/util/regex/PatternPerformanceTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.util.regex;
+
+import static org.junit.Assert.assertTrue;
+
+import java.sql.SQLException;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.schema.types.PBoolean;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.schema.types.PVarcharArray;
+import org.apache.phoenix.schema.types.PhoenixArray;
+import org.junit.Test;
+
+public class PatternPerformanceTest {
+
+    static private class Timer {
+        private long startTimeStamp;
+
+        public void reset() {
+            startTimeStamp = System.currentTimeMillis();
+        }
+
+        public double currentTime() {
+            return (System.currentTimeMillis() - startTimeStamp) / 1000.0;
+        }
+
+        public void printTime(String hint) {
+            System.out.println(hint + " Time=" + currentTime());
+        }
+    }
+
+    private String[] data = new String[] { "ONE:TWO:THREE", "ABC:DEF", "PKU:THU:FDU" };
+    private ImmutableBytesWritable[] dataPtr = new ImmutableBytesWritable[] { getPtr(data[0]),
+            getPtr(data[1]), getPtr(data[2]) };
+    private String patternString;
+    private ImmutableBytesWritable resultPtr = new ImmutableBytesWritable();
+    private int maxTimes = 10000000;
+    private Timer timer = new Timer();
+    private final boolean ENABLE_ASSERT = false;
+
+    private static ImmutableBytesWritable getPtr(String str) {
+        return new ImmutableBytesWritable(PVarchar.INSTANCE.toBytes(str));
+    }
+
+    private void testReplaceAll(ImmutableBytesWritable replacePtr, AbstractBasePattern pattern,
+            String name) {
+        timer.reset();
+        for (int i = 0; i < maxTimes; ++i) {
+            pattern.replaceAll(dataPtr[i % 3], replacePtr, resultPtr);
+            if (ENABLE_ASSERT) {
+                String result = (String) PVarchar.INSTANCE.toObject(resultPtr);
+                assertTrue((i % 3 == 1 && ":".equals(result))
+                        || (i % 3 != 1 && "::".equals(result)));
+            }
+        }
+        timer.printTime(name);
+    }
+
+    public void testReplaceAll() {
+        patternString = "[A-Z]+";
+        ImmutableBytesWritable replacePtr = getPtr("");
+        testReplaceAll(replacePtr, new JavaPattern(patternString), "Java replaceAll");
+        testReplaceAll(replacePtr, new JONIPattern(patternString), "JONI replaceAll");
+    }
+
+    private void testLike(AbstractBasePattern pattern, String name) {
+        timer.reset();
+        for (int i = 0; i < maxTimes; ++i) {
+            pattern.matches(dataPtr[i % 3], resultPtr);
+            if (ENABLE_ASSERT) {
+                Boolean b = (Boolean) PBoolean.INSTANCE.toObject(resultPtr);
+                assertTrue(i % 3 != 2 || b.booleanValue());
+            }
+        }
+        timer.printTime(name);
+    }
+
+    public void testLike() {
+        patternString = "\\Q\\E.*\\QU\\E.*\\QU\\E.*\\QU\\E.*\\Q\\E";
+        testLike(new JavaPattern(patternString), "Java Like");
+        testLike(new JONIPattern(patternString), "JONI Like");
+    }
+
+    private void testSubstr(AbstractBasePattern pattern, String name) {
+        timer.reset();
+        for (int i = 0; i < maxTimes; ++i) {
+            boolean ret = pattern.substr(dataPtr[i % 3], 0, resultPtr);
+            if (ENABLE_ASSERT) {
+                assertTrue(ret
+                        && (i % 3 != 2 || ":THU".equals(PVarchar.INSTANCE.toObject(resultPtr))));
+            }
+        }
+        timer.printTime(name);
+    }
+
+    public void testSubstr() {
+        patternString = "\\:[A-Z]+";
+        testSubstr(new JavaPattern(patternString), "Java Substr");
+        testSubstr(new JONIPattern(patternString), "JONI Substr");
+    }
+
+    private void testSplit(AbstractBaseSplitter pattern, String name) throws SQLException {
+        timer.reset();
+        for (int i = 0; i < maxTimes; ++i) {
+            boolean ret = pattern.split(dataPtr[i % 3], resultPtr);
+            if (ENABLE_ASSERT) {
+                PhoenixArray array = (PhoenixArray) PVarcharArray.INSTANCE.toObject(resultPtr);
+                assertTrue(ret && (i % 3 != 1 || ((String[]) array.getArray()).length == 2));
+            }
+        }
+        timer.printTime(name);
+    }
+
+    public void testSplit() throws SQLException {
+        patternString = "\\:";
+        testSplit(new GuavaSplitter(patternString), "GuavaSplit");
+        testSplit(new JONIPattern(patternString), "JONI Split");
+    }
+
+    @Test
+    public void test() throws Exception {
+        // testLike();
+        // testReplaceAll();
+        // testSubstr();
+        // testSplit();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/util/StringUtilTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/StringUtilTest.java b/phoenix-core/src/test/java/org/apache/phoenix/util/StringUtilTest.java
index 9c218fb..6d00562 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/StringUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/StringUtilTest.java
@@ -17,7 +17,9 @@
 package org.apache.phoenix.util;
 
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
 
+import org.apache.phoenix.schema.SortOrder;
 import org.junit.Test;
 
 public class StringUtilTest {
@@ -48,5 +50,33 @@ public class StringUtilTest {
     public void testLpadZeroPadding() throws Exception {
         testLpad("ABCD", 4, "1234", "ABCD");
     }
-    
+
+    @Test
+    public void testCalculateUTF8Offset() throws Exception {
+        String tmp, padding = "padding", data = "零一二三四五六七八九", trailing = "trailing";
+        byte[] bytes = (padding + data + trailing).getBytes();
+        int ret, offset = padding.getBytes().length, length = data.getBytes().length;
+
+        tmp = padding;
+        for (int i = 0; i < data.length(); ++i) {
+            ret = StringUtil.calculateUTF8Offset(bytes, offset, length, SortOrder.ASC, i);
+            assertEquals(tmp.getBytes().length, ret);
+            tmp = tmp + data.charAt(i);
+        }
+        for (int i = data.length(); i < data.length() + 10; ++i) {
+            ret = StringUtil.calculateUTF8Offset(bytes, offset, length, SortOrder.ASC, i);
+            assertEquals(-1, ret);
+        }
+
+        for (int i = -data.length() - 10; i < -data.length(); ++i) {
+            ret = StringUtil.calculateUTF8Offset(bytes, offset, length, SortOrder.ASC, i);
+            assertEquals(-1, ret);
+        }
+        tmp = padding;
+        for (int i = -data.length(); i <= -1; ++i) {
+            ret = StringUtil.calculateUTF8Offset(bytes, offset, length, SortOrder.ASC, i);
+            assertEquals("i=" + i, tmp.getBytes().length, ret);
+            tmp = tmp + data.charAt(i + data.length());
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
index 872c318..66695f8 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
@@ -56,15 +56,16 @@ import org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheRequest
 import org.apache.phoenix.coprocessor.generated.MetaDataProtos.ClearCacheResponse;
 import org.apache.phoenix.coprocessor.generated.MetaDataProtos.MetaDataService;
 import org.apache.phoenix.expression.AndExpression;
+import org.apache.phoenix.expression.ByteBasedLikeExpression;
 import org.apache.phoenix.expression.ComparisonExpression;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.InListExpression;
 import org.apache.phoenix.expression.KeyValueColumnExpression;
-import org.apache.phoenix.expression.LikeExpression;
 import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.expression.NotExpression;
 import org.apache.phoenix.expression.OrExpression;
 import org.apache.phoenix.expression.RowKeyColumnExpression;
+import org.apache.phoenix.expression.StringBasedLikeExpression;
 import org.apache.phoenix.expression.function.SubstrFunction;
 import org.apache.phoenix.filter.MultiCQKeyValueComparisonFilter;
 import org.apache.phoenix.filter.MultiKeyValueComparisonFilter;
@@ -77,6 +78,8 @@ import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
 import org.apache.phoenix.parse.LikeParseNode.LikeType;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.RowKeyValueAccessor;
 import org.apache.phoenix.schema.TableRef;
@@ -264,13 +267,26 @@ public class TestUtil {
         return  new ComparisonExpression(Arrays.asList(e, LiteralExpression.newConstant(o)), op);
     }
 
-    public static Expression like(Expression e, Object o) {
-        return LikeExpression.create(Arrays.asList(e, LiteralExpression.newConstant(o)), LikeType.CASE_SENSITIVE);
+    private static boolean useByteBasedRegex(StatementContext context) {
+        return context
+                .getConnection()
+                .getQueryServices()
+                .getProps()
+                .getBoolean(QueryServices.USE_BYTE_BASED_REGEX_ATTRIB,
+                    QueryServicesOptions.DEFAULT_USE_BYTE_BASED_REGEX);
     }
 
-    public static Expression ilike(Expression e, Object o) {
-      return LikeExpression.create(Arrays.asList(e, LiteralExpression.newConstant(o)), LikeType.CASE_INSENSITIVE);
-  }
+    public static Expression like(Expression e, Object o, StatementContext context) {
+        return useByteBasedRegex(context)?
+               ByteBasedLikeExpression.create(Arrays.asList(e, LiteralExpression.newConstant(o)), LikeType.CASE_SENSITIVE):
+               StringBasedLikeExpression.create(Arrays.asList(e, LiteralExpression.newConstant(o)), LikeType.CASE_SENSITIVE);
+    }
+
+    public static Expression ilike(Expression e, Object o, StatementContext context) {
+        return useByteBasedRegex(context)?
+                ByteBasedLikeExpression.create(Arrays.asList(e, LiteralExpression.newConstant(o)), LikeType.CASE_INSENSITIVE):
+                StringBasedLikeExpression.create(Arrays.asList(e, LiteralExpression.newConstant(o)), LikeType.CASE_INSENSITIVE);
+    }
 
     public static Expression substr(Expression e, Object offset, Object length) {
         return  new SubstrFunction(Arrays.asList(e, LiteralExpression.newConstant(offset), LiteralExpression.newConstant(length)));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4793cf2..c2ff589 100644
--- a/pom.xml
+++ b/pom.xml
@@ -105,6 +105,7 @@
     <htrace.version>3.1.0-incubating</htrace.version>
     <collections.version>3.2.1</collections.version>
     <jodatime.version>2.7</jodatime.version>
+    <joni.version>2.1.2</joni.version>
 
     <!-- Test Dependencies -->
     <mockito-all.version>1.8.5</mockito-all.version>


[3/3] phoenix git commit: PHOENIX-1287 Use the joni byte[] regex engine in place of j.u.regex (Shuxiong Ye)

Posted by ja...@apache.org.
PHOENIX-1287 Use the joni byte[] regex engine in place of j.u.regex (Shuxiong Ye)


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

Branch: refs/heads/master
Commit: 3f6b25947d07ea0d7756556dd80e951f12ceda69
Parents: 7ef1718
Author: James Taylor <jt...@salesforce.com>
Authored: Tue Apr 14 12:09:17 2015 -0700
Committer: James Taylor <jt...@salesforce.com>
Committed: Tue Apr 14 12:09:17 2015 -0700

----------------------------------------------------------------------
 .../src/build/components-major-client.xml       |   2 +
 phoenix-core/pom.xml                            |   5 +
 .../phoenix/end2end/LikeExpressionIT.java       |  88 ++++++++
 .../end2end/RegexpReplaceFunctionIT.java        | 100 +++++++++
 .../phoenix/end2end/RegexpSubstrFunctionIT.java |  43 ++--
 .../phoenix/compile/ExpressionCompiler.java     |  15 +-
 .../expression/ByteBasedLikeExpression.java     |  48 +++++
 .../phoenix/expression/ExpressionType.java      |  16 +-
 .../phoenix/expression/LikeExpression.java      |  64 +++---
 .../expression/StringBasedLikeExpression.java   |  48 +++++
 .../ByteBasedRegexpReplaceFunction.java         |  40 ++++
 .../function/ByteBasedRegexpSplitFunction.java  |  38 ++++
 .../function/ByteBasedRegexpSubstrFunction.java |  38 ++++
 .../function/RegexpReplaceFunction.java         |  38 ++--
 .../function/RegexpSplitFunction.java           |  54 +++--
 .../function/RegexpSubstrFunction.java          |  48 ++---
 .../StringBasedRegexpReplaceFunction.java       |  40 ++++
 .../StringBasedRegexpSplitFunction.java         |  38 ++++
 .../StringBasedRegexpSubstrFunction.java        |  38 ++++
 .../util/regex/AbstractBasePattern.java         |  33 +++
 .../util/regex/AbstractBaseSplitter.java        |  24 +++
 .../expression/util/regex/GuavaSplitter.java    |  54 +++++
 .../expression/util/regex/JONIPattern.java      | 201 +++++++++++++++++++
 .../expression/util/regex/JavaPattern.java      |  93 +++++++++
 .../visitor/CloneExpressionVisitor.java         |   3 +-
 .../phoenix/parse/RegexpReplaceParseNode.java   |  55 +++++
 .../phoenix/parse/RegexpSplitParseNode.java     |  55 +++++
 .../phoenix/parse/RegexpSubstrParseNode.java    |  55 +++++
 .../org/apache/phoenix/query/QueryServices.java |   2 +
 .../phoenix/query/QueryServicesOptions.java     |  14 +-
 .../phoenix/schema/types/PArrayDataType.java    |  91 +++++++++
 .../org/apache/phoenix/util/StringUtil.java     |  68 ++++++-
 .../phoenix/compile/WhereOptimizerTest.java     |  18 +-
 .../phoenix/expression/ILikeExpressionTest.java |  32 ++-
 .../phoenix/expression/LikeExpressionTest.java  |  39 +++-
 .../expression/RegexpReplaceFunctionTest.java   |  81 ++++++++
 .../expression/RegexpSplitFunctionTest.java     |  94 +++++++++
 .../expression/RegexpSubstrFunctionTest.java    |  83 ++++++++
 .../expression/SortOrderExpressionTest.java     |  12 +-
 .../util/regex/PatternPerformanceTest.java      | 144 +++++++++++++
 .../org/apache/phoenix/util/StringUtilTest.java |  32 ++-
 .../java/org/apache/phoenix/util/TestUtil.java  |  28 ++-
 pom.xml                                         |   1 +
 43 files changed, 1952 insertions(+), 161 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-assembly/src/build/components-major-client.xml
----------------------------------------------------------------------
diff --git a/phoenix-assembly/src/build/components-major-client.xml b/phoenix-assembly/src/build/components-major-client.xml
index 768cac0..7a2909b 100644
--- a/phoenix-assembly/src/build/components-major-client.xml
+++ b/phoenix-assembly/src/build/components-major-client.xml
@@ -49,6 +49,8 @@
         <include>org.codehaus.jackson:jackson-core-asl</include>
         <include>commons-collections:commons-collections</include>
         <include>joda-time:joda-time</include>
+        <include>org.jruby.joni:joni</include>
+        <include>org.jruby.jcodings:jcodings</include>
       </includes>
     </dependencySet>
   </dependencySets>

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/pom.xml
----------------------------------------------------------------------
diff --git a/phoenix-core/pom.xml b/phoenix-core/pom.xml
index 5e0aff7..45b8d73 100644
--- a/phoenix-core/pom.xml
+++ b/phoenix-core/pom.xml
@@ -417,5 +417,10 @@
       <groupId>org.apache.hadoop</groupId>
       <artifactId>hadoop-minicluster</artifactId>
     </dependency>
+    <dependency>
+        <groupId>org.jruby.joni</groupId>
+        <artifactId>joni</artifactId>
+        <version>${joni.version}</version>
+    </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/it/java/org/apache/phoenix/end2end/LikeExpressionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/LikeExpressionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LikeExpressionIT.java
new file mode 100644
index 0000000..1ee0669
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LikeExpressionIT.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.end2end;
+
+import static org.apache.phoenix.util.TestUtil.closeStmtAndConn;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class LikeExpressionIT extends BaseHBaseManagedTimeIT {
+    @Before
+    public void doBeforeTestSetup() throws Exception {
+        Connection conn = null;
+        PreparedStatement stmt = null;
+        try {
+            conn = DriverManager.getConnection(getUrl());
+            String ddl;
+            ddl = "CREATE TABLE testTable (k VARCHAR NOT NULL PRIMARY KEY, i INTEGER)";
+            conn.createStatement().execute(ddl);
+            conn.commit();
+        } finally {
+            closeStmtAndConn(stmt, conn);
+        }
+        insertRow(conn, "123n7-app-2-", 1);
+        insertRow(conn, "132n7-App-2-", 2);
+        insertRow(conn, "213n7-app-2-", 4);
+        insertRow(conn, "231n7-App-2-", 8);
+        insertRow(conn, "312n7-app-2-", 16);
+        insertRow(conn, "321n7-App-2-", 32);
+    }
+
+    private void insertRow(Connection conn, String k, int i) throws SQLException {
+        PreparedStatement stmt = conn.prepareStatement("UPSERT INTO testTable VALUES (?, ?)");
+        stmt.setString(1, k);
+        stmt.setInt(2, i);
+        stmt.executeUpdate();
+        conn.commit();
+    }
+
+    private void testLikeExpression(Connection conn, String likeStr, int numResult, int expectedSum)
+            throws Exception {
+        String cmd = "select k, i from testTable where k like '" + likeStr + "'";
+        Statement stmt = conn.createStatement();
+        ResultSet rs = stmt.executeQuery(cmd);
+        int sum = 0;
+        for (int i = 0; i < numResult; ++i) {
+            assertTrue(rs.next());
+            sum += rs.getInt("i");
+        }
+        assertFalse(rs.next());
+        assertEquals(sum, expectedSum);
+    }
+
+    @Test
+    public void testLikeExpression() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        // wildcard
+        testLikeExpression(conn, "%1%3%7%2%", 3, 7);
+        // CaseSensitive
+        testLikeExpression(conn, "%A%", 3, 42);
+        conn.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpReplaceFunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpReplaceFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpReplaceFunctionIT.java
new file mode 100644
index 0000000..dcc20ff
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpReplaceFunctionIT.java
@@ -0,0 +1,100 @@
+/*
+ * 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.apache.phoenix.util.TestUtil.GROUPBYTEST_NAME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class RegexpReplaceFunctionIT extends BaseHBaseManagedTimeIT {
+
+    private int id;
+
+    @Before
+    public void doBeforeTestSetup() throws Exception {
+        ensureTableCreated(getUrl(), GROUPBYTEST_NAME);
+        Connection conn = DriverManager.getConnection(getUrl());
+        insertRow(conn, "Report11", 10);
+        insertRow(conn, "Report11", 10);
+        insertRow(conn, "Report22", 30);
+        insertRow(conn, "Report33", 30);
+        conn.commit();
+        conn.close();
+    }
+
+    private void insertRow(Connection conn, String uri, int appcpu) throws SQLException {
+        PreparedStatement statement = conn.prepareStatement("UPSERT INTO " + GROUPBYTEST_NAME + "(id, uri, appcpu) values (?,?,?)");
+        statement.setString(1, "id" + id);
+        statement.setString(2, uri);
+        statement.setInt(3, appcpu);
+        statement.executeUpdate();
+        id++;
+    }
+
+    @Test
+    public void testGroupByScanWithRegexpReplace() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        Statement stmt = conn.createStatement();
+        ResultSet rs = stmt.executeQuery("select REGEXP_REPLACE(uri, '[1-3]+', '*') suburi, sum(appcpu) sumcpu from " + GROUPBYTEST_NAME + " group by suburi");
+        assertTrue(rs.next());
+        assertEquals(rs.getString("suburi"), "Report*");
+        assertEquals(rs.getInt("sumcpu"), 80);
+        assertFalse(rs.next());
+
+        stmt = conn.createStatement();
+        rs = stmt.executeQuery("select REGEXP_REPLACE(uri, '[1-3]+') suburi, sum(appcpu) sumcpu from " + GROUPBYTEST_NAME + " group by suburi");
+        assertTrue(rs.next());
+        assertEquals(rs.getString("suburi"), "Report");
+        assertEquals(rs.getInt("sumcpu"), 80);
+        assertFalse(rs.next());
+
+        conn.close();
+    }
+
+    @Test
+    public void testFilterWithRegexReplace() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        ResultSet rs = conn.createStatement().executeQuery("select id from " + GROUPBYTEST_NAME + " where REGEXP_REPLACE(uri, '[2-3]+', '*') = 'Report*'");
+        assertTrue(rs.next());
+        assertEquals("id2", rs.getString(1));
+        assertTrue(rs.next());
+        assertEquals("id3", rs.getString(1));
+        assertFalse(rs.next());
+
+        rs = conn.createStatement().executeQuery("select id from " + GROUPBYTEST_NAME + " where REGEXP_REPLACE(uri, '[2-3]+') = 'Report'");
+        assertTrue(rs.next());
+        assertEquals("id2", rs.getString(1));
+        assertTrue(rs.next());
+        assertEquals("id3", rs.getString(1));
+        assertFalse(rs.next());
+        conn.close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpSubstrFunctionIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpSubstrFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpSubstrFunctionIT.java
index ff4b95e..938fd5d 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpSubstrFunctionIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RegexpSubstrFunctionIT.java
@@ -58,30 +58,37 @@ public class RegexpSubstrFunctionIT extends BaseHBaseManagedTimeIT {
         id++;
     }
 
-    @Test
-    public void testGroupByScanWithRegexpSubstr() throws Exception {
-        Connection conn = DriverManager.getConnection(getUrl());
+    private void testGroupByScanWithRegexpSubstr(Connection conn, Integer offset, String exceptedSubstr) throws Exception {
+        String cmd = "select REGEXP_SUBSTR(uri, '[^\\\\?]+'" + ((offset == null) ? "" : ", " + offset.intValue()) +") suburi, sum(appcpu) sumcpu from " + GROUPBYTEST_NAME + " group by suburi";
         Statement stmt = conn.createStatement();
-        ResultSet rs = stmt.executeQuery("select REGEXP_SUBSTR(uri, '[^\\\\?]+') suburi, sum(appcpu) sumcpu from " + GROUPBYTEST_NAME
-            + " group by suburi");
+        ResultSet rs = stmt.executeQuery(cmd);
         assertTrue(rs.next());
-        assertEquals(rs.getString("suburi"), "Report1");
+        assertEquals(rs.getString("suburi"), exceptedSubstr + "1");
         assertEquals(rs.getInt("sumcpu"), 20);
         assertTrue(rs.next());
-        assertEquals(rs.getString("suburi"), "Report2");
+        assertEquals(rs.getString("suburi"), exceptedSubstr + "2");
         assertEquals(rs.getInt("sumcpu"), 30);
         assertTrue(rs.next());
-        assertEquals(rs.getString("suburi"), "Report3");
+        assertEquals(rs.getString("suburi"), exceptedSubstr + "3");
         assertEquals(rs.getInt("sumcpu"), 30);
         assertFalse(rs.next());
-        conn.close();
     }
 
     @Test
-    public void testFilterWithRegexSubstr() throws Exception {
+    public void testGroupByScanWithRegexpSubstr() throws Exception {
         Connection conn = DriverManager.getConnection(getUrl());
-        ResultSet rs = conn.createStatement().executeQuery(
-                "select id from " + GROUPBYTEST_NAME + " where REGEXP_SUBSTR(uri, '[^\\\\?]+') = 'Report1'");
+        // Default offset
+        testGroupByScanWithRegexpSubstr(conn, null, "Report");
+        // Positive offset
+        testGroupByScanWithRegexpSubstr(conn, Integer.valueOf(2), "eport");
+        // Negative offset
+        testGroupByScanWithRegexpSubstr(conn, Integer.valueOf(-5), "rt");
+        conn.close();
+    }
+
+    private void testFilterWithRegexSubstr(Connection conn, Integer offset, String exceptedSubstr) throws Exception {
+        String cmd = "select id from " + GROUPBYTEST_NAME + " where REGEXP_SUBSTR(uri, '[^\\\\?]+'"+ ((offset == null) ? "" : ", " + offset.intValue()) +") = '" + exceptedSubstr + "1'";
+        ResultSet rs = conn.createStatement().executeQuery(cmd);
         assertTrue(rs.next());
         assertEquals("id0", rs.getString(1));
         assertTrue(rs.next());
@@ -89,4 +96,16 @@ public class RegexpSubstrFunctionIT extends BaseHBaseManagedTimeIT {
         assertFalse(rs.next());
     }
 
+    @Test
+    public void testFilterWithRegexSubstr() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        // Default offset
+        testFilterWithRegexSubstr(conn, null, "Report");
+        // Positive offset
+        testFilterWithRegexSubstr(conn, Integer.valueOf(2), "eport");
+        // Negative offset
+        testFilterWithRegexSubstr(conn, Integer.valueOf(-5), "rt");
+        conn.close();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
index 52c67f1..ce95850 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java
@@ -32,6 +32,7 @@ import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.exception.SQLExceptionInfo;
 import org.apache.phoenix.expression.AndExpression;
 import org.apache.phoenix.expression.ArrayConstructorExpression;
+import org.apache.phoenix.expression.ByteBasedLikeExpression;
 import org.apache.phoenix.expression.CaseExpression;
 import org.apache.phoenix.expression.CoerceExpression;
 import org.apache.phoenix.expression.ComparisonExpression;
@@ -60,6 +61,7 @@ import org.apache.phoenix.expression.NotExpression;
 import org.apache.phoenix.expression.OrExpression;
 import org.apache.phoenix.expression.RowKeyColumnExpression;
 import org.apache.phoenix.expression.RowValueConstructorExpression;
+import org.apache.phoenix.expression.StringBasedLikeExpression;
 import org.apache.phoenix.expression.StringConcatExpression;
 import org.apache.phoenix.expression.TimestampAddExpression;
 import org.apache.phoenix.expression.TimestampSubtractExpression;
@@ -100,6 +102,8 @@ import org.apache.phoenix.parse.StringConcatParseNode;
 import org.apache.phoenix.parse.SubqueryParseNode;
 import org.apache.phoenix.parse.SubtractParseNode;
 import org.apache.phoenix.parse.UnsupportedAllParseNodeVisitor;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
 import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
 import org.apache.phoenix.schema.ColumnRef;
@@ -497,7 +501,16 @@ public class ExpressionCompiler extends UnsupportedAllParseNodeVisitor<Expressio
                 }
             }
         }
-        Expression expression = LikeExpression.create(children, node.getLikeType());
+        QueryServices services = context.getConnection().getQueryServices();
+        boolean useByteBasedRegex =
+                services.getProps().getBoolean(QueryServices.USE_BYTE_BASED_REGEX_ATTRIB,
+                    QueryServicesOptions.DEFAULT_USE_BYTE_BASED_REGEX);
+        Expression expression;
+        if (useByteBasedRegex) {
+            expression = ByteBasedLikeExpression.create(children, node.getLikeType());
+        } else {
+            expression = StringBasedLikeExpression.create(children, node.getLikeType());
+        }
         if (ExpressionUtil.isConstant(expression)) {
             ImmutableBytesWritable ptr = context.getTempPtr();
             if (!expression.evaluate(null, ptr)) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/ByteBasedLikeExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/ByteBasedLikeExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/ByteBasedLikeExpression.java
new file mode 100644
index 0000000..4dd4f70
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ByteBasedLikeExpression.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.List;
+
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
+import org.apache.phoenix.expression.util.regex.JONIPattern;
+import org.apache.phoenix.parse.LikeParseNode.LikeType;
+
+public class ByteBasedLikeExpression extends LikeExpression {
+
+    public ByteBasedLikeExpression() {
+    }
+
+    public ByteBasedLikeExpression(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBasePattern compilePatternSpec(String value) {
+        return new JONIPattern(value);
+    }
+
+    public static LikeExpression create(List<Expression> children, LikeType likeType) {
+        return new ByteBasedLikeExpression(addLikeTypeChild(children, likeType));
+    }
+
+    @Override
+    public LikeExpression clone(List<Expression> children) {
+        return new ByteBasedLikeExpression(children);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/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 22778ce..5f598b9 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,9 @@ 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.ByteBasedRegexpReplaceFunction;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction;
 import org.apache.phoenix.expression.function.CeilDateExpression;
 import org.apache.phoenix.expression.function.CeilDecimalExpression;
 import org.apache.phoenix.expression.function.CeilFunction;
@@ -79,6 +82,9 @@ import org.apache.phoenix.expression.function.SignFunction;
 import org.apache.phoenix.expression.function.SqlTypeNameFunction;
 import org.apache.phoenix.expression.function.StddevPopFunction;
 import org.apache.phoenix.expression.function.StddevSampFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpReplaceFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSplitFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSubstrFunction;
 import org.apache.phoenix.expression.function.SubstrFunction;
 import org.apache.phoenix.expression.function.SumAggregateFunction;
 import org.apache.phoenix.expression.function.TimezoneOffsetFunction;
@@ -137,6 +143,8 @@ public enum ExpressionType {
     MinAggregateFunction(MinAggregateFunction.class),
     MaxAggregateFunction(MaxAggregateFunction.class),
     LikeExpression(LikeExpression.class),
+    ByteBasedLikeExpression(ByteBasedLikeExpression.class),
+    StringBasedLikeExpression(StringBasedLikeExpression.class),
     NotExpression(NotExpression.class),
     CaseExpression(CaseExpression.class),
     InListExpression(InListExpression.class),
@@ -153,8 +161,12 @@ public enum ExpressionType {
     DecimalDivideExpression(DecimalDivideExpression.class),
     CoalesceFunction(CoalesceFunction.class),
     RegexpReplaceFunction(RegexpReplaceFunction.class),
+    ByteBasedRegexpReplaceFunction(ByteBasedRegexpReplaceFunction.class),
+    StringBasedRegexpReplaceFunction(StringBasedRegexpReplaceFunction.class),
     SQLTypeNameFunction(SqlTypeNameFunction.class),
     RegexpSubstrFunction(RegexpSubstrFunction.class),
+    ByteBasedRegexpSubstrFunction(ByteBasedRegexpSubstrFunction.class),
+    StringBasedRegexpSubstrFunction(StringBasedRegexpSubstrFunction.class),
     StringConcatExpression(StringConcatExpression.class),
     LengthFunction(LengthFunction.class),
     LTrimFunction(LTrimFunction.class),
@@ -199,7 +211,9 @@ public enum ExpressionType {
     SQLIndexTypeFunction(SQLIndexTypeFunction.class),
     ModulusExpression(ModulusExpression.class),
     DistinctValueAggregateFunction(DistinctValueAggregateFunction.class),
-    RegexpSplitFunctiond(RegexpSplitFunction.class),
+    RegexpSplitFunction(RegexpSplitFunction.class),
+    ByteBasedRegexpSplitFunction(ByteBasedRegexpSplitFunction.class),
+    StringBasedRegexpSplitFunction(StringBasedRegexpSplitFunction.class),
     RandomFunction(RandomFunction.class),
     ToTimeFunction(ToTimeFunction.class),
     ToTimestampFunction(ToTimestampFunction.class),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/LikeExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/LikeExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/LikeExpression.java
index 730cffb..52ac969 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/LikeExpression.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/LikeExpression.java
@@ -21,11 +21,12 @@ import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
 import java.util.List;
-import java.util.regex.Pattern;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
 import org.apache.phoenix.expression.visitor.ExpressionVisitor;
 import org.apache.phoenix.parse.LikeParseNode.LikeType;
+import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.schema.types.PBoolean;
 import org.apache.phoenix.schema.types.PDataType;
@@ -49,7 +50,7 @@ import com.google.common.collect.Lists;
  * 
  * @since 0.1
  */
-public class LikeExpression extends BaseCompoundExpression {
+public abstract class LikeExpression extends BaseCompoundExpression {
     private static final Logger logger = LoggerFactory.getLogger(LikeExpression.class);
 
     private static final String ZERO_OR_MORE = "\\E.*\\Q";
@@ -195,10 +196,6 @@ public class LikeExpression extends BaseCompoundExpression {
 //        return sb.toString();
 //    }
 
-    public static LikeExpression create(List<Expression> children, LikeType likeType) {
-        return new LikeExpression(addLikeTypeChild(children,likeType));
-    }
-    
     private static final int LIKE_TYPE_INDEX = 2;
     private static final LiteralExpression[] LIKE_TYPE_LITERAL = new LiteralExpression[LikeType.values().length];
     static {
@@ -206,12 +203,12 @@ public class LikeExpression extends BaseCompoundExpression {
             LIKE_TYPE_LITERAL[likeType.ordinal()] = LiteralExpression.newConstant(likeType.name());
         }
     }
-    private Pattern pattern;
+    private AbstractBasePattern pattern;
 
     public LikeExpression() {
     }
 
-    private static List<Expression> addLikeTypeChild(List<Expression> children, LikeType likeType) {
+    protected static List<Expression> addLikeTypeChild(List<Expression> children, LikeType likeType) {
         List<Expression> newChildren = Lists.newArrayListWithExpectedSize(children.size()+1);
         newChildren.addAll(children);
         newChildren.add(LIKE_TYPE_LITERAL[likeType.ordinal()]);
@@ -247,11 +244,14 @@ public class LikeExpression extends BaseCompoundExpression {
         }
     }
 
-    protected Pattern compilePattern (String value) {
-        if (likeType == LikeType.CASE_SENSITIVE)
-            return Pattern.compile(toPattern(value));
-        else
-            return Pattern.compile("(?i)" + toPattern(value));
+    protected abstract AbstractBasePattern compilePatternSpec(String value);
+
+    protected AbstractBasePattern compilePattern(String value) {
+        if (likeType == LikeType.CASE_SENSITIVE) {
+            return compilePatternSpec(toPattern(value));
+        } else {
+            return compilePatternSpec("(?i)" + toPattern(value));
+        }
     }
 
     private Expression getStrExpression() {
@@ -264,36 +264,40 @@ public class LikeExpression extends BaseCompoundExpression {
 
     @Override
     public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) {
-        Pattern pattern = this.pattern;
+        AbstractBasePattern pattern = this.pattern;
         if (pattern == null) { // TODO: don't allow? this is going to be slooowwww
             if (!getPatternExpression().evaluate(tuple, ptr)) {
-                if (logger.isDebugEnabled()) {
-                    logger.debug("LIKE is FALSE: pattern is null");
+                if (logger.isTraceEnabled()) {
+                    logger.trace("LIKE is FALSE: pattern is null");
                 }
                 return false;
             }
             String value = (String) PVarchar.INSTANCE.toObject(ptr, getPatternExpression().getSortOrder());
             pattern = compilePattern(value);
-            if (logger.isDebugEnabled()) {
-                logger.debug("LIKE pattern is expression: " + pattern.pattern());
+            if (logger.isTraceEnabled()) {
+                logger.trace("LIKE pattern is expression: " + pattern.pattern());
             }
         }
 
-        if (!getStrExpression().evaluate(tuple, ptr)) {
-            if (logger.isDebugEnabled()) {
-                logger.debug("LIKE is FALSE: child expression is null");
+        Expression strExpression = getStrExpression();
+        SortOrder strSortOrder = strExpression.getSortOrder();
+        PVarchar strDataType = PVarchar.INSTANCE;
+        if (!strExpression.evaluate(tuple, ptr)) {
+            if (logger.isTraceEnabled()) {
+                logger.trace("LIKE is FALSE: child expression is null");
             }
             return false;
         }
-        if (ptr.getLength() == 0) {
-            return true;
-        }
 
-        String value = (String) PVarchar.INSTANCE.toObject(ptr, getStrExpression().getSortOrder());
-        boolean matched = pattern.matcher(value).matches();
-        ptr.set(matched ? PDataType.TRUE_BYTES : PDataType.FALSE_BYTES);
-        if (logger.isDebugEnabled()) {
-            logger.debug("LIKE(value='" + value + "'pattern='" + pattern.pattern() + "' is " + matched);
+        String value = null;
+        if (logger.isTraceEnabled()) {
+            value = (String) strDataType.toObject(ptr, strSortOrder);
+        }
+        strDataType.coerceBytes(ptr, strDataType, strSortOrder, SortOrder.ASC);
+        pattern.matches(ptr, ptr);
+        if (logger.isTraceEnabled()) {
+            boolean matched = ((Boolean) PBoolean.INSTANCE.toObject(ptr)).booleanValue();
+            logger.trace("LIKE(value='" + value + "'pattern='" + pattern.pattern() + "' is " + matched);
         }
         return true;
     }
@@ -348,4 +352,6 @@ public class LikeExpression extends BaseCompoundExpression {
     public String toString() {
         return (children.get(0) + " LIKE " + children.get(1));
     }
+
+    abstract public LikeExpression clone(List<Expression> children);
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/StringBasedLikeExpression.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/StringBasedLikeExpression.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/StringBasedLikeExpression.java
new file mode 100644
index 0000000..e2afea2
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/StringBasedLikeExpression.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.List;
+
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
+import org.apache.phoenix.expression.util.regex.JavaPattern;
+import org.apache.phoenix.parse.LikeParseNode.LikeType;
+
+public class StringBasedLikeExpression extends LikeExpression {
+
+    public StringBasedLikeExpression() {
+    }
+
+    public StringBasedLikeExpression(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBasePattern compilePatternSpec(String value) {
+        return new JavaPattern(value);
+    }
+
+    public static LikeExpression create(List<Expression> children, LikeType likeType) {
+        return new StringBasedLikeExpression(addLikeTypeChild(children, likeType));
+    }
+
+    @Override
+    public LikeExpression clone(List<Expression> children) {
+        return new StringBasedLikeExpression(children);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpReplaceFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpReplaceFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpReplaceFunction.java
new file mode 100644
index 0000000..0d6543c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpReplaceFunction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.phoenix.expression.Expression;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
+import org.apache.phoenix.expression.util.regex.JONIPattern;
+
+public class ByteBasedRegexpReplaceFunction extends RegexpReplaceFunction {
+
+    public ByteBasedRegexpReplaceFunction() {
+    }
+
+    public ByteBasedRegexpReplaceFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBasePattern compilePatternSpec(String value) {
+        return new JONIPattern(value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSplitFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSplitFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSplitFunction.java
new file mode 100644
index 0000000..062713e
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSplitFunction.java
@@ -0,0 +1,38 @@
+/*
+ * 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.phoenix.expression.Expression;
+import org.apache.phoenix.expression.util.regex.AbstractBaseSplitter;
+import org.apache.phoenix.expression.util.regex.JONIPattern;
+
+public class ByteBasedRegexpSplitFunction extends RegexpSplitFunction {
+    public ByteBasedRegexpSplitFunction() {
+    }
+
+    public ByteBasedRegexpSplitFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBaseSplitter compilePatternSpec(String value) {
+        return new JONIPattern(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSubstrFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSubstrFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSubstrFunction.java
new file mode 100644
index 0000000..7ee99bf
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ByteBasedRegexpSubstrFunction.java
@@ -0,0 +1,38 @@
+/*
+ * 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.phoenix.expression.Expression;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
+import org.apache.phoenix.expression.util.regex.JONIPattern;
+
+public class ByteBasedRegexpSubstrFunction extends RegexpSubstrFunction {
+    public ByteBasedRegexpSubstrFunction() {
+    }
+
+    public ByteBasedRegexpSubstrFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBasePattern compilePatternSpec(String value) {
+        return new JONIPattern(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpReplaceFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpReplaceFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpReplaceFunction.java
index 3f470a9..f22c978 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpReplaceFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpReplaceFunction.java
@@ -20,17 +20,18 @@ package org.apache.phoenix.expression.function;
 import java.io.DataInput;
 import java.io.IOException;
 import java.util.List;
-import java.util.regex.Pattern;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
-
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
 import org.apache.phoenix.parse.FunctionParseNode.Argument;
 import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
+import org.apache.phoenix.parse.RegexpReplaceParseNode;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PVarchar;
-import org.apache.phoenix.schema.tuple.Tuple;
 
 
 /**
@@ -48,15 +49,16 @@ import org.apache.phoenix.schema.tuple.Tuple;
  * 
  * @since 0.1
  */
-@BuiltInFunction(name=RegexpReplaceFunction.NAME, args= {
+@BuiltInFunction(name=RegexpReplaceFunction.NAME,
+    nodeClass = RegexpReplaceParseNode.class, args= {
     @Argument(allowedTypes={PVarchar.class}),
     @Argument(allowedTypes={PVarchar.class}),
     @Argument(allowedTypes={PVarchar.class},defaultValue="null")} )
-public class RegexpReplaceFunction extends ScalarFunction {
+public abstract class RegexpReplaceFunction extends ScalarFunction {
     public static final String NAME = "REGEXP_REPLACE";
 
     private boolean hasReplaceStr;
-    private Pattern pattern;
+    private AbstractBasePattern pattern;
     
     public RegexpReplaceFunction() { }
 
@@ -66,11 +68,13 @@ public class RegexpReplaceFunction extends ScalarFunction {
         init();
     }
 
+    protected abstract AbstractBasePattern compilePatternSpec(String value);
+
     private void init() {
         hasReplaceStr = ((LiteralExpression)getReplaceStrExpression()).getValue() != null;
         Object patternString = ((LiteralExpression)children.get(1)).getValue();
         if (patternString != null) {
-            pattern = Pattern.compile((String)patternString);
+            pattern = compilePatternSpec((String) patternString);
         }
     }
 
@@ -84,22 +88,20 @@ public class RegexpReplaceFunction extends ScalarFunction {
         if (!sourceStrExpression.evaluate(tuple, ptr)) {
             return false;
         }
-        String sourceStr = (String) PVarchar.INSTANCE.toObject(ptr, sourceStrExpression.getSortOrder());
-        if (sourceStr == null) {
-            return false;
-        }
-        String replaceStr;
+        if (ptr == null) return false;
+        PVarchar type = PVarchar.INSTANCE;
+        type.coerceBytes(ptr, type, sourceStrExpression.getSortOrder(), SortOrder.ASC);
+        ImmutableBytesWritable replacePtr = new ImmutableBytesWritable();
         if (hasReplaceStr) {
-            Expression replaceStrExpression = this.getReplaceStrExpression();
-            if (!replaceStrExpression.evaluate(tuple, ptr)) {
+            Expression replaceStrExpression = getReplaceStrExpression();
+            if (!replaceStrExpression.evaluate(tuple, replacePtr)) {
                 return false;
             }
-            replaceStr = (String) PVarchar.INSTANCE.toObject(ptr, replaceStrExpression.getSortOrder());
+            type.coerceBytes(replacePtr, type, replaceStrExpression.getSortOrder(), SortOrder.ASC);
         } else {
-            replaceStr = "";
+            replacePtr.set(type.toBytes(""));
         }
-        String replacedStr = pattern.matcher(sourceStr).replaceAll(replaceStr);
-        ptr.set(PVarchar.INSTANCE.toBytes(replacedStr));
+        pattern.replaceAll(ptr, replacePtr, ptr);
         return true;
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSplitFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSplitFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSplitFunction.java
index 89c7c9e..b43dec9 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSplitFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSplitFunction.java
@@ -24,17 +24,16 @@ import java.util.List;
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.util.regex.AbstractBaseSplitter;
 import org.apache.phoenix.parse.FunctionParseNode;
+import org.apache.phoenix.parse.RegexpSplitParseNode;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PVarchar;
 import org.apache.phoenix.schema.types.PVarcharArray;
-import org.apache.phoenix.schema.types.PhoenixArray;
-import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.ByteUtil;
 
-import com.google.common.base.Splitter;
-import com.google.common.collect.Lists;
-
 /**
  * Function to split a string value into a {@code VARCHAR_ARRAY}.
  * <p>
@@ -46,14 +45,15 @@ import com.google.common.collect.Lists;
  *
  * The function returns a {@link org.apache.phoenix.schema.types.PVarcharArray}
  */
- @FunctionParseNode.BuiltInFunction(name=RegexpSplitFunction.NAME, args= {
+ @FunctionParseNode.BuiltInFunction(name=RegexpSplitFunction.NAME,
+        nodeClass = RegexpSplitParseNode.class, args= {
         @FunctionParseNode.Argument(allowedTypes={PVarchar.class}),
         @FunctionParseNode.Argument(allowedTypes={PVarchar.class})})
-public class RegexpSplitFunction extends ScalarFunction {
+public abstract class RegexpSplitFunction extends ScalarFunction {
 
     public static final String NAME = "REGEXP_SPLIT";
 
-    private Splitter initializedSplitter = null;
+    private AbstractBaseSplitter initializedSplitter = null;
 
     public RegexpSplitFunction() {}
 
@@ -67,11 +67,13 @@ public class RegexpSplitFunction extends ScalarFunction {
         if (patternExpression instanceof LiteralExpression) {
             Object patternValue = ((LiteralExpression) patternExpression).getValue();
             if (patternValue != null) {
-                initializedSplitter = Splitter.onPattern(patternValue.toString());
+                initializedSplitter = compilePatternSpec(patternValue.toString());
             }
         }
     }
 
+    protected abstract AbstractBaseSplitter compilePatternSpec(String value);
+
     @Override
     public void readFields(DataInput input) throws IOException {
         super.readFields(input);
@@ -90,38 +92,28 @@ public class RegexpSplitFunction extends ScalarFunction {
         }
 
         Expression sourceStrExpression = children.get(0);
-        String sourceStr = (String) PVarchar.INSTANCE.toObject(ptr, sourceStrExpression.getSortOrder());
-        if (sourceStr == null) { // sourceStr evaluated to null
-            ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
-            return true;
-        }
+        PVarchar type = PVarchar.INSTANCE;
+        type.coerceBytes(ptr, type, sourceStrExpression.getSortOrder(), SortOrder.ASC);
 
-        return split(tuple, ptr, sourceStr);
-    }
-
-    private boolean split(Tuple tuple, ImmutableBytesWritable ptr, String sourceStr) {
-        Splitter splitter = initializedSplitter;
+        AbstractBaseSplitter splitter = initializedSplitter;
         if (splitter == null) {
+            ImmutableBytesWritable tmpPtr = new ImmutableBytesWritable();
             Expression patternExpression = children.get(1);
-            if (!patternExpression.evaluate(tuple, ptr)) {
+            if (!patternExpression.evaluate(tuple, tmpPtr)) {
                 return false;
             }
-            if (ptr.getLength() == 0) {
-                return true; // ptr is already set to null
+            if (tmpPtr.getLength() == 0) {
+                ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+                return true; // set ptr to null
             }
-
-            String patternStr = (String) PVarchar.INSTANCE.toObject(
-                    ptr, patternExpression.getSortOrder());
-            splitter = Splitter.onPattern(patternStr);
+            String patternStr =
+                    (String) PVarchar.INSTANCE.toObject(tmpPtr, patternExpression.getSortOrder());
+            splitter = compilePatternSpec(patternStr);
         }
 
-        List<String> splitStrings = Lists.newArrayList(splitter.split(sourceStr));
-        PhoenixArray splitArray = new PhoenixArray(PVarchar.INSTANCE, splitStrings.toArray());
-        ptr.set(PVarcharArray.INSTANCE.toBytes(splitArray));
-        return true;
+        return splitter.split(ptr, ptr);
     }
 
-
     @Override
     public PDataType getDataType() {
         return PVarcharArray.INSTANCE;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java
index 93d8706..430b444 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/RegexpSubstrFunction.java
@@ -20,19 +20,19 @@ package org.apache.phoenix.expression.function;
 import java.io.DataInput;
 import java.io.IOException;
 import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
 import org.apache.phoenix.parse.FunctionParseNode.Argument;
 import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
-import org.apache.phoenix.schema.types.PLong;
+import org.apache.phoenix.parse.RegexpSubstrParseNode;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PLong;
 import org.apache.phoenix.schema.types.PVarchar;
-import org.apache.phoenix.schema.tuple.Tuple;
-import org.apache.phoenix.util.ByteUtil;
 
 
 /**
@@ -47,17 +47,20 @@ import org.apache.phoenix.util.ByteUtil;
  * 
  * @since 0.1
  */
-@BuiltInFunction(name=RegexpSubstrFunction.NAME, args={
+@BuiltInFunction(name=RegexpSubstrFunction.NAME,
+    nodeClass = RegexpSubstrParseNode.class, args={
     @Argument(allowedTypes={PVarchar.class}),
     @Argument(allowedTypes={PVarchar.class}),
     @Argument(allowedTypes={PLong.class}, defaultValue="1")} )
-public class RegexpSubstrFunction extends PrefixFunction {
+public abstract class RegexpSubstrFunction extends PrefixFunction {
     public static final String NAME = "REGEXP_SUBSTR";
 
-    private Pattern pattern;
+    private AbstractBasePattern pattern;
     private boolean isOffsetConstant;
     private Integer maxLength;
 
+    private static final PDataType TYPE = PVarchar.INSTANCE;
+
     public RegexpSubstrFunction() { }
 
     public RegexpSubstrFunction(List<Expression> children) {
@@ -65,10 +68,12 @@ public class RegexpSubstrFunction extends PrefixFunction {
         init();
     }
 
+    protected abstract AbstractBasePattern compilePatternSpec(String value);
+
     private void init() {
         Object patternString = ((LiteralExpression)children.get(1)).getValue();
         if (patternString != null) {
-            pattern = Pattern.compile((String)patternString);
+            pattern = compilePatternSpec((String) patternString);
         }
         // If the source string has a fixed width, then the max length would be the length 
         // of the source string minus the offset, or the absolute value of the offset if 
@@ -95,13 +100,11 @@ public class RegexpSubstrFunction extends PrefixFunction {
         if (pattern == null) {
             return false;
         }
-        if (!getSourceStrExpression().evaluate(tuple, ptr)) {
-            return false;
-        }
-        String sourceStr = (String) PVarchar.INSTANCE.toObject(ptr, getSourceStrExpression().getSortOrder());
-        if (sourceStr == null) {
+        ImmutableBytesWritable srcPtr = new ImmutableBytesWritable();
+        if (!getSourceStrExpression().evaluate(tuple, srcPtr)) {
             return false;
         }
+        TYPE.coerceBytes(srcPtr, TYPE, getSourceStrExpression().getSortOrder(), SortOrder.ASC);
 
         Expression offsetExpression = getOffsetExpression();
         if (!offsetExpression.evaluate(tuple, ptr)) {
@@ -109,25 +112,10 @@ public class RegexpSubstrFunction extends PrefixFunction {
         }
         int offset = offsetExpression.getDataType().getCodec().decodeInt(ptr, offsetExpression.getSortOrder());
 
-        int strlen = sourceStr.length();
         // Account for 1 versus 0-based offset
         offset = offset - (offset <= 0 ? 0 : 1);
-        if (offset < 0) { // Offset < 0 means get from end
-            offset = strlen + offset;
-        }
-        if (offset < 0 || offset >= strlen) {
-            return false;
-        }
 
-        Matcher matcher = pattern.matcher(sourceStr);
-        boolean hasSubString = matcher.find(offset);
-        if (!hasSubString) {
-            ptr.set(ByteUtil.EMPTY_BYTE_ARRAY);
-            return true;
-        }
-        String subString = matcher.group();
-        ptr.set(PVarchar.INSTANCE.toBytes(subString));
-        return true;
+        return pattern.substr(srcPtr, offset, ptr);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpReplaceFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpReplaceFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpReplaceFunction.java
new file mode 100644
index 0000000..9aaec70
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpReplaceFunction.java
@@ -0,0 +1,40 @@
+/*
+ * 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.phoenix.expression.Expression;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
+import org.apache.phoenix.expression.util.regex.JavaPattern;
+
+public class StringBasedRegexpReplaceFunction extends RegexpReplaceFunction {
+
+    public StringBasedRegexpReplaceFunction() {
+    }
+
+    public StringBasedRegexpReplaceFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBasePattern compilePatternSpec(String value) {
+        return new JavaPattern(value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSplitFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSplitFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSplitFunction.java
new file mode 100644
index 0000000..77321c2
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSplitFunction.java
@@ -0,0 +1,38 @@
+/*
+ * 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.phoenix.expression.Expression;
+import org.apache.phoenix.expression.util.regex.AbstractBaseSplitter;
+import org.apache.phoenix.expression.util.regex.GuavaSplitter;
+
+public class StringBasedRegexpSplitFunction extends RegexpSplitFunction {
+    public StringBasedRegexpSplitFunction() {
+    }
+
+    public StringBasedRegexpSplitFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBaseSplitter compilePatternSpec(String value) {
+        return new GuavaSplitter(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSubstrFunction.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSubstrFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSubstrFunction.java
new file mode 100644
index 0000000..253db36
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/StringBasedRegexpSubstrFunction.java
@@ -0,0 +1,38 @@
+/*
+ * 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.phoenix.expression.Expression;
+import org.apache.phoenix.expression.util.regex.AbstractBasePattern;
+import org.apache.phoenix.expression.util.regex.JavaPattern;
+
+public class StringBasedRegexpSubstrFunction extends RegexpSubstrFunction {
+    public StringBasedRegexpSubstrFunction() {
+    }
+
+    public StringBasedRegexpSubstrFunction(List<Expression> children) {
+        super(children);
+    }
+
+    @Override
+    protected AbstractBasePattern compilePatternSpec(String value) {
+        return new JavaPattern(value);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBasePattern.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBasePattern.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBasePattern.java
new file mode 100644
index 0000000..27b47a0
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBasePattern.java
@@ -0,0 +1,33 @@
+/*
+ * 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.util.regex;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+public abstract class AbstractBasePattern {
+
+    public abstract void matches(ImmutableBytesWritable srcPtr, ImmutableBytesWritable outPtr);
+
+    public abstract void replaceAll(ImmutableBytesWritable srcPtr,
+            ImmutableBytesWritable replacePtr, ImmutableBytesWritable outPtr);
+
+    public abstract boolean substr(ImmutableBytesWritable srcPtr, int offsetInStr,
+            ImmutableBytesWritable outPtr);
+
+    public abstract String pattern();
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBaseSplitter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBaseSplitter.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBaseSplitter.java
new file mode 100644
index 0000000..323eed0
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/AbstractBaseSplitter.java
@@ -0,0 +1,24 @@
+/*
+ * 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.util.regex;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+
+public abstract interface AbstractBaseSplitter {
+    public abstract boolean split(ImmutableBytesWritable srcPtr, ImmutableBytesWritable outPtr);
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/GuavaSplitter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/GuavaSplitter.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/GuavaSplitter.java
new file mode 100644
index 0000000..325919e
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/GuavaSplitter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.regex;
+
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.schema.types.PVarcharArray;
+import org.apache.phoenix.schema.types.PhoenixArray;
+import org.apache.phoenix.util.ByteUtil;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+public class GuavaSplitter implements AbstractBaseSplitter {
+    private final Splitter splitter;
+
+    public GuavaSplitter(String patternString) {
+        if (patternString != null) {
+            splitter = Splitter.onPattern(patternString);
+        } else {
+            splitter = null;
+        }
+    }
+
+    @Override
+    public boolean split(ImmutableBytesWritable srcPtr, ImmutableBytesWritable outPtr) {
+        String sourceStr = (String) PVarchar.INSTANCE.toObject(srcPtr);
+        if (sourceStr == null) { // sourceStr evaluated to null
+            outPtr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+        } else {
+            List<String> splitStrings = Lists.newArrayList(splitter.split(sourceStr));
+            PhoenixArray splitArray = new PhoenixArray(PVarchar.INSTANCE, splitStrings.toArray());
+            outPtr.set(PVarcharArray.INSTANCE.toBytes(splitArray));
+        }
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
new file mode 100644
index 0000000..5c0b1bc
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JONIPattern.java
@@ -0,0 +1,201 @@
+/*
+ * 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.util.regex;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.schema.SortOrder;
+import org.apache.phoenix.schema.types.PArrayDataType.PArrayDataTypeBytesArrayBuilder;
+import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.StringUtil;
+import org.jcodings.Encoding;
+import org.jcodings.specific.UTF8Encoding;
+import org.joni.Matcher;
+import org.joni.Option;
+import org.joni.Regex;
+import org.joni.Syntax;
+
+import com.google.common.base.Preconditions;
+
+public class JONIPattern extends AbstractBasePattern implements AbstractBaseSplitter {
+
+    private final Regex pattern;
+    private final String patternString;
+
+    public JONIPattern(String patternString) {
+        this(patternString, 0);
+    }
+
+    public JONIPattern(String patternString, int flags) {
+        this(patternString, flags, UTF8Encoding.INSTANCE);
+    }
+
+    public JONIPattern(String patternString, int flags, Encoding coding) {
+        this.patternString = patternString;
+        if (patternString != null) {
+            byte[] bytes = patternString.getBytes();
+            pattern = new Regex(bytes, 0, bytes.length, flags, coding, Syntax.Java);
+        } else {
+            pattern = null;
+        }
+    }
+
+    @Override
+    public void matches(ImmutableBytesWritable srcPtr, ImmutableBytesWritable outPtr) {
+        Preconditions.checkNotNull(srcPtr);
+        Preconditions.checkNotNull(outPtr);
+        boolean ret = matches(srcPtr.get(), srcPtr.getOffset(), srcPtr.getLength());
+        outPtr.set(ret ? PDataType.TRUE_BYTES : PDataType.FALSE_BYTES);
+    }
+
+    private boolean matches(byte[] bytes, int offset, int len) {
+        int range = offset + len;
+        Matcher matcher = pattern.matcher(bytes, offset, range);
+        int ret = matcher.match(offset, range, Option.DEFAULT);
+        return len == ret;
+    }
+
+    @Override
+    public String pattern() {
+        return patternString;
+    }
+
+    @Override
+    public void replaceAll(ImmutableBytesWritable srcPtr, ImmutableBytesWritable replacePtr,
+            ImmutableBytesWritable replacedPtr) {
+        Preconditions.checkNotNull(srcPtr);
+        Preconditions.checkNotNull(replacePtr);
+        Preconditions.checkNotNull(replacedPtr);
+        byte[] replacedBytes =
+                replaceAll(srcPtr.get(), srcPtr.getOffset(), srcPtr.getLength(), replacePtr.get(),
+                    replacePtr.getOffset(), replacePtr.getLength());
+        replacedPtr.set(replacedBytes);
+    }
+
+    private byte[] replaceAll(byte[] srcBytes, int srcOffset, int srcLen, byte[] replaceBytes,
+            int replaceOffset, int replaceLen) {
+        class PairInt {
+            public int begin, end;
+
+            public PairInt(int begin, int end) {
+                this.begin = begin;
+                this.end = end;
+            }
+        }
+        int srcRange = srcOffset + srcLen;
+        Matcher matcher = pattern.matcher(srcBytes, 0, srcRange);
+        int cur = srcOffset;
+        List<PairInt> searchResults = new LinkedList<PairInt>();
+        int totalBytesNeeded = 0;
+        while (true) {
+            int nextCur = matcher.search(cur, srcRange, Option.DEFAULT);
+            if (nextCur < 0) {
+                totalBytesNeeded += srcRange - cur;
+                break;
+            }
+            searchResults.add(new PairInt(matcher.getBegin(), matcher.getEnd()));
+            totalBytesNeeded += (nextCur - cur) + replaceLen;
+            cur = matcher.getEnd();
+        }
+        byte[] ret = new byte[totalBytesNeeded];
+        int curPosInSrc = srcOffset, curPosInRet = 0;
+        for (PairInt pair : searchResults) {
+            System.arraycopy(srcBytes, curPosInSrc, ret, curPosInRet, pair.begin - curPosInSrc);
+            curPosInRet += pair.begin - curPosInSrc;
+            System.arraycopy(replaceBytes, replaceOffset, ret, curPosInRet, replaceLen);
+            curPosInRet += replaceLen;
+            curPosInSrc = pair.end;
+        }
+        System.arraycopy(srcBytes, curPosInSrc, ret, curPosInRet, srcRange - curPosInSrc);
+        return ret;
+    }
+
+    @Override
+    public boolean substr(ImmutableBytesWritable srcPtr, int offsetInStr,
+            ImmutableBytesWritable outPtr) {
+        Preconditions.checkNotNull(srcPtr);
+        Preconditions.checkNotNull(outPtr);
+        int offsetInBytes = StringUtil.calculateUTF8Offset(srcPtr.get(), srcPtr.getOffset(),
+            srcPtr.getLength(), SortOrder.ASC, offsetInStr);
+        if (offsetInBytes < 0) return false;
+        substr(srcPtr.get(), offsetInBytes, srcPtr.getOffset() + srcPtr.getLength(), outPtr);
+        return true;
+    }
+
+    private boolean substr(byte[] srcBytes, int offset, int range, ImmutableBytesWritable outPtr) {
+        Matcher matcher = pattern.matcher(srcBytes, 0, range);
+        boolean ret = matcher.search(offset, range, Option.DEFAULT) >= 0;
+        if (ret) {
+            int len = matcher.getEnd() - matcher.getBegin();
+            outPtr.set(srcBytes, matcher.getBegin(), len);
+        } else {
+            outPtr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+        }
+        return ret;
+    }
+
+    @Override
+    public boolean split(ImmutableBytesWritable srcPtr, ImmutableBytesWritable outPtr) {
+        return split(srcPtr.get(), srcPtr.getOffset(), srcPtr.getLength(), outPtr);
+    }
+
+    private boolean
+            split(byte[] srcBytes, int srcOffset, int srcLen, ImmutableBytesWritable outPtr) {
+        PArrayDataTypeBytesArrayBuilder builder =
+                new PArrayDataTypeBytesArrayBuilder(PVarchar.INSTANCE, SortOrder.ASC);
+        int srcRange = srcOffset + srcLen;
+        Matcher matcher = pattern.matcher(srcBytes, 0, srcRange);
+        int cur = srcOffset;
+        boolean append;
+        while (true) {
+            int nextCur = matcher.search(cur, srcRange, Option.DEFAULT);
+            if (nextCur < 0) {
+                append = builder.appendElem(srcBytes, cur, srcRange - cur);
+                if (!append) return false;
+                break;
+            }
+
+            // To handle the following case, which adds null at first.
+            // REGEXP_SPLIT("12ONE34TWO56THREE78","[0-9]+")={null, "ONE", "TWO", "THREE", null}
+            if (cur == matcher.getBegin()) {
+                builder.appendElem(srcBytes, cur, 0);
+            }
+
+            if (cur < matcher.getBegin()) {
+                append = builder.appendElem(srcBytes, cur, matcher.getBegin() - cur);
+                if (!append) return false;
+            }
+            cur = matcher.getEnd();
+
+            // To handle the following case, which adds null at last.
+            // REGEXP_SPLIT("12ONE34TWO56THREE78","[0-9]+")={null, "ONE", "TWO", "THREE", null}
+            if (cur == srcRange) {
+                builder.appendElem(srcBytes, cur, 0);
+                break;
+            }
+        }
+        byte[] bytes = builder.getBytesAndClose();
+        if (bytes == null) return false;
+        outPtr.set(bytes);
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JavaPattern.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JavaPattern.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JavaPattern.java
new file mode 100644
index 0000000..be1188c
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/util/regex/JavaPattern.java
@@ -0,0 +1,93 @@
+/*
+ * 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.util.regex;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.phoenix.schema.types.PDataType;
+import org.apache.phoenix.schema.types.PVarchar;
+import org.apache.phoenix.util.ByteUtil;
+
+import com.google.common.base.Preconditions;
+
+public class JavaPattern extends AbstractBasePattern {
+
+    private final Pattern pattern;
+
+    public JavaPattern(String patternString) {
+        this(patternString, 0);
+    }
+
+    public JavaPattern(String patternString, int flags) {
+        if (patternString != null) {
+            pattern = Pattern.compile(patternString, flags);
+        } else {
+            pattern = null;
+        }
+    }
+
+    @Override
+    public void matches(ImmutableBytesWritable srcPtr, ImmutableBytesWritable outPtr) {
+        Preconditions.checkNotNull(srcPtr);
+        Preconditions.checkNotNull(outPtr);
+        String matcherSourceStr = (String) PVarchar.INSTANCE.toObject(srcPtr);
+        if (srcPtr.get().length == 0 && matcherSourceStr == null) matcherSourceStr = "";
+        boolean ret = pattern.matcher(matcherSourceStr).matches();
+        outPtr.set(ret ? PDataType.TRUE_BYTES : PDataType.FALSE_BYTES);
+    }
+
+    @Override
+    public String pattern() {
+        return pattern.pattern();
+    }
+
+    @Override
+    public void replaceAll(ImmutableBytesWritable srcPtr, ImmutableBytesWritable replacePtr,
+            ImmutableBytesWritable replacedPtr) {
+        Preconditions.checkNotNull(srcPtr);
+        Preconditions.checkNotNull(replacePtr);
+        Preconditions.checkNotNull(replacedPtr);
+        String sourceStr = (String) PVarchar.INSTANCE.toObject(srcPtr);
+        String replaceStr = (String) PVarchar.INSTANCE.toObject(replacePtr);
+        if (srcPtr.get().length == 0 && sourceStr == null) sourceStr = "";
+        if (replacePtr.get().length == 0 && replaceStr == null) replaceStr = "";
+        String replacedStr = pattern.matcher(sourceStr).replaceAll(replaceStr);
+        replacedPtr.set(PVarchar.INSTANCE.toBytes(replacedStr));
+    }
+
+    @Override
+    public boolean substr(ImmutableBytesWritable srcPtr, int offsetInStr,
+            ImmutableBytesWritable outPtr) {
+        Preconditions.checkNotNull(srcPtr);
+        Preconditions.checkNotNull(outPtr);
+        String sourceStr = (String) PVarchar.INSTANCE.toObject(srcPtr);
+        if (srcPtr.get().length == 0 && sourceStr == null) sourceStr = "";
+        if (offsetInStr < 0) offsetInStr += sourceStr.length();
+        if (offsetInStr < 0 || offsetInStr >= sourceStr.length()) return false;
+        Matcher matcher = pattern.matcher(sourceStr);
+        boolean ret = matcher.find(offsetInStr);
+        if (ret) {
+            outPtr.set(PVarchar.INSTANCE.toBytes(matcher.group()));
+        } else {
+            outPtr.set(ByteUtil.EMPTY_BYTE_ARRAY);
+        }
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
index f415b01..e6ede7c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/visitor/CloneExpressionVisitor.java
@@ -107,7 +107,8 @@ public class CloneExpressionVisitor extends TraverseAllExpressionVisitor<Express
 
     @Override
     public Expression visitLeave(LikeExpression node, List<Expression> l) {
-        return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node :  new LikeExpression(l);
+        return Determinism.PER_INVOCATION.compareTo(node.getDeterminism()) > 0 ? node : node
+                .clone(l);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpReplaceParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpReplaceParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpReplaceParseNode.java
new file mode 100644
index 0000000..4d98405
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpReplaceParseNode.java
@@ -0,0 +1,55 @@
+/*
+ * 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.parse;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction;
+import org.apache.phoenix.expression.function.RegexpReplaceFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpReplaceFunction;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
+
+/**
+ * Parse node corresponding to {@link RegexpReplaceFunction}. It also acts as a factory for creating
+ * the right kind of RegexpReplaceFunction according to setting in
+ * QueryServices.USE_BYTE_BASED_REGEX_ATTRIB
+ */
+public class RegexpReplaceParseNode extends FunctionParseNode {
+
+    RegexpReplaceParseNode(String name, List<ParseNode> children, BuiltInFunctionInfo info) {
+        super(name, children, info);
+    }
+
+    @Override
+    public Expression create(List<Expression> children, StatementContext context)
+            throws SQLException {
+        QueryServices services = context.getConnection().getQueryServices();
+        boolean useByteBasedRegex =
+                services.getProps().getBoolean(QueryServices.USE_BYTE_BASED_REGEX_ATTRIB,
+                    QueryServicesOptions.DEFAULT_USE_BYTE_BASED_REGEX);
+        if (useByteBasedRegex) {
+            return new ByteBasedRegexpReplaceFunction(children);
+        } else {
+            return new StringBasedRegexpReplaceFunction(children);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f6b2594/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSplitParseNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSplitParseNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSplitParseNode.java
new file mode 100644
index 0000000..74bee07
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/RegexpSplitParseNode.java
@@ -0,0 +1,55 @@
+/*
+ * 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.parse;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction;
+import org.apache.phoenix.expression.function.RegexpSplitFunction;
+import org.apache.phoenix.expression.function.StringBasedRegexpSplitFunction;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
+
+/**
+ * Parse node corresponding to {@link RegexpSplitFunction}. It also acts as a factory for creating
+ * the right kind of RegexpSplitFunction according to setting in
+ * QueryServices.USE_BYTE_BASED_REGEX_ATTRIB
+ */
+public class RegexpSplitParseNode extends FunctionParseNode {
+
+    RegexpSplitParseNode(String name, List<ParseNode> children, BuiltInFunctionInfo info) {
+        super(name, children, info);
+    }
+
+    @Override
+    public Expression create(List<Expression> children, StatementContext context)
+            throws SQLException {
+        QueryServices services = context.getConnection().getQueryServices();
+        boolean useByteBasedRegex =
+                services.getProps().getBoolean(QueryServices.USE_BYTE_BASED_REGEX_ATTRIB,
+                    QueryServicesOptions.DEFAULT_USE_BYTE_BASED_REGEX);
+        if (useByteBasedRegex) {
+            return new ByteBasedRegexpSplitFunction(children);
+        } else {
+            return new StringBasedRegexpSplitFunction(children);
+        }
+    }
+}