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