/src/duckdb/extension/json/json_functions.cpp
Line | Count | Source |
1 | | #include "duckdb/common/vector/flat_vector.hpp" |
2 | | #include "duckdb/common/vector/list_vector.hpp" |
3 | | #include "duckdb/common/vector/string_vector.hpp" |
4 | | #include "json_functions.hpp" |
5 | | |
6 | | #include "duckdb/common/file_system.hpp" |
7 | | #include "duckdb/execution/expression_executor.hpp" |
8 | | #include "duckdb/function/cast/cast_function_set.hpp" |
9 | | #include "duckdb/function/cast/default_casts.hpp" |
10 | | #include "duckdb/function/replacement_scan.hpp" |
11 | | #include "duckdb/parser/expression/constant_expression.hpp" |
12 | | #include "duckdb/parser/expression/function_expression.hpp" |
13 | | #include "duckdb/parser/parsed_data/create_pragma_function_info.hpp" |
14 | | #include "duckdb/parser/tableref/table_function_ref.hpp" |
15 | | |
16 | | namespace duckdb { |
17 | | |
18 | | using JSONPathType = JSONCommon::JSONPathType; |
19 | | |
20 | 831 | JSONPathType JSONReadFunctionData::CheckPath(const Value &path_val, string &path, idx_t &len) { |
21 | 831 | if (path_val.IsNull()) { |
22 | 0 | throw BinderException("JSON path cannot be NULL"); |
23 | 0 | } |
24 | 831 | const auto path_str_val = path_val.DefaultCastAs(LogicalType::VARCHAR); |
25 | 831 | auto path_str = path_str_val.GetValueUnsafe<string_t>(); |
26 | 831 | len = path_str.GetSize(); |
27 | 831 | const auto ptr = path_str.GetData(); |
28 | 831 | JSONPathType path_type = JSONPathType::REGULAR; |
29 | | // Copy over string to the bind data |
30 | 831 | if (len != 0) { |
31 | 831 | if (*ptr == '/' || *ptr == '$') { |
32 | 831 | path = string(ptr, len); |
33 | 831 | } else if (path_val.type().IsIntegral()) { |
34 | 0 | path = "$[" + string(ptr, len) + "]"; |
35 | 0 | } else if (memchr(ptr, '"', len)) { |
36 | 0 | path = "/" + string(ptr, len); |
37 | 0 | } else { |
38 | 0 | path = "$.\"" + string(ptr, len) + "\""; |
39 | 0 | } |
40 | 831 | len = path.length(); |
41 | 831 | if (*path.c_str() == '$') { |
42 | 831 | path_type = JSONCommon::ValidatePath(path.c_str(), len, true); |
43 | 831 | } |
44 | 831 | } |
45 | 831 | return path_type; |
46 | 831 | } |
47 | | |
48 | | JSONReadFunctionData::JSONReadFunctionData(bool constant, string path_p, idx_t len, JSONPathType path_type_p) |
49 | 1.43k | : constant(constant), path(std::move(path_p)), path_type(path_type_p), ptr(path.c_str()), len(len) { |
50 | 1.43k | } |
51 | | |
52 | 605 | unique_ptr<FunctionData> JSONReadFunctionData::Copy() const { |
53 | 605 | return make_uniq<JSONReadFunctionData>(constant, path, len, path_type); |
54 | 605 | } |
55 | | |
56 | 0 | bool JSONReadFunctionData::Equals(const FunctionData &other_p) const { |
57 | 0 | auto &other = other_p.Cast<JSONReadFunctionData>(); |
58 | 0 | return constant == other.constant && path == other.path && len == other.len && path_type == other.path_type; |
59 | 0 | } |
60 | | |
61 | 831 | unique_ptr<FunctionData> JSONReadFunctionData::Bind(BindScalarFunctionInput &input) { |
62 | 831 | auto &context = input.GetClientContext(); |
63 | 831 | auto &bound_function = input.GetBoundFunction(); |
64 | 831 | auto &arguments = input.GetArguments(); |
65 | 831 | D_ASSERT(bound_function.GetArguments().size() == 2); |
66 | 831 | bool constant = false; |
67 | 831 | string path; |
68 | 831 | idx_t len = 0; |
69 | 831 | JSONPathType path_type = JSONPathType::REGULAR; |
70 | 831 | if (arguments[1]->IsFoldable()) { |
71 | 831 | const auto path_val = ExpressionExecutor::EvaluateScalar(context, *arguments[1]); |
72 | 831 | if (!path_val.IsNull()) { |
73 | 831 | constant = true; |
74 | 831 | path_type = CheckPath(path_val, path, len); |
75 | 831 | } |
76 | 831 | } |
77 | 831 | if (arguments[1]->GetReturnType().IsIntegral()) { |
78 | 0 | bound_function.GetArguments()[1] = LogicalType::BIGINT; |
79 | 831 | } else { |
80 | 831 | bound_function.GetArguments()[1] = LogicalType::VARCHAR; |
81 | 831 | } |
82 | 831 | if (path_type == JSONCommon::JSONPathType::WILDCARD) { |
83 | 0 | bound_function.SetReturnType(LogicalType::LIST(bound_function.GetReturnType())); |
84 | 0 | } |
85 | 831 | return make_uniq<JSONReadFunctionData>(constant, std::move(path), len, path_type); |
86 | 831 | } |
87 | | |
88 | | JSONReadManyFunctionData::JSONReadManyFunctionData(vector<string> paths_p, vector<idx_t> lens_p) |
89 | 0 | : paths(std::move(paths_p)), lens(std::move(lens_p)) { |
90 | 0 | for (const auto &path : paths) { |
91 | 0 | ptrs.push_back(path.c_str()); |
92 | 0 | } |
93 | 0 | } |
94 | | |
95 | 0 | unique_ptr<FunctionData> JSONReadManyFunctionData::Copy() const { |
96 | 0 | return make_uniq<JSONReadManyFunctionData>(paths, lens); |
97 | 0 | } |
98 | | |
99 | 0 | bool JSONReadManyFunctionData::Equals(const FunctionData &other_p) const { |
100 | 0 | auto &other = other_p.Cast<JSONReadManyFunctionData>(); |
101 | 0 | return paths == other.paths && lens == other.lens; |
102 | 0 | } |
103 | | |
104 | 0 | unique_ptr<FunctionData> JSONReadManyFunctionData::Bind(BindScalarFunctionInput &input) { |
105 | 0 | auto &context = input.GetClientContext(); |
106 | 0 | auto &arguments = input.GetArguments(); |
107 | 0 | D_ASSERT(input.GetBoundFunction().GetArguments().size() == 2); |
108 | 0 | if (arguments[1]->HasParameter()) { |
109 | 0 | throw ParameterNotResolvedException(); |
110 | 0 | } |
111 | 0 | if (!arguments[1]->IsFoldable()) { |
112 | 0 | throw BinderException("List of paths must be constant"); |
113 | 0 | } |
114 | | |
115 | 0 | vector<string> paths; |
116 | 0 | vector<idx_t> lens; |
117 | 0 | auto paths_val = ExpressionExecutor::EvaluateScalar(context, *arguments[1]); |
118 | |
|
119 | 0 | for (auto &path_val : ListValue::GetChildren(paths_val)) { |
120 | 0 | paths.emplace_back(""); |
121 | 0 | lens.push_back(0); |
122 | 0 | if (JSONReadFunctionData::CheckPath(path_val, paths.back(), lens.back()) == JSONPathType::WILDCARD) { |
123 | 0 | throw BinderException("Cannot have wildcards in JSON path when supplying multiple paths"); |
124 | 0 | } |
125 | 0 | } |
126 | | |
127 | 0 | return make_uniq<JSONReadManyFunctionData>(std::move(paths), std::move(lens)); |
128 | 0 | } |
129 | | |
130 | | JSONFunctionLocalState::JSONFunctionLocalState(Allocator &allocator) |
131 | 3.70k | : json_allocator(make_shared_ptr<JSONAllocator>(allocator)) { |
132 | 3.70k | } |
133 | | |
134 | | JSONFunctionLocalState::JSONFunctionLocalState(ClientContext &context) |
135 | 3.70k | : JSONFunctionLocalState(BufferAllocator::Get(context)) { |
136 | 3.70k | } |
137 | | |
138 | | unique_ptr<FunctionLocalState> JSONFunctionLocalState::Init(ExpressionState &state, const BoundFunctionExpression &expr, |
139 | 3.70k | FunctionData *bind_data) { |
140 | 3.70k | return make_uniq<JSONFunctionLocalState>(state.GetContext()); |
141 | 3.70k | } |
142 | | |
143 | 0 | unique_ptr<FunctionLocalState> JSONFunctionLocalState::InitCastLocalState(CastLocalStateParameters ¶meters) { |
144 | 0 | return parameters.context ? make_uniq<JSONFunctionLocalState>(*parameters.context) |
145 | 0 | : make_uniq<JSONFunctionLocalState>(Allocator::DefaultAllocator()); |
146 | 0 | } |
147 | | |
148 | 3.70k | JSONFunctionLocalState &JSONFunctionLocalState::ResetAndGet(ExpressionState &state) { |
149 | 3.70k | auto &lstate = ExecuteFunctionState::GetFunctionState(state)->Cast<JSONFunctionLocalState>(); |
150 | 3.70k | lstate.json_allocator->Reset(); |
151 | 3.70k | return lstate; |
152 | 3.70k | } |
153 | | |
154 | 7.23k | vector<ScalarFunctionSet> JSONFunctions::GetScalarFunctions() { |
155 | 7.23k | vector<ScalarFunctionSet> functions; |
156 | | |
157 | | // Extract functions |
158 | 7.23k | AddAliases({"json_extract", "json_extract_path"}, GetExtractFunction(), functions); |
159 | 7.23k | AddAliases({"json_extract_string", "json_extract_path_text", "->>"}, GetExtractStringFunction(), functions); |
160 | | |
161 | | // Create functions |
162 | 7.23k | functions.push_back(GetArrayFunction()); |
163 | 7.23k | functions.push_back(GetObjectFunction()); |
164 | 7.23k | AddAliases({"to_json", "json_quote"}, GetToJSONFunction(), functions); |
165 | 7.23k | functions.push_back(ScalarFunctionSet(GetJSONCopyToJSONFunction())); |
166 | 7.23k | functions.push_back(GetArrayToJSONFunction()); |
167 | 7.23k | functions.push_back(GetRowToJSONFunction()); |
168 | 7.23k | functions.push_back(GetMergePatchFunction()); |
169 | 7.23k | functions.push_back(GetMergePatchDiffFunction()); |
170 | 7.23k | functions.push_back(GetDeepMergeFunction()); |
171 | | |
172 | | // Structure/Transform |
173 | 7.23k | functions.push_back(GetStructureFunction()); |
174 | 7.23k | AddAliases({"json_transform", "from_json"}, GetTransformFunction(), functions); |
175 | 7.23k | AddAliases({"json_transform_strict", "from_json_strict"}, GetTransformStrictFunction(), functions); |
176 | | |
177 | | // Other |
178 | 7.23k | functions.push_back(GetArrayLengthFunction()); |
179 | 7.23k | functions.push_back(GetContainsFunction()); |
180 | 7.23k | functions.push_back(GetExistsFunction()); |
181 | 7.23k | functions.push_back(GetKeysFunction()); |
182 | 7.23k | functions.push_back(GetTypeFunction()); |
183 | 7.23k | functions.push_back(GetValidFunction()); |
184 | 7.23k | functions.push_back(GetValueFunction()); |
185 | 7.23k | functions.push_back(GetSerializePlanFunction()); |
186 | 7.23k | functions.push_back(GetSerializeSqlFunction()); |
187 | 7.23k | functions.push_back(GetDeserializeSqlFunction()); |
188 | | |
189 | 7.23k | functions.push_back(GetPrettyPrintFunction()); |
190 | 7.23k | functions.push_back(GetNormalizeFunction()); |
191 | 7.23k | functions.push_back(GetStripNullsFunction()); |
192 | | |
193 | 7.23k | return functions; |
194 | 7.23k | } |
195 | | |
196 | 7.23k | vector<PragmaFunctionSet> JSONFunctions::GetPragmaFunctions() { |
197 | 7.23k | vector<PragmaFunctionSet> functions; |
198 | 7.23k | functions.push_back(GetExecuteJsonSerializedSqlPragmaFunction()); |
199 | 7.23k | return functions; |
200 | 7.23k | } |
201 | | |
202 | 7.23k | vector<TableFunctionSet> JSONFunctions::GetTableFunctions() { |
203 | 7.23k | vector<TableFunctionSet> functions; |
204 | | |
205 | | // Reads JSON as string |
206 | 7.23k | functions.push_back(GetReadJSONObjectsFunction()); |
207 | 7.23k | functions.push_back(GetReadNDJSONObjectsFunction()); |
208 | 7.23k | functions.push_back(GetReadJSONObjectsAutoFunction()); |
209 | | |
210 | | // Read JSON as columnar data |
211 | 7.23k | functions.push_back(GetReadJSONFunction()); |
212 | 7.23k | functions.push_back(GetReadNDJSONFunction()); |
213 | 7.23k | functions.push_back(GetReadJSONAutoFunction()); |
214 | 7.23k | functions.push_back(GetReadNDJSONAutoFunction()); |
215 | | |
216 | | // Table in-out |
217 | 7.23k | functions.push_back(GetJSONEachFunction()); |
218 | 7.23k | functions.push_back(GetJSONTreeFunction()); |
219 | | |
220 | | // Serialized plan |
221 | 7.23k | functions.push_back(GetExecuteJsonSerializedSqlFunction()); |
222 | | |
223 | 7.23k | return functions; |
224 | 7.23k | } |
225 | | |
226 | | unique_ptr<TableRef> JSONFunctions::ReadJSONReplacement(ClientContext &context, ReplacementScanInput &input, |
227 | 6 | optional_ptr<ReplacementScanData> data) { |
228 | 6 | auto table_name = ReplacementScan::GetFullPath(input); |
229 | 6 | if (!ReplacementScan::CanReplace(table_name, {"json", "jsonl", "ndjson"})) { |
230 | 6 | return nullptr; |
231 | 6 | } |
232 | 0 | auto table_function = make_uniq<TableFunctionRef>(); |
233 | 0 | vector<unique_ptr<ParsedExpression>> children; |
234 | 0 | children.push_back(make_uniq<ConstantExpression>(Value(table_name))); |
235 | 0 | table_function->function = make_uniq<FunctionExpression>("read_json_auto", std::move(children)); |
236 | |
|
237 | 0 | if (!FileSystem::HasGlob(table_name)) { |
238 | 0 | auto &fs = FileSystem::GetFileSystem(context); |
239 | 0 | table_function->alias = Identifier(fs.ExtractBaseName(table_name)); |
240 | 0 | } |
241 | |
|
242 | 0 | return std::move(table_function); |
243 | 6 | } |
244 | | |
245 | 0 | static bool CastVarcharToJSON(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { |
246 | 0 | auto &lstate = parameters.local_state->Cast<JSONFunctionLocalState>(); |
247 | 0 | lstate.json_allocator->Reset(); |
248 | 0 | auto alc = lstate.json_allocator->GetYYAlc(); |
249 | |
|
250 | 0 | bool success = true; |
251 | 0 | UnaryExecutor::Execute<string_t, string_t>(source, result, count, [&](string_t input) -> optional<string_t> { |
252 | 0 | auto data = input.GetDataWriteable(); |
253 | 0 | const auto length = input.GetSize(); |
254 | |
|
255 | 0 | yyjson_read_err error; |
256 | 0 | auto doc = JSONCommon::ReadDocumentUnsafe(data, length, JSONCommon::READ_FLAG, alc, &error); |
257 | |
|
258 | 0 | if (!doc) { |
259 | 0 | if (success) { |
260 | 0 | HandleCastError::AssignError(JSONCommon::FormatParseError(data, length, error), parameters); |
261 | 0 | success = false; |
262 | 0 | } |
263 | 0 | return nullopt; |
264 | 0 | } |
265 | | |
266 | 0 | return input; |
267 | 0 | }); |
268 | 0 | StringVector::AddHeapReference(result, source); |
269 | 0 | return success; |
270 | 0 | } |
271 | | |
272 | 0 | static bool CastJSONListToVarchar(Vector &source, Vector &result, idx_t count, CastParameters &) { |
273 | 0 | static constexpr char const *NULL_STRING = "NULL"; |
274 | 0 | static constexpr idx_t NULL_STRING_LENGTH = 4; |
275 | |
|
276 | 0 | auto input_jsons = source.Values<VectorListType<string_t>>(); |
277 | 0 | auto result_data = FlatVector::Writer<string_t>(result, count); |
278 | 0 | for (idx_t r = 0; r < count; r++) { |
279 | 0 | auto entry = input_jsons[r]; |
280 | 0 | if (!entry.IsValid()) { |
281 | 0 | result_data.WriteNull(); |
282 | 0 | continue; |
283 | 0 | } |
284 | | // Compute len (start with [] and ,) |
285 | 0 | idx_t len = 2; |
286 | 0 | bool seen_value = false; |
287 | 0 | for (auto child : entry.GetChildValues()) { |
288 | 0 | if (seen_value) { |
289 | 0 | len += 2; |
290 | 0 | } |
291 | 0 | if (child.IsValid()) { |
292 | 0 | len += child.GetValue().GetSize(); |
293 | 0 | } else { |
294 | 0 | len += NULL_STRING_LENGTH; |
295 | 0 | } |
296 | 0 | seen_value = true; |
297 | 0 | } |
298 | | |
299 | | // Allocate string |
300 | 0 | auto &res = result_data.WriteEmptyString(len); |
301 | 0 | auto ptr = res.GetDataWriteable(); |
302 | | |
303 | | // Populate string |
304 | 0 | *ptr++ = '['; |
305 | 0 | seen_value = false; |
306 | 0 | for (auto child : entry.GetChildValues()) { |
307 | 0 | if (seen_value) { |
308 | 0 | *ptr++ = ','; |
309 | 0 | *ptr++ = ' '; |
310 | 0 | } |
311 | 0 | if (child.IsValid()) { |
312 | 0 | auto &input_json = child.GetValue(); |
313 | 0 | memcpy(ptr, input_json.GetData(), input_json.GetSize()); |
314 | 0 | ptr += input_json.GetSize(); |
315 | 0 | } else { |
316 | 0 | memcpy(ptr, NULL_STRING, NULL_STRING_LENGTH); |
317 | 0 | ptr += NULL_STRING_LENGTH; |
318 | 0 | } |
319 | 0 | seen_value = true; |
320 | 0 | } |
321 | 0 | *ptr = ']'; |
322 | |
|
323 | 0 | res.Finalize(); |
324 | 0 | } |
325 | 0 | return true; |
326 | 0 | } |
327 | | |
328 | 0 | static bool CastVarcharToJSONList(Vector &source, Vector &result, idx_t count, CastParameters ¶meters) { |
329 | 0 | auto &lstate = parameters.local_state->Cast<JSONFunctionLocalState>(); |
330 | 0 | lstate.json_allocator->Reset(); |
331 | 0 | auto alc = lstate.json_allocator->GetYYAlc(); |
332 | |
|
333 | 0 | bool success = true; |
334 | 0 | UnaryExecutor::Execute<string_t, list_entry_t>( |
335 | 0 | source, result, count, [&](const string_t &input) -> optional<list_entry_t> { |
336 | | // Figure out if the cast can succeed |
337 | 0 | yyjson_read_err error; |
338 | 0 | const auto doc = JSONCommon::ReadDocumentUnsafe(input.GetDataWriteable(), input.GetSize(), |
339 | 0 | JSONCommon::READ_FLAG, alc, &error); |
340 | 0 | if (!doc || !unsafe_yyjson_is_arr(doc->root)) { |
341 | 0 | if (success) { |
342 | 0 | if (!doc) { |
343 | 0 | HandleCastError::AssignError( |
344 | 0 | JSONCommon::FormatParseError(input.GetDataWriteable(), input.GetSize(), error), parameters); |
345 | 0 | } else if (!unsafe_yyjson_is_arr(doc->root)) { |
346 | 0 | auto truncated_input = |
347 | 0 | input.GetSize() > 50 ? string(input.GetData(), 47) + "..." : input.GetString(); |
348 | 0 | HandleCastError::AssignError( |
349 | 0 | StringUtil::Format("Cannot cast to list of JSON. Input \"%s\"", truncated_input), |
350 | 0 | parameters); |
351 | 0 | } |
352 | 0 | success = false; |
353 | 0 | } |
354 | 0 | return nullopt; |
355 | 0 | } |
356 | | |
357 | 0 | auto current_size = ListVector::GetListSize(result); |
358 | 0 | const auto arr_len = unsafe_yyjson_get_len(doc->root); |
359 | 0 | const auto new_size = current_size + arr_len; |
360 | | |
361 | | // Grow list if needed |
362 | 0 | if (ListVector::GetListCapacity(result) < new_size) { |
363 | 0 | ListVector::Reserve(result, new_size); |
364 | 0 | } |
365 | | |
366 | | // Populate list |
367 | 0 | const auto result_jsons = FlatVector::GetDataMutable<string_t>(ListVector::GetChildMutable(result)); |
368 | 0 | size_t arr_idx, max; |
369 | 0 | yyjson_val *val; |
370 | 0 | yyjson_arr_foreach(doc->root, arr_idx, max, val) { |
371 | 0 | result_jsons[current_size + arr_idx] = JSONCommon::WriteVal(val, alc); |
372 | 0 | } |
373 | | |
374 | | // Update size |
375 | 0 | ListVector::SetListSize(result, current_size + arr_len); |
376 | |
|
377 | 0 | return list_entry_t {current_size, arr_len}; |
378 | 0 | }); |
379 | |
|
380 | 0 | JSONAllocator::AddBuffer(ListVector::GetChildMutable(result), alc); |
381 | 0 | return success; |
382 | 0 | } |
383 | | |
384 | 7.23k | void JSONFunctions::RegisterSimpleCastFunctions(ExtensionLoader &loader) { |
385 | 7.23k | auto &db = loader.GetDatabaseInstance(); |
386 | | |
387 | | // JSON to VARCHAR is basically free |
388 | 7.23k | loader.RegisterCastFunction(LogicalType::JSON(), LogicalType::VARCHAR, DefaultCasts::ReinterpretCast, 1); |
389 | | |
390 | | // VARCHAR to JSON requires a parse so it's not free. Let's make it 1 more than a cast to STRUCT |
391 | 7.23k | const auto varchar_to_json_cost = |
392 | 7.23k | CastFunctionSet::ImplicitCastCost(db, LogicalType::SQLNULL, LogicalTypeId::STRUCT) + 1; |
393 | 7.23k | BoundCastInfo varchar_to_json_info(CastVarcharToJSON, nullptr, JSONFunctionLocalState::InitCastLocalState); |
394 | 7.23k | loader.RegisterCastFunction(LogicalType::VARCHAR, LogicalType::JSON(), std::move(varchar_to_json_info), |
395 | 7.23k | varchar_to_json_cost); |
396 | | |
397 | | // Register NULL to JSON with a different cost than NULL to VARCHAR so the binder can disambiguate functions |
398 | 7.23k | const auto null_to_json_cost = |
399 | 7.23k | CastFunctionSet::ImplicitCastCost(db, LogicalType::SQLNULL, LogicalTypeId::VARCHAR) + 1; |
400 | 7.23k | loader.RegisterCastFunction(LogicalType::SQLNULL, LogicalType::JSON(), DefaultCasts::TryVectorNullCast, |
401 | 7.23k | null_to_json_cost); |
402 | | |
403 | | // JSON[] to VARCHAR (this needs a special case otherwise the cast will escape quotes) |
404 | 7.23k | const auto json_list_to_varchar_cost = |
405 | 7.23k | CastFunctionSet::ImplicitCastCost(db, LogicalType::LIST(LogicalType::JSON()), LogicalTypeId::VARCHAR) - 1; |
406 | 7.23k | loader.RegisterCastFunction(LogicalType::LIST(LogicalType::JSON()), LogicalTypeId::VARCHAR, CastJSONListToVarchar, |
407 | 7.23k | json_list_to_varchar_cost); |
408 | | |
409 | | // JSON[] to JSON is allowed implicitly |
410 | 7.23k | loader.RegisterCastFunction(LogicalType::LIST(LogicalType::JSON()), LogicalType::JSON(), CastJSONListToVarchar, |
411 | 7.23k | 100); |
412 | | |
413 | | // VARCHAR to JSON[] (also needs a special case otherwise we get a VARCHAR -> VARCHAR[] cast first) |
414 | 7.23k | const auto varchar_to_json_list_cost = |
415 | 7.23k | CastFunctionSet::ImplicitCastCost(db, LogicalType::VARCHAR, LogicalType::LIST(LogicalType::JSON())) - 1; |
416 | 7.23k | BoundCastInfo varchar_to_json_list_info(CastVarcharToJSONList, nullptr, JSONFunctionLocalState::InitCastLocalState); |
417 | 7.23k | loader.RegisterCastFunction(LogicalType::VARCHAR, LogicalType::LIST(LogicalType::JSON()), |
418 | 7.23k | std::move(varchar_to_json_list_info), varchar_to_json_list_cost); |
419 | 7.23k | } |
420 | | |
421 | | } // namespace duckdb |