• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

estebanzimanyi / MobilityDB / 20645520862

01 Jan 2026 09:04PM UTC coverage: 92.448% (-0.3%) from 92.749%
20645520862

push

github

estebanzimanyi
Next

36 of 196 new or added lines in 18 files covered. (18.37%)

3 existing lines in 2 files now uncovered.

32280 of 34917 relevant lines covered (92.45%)

1150391.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.93
/mobilitydb/src/json/tjsonb_jsonfuncs.c
1
/*****************************************************************************
2
 *
3
 * This MobilityDB code is provided under The PostgreSQL License.
4
 * Copyright (c) 2016-2025, Université libre de Bruxelles and MobilityDB
5
 * contributors
6
 *
7
 * MobilityDB includes portions of PostGIS version 3 source code released
8
 * under the GNU General Public License (GPLv2 or later).
9
 * Copyright (c) 2001-2025, PostGIS contributors
10
 *
11
 * Permission to use, copy, modify, and distribute this software and its
12
 * documentation for any purpose, without fee, and without a written
13
 * agreement is hereby granted, provided that the above copyright notice and
14
 * this paragraph and the following two paragraphs appear in all copies.
15
 *
16
 * IN NO EVENT SHALL UNIVERSITE LIBRE DE BRUXELLES BE LIABLE TO ANY PARTY FOR
17
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
18
 * LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
19
 * EVEN IF UNIVERSITE LIBRE DE BRUXELLES HAS BEEN ADVISED OF THE POSSIBILITY
20
 * OF SUCH DAMAGE.
21
 *
22
 * UNIVERSITE LIBRE DE BRUXELLES SPECIFICALLY DISCLAIMS ANY WARRANTIES,
23
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
24
 * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON
25
 * AN "AS IS" BASIS, AND UNIVERSITE LIBRE DE BRUXELLES HAS NO OBLIGATIONS TO
26
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
27
 *
28
 *****************************************************************************/
29

30
/**
31
 * @file
32
 * @brief JSON functions for temporal JSONB
33
 */
34

35
/* C */
36
#include <stdbool.h>
37
/* PostgreSQL */
38
#include <postgres.h>
39
#include "utils/array.h"
40
#include "utils/jsonb.h"
41
#include "utils/jsonpath.h"
42
/* MEOS */
43
#include <meos.h>
44
#include <meos_json.h>
45
#include "temporal/span.h"
46
#include "json/tjsonb.h"
47
#include <pgtypes.h>
48
/* MobilityDB */
49
#include "pg_temporal/temporal.h"
50

51
/*****************************************************************************
52
 * Temporal JSONB casting to/from temporal text
53
 *****************************************************************************/
54

55
PGDLLEXPORT Datum Jsonb_as_text(PG_FUNCTION_ARGS);
56
PG_FUNCTION_INFO_V1(Jsonb_as_text);
×
57
/**
58
 * @ingroup mobilitydb_json_json
59
 * @brief Transform a JSONB value into a text value
60
 * @sqlfn text()
61
 * @sqlop @p ::
62
 */
63
Datum
64
Jsonb_as_text(PG_FUNCTION_ARGS)
×
65
{
66
  Jsonb *jb = PG_GETARG_JSONB_P(0);
×
NEW
67
  text *result = pg_jsonb_to_text(jb);
×
NEW
68
  PG_FREE_IF_COPY(jb, 0);
×
UNCOV
69
  PG_RETURN_TEXT_P(result);
×
70
}
71

72
PGDLLEXPORT Datum Tjsonb_as_ttext(PG_FUNCTION_ARGS);
73
PG_FUNCTION_INFO_V1(Tjsonb_as_ttext);
1✔
74
/**
75
 * @ingroup mobilitydb_json_json
76
 * @brief Transform a temporal JSONB value into a temporal text value
77
 * @sqlfn ttext()
78
 * @sqlop @p ::
79
 */
80
Datum
81
Tjsonb_as_ttext(PG_FUNCTION_ARGS)
×
82
{
83
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
84
  Temporal *result = tjsonb_to_ttext(temp);
×
85
  PG_FREE_IF_COPY(temp, 0);
×
86
  PG_RETURN_TEMPORAL_P(result);
×
87
}
88

89
PGDLLEXPORT Datum Ttext_as_tjsonb(PG_FUNCTION_ARGS);
90
PG_FUNCTION_INFO_V1(Ttext_as_tjsonb);
1✔
91
/**
92
 * @ingroup mobilitydb_json_json
93
 * @brief Transform a temporal text value into a temporal JSONB value
94
 * @sqlfn ttext()
95
 * @sqlop @p ::
96
 */
97
Datum
98
Ttext_as_tjsonb(PG_FUNCTION_ARGS)
×
99
{
100
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
101
  Temporal *result = ttext_to_tjsonb(temp);
×
102
  PG_FREE_IF_COPY(temp, 0);
×
103
  if (! result)
×
104
    PG_RETURN_NULL();
×
105
  PG_RETURN_TEMPORAL_P(result);
×
106
}
107

108
/*****************************************************************************
109
 * JSON functions
110
 *****************************************************************************/
111

112
PGDLLEXPORT Datum Tjson_array_length(PG_FUNCTION_ARGS);
113
PG_FUNCTION_INFO_V1(Tjson_array_length);
1✔
114
/**
115
 * @ingroup mobilitydb_json_json
116
 * @brief Return the array length of a temporal JSON value
117
 * @sqlfn tjson_array_length()
118
 */
119
Datum
NEW
120
Tjson_array_length(PG_FUNCTION_ARGS)
×
121
{
NEW
122
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
NEW
123
  Temporal *result = tjson_array_length(temp);
×
NEW
124
  PG_FREE_IF_COPY(temp, 0);
×
NEW
125
  if (! result)
×
NEW
126
    PG_RETURN_NULL();
×
NEW
127
  PG_RETURN_TEMPORAL_P(result);
×
128
}
129

130
PGDLLEXPORT Datum Tjsonb_array_length(PG_FUNCTION_ARGS);
131
PG_FUNCTION_INFO_V1(Tjsonb_array_length);
1✔
132
/**
133
 * @ingroup mobilitydb_json_json
134
 * @brief Return the array length of a temporal JSONB value
135
 * @sqlfn tjsonb_array_length()
136
 */
137
Datum
NEW
138
Tjsonb_array_length(PG_FUNCTION_ARGS)
×
139
{
NEW
140
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
NEW
141
  Temporal *result = tjsonb_array_length(temp);
×
NEW
142
  PG_FREE_IF_COPY(temp, 0);
×
NEW
143
  if (! result)
×
NEW
144
    PG_RETURN_NULL();
×
NEW
145
  PG_RETURN_TEMPORAL_P(result);
×
146
}
147

148
/*****************************************************************************/
149

150
PGDLLEXPORT Datum Tjson_object_field(PG_FUNCTION_ARGS);
151
PG_FUNCTION_INFO_V1(Tjson_object_field);
1✔
152
/**
153
 * @ingroup mobilitydb_json_json
154
 * @brief Extract a field from a temporal JSON value
155
 * @sqlfn tjson_object_field()
156
 * @sqlop @p ->
157
 */
158
Datum
159
Tjson_object_field(PG_FUNCTION_ARGS)
×
160
{
161
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
162
  text *key = PG_GETARG_TEXT_P(1);
×
163
  Temporal *result = tjson_object_field(temp, key, false);
×
164
  PG_FREE_IF_COPY(temp, 0);
×
165
  PG_FREE_IF_COPY(key, 1);
×
166
  if (! result)
×
167
    PG_RETURN_NULL();
×
168
  PG_RETURN_TEMPORAL_P(result);
×
169
}
170

171
PGDLLEXPORT Datum Tjsonb_object_field(PG_FUNCTION_ARGS);
172
PG_FUNCTION_INFO_V1(Tjsonb_object_field);
1✔
173
/**
174
 * @ingroup mobilitydb_json_json
175
 * @brief Extract a field from a temporal JSONB value
176
 * @sqlfn tjsonb_object_field()
177
 * @sqlop @p ->
178
 */
179
Datum
180
Tjsonb_object_field(PG_FUNCTION_ARGS)
×
181
{
182
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
183
  text *key = PG_GETARG_TEXT_P(1);
×
184
  Temporal *result = tjsonb_object_field(temp, key, false);
×
185
  PG_FREE_IF_COPY(temp, 0);
×
186
  PG_FREE_IF_COPY(key, 1);
×
187
  if (! result)
×
188
    PG_RETURN_NULL();
×
189
  PG_RETURN_TEMPORAL_P(result);
×
190
}
191

192
PGDLLEXPORT Datum Tjson_object_field_text(PG_FUNCTION_ARGS);
193
PG_FUNCTION_INFO_V1(Tjson_object_field_text);
1✔
194
/**
195
 * @ingroup mobilitydb_json_json
196
 * @brief Extract a field from a temporal JSON value as text
197
 * @sqlfn tjson_object_field_text()
198
 * @sqlop @p ->>
199
 */
200
Datum
201
Tjson_object_field_text(PG_FUNCTION_ARGS)
×
202
{
203
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
204
  text *key = PG_GETARG_TEXT_P(1);
×
205
  Temporal *result = tjson_object_field(temp, key, true);
×
206
  PG_FREE_IF_COPY(temp, 0);
×
207
  PG_FREE_IF_COPY(key, 1);
×
208
  if (! result)
×
209
    PG_RETURN_NULL();
×
210
  PG_RETURN_TEMPORAL_P(result);
×
211
}
212

213
PGDLLEXPORT Datum Tjsonb_object_field_text(PG_FUNCTION_ARGS);
214
PG_FUNCTION_INFO_V1(Tjsonb_object_field_text);
1✔
215
/**
216
 * @ingroup mobilitydb_json_json
217
 * @brief Extract a field from a temporal JSONB value as text
218
 * @sqlfn tjsonb_object_field_text()
219
 * @sqlop @p ->>
220
 */
221
Datum
222
Tjsonb_object_field_text(PG_FUNCTION_ARGS)
×
223
{
224
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
225
  text *key = PG_GETARG_TEXT_P(1);
×
226
  Temporal *result = tjsonb_object_field(temp, key, true);
×
227
  PG_FREE_IF_COPY(temp, 0);
×
228
  PG_FREE_IF_COPY(key, 1);
×
229
  if (! result)
×
230
    PG_RETURN_NULL();
×
231
  PG_RETURN_TEMPORAL_P(result);
×
232
}
233

234
/*****************************************************************************/
235

236
PGDLLEXPORT Datum Concat_jsonb_tjsonb(PG_FUNCTION_ARGS);
237
PG_FUNCTION_INFO_V1(Concat_jsonb_tjsonb);
1✔
238
/**
239
 * @ingroup mobilitydb_json_json
240
 * @brief Concat a JSONB value with a temporal JSONB
241
 * @sqlfn tjsonb_concat()
242
 * @sqlop @p ||
243
 */
244
Datum
245
Concat_jsonb_tjsonb(PG_FUNCTION_ARGS)
×
246
{
247
  Jsonb *jb = PG_GETARG_JSONB_P(0);
×
248
  Temporal *temp = PG_GETARG_TEMPORAL_P(1);
×
249
  Temporal *result = concat_tjsonb_jsonb(temp, jb, INVERT);
×
250
  PG_FREE_IF_COPY(jb, 0);
×
251
  PG_FREE_IF_COPY(temp, 1);
×
252
  PG_RETURN_TEMPORAL_P(result);
×
253
}
254

255
PGDLLEXPORT Datum Concat_tjsonb_jsonb(PG_FUNCTION_ARGS);
256
PG_FUNCTION_INFO_V1(Concat_tjsonb_jsonb);
1✔
257
/**
258
 * @ingroup mobilitydb_json_json
259
 * @brief Concat a temporal JSONB with a JSONB value
260
 * @sqlfn jsonb_concat()
261
 * @sqlop @p ||
262
 */
263
Datum
264
Concat_tjsonb_jsonb(PG_FUNCTION_ARGS)
×
265
{
266
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
267
  Jsonb *jb = PG_GETARG_JSONB_P(1);
×
268
  Temporal *result = concat_tjsonb_jsonb(temp, jb, INVERT_NO);
×
269
  PG_FREE_IF_COPY(temp, 0);
×
270
  PG_FREE_IF_COPY(jb, 1);
×
271
  PG_RETURN_TEMPORAL_P(result);
×
272
}
273

274
PGDLLEXPORT Datum Concat_tjsonb_tjsonb(PG_FUNCTION_ARGS);
275
PG_FUNCTION_INFO_V1(Concat_tjsonb_tjsonb);
1✔
276
/**
277
 * @ingroup mobilitydb_json_json
278
 * @brief Concat two temporal JSONB values
279
 * @sqlfn jsonb_concat()
280
 * @sqlop @p ||
281
 */
282
Datum
283
Concat_tjsonb_tjsonb(PG_FUNCTION_ARGS)
×
284
{
285
  Temporal *temp1 = PG_GETARG_TEMPORAL_P(0);
×
286
  Temporal *temp2 = PG_GETARG_TEMPORAL_P(1);
×
287
  Temporal *result = concat_tjsonb_tjsonb(temp1, temp2);
×
288
  PG_FREE_IF_COPY(temp1, 0);
×
289
  PG_FREE_IF_COPY(temp2, 1);
×
290
  if (! result)
×
291
    PG_RETURN_NULL();
×
292
  PG_RETURN_TEMPORAL_P(result);
×
293
}
294

295
/*****************************************************************************/
296

297
PGDLLEXPORT Datum Tjsonb_delete_key(PG_FUNCTION_ARGS);
298
PG_FUNCTION_INFO_V1(Tjsonb_delete_key);
1✔
299
/**
300
 * @ingroup mobilitydb_json_json
301
 * @brief Delete a key from a temporal JSONB value
302
 * @sqlfn jsonb_delete()
303
 * @sqlop @p -
304
 */
305
Datum
306
Tjsonb_delete_key(PG_FUNCTION_ARGS)
×
307
{
308
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
309
  text *key = PG_GETARG_TEXT_P(1);
×
310
  Temporal *result = tjsonb_delete_key(temp, key);
×
311
  PG_FREE_IF_COPY(temp, 0);
×
312
  PG_FREE_IF_COPY(key, 1);
×
313
  if (! result)
×
314
    PG_RETURN_NULL();
×
315
  PG_RETURN_TEMPORAL_P(result);
×
316
}
317

318
PGDLLEXPORT Datum Tjsonb_delete_key_array(PG_FUNCTION_ARGS);
319
PG_FUNCTION_INFO_V1(Tjsonb_delete_key_array);
1✔
320
/**
321
 * @ingroup mobilitydb_json_json
322
 * @brief Delete an array of keys from a temporal JSONB value
323
 * @sqlfn jsonb_delete_array()
324
 * @sqlop @p -
325
 */
326
Datum
327
Tjsonb_delete_key_array(PG_FUNCTION_ARGS)
×
328
{
329
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
330
  ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
×
331
  if (ARR_NDIM(keys) > 1)
×
332
    ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
×
333
       errmsg("wrong number of array subscripts")));
334

335
  /* Extract the keys from the array */
336
  int keys_len;
337
  Datum *keys_elems;
338
  bool *keys_nulls;
339
  deconstruct_array(keys, TEXTOID, -1, false, 'd', &keys_elems, &keys_nulls,
×
340
    &keys_len);
341
  if (keys_len == 0)
×
342
    PG_RETURN_TEMPORAL_P(temp);
×
343

344
  /* Compute the result */
345
  Temporal *result = tjsonb_delete_key_array(temp, (text **) keys_elems,
×
346
    keys_len);
347
  pfree(keys_elems); pfree(keys_nulls);
×
348
  PG_FREE_IF_COPY(temp, 0);
×
349
  PG_FREE_IF_COPY(keys, 1);
×
350
  if (! result)
×
351
    PG_RETURN_NULL();
×
352
  PG_RETURN_TEMPORAL_P(result);
×
353
}
354

355
PGDLLEXPORT Datum Tjsonb_delete_idx(PG_FUNCTION_ARGS);
356
PG_FUNCTION_INFO_V1(Tjsonb_delete_idx);
1✔
357
/**
358
 * @ingroup mobilitydb_json_json
359
 * @brief Delete a key specified by an index from a temporal JSONB value
360
 * @sqlfn jsonb_delete()
361
 * @sqlop @p -
362
 */
363
Datum
364
Tjsonb_delete_idx(PG_FUNCTION_ARGS)
×
365
{
366
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
367
  int idx = PG_GETARG_INT32(1);
×
368
  Temporal *result = tjsonb_delete_idx(temp, idx);
×
369
  PG_FREE_IF_COPY(temp, 0);
×
370
  if (! result)
×
371
    PG_RETURN_NULL();
×
372
  PG_RETURN_TEMPORAL_P(result);
×
373
}
374

375
/*****************************************************************************/
376

377
PGDLLEXPORT Datum Tjson_array_element(PG_FUNCTION_ARGS);
378
PG_FUNCTION_INFO_V1(Tjson_array_element);
1✔
379
/**
380
 * @ingroup mobilitydb_json_json
381
 * @brief Extract an array element from a temporal JSON value
382
 * @sqlfn tjson_array_element()
383
 */
384
Datum
385
Tjson_array_element(PG_FUNCTION_ARGS)
×
386
{
387
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
388
  int element = PG_GETARG_INT32(1);
×
389
  Temporal *result = tjson_array_element(temp, element, false);
×
390
  PG_FREE_IF_COPY(temp, 0);
×
391
  if (! result)
×
392
    PG_RETURN_NULL();
×
393
  PG_RETURN_TEMPORAL_P(result);
×
394
}
395

396
PGDLLEXPORT Datum Tjsonb_array_element(PG_FUNCTION_ARGS);
397
PG_FUNCTION_INFO_V1(Tjsonb_array_element);
1✔
398
/**
399
 * @ingroup mobilitydb_json_json
400
 * @brief Extract an array element from a temporal JSONB value
401
 * @sqlfn tjsonb_array_element()
402
 */
403
Datum
404
Tjsonb_array_element(PG_FUNCTION_ARGS)
×
405
{
406
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
407
  int element = PG_GETARG_INT32(1);
×
408
  Temporal *result = tjsonb_array_element(temp, element, false);
×
409
  PG_FREE_IF_COPY(temp, 0);
×
410
  if (! result)
×
411
    PG_RETURN_NULL();
×
412
  PG_RETURN_TEMPORAL_P(result);
×
413
}
414

415
PGDLLEXPORT Datum Tjson_array_element_text(PG_FUNCTION_ARGS);
416
PG_FUNCTION_INFO_V1(Tjson_array_element_text);
×
417
/**
418
 * @ingroup mobilitydb_json_json
419
 * @brief Extract an array element from a temporal JSON value as text
420
 * @sqlfn tjson_array_element_text()
421
 */
422
Datum
423
Tjson_array_element_text(PG_FUNCTION_ARGS)
×
424
{
425
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
426
  int element = PG_GETARG_INT32(1);
×
427
  Temporal *result = tjson_array_element(temp, element, true);
×
428
  PG_FREE_IF_COPY(temp, 0);
×
429
  if (! result)
×
430
    PG_RETURN_NULL();
×
431
  PG_RETURN_TEMPORAL_P(result);
×
432
}
433

434
PGDLLEXPORT Datum Tjsonb_array_element_text(PG_FUNCTION_ARGS);
435
PG_FUNCTION_INFO_V1(Tjsonb_array_element_text);
1✔
436
/**
437
 * @ingroup mobilitydb_json_json
438
 * @brief Extract an array element from a temporal JSONB value as text
439
 * @sqlfn tjsonb_array_element_text()
440
 */
441
Datum
442
Tjsonb_array_element_text(PG_FUNCTION_ARGS)
×
443
{
444
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
445
  int element = PG_GETARG_INT32(1);
×
446
  Temporal *result = tjsonb_array_element(temp, element, true);
×
447
  PG_FREE_IF_COPY(temp, 0);
×
448
  if (! result)
×
449
    PG_RETURN_NULL();
×
450
  PG_RETURN_TEMPORAL_P(result);
×
451
}
452

453
/*****************************************************************************/
454

455
/**
456
 * @brief Extract a path from a temporal JSONB value
457
 */
458
Datum
NEW
459
Tjson_extract_path_common(FunctionCallInfo fcinfo, bool isjsonb, bool astext)
×
460
{
461
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
462
  ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
×
463
  if (ARR_NDIM(path) > 1)
×
464
    ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
×
465
       errmsg("wrong number of array subscripts")));
466

467
  /* Extract the path from the array */
468
  int path_len;
469
  Datum *path_elems;
470
  bool *path_nulls;
471
  deconstruct_array(path, TEXTOID, -1, false, 'i', &path_elems, &path_nulls,
×
472
    &path_len);
473
  if (path_len == 0)
×
474
    PG_RETURN_TEMPORAL_P(temp);
×
475
  /*
476
   * If the array contains any null elements, return NULL, on the grounds
477
   * that you'd have gotten NULL if any RHS value were NULL in a nested
478
   * series of applications of the -> operator.  (Note: because we also
479
   * return NULL for error cases such as no-such-field, this is true
480
   * regardless of the contents of the rest of the array.)
481
   */
482
  if (array_contains_nulls(path))
×
483
    PG_RETURN_NULL();
×
484
  
485
  /* Compute the result */
486
  Temporal *result = isjsonb ?
487
    tjsonb_extract_path(temp, (text **) path_elems, path_len, astext) :
×
488
    tjson_extract_path(temp, (text **) path_elems, path_len, astext);
×
489

490
  pfree(path_elems); pfree(path_nulls);
×
491
  PG_FREE_IF_COPY(temp, 0);
×
492
  PG_FREE_IF_COPY(path, 1);
×
493
  if (! result)
×
494
    PG_RETURN_NULL();
×
495
  PG_RETURN_TEMPORAL_P(result);
×
496
}
497

498
PGDLLEXPORT Datum Tjson_extract_path(PG_FUNCTION_ARGS);
499
PG_FUNCTION_INFO_V1(Tjson_extract_path);
1✔
500
/**
501
 * @ingroup mobilitydb_json_json
502
 * @brief Extract an item specified by a path from a temporal JSON value
503
 * @sqlfn tjson_extract_path()
504
 */
505
Datum
506
Tjson_extract_path(PG_FUNCTION_ARGS)
×
507
{
NEW
508
  return Tjson_extract_path_common(fcinfo, false, false);
×
509
}
510

511
PGDLLEXPORT Datum Tjson_extract_path_text(PG_FUNCTION_ARGS);
512
PG_FUNCTION_INFO_V1(Tjson_extract_path_text);
1✔
513
/**
514
 * @ingroup mobilitydb_json_json
515
 * @brief Extract an item specified by a path from a temporal JSON value as text
516
 * @sqlfn tjson_extract_path_text()
517
 */
518
Datum
519
Tjson_extract_path_text(PG_FUNCTION_ARGS)
×
520
{
NEW
521
  return Tjson_extract_path_common(fcinfo, false, true);
×
522
}
523

524
PGDLLEXPORT Datum Tjsonb_extract_path(PG_FUNCTION_ARGS);
525
PG_FUNCTION_INFO_V1(Tjsonb_extract_path);
1✔
526
/**
527
 * @ingroup mobilitydb_json_json
528
 * @brief Extract a path from a temporal JSONB value
529
 * @sqlfn tjsonb_extract_path()
530
 */
531
Datum
532
Tjsonb_extract_path(PG_FUNCTION_ARGS)
×
533
{
NEW
534
  return Tjson_extract_path_common(fcinfo, true, false);
×
535
}
536

537
PGDLLEXPORT Datum Tjsonb_extract_path_text(PG_FUNCTION_ARGS);
538
PG_FUNCTION_INFO_V1(Tjsonb_extract_path_text);
1✔
539
/**
540
 * @ingroup mobilitydb_json_json
541
 * @brief Extract a path from a temporal JSONB value as text
542
 * @sqlfn tjsonb_extract_path_text()
543
 */
544
Datum
545
Tjsonb_extract_path_text(PG_FUNCTION_ARGS)
×
546
{
NEW
547
  return Tjson_extract_path_common(fcinfo, true, true);
×
548
}
549

550
/*****************************************************************************/
551

552
/**
553
 * @brief Replace a value specified by a path with a new value in a temporal
554
 * JSONB value 
555
 * @sqlfn tjsonb_set(), tjsonb_set_lax()
556
 */
557
Datum
NEW
558
Tjsonb_set_common(FunctionCallInfo fcinfo, bool lax)
×
559
{
560
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
561
  ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
×
562
  Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
×
563
  bool create = PG_GETARG_BOOL(3);
×
564
  text *handle_null = NULL;
565
  if (lax)
×
566
    handle_null = PG_GETARG_TEXT_P(4);
×
567
 
568
 if (ARR_NDIM(path) > 1)
×
569
    ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
×
570
      errmsg("wrong number of array subscripts")));
571

572
  Datum *path_elems;
573
  bool *path_nulls;
574
  int path_len;
575
  deconstruct_array(path, TEXTOID, -1, false, 'd', &path_elems, &path_nulls,
×
576
    &path_len);
577
  if (path_len == 0)
×
578
    PG_RETURN_TEMPORAL_P(temp);
×
579

580
  /* Compute the result */
581
  Temporal *result = tjsonb_set(temp, (text **) path_elems, path_len,
×
582
    newjsonb, create, handle_null, lax);
583
  pfree(path_elems); pfree(path_nulls);
×
584
  PG_FREE_IF_COPY(temp, 0);
×
585
  PG_FREE_IF_COPY(path, 1);
×
586
  if (! result)
×
587
    PG_RETURN_NULL();
×
588
  PG_RETURN_TEMPORAL_P(result);
×
589
}
590

591
PGDLLEXPORT Datum Tjsonb_set(PG_FUNCTION_ARGS);
592
PG_FUNCTION_INFO_V1(Tjsonb_set);
1✔
593
/**
594
 * @ingroup mobilitydb_json_json
595
 * @brief Replace a value specified by a path with a new value in a temporal
596
 * JSONB value
597
 * @sqlfn tjsonb_set()
598
 */
599
Datum
600
Tjsonb_set(PG_FUNCTION_ARGS)
×
601
{
NEW
602
  return Tjsonb_set_common(fcinfo, false);
×
603
}
604

605
PGDLLEXPORT Datum Tjsonb_set_lax(PG_FUNCTION_ARGS);
606
PG_FUNCTION_INFO_V1(Tjsonb_set_lax);
1✔
607
/**
608
 * @ingroup mobilitydb_json_json
609
 * @brief Replace a value specified by a path with a new value in a temporal
610
 * JSONB value using the lax mode
611
 * @sqlfn tjsonb_set_lax()
612
 */
613
Datum
614
Tjsonb_set_lax(PG_FUNCTION_ARGS)
×
615
{
NEW
616
  return Tjsonb_set_common(fcinfo, true);
×
617
}
618

619
/*****************************************************************************/
620

621
PGDLLEXPORT Datum Tjsonb_delete_path(PG_FUNCTION_ARGS);
622
PG_FUNCTION_INFO_V1(Tjsonb_delete_path);
1✔
623
/**
624
 * @ingroup mobilitydb_json_json
625
 * @brief Delete a path from a temporal JSONB value
626
 * @sqlfn Tjsonb_delete_path()
627
 */
628
Datum
629
Tjsonb_delete_path(PG_FUNCTION_ARGS)
×
630
{
631
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
632
  ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
×
633
  if (ARR_NDIM(path) > 1)
×
634
    ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
×
635
       errmsg("wrong number of array subscripts")));
636

637
  /* Extract the path from the array */
638
  int path_len;
639
  Datum *path_elems;
640
  bool *path_nulls;
641
  deconstruct_array(path, TEXTOID, -1, false, 'd', &path_elems, &path_nulls,
×
642
    &path_len);
643
  if (path_len == 0)
×
644
    PG_RETURN_TEMPORAL_P(temp);
×
645

646
  /* Compute the result */
647
  Temporal *result = tjsonb_delete_path(temp, (text **) path_elems, path_len);
×
648

649
  pfree(path_elems); pfree(path_nulls);
×
650
  PG_FREE_IF_COPY(temp, 0);
×
651
  PG_FREE_IF_COPY(path, 1);
×
652
  if (! result)
×
653
    PG_RETURN_NULL();
×
654
  PG_RETURN_TEMPORAL_P(result);
×
655
}
656

657
/*****************************************************************************/
658

659
PGDLLEXPORT Datum Tjsonb_insert(PG_FUNCTION_ARGS);
660
PG_FUNCTION_INFO_V1(Tjsonb_insert);
1✔
661
/**
662
 * @ingroup mobilitydb_json_json
663
 * @brief Insert a path into a temporal JSONB value
664
 * @sqlfn tjsonb_insert()
665
 */
666
Datum
667
Tjsonb_insert(PG_FUNCTION_ARGS)
×
668
{
669
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
670
  ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
×
671
  Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
×
672
  bool after = PG_GETARG_BOOL(3);
×
673
  if (ARR_NDIM(path) > 1)
×
674
    ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
×
675
      errmsg("wrong number of array subscripts")));
676

677
  /* Extract the path from the array */
678
  int path_len;
679
  Datum *path_elems;
680
  bool *path_nulls;
681
  deconstruct_array(path, TEXTOID, -1, false, 'd', &path_elems, &path_nulls,
×
682
    &path_len);
683
  if (path_len == 0)
×
684
    PG_RETURN_TEMPORAL_P(temp);
×
685

686
  /* Compute the result */
687
  Temporal *result = tjsonb_insert(temp, (text **) path_elems, path_len,
×
688
    newjsonb, after);
689
  pfree(path_elems); pfree(path_nulls);
×
690
  PG_FREE_IF_COPY(temp, 0);
×
691
  PG_FREE_IF_COPY(path, 1);
×
692
  if (! result)
×
693
    PG_RETURN_NULL();
×
694
  PG_RETURN_TEMPORAL_P(result);
×
695
}
696

697
/*****************************************************************************/
698

699
/**
700
 * @brief Convert a temporal JSONB into a temporal alphanumeric type
701
 * @sqlfn tjsonb_to_tint()
702
 */
703
Datum
704
Tjsonb_to_talphanum(FunctionCallInfo fcinfo, meosType temptype)
×
705
{
706
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
707
  text *key_text = PG_GETARG_TEXT_PP(1);
×
708
  char *key = pg_text_to_cstring(key_text);
×
709
  interpType interp = INTERP_NONE;
710
  if (PG_NARGS() > 2 && ! PG_ARGISNULL(2))
×
711
    interp = input_interp_string(fcinfo, 2);
×
712
  Temporal *result = tjsonb_to_talphanum(temp, key, temptype, interp);
×
713
  PG_FREE_IF_COPY(temp, 0);
×
714
  PG_FREE_IF_COPY(key_text, 1);
×
715
  PG_RETURN_POINTER(result);
×
716
}
717

718
PGDLLEXPORT Datum Tjsonb_to_tbool(PG_FUNCTION_ARGS);
719
PG_FUNCTION_INFO_V1(Tjsonb_to_tbool);
1✔
720
/**
721
 * @ingroup mobilitydb_json_json
722
 * @brief Convert a temporal JSONB into a temporal boolean
723
 * @sqlfn tbool()
724
 */
725
Datum
726
Tjsonb_to_tbool(PG_FUNCTION_ARGS)
×
727
{
728
  return Tjsonb_to_talphanum(fcinfo, T_TBOOL);
×
729
}
730

731
PGDLLEXPORT Datum Tjsonb_to_tint(PG_FUNCTION_ARGS);
732
PG_FUNCTION_INFO_V1(Tjsonb_to_tint);
1✔
733
/**
734
 * @ingroup mobilitydb_json_json
735
 * @brief Convert a temporal JSONB into a temporal integer
736
 * @sqlfn tint()
737
 */
738
Datum
739
Tjsonb_to_tint(PG_FUNCTION_ARGS)
×
740
{
741
  return Tjsonb_to_talphanum(fcinfo, T_TINT);
×
742
}
743

744
PGDLLEXPORT Datum Tjsonb_to_tfloat(PG_FUNCTION_ARGS);
745
PG_FUNCTION_INFO_V1(Tjsonb_to_tfloat);
1✔
746
/**
747
 * @ingroup mobilitydb_json_json
748
 * @brief Convert a temporal JSONB into a temporal float
749
 * @sqlfn tfloat()
750
 */
751
Datum
752
Tjsonb_to_tfloat(PG_FUNCTION_ARGS)
×
753
{
754
  return Tjsonb_to_talphanum(fcinfo, T_TFLOAT);
×
755
}
756

757
PGDLLEXPORT Datum Tjsonb_to_ttext_key(PG_FUNCTION_ARGS);
758
PG_FUNCTION_INFO_V1(Tjsonb_to_ttext_key);
1✔
759
/**
760
 * @ingroup mobilitydb_json_json
761
 * @brief Convert a temporal JSONB into a temporal text
762
 * @sqlfn ttext()
763
 */
764
Datum
765
Tjsonb_to_ttext_key(PG_FUNCTION_ARGS)
×
766
{
767
  return Tjsonb_to_talphanum(fcinfo, T_TTEXT);
×
768
}
769

770
/*****************************************************************************/
771

772
PGDLLEXPORT Datum Tjson_strip_nulls(PG_FUNCTION_ARGS);
773
PG_FUNCTION_INFO_V1(Tjson_strip_nulls);
1✔
774
/**
775
 * @ingroup mobilitydb_json_json
776
 * @brief Return a temporal JSON value without nulls
777
 * @sqlfn tjson_strip_nulls()
778
 */
779
Datum
780
Tjson_strip_nulls(PG_FUNCTION_ARGS)
×
781
{
782
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
783
  bool strip_nulls = PG_GETARG_BOOL(1);
×
784
  Temporal *result = tjson_strip_nulls(temp, strip_nulls);
×
785
  PG_FREE_IF_COPY(temp, 0);
×
786
  PG_RETURN_POINTER(result);
×
787
}
788

789
PGDLLEXPORT Datum Tjsonb_strip_nulls(PG_FUNCTION_ARGS);
790
PG_FUNCTION_INFO_V1(Tjsonb_strip_nulls);
1✔
791
/**
792
 * @ingroup mobilitydb_json_json
793
 * @brief Return a temporal JSONB value without nulls
794
 * @sqlfn tjsonb_strip_nulls()
795
 */
796
Datum
797
Tjsonb_strip_nulls(PG_FUNCTION_ARGS)
×
798
{
799
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
800
  bool strip_nulls = PG_GETARG_BOOL(1);
×
801
  Temporal *result = tjsonb_strip_nulls(temp, strip_nulls);
×
802
  PG_FREE_IF_COPY(temp, 0);
×
803
  PG_RETURN_POINTER(result);
×
804
}
805

806
/*****************************************************************************/
807

808
PGDLLEXPORT Datum Tjsonb_pretty(PG_FUNCTION_ARGS);
809
PG_FUNCTION_INFO_V1(Tjsonb_pretty);
1✔
810
/**
811
 * @ingroup mobilitydb_json_json
812
 * @brief Return a temporal JSONB value without nulls
813
 * @sqlfn tjsonb_pretty()
814
 */
815
Datum
816
Tjsonb_pretty(PG_FUNCTION_ARGS)
×
817
{
818
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
819
  Temporal *result = tjsonb_pretty(temp);
×
820
  PG_FREE_IF_COPY(temp, 0);
×
821
  PG_RETURN_POINTER(result);
×
822
}
823

824
/*****************************************************************************
825
 * JSON path functions
826
 *****************************************************************************/
827

828
/**
829
 * @brief Return true if a JSON path returns at least one item for a temporal
830
 * JSONB value
831
 * @sqlfn tjsonb_path_exists(), tjsonb_path_exists_tz()
832
 */
833
Datum
NEW
834
Tjsonb_path_exists_common(FunctionCallInfo fcinfo, bool tz)
×
835
{
NEW
836
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
NEW
837
  JsonPath *jp = PG_GETARG_JSONPATH_P(1);
×
NEW
838
  Jsonb *vars = PG_GETARG_JSONB_P(2);
×
NEW
839
  bool silent = PG_GETARG_BOOL(3);
×
NEW
840
  Temporal *result = tjsonb_path_exists(temp, jp, vars, silent, tz);
×
NEW
841
  PG_FREE_IF_COPY(temp, 0);
×
NEW
842
  PG_FREE_IF_COPY(jp, 1);
×
NEW
843
  if (! result)
×
NEW
844
    PG_RETURN_NULL();
×
NEW
845
  PG_RETURN_TEMPORAL_P(result);
×
846
}
847

848
PGDLLEXPORT Datum Tjsonb_path_exists(PG_FUNCTION_ARGS);
849
PG_FUNCTION_INFO_V1(Tjsonb_path_exists);
1✔
850
/**
851
 * @ingroup mobilitydb_json_json
852
 * @brief Return true if a JSON path returns at least one item for a temporal
853
 * JSONB value
854
 * @sqlfn tjsonb_path_exists()
855
 */
856
Datum
NEW
857
Tjsonb_path_exists(PG_FUNCTION_ARGS)
×
858
{
NEW
859
 return Tjsonb_path_exists_common(fcinfo, false);
×
860
}
861

862
PGDLLEXPORT Datum Tjsonb_path_exists_tz(PG_FUNCTION_ARGS);
863
PG_FUNCTION_INFO_V1(Tjsonb_path_exists_tz);
1✔
864
/**
865
 * @ingroup mobilitydb_json_json
866
 * @brief Return true if a JSON path returns at least one item for a temporal
867
 * JSONB value
868
 * @sqlfn tjsonb_path_exists_tz()
869
 */
870
Datum
NEW
871
Tjsonb_path_exists_tz(PG_FUNCTION_ARGS)
×
872
{
NEW
873
  return Tjsonb_path_exists_common(fcinfo, true);
×
874
}
875

876
PGDLLEXPORT Datum Tjsonb_path_exists_opr(PG_FUNCTION_ARGS);
877
PG_FUNCTION_INFO_V1(Tjsonb_path_exists_opr);
1✔
878
/**
879
 * @ingroup mobilitydb_json_json
880
 * @brief Return true if a JSON path returns at least one item for a temporal
881
 * JSONB value
882
 * @details Implementation of operator "tjsonb @? jsonpath" (2-argument version
883
 * of tjsonb_path_exists())
884
 * @sqlfn tjsonb_path_exists_opr()
885
 */
886
Datum
NEW
887
Tjsonb_path_exists_opr(PG_FUNCTION_ARGS)
×
888
{
NEW
889
 return Tjsonb_path_exists_common(fcinfo, false);
×
890
}
891

892
/*****************************************************************************/
893

894
/**
895
 * @brief Extract an item specified by a JSON path predicate from a temporal
896
 * JSONB value
897
 * @sqlfn tjsonb_path_match(), tjsonb_path_match_tz()
898
 */
899
Datum
NEW
900
Tjsonb_path_match_common(FunctionCallInfo fcinfo, bool tz)
×
901
{
NEW
902
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
NEW
903
  JsonPath *jp = PG_GETARG_JSONPATH_P(1);
×
NEW
904
  Jsonb *vars = PG_GETARG_JSONB_P(2);
×
NEW
905
  bool silent = PG_GETARG_BOOL(3);
×
NEW
906
  Temporal *result = tjsonb_path_match(temp, jp, vars, silent, tz);
×
NEW
907
  PG_FREE_IF_COPY(temp, 0);
×
NEW
908
  PG_FREE_IF_COPY(jp, 1);
×
NEW
909
  if (! result)
×
NEW
910
    PG_RETURN_NULL();
×
NEW
911
  PG_RETURN_TEMPORAL_P(result);
×
912
}
913

914
PGDLLEXPORT Datum Tjsonb_path_match(PG_FUNCTION_ARGS);
915
PG_FUNCTION_INFO_V1(Tjsonb_path_match);
1✔
916
/**
917
 * @ingroup mobilitydb_json_json
918
 * @brief Extract an item specified by a JSON path predicate from a temporal
919
 * JSONB value
920
 * @sqlfn tjsonb_path_match()
921
 */
922
Datum
NEW
923
Tjsonb_path_match(PG_FUNCTION_ARGS)
×
924
{
NEW
925
 return Tjsonb_path_match_common(fcinfo, false);
×
926
}
927

928
PGDLLEXPORT Datum Tjsonb_path_match_tz(PG_FUNCTION_ARGS);
929
PG_FUNCTION_INFO_V1(Tjsonb_path_match_tz);
1✔
930
/**
931
 * @ingroup mobilitydb_json_json
932
 * @brief Extract an item specified by a JSON path predicate from a temporal
933
 * JSONB value
934
 * @sqlfn tjsonb_path_match_tz()
935
 */
936
Datum
NEW
937
Tjsonb_path_match_tz(PG_FUNCTION_ARGS)
×
938
{
NEW
939
  return Tjsonb_path_match_common(fcinfo, true);
×
940
}
941

942
/*****************************************************************************/
943

944
/**
945
 * @brief Extract the items specified by a JSON path predicate from a
946
 * temporal JSONB value as a JSONB array
947
 * @sqlfn tjsonb_path_query_array(), tjsonb_path_query_array_tz()
948
 */
949
Datum
NEW
950
Tjsonb_path_query_array_common(FunctionCallInfo fcinfo, bool tz)
×
951
{
NEW
952
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
NEW
953
  JsonPath *jp = PG_GETARG_JSONPATH_P(1);
×
NEW
954
  Jsonb *vars = PG_GETARG_JSONB_P(2);
×
NEW
955
  bool silent = PG_GETARG_BOOL(3);
×
NEW
956
  Temporal *result = tjsonb_path_query_array(temp, jp, vars, silent, tz);
×
NEW
957
  PG_FREE_IF_COPY(temp, 0);
×
NEW
958
  PG_FREE_IF_COPY(jp, 1);
×
NEW
959
  if (! result)
×
NEW
960
    PG_RETURN_NULL();
×
NEW
961
  PG_RETURN_TEMPORAL_P(result);
×
962
}
963

964
PGDLLEXPORT Datum Tjsonb_path_query_array(PG_FUNCTION_ARGS);
965
PG_FUNCTION_INFO_V1(Tjsonb_path_query_array);
1✔
966
/**
967
 * @ingroup mobilitydb_json_json
968
 * @brief Extract the items specified by a JSON path predicate from a
969
 * temporal JSONB value as a JSONB array
970
 * @sqlfn tjsonb_path_query_array()
971
 */
972
Datum
NEW
973
Tjsonb_path_query_array(PG_FUNCTION_ARGS)
×
974
{
NEW
975
 return Tjsonb_path_query_array_common(fcinfo, false);
×
976
}
977

978
PGDLLEXPORT Datum Tjsonb_path_query_array_tz(PG_FUNCTION_ARGS);
979
PG_FUNCTION_INFO_V1(Tjsonb_path_query_array_tz);
1✔
980
/**
981
 * @ingroup mobilitydb_json_json
982
 * @brief Extract the items specified by a JSON path predicate from a
983
 * temporal JSONB value as a JSONB array
984
 * @sqlfn tjsonb_path_query_array_tz()
985
 */
986
Datum
NEW
987
Tjsonb_path_query_array_tz(PG_FUNCTION_ARGS)
×
988
{
NEW
989
  return Tjsonb_path_query_array_common(fcinfo, true);
×
990
}
991

992
/*****************************************************************************/
993

994
/**
995
 * @brief Extract the first item specified by a JSON path predicate from a
996
 * temporal JSONB value. If there are no items, return NULL.
997
 * @sqlfn tjsonb_query_first(), tjsonb_query_first_tz()
998
 */
999
Datum
NEW
1000
Tjsonb_path_query_first_common(FunctionCallInfo fcinfo, bool tz)
×
1001
{
NEW
1002
  Temporal *temp = PG_GETARG_TEMPORAL_P(0);
×
NEW
1003
  JsonPath *jp = PG_GETARG_JSONPATH_P(1);
×
NEW
1004
  Jsonb *vars = PG_GETARG_JSONB_P(2);
×
NEW
1005
  bool silent = PG_GETARG_BOOL(3);
×
NEW
1006
  Temporal *result = tjsonb_path_query_first(temp, jp, vars, silent, tz);
×
NEW
1007
  PG_FREE_IF_COPY(temp, 0);
×
NEW
1008
  PG_FREE_IF_COPY(jp, 1);
×
NEW
1009
  if (! result)
×
NEW
1010
    PG_RETURN_NULL();
×
NEW
1011
  PG_RETURN_TEMPORAL_P(result);
×
1012
}
1013

1014
PGDLLEXPORT Datum Tjsonb_path_query_first(PG_FUNCTION_ARGS);
1015
PG_FUNCTION_INFO_V1(Tjsonb_path_query_first);
1✔
1016
/**
1017
 * @ingroup mobilitydb_json_json
1018
 * @brief Extract the first item specified by a JSON path predicate from a
1019
 * temporal JSONB value. If there are no items, return NULL.
1020
 * @sqlfn tjsonb_path_query_first()
1021
 */
1022
Datum
NEW
1023
Tjsonb_path_query_first(PG_FUNCTION_ARGS)
×
1024
{
NEW
1025
 return Tjsonb_path_query_first_common(fcinfo, false);
×
1026
}
1027

1028
PGDLLEXPORT Datum Tjsonb_path_query_first_tz(PG_FUNCTION_ARGS);
1029
PG_FUNCTION_INFO_V1(Tjsonb_path_query_first_tz);
1✔
1030
/**
1031
 * @ingroup mobilitydb_json_json
1032
 * @brief Extract the first item specified by a JSON path predicate from a
1033
 * temporal JSONB value. If there are no items, return NULL.
1034
 * @sqlfn tjsonb_path_query_first_tz()
1035
 */
1036
Datum
NEW
1037
Tjsonb_path_query_first_tz(PG_FUNCTION_ARGS)
×
1038
{
NEW
1039
  return Tjsonb_path_query_first_common(fcinfo, true);
×
1040
}
1041

1042
/*****************************************************************************/
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc