twalthr commented on code in PR #26022: URL: https://github.com/apache/flink/pull/26022#discussion_r1922292809
########## docs/data/sql_functions.yml: ########## @@ -1069,6 +1072,30 @@ json: ) ) ``` + - sql: JSON(value) + table: json(value) + description: | + Expects a JSON string and returns its values as-is without escaping it as a string. Review Comment: ```suggestion Expects a raw, pre-formatted JSON string and returns its values as-is without escaping it as a string. ``` ########## flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/Expressions.java: ########## @@ -861,9 +862,13 @@ public static ApiExpression withoutColumns(Object head, Object... tail) { * jsonObject(JsonOnNull.ABSENT, "K1", nullOf(DataTypes.STRING())) // "{}" * * // {"K1":{"K2":"V"}} + * jsonObject(JsonOnNull.NULL, "K1", json('{"K2":"V"}')) Review Comment: ```suggestion * jsonObject(JsonOnNull.NULL, "K1", json("{'K2':'V'}")) ``` ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/JsonGenerateUtils.scala: ########## @@ -275,4 +306,5 @@ object JsonGenerateUtils { ctx.addReusableMember(methodCode) methodName } + Review Comment: nit: remove ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/CodeGenUtils.scala: ########## @@ -1098,4 +1098,16 @@ object CodeGenUtils { GenerateUtils.generateFieldAccess(ctx, inputType, inputTerm, index) } } + + def removeFunctionCall(code: String, functionPath: String, functionName: String): String = { Review Comment: This looks really hacky. Could we investigate a cleaner solution? ########## flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/JsonGenerateUtils.scala: ########## @@ -160,6 +165,17 @@ object JsonGenerateUtils { |""".stripMargin } + /** Returns a term which wraps the given input parameter as a raw value. */ + private def createRawSingleStringInputTerm(valueExpr: GeneratedExpression): String = { + s""" + | + |$jsonUtils.getNodeFactory().rawValueNode( + | new ${typeTerm(classOf[RawValue])}( + | ${valueExpr.resultTerm} != null && ${valueExpr.resultTerm}.toString().equals("") ? Review Comment: nullability checking should be done with the `nullTerm` term ########## flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/Expressions.java: ########## @@ -873,6 +878,35 @@ public static ApiExpression jsonObject(JsonOnNull onNull, Object... keyValues) { return apiCall(JSON_OBJECT, arguments); } + /** + * Expects a JSON string and returns its values as-is without escaping it as a string. + * + * <p>This function can currently only be used within the {@code JSON_OBJECT} function. It Review Comment: ```suggestion * <p>This function can currently only be used within the {@link #jsonObject(...)} function. It ``` ########## flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/Expressions.java: ########## @@ -861,9 +862,13 @@ public static ApiExpression withoutColumns(Object head, Object... tail) { * jsonObject(JsonOnNull.ABSENT, "K1", nullOf(DataTypes.STRING())) // "{}" * * // {"K1":{"K2":"V"}} + * jsonObject(JsonOnNull.NULL, "K1", json('{"K2":"V"}')) Review Comment: or escaped? not sure how strict the user-defined JSON needs to be? maybe we should check and document this. ########## flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/LogicalTypeRoot.java: ########## @@ -139,6 +139,8 @@ public enum LogicalTypeRoot { SYMBOL(LogicalTypeFamily.EXTENSION), + JSON(LogicalTypeFamily.CHARACTER_STRING), Review Comment: This class can't and shouldn't be changed without a FLIP. Please revert this. ########## flink-table/flink-table-api-java/src/main/java/org/apache/flink/table/api/Expressions.java: ########## @@ -873,6 +878,35 @@ public static ApiExpression jsonObject(JsonOnNull onNull, Object... keyValues) { return apiCall(JSON_OBJECT, arguments); } + /** + * Expects a JSON string and returns its values as-is without escaping it as a string. + * + * <p>This function can currently only be used within the {@code JSON_OBJECT} function. It + * allows passing pre-formatted JSON strings that will be inserted directly into the resulting + * JSON structure rather than being escaped as a string value. This allows storing nested JSON + * structures in a JSON_OBJECT without processing them as strings. If the value is null or + * empty, the function returns {@code null}. + * + * <p>Examples: + * + * <pre>{@code + * // {"K":{"K2":42}} + * jsonObject(JsonOnNull.NULL, "K", json('{"K2": 42}')) + * + * // {"K":{"K2":{"K3":42}}} + * jsonObject(JsonOnNull.NULL, "K", json('{"K2":{"K3":42}}')) + * + * // {"K": null} + * jsonObject(JsonOnNull.NULL, "K", json('')) + * + * // Invalid - JSON function can only be used within JSON_OBJECT + * json("{\"value\": 42}") + * }</pre> + */ + public static ApiExpression json(Object value) { + return apiCallAtLeastOneArgument(JSON, value); Review Comment: ```suggestion return apiCall(JSON, value); ``` -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@flink.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org