Coverage Report

Created: 2026-07-07 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 &parameters) {
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 &parameters) {
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 &parameters) {
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