You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mm...@apache.org on 2018/11/09 16:40:50 UTC

[3/3] calcite git commit: [CALCITE-2266] Implement SQL:2016 JSON functions: JSON_EXISTS, JSON_VALUE, JSON_QUERY, JSON_OBJECT, JSON_OBJECTAGG, JSON_ARRAY, JSON_ARRAYAGG, IS JSON predicate (Hongze Zhang)

[CALCITE-2266] Implement SQL:2016 JSON functions: JSON_EXISTS, JSON_VALUE, JSON_QUERY, JSON_OBJECT, JSON_OBJECTAGG, JSON_ARRAY, JSON_ARRAYAGG, IS JSON predicate (Hongze Zhang)

Close apache/calcite#785


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

Branch: refs/heads/master
Commit: 8e557d26cffceaf7d7f98d8f8bafd9fbe3bc2447
Parents: d7829a3
Author: Michael Mior <mm...@cs.rit.edu>
Authored: Fri Nov 9 11:39:20 2018 -0500
Committer: Michael Mior <mm...@cs.rit.edu>
Committed: Fri Nov 9 11:39:58 2018 -0500

----------------------------------------------------------------------
 babel/src/main/codegen/config.fmpp              |  19 +
 core/pom.xml                                    |   4 +
 core/src/main/codegen/config.fmpp               |  12 +
 core/src/main/codegen/templates/Parser.jj       | 585 ++++++++++++++
 .../calcite/adapter/enumerable/RexImpTable.java | 153 ++++
 .../apache/calcite/runtime/CalciteResource.java |  77 ++
 .../apache/calcite/runtime/SqlFunctions.java    | 437 +++++++++-
 .../sql/SqlJsonConstructorNullClause.java       |  26 +
 .../apache/calcite/sql/SqlJsonEmptyOrError.java |  33 +
 .../org/apache/calcite/sql/SqlJsonEncoding.java |  42 +
 .../calcite/sql/SqlJsonExistsErrorBehavior.java |  26 +
 .../sql/SqlJsonQueryEmptyOrErrorBehavior.java   |  32 +
 .../sql/SqlJsonQueryWrapperBehavior.java        |  26 +
 .../sql/SqlJsonValueEmptyOrErrorBehavior.java   |  32 +
 .../java/org/apache/calcite/sql/SqlKind.java    |  25 +-
 .../sql/fun/SqlJsonApiCommonSyntaxOperator.java |  75 ++
 .../sql/fun/SqlJsonArrayAggAggFunction.java     |  75 ++
 .../calcite/sql/fun/SqlJsonArrayFunction.java   | 109 +++
 .../calcite/sql/fun/SqlJsonExistsFunction.java  |  61 ++
 .../sql/fun/SqlJsonObjectAggAggFunction.java    |  79 ++
 .../calcite/sql/fun/SqlJsonObjectFunction.java  | 137 ++++
 .../calcite/sql/fun/SqlJsonQueryFunction.java   | 119 +++
 .../sql/fun/SqlJsonValueExpressionOperator.java |  75 ++
 .../calcite/sql/fun/SqlJsonValueFunction.java   | 196 +++++
 .../calcite/sql/fun/SqlStdOperatorTable.java    | 111 +++
 .../apache/calcite/sql/type/ReturnTypes.java    |   6 +
 .../sql2rel/StandardConvertletTable.java        |  24 +
 .../org/apache/calcite/util/BuiltInMethod.java  |  34 +
 .../calcite/runtime/CalciteResource.properties  |  25 +
 core/src/test/codegen/config.fmpp               |  12 +
 .../rel/rel2sql/RelToSqlConverterTest.java      |  81 ++
 .../calcite/sql/parser/SqlParserTest.java       | 147 ++++
 .../calcite/sql/test/AbstractSqlTester.java     |  10 +
 .../apache/calcite/sql/test/SqlAdvisorTest.java |   7 +
 .../calcite/sql/test/SqlOperatorBaseTest.java   | 311 ++++++-
 .../org/apache/calcite/sql/test/SqlTester.java  |  17 +
 .../org/apache/calcite/sql/test/SqlTests.java   |  38 +
 .../org/apache/calcite/test/CalciteSuite.java   |   1 +
 .../apache/calcite/test/SqlFunctionsTest.java   |  17 +-
 .../calcite/test/SqlJsonFunctionsTest.java      | 807 +++++++++++++++++++
 .../apache/calcite/test/SqlValidatorTest.java   |  93 +++
 core/src/test/resources/sql/agg.iq              |  41 +
 pom.xml                                         |  12 +
 server/src/main/codegen/config.fmpp             |  12 +
 site/_docs/reference.md                         |  81 ++
 45 files changed, 4313 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/babel/src/main/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/babel/src/main/codegen/config.fmpp b/babel/src/main/codegen/config.fmpp
index d17215d..2fa6061 100644
--- a/babel/src/main/codegen/config.fmpp
+++ b/babel/src/main/codegen/config.fmpp
@@ -31,6 +31,7 @@ data: {
       # List of keywords from "keywords" section that are not reserved.
       nonReservedKeywords: [
         "A"
+        "ABSENT"
         "ABSOLUTE"
         "ACTION"
         "ADA"
@@ -69,6 +70,7 @@ data: {
         "COMMAND_FUNCTION_CODE"
         "COMMITTED"
         "CONDITION_NUMBER"
+        "CONDITIONAL"
         "CONNECTION"
         "CONNECTION_NAME"
         "CONSTRAINT_CATALOG"
@@ -101,13 +103,16 @@ data: {
         "DOY"
         "DYNAMIC_FUNCTION"
         "DYNAMIC_FUNCTION_CODE"
+        "ENCODING"
         "EPOCH"
+        "ERROR"
         "EXCEPTION"
         "EXCLUDE"
         "EXCLUDING"
         "FINAL"
         "FIRST"
         "FOLLOWING"
+        "FORMAT"
         "FORTRAN"
         "FOUND"
         "FRAC_SECOND"
@@ -183,6 +188,7 @@ data: {
         "PARAMETER_SPECIFIC_SCHEMA"
         "PARTIAL"
         "PASCAL"
+        "PASSING"
         "PASSTHROUGH"
         "PAST"
         "PATH"
@@ -205,12 +211,14 @@ data: {
         "RETURNED_LENGTH"
         "RETURNED_OCTET_LENGTH"
         "RETURNED_SQLSTATE"
+        "RETURNING"
         "ROLE"
         "ROUTINE"
         "ROUTINE_CATALOG"
         "ROUTINE_NAME"
         "ROUTINE_SCHEMA"
         "ROW_COUNT"
+        "SCALAR"
         "SCALE"
         "SCHEMA"
         "SCHEMA_NAME"
@@ -304,6 +312,7 @@ data: {
         "TYPE"
         "UNBOUNDED"
         "UNCOMMITTED"
+        "UNCONDITIONAL"
         "UNDER"
         "UNNAMED"
         "USAGE"
@@ -311,6 +320,9 @@ data: {
         "USER_DEFINED_TYPE_CODE"
         "USER_DEFINED_TYPE_NAME"
         "USER_DEFINED_TYPE_SCHEMA"
+        "UTF8"
+        "UTF16"
+        "UTF32"
         "VERSION"
         "VIEW"
         "WEEK"
@@ -537,6 +549,13 @@ data: {
         "ISOLATION",
 #       # "ITERATE", # not a keyword in Calcite
 #       "JOIN",
+        "JSON_ARRAY",
+        "JSON_ARRAYAGG",
+        "JSON_EXISTS",
+        "JSON_VALUE",
+        "JSON_OBJECT",
+        "JSON_OBJECTAGG",
+        "JSON_QUERY",
 #       # "KEEP", # not a keyword in Calcite
         "KEY",
         "LAG",

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 046f437..0da66e6 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -103,6 +103,10 @@ limitations under the License.
       <artifactId>sketches-core</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.jayway.jsonpath</groupId>
+      <artifactId>json-path</artifactId>
+    </dependency>
+    <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <scope>test</scope>

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/codegen/config.fmpp
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/config.fmpp b/core/src/main/codegen/config.fmpp
index 4e77ce5..d7d5066 100644
--- a/core/src/main/codegen/config.fmpp
+++ b/core/src/main/codegen/config.fmpp
@@ -51,6 +51,7 @@ data: {
     # List of keywords from "keywords" section that are not reserved.
     nonReservedKeywords: [
         "A"
+        "ABSENT"
         "ABSOLUTE"
         "ACTION"
         "ADA"
@@ -89,6 +90,7 @@ data: {
         "COMMAND_FUNCTION_CODE"
         "COMMITTED"
         "CONDITION_NUMBER"
+        "CONDITIONAL"
         "CONNECTION"
         "CONNECTION_NAME"
         "CONSTRAINT_CATALOG"
@@ -121,13 +123,16 @@ data: {
         "DOY"
         "DYNAMIC_FUNCTION"
         "DYNAMIC_FUNCTION_CODE"
+        "ENCODING"
         "EPOCH"
+        "ERROR"
         "EXCEPTION"
         "EXCLUDE"
         "EXCLUDING"
         "FINAL"
         "FIRST"
         "FOLLOWING"
+        "FORMAT"
         "FORTRAN"
         "FOUND"
         "FRAC_SECOND"
@@ -203,6 +208,7 @@ data: {
         "PARAMETER_SPECIFIC_SCHEMA"
         "PARTIAL"
         "PASCAL"
+        "PASSING"
         "PASSTHROUGH"
         "PAST"
         "PATH"
@@ -225,12 +231,14 @@ data: {
         "RETURNED_LENGTH"
         "RETURNED_OCTET_LENGTH"
         "RETURNED_SQLSTATE"
+        "RETURNING"
         "ROLE"
         "ROUTINE"
         "ROUTINE_CATALOG"
         "ROUTINE_NAME"
         "ROUTINE_SCHEMA"
         "ROW_COUNT"
+        "SCALAR"
         "SCALE"
         "SCHEMA"
         "SCHEMA_NAME"
@@ -324,6 +332,7 @@ data: {
         "TYPE"
         "UNBOUNDED"
         "UNCOMMITTED"
+        "UNCONDITIONAL"
         "UNDER"
         "UNNAMED"
         "USAGE"
@@ -331,6 +340,9 @@ data: {
         "USER_DEFINED_TYPE_CODE"
         "USER_DEFINED_TYPE_NAME"
         "USER_DEFINED_TYPE_SCHEMA"
+        "UTF8"
+        "UTF16"
+        "UTF32"
         "VERSION"
         "VIEW"
         "WEEK"

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 7bfc02c..81c79ed 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -64,6 +64,13 @@ import org.apache.calcite.sql.SqlIntervalQualifier;
 import org.apache.calcite.sql.SqlJdbcDataTypeName;
 import org.apache.calcite.sql.SqlJdbcFunctionCall;
 import org.apache.calcite.sql.SqlJoin;
+import org.apache.calcite.sql.SqlJsonConstructorNullClause;
+import org.apache.calcite.sql.SqlJsonEncoding;
+import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
+import org.apache.calcite.sql.SqlJsonEmptyOrError;
+import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
+import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
+import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlLiteral;
 import org.apache.calcite.sql.SqlMatchRecognize;
@@ -4792,7 +4799,556 @@ SqlNode BuiltinFunctionCall() :
         node = ExtendedBuiltinFunctionCall() { return node; }
     |
         node = MatchRecognizeFunctionCall() { return node; }
+    |
+        node = JsonExistsFunctionCall() { return node; }
+    |
+        node = JsonValueFunctionCall() { return node; }
+    |
+        node = JsonQueryFunctionCall() { return node; }
+    |
+        node = JsonObjectFunctionCall() { return node; }
+    |
+        node = JsonObjectAggFunctionCall() { return node; }
+    |
+        node = JsonArrayFunctionCall() { return node; }
+    |
+        node = JsonArrayAggFunctionCall() { return node; }
+    )
+}
+
+SqlJsonEncoding JsonRepresentation() :
+{
+
+}
+{
+    <JSON>
+    [
+        // Encoding is currently ignored.
+        <ENCODING>
+        (
+            <UTF8> { return SqlJsonEncoding.UTF8; }
+            |
+            <UTF16> { return SqlJsonEncoding.UTF16; }
+            |
+            <UTF32> { return SqlJsonEncoding.UTF32; }
+        )
+    ]
+    {
+        return SqlJsonEncoding.UTF8;
+    }
+}
+
+void JsonInputClause() :
+{
+
+}
+{
+    <FORMAT> JsonRepresentation()
+}
+
+SqlDataTypeSpec JsonReturningClause() :
+{
+    SqlDataTypeSpec dt;
+}
+{
+    <RETURNING> dt = DataType() { return dt; }
+}
+
+SqlDataTypeSpec JsonOutputClause() :
+{
+    SqlDataTypeSpec dataType;
+}
+{
+    dataType = JsonReturningClause()
+    [
+        <FORMAT> JsonRepresentation()
+    ]
+    {
+        return dataType;
+    }
+}
+
+SqlNode JsonValueExpression(boolean implicitFormatJson) :
+{
+    SqlNode e;
+    List<SqlNode> args = new ArrayList<SqlNode>();
+    Span span;
+}
+{
+    e = Expression(ExprContext.ACCEPT_NON_QUERY) {
+        args.add(e);
+        span = Span.of(e);
+    }
+    [
+        JsonInputClause() {
+            return SqlStdOperatorTable.JSON_VALUE_EXPRESSION.createCall(span.end(this), args);
+        }
+    ]
+    {
+        if (implicitFormatJson) {
+            return SqlStdOperatorTable.JSON_VALUE_EXPRESSION.createCall(span.end(this), args);
+        }
+        return SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION.createCall(span.end(this), args);
+    }
+}
+
+SqlNode JsonPathSpec() :
+{
+    SqlNode e;
+}
+{
+    e = StringLiteral() {
+        return e;
+    }
+}
+
+SqlNode JsonApiCommonSyntax() :
+{
+    SqlNode e;
+    List<SqlNode> args = new ArrayList<SqlNode>();
+    Span span;
+}
+{
+    e = JsonValueExpression(true) {
+        args.add(e);
+        span = Span.of(e);
+    }
+    <COMMA>
+    e = Expression(ExprContext.ACCEPT_NON_QUERY) {
+        args.add(e);
+    }
+    [
+        <PASSING> e = JsonValueExpression(false) {
+            args.add(e);
+        }
+        <AS> e = SimpleIdentifier() {
+            args.add(e);
+        }
+        (
+            <COMMA>
+            e = JsonValueExpression(false) {
+                        args.add(e);
+            }
+            <AS> e = SimpleIdentifier() {
+                        args.add(e);
+            }
+        )*
+    ]
+    {
+        return SqlStdOperatorTable.JSON_API_COMMON_SYNTAX.createCall(span.end(this), args);
+    }
+
+}
+
+SqlJsonExistsErrorBehavior JsonExistsErrorBehavior() :
+{
+
+}
+{
+    <TRUE> { return SqlJsonExistsErrorBehavior.TRUE; }
+    |
+    <FALSE> { return SqlJsonExistsErrorBehavior.FALSE; }
+    |
+    <UNKNOWN> { return SqlJsonExistsErrorBehavior.UNKNOWN; }
+    |
+    <ERROR> { return SqlJsonExistsErrorBehavior.ERROR; }
+}
+
+SqlCall JsonExistsFunctionCall() :
+{
+    List<SqlNode> args;
+    SqlNode e;
+    final Span span;
+    SqlJsonExistsErrorBehavior errorBehavior;
+}
+{
+    <JSON_EXISTS> { span = span(); }
+    <LPAREN> e = JsonApiCommonSyntax() {
+        args = new ArrayList<SqlNode>();
+        args.add(e);
+    }
+    [
+        errorBehavior = JsonExistsErrorBehavior() { args.add(SqlLiteral.createSymbol(errorBehavior, getPos())); }
+        <ON> <ERROR>
+    ]
+    <RPAREN> {
+        return SqlStdOperatorTable.JSON_EXISTS.createCall(span.end(this), args);
+    }
+}
+
+List<SqlNode> JsonValueEmptyOrErrorBehavior() :
+{
+    List<SqlNode> list = new ArrayList<SqlNode>();
+    SqlNode e;
+}
+{
+    (
+        <ERROR>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonValueEmptyOrErrorBehavior.ERROR, getPos()));
+            list.add(SqlLiteral.createNull(getPos()));
+        }
+        |
+        <NULL>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonValueEmptyOrErrorBehavior.NULL, getPos()));
+            list.add(SqlLiteral.createNull(getPos()));
+        }
+        |
+        <DEFAULT_> e = Expression(ExprContext.ACCEPT_NON_QUERY)
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonValueEmptyOrErrorBehavior.DEFAULT, getPos()));
+            list.add(e);
+        }
+    )
+    <ON>
+    (
+        <EMPTY>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.EMPTY, getPos()));
+        }
+        |
+        <ERROR>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.ERROR, getPos()));
+        }
+    )
+
+    { return list;}
+}
+
+SqlCall JsonValueFunctionCall() :
+{
+    SqlNode[] args;
+    SqlNode e;
+    final Span span;
+    List<SqlNode> behavior;
+}
+{
+    <JSON_VALUE> { span = span(); }
+    <LPAREN> e = JsonApiCommonSyntax() {
+        args = new SqlNode[6];
+        args[0] = e;
+    }
+    [
+        e = JsonReturningClause() { args[5] = e; }
+    ]
+    (
+        behavior = JsonValueEmptyOrErrorBehavior()
+        {
+            SqlJsonEmptyOrError symbol = ((SqlLiteral) behavior.get(2)).getValueAs(SqlJsonEmptyOrError.class);
+            switch (symbol) {
+            case EMPTY:
+                args[1] = behavior.get(0);
+                args[2] = behavior.get(1);
+                break;
+            case ERROR:
+                args[3] = behavior.get(0);
+                args[4] = behavior.get(1);
+                break;
+            }
+        }
+    )*
+    <RPAREN> {
+        return SqlStdOperatorTable.JSON_VALUE.createCall(span.end(this), args);
+    }
+}
+
+List<SqlNode> JsonQueryEmptyOrErrorBehavior() :
+{
+    List<SqlNode> list = new ArrayList<SqlNode>();
+    SqlNode e;
+}
+{
+    (
+        <ERROR>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.ERROR, getPos()));
+        }
+        |
+        <NULL>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.NULL, getPos()));
+        }
+        |
+        <EMPTY> <ARRAY>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.EMPTY_ARRAY, getPos()));
+        }
+        |
+        <EMPTY> <OBJECT>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonQueryEmptyOrErrorBehavior.EMPTY_OBJECT, getPos()));
+        }
+    )
+    <ON>
+    (
+        <EMPTY>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.EMPTY, getPos()));
+        }
+        |
+        <ERROR>
+        {
+            list.add(SqlLiteral.createSymbol(SqlJsonEmptyOrError.ERROR, getPos()));
+        }
     )
+
+    { return list;}
+}
+
+SqlNode JsonQueryWrapperBehavior() :
+{
+    SqlNode e;
+}
+{
+    <WITHOUT> [<ARRAY>]
+    {
+        return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITHOUT_ARRAY, getPos());
+    }
+    |
+    LOOKAHEAD(2)
+    <WITH> <CONDITIONAL> [<ARRAY>]
+    {
+        return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITH_CONDITIONAL_ARRAY, getPos());
+    }
+    |
+    <WITH> [<UNCONDITIONAL>] [<ARRAY>]
+    {
+        return SqlLiteral.createSymbol(SqlJsonQueryWrapperBehavior.WITH_UNCONDITIONAL_ARRAY, getPos());
+    }
+}
+
+SqlCall JsonQueryFunctionCall() :
+{
+    SqlNode[] args;
+    SqlNode e;
+    final Span span;
+    List<SqlNode> behavior;
+}
+{
+    <JSON_QUERY> { span = span(); }
+    <LPAREN> e = JsonApiCommonSyntax() {
+        args = new SqlNode[4];
+        args[0] = e;
+    }
+    [
+        e = JsonQueryWrapperBehavior() <WRAPPER> { args[1] = e; }
+    ]
+    (
+        behavior = JsonQueryEmptyOrErrorBehavior()
+        {
+            SqlJsonEmptyOrError symbol = ((SqlLiteral) behavior.get(1)).getValueAs(SqlJsonEmptyOrError.class);
+            switch (symbol) {
+            case EMPTY:
+                args[2] = behavior.get(0);
+                break;
+            case ERROR:
+                args[3] = behavior.get(0);
+                break;
+            }
+        }
+    )*
+    <RPAREN> {
+        return SqlStdOperatorTable.JSON_QUERY.createCall(span.end(this), args);
+    }
+}
+
+SqlNode JsonName() :
+{
+    SqlNode e;
+}
+{
+     e = Expression(ExprContext.ACCEPT_NON_QUERY) {
+        return e;
+     }
+}
+
+List<SqlNode> JsonNameAndValue() :
+{
+    List<SqlNode> list = new ArrayList<SqlNode>();
+    SqlNode e;
+    boolean kvMode = false;
+}
+{
+    [ <KEY> { kvMode = true; } ]
+    e = JsonName() {
+        list.add(e);
+    }
+    (
+        <VALUE>
+    |
+        <COLON> {
+            if (kvMode) {
+                throw SqlUtil.newContextException(getPos(), RESOURCE.illegalColon());
+            }
+        }
+    )
+    e =JsonValueExpression(false) {
+        list.add(e);
+    }
+    {
+        return list;
+    }
+}
+
+SqlNode JsonConstructorNullClause() :
+{
+    SqlNode e;
+}
+{
+    <NULL> <ON> <NULL>
+    {
+        return SqlLiteral.createSymbol(SqlJsonConstructorNullClause.NULL_ON_NULL, getPos());
+    }
+    |
+    <ABSENT> <ON> <NULL>
+    {
+        return SqlLiteral.createSymbol(SqlJsonConstructorNullClause.ABSENT_ON_NULL, getPos());
+    }
+}
+
+SqlCall JsonObjectFunctionCall() :
+{
+    List<SqlNode> nvArgs;
+    SqlNode[] otherArgs;
+    SqlNode e;
+    List<SqlNode> list;
+    final Span span;
+}
+{
+    <JSON_OBJECT> {
+        span = span();
+        nvArgs = new ArrayList<SqlNode>();
+        otherArgs = new SqlNode[1];
+    }
+    <LPAREN> [
+        list = JsonNameAndValue() {
+            nvArgs.addAll(list);
+        }
+        (
+            <COMMA>
+            list = JsonNameAndValue() {
+                nvArgs.addAll(list);
+            }
+        )*
+    ]
+    [
+        e = JsonConstructorNullClause() {
+            otherArgs[0] = e;
+        }
+    ]
+    <RPAREN> {
+        List<SqlNode> args = new ArrayList();
+        args.addAll(Arrays.asList(otherArgs));
+        args.addAll(nvArgs);
+        return SqlStdOperatorTable.JSON_OBJECT.createCall(span.end(this), args);
+    }
+}
+
+SqlCall JsonObjectAggFunctionCall() :
+{
+    SqlNode[] args;
+    List<SqlNode> list;
+    final Span span;
+    SqlJsonConstructorNullClause nullClause;
+    SqlNode e;
+}
+{
+    <JSON_OBJECTAGG> {
+        span = span();
+        args = new SqlNode[2];
+        nullClause = SqlJsonConstructorNullClause.NULL_ON_NULL;
+    }
+    <LPAREN> list = JsonNameAndValue() {
+            args[0] = list.get(0);
+            args[1] = list.get(1);
+    }
+    [
+        e = JsonConstructorNullClause() {
+            nullClause = (SqlJsonConstructorNullClause) ((SqlLiteral) e).getValue();
+        }
+    ]
+    <RPAREN> {
+        switch (nullClause) {
+        case ABSENT_ON_NULL:
+          return SqlStdOperatorTable.JSON_OBJECTAGG_ABSENT_ON_NULL.createCall(span.end(this), args);
+        case NULL_ON_NULL:
+          return SqlStdOperatorTable.JSON_OBJECTAGG_NULL_ON_NULL.createCall(span.end(this), args);
+        default:
+          return SqlStdOperatorTable.JSON_OBJECTAGG_NULL_ON_NULL.createCall(span.end(this), args);
+        }
+    }
+}
+
+SqlCall JsonArrayFunctionCall() :
+{
+    List<SqlNode> elements;
+    SqlNode[] otherArgs;
+    SqlNode e;
+    final Span span;
+}
+{
+    <JSON_ARRAY> {
+        span = span();
+        elements = new ArrayList<SqlNode>();
+        otherArgs = new SqlNode[1];
+    }
+    <LPAREN> [
+        e = JsonValueExpression(false) {
+            elements.add(e);
+        }
+        (
+            <COMMA>
+            e = JsonValueExpression(false) {
+                elements.add(e);
+            }
+        )*
+    ]
+    [
+        e = JsonConstructorNullClause() {
+            otherArgs[0] = e;
+        }
+    ]
+    <RPAREN> {
+        List<SqlNode> args = new ArrayList();
+        args.addAll(Arrays.asList(otherArgs));
+        args.addAll(elements);
+        return SqlStdOperatorTable.JSON_ARRAY.createCall(span.end(this), args);
+    }
+}
+
+SqlCall JsonArrayAggFunctionCall() :
+{
+    SqlNode arg;
+    List<SqlNode> list;
+    final Span span;
+    SqlJsonConstructorNullClause nullClause;
+    SqlNode e;
+}
+{
+    <JSON_ARRAYAGG> {
+        span = span();
+        nullClause = SqlJsonConstructorNullClause.ABSENT_ON_NULL;
+    }
+    <LPAREN> e = JsonValueExpression(false) {
+            arg = e;
+    }
+    [
+        e = JsonConstructorNullClause() {
+            nullClause = (SqlJsonConstructorNullClause) ((SqlLiteral) e).getValue();
+        }
+    ]
+    <RPAREN> {
+        switch (nullClause) {
+        case ABSENT_ON_NULL:
+          return SqlStdOperatorTable.JSON_ARRAYAGG_ABSENT_ON_NULL.createCall(span.end(this), arg);
+        case NULL_ON_NULL:
+          return SqlStdOperatorTable.JSON_ARRAYAGG_NULL_ON_NULL.createCall(span.end(this), arg);
+        default:
+          return SqlStdOperatorTable.JSON_ARRAYAGG_ABSENT_ON_NULL.createCall(span.end(this), arg);
+        }
+    }
 }
 
 /**
@@ -5441,6 +5997,11 @@ SqlPostfixOperator PostfixRowOperator() :
         |   <UNKNOWN> { return SqlStdOperatorTable.IS_NOT_UNKNOWN; }
         |   <A> <SET> { return SqlStdOperatorTable.IS_NOT_A_SET; }
         |   <EMPTY> { return SqlStdOperatorTable.IS_NOT_EMPTY; }
+        |   LOOKAHEAD(2) <JSON> <VALUE> { return SqlStdOperatorTable.IS_NOT_JSON_VALUE; }
+        |   LOOKAHEAD(2) <JSON> <OBJECT> { return SqlStdOperatorTable.IS_NOT_JSON_OBJECT; }
+        |   LOOKAHEAD(2) <JSON> <ARRAY> { return SqlStdOperatorTable.IS_NOT_JSON_ARRAY; }
+        |   LOOKAHEAD(2) <JSON> <SCALAR> { return SqlStdOperatorTable.IS_NOT_JSON_SCALAR; }
+        |   <JSON> { return SqlStdOperatorTable.IS_NOT_JSON_VALUE; }
         )
     |
         (
@@ -5449,6 +6010,11 @@ SqlPostfixOperator PostfixRowOperator() :
         |   <FALSE> { return SqlStdOperatorTable.IS_FALSE; }
         |   <UNKNOWN> { return SqlStdOperatorTable.IS_UNKNOWN; }
         |   <EMPTY> { return SqlStdOperatorTable.IS_EMPTY; }
+        |   LOOKAHEAD(2) <JSON> <VALUE> { return SqlStdOperatorTable.IS_JSON_VALUE; }
+        |   LOOKAHEAD(2) <JSON> <OBJECT> { return SqlStdOperatorTable.IS_JSON_OBJECT; }
+        |   LOOKAHEAD(2) <JSON> <ARRAY> { return SqlStdOperatorTable.IS_JSON_ARRAY; }
+        |   LOOKAHEAD(2) <JSON> <SCALAR> { return SqlStdOperatorTable.IS_JSON_SCALAR; }
+        |   <JSON> { return SqlStdOperatorTable.IS_JSON_VALUE; }
         )
     )
 }
@@ -5461,6 +6027,7 @@ SqlPostfixOperator PostfixRowOperator() :
 {
     < A: "A" >
 |   < ABS: "ABS" >
+|   < ABSENT: "ABSENT" >
 |   < ABSOLUTE: "ABSOLUTE" >
 |   < ACTION: "ACTION" >
 |   < ADA: "ADA" >
@@ -5547,6 +6114,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < COMMIT: "COMMIT" >
 |   < COMMITTED: "COMMITTED" >
 |   < CONDITION: "CONDITION" >
+|   < CONDITIONAL: "CONDITIONAL" >
 |   < CONDITION_NUMBER: "CONDITION_NUMBER" >
 |   < CONNECT: "CONNECT" >
 |   < CONNECTION: "CONNECTION" >
@@ -5630,12 +6198,14 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < ELEMENT: "ELEMENT" >
 |   < ELSE: "ELSE" >
 |   < EMPTY: "EMPTY" >
+|   < ENCODING: "ENCODING">
 |   < END: "END" >
 |   < END_EXEC: "END-EXEC" >
 |   < END_FRAME: "END_FRAME" >
 |   < END_PARTITION: "END_PARTITION" >
 |   < EPOCH: "EPOCH" >
 |   < EQUALS: "EQUALS" >
+|   < ERROR: "ERROR" >
 |   < ESCAPE: "ESCAPE" >
 |   < EVERY: "EVERY" >
 |   < EXCEPT: "EXCEPT" >
@@ -5660,6 +6230,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < FLOOR: "FLOOR" >
 |   < FOLLOWING: "FOLLOWING" >
 |   < FOR: "FOR" >
+|   < FORMAT: "FORMAT" >
 |   < FOREIGN: "FOREIGN" >
 |   < FORTRAN: "FORTRAN" >
 |   < FOUND: "FOUND" >
@@ -5719,6 +6290,13 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < JAVA: "JAVA" >
 |   < JOIN: "JOIN" >
 |   < JSON: "JSON" >
+|   < JSON_ARRAY: "JSON_ARRAY">
+|   < JSON_ARRAYAGG: "JSON_ARRAYAGG">
+|   < JSON_EXISTS: "JSON_EXISTS" >
+|   < JSON_VALUE: "JSON_VALUE" >
+|   < JSON_OBJECT: "JSON_OBJECT">
+|   < JSON_OBJECTAGG: "JSON_OBJECTAGG">
+|   < JSON_QUERY: "JSON_QUERY" >
 |   < K: "K" >
 |   < KEY: "KEY" >
 |   < KEY_MEMBER: "KEY_MEMBER" >
@@ -5834,6 +6412,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < PARTIAL: "PARTIAL" >
 |   < PARTITION: "PARTITION" >
 |   < PASCAL: "PASCAL" >
+|   < PASSING: "PASSING" >
 |   < PASSTHROUGH: "PASSTHROUGH" >
 |   < PAST: "PAST" >
 |   < PATH: "PATH" >
@@ -5895,6 +6474,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < RETURNED_LENGTH: "RETURNED_LENGTH" >
 |   < RETURNED_OCTET_LENGTH: "RETURNED_OCTET_LENGTH" >
 |   < RETURNED_SQLSTATE: "RETURNED_SQLSTATE" >
+|   < RETURNING: "RETURNING" >
 |   < RETURNS: "RETURNS" >
 |   < REVOKE: "REVOKE" >
 |   < RIGHT: "RIGHT" >
@@ -5911,6 +6491,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < ROWS: "ROWS" >
 |   < RUNNING: "RUNNING" >
 |   < SAVEPOINT: "SAVEPOINT" >
+|   < SCALAR: "SCALAR" >
 |   < SCALE: "SCALE" >
 |   < SCHEMA: "SCHEMA" >
 |   < SCHEMA_NAME: "SCHEMA_NAME" >
@@ -6061,6 +6642,7 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < UESCAPE: "UESCAPE" >
 |   < UNBOUNDED: "UNBOUNDED" >
 |   < UNCOMMITTED: "UNCOMMITTED" >
+|   < UNCONDITIONAL: "UNCONDITIONAL" >
 |   < UNDER: "UNDER" >
 |   < UNION: "UNION" >
 |   < UNIQUE: "UNIQUE" >
@@ -6077,6 +6659,9 @@ SqlPostfixOperator PostfixRowOperator() :
 |   < USER_DEFINED_TYPE_NAME: "USER_DEFINED_TYPE_NAME" >
 |   < USER_DEFINED_TYPE_SCHEMA: "USER_DEFINED_TYPE_SCHEMA" >
 |   < USING: "USING" >
+|   < UTF8: "UTF8" >
+|   < UTF16: "UTF16" >
+|   < UTF32: "UTF32" >
 |   < VALUE: "VALUE" >
 |   < VALUES: "VALUES" >
 |   < VALUE_OF: "VALUE_OF" >

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 429cac5..b24ac61 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -56,6 +56,7 @@ import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -138,14 +139,34 @@ import static org.apache.calcite.sql.fun.SqlStdOperatorTable.INITCAP;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_A_SET;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_EMPTY;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_FALSE;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_ARRAY;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_OBJECT;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_SCALAR;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_JSON_VALUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_A_SET;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_EMPTY;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_FALSE;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_ARRAY;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_OBJECT;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_SCALAR;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_JSON_VALUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_NULL;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NOT_TRUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NULL;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_TRUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ITEM;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_API_COMMON_SYNTAX;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAY;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAYAGG_ABSENT_ON_NULL;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAYAGG_NULL_ON_NULL;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_EXISTS;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECT;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECTAGG_ABSENT_ON_NULL;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECTAGG_NULL_ON_NULL;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_QUERY;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_ANY;
+import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.LAG;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.LAST_VALUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.LEAD;
@@ -416,6 +437,51 @@ public class RexImpTable {
     defineMethod(CURRENT_VALUE, BuiltInMethod.SEQUENCE_CURRENT_VALUE.method, NullPolicy.STRICT);
     defineMethod(NEXT_VALUE, BuiltInMethod.SEQUENCE_NEXT_VALUE.method, NullPolicy.STRICT);
 
+    // Json Operators
+    defineMethod(JSON_VALUE_EXPRESSION,
+        BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT);
+    defineMethod(JSON_STRUCTURED_VALUE_EXPRESSION,
+        BuiltInMethod.JSON_STRUCTURED_VALUE_EXPRESSION.method, NullPolicy.STRICT);
+    defineMethod(JSON_API_COMMON_SYNTAX, BuiltInMethod.JSON_API_COMMON_SYNTAX.method,
+        NullPolicy.NONE);
+    defineMethod(JSON_EXISTS, BuiltInMethod.JSON_EXISTS.method, NullPolicy.NONE);
+    defineMethod(JSON_VALUE_ANY, BuiltInMethod.JSON_VALUE_ANY.method, NullPolicy.NONE);
+    defineMethod(JSON_QUERY, BuiltInMethod.JSON_QUERY.method, NullPolicy.NONE);
+    defineMethod(JSON_OBJECT, BuiltInMethod.JSON_OBJECT.method, NullPolicy.NONE);
+    aggMap.put(JSON_OBJECTAGG_NULL_ON_NULL,
+        JsonObjectAggImplementor
+            .supplierFor(BuiltInMethod.JSON_OBJECTAGG_ADD_NULL_ON_NULL.method));
+    aggMap.put(JSON_OBJECTAGG_ABSENT_ON_NULL,
+        JsonObjectAggImplementor
+            .supplierFor(BuiltInMethod.JSON_OBJECTAGG_ADD_ABSENT_ON_NULL.method));
+    defineMethod(JSON_ARRAY, BuiltInMethod.JSON_ARRAY.method, NullPolicy.NONE);
+    aggMap.put(JSON_ARRAYAGG_NULL_ON_NULL,
+        JsonArrayAggImplementor
+            .supplierFor(BuiltInMethod.JSON_ARRAYAGG_ADD_NULL_ON_NULL.method));
+    aggMap.put(JSON_ARRAYAGG_ABSENT_ON_NULL,
+        JsonArrayAggImplementor
+            .supplierFor(BuiltInMethod.JSON_ARRAYAGG_ADD_ABSENT_ON_NULL.method));
+    defineImplementor(IS_JSON_VALUE, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method), false);
+    defineImplementor(IS_JSON_OBJECT, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method), false);
+    defineImplementor(IS_JSON_ARRAY, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method), false);
+    defineImplementor(IS_JSON_SCALAR, NullPolicy.NONE,
+            new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method), false);
+    defineImplementor(IS_NOT_JSON_VALUE, NullPolicy.NONE,
+        NotImplementor.of(
+            new MethodImplementor(BuiltInMethod.IS_JSON_VALUE.method)), false);
+    defineImplementor(IS_NOT_JSON_OBJECT, NullPolicy.NONE,
+        NotImplementor.of(
+            new MethodImplementor(BuiltInMethod.IS_JSON_OBJECT.method)), false);
+    defineImplementor(IS_NOT_JSON_ARRAY, NullPolicy.NONE,
+        NotImplementor.of(
+            new MethodImplementor(BuiltInMethod.IS_JSON_ARRAY.method)), false);
+    defineImplementor(IS_NOT_JSON_SCALAR, NullPolicy.NONE,
+        NotImplementor.of(
+            new MethodImplementor(BuiltInMethod.IS_JSON_SCALAR.method)), false);
+
     // System functions
     final SystemFunctionImplementor systemFunctionImplementor =
         new SystemFunctionImplementor();
@@ -1722,6 +1788,93 @@ public class RexImpTable {
     }
   }
 
+  /** Implementor for the {@code JSON_OBJECTAGG} aggregate function. */
+  static class JsonObjectAggImplementor implements AggImplementor {
+
+    private final Method m;
+
+    JsonObjectAggImplementor(Method m) {
+      this.m = m;
+    }
+
+    static Supplier<JsonObjectAggImplementor> supplierFor(Method m) {
+      return () -> new JsonObjectAggImplementor(m);
+    }
+
+    @Override public List<Type> getStateType(AggContext info) {
+      return Collections.singletonList(Map.class);
+    }
+
+    @Override public void implementReset(AggContext info,
+                                                   AggResetContext reset) {
+      reset.currentBlock().add(
+          Expressions.statement(
+              Expressions.assign(reset.accumulator().get(0),
+                  Expressions.new_(HashMap.class))));
+    }
+
+    @Override public void implementAdd(AggContext info,
+                                              AggAddContext add) {
+      add.currentBlock().add(
+          Expressions.statement(
+              Expressions.call(
+                  m,
+                  Iterables.concat(
+                      Collections.singletonList(
+                          add.accumulator().get(0)),
+                      add.arguments()))));
+    }
+
+    @Override public Expression implementResult(AggContext info,
+                                                       AggResultContext result) {
+      return Expressions.call(BuiltInMethod.JSONIZE.method,
+          result.accumulator().get(0));
+    }
+  }
+
+  /** Implementor for the {@code JSON_ARRAYAGG} aggregate function. */
+  static class JsonArrayAggImplementor implements AggImplementor {
+
+    private final Method m;
+
+    JsonArrayAggImplementor(Method m) {
+      this.m = m;
+    }
+
+    static Supplier<JsonArrayAggImplementor> supplierFor(Method m) {
+      return () -> new JsonArrayAggImplementor(m);
+    }
+    @Override public List<Type> getStateType(AggContext info) {
+      return Collections.singletonList(List.class);
+    }
+
+    @Override public void implementReset(AggContext info,
+                                         AggResetContext reset) {
+      reset.currentBlock().add(
+          Expressions.statement(
+              Expressions.assign(reset.accumulator().get(0),
+                  Expressions.new_(ArrayList.class))));
+    }
+
+    @Override public void implementAdd(AggContext info,
+                                       AggAddContext add) {
+      add.currentBlock().add(
+          Expressions.statement(
+              Expressions.call(
+                  m,
+                  Iterables.concat(
+                      Collections.singletonList(
+                          add.accumulator().get(0)),
+                      add.arguments()))));
+    }
+
+    @Override public Expression implementResult(AggContext info,
+                                                AggResultContext result) {
+      return Expressions.call(BuiltInMethod.JSONIZE.method,
+          result.accumulator().get(0));
+    }
+  }
+
   /** Implementor for the {@code TRIM} function. */
   private static class TrimImplementor implements NotNullImplementor {
     public Expression implement(RexToLixTranslator translator, RexCall call,

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 54a8c2d..b5a512d 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -89,6 +89,9 @@ public interface CalciteResource {
   @BaseMessage("ROW expression encountered in illegal context")
   ExInst<CalciteException> illegalRowExpression();
 
+  @BaseMessage("Illegal identifier '':''. Was expecting ''VALUE''")
+  ExInst<CalciteException> illegalColon();
+
   @BaseMessage("TABLESAMPLE percentage must be between 0 and 100, inclusive")
   @Property(name = "SQLSTATE", value = "2202H")
   ExInst<CalciteException> invalidSampleSize();
@@ -224,6 +227,9 @@ public interface CalciteResource {
   @BaseMessage("Expected a boolean type")
   ExInst<SqlValidatorException> expectedBoolean();
 
+  @BaseMessage("Expected a character type")
+  ExInst<SqlValidatorException> expectedCharacter();
+
   @BaseMessage("ELSE clause or at least one THEN clause must be non-NULL")
   ExInst<SqlValidatorException> mustNotNullInElse();
 
@@ -769,6 +775,77 @@ public interface CalciteResource {
 
   @BaseMessage("Dialect does not support feature: ''{0}''")
   ExInst<SqlValidatorException> dialectDoesNotSupportFeature(String featureName);
+
+  @BaseMessage("Substring error: negative substring length not allowed")
+  ExInst<CalciteException> illegalNegativeSubstringLength();
+
+  @BaseMessage("Trim error: trim character must be exactly 1 character")
+  ExInst<CalciteException> trimError();
+
+  @BaseMessage("Invalid types for arithmetic: {0} {1} {2}")
+  ExInst<CalciteException> invalidTypesForArithmetic(String clazzName0, String op,
+                                                     String clazzName1);
+
+  @BaseMessage("Invalid types for comparison: {0} {1} {2}")
+  ExInst<CalciteException> invalidTypesForComparison(String clazzName0, String op,
+                                                     String clazzName1);
+
+  @BaseMessage("Cannot convert {0} to {1}")
+  ExInst<CalciteException> cannotConvert(String o, String toType);
+
+  @BaseMessage("Invalid character for cast: {0}")
+  ExInst<CalciteException> invalidCharacterForCast(String s);
+
+  @BaseMessage("More than one value in list: {0}")
+  ExInst<CalciteException> moreThanOneValueInList(String list);
+
+  @BaseMessage("Failed to access field ''{0}'' of object of type {1}")
+  ExInstWithCause<CalciteException> failedToAccessField(String fieldName, String typeName);
+
+  @BaseMessage("Illegal jsonpath spec ''{0}'', format of the spec should be: ''<lax|strict> $'{'expr'}'''")
+  ExInst<CalciteException> illegalJsonPathSpec(String pathSpec);
+
+  @BaseMessage("Illegal jsonpath mode ''{0}''")
+  ExInst<CalciteException> illegalJsonPathMode(String pathMode);
+
+  @BaseMessage("Illegal jsonpath mode ''{0}'' in jsonpath spec: ''{1}''")
+  ExInst<CalciteException> illegalJsonPathModeInPathSpec(String pathMode, String pathSpec);
+
+  @BaseMessage("Strict jsonpath mode requires a non empty returned value, but is null")
+  ExInst<CalciteException> strictPathModeRequiresNonEmptyValue();
+
+  @BaseMessage("Illegal error behavior ''{0}'' specified in JSON_EXISTS function")
+  ExInst<CalciteException> illegalErrorBehaviorInJsonExistsFunc(String errorBehavior);
+
+  @BaseMessage("Empty result of JSON_VALUE function is not allowed")
+  ExInst<CalciteException> emptyResultOfJsonValueFuncNotAllowed();
+
+  @BaseMessage("Illegal empty behavior ''{0}'' specified in JSON_VALUE function")
+  ExInst<CalciteException> illegalEmptyBehaviorInJsonValueFunc(String emptyBehavior);
+
+  @BaseMessage("Illegal error behavior ''{0}'' specified in JSON_VALUE function")
+  ExInst<CalciteException> illegalErrorBehaviorInJsonValueFunc(String errorBehavior);
+
+  @BaseMessage("Strict jsonpath mode requires scalar value, and the actual value is: ''{0}''")
+  ExInst<CalciteException> scalarValueRequiredInStrictModeOfJsonValueFunc(String value);
+
+  @BaseMessage("Illegal wrapper behavior ''{0}'' specified in JSON_QUERY function")
+  ExInst<CalciteException> illegalWrapperBehaviorInJsonQueryFunc(String wrapperBehavior);
+
+  @BaseMessage("Empty result of JSON_QUERY function is not allowed")
+  ExInst<CalciteException> emptyResultOfJsonQueryFuncNotAllowed();
+
+  @BaseMessage("Illegal empty behavior ''{0}'' specified in JSON_VALUE function")
+  ExInst<CalciteException> illegalEmptyBehaviorInJsonQueryFunc(String emptyBehavior);
+
+  @BaseMessage("Strict jsonpath mode requires array or object value, and the actual value is: ''{0}''")
+  ExInst<CalciteException> arrayOrObjectValueRequiredInStrictModeOfJsonQueryFunc(String value);
+
+  @BaseMessage("Illegal error behavior ''{0}'' specified in JSON_VALUE function")
+  ExInst<CalciteException> illegalErrorBehaviorInJsonQueryFunc(String errorBehavior);
+
+  @BaseMessage("Null key of JSON object is not allowed")
+  ExInst<CalciteException> nullKeyOfJsonObjectNotAllowed();
 }
 
 // End CalciteResource.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 837171b..adda9d8 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -33,11 +33,25 @@ import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.function.NonDeterministic;
 import org.apache.calcite.linq4j.tree.Primitive;
 import org.apache.calcite.runtime.FlatLists.ComparableList;
+import org.apache.calcite.sql.SqlJsonConstructorNullClause;
+import org.apache.calcite.sql.SqlJsonExistsErrorBehavior;
+import org.apache.calcite.sql.SqlJsonQueryEmptyOrErrorBehavior;
+import org.apache.calcite.sql.SqlJsonQueryWrapperBehavior;
+import org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior;
 import org.apache.calcite.util.Bug;
 import org.apache.calcite.util.NumberUtil;
 import org.apache.calcite.util.TimeWithTimeZoneString;
 import org.apache.calcite.util.TimestampWithTimeZoneString;
 
+import com.jayway.jsonpath.Configuration;
+import com.jayway.jsonpath.DocumentContext;
+import com.jayway.jsonpath.JsonPath;
+import com.jayway.jsonpath.Option;
+import com.jayway.jsonpath.spi.json.JacksonJsonProvider;
+import com.jayway.jsonpath.spi.json.JsonProvider;
+import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
+import com.jayway.jsonpath.spi.mapper.MappingProvider;
+
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
 import java.math.BigInteger;
@@ -49,6 +63,7 @@ import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -57,11 +72,15 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TimeZone;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static org.apache.calcite.util.Static.RESOURCE;
+
 /**
  * Helper methods to implement SQL functions in generated code.
  *
@@ -107,6 +126,13 @@ public class SqlFunctions {
   private static final ThreadLocal<Map<String, AtomicLong>> THREAD_SEQUENCES =
       ThreadLocal.withInitial(HashMap::new);
 
+  private static final Pattern JSON_PATH_BASE =
+      Pattern.compile("^\\s*(?<mode>strict|lax)\\s+(?<spec>.+)$",
+          Pattern.CASE_INSENSITIVE | Pattern.DOTALL | Pattern.MULTILINE);
+
+  private static final JsonProvider JSON_PATH_JSON_PROVIDER = new JacksonJsonProvider();
+  private static final MappingProvider JSON_PATH_MAPPING_PROVIDER = new JacksonMappingProvider();
+
   private SqlFunctions() {
   }
 
@@ -118,7 +144,7 @@ public class SqlFunctions {
     }
     int e = s + l;
     if (e < s) {
-      throw new IllegalArgumentException("substring error: negative substring length not allowed");
+      throw RESOURCE.illegalNegativeSubstringLength().ex();
     }
     if (s > lc || e < 1) {
       return "";
@@ -141,7 +167,7 @@ public class SqlFunctions {
     }
     int e = s + l;
     if (e < s) {
-      throw new IllegalArgumentException("substring error: negative substring length not allowed");
+      throw RESOURCE.illegalNegativeSubstringLength().ex();
     }
     if (s > lc || e < 1) {
       return ByteString.EMPTY;
@@ -237,7 +263,7 @@ public class SqlFunctions {
   public static String trim(boolean left, boolean right, String seek,
       String s, boolean strict) {
     if (strict && seek.length() != 1) {
-      throw new IllegalArgumentException("trim error: trim character must be exactly 1 character");
+      throw RESOURCE.trimError().ex();
     }
     int j = s.length();
     if (right) {
@@ -812,16 +838,16 @@ public class SqlFunctions {
     throw notArithmetic("*", b0, b1);
   }
 
-  private static IllegalArgumentException notArithmetic(String op, Object b0,
+  private static RuntimeException notArithmetic(String op, Object b0,
       Object b1) {
-    return new IllegalArgumentException("Invalid types for arithmetic: "
-        + b0.getClass() + " " + op + " " + b1.getClass());
+    return RESOURCE.invalidTypesForArithmetic(b0.getClass().toString(),
+        op, b1.getClass().toString()).ex();
   }
 
-  private static IllegalArgumentException notComparable(String op, Object b0,
+  private static RuntimeException notComparable(String op, Object b0,
       Object b1) {
-    return new IllegalArgumentException("Invalid types for comparison: "
-        + b0.getClass() + " " + op + " " + b1.getClass());
+    return RESOURCE.invalidTypesForComparison(b0.getClass().toString(),
+        op, b1.getClass().toString()).ex();
   }
 
   // EXP
@@ -1498,7 +1524,7 @@ public class SqlFunctions {
 
   @NonDeterministic
   private static Object cannotConvert(Object o, Class toType) {
-    throw new RuntimeException("Cannot convert " + o + " to " + toType);
+    throw RESOURCE.cannotConvert(o.toString(), toType.toString()).ex();
   }
 
   /** CAST(VARCHAR AS BOOLEAN). */
@@ -1509,7 +1535,7 @@ public class SqlFunctions {
     } else if (s.equalsIgnoreCase("FALSE")) {
       return false;
     } else {
-      throw new RuntimeException("Invalid character for cast");
+      throw RESOURCE.invalidCharacterForCast(s).ex();
     }
   }
 
@@ -2092,7 +2118,7 @@ public class SqlFunctions {
     try {
       return Primitive.asList(a.getArray());
     } catch (SQLException e) {
-      throw new RuntimeException(e);
+      throw toUnchecked(e);
     }
   }
 
@@ -2131,7 +2157,7 @@ public class SqlFunctions {
     case 1:
       return list.get(0);
     default:
-      throw new RuntimeException("more than one value");
+      throw RESOURCE.moreThanOneValueInList(list.toString()).ex();
     }
   }
 
@@ -2419,12 +2445,393 @@ public class SqlFunctions {
         Field structField = beanClass.getDeclaredField(fieldName);
         return structField.get(structObject);
       } catch (NoSuchFieldException | IllegalAccessException ex) {
-        throw new IllegalStateException("Failed to access field '" + fieldName
-            + "' of object of type " + beanClass.getName(), ex);
+        throw RESOURCE.failedToAccessField(fieldName, beanClass.getName()).ex(ex);
+      }
+    }
+  }
+
+
+  private static boolean isScalarObject(Object obj) {
+    if (obj instanceof Collection) {
+      return false;
+    }
+    if (obj instanceof Map) {
+      return false;
+    }
+    return true;
+  }
+
+  public static Object jsonValueExpression(String input) {
+    try {
+      return dejsonize(input);
+    } catch (Exception e) {
+      return e;
+    }
+  }
+
+  public static Object jsonStructuredValueExpression(Object input) {
+    return input;
+  }
+
+  public static PathContext jsonApiCommonSyntax(Object input, String pathSpec) {
+    try {
+      Matcher matcher = JSON_PATH_BASE.matcher(pathSpec);
+      if (!matcher.matches()) {
+        throw RESOURCE.illegalJsonPathSpec(pathSpec).ex();
+      }
+      PathMode mode = PathMode.valueOf(matcher.group(1).toUpperCase(Locale.ENGLISH));
+      String pathWff = matcher.group(2);
+      DocumentContext ctx;
+      switch (mode) {
+      case STRICT:
+        if (input instanceof Exception) {
+          return PathContext.withStrictException((Exception) input);
+        }
+        ctx = JsonPath.parse(input,
+            Configuration
+                .builder()
+                .jsonProvider(JSON_PATH_JSON_PROVIDER)
+                .mappingProvider(JSON_PATH_MAPPING_PROVIDER)
+                .build()
+        );
+        break;
+      case LAX:
+        if (input instanceof Exception) {
+          return PathContext.withReturned(PathMode.LAX, null);
+        }
+        ctx = JsonPath.parse(input,
+            Configuration
+                .builder()
+                .options(Option.SUPPRESS_EXCEPTIONS)
+                .jsonProvider(JSON_PATH_JSON_PROVIDER)
+                .mappingProvider(JSON_PATH_MAPPING_PROVIDER)
+                .build()
+        );
+        break;
+      default:
+        throw RESOURCE.illegalJsonPathModeInPathSpec(mode.toString(), pathSpec).ex();
+      }
+      try {
+        return PathContext.withReturned(mode, ctx.read(pathWff));
+      } catch (Exception e) {
+        return PathContext.withStrictException(e);
+      }
+    } catch (Exception e) {
+      return PathContext.withUnknownException(e);
+    }
+  }
+
+  public static Boolean jsonExists(Object input) {
+    return jsonExists(input, SqlJsonExistsErrorBehavior.FALSE);
+  }
+
+  public static Boolean jsonExists(Object input, SqlJsonExistsErrorBehavior errorBehavior) {
+    PathContext context = (PathContext) input;
+    if (context.exc != null) {
+      switch (errorBehavior) {
+      case TRUE:
+        return Boolean.TRUE;
+      case FALSE:
+        return Boolean.FALSE;
+      case ERROR:
+        throw toUnchecked(context.exc);
+      case UNKNOWN:
+        return null;
+      default:
+        throw RESOURCE.illegalErrorBehaviorInJsonExistsFunc(errorBehavior.toString()).ex();
+      }
+    } else {
+      return !Objects.isNull(context.pathReturned);
+    }
+  }
+
+  public static Object jsonValueAny(Object input,
+                                 SqlJsonValueEmptyOrErrorBehavior emptyBehavior,
+                                 Object defaultValueOnEmpty,
+                                 SqlJsonValueEmptyOrErrorBehavior errorBehavior,
+                                 Object defaultValueOnError) {
+    PathContext context = (PathContext) input;
+    Exception exc;
+    if (context.exc != null) {
+      exc = context.exc;
+    } else {
+      Object value = context.pathReturned;
+      if (value == null || context.mode == PathMode.LAX
+          && !isScalarObject(value)) {
+        switch (emptyBehavior) {
+        case ERROR:
+          throw RESOURCE.emptyResultOfJsonValueFuncNotAllowed().ex();
+        case NULL:
+          return null;
+        case DEFAULT:
+          return defaultValueOnEmpty;
+        default:
+          throw RESOURCE.illegalEmptyBehaviorInJsonValueFunc(emptyBehavior.toString()).ex();
+        }
+      } else if (context.mode == PathMode.STRICT && !isScalarObject(value)) {
+        exc = RESOURCE.scalarValueRequiredInStrictModeOfJsonValueFunc(value.toString()).ex();
+      } else {
+        return value;
+      }
+    }
+    switch (errorBehavior) {
+    case ERROR:
+      throw toUnchecked(exc);
+    case NULL:
+      return null;
+    case DEFAULT:
+      return defaultValueOnError;
+    default:
+      throw RESOURCE.illegalErrorBehaviorInJsonValueFunc(errorBehavior.toString()).ex();
+    }
+  }
+
+  public static String jsonQuery(Object input,
+                                 SqlJsonQueryWrapperBehavior wrapperBehavior,
+                                 SqlJsonQueryEmptyOrErrorBehavior emptyBehavior,
+                                 SqlJsonQueryEmptyOrErrorBehavior errorBehavior) {
+    PathContext context = (PathContext) input;
+    Exception exc;
+    if (context.exc != null) {
+      exc = context.exc;
+    } else {
+      Object value;
+      if (context.pathReturned == null) {
+        value = null;
+      } else {
+        switch (wrapperBehavior) {
+        case WITHOUT_ARRAY:
+          value = context.pathReturned;
+          break;
+        case WITH_UNCONDITIONAL_ARRAY:
+          value = Collections.singletonList(context.pathReturned);
+          break;
+        case WITH_CONDITIONAL_ARRAY:
+          if (context.pathReturned instanceof Collection) {
+            value = context.pathReturned;
+          } else {
+            value = Collections.singletonList(context.pathReturned);
+          }
+          break;
+        default:
+          throw RESOURCE.illegalWrapperBehaviorInJsonQueryFunc(wrapperBehavior.toString()).ex();
+        }
+      }
+      if (value == null || context.mode == PathMode.LAX
+          && isScalarObject(value)) {
+        switch (emptyBehavior) {
+        case ERROR:
+          throw RESOURCE.emptyResultOfJsonQueryFuncNotAllowed().ex();
+        case NULL:
+          return null;
+        case EMPTY_ARRAY:
+          return "[]";
+        case EMPTY_OBJECT:
+          return "{}";
+        default:
+          throw RESOURCE.illegalEmptyBehaviorInJsonQueryFunc(emptyBehavior.toString()).ex();
+        }
+      } else if (context.mode == PathMode.STRICT && isScalarObject(value)) {
+        exc = RESOURCE.arrayOrObjectValueRequiredInStrictModeOfJsonQueryFunc(value.toString()).ex();
+      } else {
+        try {
+          return jsonize(value);
+        } catch (Exception e) {
+          exc = e;
+        }
+      }
+    }
+    switch (errorBehavior) {
+    case ERROR:
+      throw toUnchecked(exc);
+    case NULL:
+      return null;
+    case EMPTY_ARRAY:
+      return "[]";
+    case EMPTY_OBJECT:
+      return "{}";
+    default:
+      throw RESOURCE.illegalErrorBehaviorInJsonQueryFunc(errorBehavior.toString()).ex();
+    }
+  }
+
+  public static String jsonize(Object input) {
+    return JSON_PATH_JSON_PROVIDER.toJson(input);
+  }
+
+  public static Object dejsonize(String input) {
+    return JSON_PATH_JSON_PROVIDER.parse(input);
+  }
+
+  public static String jsonObject(SqlJsonConstructorNullClause nullClause, Object... kvs) {
+    assert kvs.length % 2 == 0;
+    Map<String, Object> map = new HashMap<>();
+    for (int i = 0; i < kvs.length; i += 2) {
+      String k = (String) kvs[i];
+      Object v = kvs[i + 1];
+      if (k == null) {
+        throw RESOURCE.nullKeyOfJsonObjectNotAllowed().ex();
+      }
+      if (v == null) {
+        if (nullClause == SqlJsonConstructorNullClause.NULL_ON_NULL) {
+          map.put(k, null);
+        }
+      } else {
+        map.put(k, v);
+      }
+    }
+    return jsonize(map);
+  }
+
+  public static void jsonObjectAggAdd(Map map, String k, Object v,
+                                        SqlJsonConstructorNullClause nullClause) {
+    if (k == null) {
+      throw RESOURCE.nullKeyOfJsonObjectNotAllowed().ex();
+    }
+    if (v == null) {
+      if (nullClause == SqlJsonConstructorNullClause.NULL_ON_NULL) {
+        map.put(k, null);
+      }
+    } else {
+      map.put(k, v);
+    }
+  }
+
+  public static void jsonObjectAggAddNullOnNull(Map map, String k, Object v) {
+    jsonObjectAggAdd(map, k, v, SqlJsonConstructorNullClause.NULL_ON_NULL);
+  }
+
+  public static void jsonObjectAggAddAbsentOnNull(Map map, String k, Object v) {
+    jsonObjectAggAdd(map, k, v, SqlJsonConstructorNullClause.ABSENT_ON_NULL);
+  }
+
+  public static String jsonArray(SqlJsonConstructorNullClause nullClause, Object... elements) {
+    List<Object> list = new ArrayList<>();
+    for (Object element : elements) {
+      if (element == null) {
+        if (nullClause == SqlJsonConstructorNullClause.NULL_ON_NULL) {
+          list.add(null);
+        }
+      } else {
+        list.add(element);
+      }
+    }
+    return jsonize(list);
+  }
+
+  public static void jsonArrayAggAdd(List list, Object element,
+                                      SqlJsonConstructorNullClause nullClause) {
+    if (element == null) {
+      if (nullClause == SqlJsonConstructorNullClause.NULL_ON_NULL) {
+        list.add(null);
+      }
+    } else {
+      list.add(element);
+    }
+  }
+
+  public static void jsonArrayAggAddNullOnNull(List list, Object element) {
+    jsonArrayAggAdd(list, element, SqlJsonConstructorNullClause.NULL_ON_NULL);
+  }
+
+  public static void jsonArrayAggAddAbsentOnNull(List list, Object element) {
+    jsonArrayAggAdd(list, element, SqlJsonConstructorNullClause.ABSENT_ON_NULL);
+  }
+
+  public static boolean isJsonValue(String input) {
+    try {
+      dejsonize(input);
+      return true;
+    } catch (Exception e) {
+      return false;
+    }
+  }
+
+  public static boolean isJsonObject(String input) {
+    try {
+      Object o = dejsonize(input);
+      return o instanceof Map;
+    } catch (Exception e) {
+      return false;
+    }
+  }
+
+  public static boolean isJsonArray(String input) {
+    try {
+      Object o = dejsonize(input);
+      return o instanceof Collection;
+    } catch (Exception e) {
+      return false;
+    }
+  }
+
+  public static boolean isJsonScalar(String input) {
+    try {
+      Object o = dejsonize(input);
+      return !(o instanceof Map) && !(o instanceof Collection);
+    } catch (Exception e) {
+      return false;
+    }
+  }
+
+  private static RuntimeException toUnchecked(Exception e) {
+    if (e instanceof RuntimeException) {
+      return (RuntimeException) e;
+    }
+    return new RuntimeException(e);
+  }
+
+  /**
+   * Returned path context of JsonApiCommonSyntax, public for testing.
+   */
+  public static class PathContext {
+    public final PathMode mode;
+    public final Object pathReturned;
+    public final Exception exc;
+
+    private PathContext(PathMode mode, Object pathReturned, Exception exc) {
+      this.mode = mode;
+      this.pathReturned = pathReturned;
+      this.exc = exc;
+    }
+
+    public static PathContext withUnknownException(Exception exc) {
+      return new PathContext(PathMode.UNKNOWN, null, exc);
+    }
+
+    public static PathContext withStrictException(Exception exc) {
+      return new PathContext(PathMode.STRICT, null, exc);
+    }
+
+    public static PathContext withReturned(PathMode mode, Object pathReturned) {
+      if (mode == PathMode.UNKNOWN) {
+        throw RESOURCE.illegalJsonPathMode(mode.toString()).ex();
+      }
+      if (mode == PathMode.STRICT && pathReturned == null) {
+        throw RESOURCE.strictPathModeRequiresNonEmptyValue().ex();
       }
+      return new PathContext(mode, pathReturned, null);
+    }
+
+    @Override public String toString() {
+      return "PathContext{"
+          + "mode=" + mode
+          + ", pathReturned=" + pathReturned
+          + ", exc=" + exc
+          + '}';
     }
   }
 
+  /**
+   * Path spec has two different modes: lax mode and strict mode. Lax mode suppress any thrown
+   * exception and return null; where strict mode throws exceptions.
+   */
+  public enum PathMode {
+    LAX,
+    STRICT,
+    UNKNOWN
+  }
+
   /** Enumerates over the cartesian product of the given lists, returning
    * a comparable list for each row.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonConstructorNullClause.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonConstructorNullClause.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonConstructorNullClause.java
new file mode 100644
index 0000000..a4a94b8
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonConstructorNullClause.java
@@ -0,0 +1,26 @@
+/*
+ * 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.calcite.sql;
+
+/**
+ * Indicating that how do Json constructors handle null
+ */
+public enum SqlJsonConstructorNullClause {
+  NULL_ON_NULL, ABSENT_ON_NULL
+}
+
+// End SqlJsonConstructorNullClause.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonEmptyOrError.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonEmptyOrError.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonEmptyOrError.java
new file mode 100644
index 0000000..82491cd
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonEmptyOrError.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql;
+
+import java.util.Locale;
+
+/**
+ * Flag to indicate if the json value is missing or an error is thrown where
+ * EmptyOrErrorBehavior is invoked.
+ */
+public enum SqlJsonEmptyOrError {
+  EMPTY, ERROR;
+
+  @Override public String toString() {
+    return String.format(Locale.ENGLISH, "SqlJsonEmptyOrError[%s]", name());
+  }
+}
+
+// End SqlJsonEmptyOrError.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonEncoding.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonEncoding.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonEncoding.java
new file mode 100644
index 0000000..77bc632
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonEncoding.java
@@ -0,0 +1,42 @@
+/*
+ * 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.calcite.sql;
+
+/**
+ * SqlJsonEncoding lists the supported json encodings that could be passed to JsonValueExpression.
+ */
+public enum SqlJsonEncoding {
+  UTF8("UTF8"),
+  UTF16("UTF16"),
+  UTF32("UTF32");
+
+  private final String standardName;
+
+  SqlJsonEncoding(String standardName) {
+    this.standardName = standardName;
+  }
+
+  public String getStandardName() {
+    return standardName;
+  }
+
+  @Override public String toString() {
+    return getStandardName();
+  }
+}
+
+// End SqlJsonEncoding.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonExistsErrorBehavior.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonExistsErrorBehavior.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonExistsErrorBehavior.java
new file mode 100644
index 0000000..4da8a4f
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonExistsErrorBehavior.java
@@ -0,0 +1,26 @@
+/*
+ * 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.calcite.sql;
+
+/**
+ * Categorizing Json exists error behaviors.
+ */
+public enum SqlJsonExistsErrorBehavior {
+  TRUE, FALSE, UNKNOWN, ERROR
+}
+
+// End SqlJsonExistsErrorBehavior.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryEmptyOrErrorBehavior.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryEmptyOrErrorBehavior.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryEmptyOrErrorBehavior.java
new file mode 100644
index 0000000..ce363fc
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryEmptyOrErrorBehavior.java
@@ -0,0 +1,32 @@
+/*
+ * 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.calcite.sql;
+
+import java.util.Locale;
+
+/**
+ * Categorizing Json query empty or error behaviors.
+ */
+public enum SqlJsonQueryEmptyOrErrorBehavior {
+  ERROR, NULL, EMPTY_ARRAY, EMPTY_OBJECT;
+
+  @Override public String toString() {
+    return String.format(Locale.ENGLISH, "SqlJsonQueryEmptyOrErrorBehavior[%s]", name());
+  }
+}
+
+// End SqlJsonQueryEmptyOrErrorBehavior.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryWrapperBehavior.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryWrapperBehavior.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryWrapperBehavior.java
new file mode 100644
index 0000000..12c9dc4
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonQueryWrapperBehavior.java
@@ -0,0 +1,26 @@
+/*
+ * 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.calcite.sql;
+
+/**
+ * How json query function handle array result.
+ */
+public enum SqlJsonQueryWrapperBehavior {
+  WITHOUT_ARRAY, WITH_CONDITIONAL_ARRAY, WITH_UNCONDITIONAL_ARRAY
+}
+
+// End SqlJsonQueryWrapperBehavior.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlJsonValueEmptyOrErrorBehavior.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlJsonValueEmptyOrErrorBehavior.java b/core/src/main/java/org/apache/calcite/sql/SqlJsonValueEmptyOrErrorBehavior.java
new file mode 100644
index 0000000..42cc84c
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlJsonValueEmptyOrErrorBehavior.java
@@ -0,0 +1,32 @@
+/*
+ * 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.calcite.sql;
+
+import java.util.Locale;
+
+/**
+ * Categorizing Json value empty or error behaviors.
+ */
+public enum SqlJsonValueEmptyOrErrorBehavior {
+  ERROR, NULL, DEFAULT;
+
+  @Override public String toString() {
+    return String.format(Locale.ENGLISH, "SqlJsonValueEmptyOrErrorBehavior[%s]", name());
+  }
+}
+
+// End SqlJsonValueEmptyOrErrorBehavior.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index 225d3b4..e56b066 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -727,6 +727,26 @@ public enum SqlKind {
   MULTISET_QUERY_CONSTRUCTOR,
 
   /**
+   * The JSON value expression.
+   */
+  JSON_VALUE_EXPRESSION,
+
+  /**
+   * The JSON API common syntax.
+   */
+  JSON_API_COMMON_SYNTAX,
+
+  /**
+   * The {@code JSON_ARRAYAGG} aggregate function.
+   */
+  JSON_ARRAYAGG,
+
+  /**
+   * The {@code JSON_OBJECTAGG} aggregate function.
+   */
+  JSON_OBJECTAGG,
+
+  /**
    * The "UNNEST" operator.
    */
   UNNEST,
@@ -1072,7 +1092,7 @@ public enum SqlKind {
           LAST_VALUE, COVAR_POP, COVAR_SAMP, REGR_COUNT, REGR_SXX, REGR_SYY,
           AVG, STDDEV_POP, STDDEV_SAMP, VAR_POP, VAR_SAMP, NTILE, COLLECT,
           FUSION, SINGLE_VALUE, ROW_NUMBER, RANK, PERCENT_RANK, DENSE_RANK,
-          CUME_DIST);
+          CUME_DIST, JSON_ARRAYAGG, JSON_OBJECTAGG);
 
   /**
    * Category consisting of all DML operators.
@@ -1156,7 +1176,8 @@ public enum SqlKind {
                   TIMESTAMP_ADD, TIMESTAMP_DIFF, EXTRACT,
                   LITERAL_CHAIN, JDBC_FN, PRECEDING, FOLLOWING, ORDER_BY,
                   NULLS_FIRST, NULLS_LAST, COLLECTION_TABLE, TABLESAMPLE,
-                  VALUES, WITH, WITH_ITEM, SKIP_TO_FIRST, SKIP_TO_LAST),
+                  VALUES, WITH, WITH_ITEM, SKIP_TO_FIRST, SKIP_TO_LAST,
+                  JSON_VALUE_EXPRESSION, JSON_API_COMMON_SYNTAX),
               AGGREGATE, DML, DDL));
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
new file mode 100644
index 0000000..3cf5baa
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.fun;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlSpecialOperator;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.validate.SqlValidator;
+
+/**
+ * The JSON API common syntax including a path specification, which is for
+ * JSON querying and processing.
+ */
+public class SqlJsonApiCommonSyntaxOperator extends SqlSpecialOperator {
+
+  public SqlJsonApiCommonSyntaxOperator() {
+    super(
+        "JSON_API_COMMON_SYNTAX",
+        SqlKind.JSON_API_COMMON_SYNTAX,
+        100,
+        true,
+        ReturnTypes.explicit(SqlTypeName.ANY),
+        null,
+        OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)
+    );
+  }
+
+  @Override protected void checkOperandCount(
+      SqlValidator validator,
+      SqlOperandTypeChecker argType,
+      SqlCall call
+  ) {
+    if (call.operandCount() != 2) {
+      throw new UnsupportedOperationException("json passing syntax is not yet supported");
+    }
+  }
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
+    SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.SIMPLE);
+    call.operand(0).unparse(writer, 0, 0);
+    writer.sep(",", true);
+    call.operand(1).unparse(writer, 0, 0);
+    if (call.operandCount() > 2) {
+      writer.keyword("PASSING");
+      for (int i = 2; i < call.getOperandList().size(); i += 2) {
+        call.operand(i).unparse(writer, 0, 0);
+        writer.keyword("AS");
+        call.operand(i + 1).unparse(writer, 0, 0);
+      }
+    }
+    writer.endFunCall(frame);
+  }
+}
+
+// End SqlJsonApiCommonSyntaxOperator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
new file mode 100644
index 0000000..4cdfea1
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.sql.fun;
+
+import org.apache.calcite.sql.SqlAggFunction;
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlJsonConstructorNullClause;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+/**
+ * The <code>JSON_OBJECTAGG</code> aggregation function.
+ */
+public class SqlJsonArrayAggAggFunction extends SqlAggFunction {
+  private final String name;
+  private final SqlJsonConstructorNullClause nullClause;
+
+  public SqlJsonArrayAggAggFunction(String name, SqlJsonConstructorNullClause nullClause) {
+    super(
+        name,
+        null,
+        SqlKind.JSON_ARRAYAGG,
+        ReturnTypes.VARCHAR_2000,
+        null,
+        OperandTypes.ANY,
+        SqlFunctionCategory.SYSTEM,
+        false,
+        false
+    );
+    this.name = name;
+    this.nullClause = nullClause;
+  }
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
+    assert call.operandCount() == 1;
+    final SqlWriter.Frame frame = writer.startFunCall("JSON_ARRAYAGG");
+    call.operand(0).unparse(writer, leftPrec, rightPrec);
+    switch (nullClause) {
+    case ABSENT_ON_NULL:
+      writer.keyword("ABSENT ON NULL");
+      break;
+    case NULL_ON_NULL:
+      writer.keyword("NULL ON NULL");
+      break;
+    default:
+      throw new IllegalStateException("unreachable code");
+    }
+    writer.endFunCall(frame);
+  }
+
+  private <E extends Enum<E>> E getEnumValue(SqlNode operand) {
+    return (E) ((SqlLiteral) operand).getValue();
+  }
+}
+
+// End SqlJsonArrayAggAggFunction.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
new file mode 100644
index 0000000..b702a98
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
@@ -0,0 +1,109 @@
+/*
+ * 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.calcite.sql.fun;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlJsonConstructorNullClause;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlLiteral;
+import org.apache.calcite.sql.SqlNode;
+import org.apache.calcite.sql.SqlOperandCountRange;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlOperandCountRanges;
+import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.validate.SqlValidator;
+
+import java.util.Locale;
+
+/**
+ * The <code>JSON_ARRAY</code> function.
+ */
+public class SqlJsonArrayFunction extends SqlFunction {
+  public SqlJsonArrayFunction() {
+    super(
+        "JSON_ARRAY",
+        SqlKind.OTHER_FUNCTION,
+        ReturnTypes.VARCHAR_2000,
+        null,
+        OperandTypes.VARIADIC,
+        SqlFunctionCategory.SYSTEM
+    );
+  }
+
+  @Override public SqlOperandCountRange getOperandCountRange() {
+    return SqlOperandCountRanges.from(1);
+  }
+
+  @Override protected void checkOperandCount(SqlValidator validator, SqlOperandTypeChecker argType,
+                                             SqlCall call) {
+    assert call.operandCount() >= 1;
+  }
+
+  @Override public SqlCall createCall(SqlLiteral functionQualifier, SqlParserPos pos,
+                                      SqlNode... operands) {
+    if (operands[0] == null) {
+      operands[0] = SqlLiteral.createSymbol(SqlJsonConstructorNullClause.ABSENT_ON_NULL, pos);
+    }
+    return super.createCall(functionQualifier, pos, operands);
+  }
+
+  @Override public String getSignatureTemplate(int operandsCount) {
+    assert operandsCount >= 1;
+    StringBuilder sb = new StringBuilder();
+    sb.append("{0}(");
+    for (int i = 1; i < operandsCount; i++) {
+      sb.append(String.format(Locale.ENGLISH, "{%d} ", i + 1));
+    }
+    sb.append("{1})");
+    return sb.toString();
+  }
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
+    assert call.operandCount() >= 1;
+    final SqlWriter.Frame frame = writer.startFunCall(getName());
+    SqlWriter.Frame listFrame = writer.startList("", "");
+    for (int i = 1; i < call.operandCount(); i++) {
+      writer.sep(",");
+      call.operand(i).unparse(writer, leftPrec, rightPrec);
+    }
+    writer.endList(listFrame);
+
+    SqlJsonConstructorNullClause nullClause = getEnumValue(call.operand(0));
+    switch (nullClause) {
+    case ABSENT_ON_NULL:
+      writer.keyword("ABSENT ON NULL");
+      break;
+    case NULL_ON_NULL:
+      writer.keyword("NULL ON NULL");
+      break;
+    default:
+      throw new IllegalStateException("unreachable code");
+    }
+    writer.endFunCall(frame);
+  }
+
+  private <E extends Enum<E>> E getEnumValue(SqlNode operand) {
+    return (E) ((SqlLiteral) operand).getValue();
+  }
+}
+
+// End SqlJsonArrayFunction.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/8e557d26/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonExistsFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonExistsFunction.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonExistsFunction.java
new file mode 100644
index 0000000..4c10c01
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonExistsFunction.java
@@ -0,0 +1,61 @@
+/*
+ * 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.calcite.sql.fun;
+
+import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlFunction;
+import org.apache.calcite.sql.SqlFunctionCategory;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+
+/**
+ * The <code>JSON_EXISTS</code> function.
+ */
+public class SqlJsonExistsFunction extends SqlFunction {
+  public SqlJsonExistsFunction() {
+    super(
+        "JSON_EXISTS",
+        SqlKind.OTHER_FUNCTION,
+        ReturnTypes.BOOLEAN_FORCE_NULLABLE,
+        null,
+        OperandTypes.or(OperandTypes.ANY, OperandTypes.ANY_ANY),
+        SqlFunctionCategory.SYSTEM
+    );
+  }
+
+  @Override public String getSignatureTemplate(int operandsCount) {
+    assert operandsCount == 1 || operandsCount == 2;
+    if (operandsCount == 1) {
+      return "{0}({1})";
+    }
+    return "{0}({1} {2} ON ERROR)";
+  }
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, int rightPrec) {
+    final SqlWriter.Frame frame = writer.startFunCall(getName());
+    call.operand(0).unparse(writer, 0, 0);
+    if (call.operandCount() == 2) {
+      call.operand(1).unparse(writer, 0, 0);
+      writer.keyword("ON ERROR");
+    }
+    writer.endFunCall(frame);
+  }
+}
+
+// End SqlJsonExistsFunction.java