From 26383070e5980697a0de1193c069692a8107b7c2 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sat, 21 Mar 2015 23:32:40 +0800 Subject: [PATCH 01/24] Create ArrayToJsonFunction.java add a function to support "ArrayToJson" function as a build-in function for JSON --- .../function/ArrayToJsonFunction.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java new file mode 100644 index 00000000000..f0f93945d0e --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java @@ -0,0 +1,70 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode; +import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; + +import java.sql.SQLException; +import java.util.List; + +/** + * Created by WangLei on 2015/3/20. + */ +@FunctionParseNode.BuiltInFunction(name=ArrayToJsonFunction.NAME, args={ + @FunctionParseNode.Argument(allowedTypes={PVarchar.class})} ) +public class ArrayToJsonFunction extends ScalarFunction { + public static final String NAME = "ArrayToJson"; + + public ArrayToJsonFunction() { + } + + public ArrayToJsonFunction(List children) throws SQLException { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + Expression arrayExpr = getChildren().get(0); + + if (!arrayExpr.evaluate(tuple, ptr)) { + return false; + } else if (ptr.getLength() == 0) { return true; } + + PDataType baseType = PDataType.fromTypeId(arrayExpr.getDataType() + .getSqlType() + - PDataType.ARRAY_TYPE_BASE); + int length = PArrayDataType.getArrayLength(ptr, baseType, arrayExpr.getMaxLength()); + StringBuilder builder = new StringBuilder("["); + ImmutableBytesWritable tmp = new ImmutableBytesWritable(); + for(int i=1;i<=length;i++){ + tmp.set(ptr.get()); + PArrayDataType.positionAtArrayElement(tmp, i - 1,baseType, arrayExpr.getMaxLength()); + Object re =baseType.toObject(tmp); + builder.append(re); + if(i != length) + builder.append(","); + } + builder.append("]"); + ptr.set(PVarchar.INSTANCE.toBytes(builder.toString())); + return true; + } + + @Override + public SortOrder getSortOrder() { + return getChildren().get(0).getSortOrder(); + } + + @Override + public PDataType getDataType() { + return PVarchar.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + +} From 2c63494f99267fbcd0b7e52be76a1dc0154e306a Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sat, 21 Mar 2015 23:34:57 +0800 Subject: [PATCH 02/24] Create ArrayToJsonFunctionTest.java a simple test for testing ArrayToJsonFunction --- .../function/ArrayToJsonFunctionTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java new file mode 100644 index 00000000000..c34d9586ec2 --- /dev/null +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java @@ -0,0 +1,39 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.expression.LiteralExpression; +import org.apache.phoenix.schema.types.*; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + + +/** + * Created by WangLei on 2015/3/20. + */ +public class ArrayToJsonFunctionTest { + + public String testIntArrayToJson (Object[] array) throws Exception { + LiteralExpression arrayExpr; + List children; + PhoenixArray pa =new PhoenixArray.PrimitiveIntPhoenixArray( PInteger.INSTANCE,array); + arrayExpr = LiteralExpression.newConstant(pa,PIntegerArray.INSTANCE ); + children = Arrays.asList(arrayExpr); + ArrayToJsonFunction e = new ArrayToJsonFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + String result = (String)e.getDataType().toObject(ptr); + return result; + } + @Test + public void testArrayToJson() throws Exception { + Object[] testarray = new Object[]{1,12,32}; + String result = testIntArrayToJson(testarray); + String expected ="[1,12,32]"; + assertEquals(result, expected); + } +} From 241f4c3186a9f70f057e9196f962a0ed1b2f593b Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:26:56 +0800 Subject: [PATCH 03/24] Delete ArrayToJsonFunctionTest.java --- .../function/ArrayToJsonFunctionTest.java | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java deleted file mode 100644 index c34d9586ec2..00000000000 --- a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/ArrayToJsonFunctionTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.apache.phoenix.expression.function; - -import org.apache.hadoop.hbase.io.ImmutableBytesWritable; -import org.apache.phoenix.expression.Expression; -import org.apache.phoenix.expression.LiteralExpression; -import org.apache.phoenix.schema.types.*; -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; - - -/** - * Created by WangLei on 2015/3/20. - */ -public class ArrayToJsonFunctionTest { - - public String testIntArrayToJson (Object[] array) throws Exception { - LiteralExpression arrayExpr; - List children; - PhoenixArray pa =new PhoenixArray.PrimitiveIntPhoenixArray( PInteger.INSTANCE,array); - arrayExpr = LiteralExpression.newConstant(pa,PIntegerArray.INSTANCE ); - children = Arrays.asList(arrayExpr); - ArrayToJsonFunction e = new ArrayToJsonFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - String result = (String)e.getDataType().toObject(ptr); - return result; - } - @Test - public void testArrayToJson() throws Exception { - Object[] testarray = new Object[]{1,12,32}; - String result = testIntArrayToJson(testarray); - String expected ="[1,12,32]"; - assertEquals(result, expected); - } -} From f66ba90f92a667405759fceaf1ee74c27ef1d1c8 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:35:37 +0800 Subject: [PATCH 04/24] Create JsonFunctionTest.java --- .../expression/function/JsonFunctionTest.java | 273 ++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java new file mode 100644 index 00000000000..7fa988f776a --- /dev/null +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java @@ -0,0 +1,273 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.expression.LiteralExpression; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.types.*; +import org.junit.Test; + +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Date; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; + + +public class JsonFunctionTest { + public static final String TEST_JSON_STR = + "{\"f2\":{\"f3\":\"value\"},\"f4\":{\"f5\":99,\"f6\":[1,true,\"foo\"]},\"f7\":true}"; + + public PhoenixJson testArrayToJson (Object[] array,PDataType datatype,PArrayDataType arraydatatype) throws Exception { + LiteralExpression arrayExpr; + List children; + PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( datatype,array); + arrayExpr = LiteralExpression.newConstant(pa,arraydatatype ); + children = Arrays.asList(arrayExpr); + ArrayToJsonFunction e = new ArrayToJsonFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + PhoenixJson result = (PhoenixJson)e.getDataType().toObject(ptr); + return result; + } + + @Test + public void testNumberArrayToJson() throws Exception { + Object[] testarray = new Object[]{1,12,32,432}; + PhoenixJson result = testArrayToJson(testarray, PInteger.INSTANCE, PIntegerArray.INSTANCE); + String expected ="[1,12,32,432]"; + assertEquals(result.serializeToString(), expected); + Object[] testarray2 = new Object[]{1.12,12.34,32.45,432.78}; + PhoenixJson result2 = testArrayToJson(testarray2, PDouble.INSTANCE, PDoubleArray.INSTANCE); + String expected2 ="[1.12,12.34,32.45,432.78]"; + assertEquals(result2.serializeToString(), expected2); + } + @Test + public void testBooleanArrayToJson() throws Exception { + Object[] testarray = new Object[]{false,true}; + PhoenixJson result = testArrayToJson(testarray, PBoolean.INSTANCE, PBooleanArray.INSTANCE); + String expected ="[false,true]"; + assertEquals(result.toString(), expected); + } + + @Test + public void testStringArrayToJson() throws Exception { + Object[] testarray = new Object[]{"abc123","12.3","string","汉字"}; + PhoenixJson result = testArrayToJson(testarray, PVarchar.INSTANCE, PVarcharArray.INSTANCE); + String expected ="[\"abc123\",\"12.3\",\"string\",\"汉字\"]"; + assertEquals(result.serializeToString(), expected); + } + @Test + public void testDateArrayToJson() throws Exception { + SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss.SSS"); + Date date1= myFormatter.parse("1990-12-01 11:01:45.0"); + Date date2 = myFormatter.parse("1989-03-12 13:01:45.0"); + Date date3 = myFormatter.parse("1974-06-06 12:01:45.0"); + Object[] testarray = new Object[]{date1,date2,date3}; + PhoenixJson result = testArrayToJson(testarray, PDate.INSTANCE,PDateArray.INSTANCE); + String expected ="[\"1990-12-01\",\"1989-03-12\",\"1974-06-06\"]"; + assertEquals(result.serializeToString(), expected); + + Timestamp ts1 = Timestamp.valueOf("1990-12-01 11:01:45.123"); + Timestamp ts2 = Timestamp.valueOf("1989-03-12 01:01:01.0"); + Timestamp ts3 = Timestamp.valueOf("1989-03-12 23:59:59.1"); + testarray = new Object[]{ts1,ts2,ts3}; + result = testArrayToJson(testarray, PTimestamp.INSTANCE,PTimestampArray.INSTANCE); + expected ="[\"1990-12-01 11:01:45.123\",\"1989-03-12 01:01:01.0\",\"1989-03-12 23:59:59.1\"]"; + assertEquals(result.serializeToString(), expected); + + Time t1 = new Time(date1.getTime()); + Time t2 = new Time(date2.getTime()); + Time t3 = new Time(date3.getTime()); + testarray = new Object[]{t1,t2,t3}; + result = testArrayToJson(testarray, PTime.INSTANCE,PTimeArray.INSTANCE); + expected ="[\"11:01:45\",\"13:01:45\",\"12:01:45\"]"; + assertEquals(result.serializeToString(), expected); + + + } + + + public String[] JsonArrayElements (String json) throws Exception { + PhoenixJson phoenixJson = PhoenixJson.getInstance(json); + LiteralExpression JsonExpr; + List children; + JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + children = Arrays.asList(JsonExpr); + JsonArrayElementsFunction e = new JsonArrayElementsFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + PhoenixArray pa = (PhoenixArray)e.getDataType().toObject(ptr); + return (String[] )pa.getArray(); + } + + @Test + public void testJsonArrayElements() throws Exception { + String json = "[1,true,\"string\",[2,false]]"; + Object[] expected = new Object[]{"1","true","\"string\"","[2,false]"}; + String[] result = JsonArrayElements(json); + + assertEquals(result.length, expected.length); + for(int i = 0; i children; + PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( PVarchar.INSTANCE,types); + LiteralExpression typesExpr = LiteralExpression.newConstant(pa,PVarcharArray.INSTANCE ); + PhoenixJson phoenixJson = PhoenixJson.getInstance(json); + LiteralExpression JsonExpr; + JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + children = Arrays.asList(typesExpr,JsonExpr); + JsonPopulateRecordFunction e = new JsonPopulateRecordFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + String record = (String)e.getDataType().toObject(ptr); + return record; + } + + @Test + public void testJsonPopulateRecord() throws Exception { + Object[] types= new Object[]{"a","b"}; + String json = "{\"a\":1,\"b\":2}"; + String expected = "1,2"; + String result = JsonPopulateRecord(types,json); + assertEquals(result, expected); + } + + public String[] JsonPopulateRecordSet (Object[] types,String json) throws Exception { + List children; + PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( PVarchar.INSTANCE,types); + LiteralExpression typesExpr = LiteralExpression.newConstant(pa,PVarcharArray.INSTANCE ); + PhoenixJson phoenixJson = PhoenixJson.getInstance(json); + LiteralExpression JsonExpr; + JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + children = Arrays.asList(typesExpr,JsonExpr); + JsonPopulateRecordSetFunction e = new JsonPopulateRecordSetFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + PhoenixArray record = (PhoenixArray)e.getDataType().toObject(ptr); + return (String[] )record.getArray(); + } + + + @Test + public void testJsonPopulateRecordSet() throws Exception { + Object[] types= new Object[]{"a","b"}; + String json = "[{\"a\":1,\"b\":2},{\"a\":2,\"b\":3},{\"a\":4,\"b\":5}]"; + Object[] expected = new Object[]{"1,2","2,3","4,5"}; + String[] result = JsonPopulateRecordSet(types,json); + assertEquals(result.length, expected.length); + for(int i = 0; i children; + JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + children = Arrays.asList(JsonExpr); + JsonArrayLengthFunction e = new JsonArrayLengthFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + int len = (int)e.getDataType().toObject(ptr); + return len; + } + + @Test + public void testJsonArrayLength() throws Exception { + String array1 = "[1,true,\"string\",[2,false]]"; + String array2 = "[1,2.34,[1,\"abc\"],4,true,\"string\",[2,false]]"; + assertEquals(JsonArrayLength(array1),4); + assertEquals(JsonArrayLength(array2),7); + } + + + public PhoenixJson ToJson (Object obj,PDataType datatype) throws Exception { + List children; + //LiteralExpression op =LiteralExpression.newConstant(new BigDecimal("9999.1"), PDecimal.INSTANCE); + LiteralExpression op =LiteralExpression.newConstant(obj, datatype); + children = Arrays.asList(op); + ToJsonFunction e = new ToJsonFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + PhoenixJson result = (PhoenixJson)e.getDataType().toObject(ptr); + return result; + } + + + @Test + public void testToJson() throws Exception { + assertEquals(ToJson(-256, PInteger.INSTANCE).serializeToString(),"-256"); + assertEquals(ToJson(-256, PLong.INSTANCE).serializeToString(),"-256"); + assertEquals(ToJson(-1, PSmallint.INSTANCE).serializeToString(),"-1"); + assertEquals(ToJson(-1, PTinyint.INSTANCE).serializeToString(),"-1"); + assertEquals(ToJson(12, PUnsignedInt.INSTANCE).serializeToString(),"12"); + assertEquals(ToJson(12, PUnsignedSmallint.INSTANCE).serializeToString(),"12"); + assertEquals(ToJson(12, PUnsignedLong.INSTANCE).serializeToString(),"12"); + assertEquals(ToJson(123.456, PDouble.INSTANCE).serializeToString(),"123.456"); + assertEquals(ToJson(123.456, PFloat.INSTANCE).serializeToString(),"123.456"); + assertEquals(ToJson(123.456,PUnsignedDouble.INSTANCE).serializeToString(),"123.456"); + assertEquals(ToJson(123.456, PUnsignedFloat.INSTANCE).serializeToString(),"123.456"); + assertEquals(ToJson(false, PBoolean.INSTANCE).serializeToString(),"false"); + assertEquals(ToJson(true, PBoolean.INSTANCE).serializeToString(),"true"); + assertEquals(ToJson("string_abc", PVarchar.INSTANCE).toString(),"\"string_abc\""); + assertEquals(ToJson("string_abc", PVarchar.INSTANCE).serializeToString(),"string_abc"); + } + public String[] JsonObjectKeys (String json) throws Exception { + PhoenixJson phoenixJson = PhoenixJson.getInstance(json); + LiteralExpression JsonExpr; + List children; + JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + children = Arrays.asList(JsonExpr); + JsonObjectKeysFunction e = new JsonObjectKeysFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + PhoenixArray pa = (PhoenixArray)e.getDataType().toObject(ptr); + return (String[] )pa.getArray(); + } + + + + @Test + public void testJsonObjectKeys() throws Exception { + Object[] expected = new Object[]{"f2","f4","f7"}; + String[] result = JsonObjectKeys(TEST_JSON_STR); + + assertEquals(result.length, expected.length); + for(int i = 0; i children; + JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + children = Arrays.asList(JsonExpr); + JsonEachFunction e = new JsonEachFunction(children); + ImmutableBytesWritable ptr = new ImmutableBytesWritable(); + boolean evaluated = e.evaluate(null, ptr); + PhoenixArray pa = (PhoenixArray)e.getDataType().toObject(ptr); + return (String[] )pa.getArray(); + } + @Test + public void testJsonEach() throws Exception { + Object[] expected = new Object[]{"f2,{\"f3\":\"value\"}","f4,{\"f5\":99,\"f6\":[1,true,\"foo\"]}","f7,true"}; + String[] result = JsonEach(TEST_JSON_STR); + + assertEquals(result.length, expected.length); + for(int i = 0; i Date: Sun, 28 Jun 2015 19:53:44 +0800 Subject: [PATCH 05/24] PHOENIX-1661 Implement built-in functions for JSON --- .../function/ArrayToJsonFunction1.java | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction1.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction1.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction1.java new file mode 100644 index 00000000000..9b52707e6e2 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction1.java @@ -0,0 +1,89 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode; +import org.apache.phoenix.schema.IllegalDataException; +import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; +import org.apache.phoenix.util.ByteUtil; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; + +/** + * Created by WangLei on 2015/3/20. + */ +@FunctionParseNode.BuiltInFunction(name=ArrayToJsonFunction.NAME, args={ + @FunctionParseNode.Argument(allowedTypes={PVarchar.class})} ) +public class ArrayToJsonFunction extends ScalarFunction { + public static final String NAME = "Array_To_Json"; + + public ArrayToJsonFunction() { + } + + public ArrayToJsonFunction(List children) throws SQLException { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + Expression arrayExpr = getChildren().get(0); + + if (!arrayExpr.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + + + PDataType baseType = PDataType.fromTypeId(arrayExpr.getDataType() + .getSqlType() + - PDataType.ARRAY_TYPE_BASE); + int length = PArrayDataType.getArrayLength(ptr, baseType, arrayExpr.getMaxLength()); + StringBuilder builder = new StringBuilder("["); + ImmutableBytesWritable tmp = new ImmutableBytesWritable(); + for(int i=1;i<=length;i++){ + tmp.set(ptr.get()); + PArrayDataType.positionAtArrayElement(tmp, i - 1,baseType, arrayExpr.getMaxLength()); + Object re =baseType.toObject(tmp); + builder.append(PhoenixJson.DataToJsonValue(baseType, re)); + if(i != length) + builder.append(","); + } + builder.append("]"); + + try { + String str = builder.toString(); + PhoenixJson phoenixJson = PhoenixJson.getInstance(str); + byte[] json = PJson.INSTANCE.toBytes(phoenixJson); + ptr.set(json); + } catch (SQLException sqe) { + System.out.println(sqe.getMessage()); + } + return true; + } + + @Override + public SortOrder getSortOrder() { + return getChildren().get(0).getSortOrder(); + } + + @Override + public PDataType getDataType() { + return PJson.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + +} From db5c9e1cfbb1f3145a992efef13a8dca7cee0ec7 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:54:53 +0800 Subject: [PATCH 06/24] Delete ArrayToJsonFunction.java --- .../function/ArrayToJsonFunction.java | 70 ------------------- 1 file changed, 70 deletions(-) delete mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java deleted file mode 100644 index f0f93945d0e..00000000000 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.apache.phoenix.expression.function; - -import org.apache.hadoop.hbase.io.ImmutableBytesWritable; -import org.apache.phoenix.expression.Expression; -import org.apache.phoenix.parse.FunctionParseNode; -import org.apache.phoenix.schema.SortOrder; -import org.apache.phoenix.schema.tuple.Tuple; -import org.apache.phoenix.schema.types.*; - -import java.sql.SQLException; -import java.util.List; - -/** - * Created by WangLei on 2015/3/20. - */ -@FunctionParseNode.BuiltInFunction(name=ArrayToJsonFunction.NAME, args={ - @FunctionParseNode.Argument(allowedTypes={PVarchar.class})} ) -public class ArrayToJsonFunction extends ScalarFunction { - public static final String NAME = "ArrayToJson"; - - public ArrayToJsonFunction() { - } - - public ArrayToJsonFunction(List children) throws SQLException { - super(children); - } - - @Override - public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { - Expression arrayExpr = getChildren().get(0); - - if (!arrayExpr.evaluate(tuple, ptr)) { - return false; - } else if (ptr.getLength() == 0) { return true; } - - PDataType baseType = PDataType.fromTypeId(arrayExpr.getDataType() - .getSqlType() - - PDataType.ARRAY_TYPE_BASE); - int length = PArrayDataType.getArrayLength(ptr, baseType, arrayExpr.getMaxLength()); - StringBuilder builder = new StringBuilder("["); - ImmutableBytesWritable tmp = new ImmutableBytesWritable(); - for(int i=1;i<=length;i++){ - tmp.set(ptr.get()); - PArrayDataType.positionAtArrayElement(tmp, i - 1,baseType, arrayExpr.getMaxLength()); - Object re =baseType.toObject(tmp); - builder.append(re); - if(i != length) - builder.append(","); - } - builder.append("]"); - ptr.set(PVarchar.INSTANCE.toBytes(builder.toString())); - return true; - } - - @Override - public SortOrder getSortOrder() { - return getChildren().get(0).getSortOrder(); - } - - @Override - public PDataType getDataType() { - return PVarchar.INSTANCE; - } - - @Override - public String getName() { - return NAME; - } - -} From a02b954453ff67220c08801e0eafd1c23bdd4b9c Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:55:10 +0800 Subject: [PATCH 07/24] Rename ArrayToJsonFunction1.java to ArrayToJsonFunction.java --- .../{ArrayToJsonFunction1.java => ArrayToJsonFunction.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename phoenix-core/src/main/java/org/apache/phoenix/expression/function/{ArrayToJsonFunction1.java => ArrayToJsonFunction.java} (100%) diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction1.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java similarity index 100% rename from phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction1.java rename to phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java From eb76667b0c61ea646a63c1b46b41f418c6b43239 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:55:33 +0800 Subject: [PATCH 08/24] Update ArrayToJsonFunction.java --- .../phoenix/expression/function/ArrayToJsonFunction.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java index 9b52707e6e2..476d5f7112e 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java @@ -17,9 +17,7 @@ import java.sql.SQLException; import java.util.List; -/** - * Created by WangLei on 2015/3/20. - */ + @FunctionParseNode.BuiltInFunction(name=ArrayToJsonFunction.NAME, args={ @FunctionParseNode.Argument(allowedTypes={PVarchar.class})} ) public class ArrayToJsonFunction extends ScalarFunction { From 6546bc5fb0620b954e31e54e0c71671c54a8f9dc Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:56:50 +0800 Subject: [PATCH 09/24] PHOENIX-1661 Implement built-in functions for JSON --- .../function/JsonArrayElementsFunction.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java new file mode 100644 index 00000000000..f2b91fa9145 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java @@ -0,0 +1,84 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.IllegalDataException; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; +import org.apache.phoenix.util.ByteUtil; + +import java.sql.SQLException; +import java.util.List; + +@BuiltInFunction(name = JsonArrayElementsFunction.NAME, args = { + @Argument(allowedTypes = { PJson.class })}) +public class JsonArrayElementsFunction extends ScalarFunction { + public static final String NAME = "JSON_ARRAY_ELEMENTS"; + + public JsonArrayElementsFunction() { + super(); + } + + public JsonArrayElementsFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression jsonExpression = this.children.get(0); + if (!jsonExpression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + Object[] elements = phoenixJson.getJsonArrayElements(); + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PVarcharArray.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PVarcharArray.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From 6bb8b116db3dd46434f2020a0ca2f46545b0ac05 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:58:22 +0800 Subject: [PATCH 10/24] PHOENIX-1661 Implement built-in functions for JSON --- .../function/JsonArrayLengthFunction.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java new file mode 100644 index 00000000000..ae234828ae8 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java @@ -0,0 +1,78 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; + +import java.util.List; + +@BuiltInFunction(name = JsonArrayLengthFunction.NAME, args = { + @Argument(allowedTypes = { PJson.class })}) +public class JsonArrayLengthFunction extends ScalarFunction { + public static final String NAME = "JSON_ARRAY_LENGTH"; + + public JsonArrayLengthFunction() { + super(); + } + + public JsonArrayLengthFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression jsonExpression = this.children.get(0); + if (!jsonExpression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + int length = phoenixJson.getJsonArrayLength(); + byte[] array = PInteger.INSTANCE.toBytes(length); + ptr.set(array); + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PInteger.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PInteger.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From 3782d70ce1c0fedbcea48e3df968564311c06a2a Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 19:59:25 +0800 Subject: [PATCH 11/24] PHOENIX-1661 Implement built-in functions for JSON --- .../expression/function/JsonEachFunction.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java new file mode 100644 index 00000000000..3737b837955 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java @@ -0,0 +1,79 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; + +import java.util.List; + +@BuiltInFunction(name = JsonEachFunction.NAME, args = { + @Argument(allowedTypes = { PJson.class })}) +public class JsonEachFunction extends ScalarFunction { + public static final String NAME = "JSON_EACH"; + + public JsonEachFunction() { + super(); + } + + public JsonEachFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression jsonExpression = this.children.get(0); + if (!jsonExpression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + Object[] elements = phoenixJson.getJsonFields(); + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PVarcharArray.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PVarcharArray.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From 5ff61520d2aa351ea4dab18ec131c74f34b1b463 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 20:00:08 +0800 Subject: [PATCH 12/24] PHOENIX-1661 Implement built-in functions for JSON --- .../function/JsonObjectKeysFunction.java | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java new file mode 100644 index 00000000000..10d0d0be910 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java @@ -0,0 +1,79 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; + +import java.util.List; + +@BuiltInFunction(name = JsonObjectKeysFunction.NAME, args = { + @Argument(allowedTypes = { PJson.class })}) +public class JsonObjectKeysFunction extends ScalarFunction { + public static final String NAME = "JSON_OBJECT_KEYS"; + + public JsonObjectKeysFunction() { + super(); + } + + public JsonObjectKeysFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression jsonExpression = this.children.get(0); + if (!jsonExpression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + Object[] elements = phoenixJson.getJsonObjectKeys(); + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PVarcharArray.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PVarcharArray.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From d21c22b3697b64b920cc22a33e32d76b2b8eb074 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 20:01:14 +0800 Subject: [PATCH 13/24] PHOENIX-1661 Implement built-in functions for JSON --- .../function/JsonPopulateRecordFunction.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java new file mode 100644 index 00000000000..835a1a3cc75 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java @@ -0,0 +1,103 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.IllegalDataException; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; +import org.apache.phoenix.util.ByteUtil; + +import java.sql.SQLException; +import java.util.List; + +@BuiltInFunction(name = JsonPopulateRecordFunction.NAME, args = { + @Argument(allowedTypes = { PVarcharArray.class }), + @Argument(allowedTypes = { PJson.class }) + }) +public class JsonPopulateRecordFunction extends ScalarFunction { + public static final String NAME = "JSON_POPULATE_RECORD"; + + public JsonPopulateRecordFunction() { + super(); + } + + public JsonPopulateRecordFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression typeArrayExpression = children.get(0); + if (!typeArrayExpression.evaluate(tuple, ptr)) { + return false; + } + + if (ptr.getLength() == 0) { + return false; + } + + PhoenixArray phoenixArray = (PhoenixArray) PVarcharArray.INSTANCE.toObject(ptr); + Expression jsonExpression = this.children.get(1); + if (!jsonExpression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + try { + String[] types = (String[]) phoenixArray.getArray(); + String records = phoenixJson.PopulateRecord(types); + byte[] array = PVarchar.INSTANCE.toBytes(records); + ptr.set(array); + + + } catch (SQLException sqe) { + new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) + .setRootCause(sqe).build().buildException()); + } + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PVarchar.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PVarchar.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From 541697d72d27a809cb8172efd58d83960b79f362 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 20:01:50 +0800 Subject: [PATCH 14/24] PHOENIX-1661 Implement built-in functions for JSON --- .../JsonPopulateRecordSetFunction.java | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java new file mode 100644 index 00000000000..1062cf6edc4 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java @@ -0,0 +1,103 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.IllegalDataException; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.*; + +import java.sql.SQLException; +import java.util.List; + +@BuiltInFunction(name = JsonPopulateRecordSetFunction.NAME, args = { + @Argument(allowedTypes = { PVarcharArray.class }), + @Argument(allowedTypes = { PJson.class }) + }) +public class JsonPopulateRecordSetFunction extends ScalarFunction { + public static final String NAME = "JSON_POPULATE_RECORDSET"; + + public JsonPopulateRecordSetFunction() { + super(); + } + + public JsonPopulateRecordSetFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression typeArrayExpression = children.get(0); + if (!typeArrayExpression.evaluate(tuple, ptr)) { + return false; + } + + if (ptr.getLength() == 0) { + return false; + } + + PhoenixArray phoenixArray = (PhoenixArray) PVarcharArray.INSTANCE.toObject(ptr); + Expression jsonExpression = this.children.get(1); + if (!jsonExpression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + try { + String[] types = (String[]) phoenixArray.getArray(); + Object[] records = phoenixJson.PopulateRecordSet(types); + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, records); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + + + } catch (SQLException sqe) { + new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) + .setRootCause(sqe).build().buildException()); + } + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PVarcharArray.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PVarcharArray.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From 7e0dbaf950b4292bf673bb64367e57085ada25f8 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 20:03:16 +0800 Subject: [PATCH 15/24] PHOENIX-1661 Implement built-in functions for JSON --- .../expression/function/ToJsonFunction.java | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java new file mode 100644 index 00000000000..04736651566 --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java @@ -0,0 +1,92 @@ +package org.apache.phoenix.expression.function; + +import org.apache.hadoop.hbase.io.ImmutableBytesWritable; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.apache.phoenix.expression.Expression; +import org.apache.phoenix.parse.FunctionParseNode.Argument; +import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.IllegalDataException; +import org.apache.phoenix.schema.json.PhoenixJson; +import org.apache.phoenix.schema.tuple.Tuple; +import org.apache.phoenix.schema.types.PDataType; +import org.apache.phoenix.schema.types.PJson; +import org.apache.phoenix.schema.types.PVarcharArray; +import org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.util.ByteUtil; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.List; + +@BuiltInFunction(name = ToJsonFunction.NAME, args = { + @Argument(allowedTypes = { PDataType.class })}) +public class ToJsonFunction extends ScalarFunction { + public static final String NAME = "TO_JSON"; + + public ToJsonFunction() { + super(); + } + + public ToJsonFunction(List children) { + super(children); + } + + @Override + public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { + + Expression expression = this.children.get(0); + if (!expression.evaluate(tuple, ptr)) { + return false; + } + if (ptr.getLength() == 0) { + return false; + } + PDataType baseType = expression.getDataType(); + Object re =baseType.toObject(ptr); + String jsons = PhoenixJson.DataToJsonValue(baseType, re); + try { + PhoenixJson phoenixJson = PhoenixJson.getInstance(jsons); + byte[] json = PJson.INSTANCE.toBytes(phoenixJson); + ptr.set(json); + } catch (SQLException sqe) { + System.out.println(sqe.getMessage()); + } + + return true; + } + + @SuppressWarnings("rawtypes") + @Override + public PDataType getDataType() { + return PJson.INSTANCE; + } + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean isNullable() { + return PJson.INSTANCE.isNullable(); + } + + @Override + public int getKeyFormationTraversalIndex() { + return NO_TRAVERSAL; + } + + @Override + public KeyPart newKeyPart(KeyPart childPart) { + return null; + } + + @Override + public OrderPreserving preservesOrder() { + return OrderPreserving.NO; + } + +} From 331e09b9df48611421830b3c52afc80fd75099d5 Mon Sep 17 00:00:00 2001 From: ictwanglei <597316513@qq.com> Date: Sun, 28 Jun 2015 20:09:54 +0800 Subject: [PATCH 16/24] PHOENIX-1661 Implement built-in functions for JSON PHOENIX-1661 Implement built-in functions for JSON --- .../phoenix/schema/json/PhoenixJson.java | 355 ++++++++++++++++++ 1 file changed, 355 insertions(+) create mode 100644 phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java new file mode 100644 index 00000000000..736ec690f8c --- /dev/null +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java @@ -0,0 +1,355 @@ +/* + * 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.schema.json; + +import java.io.IOException; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; +import org.apache.phoenix.schema.EqualityNotSupportedException; +import org.apache.phoenix.schema.types.*; +import org.codehaus.jackson.JsonFactory; +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.JsonParser; +import org.codehaus.jackson.JsonParser.Feature; +import org.codehaus.jackson.JsonProcessingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.node.ValueNode; + +import com.google.common.base.Preconditions; + +/** + * The {@link PhoenixJson} wraps json and uses Jackson library to parse and traverse the json. It + * should be used to represent the JSON data type and also should be used to parse Json data and + * read the value from it. It always conside the last value if same key exist more than once. + */ +public class PhoenixJson implements Comparable { + private final JsonNode rootNode; + /* + * input data has been stored as it is, since some data is lost when json parser runs, for + * example if a JSON object within the value contains the same key more than once then only last + * one is stored rest all of them are ignored, which will defy the contract of PJsonDataType of + * keeping user data as it is. + */ + private final String jsonAsString; + + /** + * Static Factory method to get an {@link PhoenixJson} object. It also validates the json and + * throws {@link SQLException} if it is invalid with line number and character. + * @param jsonData Json data as {@link String}. + * @return {@link PhoenixJson}. + * @throws SQLException + */ + public static PhoenixJson getInstance(String jsonData) throws SQLException { + if (jsonData == null) { + return null; + } + try { + JsonFactory jsonFactory = new JsonFactory(); + JsonParser jsonParser = jsonFactory.createJsonParser(jsonData); + JsonNode jsonNode = getRootJsonNode(jsonParser); + return new PhoenixJson(jsonNode, jsonData); + } catch (IOException x) { + throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_JSON_DATA).setRootCause(x) + .setMessage(x.getMessage()).build().buildException(); + } + + } + + /** + * Returns the root of the resulting {@link JsonNode} tree. + */ + private static JsonNode getRootJsonNode(JsonParser jsonParser) throws IOException, + JsonProcessingException { + jsonParser.configure(Feature.ALLOW_COMMENTS, true); + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.readTree(jsonParser); + } finally { + jsonParser.close(); + } + } + + /* Default for unit testing */PhoenixJson(final JsonNode node, final String jsonData) { + Preconditions.checkNotNull(node, "root node cannot be null for json"); + this.rootNode = node; + this.jsonAsString = jsonData; + } + + /** + * Get {@link PhoenixJson} for a given json paths. For example : + *

+ * + * {"f2":{"f3":1},"f4":{"f5":99,"f6":{"f7":"2"}}}' + * + *

+ * for this source json, if we want to know the json at path {'f4','f6'} it will return + * {@link PhoenixJson} object for json {"f7":"2"}. It always returns the last key if same key + * exist more than once. + *

+ * If the given path is unreachable then it throws {@link SQLException}. + * @param paths {@link String []} of path in the same order as they appear in json. + * @return {@link PhoenixJson} for the json against @paths. + * @throws SQLException + */ + public PhoenixJson getPhoenixJson(String[] paths) throws SQLException { + try { + PhoenixJson phoenixJson = getPhoenixJsonInternal(paths); + if (phoenixJson == null) { + throw new SQLException("path: " + Arrays.asList(paths) + " not found."); + } + return phoenixJson; + } catch (NumberFormatException nfe) { + throw new SQLException("path: " + Arrays.asList(paths) + " not found.", nfe); + } + } + + /** + * Get {@link PhoenixJson} for a given json paths. For example : + *

+ * + * {"f2":{"f3":1},"f4":{"f5":99,"f6":{"f7":"2"}}}' + * + *

+ * for this source json, if we want to know the json at path {'f4','f6'} it will return + * {@link PhoenixJson} object for json {"f7":"2"}. It always returns the last key if same key + * exist more than once. + *

+ * If the given path is unreachable then it return null. + * @param paths {@link String []} of path in the same order as they appear in json. + * @return {@link PhoenixJson} for the json against @paths. + */ + public PhoenixJson getPhoenixJsonOrNull(String[] paths) { + try { + return getPhoenixJsonInternal(paths); + } catch (NumberFormatException nfe) { + // ignore + } + return null; + } + + /** + * Serialize the current {@link PhoenixJson} to String. Its required for + * json_extract_path_text(). If we just return node.toString() it will wrap String value in + * double quote which is not the expectation, hence avoiding calling toString() on + * {@link JsonNode} until PhoenixJson represent a Json Array or container for Json object. If + * PhoenixJson just represent a {@link ValueNode} then it should return value returned from + * objects toString(). + */ + public String serializeToString() { + if (this.rootNode == null || this.rootNode.isNull()) { + return null; + } else if (this.rootNode.isValueNode()) { + + if (this.rootNode.isNumber()) { + return this.rootNode.getNumberValue().toString(); + } else if (this.rootNode.isBoolean()) { + return String.valueOf(this.rootNode.getBooleanValue()); + } else if (this.rootNode.isTextual()) { + return this.rootNode.getTextValue(); + } else { + return this.jsonAsString; + } + } else if (this.rootNode.isArray()) { + return this.jsonAsString; + } else if (this.rootNode.isContainerNode()) { + return this.jsonAsString; + } + + return null; + + } + + @Override + public String toString() { + return this.jsonAsString; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + this.jsonAsString.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + throw new EqualityNotSupportedException(PJson.INSTANCE); + } + + /** + * @return length of the string represented by the current {@link PhoenixJson}. + */ + public int estimateByteSize() { + String jsonStr = toString(); + return jsonStr == null ? 1 : jsonStr.length(); + } + + public byte[] toBytes() { + return Bytes.toBytes(this.jsonAsString); + } + + @Override + public int compareTo(PhoenixJson o) { + throw new EqualityNotSupportedException(PJson.INSTANCE); + } + + private PhoenixJson getPhoenixJsonInternal(String[] paths) { + JsonNode node = this.rootNode; + for (String path : paths) { + JsonNode nodeTemp = null; + if (node.isArray()) { + int index = Integer.parseInt(path); + nodeTemp = node.path(index); + } else { + nodeTemp = node.path(path); + } + if (nodeTemp == null || nodeTemp.isMissingNode()) { + return null; + } + node = nodeTemp; + } + return new PhoenixJson(node, node.toString()); + } + + public int getJsonArrayLength() { + int count = 0; + Iterator elements = this.rootNode.getElements(); + while(elements.hasNext()){ + elements.next(); + count++; + } + return count; + } + + public Object[] getJsonArrayElements() { + List elementlist = new ArrayList(); + Iterator elements = this.rootNode.getElements(); + while(elements.hasNext()){ + JsonNode e = elements.next(); + elementlist.add(e.toString()); + } + return elementlist.toArray(); + } + + public Object[] getJsonObjectKeys() { + List elementlist = new ArrayList(); + Iterator fieldnames = this.rootNode.getFieldNames(); + while(fieldnames.hasNext()){ + elementlist.add(fieldnames.next()); + } + return elementlist.toArray(); + } + + public Object[] getJsonFields() { + List elementlist = new ArrayList(); + Iterator> fields = this.rootNode.getFields(); + + while(fields.hasNext()){ + String fieldsstr = ""; + Map.Entry entry = fields.next(); + fieldsstr += entry.getKey() +","+entry.getValue().toString(); + elementlist.add(fieldsstr); + } + return elementlist.toArray(); + } + public String PopulateRecord(String [] types) { + String records = ""; + for(int i =0 ;i < types.length; i++){ + List nodelist =this.rootNode.findValues(types[i]); + if(nodelist.size()!=0){ + records += nodelist.get(0).toString(); + }else{ + records += "null"; + } + if(i != types.length-1){ + records +=","; + } + } + return records; + } + public Object[] PopulateRecordSet(String[] types) { + List recordslist = new ArrayList(); + Iterator elements = this.rootNode.getElements(); + while(elements.hasNext()){ + JsonNode e = elements.next(); + String records = ""; + for(int i =0 ;i < types.length; i++){ + List nodelist =e.findValues(types[i]); + if(nodelist.size()!=0){ + records += nodelist.get(0).toString(); + }else{ + records += "null"; + } + if(i != types.length-1){ + records +=","; + } + } + recordslist.add(records); + } + return recordslist.toArray(); + } + + + + + public static String DataToJsonValue(PDataType targetType, Object obj) { + String Value = null; + if (obj != null) { + if (PDataType.equalsAny(targetType, PUnsignedDouble.INSTANCE, PUnsignedFloat.INSTANCE, + PDouble.INSTANCE)) { + Value = PDouble.INSTANCE.toStringLiteral( obj,null); + + } else if (PDataType.equalsAny(targetType, PInteger.INSTANCE, PUnsignedSmallint.INSTANCE, + PUnsignedLong.INSTANCE, PUnsignedInt.INSTANCE,PUnsignedTinyint.INSTANCE)) { + Value = PLong.INSTANCE.toStringLiteral(obj,null); + }else if (PDataType.equalsAny(targetType, PBoolean.INSTANCE)) { + Value = PBoolean.INSTANCE.toStringLiteral(obj,null); + } else if (PDataType.equalsAny(targetType, PVarchar.INSTANCE,PChar.INSTANCE)) { + Value = "\""; + String tmp = PVarchar.INSTANCE.toStringLiteral(obj,null); + Value += tmp.substring(1,tmp.length()-1); + Value += "\""; + }else if (PDataType.equalsAny(targetType,PDate.INSTANCE,PTime.INSTANCE,PTimestamp.INSTANCE)){ + Value = "\""; + String tmp = PVarchar.INSTANCE.toStringLiteral(obj,null); + Value += tmp.substring(1,tmp.length()-1); + Value += "\""; + } else{ + Value = "\""; + String tmp = PVarchar.INSTANCE.toStringLiteral(obj,null); + Value += tmp.substring(1,tmp.length()-1); + Value += "\""; + } + }else{ + Value = "null"; + } + return Value ; + } + + +} From c8eb37c5e2a9df5b35bc92a169deba56f25599fe Mon Sep 17 00:00:00 2001 From: ictwanglei Date: Tue, 30 Jun 2015 19:35:16 +0800 Subject: [PATCH 17/24] 1 add apache license 2 add some javadocs and commemts in the code 3 define variables only at the place where it is used 4 use StringBuilder replace "+" operator 5 rename some method name in PhoenixJson 6 change some helper methods level in JsonFunctionTest --- .../function/ArrayToJsonFunction.java | 7 +- .../function/JsonArrayElementsFunction.java | 20 +- .../function/JsonArrayLengthFunction.java | 21 +- .../function/JsonPopulateRecordFunction.java | 2 +- .../JsonPopulateRecordSetFunction.java | 2 +- .../expression/function/ToJsonFunction.java | 5 +- .../phoenix/schema/json/PhoenixJson.java | 187 ++++++++++++------ .../expression/function/JsonFunctionTest.java | 147 +++++++------- 8 files changed, 248 insertions(+), 143 deletions(-) mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java mode change 100644 => 100755 phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java old mode 100644 new mode 100755 index 476d5f7112e..a7a0f1a0d1a --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java @@ -21,7 +21,7 @@ @FunctionParseNode.BuiltInFunction(name=ArrayToJsonFunction.NAME, args={ @FunctionParseNode.Argument(allowedTypes={PVarchar.class})} ) public class ArrayToJsonFunction extends ScalarFunction { - public static final String NAME = "Array_To_Json"; + public static final String NAME = "ARRAY_TO_JSON"; public ArrayToJsonFunction() { } @@ -52,7 +52,7 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { tmp.set(ptr.get()); PArrayDataType.positionAtArrayElement(tmp, i - 1,baseType, arrayExpr.getMaxLength()); Object re =baseType.toObject(tmp); - builder.append(PhoenixJson.DataToJsonValue(baseType, re)); + builder.append(PhoenixJson.dataToJsonValue(baseType, re)); if(i != length) builder.append(","); } @@ -64,7 +64,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { byte[] json = PJson.INSTANCE.toBytes(phoenixJson); ptr.set(json); } catch (SQLException sqe) { - System.out.println(sqe.getMessage()); + new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) + .setRootCause(sqe).build().buildException()); } return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java old mode 100644 new mode 100755 index f2b91fa9145..a10c1e28a52 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java @@ -39,13 +39,19 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (ptr.getLength() == 0) { return false; } - PhoenixJson phoenixJson = - (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), - ptr.getLength()); - Object[] elements = phoenixJson.getJsonArrayElements(); - PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); - byte[] array = PVarcharArray.INSTANCE.toBytes(pa); - ptr.set(array); + try { + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + Object[] elements = phoenixJson.getJsonArrayElements(); + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + } + catch (SQLException sqe) { + new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) + .setRootCause(sqe).build().buildException()); + } return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java old mode 100644 new mode 100755 index ae234828ae8..43bf1b7c407 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java @@ -2,13 +2,17 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable; import org.apache.phoenix.compile.KeyPart; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.parse.FunctionParseNode.Argument; import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction; +import org.apache.phoenix.schema.IllegalDataException; import org.apache.phoenix.schema.json.PhoenixJson; import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.*; +import java.sql.SQLException; import java.util.List; @BuiltInFunction(name = JsonArrayLengthFunction.NAME, args = { @@ -34,12 +38,17 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (ptr.getLength() == 0) { return false; } - PhoenixJson phoenixJson = - (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), - ptr.getLength()); - int length = phoenixJson.getJsonArrayLength(); - byte[] array = PInteger.INSTANCE.toBytes(length); - ptr.set(array); + try{ + PhoenixJson phoenixJson = + (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), + ptr.getLength()); + int length = phoenixJson.getJsonArrayLength(); + byte[] array = PInteger.INSTANCE.toBytes(length); + ptr.set(array); + } catch (SQLException sqe) { + new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) + .setRootCause(sqe).build().buildException()); + } return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java old mode 100644 new mode 100755 index 835a1a3cc75..78d54130de0 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java @@ -56,7 +56,7 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { ptr.getLength()); try { String[] types = (String[]) phoenixArray.getArray(); - String records = phoenixJson.PopulateRecord(types); + String records = phoenixJson.jsonPopulateRecord(types); byte[] array = PVarchar.INSTANCE.toBytes(records); ptr.set(array); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java old mode 100644 new mode 100755 index 1062cf6edc4..8186cba33c6 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java @@ -55,7 +55,7 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { ptr.getLength()); try { String[] types = (String[]) phoenixArray.getArray(); - Object[] records = phoenixJson.PopulateRecordSet(types); + Object[] records = phoenixJson.jsonPopulateRecordSet(types); PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, records); byte[] array = PVarcharArray.INSTANCE.toBytes(pa); ptr.set(array); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java old mode 100644 new mode 100755 index 04736651566..7cc3cfe5566 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java @@ -46,13 +46,14 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { } PDataType baseType = expression.getDataType(); Object re =baseType.toObject(ptr); - String jsons = PhoenixJson.DataToJsonValue(baseType, re); + String jsons = PhoenixJson.dataToJsonValue(baseType, re); try { PhoenixJson phoenixJson = PhoenixJson.getInstance(jsons); byte[] json = PJson.INSTANCE.toBytes(phoenixJson); ptr.set(json); } catch (SQLException sqe) { - System.out.println(sqe.getMessage()); + new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) + .setRootCause(sqe).build().buildException()); } return true; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java old mode 100644 new mode 100755 index 736ec690f8c..67dec361bce --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.sql.SQLException; +import java.text.Format; import java.util.Arrays; import java.util.ArrayList; import java.util.Iterator; @@ -236,26 +237,48 @@ private PhoenixJson getPhoenixJsonInternal(String[] paths) { return new PhoenixJson(node, node.toString()); } - public int getJsonArrayLength() { - int count = 0; - Iterator elements = this.rootNode.getElements(); - while(elements.hasNext()){ - elements.next(); - count++; - } - return count; + /** + * If the current {@link PhoenixJson} is a JsonArray,then it returns the length of the JsonArray + * For example:[1,2,3] ,it will return 3 + * Its required for json_array_length(). + * @throws SQLException + */ + public int getJsonArrayLength() throws SQLException { + if(this.rootNode.isArray()){ + return this.rootNode.size(); + }else{ + throw new SQLException("The JsonNode should be an Array"); + } } - public Object[] getJsonArrayElements() { - List elementlist = new ArrayList(); - Iterator elements = this.rootNode.getElements(); - while(elements.hasNext()){ - JsonNode e = elements.next(); - elementlist.add(e.toString()); + /** + * If the current {@link PhoenixJson} is a JsonArray,then it returns the set of array elements + * For example:[1,false,[2,"string"]] + * it will return (new Object[]{"1","false","[2,\"string\"]"}) + * Its required for json_array_elements(). + * @return {@link String[]} as the set of JSON elements + * @throws SQLException + */ + public Object[] getJsonArrayElements() throws SQLException { + if(this.rootNode.isArray()) { + List elementlist = new ArrayList(); + Iterator elements = this.rootNode.getElements(); + while (elements.hasNext()) { + JsonNode e = elements.next(); + elementlist.add(e.toString()); + } + return elementlist.toArray(); + }else{ + throw new SQLException("The JsonNode should be an Array"); } - return elementlist.toArray(); } - + /** + * It returns the set of JSON keys for the current {@link PhoenixJson}.Only the outermost keys will be generated + * For example:{"f1":"abc","f2":{"f3":"a", "f4":"b"}} + * it will return (new Object[]{"f1","f2"}) + * Its required for json_object_keys(). + * @return {@link String[]} as the set of JSON keys + */ public Object[] getJsonObjectKeys() { List elementlist = new ArrayList(); Iterator fieldnames = this.rootNode.getFieldNames(); @@ -265,91 +288,145 @@ public Object[] getJsonObjectKeys() { return elementlist.toArray(); } + /** + * It returns the SET of JSON key/value pairs for the current {@link PhoenixJson}.Only the outermost key/value will be generated + * Probably it seems we should use a special SET class which can hold different types of elements. + * but we use the {@link org.apache.phoenix.schema.types.PVarcharArray} as an alternative of SET TYPE + * when implementing the build-in function ,so we use {@link String} directly to store the pair + * and in this case we use "," to separate key and value + * For example:{"f1":"abc","f2":"edf"} + * it will return (new Object[]{"f1,abc","f2,edf"}) + * + * Its required for json_each(). + * @return {@link String[]} as the SET of JSON key/value pairs + */ public Object[] getJsonFields() { List elementlist = new ArrayList(); Iterator> fields = this.rootNode.getFields(); - while(fields.hasNext()){ - String fieldsstr = ""; Map.Entry entry = fields.next(); - fieldsstr += entry.getKey() +","+entry.getValue().toString(); - elementlist.add(fieldsstr); + StringBuilder fieldBuilder = new StringBuilder(); + fieldBuilder.append(entry.getKey()); + fieldBuilder.append(","); + fieldBuilder.append(entry.getValue().toString()); + elementlist.add(fieldBuilder.toString()); } return elementlist.toArray(); } - public String PopulateRecord(String [] types) { - String records = ""; + /** + * Expands the object in current {@link PhoenixJson} to a record whose columns match the record type defined by base. + * Conversion will be best effort; columns in base with no corresponding key will be left null. + * If a column is specified more than once, the last value is used. + * Also use "," to separate each columns + * For example:types :{"a","b"} json: {"a":"1","b":"2"} + * it will return new String("1,2") + * + * Its required for json_populate_record(). + * @param types {@link String} the record type + * @return {@link String} as the result record + */ + public String jsonPopulateRecord(String [] types) { + StringBuilder recordsBuilder = new StringBuilder(); for(int i =0 ;i < types.length; i++){ List nodelist =this.rootNode.findValues(types[i]); if(nodelist.size()!=0){ - records += nodelist.get(0).toString(); + recordsBuilder.append(nodelist.get(0).toString()); }else{ - records += "null"; + recordsBuilder.append("null"); } if(i != types.length-1){ - records +=","; + recordsBuilder.append(","); } } - return records; + return recordsBuilder.toString(); } - public Object[] PopulateRecordSet(String[] types) { - List recordslist = new ArrayList(); + /** + * Expands the outermost set of objects in current {@link PhoenixJson} to a SET of records whose columns match the record type defined by base. + * Conversion will be best effort; columns in base with no corresponding key will be left null. + * If a column is specified more than once, the last value is used. + * Also use "," to separate each columns + * For example:types :{"a","b"} json: {[{"a":"1","b":"2"},{"a":"3","b":"4"}]} + * it will return (new Object[]{"1,2","3,4"}) + * + * Its required for json_populate_recordset(). + * @param types {@link String} the record type + * @return {@link String[]} as the SET of records + */ + public Object[] jsonPopulateRecordSet(String[] types) { + List recordsList = new ArrayList(); Iterator elements = this.rootNode.getElements(); while(elements.hasNext()){ JsonNode e = elements.next(); - String records = ""; + StringBuilder recordsBuilder = new StringBuilder(); for(int i =0 ;i < types.length; i++){ List nodelist =e.findValues(types[i]); if(nodelist.size()!=0){ - records += nodelist.get(0).toString(); + recordsBuilder.append(nodelist.get(0).toString()); }else{ - records += "null"; + recordsBuilder.append("null"); } if(i != types.length-1){ - records +=","; + recordsBuilder.append(","); } } - recordslist.add(records); + recordsList.add(recordsBuilder.toString()); } - return recordslist.toArray(); + return recordsList.toArray(); } - - - public static String DataToJsonValue(PDataType targetType, Object obj) { - String Value = null; + /** + * Returns the value as JSON. + * If the data type is not built in, and there is a cast from the type to json, + * the cast function will be used to perform the conversion. + * Otherwise, for any value other than a number, a Boolean, or a null value, + * the text representation will be used, escaped and quoted so that it is legal JSON. + * If the formatter is given ,perform the conversion based on it. + * + * Its required for to_json(),array_to_json(). + * @param targetType {@link PDataType} type of the value + * @param obj {@link Object} the value object + * @param formatter {@link Format} format of the value + * @return {@link String} as JSON + */ + public static String dataToJsonValue(PDataType targetType, Object obj,Format formatter) { + StringBuilder valueBuilder = new StringBuilder(); if (obj != null) { if (PDataType.equalsAny(targetType, PUnsignedDouble.INSTANCE, PUnsignedFloat.INSTANCE, PDouble.INSTANCE)) { - Value = PDouble.INSTANCE.toStringLiteral( obj,null); + valueBuilder.append(PDouble.INSTANCE.toStringLiteral(obj, formatter)); } else if (PDataType.equalsAny(targetType, PInteger.INSTANCE, PUnsignedSmallint.INSTANCE, PUnsignedLong.INSTANCE, PUnsignedInt.INSTANCE,PUnsignedTinyint.INSTANCE)) { - Value = PLong.INSTANCE.toStringLiteral(obj,null); + valueBuilder.append(PLong.INSTANCE.toStringLiteral(obj, formatter)); }else if (PDataType.equalsAny(targetType, PBoolean.INSTANCE)) { - Value = PBoolean.INSTANCE.toStringLiteral(obj,null); + valueBuilder.append(PBoolean.INSTANCE.toStringLiteral(obj, formatter)); } else if (PDataType.equalsAny(targetType, PVarchar.INSTANCE,PChar.INSTANCE)) { - Value = "\""; - String tmp = PVarchar.INSTANCE.toStringLiteral(obj,null); - Value += tmp.substring(1,tmp.length()-1); - Value += "\""; + valueBuilder.append("\""); + String tmp = PVarchar.INSTANCE.toStringLiteral(obj,formatter); + valueBuilder.append(tmp.substring(1,tmp.length()-1)); + valueBuilder.append("\""); }else if (PDataType.equalsAny(targetType,PDate.INSTANCE,PTime.INSTANCE,PTimestamp.INSTANCE)){ - Value = "\""; - String tmp = PVarchar.INSTANCE.toStringLiteral(obj,null); - Value += tmp.substring(1,tmp.length()-1); - Value += "\""; + valueBuilder.append("\""); + String tmp = PVarchar.INSTANCE.toStringLiteral(obj,formatter); + valueBuilder.append(tmp.substring(1,tmp.length()-1)); + valueBuilder.append("\""); } else{ - Value = "\""; - String tmp = PVarchar.INSTANCE.toStringLiteral(obj,null); - Value += tmp.substring(1,tmp.length()-1); - Value += "\""; + valueBuilder.append("\""); + String tmp = PVarchar.INSTANCE.toStringLiteral(obj,formatter); + valueBuilder.append(tmp.substring(1,tmp.length()-1)); + valueBuilder.append("\""); } }else{ - Value = "null"; + valueBuilder.append("null"); } - return Value ; + return valueBuilder.toString() ; + } + + public static String dataToJsonValue(PDataType targetType, Object obj){ + return dataToJsonValue(targetType,obj,null); } + } diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java old mode 100644 new mode 100755 index 7fa988f776a..5a7128cf8ac --- a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; @@ -18,16 +35,27 @@ import static org.junit.Assert.assertEquals; +/** + * Unit tests for JSON build-in function. + * Testing function includes below: + * {@link ArrayToJsonFunction} + * {@link JsonArrayElementsFunction} + * {@link JsonPopulateRecordFunction} + * {@link JsonPopulateRecordSetFunction} + * {@link JsonArrayLengthFunction} + * {@link JsonObjectKeysFunction} + * {@link ToJsonFunction} + * {@link JsonEachFunction} + * + */ public class JsonFunctionTest { public static final String TEST_JSON_STR = "{\"f2\":{\"f3\":\"value\"},\"f4\":{\"f5\":99,\"f6\":[1,true,\"foo\"]},\"f7\":true}"; - public PhoenixJson testArrayToJson (Object[] array,PDataType datatype,PArrayDataType arraydatatype) throws Exception { - LiteralExpression arrayExpr; - List children; - PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( datatype,array); - arrayExpr = LiteralExpression.newConstant(pa,arraydatatype ); - children = Arrays.asList(arrayExpr); + private static PhoenixJson testArrayToJson (Object[] array,PDataType datatype,PArrayDataType arraydatatype) throws Exception { + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray( datatype,array); + LiteralExpression arrayExpr = LiteralExpression.newConstant(pa,arraydatatype ); + List children = Arrays.asList(arrayExpr); ArrayToJsonFunction e = new ArrayToJsonFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -39,18 +67,18 @@ public PhoenixJson testArrayToJson (Object[] array,PDataType datatype,PArrayData public void testNumberArrayToJson() throws Exception { Object[] testarray = new Object[]{1,12,32,432}; PhoenixJson result = testArrayToJson(testarray, PInteger.INSTANCE, PIntegerArray.INSTANCE); - String expected ="[1,12,32,432]"; + String expected = "[1,12,32,432]"; assertEquals(result.serializeToString(), expected); Object[] testarray2 = new Object[]{1.12,12.34,32.45,432.78}; PhoenixJson result2 = testArrayToJson(testarray2, PDouble.INSTANCE, PDoubleArray.INSTANCE); - String expected2 ="[1.12,12.34,32.45,432.78]"; + String expected2 = "[1.12,12.34,32.45,432.78]"; assertEquals(result2.serializeToString(), expected2); } @Test public void testBooleanArrayToJson() throws Exception { Object[] testarray = new Object[]{false,true}; PhoenixJson result = testArrayToJson(testarray, PBoolean.INSTANCE, PBooleanArray.INSTANCE); - String expected ="[false,true]"; + String expected = "[false,true]"; assertEquals(result.toString(), expected); } @@ -92,12 +120,10 @@ public void testDateArrayToJson() throws Exception { } - public String[] JsonArrayElements (String json) throws Exception { + private static String[] jsonArrayElements (String json) throws Exception { PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr; - List children; - JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - children = Arrays.asList(JsonExpr); + LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + List children = Arrays.asList(JsonExpr); JsonArrayElementsFunction e = new JsonArrayElementsFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -109,21 +135,18 @@ public String[] JsonArrayElements (String json) throws Exception { public void testJsonArrayElements() throws Exception { String json = "[1,true,\"string\",[2,false]]"; Object[] expected = new Object[]{"1","true","\"string\"","[2,false]"}; - String[] result = JsonArrayElements(json); - + String[] result = jsonArrayElements(json); assertEquals(result.length, expected.length); for(int i = 0; i children; + private static String jsonPopulateRecord (Object[] types,String json) throws Exception { PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( PVarchar.INSTANCE,types); LiteralExpression typesExpr = LiteralExpression.newConstant(pa,PVarcharArray.INSTANCE ); PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr; - JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - children = Arrays.asList(typesExpr,JsonExpr); + LiteralExpression JsonExpr= LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + List children = Arrays.asList(typesExpr,JsonExpr); JsonPopulateRecordFunction e = new JsonPopulateRecordFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -136,18 +159,16 @@ public void testJsonPopulateRecord() throws Exception { Object[] types= new Object[]{"a","b"}; String json = "{\"a\":1,\"b\":2}"; String expected = "1,2"; - String result = JsonPopulateRecord(types,json); + String result = jsonPopulateRecord(types, json); assertEquals(result, expected); } - public String[] JsonPopulateRecordSet (Object[] types,String json) throws Exception { - List children; + private static String[] jsonPopulateRecordSet (Object[] types,String json) throws Exception { PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( PVarchar.INSTANCE,types); LiteralExpression typesExpr = LiteralExpression.newConstant(pa,PVarcharArray.INSTANCE ); PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr; - JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - children = Arrays.asList(typesExpr,JsonExpr); + LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + List children = Arrays.asList(typesExpr,JsonExpr); JsonPopulateRecordSetFunction e = new JsonPopulateRecordSetFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -161,19 +182,17 @@ public void testJsonPopulateRecordSet() throws Exception { Object[] types= new Object[]{"a","b"}; String json = "[{\"a\":1,\"b\":2},{\"a\":2,\"b\":3},{\"a\":4,\"b\":5}]"; Object[] expected = new Object[]{"1,2","2,3","4,5"}; - String[] result = JsonPopulateRecordSet(types,json); + String[] result = jsonPopulateRecordSet(types, json); assertEquals(result.length, expected.length); for(int i = 0; i children; - JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - children = Arrays.asList(JsonExpr); + LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + List children= Arrays.asList(JsonExpr); JsonArrayLengthFunction e = new JsonArrayLengthFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -185,16 +204,14 @@ public int JsonArrayLength (String json) throws Exception { public void testJsonArrayLength() throws Exception { String array1 = "[1,true,\"string\",[2,false]]"; String array2 = "[1,2.34,[1,\"abc\"],4,true,\"string\",[2,false]]"; - assertEquals(JsonArrayLength(array1),4); - assertEquals(JsonArrayLength(array2),7); + assertEquals(jsonArrayLength(array1),4); + assertEquals(jsonArrayLength(array2),7); } - public PhoenixJson ToJson (Object obj,PDataType datatype) throws Exception { - List children; - //LiteralExpression op =LiteralExpression.newConstant(new BigDecimal("9999.1"), PDecimal.INSTANCE); + private static PhoenixJson toJson (Object obj,PDataType datatype) throws Exception { LiteralExpression op =LiteralExpression.newConstant(obj, datatype); - children = Arrays.asList(op); + List children = Arrays.asList(op); ToJsonFunction e = new ToJsonFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -205,28 +222,26 @@ public PhoenixJson ToJson (Object obj,PDataType datatype) throws Exception { @Test public void testToJson() throws Exception { - assertEquals(ToJson(-256, PInteger.INSTANCE).serializeToString(),"-256"); - assertEquals(ToJson(-256, PLong.INSTANCE).serializeToString(),"-256"); - assertEquals(ToJson(-1, PSmallint.INSTANCE).serializeToString(),"-1"); - assertEquals(ToJson(-1, PTinyint.INSTANCE).serializeToString(),"-1"); - assertEquals(ToJson(12, PUnsignedInt.INSTANCE).serializeToString(),"12"); - assertEquals(ToJson(12, PUnsignedSmallint.INSTANCE).serializeToString(),"12"); - assertEquals(ToJson(12, PUnsignedLong.INSTANCE).serializeToString(),"12"); - assertEquals(ToJson(123.456, PDouble.INSTANCE).serializeToString(),"123.456"); - assertEquals(ToJson(123.456, PFloat.INSTANCE).serializeToString(),"123.456"); - assertEquals(ToJson(123.456,PUnsignedDouble.INSTANCE).serializeToString(),"123.456"); - assertEquals(ToJson(123.456, PUnsignedFloat.INSTANCE).serializeToString(),"123.456"); - assertEquals(ToJson(false, PBoolean.INSTANCE).serializeToString(),"false"); - assertEquals(ToJson(true, PBoolean.INSTANCE).serializeToString(),"true"); - assertEquals(ToJson("string_abc", PVarchar.INSTANCE).toString(),"\"string_abc\""); - assertEquals(ToJson("string_abc", PVarchar.INSTANCE).serializeToString(),"string_abc"); + assertEquals(toJson(-256, PInteger.INSTANCE).serializeToString(),"-256"); + assertEquals(toJson(-256, PLong.INSTANCE).serializeToString(),"-256"); + assertEquals(toJson(-1, PSmallint.INSTANCE).serializeToString(),"-1"); + assertEquals(toJson(-1, PTinyint.INSTANCE).serializeToString(),"-1"); + assertEquals(toJson(12, PUnsignedInt.INSTANCE).serializeToString(),"12"); + assertEquals(toJson(12, PUnsignedSmallint.INSTANCE).serializeToString(),"12"); + assertEquals(toJson(12, PUnsignedLong.INSTANCE).serializeToString(),"12"); + assertEquals(toJson(123.456, PDouble.INSTANCE).serializeToString(),"123.456"); + assertEquals(toJson(123.456, PFloat.INSTANCE).serializeToString(),"123.456"); + assertEquals(toJson(123.456, PUnsignedDouble.INSTANCE).serializeToString(),"123.456"); + assertEquals(toJson(123.456, PUnsignedFloat.INSTANCE).serializeToString(),"123.456"); + assertEquals(toJson(false, PBoolean.INSTANCE).serializeToString(),"false"); + assertEquals(toJson(true, PBoolean.INSTANCE).serializeToString(),"true"); + assertEquals(toJson("string_abc", PVarchar.INSTANCE).toString(),"\"string_abc\""); + assertEquals(toJson("string_abc", PVarchar.INSTANCE).serializeToString(),"string_abc"); } - public String[] JsonObjectKeys (String json) throws Exception { + private static String[] jsonObjectKeys (String json) throws Exception { PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr; - List children; - JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - children = Arrays.asList(JsonExpr); + LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + List children = Arrays.asList(JsonExpr); JsonObjectKeysFunction e = new JsonObjectKeysFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -234,12 +249,10 @@ public String[] JsonObjectKeys (String json) throws Exception { return (String[] )pa.getArray(); } - - @Test public void testJsonObjectKeys() throws Exception { Object[] expected = new Object[]{"f2","f4","f7"}; - String[] result = JsonObjectKeys(TEST_JSON_STR); + String[] result = jsonObjectKeys(TEST_JSON_STR); assertEquals(result.length, expected.length); for(int i = 0; i children; - JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - children = Arrays.asList(JsonExpr); + LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); + List children = Arrays.asList(JsonExpr); JsonEachFunction e = new JsonEachFunction(children); ImmutableBytesWritable ptr = new ImmutableBytesWritable(); boolean evaluated = e.evaluate(null, ptr); @@ -262,7 +273,7 @@ public String[] JsonEach (String json) throws Exception { @Test public void testJsonEach() throws Exception { Object[] expected = new Object[]{"f2,{\"f3\":\"value\"}","f4,{\"f5\":99,\"f6\":[1,true,\"foo\"]}","f7,true"}; - String[] result = JsonEach(TEST_JSON_STR); + String[] result = jsonEach(TEST_JSON_STR); assertEquals(result.length, expected.length); for(int i = 0; i Date: Wed, 1 Jul 2015 08:59:53 +0800 Subject: [PATCH 18/24] add apache license --- .../function/ArrayToJsonFunction.java | 17 +++++++++++++++++ .../function/JsonArrayElementsFunction.java | 17 +++++++++++++++++ .../function/JsonArrayLengthFunction.java | 17 +++++++++++++++++ .../expression/function/JsonEachFunction.java | 17 +++++++++++++++++ .../function/JsonObjectKeysFunction.java | 17 +++++++++++++++++ .../function/JsonPopulateRecordFunction.java | 17 +++++++++++++++++ .../function/JsonPopulateRecordSetFunction.java | 17 +++++++++++++++++ .../expression/function/ToJsonFunction.java | 17 +++++++++++++++++ 8 files changed, 136 insertions(+) mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java index a7a0f1a0d1a..33e388a3898 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java index a10c1e28a52..cf61671fced 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java index 43bf1b7c407..ba9c18c1f13 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java old mode 100644 new mode 100755 index 3737b837955..dcf6c7ba48c --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java old mode 100644 new mode 100755 index 10d0d0be910..87208552e58 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java index 78d54130de0..4066428fb44 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java index 8186cba33c6..2978db66c53 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java index 7cc3cfe5566..da9ddc3e313 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java @@ -1,3 +1,20 @@ +/* + * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; From 684ed5264aabaad8afc8a261e09cabeacf214ee1 Mon Sep 17 00:00:00 2001 From: ictwanglei Date: Mon, 20 Jul 2015 11:51:28 +0800 Subject: [PATCH 19/24] fix bugs add End2End Test --- .../end2end/JsonArrayLengthFunctionIT.java | 114 ++++++++++++++++++ .../phoenix/expression/ExpressionType.java | 100 ++------------- .../phoenix/schema/json/PhoenixJson.java | 40 +++--- 3 files changed, 144 insertions(+), 110 deletions(-) create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java mode change 100644 => 100755 phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java new file mode 100755 index 00000000000..1f911acf4d3 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java @@ -0,0 +1,114 @@ +/** + * + */ +package org.apache.phoenix.end2end; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Properties; +import org.apache.phoenix.expression.function.JsonArrayLengthFunction; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +/** + * End to end test for {@link JsonArrayLengthFunction}. + * + */ +public class JsonArrayLengthFunctionIT extends BaseHBaseManagedTimeIT { + + @Test + public void testJsonArrayLengthWithWhereClause() throws Exception { + Connection conn = getConnection(); + String json = "[1,2,3]"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT col1 FROM testJson WHERE json_array_length(col1) = 3"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals("Json data read from DB is not as expected.", json, + rs.getString(1)); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonArrayLengthWhenFirstArgumentIsJsonString() + throws Exception { + Connection conn = getConnection(); + String json = "[1,2,true,[\"string\",3]]"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_array_length(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals("Json data read from DB is not as expected.", + 4, rs.getInt(1)); + + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonArrayElementsWithWhereClause() throws Exception { + Connection conn = getConnection(); + String json = "[1,2,3]"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT col1 FROM testJson WHERE ARRAY_ELEM(json_array_elements(col1),2) = 2"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals("Json data read from DB is not as expected.", json, + rs.getString(1)); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + + + private void populateJsonTable(Connection conn, String json, String pk) + throws SQLException { + String ddl = "CREATE TABLE testJson" + + " (pk VARCHAR NOT NULL PRIMARY KEY, " + "col1 json)"; + createTestTable(getUrl(), ddl); + + String query = "UPSERT INTO testJson(pk, col1) VALUES(?,?)"; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, pk); + stmt.setString(2, json); + stmt.execute(); + conn.commit(); + } + + private Connection getConnection() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + return conn; + } +} 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 old mode 100644 new mode 100755 index 51f40898b4b..af27cda5f2c --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/ExpressionType.java @@ -19,95 +19,7 @@ import java.util.Map; -import org.apache.phoenix.expression.function.AbsFunction; -import org.apache.phoenix.expression.function.ArrayAllComparisonExpression; -import org.apache.phoenix.expression.function.ArrayAnyComparisonExpression; -import org.apache.phoenix.expression.function.ArrayAppendFunction; -import org.apache.phoenix.expression.function.ArrayConcatFunction; -import org.apache.phoenix.expression.function.ArrayElemRefExpression; -import org.apache.phoenix.expression.function.ArrayIndexFunction; -import org.apache.phoenix.expression.function.ArrayLengthFunction; -import org.apache.phoenix.expression.function.ArrayPrependFunction; -import org.apache.phoenix.expression.function.ByteBasedRegexpReplaceFunction; -import org.apache.phoenix.expression.function.ByteBasedRegexpSplitFunction; -import org.apache.phoenix.expression.function.ByteBasedRegexpSubstrFunction; -import org.apache.phoenix.expression.function.CbrtFunction; -import org.apache.phoenix.expression.function.CeilDateExpression; -import org.apache.phoenix.expression.function.CeilDecimalExpression; -import org.apache.phoenix.expression.function.CeilFunction; -import org.apache.phoenix.expression.function.CeilTimestampExpression; -import org.apache.phoenix.expression.function.CoalesceFunction; -import org.apache.phoenix.expression.function.ConvertTimezoneFunction; -import org.apache.phoenix.expression.function.CountAggregateFunction; -import org.apache.phoenix.expression.function.DayOfMonthFunction; -import org.apache.phoenix.expression.function.DecodeFunction; -import org.apache.phoenix.expression.function.DistinctCountAggregateFunction; -import org.apache.phoenix.expression.function.DistinctValueAggregateFunction; -import org.apache.phoenix.expression.function.EncodeFunction; -import org.apache.phoenix.expression.function.ExpFunction; -import org.apache.phoenix.expression.function.ExternalSqlTypeIdFunction; -import org.apache.phoenix.expression.function.FirstValueFunction; -import org.apache.phoenix.expression.function.FloorDateExpression; -import org.apache.phoenix.expression.function.FloorDecimalExpression; -import org.apache.phoenix.expression.function.FloorFunction; -import org.apache.phoenix.expression.function.HourFunction; -import org.apache.phoenix.expression.function.IndexStateNameFunction; -import org.apache.phoenix.expression.function.InstrFunction; -import org.apache.phoenix.expression.function.InvertFunction; -import org.apache.phoenix.expression.function.LTrimFunction; -import org.apache.phoenix.expression.function.LastValueFunction; -import org.apache.phoenix.expression.function.LengthFunction; -import org.apache.phoenix.expression.function.LnFunction; -import org.apache.phoenix.expression.function.LogFunction; -import org.apache.phoenix.expression.function.LowerFunction; -import org.apache.phoenix.expression.function.LpadFunction; -import org.apache.phoenix.expression.function.MD5Function; -import org.apache.phoenix.expression.function.MaxAggregateFunction; -import org.apache.phoenix.expression.function.MinAggregateFunction; -import org.apache.phoenix.expression.function.MinuteFunction; -import org.apache.phoenix.expression.function.MonthFunction; -import org.apache.phoenix.expression.function.NowFunction; -import org.apache.phoenix.expression.function.NthValueFunction; -import org.apache.phoenix.expression.function.PercentRankAggregateFunction; -import org.apache.phoenix.expression.function.PercentileContAggregateFunction; -import org.apache.phoenix.expression.function.PercentileDiscAggregateFunction; -import org.apache.phoenix.expression.function.PowerFunction; -import org.apache.phoenix.expression.function.RTrimFunction; -import org.apache.phoenix.expression.function.RandomFunction; -import org.apache.phoenix.expression.function.RegexpReplaceFunction; -import org.apache.phoenix.expression.function.RegexpSplitFunction; -import org.apache.phoenix.expression.function.RegexpSubstrFunction; -import org.apache.phoenix.expression.function.ReverseFunction; -import org.apache.phoenix.expression.function.RoundDateExpression; -import org.apache.phoenix.expression.function.RoundDecimalExpression; -import org.apache.phoenix.expression.function.RoundFunction; -import org.apache.phoenix.expression.function.RoundTimestampExpression; -import org.apache.phoenix.expression.function.SQLIndexTypeFunction; -import org.apache.phoenix.expression.function.SQLTableTypeFunction; -import org.apache.phoenix.expression.function.SQLViewTypeFunction; -import org.apache.phoenix.expression.function.SecondFunction; -import org.apache.phoenix.expression.function.SignFunction; -import org.apache.phoenix.expression.function.SqlTypeNameFunction; -import org.apache.phoenix.expression.function.SqrtFunction; -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; -import org.apache.phoenix.expression.function.ToCharFunction; -import org.apache.phoenix.expression.function.ToDateFunction; -import org.apache.phoenix.expression.function.ToNumberFunction; -import org.apache.phoenix.expression.function.ToTimeFunction; -import org.apache.phoenix.expression.function.ToTimestampFunction; -import org.apache.phoenix.expression.function.TrimFunction; -import org.apache.phoenix.expression.function.TruncFunction; -import org.apache.phoenix.expression.function.UDFExpression; -import org.apache.phoenix.expression.function.UpperFunction; -import org.apache.phoenix.expression.function.WeekFunction; -import org.apache.phoenix.expression.function.YearFunction; +import org.apache.phoenix.expression.function.*; import com.google.common.collect.Maps; @@ -247,7 +159,15 @@ public enum ExpressionType { LogFunction(LogFunction.class), ExpFunction(ExpFunction.class), PowerFunction(PowerFunction.class), - ArrayConcatFunction(ArrayConcatFunction.class) + ArrayConcatFunction(ArrayConcatFunction.class), + JsonArrayLengthFunction(JsonArrayLengthFunction.class), + JsonArrayElementsFunction(JsonArrayElementsFunction.class), + JsonEachFunction(JsonEachFunction.class), + JsonObjectKeysFunction(JsonObjectKeysFunction.class), + JsonPopulateRecordFunction(JsonPopulateRecordFunction.class), + JsonPopulateRecordSetFunction(JsonPopulateRecordSetFunction.class), + ToJsonFunction(ToJsonFunction.class), + ArrayToJsonFunction(ArrayToJsonFunction.class) ; ExpressionType(Class clazz) { diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java index 67dec361bce..fd7a44bfbbb 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java @@ -238,9 +238,9 @@ private PhoenixJson getPhoenixJsonInternal(String[] paths) { } /** - * If the current {@link PhoenixJson} is a JsonArray,then it returns the length of the JsonArray - * For example:[1,2,3] ,it will return 3 - * Its required for json_array_length(). + * If the current {@link PhoenixJson} is a JsonArray,then it returns the length of the JsonArray. + *

For example:[1,2,3] ,it will return 3 + *

Its required for json_array_length(). * @throws SQLException */ public int getJsonArrayLength() throws SQLException { @@ -252,11 +252,11 @@ public int getJsonArrayLength() throws SQLException { } /** - * If the current {@link PhoenixJson} is a JsonArray,then it returns the set of array elements - * For example:[1,false,[2,"string"]] + * If the current {@link PhoenixJson} is a JsonArray,then it returns the set of array elements. + *

For example:[1,false,[2,"string"]] * it will return (new Object[]{"1","false","[2,\"string\"]"}) - * Its required for json_array_elements(). - * @return {@link String[]} as the set of JSON elements + *

Its required for json_array_elements(). + * @return {@link String []} as the set of JSON elements * @throws SQLException */ public Object[] getJsonArrayElements() throws SQLException { @@ -274,10 +274,10 @@ public Object[] getJsonArrayElements() throws SQLException { } /** * It returns the set of JSON keys for the current {@link PhoenixJson}.Only the outermost keys will be generated - * For example:{"f1":"abc","f2":{"f3":"a", "f4":"b"}} + *

For example:{"f1":"abc","f2":{"f3":"a", "f4":"b"}} * it will return (new Object[]{"f1","f2"}) - * Its required for json_object_keys(). - * @return {@link String[]} as the set of JSON keys + *

Its required for json_object_keys(). + * @return {@link String []} as the set of JSON keys */ public Object[] getJsonObjectKeys() { List elementlist = new ArrayList(); @@ -294,11 +294,11 @@ public Object[] getJsonObjectKeys() { * but we use the {@link org.apache.phoenix.schema.types.PVarcharArray} as an alternative of SET TYPE * when implementing the build-in function ,so we use {@link String} directly to store the pair * and in this case we use "," to separate key and value - * For example:{"f1":"abc","f2":"edf"} + *

For example:{"f1":"abc","f2":"edf"} * it will return (new Object[]{"f1,abc","f2,edf"}) * - * Its required for json_each(). - * @return {@link String[]} as the SET of JSON key/value pairs + *

Its required for json_each(). + * @return {@link String []} as the SET of JSON key/value pairs */ public Object[] getJsonFields() { List elementlist = new ArrayList(); @@ -318,10 +318,10 @@ public Object[] getJsonFields() { * Conversion will be best effort; columns in base with no corresponding key will be left null. * If a column is specified more than once, the last value is used. * Also use "," to separate each columns - * For example:types :{"a","b"} json: {"a":"1","b":"2"} + *

For example:types :{"a","b"} json: {"a":"1","b":"2"} * it will return new String("1,2") * - * Its required for json_populate_record(). + *

Its required for json_populate_record(). * @param types {@link String} the record type * @return {@link String} as the result record */ @@ -345,12 +345,12 @@ public String jsonPopulateRecord(String [] types) { * Conversion will be best effort; columns in base with no corresponding key will be left null. * If a column is specified more than once, the last value is used. * Also use "," to separate each columns - * For example:types :{"a","b"} json: {[{"a":"1","b":"2"},{"a":"3","b":"4"}]} + *

For example:types :{"a","b"} json: {[{"a":"1","b":"2"},{"a":"3","b":"4"}]} * it will return (new Object[]{"1,2","3,4"}) * - * Its required for json_populate_recordset(). + *

Its required for json_populate_recordset(). * @param types {@link String} the record type - * @return {@link String[]} as the SET of records + * @return {@link String []} as the SET of records */ public Object[] jsonPopulateRecordSet(String[] types) { List recordsList = new ArrayList(); @@ -377,13 +377,13 @@ public Object[] jsonPopulateRecordSet(String[] types) { /** * Returns the value as JSON. - * If the data type is not built in, and there is a cast from the type to json, + *

If the data type is not built in, and there is a cast from the type to json, * the cast function will be used to perform the conversion. * Otherwise, for any value other than a number, a Boolean, or a null value, * the text representation will be used, escaped and quoted so that it is legal JSON. * If the formatter is given ,perform the conversion based on it. * - * Its required for to_json(),array_to_json(). + *

Its required for to_json(),array_to_json(). * @param targetType {@link PDataType} type of the value * @param obj {@link Object} the value object * @param formatter {@link Format} format of the value From dfad2949604de1035899398f026a1dc351490b20 Mon Sep 17 00:00:00 2001 From: ictwanglei Date: Sun, 26 Jul 2015 11:22:53 +0800 Subject: [PATCH 20/24] add end2end test files add SQLExceptionCode fix bugs --- .../end2end/ArrayToJsonFunctionIT.java | 150 +++++++++++++++ .../end2end/JsonArrayElementsFunctionIT.java | 175 ++++++++++++++++++ .../end2end/JsonArrayLengthFunctionIT.java | 121 +++++++++--- .../phoenix/end2end/JsonEachFunctionIT.java | 107 +++++++++++ .../end2end/JsonObjectKeysFunctionIT.java | 108 +++++++++++ .../end2end/JsonPopulateRecordFunctionIT.java | 101 ++++++++++ .../JsonPopulateRecordSetFunctionIT.java | 139 ++++++++++++++ .../phoenix/end2end/ToJsonFunctionIT.java | 117 ++++++++++++ .../phoenix/exception/SQLExceptionCode.java | 1 + .../function/ArrayToJsonFunction.java | 37 ++-- .../function/JsonArrayElementsFunction.java | 13 +- .../function/JsonArrayLengthFunction.java | 3 +- .../expression/function/JsonEachFunction.java | 11 +- .../function/JsonObjectKeysFunction.java | 11 +- .../JsonPopulateRecordSetFunction.java | 6 +- .../expression/function/ToJsonFunction.java | 7 +- .../phoenix/schema/json/PhoenixJson.java | 98 +++++----- 17 files changed, 1085 insertions(+), 120 deletions(-) create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonEachFunctionIT.java create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonObjectKeysFunctionIT.java create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordFunctionIT.java create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java create mode 100755 phoenix-core/src/it/java/org/apache/phoenix/end2end/ToJsonFunctionIT.java diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java new file mode 100755 index 00000000000..d3a9413a0be --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java @@ -0,0 +1,150 @@ +/* + * 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.ROW1; +import static org.apache.phoenix.util.TestUtil.TABLE_WITH_ARRAY; +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.sql.*; +import java.util.Properties; +import org.apache.phoenix.expression.function.ArrayToJsonFunction; +import org.apache.phoenix.query.BaseTest; +import org.apache.phoenix.util.PhoenixRuntime; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.ArrayToJsonFunction}. + * + */ +public class ArrayToJsonFunctionIT extends BaseHBaseManagedTimeIT { + private static final String TABLE_WITH_ALL_ARRAY_TYPES = "TABLE_WITH_ALL_ARRAY_TYPES"; + + @Test + public void testArrayToJsonWithAllArrayTypes() throws Exception { + // create the table + createTableWithAllArrayTypes(getUrl()); + + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + + try{ + // populate the table with data + PreparedStatement stmt = + conn.prepareStatement("UPSERT INTO " + + TABLE_WITH_ALL_ARRAY_TYPES + + "(pk, BOOLEAN_ARRAY, BYTE_ARRAY, DOUBLE_ARRAY, FLOAT_ARRAY, INT_ARRAY, LONG_ARRAY, SHORT_ARRAY, STRING_ARRAY)\n" + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); + + stmt.setString(1, "valueOne"); + + // boolean array + Array boolArray = conn.createArrayOf("BOOLEAN", new Boolean[] { true,false }); + int boolIndex = 2; + stmt.setArray(boolIndex, boolArray); + // byte array + Array byteArray = conn.createArrayOf("TINYINT", new Byte[] { 11, 22 }); + int byteIndex = 3; + stmt.setArray(byteIndex, byteArray); + // double array + Array doubleArray = conn.createArrayOf("DOUBLE", new Double[] { 67.78, 78.89 }); + int doubleIndex = 4; + stmt.setArray(doubleIndex, doubleArray); + // float array + Array floatArray = conn.createArrayOf("FLOAT", new Float[] { 12.23f, 45.56f }); + int floatIndex = 5; + stmt.setArray(floatIndex, floatArray); + // int array + Array intArray = conn.createArrayOf("INTEGER", new Integer[] { 5555, 6666 }); + int intIndex = 6; + stmt.setArray(intIndex, intArray); + // long array + Array longArray = conn.createArrayOf("BIGINT", new Long[] { 7777777L, 8888888L }); + int longIndex = 7; + stmt.setArray(longIndex, longArray); + // short array + Array shortArray = conn.createArrayOf("SMALLINT", new Short[] { 333, 444 }); + int shortIndex = 8; + stmt.setArray(shortIndex, shortArray); + // create character array + Array stringArray = conn.createArrayOf("VARCHAR", new String[] { "a", "b" }); + int stringIndex = 9; + stmt.setArray(stringIndex, stringArray); + stmt.execute(); + conn.commit(); + + stmt = + conn.prepareStatement("SELECT pk, " + + "array_to_json(boolean_array), " + + "array_to_json(byte_array), " + + "array_to_json(double_array), " + + "array_to_json(float_array), " + + "array_to_json(int_array), " + + "array_to_json(long_array), " + + "array_to_json(short_array)," + + "array_to_json(string_array) FROM " + + TABLE_WITH_ALL_ARRAY_TYPES); + + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + + assertEquals("valueOne", rs.getString(1)); + assertArrayToJson(rs, boolIndex, "[true,false]"); + assertArrayToJson(rs, byteIndex, "[11,22]"); + assertArrayToJson(rs, doubleIndex, "[67.78,78.89]"); + assertArrayToJson(rs, floatIndex, "[12.23,45.56]"); + assertArrayToJson(rs, intIndex, "[5555,6666]"); + assertArrayToJson(rs, longIndex, "[7777777,8888888]"); + assertArrayToJson(rs, shortIndex, "[333,444]"); + assertArrayToJson(rs, stringIndex, "[\"a\",\"b\"]"); + + } finally { + conn.close(); + } + + } + + + private void assertArrayToJson(ResultSet rs, int arrayIndex, String expectedJson) + throws SQLException { + assertEquals("Json array data is not as expected.",expectedJson, rs.getString(arrayIndex)); + } + + + private static void createTableWithAllArrayTypes(String url) throws SQLException { + String ddlStmt = "create table " + + TABLE_WITH_ALL_ARRAY_TYPES + + " (pk VARCHAR NOT NULL PRIMARY KEY,\n" + + " boolean_array boolean array[2],\n" + + " byte_array tinyint[2],\n" + + " double_array double[2],\n" + + " float_array float[2],\n" + + " int_array integer[2],\n" + + " long_array bigint[5],\n" + + " short_array smallint[2],\n" + + " string_array varchar(100) array[2]" + + ")"; + createTestTable(url, ddlStmt); + } + +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java new file mode 100755 index 00000000000..5b54f003da8 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java @@ -0,0 +1,175 @@ +/* + * 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 org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +import java.sql.*; +import java.util.Properties; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.*; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.JsonArrayElementsFunction}. + * + */ +public class JsonArrayElementsFunctionIT extends BaseHBaseManagedTimeIT { + + @Test + public void testJsonArrayElementsWithSameType() throws Exception { + Connection conn = getConnection(); + + try { + String json = "[25.343,36.763,37.56,386.63]"; + String pk = "valueOne"; + + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_array_elements(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[4]; + strArr[0] = "25.343"; + strArr[1] = "36.763"; + strArr[2] = "37.56"; + strArr[3] = "386.63"; + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("Json array elements is not as expected.", resultArray, + array); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + @Test + public void testJsonArrayElementsWithDifferentDataTypes() throws Exception { + Connection conn = getConnection(); + + try { + String json = "[1,36.763,false,\"string\"]"; + String pk = "valueOne"; + + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_array_elements(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[4]; + strArr[0] = "1"; + strArr[1] = "36.763"; + strArr[2] = "false"; + strArr[3] = "\"string\""; + + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + + assertEquals("Json array elements is not as expected.", resultArray, + array); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + @Test + public void testJsonArrayElementsWithNestJson() throws Exception { + Connection conn = getConnection(); + + try { + String json = "[1,[1,true,\"string\"]]"; + String pk = "valueOne"; + + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_array_elements(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[2]; + strArr[0] = "1"; + strArr[1] = "[1,true,\"string\"]"; + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("Json array elements is not as expected.", resultArray, + array); + + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonArrayElementsWithInvalidJson() throws Exception { + Connection conn = getConnection(); + String json = "{\"f1\":1,\"f2\":\"abc\"}"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_array_elements(col1) FROM testJson WHERE pk = 'valueOne'"; + + try { + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + rs.getArray(1); + fail("The Json Node should be an array!"); + } catch (SQLException sqe) { + assertEquals("SQL error code is not as expected.", + SQLExceptionCode.JSON_NODE_MISMATCH.getErrorCode(), sqe.getErrorCode()); + assertEquals("SQL state is not expected.", "22001", + sqe.getSQLState()); + } + + } finally { + conn.close(); + } + } + + + private void populateJsonTable(Connection conn, String json, String pk) + throws SQLException { + String ddl = "CREATE TABLE testJson" + + " (pk VARCHAR NOT NULL PRIMARY KEY, " + "col1 json)"; + createTestTable(getUrl(), ddl); + + String query = "UPSERT INTO testJson(pk, col1) VALUES(?,?)"; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, pk); + stmt.setString(2, json); + stmt.execute(); + conn.commit(); + } + + private Connection getConnection() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + return conn; + } +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java index 1f911acf4d3..c0116c6d3de 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java @@ -1,12 +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.end2end; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.sql.Connection; import java.sql.DriverManager; @@ -14,7 +26,10 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; + +import org.apache.phoenix.exception.SQLExceptionCode; import org.apache.phoenix.expression.function.JsonArrayLengthFunction; +import org.apache.phoenix.schema.IllegalDataException; import org.apache.phoenix.util.PropertiesUtil; import org.junit.Test; @@ -25,7 +40,7 @@ public class JsonArrayLengthFunctionIT extends BaseHBaseManagedTimeIT { @Test - public void testJsonArrayLengthWithWhereClause() throws Exception { + public void testJsonArrayLengthWithIntTypeInWhereClause() throws Exception { Connection conn = getConnection(); String json = "[1,2,3]"; String pk = "valueOne"; @@ -36,7 +51,7 @@ public void testJsonArrayLengthWithWhereClause() throws Exception { PreparedStatement stmt = conn.prepareStatement(selectQuery); ResultSet rs = stmt.executeQuery(); assertTrue(rs.next()); - assertEquals("Json data read from DB is not as expected.", json, + assertEquals("Json data is not as expected.", json, rs.getString(1)); assertFalse(rs.next()); @@ -45,38 +60,58 @@ public void testJsonArrayLengthWithWhereClause() throws Exception { } } - @Test - public void testJsonArrayLengthWhenFirstArgumentIsJsonString() - throws Exception { - Connection conn = getConnection(); - String json = "[1,2,true,[\"string\",3]]"; - String pk = "valueOne"; - try { - populateJsonTable(conn, json, pk); + @Test + public void testJsonArrayLengthWithDoubleType() throws Exception { + Connection conn = getConnection(); + String json = "[1.23,2.34,3.56,54.3]"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); - String selectQuery = "SELECT json_array_length(col1) FROM testJson WHERE pk = 'valueOne'"; - PreparedStatement stmt = conn.prepareStatement(selectQuery); - ResultSet rs = stmt.executeQuery(); - assertTrue(rs.next()); - assertEquals("Json data read from DB is not as expected.", - 4, rs.getInt(1)); + String selectQuery = "SELECT json_array_length(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals("Json array length is not as expected.", 4, + rs.getInt(1)); + assertFalse(rs.next()); - assertFalse(rs.next()); + } finally { + conn.close(); + } + } - } finally { - conn.close(); - } - } + @Test + public void testJsonArrayLengthWithDifferentDataTypes() + throws Exception { + Connection conn = getConnection(); + String json = "[1,2.3,true,\"f1\",[\"string\",3]]"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + String selectQuery = "SELECT json_array_length(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals("Json array length is not as expected.", + 5, rs.getInt(1)); + + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } @Test - public void testJsonArrayElementsWithWhereClause() throws Exception { + public void testJsonArrayLengthWithNestedJson() throws Exception { Connection conn = getConnection(); - String json = "[1,2,3]"; + String json = "[1,\"string\",false,[1.23,[true,\"ok\"]]]"; String pk = "valueOne"; try { populateJsonTable(conn, json, pk); - String selectQuery = "SELECT col1 FROM testJson WHERE ARRAY_ELEM(json_array_elements(col1),2) = 2"; + String selectQuery = "SELECT col1 FROM testJson WHERE json_array_length(col1) = 4"; PreparedStatement stmt = conn.prepareStatement(selectQuery); ResultSet rs = stmt.executeQuery(); assertTrue(rs.next()); @@ -88,6 +123,34 @@ public void testJsonArrayElementsWithWhereClause() throws Exception { conn.close(); } } + @Test + public void testJsonArrayLengthWithInvalidJson() throws Exception { + Connection conn = getConnection(); + String json = "{\"f1\":1,\"f2\":\"abc\"}"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_array_length(col1) FROM testJson WHERE pk = 'valueOne'"; + + try { + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + assertEquals("Json array length is not as expected.", + 2, rs.getInt(1)); + fail("The Json Node should be an array!"); + } catch (SQLException sqe) { + assertEquals("SQL error code is not as expected.", + SQLExceptionCode.JSON_NODE_MISMATCH.getErrorCode(), sqe.getErrorCode()); + assertEquals("SQL state is not expected.", "22001", + sqe.getSQLState()); + } + + } finally { + conn.close(); + } + } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonEachFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonEachFunctionIT.java new file mode 100755 index 00000000000..c26a55704ce --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonEachFunctionIT.java @@ -0,0 +1,107 @@ +/* + * 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 org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +import java.sql.*; +import java.util.Properties; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.*; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.JsonEachFunction}. + * + */ +public class JsonEachFunctionIT extends BaseHBaseManagedTimeIT { + + @Test + public void testJsonEach() throws Exception { + Connection conn = getConnection(); + + try{ + String json = + "{\"f2\":{\"f3\":\"value\"},\"f4\":{\"f5\":99,\"f6\":[1,true,\"foo\"]},\"f7\":true}"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_each(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[]{ + "f2,{\"f3\":\"value\"}", + "f4,{\"f5\":99,\"f6\":[1,true,\"foo\"]}", + "f7,true"}; + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("JsonEach return data is not as expected.", resultArray, + array); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonEachWithNullKey() throws Exception { + Connection conn = getConnection(); + + try{ + String json = + "100"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_each(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("JsonEach return data is not as expected.", resultArray, + null); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + private void populateJsonTable(Connection conn, String json, String pk) + throws SQLException { + String ddl = "CREATE TABLE testJson" + + " (pk VARCHAR NOT NULL PRIMARY KEY, " + "col1 json)"; + createTestTable(getUrl(), ddl); + + String query = "UPSERT INTO testJson(pk, col1) VALUES(?,?)"; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, pk); + stmt.setString(2, json); + stmt.execute(); + conn.commit(); + } + + private Connection getConnection() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + return conn; + } +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonObjectKeysFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonObjectKeysFunctionIT.java new file mode 100755 index 00000000000..79ef7da52d3 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonObjectKeysFunctionIT.java @@ -0,0 +1,108 @@ +/* + * 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 org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +import java.sql.*; +import java.util.Properties; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.*; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.JsonObjectKeysFunction}. + * + */ +public class JsonObjectKeysFunctionIT extends BaseHBaseManagedTimeIT { + + @Test + public void testJsonObjectKeys() throws Exception { + Connection conn = getConnection(); + + try{ + String json = + "{\"f2\":{\"f3\":\"value\"},\"f4\":{\"f5\":99,\"f6\":[1,true,\"foo\"]},\"f7\":true}"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_object_keys(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[]{ + "f2", + "f4", + "f7"}; + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("JsonObjectKeys return data is not as expected.", resultArray, + array); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonObjectKeysWithNullKey() throws Exception { + Connection conn = getConnection(); + + try{ + String json = + "[1,2,3]"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_object_keys(col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("JsonObjectKeys return data is not as expected.", resultArray, + null); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + + private void populateJsonTable(Connection conn, String json, String pk) + throws SQLException { + String ddl = "CREATE TABLE testJson" + + " (pk VARCHAR NOT NULL PRIMARY KEY, " + "col1 json)"; + createTestTable(getUrl(), ddl); + + String query = "UPSERT INTO testJson(pk, col1) VALUES(?,?)"; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, pk); + stmt.setString(2, json); + stmt.execute(); + conn.commit(); + } + + private Connection getConnection() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + return conn; + } +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordFunctionIT.java new file mode 100755 index 00000000000..5c8c4842542 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordFunctionIT.java @@ -0,0 +1,101 @@ +/* + * 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 org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +import java.sql.*; +import java.util.Properties; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.*; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.JsonPopulateRecordFunction}. + * + */ +public class JsonPopulateRecordFunctionIT extends BaseHBaseManagedTimeIT { + + @Test + public void testJsonPopulateRecord() throws Exception { + Connection conn = getConnection(); + + try{ + String json = "[{\"a\":1,\"b\":2}]"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_populate_record(ARRAY['a','b'],col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String expectedstr = "1,2"; + assertEquals("json_populate_record return data is not as expected.", expectedstr, + rs.getString(1)); + assertFalse(rs.next()); + + + } finally { + conn.close(); + } + } + @Test + public void testJsonPopulateRecordWithNullValue() throws Exception { + Connection conn = getConnection(); + + try{ + String json = "[{\"a\":1,\"c\":2}]"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_populate_record(ARRAY['a','b'],col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String expectedstr = "1,null"; + assertEquals("json_populate_record return data is not as expected.", expectedstr, + rs.getString(1)); + assertFalse(rs.next()); + + + } finally { + conn.close(); + } + } + + private void populateJsonTable(Connection conn, String json, String pk) + throws SQLException { + String ddl = "CREATE TABLE testJson" + + " (pk VARCHAR NOT NULL PRIMARY KEY, " + "col1 json)"; + createTestTable(getUrl(), ddl); + + String query = "UPSERT INTO testJson(pk, col1) VALUES(?,?)"; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, pk); + stmt.setString(2, json); + stmt.execute(); + conn.commit(); + } + + private Connection getConnection() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + return conn; + } +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java new file mode 100755 index 00000000000..6c0e5286e81 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java @@ -0,0 +1,139 @@ +/* + * 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 org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +import java.sql.*; +import java.util.Properties; + +import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.*; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.JsonPopulateRecordSetFunction}. + * + */ +public class JsonPopulateRecordSetFunctionIT extends BaseHBaseManagedTimeIT { + + @Test + public void testJsonPopulateRecordSet() throws Exception { + Connection conn = getConnection(); + + try{ + String json = "[{\"a\":1,\"b\":2,\"c\":3},{\"a\":3,\"e\":4,\"b\":4},{\"a\":6,\"b\":8}]"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_populate_recordset(ARRAY['a','b'],col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[]{ + "1,2", + "3,4", + "6,8"}; + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("json_populate_recordset return data is not as expected.", resultArray, + array); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonPopulateRecordSetWithNullValues() throws Exception { + Connection conn = getConnection(); + + try{ + String json = "[{\"a1\":1,\"b1\":2,\"f3\":false},{\"a\":3,\"e\":4},{\"b\":\"hello\"}]"; + populateJsonTable(conn, json, "valueOne"); + + String selectQuery = "SELECT json_populate_recordset(ARRAY['a','b'],col1) FROM testJson WHERE pk = 'valueOne'"; + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + String[] strArr = new String[]{ + "null,null", + "3,null", + "null,\"hello\""}; + Array array = conn.createArrayOf("VARCHAR", strArr); + PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); + assertEquals("json_populate_recordset return data is not as expected.", resultArray, + array); + assertFalse(rs.next()); + + } finally { + conn.close(); + } + } + + @Test + public void testJsonPopulateRecordSetWithInvalidJson() throws Exception { + Connection conn = getConnection(); + String json = "{\"a\":1,\"b\":2}"; + String pk = "valueOne"; + try { + populateJsonTable(conn, json, pk); + + String selectQuery = "SELECT json_populate_recordset(ARRAY['a','b'],col1) FROM testJson WHERE pk = 'valueOne'"; + + try { + PreparedStatement stmt = conn.prepareStatement(selectQuery); + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + rs.getArray(1); + fail("The Json Node should be an array!"); + } catch (SQLException sqe) { + assertEquals("SQL error code is not as expected.", + SQLExceptionCode.JSON_NODE_MISMATCH.getErrorCode(), sqe.getErrorCode()); + assertEquals("SQL state is not expected.", "22001", + sqe.getSQLState()); + } + + } finally { + conn.close(); + } + } + + private void populateJsonTable(Connection conn, String json, String pk) + throws SQLException { + String ddl = "CREATE TABLE testJson" + + " (pk VARCHAR NOT NULL PRIMARY KEY, " + "col1 json)"; + createTestTable(getUrl(), ddl); + + String query = "UPSERT INTO testJson(pk, col1) VALUES(?,?)"; + PreparedStatement stmt = conn.prepareStatement(query); + stmt.setString(1, pk); + stmt.setString(2, json); + stmt.execute(); + conn.commit(); + } + + private Connection getConnection() throws SQLException { + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + return conn; + } +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToJsonFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToJsonFunctionIT.java new file mode 100755 index 00000000000..1c9553a3acd --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ToJsonFunctionIT.java @@ -0,0 +1,117 @@ +/* + * 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 org.apache.phoenix.query.BaseTest; +import org.apache.phoenix.util.PhoenixRuntime; +import org.apache.phoenix.util.PropertiesUtil; +import org.junit.Test; + +import java.sql.*; +import java.util.Properties; + +import static org.apache.phoenix.util.TestUtil.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * End to end test for {@link org.apache.phoenix.expression.function.ToJsonFunction}. + * + */ +public class ToJsonFunctionIT extends BaseHBaseManagedTimeIT { + private static final String TABLE_WITH_ALL_TYPES = "TABLE_WITH_ALL_TYPES"; + + @Test + public void testToJsonWithAllTypes() throws Exception { + // create the table + createTableWithAllTypes(getUrl()); + + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + try { + // populate the table with data + PreparedStatement stmt = + conn.prepareStatement("UPSERT INTO " + + TABLE_WITH_ALL_TYPES + + "(pk, BOOLEAN_COL, BYTE_COl, DOUBLE_COL, FLOAT_COL, INT_COL, LONG_COL, SHORT_COL, STRING_COL)" + + " VALUES ('valueOne', " + + "true," + + "11, " + + "67.78," + + "12.23," + + "5555," + + "7777777," + + "333," + + "'string')"); + stmt.execute(); + conn.commit(); + + stmt = + conn.prepareStatement("SELECT pk, " + + "to_json(boolean_col), " + + "to_json(byte_col), " + + "to_json(double_col), " + + "to_json(float_col), " + + "to_json(int_col), " + + "to_json(long_col), " + + "to_json(short_col)," + + "to_json(string_col) FROM " + + TABLE_WITH_ALL_TYPES + + " where pk = 'valueOne'"); + + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + + assertEquals("valueOne", rs.getString(1)); + assertToJson(rs, 2, "true"); + assertToJson(rs, 3, "11"); + assertToJson(rs, 4, "67.78"); + assertToJson(rs, 5, "12.23"); + assertToJson(rs, 6, "5555"); + assertToJson(rs, 7, "7777777"); + assertToJson(rs, 8, "333"); + assertToJson(rs, 9, "\"string\""); + }finally { + conn.close(); + } + } + + + private void assertToJson(ResultSet rs, int arrayIndex, String expectedJson) + throws SQLException { + assertEquals("Json data read from DB is not as expected.",expectedJson, rs.getString(arrayIndex)); + } + + + private static void createTableWithAllTypes(String url) throws SQLException { + String ddlStmt = "create table " + + TABLE_WITH_ALL_TYPES + + " (pk VARCHAR NOT NULL PRIMARY KEY,\n" + + " boolean_col boolean,\n" + + " byte_col tinyint,\n" + + " double_col double,\n" + + " float_col float,\n" + + " int_col integer,\n" + + " long_col bigint,\n" + + " short_col smallint,\n" + + " string_col varchar(100) " + + ")"; + createTestTable(url, ddlStmt); + } +} diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java index 674766175f9..8590e08858b 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java @@ -87,6 +87,7 @@ public SQLException newException(SQLExceptionInfo info) { AMBIGUOUS_JOIN_CONDITION(217, "22017", "Amibiguous or non-equi join condition specified. Consider using table list with where clause."), CONSTRAINT_VIOLATION(218, "22018", "Constraint violatioin."), INVALID_JSON_DATA(219, "22000", "Invalid json data."), + JSON_NODE_MISMATCH(220, "22001", "json node should be an array."), /** * Constraint Violation (errorcode 03, sqlstate 23) diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java index 33e388a3898..68f632c6dac 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java @@ -36,7 +36,7 @@ @FunctionParseNode.BuiltInFunction(name=ArrayToJsonFunction.NAME, args={ - @FunctionParseNode.Argument(allowedTypes={PVarchar.class})} ) + @FunctionParseNode.Argument(allowedTypes={PBinaryArray.class, PVarbinaryArray.class})}) public class ArrayToJsonFunction extends ScalarFunction { public static final String NAME = "ARRAY_TO_JSON"; @@ -57,32 +57,27 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (ptr.getLength() == 0) { return false; } - - - PDataType baseType = PDataType.fromTypeId(arrayExpr.getDataType() - .getSqlType() - - PDataType.ARRAY_TYPE_BASE); - int length = PArrayDataType.getArrayLength(ptr, baseType, arrayExpr.getMaxLength()); - StringBuilder builder = new StringBuilder("["); - ImmutableBytesWritable tmp = new ImmutableBytesWritable(); - for(int i=1;i<=length;i++){ - tmp.set(ptr.get()); - PArrayDataType.positionAtArrayElement(tmp, i - 1,baseType, arrayExpr.getMaxLength()); - Object re =baseType.toObject(tmp); - builder.append(PhoenixJson.dataToJsonValue(baseType, re)); - if(i != length) - builder.append(","); - } - builder.append("]"); - try { + PDataType baseType = PDataType.fromTypeId(arrayExpr.getDataType() + .getSqlType() + - PDataType.ARRAY_TYPE_BASE); + int length = PArrayDataType.getArrayLength(ptr, baseType, arrayExpr.getMaxLength()); + StringBuilder builder = new StringBuilder("["); + for(int i=1;i<=length;i++){ + ImmutableBytesWritable tmp = new ImmutableBytesWritable(ptr.get(),ptr.getOffset(),ptr.getLength()); + PArrayDataType.positionAtArrayElement(tmp, i - 1,baseType, arrayExpr.getMaxLength()); + Object re = baseType.toObject(tmp.get(),tmp.getOffset(),tmp.getLength()); + builder.append(PhoenixJson.dataToJsonValue(baseType, re)); + if(i != length) + builder.append(","); + } + builder.append("]"); String str = builder.toString(); PhoenixJson phoenixJson = PhoenixJson.getInstance(str); byte[] json = PJson.INSTANCE.toBytes(phoenixJson); ptr.set(json); } catch (SQLException sqe) { - new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) - .setRootCause(sqe).build().buildException()); + new IllegalDataException(sqe); } return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java index cf61671fced..a64f483c40d 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java @@ -61,13 +61,16 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), ptr.getLength()); Object[] elements = phoenixJson.getJsonArrayElements(); - PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); - byte[] array = PVarcharArray.INSTANCE.toBytes(pa); - ptr.set(array); + if(elements == null || elements.length == 0){ + ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); + }else{ + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + } } catch (SQLException sqe) { - new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) - .setRootCause(sqe).build().buildException()); + throw new IllegalDataException(sqe); } return true; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java index ba9c18c1f13..67ae6b17b6c 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java @@ -63,8 +63,7 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { byte[] array = PInteger.INSTANCE.toBytes(length); ptr.set(array); } catch (SQLException sqe) { - new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) - .setRootCause(sqe).build().buildException()); + throw new IllegalDataException(sqe); } return true; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java index dcf6c7ba48c..25ad42ce6ac 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java @@ -25,6 +25,7 @@ import org.apache.phoenix.schema.json.PhoenixJson; import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.*; +import org.apache.phoenix.util.ByteUtil; import java.util.List; @@ -55,9 +56,13 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), ptr.getLength()); Object[] elements = phoenixJson.getJsonFields(); - PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); - byte[] array = PVarcharArray.INSTANCE.toBytes(pa); - ptr.set(array); + if(elements == null || elements.length == 0){ + ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); + }else{ + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + } return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java index 87208552e58..b68e385e2a4 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java @@ -25,6 +25,7 @@ import org.apache.phoenix.schema.json.PhoenixJson; import org.apache.phoenix.schema.tuple.Tuple; import org.apache.phoenix.schema.types.*; +import org.apache.phoenix.util.ByteUtil; import java.util.List; @@ -55,9 +56,13 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), ptr.getLength()); Object[] elements = phoenixJson.getJsonObjectKeys(); - PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); - byte[] array = PVarcharArray.INSTANCE.toBytes(pa); - ptr.set(array); + if(elements == null || elements.length == 0){ + ptr.set(ByteUtil.EMPTY_BYTE_ARRAY); + }else{ + PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, elements); + byte[] array = PVarcharArray.INSTANCE.toBytes(pa); + ptr.set(array); + } return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java index 2978db66c53..32503475a51 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java @@ -76,13 +76,9 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { PhoenixArray pa = PArrayDataType.instantiatePhoenixArray(PVarchar.INSTANCE, records); byte[] array = PVarcharArray.INSTANCE.toBytes(pa); ptr.set(array); - - } catch (SQLException sqe) { - new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) - .setRootCause(sqe).build().buildException()); + throw new IllegalDataException(sqe); } - return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java index da9ddc3e313..dc8165a9eaa 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java @@ -28,10 +28,7 @@ import org.apache.phoenix.schema.IllegalDataException; import org.apache.phoenix.schema.json.PhoenixJson; import org.apache.phoenix.schema.tuple.Tuple; -import org.apache.phoenix.schema.types.PDataType; -import org.apache.phoenix.schema.types.PJson; -import org.apache.phoenix.schema.types.PVarcharArray; -import org.apache.phoenix.schema.types.PhoenixArray; +import org.apache.phoenix.schema.types.*; import org.apache.phoenix.util.ByteUtil; import java.io.IOException; @@ -39,7 +36,7 @@ import java.util.List; @BuiltInFunction(name = ToJsonFunction.NAME, args = { - @Argument(allowedTypes = { PDataType.class })}) + @Argument(allowedTypes={PBinary.class, PVarbinary.class})}) public class ToJsonFunction extends ScalarFunction { public static final String NAME = "TO_JSON"; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java index fd7a44bfbbb..0eaea72c156 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/json/PhoenixJson.java @@ -247,9 +247,10 @@ public int getJsonArrayLength() throws SQLException { if(this.rootNode.isArray()){ return this.rootNode.size(); }else{ - throw new SQLException("The JsonNode should be an Array"); + throw new SQLExceptionInfo.Builder(SQLExceptionCode.JSON_NODE_MISMATCH) + .build().buildException(); } - } +} /** * If the current {@link PhoenixJson} is a JsonArray,then it returns the set of array elements. @@ -269,7 +270,8 @@ public Object[] getJsonArrayElements() throws SQLException { } return elementlist.toArray(); }else{ - throw new SQLException("The JsonNode should be an Array"); + throw new SQLExceptionInfo.Builder(SQLExceptionCode.JSON_NODE_MISMATCH) + .build().buildException(); } } /** @@ -280,12 +282,17 @@ public Object[] getJsonArrayElements() throws SQLException { * @return {@link String []} as the set of JSON keys */ public Object[] getJsonObjectKeys() { - List elementlist = new ArrayList(); - Iterator fieldnames = this.rootNode.getFieldNames(); - while(fieldnames.hasNext()){ - elementlist.add(fieldnames.next()); + if(this.rootNode.isObject()){ + List elementlist = new ArrayList(); + Iterator fieldnames = this.rootNode.getFieldNames(); + while(fieldnames.hasNext()){ + elementlist.add(fieldnames.next()); + } + return elementlist.toArray(); + }else{ + return null; } - return elementlist.toArray(); + } /** @@ -301,17 +308,22 @@ public Object[] getJsonObjectKeys() { * @return {@link String []} as the SET of JSON key/value pairs */ public Object[] getJsonFields() { - List elementlist = new ArrayList(); - Iterator> fields = this.rootNode.getFields(); - while(fields.hasNext()){ - Map.Entry entry = fields.next(); - StringBuilder fieldBuilder = new StringBuilder(); - fieldBuilder.append(entry.getKey()); - fieldBuilder.append(","); - fieldBuilder.append(entry.getValue().toString()); - elementlist.add(fieldBuilder.toString()); + if(this.rootNode.isObject()){ + List elementlist = new ArrayList(); + Iterator> fields = this.rootNode.getFields(); + while(fields.hasNext()){ + Map.Entry entry = fields.next(); + StringBuilder fieldBuilder = new StringBuilder(); + fieldBuilder.append(entry.getKey()); + fieldBuilder.append(","); + fieldBuilder.append(entry.getValue().toString()); + elementlist.add(fieldBuilder.toString()); + } + return elementlist.toArray(); + }else{ + return null; } - return elementlist.toArray(); + } /** * Expands the object in current {@link PhoenixJson} to a record whose columns match the record type defined by base. @@ -326,9 +338,13 @@ public Object[] getJsonFields() { * @return {@link String} as the result record */ public String jsonPopulateRecord(String [] types) { + return jsonPopulateRecord(this.rootNode,types); + } + + private String jsonPopulateRecord(JsonNode e,String [] types) { StringBuilder recordsBuilder = new StringBuilder(); for(int i =0 ;i < types.length; i++){ - List nodelist =this.rootNode.findValues(types[i]); + List nodelist =e.findValues(types[i]); if(nodelist.size()!=0){ recordsBuilder.append(nodelist.get(0).toString()); }else{ @@ -352,26 +368,20 @@ public String jsonPopulateRecord(String [] types) { * @param types {@link String} the record type * @return {@link String []} as the SET of records */ - public Object[] jsonPopulateRecordSet(String[] types) { - List recordsList = new ArrayList(); - Iterator elements = this.rootNode.getElements(); - while(elements.hasNext()){ - JsonNode e = elements.next(); - StringBuilder recordsBuilder = new StringBuilder(); - for(int i =0 ;i < types.length; i++){ - List nodelist =e.findValues(types[i]); - if(nodelist.size()!=0){ - recordsBuilder.append(nodelist.get(0).toString()); - }else{ - recordsBuilder.append("null"); - } - if(i != types.length-1){ - recordsBuilder.append(","); - } + public Object[] jsonPopulateRecordSet(String[] types)throws SQLException { + if(this.rootNode.isArray()) { + List recordsList = new ArrayList(); + Iterator elements = this.rootNode.getElements(); + while(elements.hasNext()){ + JsonNode e = elements.next(); + recordsList.add(jsonPopulateRecord(e,types)); } - recordsList.add(recordsBuilder.toString()); + return recordsList.toArray(); + }else{ + throw new SQLExceptionInfo.Builder(SQLExceptionCode.JSON_NODE_MISMATCH) + .build().buildException(); } - return recordsList.toArray(); + } @@ -392,16 +402,7 @@ public Object[] jsonPopulateRecordSet(String[] types) { public static String dataToJsonValue(PDataType targetType, Object obj,Format formatter) { StringBuilder valueBuilder = new StringBuilder(); if (obj != null) { - if (PDataType.equalsAny(targetType, PUnsignedDouble.INSTANCE, PUnsignedFloat.INSTANCE, - PDouble.INSTANCE)) { - valueBuilder.append(PDouble.INSTANCE.toStringLiteral(obj, formatter)); - - } else if (PDataType.equalsAny(targetType, PInteger.INSTANCE, PUnsignedSmallint.INSTANCE, - PUnsignedLong.INSTANCE, PUnsignedInt.INSTANCE,PUnsignedTinyint.INSTANCE)) { - valueBuilder.append(PLong.INSTANCE.toStringLiteral(obj, formatter)); - }else if (PDataType.equalsAny(targetType, PBoolean.INSTANCE)) { - valueBuilder.append(PBoolean.INSTANCE.toStringLiteral(obj, formatter)); - } else if (PDataType.equalsAny(targetType, PVarchar.INSTANCE,PChar.INSTANCE)) { + if (PDataType.equalsAny(targetType, PVarchar.INSTANCE,PChar.INSTANCE)) { valueBuilder.append("\""); String tmp = PVarchar.INSTANCE.toStringLiteral(obj,formatter); valueBuilder.append(tmp.substring(1,tmp.length()-1)); @@ -411,12 +412,15 @@ public static String dataToJsonValue(PDataType targetType, Object obj,Format for String tmp = PVarchar.INSTANCE.toStringLiteral(obj,formatter); valueBuilder.append(tmp.substring(1,tmp.length()-1)); valueBuilder.append("\""); + }else if (targetType.isFixedWidth()) { + valueBuilder.append(targetType.toStringLiteral(obj, formatter)); } else{ valueBuilder.append("\""); String tmp = PVarchar.INSTANCE.toStringLiteral(obj,formatter); valueBuilder.append(tmp.substring(1,tmp.length()-1)); valueBuilder.append("\""); } + }else{ valueBuilder.append("null"); } From 63f0d923300e1d8a6337b908b4bed6b11d30ce20 Mon Sep 17 00:00:00 2001 From: ictwanglei Date: Sun, 26 Jul 2015 11:44:22 +0800 Subject: [PATCH 21/24] delete JsonFunctionTest.java --- .../expression/function/JsonFunctionTest.java | 284 ------------------ 1 file changed, 284 deletions(-) delete mode 100755 phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java diff --git a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java b/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java deleted file mode 100755 index 5a7128cf8ac..00000000000 --- a/phoenix-core/src/test/java/org/apache/phoenix/expression/function/JsonFunctionTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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 org.apache.hadoop.hbase.io.ImmutableBytesWritable; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.phoenix.expression.Expression; -import org.apache.phoenix.expression.LiteralExpression; -import org.apache.phoenix.schema.json.PhoenixJson; -import org.apache.phoenix.schema.types.*; -import org.junit.Test; - -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Date; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.assertEquals; - - -/** - * Unit tests for JSON build-in function. - * Testing function includes below: - * {@link ArrayToJsonFunction} - * {@link JsonArrayElementsFunction} - * {@link JsonPopulateRecordFunction} - * {@link JsonPopulateRecordSetFunction} - * {@link JsonArrayLengthFunction} - * {@link JsonObjectKeysFunction} - * {@link ToJsonFunction} - * {@link JsonEachFunction} - * - */ -public class JsonFunctionTest { - public static final String TEST_JSON_STR = - "{\"f2\":{\"f3\":\"value\"},\"f4\":{\"f5\":99,\"f6\":[1,true,\"foo\"]},\"f7\":true}"; - - private static PhoenixJson testArrayToJson (Object[] array,PDataType datatype,PArrayDataType arraydatatype) throws Exception { - PhoenixArray pa = PArrayDataType.instantiatePhoenixArray( datatype,array); - LiteralExpression arrayExpr = LiteralExpression.newConstant(pa,arraydatatype ); - List children = Arrays.asList(arrayExpr); - ArrayToJsonFunction e = new ArrayToJsonFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - PhoenixJson result = (PhoenixJson)e.getDataType().toObject(ptr); - return result; - } - - @Test - public void testNumberArrayToJson() throws Exception { - Object[] testarray = new Object[]{1,12,32,432}; - PhoenixJson result = testArrayToJson(testarray, PInteger.INSTANCE, PIntegerArray.INSTANCE); - String expected = "[1,12,32,432]"; - assertEquals(result.serializeToString(), expected); - Object[] testarray2 = new Object[]{1.12,12.34,32.45,432.78}; - PhoenixJson result2 = testArrayToJson(testarray2, PDouble.INSTANCE, PDoubleArray.INSTANCE); - String expected2 = "[1.12,12.34,32.45,432.78]"; - assertEquals(result2.serializeToString(), expected2); - } - @Test - public void testBooleanArrayToJson() throws Exception { - Object[] testarray = new Object[]{false,true}; - PhoenixJson result = testArrayToJson(testarray, PBoolean.INSTANCE, PBooleanArray.INSTANCE); - String expected = "[false,true]"; - assertEquals(result.toString(), expected); - } - - @Test - public void testStringArrayToJson() throws Exception { - Object[] testarray = new Object[]{"abc123","12.3","string","汉字"}; - PhoenixJson result = testArrayToJson(testarray, PVarchar.INSTANCE, PVarcharArray.INSTANCE); - String expected ="[\"abc123\",\"12.3\",\"string\",\"汉字\"]"; - assertEquals(result.serializeToString(), expected); - } - @Test - public void testDateArrayToJson() throws Exception { - SimpleDateFormat myFormatter = new SimpleDateFormat("yyyy-MM-dd kk:mm:ss.SSS"); - Date date1= myFormatter.parse("1990-12-01 11:01:45.0"); - Date date2 = myFormatter.parse("1989-03-12 13:01:45.0"); - Date date3 = myFormatter.parse("1974-06-06 12:01:45.0"); - Object[] testarray = new Object[]{date1,date2,date3}; - PhoenixJson result = testArrayToJson(testarray, PDate.INSTANCE,PDateArray.INSTANCE); - String expected ="[\"1990-12-01\",\"1989-03-12\",\"1974-06-06\"]"; - assertEquals(result.serializeToString(), expected); - - Timestamp ts1 = Timestamp.valueOf("1990-12-01 11:01:45.123"); - Timestamp ts2 = Timestamp.valueOf("1989-03-12 01:01:01.0"); - Timestamp ts3 = Timestamp.valueOf("1989-03-12 23:59:59.1"); - testarray = new Object[]{ts1,ts2,ts3}; - result = testArrayToJson(testarray, PTimestamp.INSTANCE,PTimestampArray.INSTANCE); - expected ="[\"1990-12-01 11:01:45.123\",\"1989-03-12 01:01:01.0\",\"1989-03-12 23:59:59.1\"]"; - assertEquals(result.serializeToString(), expected); - - Time t1 = new Time(date1.getTime()); - Time t2 = new Time(date2.getTime()); - Time t3 = new Time(date3.getTime()); - testarray = new Object[]{t1,t2,t3}; - result = testArrayToJson(testarray, PTime.INSTANCE,PTimeArray.INSTANCE); - expected ="[\"11:01:45\",\"13:01:45\",\"12:01:45\"]"; - assertEquals(result.serializeToString(), expected); - - - } - - - private static String[] jsonArrayElements (String json) throws Exception { - PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - List children = Arrays.asList(JsonExpr); - JsonArrayElementsFunction e = new JsonArrayElementsFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - PhoenixArray pa = (PhoenixArray)e.getDataType().toObject(ptr); - return (String[] )pa.getArray(); - } - - @Test - public void testJsonArrayElements() throws Exception { - String json = "[1,true,\"string\",[2,false]]"; - Object[] expected = new Object[]{"1","true","\"string\"","[2,false]"}; - String[] result = jsonArrayElements(json); - assertEquals(result.length, expected.length); - for(int i = 0; i children = Arrays.asList(typesExpr,JsonExpr); - JsonPopulateRecordFunction e = new JsonPopulateRecordFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - String record = (String)e.getDataType().toObject(ptr); - return record; - } - - @Test - public void testJsonPopulateRecord() throws Exception { - Object[] types= new Object[]{"a","b"}; - String json = "{\"a\":1,\"b\":2}"; - String expected = "1,2"; - String result = jsonPopulateRecord(types, json); - assertEquals(result, expected); - } - - private static String[] jsonPopulateRecordSet (Object[] types,String json) throws Exception { - PhoenixArray pa =PArrayDataType.instantiatePhoenixArray( PVarchar.INSTANCE,types); - LiteralExpression typesExpr = LiteralExpression.newConstant(pa,PVarcharArray.INSTANCE ); - PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - List children = Arrays.asList(typesExpr,JsonExpr); - JsonPopulateRecordSetFunction e = new JsonPopulateRecordSetFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - PhoenixArray record = (PhoenixArray)e.getDataType().toObject(ptr); - return (String[] )record.getArray(); - } - - - @Test - public void testJsonPopulateRecordSet() throws Exception { - Object[] types= new Object[]{"a","b"}; - String json = "[{\"a\":1,\"b\":2},{\"a\":2,\"b\":3},{\"a\":4,\"b\":5}]"; - Object[] expected = new Object[]{"1,2","2,3","4,5"}; - String[] result = jsonPopulateRecordSet(types, json); - assertEquals(result.length, expected.length); - for(int i = 0; i children= Arrays.asList(JsonExpr); - JsonArrayLengthFunction e = new JsonArrayLengthFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - int len = (int)e.getDataType().toObject(ptr); - return len; - } - - @Test - public void testJsonArrayLength() throws Exception { - String array1 = "[1,true,\"string\",[2,false]]"; - String array2 = "[1,2.34,[1,\"abc\"],4,true,\"string\",[2,false]]"; - assertEquals(jsonArrayLength(array1),4); - assertEquals(jsonArrayLength(array2),7); - } - - - private static PhoenixJson toJson (Object obj,PDataType datatype) throws Exception { - LiteralExpression op =LiteralExpression.newConstant(obj, datatype); - List children = Arrays.asList(op); - ToJsonFunction e = new ToJsonFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - PhoenixJson result = (PhoenixJson)e.getDataType().toObject(ptr); - return result; - } - - - @Test - public void testToJson() throws Exception { - assertEquals(toJson(-256, PInteger.INSTANCE).serializeToString(),"-256"); - assertEquals(toJson(-256, PLong.INSTANCE).serializeToString(),"-256"); - assertEquals(toJson(-1, PSmallint.INSTANCE).serializeToString(),"-1"); - assertEquals(toJson(-1, PTinyint.INSTANCE).serializeToString(),"-1"); - assertEquals(toJson(12, PUnsignedInt.INSTANCE).serializeToString(),"12"); - assertEquals(toJson(12, PUnsignedSmallint.INSTANCE).serializeToString(),"12"); - assertEquals(toJson(12, PUnsignedLong.INSTANCE).serializeToString(),"12"); - assertEquals(toJson(123.456, PDouble.INSTANCE).serializeToString(),"123.456"); - assertEquals(toJson(123.456, PFloat.INSTANCE).serializeToString(),"123.456"); - assertEquals(toJson(123.456, PUnsignedDouble.INSTANCE).serializeToString(),"123.456"); - assertEquals(toJson(123.456, PUnsignedFloat.INSTANCE).serializeToString(),"123.456"); - assertEquals(toJson(false, PBoolean.INSTANCE).serializeToString(),"false"); - assertEquals(toJson(true, PBoolean.INSTANCE).serializeToString(),"true"); - assertEquals(toJson("string_abc", PVarchar.INSTANCE).toString(),"\"string_abc\""); - assertEquals(toJson("string_abc", PVarchar.INSTANCE).serializeToString(),"string_abc"); - } - private static String[] jsonObjectKeys (String json) throws Exception { - PhoenixJson phoenixJson = PhoenixJson.getInstance(json); - LiteralExpression JsonExpr = LiteralExpression.newConstant(phoenixJson,PJson.INSTANCE ); - List children = Arrays.asList(JsonExpr); - JsonObjectKeysFunction e = new JsonObjectKeysFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - PhoenixArray pa = (PhoenixArray)e.getDataType().toObject(ptr); - return (String[] )pa.getArray(); - } - - @Test - public void testJsonObjectKeys() throws Exception { - Object[] expected = new Object[]{"f2","f4","f7"}; - String[] result = jsonObjectKeys(TEST_JSON_STR); - - assertEquals(result.length, expected.length); - for(int i = 0; i children = Arrays.asList(JsonExpr); - JsonEachFunction e = new JsonEachFunction(children); - ImmutableBytesWritable ptr = new ImmutableBytesWritable(); - boolean evaluated = e.evaluate(null, ptr); - PhoenixArray pa = (PhoenixArray)e.getDataType().toObject(ptr); - return (String[] )pa.getArray(); - } - @Test - public void testJsonEach() throws Exception { - Object[] expected = new Object[]{"f2,{\"f3\":\"value\"}","f4,{\"f5\":99,\"f6\":[1,true,\"foo\"]}","f7,true"}; - String[] result = jsonEach(TEST_JSON_STR); - - assertEquals(result.length, expected.length); - for(int i = 0; i Date: Sun, 26 Jul 2015 12:55:40 +0800 Subject: [PATCH 22/24] fix bugs --- .../expression/function/ArrayToJsonFunction.java | 5 ++--- .../function/JsonArrayElementsFunction.java | 5 ++--- .../expression/function/JsonArrayLengthFunction.java | 5 ++--- .../expression/function/JsonEachFunction.java | 5 ++--- .../expression/function/JsonObjectKeysFunction.java | 5 ++--- .../function/JsonPopulateRecordFunction.java | 12 +++--------- .../function/JsonPopulateRecordSetFunction.java | 6 ++---- .../phoenix/expression/function/ToJsonFunction.java | 5 ++--- 8 files changed, 17 insertions(+), 31 deletions(-) diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java index 68f632c6dac..aef38087f80 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ArrayToJsonFunction.java @@ -53,9 +53,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { if (!arrayExpr.evaluate(tuple, ptr)) { return false; - } - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } try { PDataType baseType = PDataType.fromTypeId(arrayExpr.getDataType() diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java index a64f483c40d..12cd1fbcbe2 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayElementsFunction.java @@ -52,9 +52,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression jsonExpression = this.children.get(0); if (!jsonExpression.evaluate(tuple, ptr)) { return false; - } - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } try { PhoenixJson phoenixJson = diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java index 67ae6b17b6c..21f6455b7c8 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonArrayLengthFunction.java @@ -51,9 +51,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression jsonExpression = this.children.get(0); if (!jsonExpression.evaluate(tuple, ptr)) { return false; - } - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } try{ PhoenixJson phoenixJson = diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java index 25ad42ce6ac..fdd8b4ac9c1 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonEachFunction.java @@ -48,9 +48,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression jsonExpression = this.children.get(0); if (!jsonExpression.evaluate(tuple, ptr)) { return false; - } - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } PhoenixJson phoenixJson = (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java index b68e385e2a4..60b6370134b 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonObjectKeysFunction.java @@ -48,9 +48,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression jsonExpression = this.children.get(0); if (!jsonExpression.evaluate(tuple, ptr)) { return false; - } - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } PhoenixJson phoenixJson = (PhoenixJson) PJson.INSTANCE.toObject(ptr.get(), ptr.getOffset(), diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java index 4066428fb44..82cdeef01d1 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordFunction.java @@ -54,10 +54,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression typeArrayExpression = children.get(0); if (!typeArrayExpression.evaluate(tuple, ptr)) { return false; - } - - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } PhoenixArray phoenixArray = (PhoenixArray) PVarcharArray.INSTANCE.toObject(ptr); @@ -76,13 +74,9 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { String records = phoenixJson.jsonPopulateRecord(types); byte[] array = PVarchar.INSTANCE.toBytes(records); ptr.set(array); - - } catch (SQLException sqe) { - new IllegalDataException(new SQLExceptionInfo.Builder(SQLExceptionCode.ILLEGAL_DATA) - .setRootCause(sqe).build().buildException()); + throw new IllegalDataException(sqe); } - return true; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java index 32503475a51..234c6749a19 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/JsonPopulateRecordSetFunction.java @@ -53,10 +53,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression typeArrayExpression = children.get(0); if (!typeArrayExpression.evaluate(tuple, ptr)) { return false; - } - - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } PhoenixArray phoenixArray = (PhoenixArray) PVarcharArray.INSTANCE.toObject(ptr); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java index dc8165a9eaa..acaeb3d37da 100755 --- a/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/expression/function/ToJsonFunction.java @@ -54,9 +54,8 @@ public boolean evaluate(Tuple tuple, ImmutableBytesWritable ptr) { Expression expression = this.children.get(0); if (!expression.evaluate(tuple, ptr)) { return false; - } - if (ptr.getLength() == 0) { - return false; + }else if (ptr.getLength() == 0) { + return true; } PDataType baseType = expression.getDataType(); Object re =baseType.toObject(ptr); From e7759f411b934fda21abe825119cdcb86f4ec0a9 Mon Sep 17 00:00:00 2001 From: ictwanglei Date: Tue, 4 Aug 2015 15:53:00 +0800 Subject: [PATCH 23/24] rename and add more testcase --- .../end2end/ArrayToJsonFunctionIT.java | 96 ++++++++++++++----- .../end2end/JsonArrayElementsFunctionIT.java | 2 +- .../end2end/JsonArrayLengthFunctionIT.java | 5 +- .../JsonPopulateRecordSetFunctionIT.java | 2 +- 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java index d3a9413a0be..3649c04e11f 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ArrayToJsonFunctionIT.java @@ -60,36 +60,36 @@ public void testArrayToJsonWithAllArrayTypes() throws Exception { // boolean array Array boolArray = conn.createArrayOf("BOOLEAN", new Boolean[] { true,false }); - int boolIndex = 2; - stmt.setArray(boolIndex, boolArray); + int boolColumnIndex = 2; + stmt.setArray(boolColumnIndex , boolArray); // byte array Array byteArray = conn.createArrayOf("TINYINT", new Byte[] { 11, 22 }); - int byteIndex = 3; - stmt.setArray(byteIndex, byteArray); + int byteColumnIndex = 3; + stmt.setArray(byteColumnIndex, byteArray); // double array Array doubleArray = conn.createArrayOf("DOUBLE", new Double[] { 67.78, 78.89 }); - int doubleIndex = 4; - stmt.setArray(doubleIndex, doubleArray); + int doubleColumnIndex = 4; + stmt.setArray(doubleColumnIndex, doubleArray); // float array Array floatArray = conn.createArrayOf("FLOAT", new Float[] { 12.23f, 45.56f }); - int floatIndex = 5; - stmt.setArray(floatIndex, floatArray); + int floatColumnIndex = 5; + stmt.setArray(floatColumnIndex, floatArray); // int array Array intArray = conn.createArrayOf("INTEGER", new Integer[] { 5555, 6666 }); - int intIndex = 6; - stmt.setArray(intIndex, intArray); + int intColumnIndex = 6; + stmt.setArray(intColumnIndex, intArray); // long array Array longArray = conn.createArrayOf("BIGINT", new Long[] { 7777777L, 8888888L }); - int longIndex = 7; - stmt.setArray(longIndex, longArray); + int longColumnIndex = 7; + stmt.setArray(longColumnIndex, longArray); // short array Array shortArray = conn.createArrayOf("SMALLINT", new Short[] { 333, 444 }); - int shortIndex = 8; - stmt.setArray(shortIndex, shortArray); + int shortColumnIndex = 8; + stmt.setArray(shortColumnIndex, shortArray); // create character array Array stringArray = conn.createArrayOf("VARCHAR", new String[] { "a", "b" }); - int stringIndex = 9; - stmt.setArray(stringIndex, stringArray); + int stringColumnIndex = 9; + stmt.setArray(stringColumnIndex, stringArray); stmt.execute(); conn.commit(); @@ -109,14 +109,14 @@ public void testArrayToJsonWithAllArrayTypes() throws Exception { assertTrue(rs.next()); assertEquals("valueOne", rs.getString(1)); - assertArrayToJson(rs, boolIndex, "[true,false]"); - assertArrayToJson(rs, byteIndex, "[11,22]"); - assertArrayToJson(rs, doubleIndex, "[67.78,78.89]"); - assertArrayToJson(rs, floatIndex, "[12.23,45.56]"); - assertArrayToJson(rs, intIndex, "[5555,6666]"); - assertArrayToJson(rs, longIndex, "[7777777,8888888]"); - assertArrayToJson(rs, shortIndex, "[333,444]"); - assertArrayToJson(rs, stringIndex, "[\"a\",\"b\"]"); + assertArrayToJson(rs, boolColumnIndex , "[true,false]"); + assertArrayToJson(rs, byteColumnIndex, "[11,22]"); + assertArrayToJson(rs, doubleColumnIndex, "[67.78,78.89]"); + assertArrayToJson(rs, floatColumnIndex, "[12.23,45.56]"); + assertArrayToJson(rs, intColumnIndex, "[5555,6666]"); + assertArrayToJson(rs, longColumnIndex, "[7777777,8888888]"); + assertArrayToJson(rs, shortColumnIndex, "[333,444]"); + assertArrayToJson(rs, stringColumnIndex, "[\"a\",\"b\"]"); } finally { conn.close(); @@ -124,6 +124,54 @@ public void testArrayToJsonWithAllArrayTypes() throws Exception { } + @Test + public void testArrayToJsonWithNullValueArray() throws Exception { + // create the table + String ddlStmt = "create table " + + "TABLE_NULL_VALUE_ARRAY" + + " (PK VARCHAR NOT NULL PRIMARY KEY,\n" + + " NULL_VALUE_ARRAY varchar(100) array[2]" + + ")"; + createTestTable(getUrl(), ddlStmt); + + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + Connection conn = DriverManager.getConnection(getUrl(), props); + conn.setAutoCommit(false); + + try{ + // populate the table with data + PreparedStatement stmt = + conn.prepareStatement("UPSERT INTO " + + "TABLE_NULL_VALUE_ARRAY" + + "(PK, NULL_VALUE_ARRAY)\n" + + "VALUES (?, ?)"); + + stmt.setString(1, "valueOne"); + + Array nullValueArray = conn.createArrayOf("VARCHAR", new String[] { null, null }); + int nullValueIndex = 2; + stmt.setArray(nullValueIndex, nullValueArray); + stmt.execute(); + conn.commit(); + + stmt = + conn.prepareStatement("SELECT PK, " + + "ARRAY_TO_JSON(NULL_VALUE_ARRAY) FROM " + + "TABLE_NULL_VALUE_ARRAY"); + + ResultSet rs = stmt.executeQuery(); + assertTrue(rs.next()); + + assertEquals("valueOne", rs.getString(1)); + assertArrayToJson(rs, nullValueIndex, "[null,null]"); + + } finally { + conn.close(); + } + + } + + private void assertArrayToJson(ResultSet rs, int arrayIndex, String expectedJson) throws SQLException { diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java index 5b54f003da8..1c726dcbb7e 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java @@ -124,7 +124,7 @@ public void testJsonArrayElementsWithNestJson() throws Exception { } @Test - public void testJsonArrayElementsWithInvalidJson() throws Exception { + public void testJsonArrayElementsWithInvalidJsonInput() throws Exception { Connection conn = getConnection(); String json = "{\"f1\":1,\"f2\":\"abc\"}"; String pk = "valueOne"; diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java index c0116c6d3de..501200bfcca 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java @@ -124,7 +124,7 @@ public void testJsonArrayLengthWithNestedJson() throws Exception { } } @Test - public void testJsonArrayLengthWithInvalidJson() throws Exception { + public void testJsonArrayLengthWithInvalidJsonInput() throws Exception { Connection conn = getConnection(); String json = "{\"f1\":1,\"f2\":\"abc\"}"; String pk = "valueOne"; @@ -137,8 +137,7 @@ public void testJsonArrayLengthWithInvalidJson() throws Exception { PreparedStatement stmt = conn.prepareStatement(selectQuery); ResultSet rs = stmt.executeQuery(); assertTrue(rs.next()); - assertEquals("Json array length is not as expected.", - 2, rs.getInt(1)); + rs.getInt(1); fail("The Json Node should be an array!"); } catch (SQLException sqe) { assertEquals("SQL error code is not as expected.", diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java index 6c0e5286e81..fd02fed7afc 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonPopulateRecordSetFunctionIT.java @@ -89,7 +89,7 @@ public void testJsonPopulateRecordSetWithNullValues() throws Exception { } @Test - public void testJsonPopulateRecordSetWithInvalidJson() throws Exception { + public void testJsonPopulateRecordSetWithInvalidJsonInput() throws Exception { Connection conn = getConnection(); String json = "{\"a\":1,\"b\":2}"; String pk = "valueOne"; From 1bb5829f66f221effe35ad8e874e3390569f0399 Mon Sep 17 00:00:00 2001 From: ictwanglei Date: Thu, 20 Aug 2015 07:27:37 +0800 Subject: [PATCH 24/24] add null json value test --- .../phoenix/end2end/JsonArrayElementsFunctionIT.java | 9 +++++---- .../phoenix/end2end/JsonArrayLengthFunctionIT.java | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java index 1c726dcbb7e..a91c4979566 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayElementsFunctionIT.java @@ -68,7 +68,7 @@ public void testJsonArrayElementsWithDifferentDataTypes() throws Exception { Connection conn = getConnection(); try { - String json = "[1,36.763,false,\"string\"]"; + String json = "[1,36.763,null,false,\"string\"]"; String pk = "valueOne"; populateJsonTable(conn, json, pk); @@ -77,11 +77,12 @@ public void testJsonArrayElementsWithDifferentDataTypes() throws Exception { PreparedStatement stmt = conn.prepareStatement(selectQuery); ResultSet rs = stmt.executeQuery(); assertTrue(rs.next()); - String[] strArr = new String[4]; + String[] strArr = new String[5]; strArr[0] = "1"; strArr[1] = "36.763"; - strArr[2] = "false"; - strArr[3] = "\"string\""; + strArr[2] = "null"; + strArr[3] = "false"; + strArr[4] = "\"string\""; Array array = conn.createArrayOf("VARCHAR", strArr); PhoenixArray resultArray = (PhoenixArray) rs.getArray(1); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java index 501200bfcca..37e452b1e48 100755 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/JsonArrayLengthFunctionIT.java @@ -85,7 +85,7 @@ public void testJsonArrayLengthWithDoubleType() throws Exception { public void testJsonArrayLengthWithDifferentDataTypes() throws Exception { Connection conn = getConnection(); - String json = "[1,2.3,true,\"f1\",[\"string\",3]]"; + String json = "[1,2.3,null,true,\"f1\",[\"string\",3]]"; String pk = "valueOne"; try { populateJsonTable(conn, json, pk); @@ -95,7 +95,7 @@ public void testJsonArrayLengthWithDifferentDataTypes() ResultSet rs = stmt.executeQuery(); assertTrue(rs.next()); assertEquals("Json array length is not as expected.", - 5, rs.getInt(1)); + 6, rs.getInt(1)); assertFalse(rs.next());