Skip to content

[FLINK-37695][table] Fix parsing for built-in function JSON in JSON_OBJECT for all positions #26474

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 30, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ import org.apache.flink.table.planner.calcite.{FlinkTypeFactory, RexDistinctKeyV
import org.apache.flink.table.planner.codegen.CodeGenUtils._
import org.apache.flink.table.planner.codegen.GeneratedExpression.{NEVER_NULL, NO_CODE}
import org.apache.flink.table.planner.codegen.GenerateUtils._
import org.apache.flink.table.planner.codegen.JsonGenerateUtils.{isJsonArrayOperand, isJsonFunctionOperand, isJsonObjectOperand}
import org.apache.flink.table.planner.codegen.JsonGenerateUtils.{isJsonFunctionOperand, isSupportedJsonOperand}
import org.apache.flink.table.planner.codegen.calls._
import org.apache.flink.table.planner.codegen.calls.ScalarOperatorGens._
import org.apache.flink.table.planner.codegen.calls.SearchOperatorGen.generateSearch
@@ -464,8 +464,8 @@ class ExprCodeGenerator(ctx: CodeGeneratorContext, nullableInput: Boolean)
// throw exception if json function is called outside JSON_OBJECT or JSON_ARRAY function
if (isJsonFunctionOperand(call)) {
throw new ValidationException(
"The JSON() function is currently only supported inside a JSON_OBJECT() or JSON_ARRAY()" +
" function. Example: JSON_OBJECT('a', JSON('{\"key\": \"value\"}')) or " +
"The JSON() function is currently only supported inside JSON_ARRAY() or as the VALUE param" +
" of JSON_OBJECT(). Example: JSON_OBJECT('a', JSON('{\"key\": \"value\"}')) or " +
"JSON_ARRAY(JSON('{\"key\": \"value\"}')).")
}

@@ -486,10 +486,8 @@ class ExprCodeGenerator(ctx: CodeGeneratorContext, nullableInput: Boolean)
call.getOperator.getReturnTypeInference == ReturnTypes.ARG0 =>
generateNullLiteral(resultType)

// We only support JSON function operands as the value param of a JSON_OBJECT or JSON_ARRAY function
case (operand: RexNode, i)
if isJsonFunctionOperand(operand) &&
(isJsonArrayOperand(call) || i == 2 && isJsonObjectOperand(call)) =>
// We only support the JSON function inside of JSON_OBJECT or JSON_ARRAY
case (operand: RexNode, i) if isSupportedJsonOperand(operand, call, i) =>
generateJsonCall(operand)

case (o @ _, _) => o.accept(this)
Original file line number Diff line number Diff line change
@@ -227,6 +227,17 @@ object JsonGenerateUtils {
}
}

/**
* Determines whether a JSON function is allowed in the current context. JSON functions are
* allowed as values in JSON_ARRAY calls or as value parameters in JSON_OBJECT calls. In the case
* of a JSON_OBJECT call, we do (i % 2) == 0 to check if it's being used in second parameter, the
* values' parameter.
*/
def isSupportedJsonOperand(operand: RexNode, call: RexNode, i: Int): Boolean = {
isJsonFunctionOperand(operand) &&
(isJsonArrayOperand(call) || isJsonObjectOperand(call) && (i % 2) == 0)
}

/** Generates a method to convert arrays into [[ArrayNode]]. */
private def generateArrayConverter(
ctx: CodeGeneratorContext,
Original file line number Diff line number Diff line change
@@ -782,11 +782,54 @@ private static List<TestSetSpec> jsonSpec() {
.testTableApiRuntimeError(
jsonObject(JsonOnNull.NULL, "K", json("{")),
TableRuntimeException.class,
"Invalid JSON string in JSON(value) function"),
// Tests for JSON calls inside of JSON_ARRAY
TestSetSpec.forFunction(BuiltInFunctionDefinitions.JSON_ARRAY)
.onFieldsWithData("{\"key\":\"value\"}", "{\"key\": {\"value\": 42}}")
.andDataTypes(STRING(), STRING())
"Invalid JSON string in JSON(value) function")

// Tests for JSON_OBJECT with multiple parameters
.testResult(
jsonObject(JsonOnNull.NULL, "key1", "val", "key2", json($("f1"))),
"JSON_OBJECT(KEY 'key1' VALUE 'val', KEY 'key2' VALUE JSON(f1))",
"{\"key1\":\"val\",\"key2\":{\"key\":{\"value\":42}}}",
STRING().notNull())
.testResult(
jsonObject(
JsonOnNull.NULL,
"key1",
json($("f0")),
"key2",
json($("f1"))),
"JSON_OBJECT(KEY 'key1' VALUE JSON(f0), KEY 'key2' VALUE JSON(f1))",
"{\"key1\":{\"key\":\"value\"},\"key2\":{\"key\":{\"value\":42}}}",
STRING().notNull())
.testResult(
jsonObject(
JsonOnNull.NULL,
"outerKey",
"outerValue",
"nestedObject",
jsonObject(JsonOnNull.NULL, "innerKey", json($("f0")))),
"JSON_OBJECT(KEY 'outerKey' VALUE 'outerValue', KEY 'nestedObject' VALUE JSON_OBJECT(KEY 'innerKey' VALUE JSON(f0)))",
"{\"nestedObject\":{\"innerKey\":{\"key\":\"value\"}},\"outerKey\":\"outerValue\"}",
STRING().notNull())
.testResult(
jsonObject(
JsonOnNull.NULL,
"p1",
json($("f0")),
"p2",
json($("f1")),
"p3",
json("[1, 2, 3]")),
"JSON_OBJECT(KEY 'p1' VALUE JSON(f0), KEY 'p2' VALUE JSON(f1), KEY 'p3' VALUE JSON('[1, 2, 3]'))",
"{\"p1\":{\"key\":\"value\"},\"p2\":{\"key\":{\"value\":42}},\"p3\":[1,2,3]}",
STRING().notNull())
.testSqlValidationError(
"JSON_OBJECT(KEY JSON('{}') VALUE 'value' ABSENT ON NULL)",
"The JSON() function is currently only supported inside JSON_ARRAY() or as the VALUE param of JSON_OBJECT()")
.testTableApiValidationError(
jsonObject(JsonOnNull.NULL, json($("f0")), "value"),
"Invalid function call:\n"
+ "JSON_OBJECT(SYMBOL NOT NULL, STRING, CHAR(5) NOT NULL)")
// Tests for JSON calls inside of JSON_ARRAY
.testResult(
jsonArray(JsonOnNull.NULL, json("{}")),
"JSON_ARRAY(JSON('{}'))",
@@ -865,10 +908,10 @@ JsonOnNull.NULL, jsonArray(JsonOnNull.NULL, json($("f1")))),
"line: 1, column: 1")
.testTableApiValidationError(
json($("f0")),
"The JSON() function is currently only supported inside a JSON_OBJECT() or JSON_ARRAY() function.")
"The JSON() function is currently only supported inside JSON_ARRAY() or as the VALUE param of JSON_OBJECT()")
.testSqlValidationError(
"JSON(f0)",
"The JSON() function is currently only supported inside a JSON_OBJECT() or JSON_ARRAY() function."));
"The JSON() function is currently only supported inside JSON_ARRAY() or as the VALUE param of JSON_OBJECT()"));
}

private static List<TestSetSpec> jsonObjectSpec() {