33#include " jsonata_extension.hpp"
44#include " duckdb.hpp"
55#include " duckdb/common/exception.hpp"
6+ #include " duckdb/common/vector_operations/ternary_executor.hpp"
67#include " duckdb/function/scalar_function.hpp"
78#include < duckdb/parser/parsed_data/create_scalar_function_info.hpp>
89
1213
1314namespace duckdb {
1415
16+ static std::unique_ptr<jsonata::Jsonata> ParseJsonataExpression (const string &expr_str) {
17+ try {
18+ return make_uniq<jsonata::Jsonata>(expr_str);
19+ } catch (const std::exception &e) {
20+ throw InvalidInputException (" Invalid JSONata expression: %s" , e.what ());
21+ }
22+ }
23+
24+ static nlohmann::json ParseJsonData (string_t data) {
25+ try {
26+ return nlohmann::json::parse (data.GetData (), data.GetData () + data.GetSize ());
27+ } catch (const nlohmann::json::parse_error &e) {
28+ throw InvalidInputException (" Invalid JSON data: %s" , e.what ());
29+ }
30+ }
31+
32+ static void BindJsonToFrame (std::shared_ptr<jsonata::Frame> frame, const nlohmann::json &bindings) {
33+ if (!bindings.is_object ()) {
34+ throw InvalidInputException (" Bindings must be a JSON object" );
35+ }
36+ for (auto &[key, value] : bindings.items ()) {
37+ frame->bind (key, jsonata::Jsonata::jsonToAny (value));
38+ }
39+ }
40+
41+ static string_t EvaluateJsonata (jsonata::Jsonata &expr, const nlohmann::json &data, Vector &result) {
42+ nlohmann::json output;
43+ try {
44+ output = expr.evaluate (data);
45+ } catch (const std::exception &e) {
46+ throw InvalidInputException (" JSONata evaluation error: %s" , e.what ());
47+ }
48+ return StringVector::AddString (result, output.dump ());
49+ }
50+
51+ static string_t EvaluateJsonataWithBindings (jsonata::Jsonata &expr, const nlohmann::json &data,
52+ const nlohmann::json &bindings, Vector &result) {
53+ nlohmann::json output;
54+ try {
55+ auto frame = expr.createFrame ();
56+ BindJsonToFrame (frame, bindings);
57+ output = expr.evaluate (data, frame);
58+ } catch (const std::exception &e) {
59+ throw InvalidInputException (" JSONata evaluation error: %s" , e.what ());
60+ }
61+ return StringVector::AddString (result, output.dump ());
62+ }
63+
1564inline void JsonataScalarFun (DataChunk &args, ExpressionState &state, Vector &result) {
1665 auto &jsonata_vector = args.data [0 ];
1766 auto &data_vector = args.data [1 ];
1867
1968 if (jsonata_vector.GetVectorType () == VectorType::CONSTANT_VECTOR) {
2069 if (ConstantVector::IsNull (jsonata_vector)) {
21- // If the JSONata expression is NULL, the result is NULL
2270 result.SetVectorType (VectorType::CONSTANT_VECTOR);
2371 ConstantVector::SetNull (result, true );
2472 return ;
2573 }
2674 auto jsonata_str = ConstantVector::GetData<string_t >(jsonata_vector)[0 ];
27-
28- // Parse the JSONata expression once for all rows (constant optimization)
29- std::unique_ptr<jsonata::Jsonata> jsonata_expr;
30- try {
31- jsonata_expr = make_uniq<jsonata::Jsonata>(jsonata_str.GetString ());
32- } catch (const std::exception &e) {
33- throw InvalidInputException (" Invalid JSONata expression: %s" , e.what ());
34- }
75+ auto jsonata_expr = ParseJsonataExpression (jsonata_str.GetString ());
3576
3677 UnaryExecutor::Execute<string_t , string_t >(data_vector, result, args.size (), [&](string_t data) {
37- nlohmann::json parsed;
38- try {
39- parsed = nlohmann::json::parse (string (data.GetData (), data.GetSize ()));
40- } catch (const nlohmann::json::parse_error &e) {
41- throw InvalidInputException (" Invalid JSON data: %s" , e.what ());
42- }
43-
44- nlohmann::json output;
45- try {
46- output = jsonata_expr->evaluate (parsed);
47- } catch (const std::exception &e) {
48- throw InvalidInputException (" JSONata evaluation error: %s" , e.what ());
49- }
50-
51- return StringVector::AddString (result, output.dump ());
78+ auto parsed = ParseJsonData (data);
79+ return EvaluateJsonata (*jsonata_expr, parsed, result);
5280 });
5381 } else {
5482 BinaryExecutor::Execute<string_t , string_t , string_t >(
5583 jsonata_vector, data_vector, result, args.size (), [&](string_t jsonata_str, string_t data) {
56- nlohmann::json parsed;
57- try {
58- parsed = nlohmann::json::parse (string (data.GetData (), data.GetSize ()));
59- } catch (const nlohmann::json::parse_error &e) {
60- throw InvalidInputException (" Invalid JSON data: %s" , e.what ());
61- }
62-
63- std::unique_ptr<jsonata::Jsonata> jsonata_expr;
64- try {
65- jsonata_expr = make_uniq<jsonata::Jsonata>(string (jsonata_str.GetData (), jsonata_str.GetSize ()));
66- } catch (const std::exception &e) {
67- throw InvalidInputException (" Invalid JSONata expression: %s" , e.what ());
68- }
69-
70- nlohmann::json output;
71- try {
72- output = jsonata_expr->evaluate (parsed);
73- } catch (const std::exception &e) {
74- throw InvalidInputException (" JSONata evaluation error: %s" , e.what ());
75- }
76-
77- return StringVector::AddString (result, output.dump ());
84+ auto jsonata_expr = ParseJsonataExpression (string (jsonata_str.GetData (), jsonata_str.GetSize ()));
85+ auto parsed = ParseJsonData (data);
86+ return EvaluateJsonata (*jsonata_expr, parsed, result);
87+ });
88+ }
89+ }
90+
91+ inline void JsonataScalarFunWithBindings (DataChunk &args, ExpressionState &state, Vector &result) {
92+ auto &jsonata_vector = args.data [0 ];
93+ auto &data_vector = args.data [1 ];
94+ auto &bindings_vector = args.data [2 ];
95+
96+ if (jsonata_vector.GetVectorType () == VectorType::CONSTANT_VECTOR) {
97+ if (ConstantVector::IsNull (jsonata_vector)) {
98+ result.SetVectorType (VectorType::CONSTANT_VECTOR);
99+ ConstantVector::SetNull (result, true );
100+ return ;
101+ }
102+ auto jsonata_str = ConstantVector::GetData<string_t >(jsonata_vector)[0 ];
103+ auto jsonata_expr = ParseJsonataExpression (jsonata_str.GetString ());
104+
105+ BinaryExecutor::Execute<string_t , string_t , string_t >(
106+ data_vector, bindings_vector, result, args.size (), [&](string_t data, string_t bindings) {
107+ auto parsed = ParseJsonData (data);
108+ auto parsed_bindings = ParseJsonData (bindings);
109+ return EvaluateJsonataWithBindings (*jsonata_expr, parsed, parsed_bindings, result);
110+ });
111+ } else {
112+ TernaryExecutor::Execute<string_t , string_t , string_t , string_t >(
113+ jsonata_vector, data_vector, bindings_vector, result, args.size (),
114+ [&](string_t jsonata_str, string_t data, string_t bindings) {
115+ auto jsonata_expr = ParseJsonataExpression (string (jsonata_str.GetData (), jsonata_str.GetSize ()));
116+ auto parsed = ParseJsonData (data);
117+ auto parsed_bindings = ParseJsonData (bindings);
118+ return EvaluateJsonataWithBindings (*jsonata_expr, parsed, parsed_bindings, result);
78119 });
79120 }
80121}
81122
82123static void LoadInternal (ExtensionLoader &loader) {
83124 ScalarFunctionSet jsonata_function_set (" jsonata" );
125+
126+ // 2-argument version: jsonata(expression, json_data)
84127 auto jsonata_scalar_function =
85128 ScalarFunction ({LogicalType::VARCHAR, LogicalType::JSON ()}, LogicalType::JSON (), JsonataScalarFun);
86129 jsonata_function_set.AddFunction (jsonata_scalar_function);
87130
131+ // 3-argument version: jsonata(expression, json_data, bindings)
132+ auto jsonata_with_bindings = ScalarFunction ({LogicalType::VARCHAR, LogicalType::JSON (), LogicalType::JSON ()},
133+ LogicalType::JSON (), JsonataScalarFunWithBindings);
134+ jsonata_function_set.AddFunction (jsonata_with_bindings);
135+
88136 CreateScalarFunctionInfo info (jsonata_function_set);
89137 info.descriptions .push_back ({
90138 // parameter_types
@@ -96,15 +144,29 @@ static void LoadInternal(ExtensionLoader &loader) {
96144 " language for JSON data. See https://jsonata.org for the full language reference." ,
97145 // examples
98146 {" jsonata('Account.Name', '{\" Account\" : {\" Name\" : \" Firefly\" }}')" ,
99- " jsonata('$.prices[price > 100]', my_json_column)" ,
100- " jsonata('$sum(Order.Product.Price)', orders)" },
147+ " jsonata('$.prices[price > 100]', my_json_column)" , " jsonata('$sum(Order.Product.Price)', orders)" },
148+ // categories
149+ {" json" },
150+ });
151+ info.descriptions .push_back ({
152+ // parameter_types
153+ {LogicalType::VARCHAR, LogicalType::JSON (), LogicalType::JSON ()},
154+ // parameter_names
155+ {" expression" , " json_data" , " bindings" },
156+ // description
157+ " Evaluates a JSONata expression against JSON data with external variable bindings. "
158+ " The bindings parameter is a JSON object where keys become variable names accessible in the expression "
159+ " using $variable_name syntax." ,
160+ // examples
161+ {" jsonata('$name', '{}', '{\" name\" : \" Alice\" }')" , " jsonata('$x + $y', '{}', '{\" x\" : 10, \" y\" : 20}')" ,
162+ " jsonata('items[price > $threshold]', my_json, '{\" threshold\" : 100}')" },
101163 // categories
102164 {" json" },
103165 });
104166
105167 loader.RegisterFunction (info);
106168
107- QueryFarmSendTelemetry (loader, " jsonata" , " 2025110901 " );
169+ QueryFarmSendTelemetry (loader, " jsonata" , " 2025121201 " );
108170}
109171
110172void JsonataExtension::Load (ExtensionLoader &loader) {
@@ -115,7 +177,7 @@ std::string JsonataExtension::Name() {
115177}
116178
117179std::string JsonataExtension::Version () const {
118- return " 2025110901 " ;
180+ return " 2025121201 " ;
119181}
120182
121183} // namespace duckdb
0 commit comments