use of org.apache.calcite.sql.SqlJsonEmptyOrError in project hazelcast by hazelcast.
the class HazelcastSqlToRelConverter method convertJsonValueCall.
/**
* Converts JSON_VALUE calls with extended syntax, with RETURNING clause among other things.
* Because there is no RexNode for type reference in Calcite (see CAST implementation),
* the type has to be instead set as the type of the parent (JSON_VALUE's RexCall), which is
* then interpreted as the desired type of the expression.
* <p>
* Supported syntax:
* JSON_VALUE(jsonArg, jsonPathArg [returning] [onEmpty|onError])
* returning: RETURNING dataType
* onEmpty: (DEFAULT value | NULL | ERROR) ON EMPTY
* onError: (DEFAULT value | NULL | ERROR) ON ERROR
*/
private RexNode convertJsonValueCall(SqlCall call, Blackboard bb) {
RexNode target = bb.convertExpression(call.operand(0));
RexNode path = bb.convertExpression(call.operand(1));
SqlJsonValueEmptyOrErrorBehavior onError = SqlJsonValueEmptyOrErrorBehavior.NULL;
SqlJsonValueEmptyOrErrorBehavior onEmpty = SqlJsonValueEmptyOrErrorBehavior.NULL;
RelDataType returning = validator.getTypeFactory().createSqlType(SqlTypeName.VARCHAR);
RexNode defaultValueOnError = getRexBuilder().makeNullLiteral(typeFactory.createSqlType(SqlTypeName.ANY));
RexNode defaultValueOnEmpty = getRexBuilder().makeNullLiteral(typeFactory.createSqlType(SqlTypeName.ANY));
// Start at 3rd Arg
int tokenIndex = 2;
// RETURNING can only be placed at the beginning, never in the middle or the end of the list of tokens.
if (call.operandCount() > 2 && isJsonValueReturningClause(call.operand(tokenIndex))) {
returning = validator.getValidatedNodeType(call.operand(tokenIndex + 1));
tokenIndex += 2;
}
boolean onEmptyDefined = false;
boolean onErrorDefined = false;
while (tokenIndex < call.operandCount()) {
if (!(call.operand(tokenIndex) instanceof SqlLiteral)) {
throw QueryException.error(SqlErrorCode.PARSING, "Unsupported JSON_VALUE extended syntax");
}
final SqlJsonValueEmptyOrErrorBehavior behavior = (SqlJsonValueEmptyOrErrorBehavior) ((SqlLiteral) call.operand(tokenIndex)).getValue();
RexNode defaultExpr = getRexBuilder().makeNullLiteral(typeFactory.createSqlType(SqlTypeName.ANY));
if (behavior == null) {
throw QueryException.error(SqlErrorCode.PARSING, "Failed to extract ON behavior for JSON_VALUE call");
}
switch(behavior) {
case DEFAULT:
defaultExpr = bb.convertExpression(call.operand(tokenIndex + 1));
tokenIndex += 2;
break;
case NULL:
case ERROR:
tokenIndex++;
break;
default:
// guard against possible unsupported updates to syntax, should never be thrown.
throw QueryException.error(SqlErrorCode.PARSING, "Unsupported JSON_VALUE OnEmptyOrErrorBehavior");
}
final SqlJsonEmptyOrError onTarget = (SqlJsonEmptyOrError) ((SqlLiteral) call.operand(tokenIndex)).getValue();
if (onTarget == null) {
throw QueryException.error(SqlErrorCode.PARSING, "Failed to extract ON-behavior target for JSON_VALUE call");
}
switch(onTarget) {
case EMPTY:
if (onEmptyDefined) {
throw QueryException.error(SqlErrorCode.PARSING, "Duplicate ON EMPTY clause in JSON_VALUE call");
}
if (behavior == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) {
defaultValueOnEmpty = defaultExpr;
}
onEmpty = behavior;
onEmptyDefined = true;
break;
case ERROR:
if (onErrorDefined) {
throw QueryException.error(SqlErrorCode.PARSING, "Duplicate ON ERROR clause in JSON_VALUE call");
}
if (behavior == SqlJsonValueEmptyOrErrorBehavior.DEFAULT) {
defaultValueOnError = defaultExpr;
}
onError = behavior;
onErrorDefined = true;
break;
default:
// guard against possible unsupported updates to syntax, should never be thrown.
throw QueryException.error(SqlErrorCode.PARSING, "Unsupported JSON_VALUE EmptyOrErrorBehavior target");
}
tokenIndex++;
}
return getRexBuilder().makeCall(returning, HazelcastJsonValueFunction.INSTANCE, asList(target, path, defaultValueOnEmpty, defaultValueOnError, bb.convertLiteral(onEmpty.symbol(SqlParserPos.ZERO)), bb.convertLiteral(onError.symbol(SqlParserPos.ZERO))));
}
Aggregations