Search in sources :

Example 1 with SqlJsonEmptyOrError

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))));
}
Also used : SqlJsonEmptyOrError(org.apache.calcite.sql.SqlJsonEmptyOrError) RelDataType(org.apache.calcite.rel.type.RelDataType) SqlJsonValueEmptyOrErrorBehavior(org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior) SqlLiteral(org.apache.calcite.sql.SqlLiteral) RexNode(org.apache.calcite.rex.RexNode)

Aggregations

RelDataType (org.apache.calcite.rel.type.RelDataType)1 RexNode (org.apache.calcite.rex.RexNode)1 SqlJsonEmptyOrError (org.apache.calcite.sql.SqlJsonEmptyOrError)1 SqlJsonValueEmptyOrErrorBehavior (org.apache.calcite.sql.SqlJsonValueEmptyOrErrorBehavior)1 SqlLiteral (org.apache.calcite.sql.SqlLiteral)1