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

estebanzimanyi / MobilityDB / 20701044430

04 Jan 2026 11:51PM UTC coverage: 95.32%. Remained the same
20701044430

push

github

estebanzimanyi
Next

365 of 369 new or added lines in 23 files covered. (98.92%)

2 existing lines in 2 files now uncovered.

32244 of 33827 relevant lines covered (95.32%)

1225298.14 hits per line

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

97.24
/meos/src/temporal/span.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 General functions for spans (a.k.a. ranges) composed of two `Datum`
33
 * values and two `Boolean` values stating whether the bounds are inclusive
34
 */
35

36
#include "temporal/span.h"
37

38
/* C */
39
#include <assert.h>
40
#include <float.h>
41
#include <limits.h>
42
/* PostgreSQL */
43
#include <postgres.h>
44
#include <common/hashfn.h>
45
#include "port/pg_bitutils.h"
46
#include <utils/float.h>
47
#include <utils/timestamp.h>
48
/* MEOS */
49
#include <meos.h>
50
#include <meos_internal.h>
51
#include "temporal/meos_catalog.h"
52
#include "temporal/set.h"
53
#include "temporal/temporal.h"
54
#include "temporal/tnumber_mathfuncs.h"
55
#include "temporal/type_parser.h"
56
#include "temporal/type_inout.h"
57
#include "temporal/type_util.h"
58

59
#include <utils/jsonb.h>
60
#include <utils/numeric.h>
61
#include <pgtypes.h>
62

63
/*****************************************************************************
64
 * Parameter tests
65
 *****************************************************************************/
66

67
/**
68
 * @brief Ensure that a span is of a given span type
69
 */
70
bool
71
ensure_span_isof_type(const Span *sp, meosType spantype)
79,950✔
72
{
73
  if (sp->spantype == spantype)
79,950✔
74
    return true;
75
  meos_error(ERROR, MEOS_ERR_INVALID_ARG_TYPE,
×
76
    "The span must be of type %s", meostype_name(spantype));
77
  return false;
×
78
}
79

80
/**
81
 * @brief Ensure that a span is of a given base type
82
 */
83
bool
84
ensure_span_isof_basetype(const Span *sp, meosType basetype)
98✔
85
{
86
  if (sp->basetype == basetype)
98✔
87
    return true;
88
  meos_error(ERROR, MEOS_ERR_INVALID_ARG_TYPE,
×
89
    "Operation on mixed span and base types: %s and %s",
90
    meostype_name(sp->spantype), meostype_name(basetype));
×
91
  return false;
×
92
}
93

94
/**
95
 * @brief Ensure that the spans have the same type
96
 */
97
bool
98
ensure_same_span_type(const Span *sp1, const Span *sp2)
90,688,123✔
99
{
100
  if (sp1->spantype == sp2->spantype)
90,688,123✔
101
    return true;
102
  meos_error(ERROR, MEOS_ERR_INVALID_ARG_TYPE,
×
103
    "Operation on mixed span types: %s and %s",
NEW
104
    meostype_name(sp1->spantype), meostype_name(sp2->spantype));
×
105
  return false;
×
106
}
107

108
/**
109
 * @brief Ensure that two span sets are of the same span type
110
 */
111
bool
112
ensure_valid_span_span(const Span *sp1, const Span *sp2)
88,569,301✔
113
{
114
  VALIDATE_NOT_NULL(sp1, false); VALIDATE_NOT_NULL(sp2, false);
115
  if (! ensure_same_span_type(sp1, sp2))
88,569,301✔
116
    return false;
117
  return true;
118
}
119

120
/*****************************************************************************
121
 * General functions
122
 *****************************************************************************/
123

124
/**
125
 * @brief Deconstruct a span
126
 * @param[in] sp Span value
127
 * @param[out] lower,upper Bounds
128
 */
129
void
130
span_deserialize(const Span *sp, SpanBound *lower, SpanBound *upper)
11,478,142✔
131
{
132
  if (lower)
11,478,142✔
133
  {
134
    lower->val = sp->lower;
11,478,142✔
135
    lower->inclusive = sp->lower_inc;
11,478,142✔
136
    lower->lower = true;
11,478,142✔
137
    lower->spantype = sp->spantype;
11,478,142✔
138
    lower->basetype = sp->basetype;
11,478,142✔
139
  }
140
  if (upper)
11,478,142✔
141
  {
142
    upper->val = sp->upper;
11,478,142✔
143
    upper->inclusive = sp->upper_inc;
11,478,142✔
144
    upper->lower = false;
11,478,142✔
145
    upper->spantype = sp->spantype;
11,478,142✔
146
    upper->basetype = sp->basetype;
11,478,142✔
147
  }
148
  return;
11,478,142✔
149
}
150

151
/**
152
 * @brief Compare two span boundary points, returning <0, 0, or >0 according to
153
 * whether the first one is less than, equal to, or greater than the second one
154
 * @details The boundaries can be any combination of upper and lower; so it is
155
 * useful for a variety of operators.
156
 *
157
 * The simple case is when b1 and b2 are both inclusive, in which
158
 * case the result is just a comparison of the values held in b1 and b2.
159
 *
160
 * If a bound is exclusive, then we need to know whether it is a lower bound,
161
 * in which case we treat the boundary point as "just greater than" the held
162
 * value; or an upper bound, in which case we treat the boundary point as
163
 * "just less than" the held value.
164
 *
165
 * There is only one case where two boundaries compare equal but are not
166
 * identical: when both bounds are inclusive and hold the same value,
167
 * but one is an upper bound and the other a lower bound.
168
 */
169
int
170
span_bound_cmp(const SpanBound *b1, const SpanBound *b2)
41,086,487✔
171
{
172
  assert(b1); assert(b2); assert(b1->basetype == b2->basetype);
173
  /* Compare the values */
174
  int32 result = datum_cmp(b1->val, b2->val, b1->basetype);
41,086,487✔
175

176
  /*
177
   * If the comparison is not equal and the bounds are both inclusive or
178
   * both exclusive, we're done. If they compare equal, we still have to
179
   * consider whether the boundaries are inclusive or exclusive.
180
  */
181
  if (result == 0)
41,086,487✔
182
  {
183
    if (! b1->inclusive && ! b2->inclusive)
1,682,385✔
184
    {
185
      /* both bounds are exclusive */
186
      if (b1->lower == b2->lower)
567,057✔
187
        /* both are lower bound */
188
        return 0;
189
      else
190
        return b1->lower ? 1 : -1;
228✔
191
    }
192
    else if (! b1->inclusive)
1,115,328✔
193
      return b1->lower ? 1 : -1;
10,159✔
194
    else if (! b2->inclusive)
1,105,169✔
195
      return b2->lower ? -1 : 1;
10,755✔
196
  }
197

198
  return result;
199
}
200

201
/**
202
 * @brief Comparison function for sorting span bounds
203
 */
204
int
205
span_bound_qsort_cmp(const void *a1, const void *a2)
23,768,041✔
206
{
207
  SpanBound *b1 = (SpanBound *) a1;
208
  SpanBound *b2 = (SpanBound *) a2;
209
  return span_bound_cmp(b1, b2);
23,768,041✔
210
}
211

212
/**
213
 * @brief Compare the lower bounds of two spans, returning <0, 0, or >0 according to
214
 * whether the first bound is less than, equal to, or greater than the second one
215
 * @note The function is equivalent to #span_bound_cmp but avoids
216
 * deserializing the spans into lower and upper bounds
217
 */
218
int
219
span_lower_cmp(const Span *sp1, const Span *sp2)
1,385,973✔
220
{
221
  assert(sp1); assert(sp2); assert(sp1->basetype == sp2->basetype);
222
  int result = datum_cmp(sp1->lower, sp2->lower, sp1->basetype);
1,385,973✔
223
  if (result != 0)
1,385,973✔
224
    return result;
225
  /* The bound values are equal */
226
  if (sp1->lower_inc == sp2->lower_inc)
31,013✔
227
    /* both are inclusive or exclusive */
228
    return 0;
229
  else if (sp1->lower_inc)
40✔
230
    /* first is inclusive and second is exclusive */
231
    return 1;
232
  else
233
    /* first is exclusive and second is inclusive */
234
    return -1;
22✔
235
}
236

237
/**
238
 * @brief Compare the upper bounds of two spans, returning <0, 0, or >0
239
 * according to whether the first bound is less than, equal to, or greater than
240
 * the second one.
241
 * @note The function is equivalent to #span_bound_cmp but avoids
242
 * deserializing the spans into lower and upper bounds
243
 */
244
int
245
span_upper_cmp(const Span *sp1, const Span *sp2)
1,384,190✔
246
{
247
  assert(sp1); assert(sp2); assert(sp1->basetype == sp2->basetype);
248
  int result = datum_cmp(sp1->upper, sp2->upper, sp1->basetype);
1,384,190✔
249
  if (result != 0)
1,384,190✔
250
    return result;
251
  /* The bound values are equal */
252
  if (sp1->upper_inc == sp2->upper_inc)
25,510✔
253
    /* both are inclusive or exclusive */
254
    return 0;
255
  else if (sp1->upper_inc)
48✔
256
    /* first is inclusive and second is exclusive */
257
    return 1;
258
  else
259
    /* first is exclusive and second is inclusive */
260
    return -1;
21✔
261
}
262

263
/**
264
 * @brief Return the bound increased by 1 for accounting for canonicalized spans
265
 */
266
Datum
267
span_incr_bound(Datum lower, meosType basetype)
30,185,048✔
268
{
269
  Datum result;
270
  switch (basetype)
30,185,048✔
271
  {
272
    case T_INT4:
273
      result = Int32GetDatum(DatumGetInt32(lower) + (int32) 1);
15,408,063✔
274
      break;
15,408,063✔
275
    case T_INT8:
276
      result = Int64GetDatum(DatumGetInt64(lower) + (int64) 1);
13,824,166✔
277
      break;
13,824,166✔
278
    case T_DATE:
279
      result = DateADTGetDatum(DatumGetDateADT(lower) + 1);
952,819✔
280
      break;
952,819✔
281
    default:
282
      result = lower;
283
  }
284
  return result;
30,185,048✔
285
}
286

287
/**
288
 * @brief Return the bound decreased by 1 for accounting for canonicalized spans
289
 */
290
Datum
291
span_decr_bound(Datum lower, meosType basetype)
4,423,593✔
292
{
293
  Datum result;
294
  switch (basetype)
4,423,593✔
295
  {
296
    case T_INT4:
297
      result = Int32GetDatum(DatumGetInt32(lower) - (int32) 1);
1,961,975✔
298
      break;
1,961,975✔
299
    case T_INT8:
300
      result = Int64GetDatum(DatumGetInt64(lower) - (int64) 1);
1,887,547✔
301
      break;
1,887,547✔
302
    case T_DATE:
303
      result = DateADTGetDatum(DatumGetDateADT(lower) - 1);
127,058✔
304
      break;
127,058✔
305
    default:
306
      result = lower;
307
  }
308
  return result;
4,423,593✔
309
}
310

311
/**
312
 * @brief Normalize an array of spans
313
 * @details The input spans may overlap and may be non contiguous.
314
 * The normalized spans are new spans that must be freed.
315
 * @param[in] spans Array of spans
316
 * @param[in] count Number of elements in the input array
317
 * @param[in] order True if the spans should be ordered
318
 * @param[out] newcount Number of elements in the output array
319
 * @pre @p count is greater than 0
320
 */
321
Span *
322
spanarr_normalize(Span *spans, int count, bool order, int *newcount)
176,820✔
323
{
324
  assert(spans); assert(count > 0); assert(newcount);
325
  /* Sort the spans if they are not ordered */
326
  if (order)
176,820✔
327
    spanarr_sort(spans, count);
162,689✔
328
  int nspans = 0;
329
  Span *result = palloc(sizeof(Span) * count);
176,820✔
330
  Span *current = &spans[0];
331
  for (int i = 1; i < count; i++)
1,237,283✔
332
  {
333
    Span *next = &spans[i];
1,060,463✔
334
    if (ovadj_span_span(current, next))
1,060,463✔
335
      /* Compute the union of the spans */
336
      span_expand(next, current);
13,827✔
337
    else
338
    {
339
      result[nspans++] = *current;
1,046,636✔
340
      current = next;
341
    }
342
  }
343
  result[nspans++] = *current;
176,820✔
344
  /* Set the output parameter */
345
  *newcount = nspans;
176,820✔
346
  return result;
176,820✔
347
}
348

349
/*****************************************************************************
350
 * Input/output functions
351
 *****************************************************************************/
352

353
/**
354
 * @ingroup meos_internal_setspan_inout
355
 * @brief Return a span from its Well-Known Text (WKT) representation
356
 * @param[in] str String
357
 * @param[in] spantype Span type
358
 */
359
Span *
360
span_in(const char *str, meosType spantype)
44,867✔
361
{
362
  assert(str);
363
  Span result;
364
  if (! span_parse(&str, spantype, true, &result))
44,867✔
365
    return NULL;
366
  return span_copy(&result);
44,862✔
367
}
368

369
/**
370
 * @brief Remove the quotes from the Well-Known Text (WKT) representation of a
371
 * span
372
 */
373
static char *
374
unquote(char *str)
375
{
376
  /* Save the initial pointer */
377
  char *result = str;
378
  char *last = str;
379
  while (*str != '\0')
131,422✔
380
  {
381
    if (*str != '"')
125,336✔
382
      *last++ = *str;
125,336✔
383
    str++;
125,336✔
384
  }
385
  *last = '\0';
6,086✔
386
  return result;
387
}
388

389
/**
390
 * @ingroup meos_internal_setspan_inout
391
 * @brief Return the Well-Known Text (WKT) representation of a span
392
 * @param[in] sp Span
393
 * @param[in] maxdd Maximum number of decimal digits
394
 */
395
char *
396
span_out(const Span *sp, int maxdd)
3,044✔
397
{
398
  assert(sp);
399
  /* Ensure the validity of the arguments */
400
  if (! ensure_not_negative(maxdd))
3,044✔
401
    return NULL;
402

403
  char *lower = unquote(basetype_out(sp->lower, sp->basetype, maxdd));
3,043✔
404
  char *upper = unquote(basetype_out(sp->upper, sp->basetype, maxdd));
3,043✔
405
  char open = sp->lower_inc ? (char) '[' : (char) '(';
3,043✔
406
  char close = sp->upper_inc ? (char) ']' : (char) ')';
3,043✔
407
  size_t size = strlen(lower) + strlen(upper) + 5;
3,043✔
408
  char *result = palloc(size);
3,043✔
409
  snprintf(result, size, "%c%s, %s%c", open, lower, upper, close);
3,043✔
410
  pfree(lower); pfree(upper);
3,043✔
411
  return result;
3,043✔
412
}
413

414
/*****************************************************************************
415
 * Constructor functions
416
 *****************************************************************************/
417

418
/**
419
 * @ingroup meos_internal_setspan_constructor
420
 * @brief Return a span from the bounds
421
 * @param[in] lower,upper Bounds
422
 * @param[in] lower_inc,upper_inc True when the bounds are inclusive
423
 * @param[in] basetype Type of the bounds
424
 */
425
Span *
426
span_make(Datum lower, Datum upper, bool lower_inc, bool upper_inc,
93,261✔
427
  meosType basetype)
428
{
429
  Span *sp = palloc(sizeof(Span));
93,261✔
430
  meosType spantype = basetype_spantype(basetype);
93,261✔
431
  span_set(lower, upper, lower_inc, upper_inc, basetype, spantype, sp);
93,261✔
432
  return sp;
93,259✔
433
}
434

435
/**
436
 * @ingroup meos_internal_setspan_constructor
437
 * @brief Return in the last argument a span constructed from the given
438
 * arguments
439
 * @param[in] lower,upper Bounds
440
 * @param[in] lower_inc,upper_inc True when the bounds are inclusive
441
 * @param[in] basetype Base type
442
 * @param[in] spantype Span type
443
 * @param[out] result Result span
444
 * @see #span_make()
445
 */
446
void
447
span_set(Datum lower, Datum upper, bool lower_inc, bool upper_inc,
60,838,439✔
448
  meosType basetype, meosType spantype, Span *result)
449
{
450
  assert(result); assert(basetype_spantype(basetype) == spantype);
451
  /* Canonicalize */
452
  if (span_canon_basetype(basetype))
60,838,439✔
453
  {
454
    if (! lower_inc)
30,893,975✔
455
    {
456
      lower = span_incr_bound(lower, basetype);
99,991✔
457
      lower_inc = true;
458
    }
459
    if (upper_inc)
30,893,975✔
460
    {
461
      upper = span_incr_bound(upper, basetype);
30,085,057✔
462
      upper_inc = false;
463
    }
464
  }
465

466
  int cmp = datum_cmp(lower, upper, basetype);
60,838,439✔
467
  /* error check: if lower bound value is above upper, it's wrong */
468
  if (cmp > 0)
60,838,439✔
469
  {
470
    meos_error(ERROR, MEOS_ERR_INVALID_ARG_VALUE,
2✔
471
      "Span lower bound must be less than or equal to span upper bound");
472
    return;
×
473
  }
474

475
  /* error check: if bounds are equal, and not both inclusive, span is empty */
476
  if (cmp == 0 && ! (lower_inc && upper_inc))
60,838,437✔
477
  {
478
    meos_error(ERROR, MEOS_ERR_INVALID_ARG_VALUE, "Span cannot be empty");
1✔
479
    return;
×
480
  }
481

482
  /* Note: zero-fill is required here, just as in heap tuples */
483
  memset(result, 0, sizeof(Span));
484
  /* Fill in the span */
485
  result->lower = lower;
60,838,436✔
486
  result->upper = upper;
60,838,436✔
487
  result->lower_inc = lower_inc;
60,838,436✔
488
  result->upper_inc = upper_inc;
60,838,436✔
489
  result->spantype = spantype;
60,838,436✔
490
  result->basetype = basetype;
60,838,436✔
491
  return;
60,838,436✔
492
}
493

494
/**
495
 * @ingroup meos_setspan_constructor
496
 * @brief Return a copy of a span
497
 * @param[in] sp Span
498
 */
499
Span *
500
span_copy(const Span *sp)
5,426,539✔
501
{
502
  /* Ensure the validity of the arguments */
503
  VALIDATE_NOT_NULL(sp, NULL);
504
  Span *result = palloc(sizeof(Span));
5,426,539✔
505
  memcpy((char *) result, (char *) sp, sizeof(Span));
506
  return result;
5,426,539✔
507
}
508

509
/*****************************************************************************
510
 * Conversion functions
511
 *****************************************************************************/
512

513
/**
514
 * @ingroup meos_internal_setspan_conversion
515
 * @brief Return in the last argument a span constructed from a value
516
 * @param[in] value Value
517
 * @param[in] basetype Type of the value
518
 * @param[out] sp Result span
519
*/
520
void
521
value_set_span(Datum value, meosType basetype, Span *sp)
94,630✔
522
{
523
  assert(sp); assert(span_basetype(basetype));
524
  meosType spantype = basetype_spantype(basetype);
94,630✔
525
  span_set(value, value, true, true, basetype, spantype, sp);
94,630✔
526
  return;
94,630✔
527
}
528

529
/**
530
 * @ingroup meos_internal_setspan_conversion
531
 * @brief Convert a value into a span
532
 * @param[in] value Value
533
 * @param[in] basetype Type of the value
534
 */
535
Span *
536
value_span(Datum value, meosType basetype)
3,440✔
537
{
538
  Span *result = palloc(sizeof(Span));
3,440✔
539
  value_set_span(value, basetype, result);
3,440✔
540
  return result;
3,440✔
541
}
542

543
/**
544
 * @ingroup meos_internal_setspan_conversion
545
 * @brief Return in the last argument the bounding span of a set
546
 * @param[in] s Set
547
 * @param[in] fromidx,toidx From and to indexes of the values used for the span
548
 * bounds
549
 * @param[in] result Span
550
 */
551
void
552
set_set_subspan(const Set *s, int fromidx, int toidx, Span *result)
26,076,856✔
553
{
554
  assert(s); assert(result);
555
  meosType spantype = basetype_spantype(s->basetype);
26,076,856✔
556
  span_set(SET_VAL_N(s, fromidx), SET_VAL_N(s, toidx), true, true,
26,076,856✔
557
    s->basetype, spantype, result);
26,076,856✔
558
  return;
26,076,856✔
559
}
560

561
/**
562
 * @ingroup meos_internal_setspan_conversion
563
 * @brief Return in the last argument the bounding span of a set
564
 * @param[in] s Set
565
 * @param[in] result Span
566
 */
567
void
568
set_set_span(const Set *s, Span *result)
26,076,815✔
569
{
570
  assert(s); assert(result);
571
  return set_set_subspan(s, 0, s->count - 1, result);
26,076,815✔
572
}
573

574
/**
575
 * @ingroup meos_internal_setspan_conversion
576
 * @brief Convert a set into a span
577
 * @param[in] s Set
578
 * @csqlfn #Set_to_span()
579
 */
580
Span *
581
set_span(const Set *s)
31,048✔
582
{
583
  /* Ensure the validity of the arguments */
584
  assert(s); assert(set_spantype(s->settype));
585
  Span *result = palloc(sizeof(Span));
31,048✔
586
  set_set_span(s, result);
31,048✔
587
  return result;
31,048✔
588
}
589

590
#if MEOS
591
/**
592
 * @ingroup meos_setspan_conversion
593
 * @brief Convert a set into a span
594
 * @param[in] s Set
595
 * @csqlfn #Set_to_span()
596
 */
597
Span *
598
set_to_span(const Set *s)
599
{
600
  /* Ensure the validity of the arguments */
601
  VALIDATE_NOT_NULL(s, NULL);
602
  if (! ensure_set_spantype(s->settype))
603
    return NULL;
604
  return set_span(s);
605
}
606
#endif /* MEOS */
607

608
/**
609
 * @ingroup meos_internal_setspan_conversion
610
 * @brief Return the second span initialized with the first one transformed to
611
 * a float span
612
 * @param[in] sp1,sp2 Spans
613
 */
614
void
615
intspan_set_floatspan(const Span *sp1, Span *sp2)
1,014✔
616
{
617
  assert(sp1); assert(sp2); assert(sp1->spantype == T_INTSPAN);
618
  Datum lower = Float8GetDatum((double) DatumGetInt32(sp1->lower));
1,014✔
619
  Datum upper = Float8GetDatum((double) (DatumGetInt32(sp1->upper) - 1));
1,014✔
620
  span_set(lower, upper, true, true, T_FLOAT8, T_FLOATSPAN, sp2);
1,014✔
621
  return;
1,014✔
622
}
623

624
/**
625
 * @ingroup meos_setspan_conversion
626
 * @brief Convert an integer span into a float span
627
 * @param[in] sp Span
628
 * @return On error return @p NULL
629
 */
630
Span *
631
intspan_to_floatspan(const Span *sp)
297✔
632
{
633
  /* Ensure the validity of the arguments */
634
  VALIDATE_INTSPAN(sp, NULL);
635
  Span *result = palloc(sizeof(Span));
297✔
636
  intspan_set_floatspan(sp, result);
297✔
637
  return result;
297✔
638
}
639

640
/**
641
 * @ingroup meos_internal_setspan_conversion
642
 * @brief Return the second span initialized with the first one transformed to
643
 * an integer span
644
 * @param[in] sp1,sp2 Spans
645
 */
646
void
647
floatspan_set_intspan(const Span *sp1, Span *sp2)
1,014✔
648
{
649
  assert(sp1); assert(sp2); assert(sp1->spantype == T_FLOATSPAN);
650
  Datum lower = Int32GetDatum((int) DatumGetFloat8(sp1->lower));
1,014✔
651
  Datum upper = Int32GetDatum((int) (DatumGetFloat8(sp1->upper)));
1,014✔
652
  span_set(lower, upper, sp1->lower_inc, sp1->upper_inc, T_INT4, T_INTSPAN, sp2);
1,014✔
653
  return;
1,014✔
654
}
655

656
/**
657
 * @ingroup meos_setspan_conversion
658
 * @brief Convert a float span into an integer span
659
 * @param[in] sp Span
660
 * @return On error return @p NULL
661
 */
662
Span *
663
floatspan_to_intspan(const Span *sp)
297✔
664
{
665
  VALIDATE_FLOATSPAN(sp, NULL);
666
  Span *result = palloc(sizeof(Span));
297✔
667
  floatspan_set_intspan(sp, result);
297✔
668
  return result;
297✔
669
}
670

671
/**
672
 * @ingroup meos_internal_setspan_conversion
673
 * @brief Return the second span initialized with the first one transformed to
674
 * a timetstamptz span
675
 * @param[in] sp1,sp2 Spans
676
 */
677
void
678
datespan_set_tstzspan(const Span *sp1, Span *sp2)
2,541✔
679
{
680
  assert(sp1); assert(sp2); assert(sp1->spantype == T_DATESPAN);
681
  Datum lower =
682
    TimestampTzGetDatum(date_to_timestamptz(DatumGetDateADT(sp1->lower)));
2,541✔
683
  Datum upper =
684
    TimestampTzGetDatum(date_to_timestamptz(DatumGetDateADT(sp1->upper)));
2,541✔
685
  /* Date spans are always canonicalized */
686
  span_set(lower, upper, true, false, T_TIMESTAMPTZ, T_TSTZSPAN, sp2);
2,541✔
687
  return;
2,541✔
688
}
689

690
/**
691
 * @ingroup meos_setspan_conversion
692
 * @brief Convert a date span into a timestamptz span
693
 * @param[in] sp Span
694
 * @return On error return @p NULL
695
 */
696
Span *
697
datespan_to_tstzspan(const Span *sp)
297✔
698
{
699
  /* Ensure the validity of the arguments */
700
  VALIDATE_DATESPAN(sp, NULL);
701
  Span *result = palloc(sizeof(Span));
297✔
702
  datespan_set_tstzspan(sp, result);
297✔
703
  return result;
297✔
704
}
705

706
/**
707
 * @ingroup meos_internal_setspan_conversion
708
 * @brief Return the last span initialized with the first one transformed to a
709
 * date span
710
 * @param[in] sp1,sp2 Spans
711
 */
712
void
713
tstzspan_set_datespan(const Span *sp1, Span *sp2)
2,005✔
714
{
715
  assert(sp1); assert(sp2); assert(sp1->spantype == T_TSTZSPAN);
716
  DateADT lower = timestamptz_to_date(DatumGetTimestampTz(sp1->lower));
2,005✔
717
  DateADT upper = timestamptz_to_date(DatumGetTimestampTz(sp1->upper));
2,005✔
718
  bool lower_inc = sp1->lower_inc;
2,005✔
719
  bool upper_inc = sp1->upper_inc;
2,005✔
720
  /* Both bounds are set to true when the resulting dates are equal, e.g.,
721
   * (2001-10-18 19:46:00, 2001-10-18 19:50:00) -> [2001-10-18, 2001-10-18] */
722
  if (lower == upper)
2,005✔
723
  {
724
    lower_inc = upper_inc = true;
725
  }
726
  /* Canonicalization takes place in the following function */
727
  span_set(DateADTGetDatum(lower), DateADTGetDatum(upper), lower_inc,
2,005✔
728
    upper_inc, T_DATE, T_DATESPAN, sp2);
729
  return;
2,005✔
730
}
731

732
/**
733
 * @ingroup meos_setspan_conversion
734
 * @brief Convert a timestamptz span into a date span
735
 * @param[in] sp Span
736
 * @return On error return @p NULL
737
 */
738
Span *
739
tstzspan_to_datespan(const Span *sp)
297✔
740
{
741
  /* Ensure the validity of the arguments */
742
  VALIDATE_TSTZSPAN(sp, NULL);
743
  Span *result = palloc(sizeof(Span));
297✔
744
  tstzspan_set_datespan(sp, result);
297✔
745
  return result;
297✔
746
}
747

748
/*****************************************************************************
749
 * Accessor functions
750
 *****************************************************************************/
751

752
/**
753
 * @ingroup meos_internal_setspan_accessor
754
 * @brief Return the width of a span
755
 * @param[in] sp Span
756
 * @csqlfn #Numspan_width()
757
 */
758
Datum
759
numspan_width(const Span *sp)
1,657✔
760
{
761
  assert(sp);
762
  return distance_value_value(sp->upper, sp->lower, sp->basetype);
1,657✔
763
}
764

765
/**
766
 * @ingroup meos_setspan_accessor
767
 * @brief Return the duration of a date span as an interval
768
 * @param[in] sp Span
769
 * @csqlfn #Datespan_duration()
770
 */
771
Interval *
772
datespan_duration(const Span *sp)
99✔
773
{
774
  /* Ensure the validity of the arguments */
775
  VALIDATE_DATESPAN(sp, NULL);
776
  Interval *result = palloc0(sizeof(Interval));
99✔
777
  result->day = DateADTGetDatum(sp->upper) - DateADTGetDatum(sp->lower);
99✔
778
  return result;
99✔
779
}
780

781
/**
782
 * @ingroup meos_setspan_accessor
783
 * @brief Return the duration of a timestamptz span as an interval
784
 * @param[in] sp Span
785
 * @csqlfn #Tstzspan_duration()
786
 */
787
Interval *
788
tstzspan_duration(const Span *sp)
58,549✔
789
{
790
  /* Ensure the validity of the arguments */
791
  VALIDATE_TSTZSPAN(sp, NULL);
792
  return minus_timestamptz_timestamptz(sp->upper, sp->lower);
58,549✔
793
}
794

795
/*****************************************************************************
796
 * Transformation functions
797
 *****************************************************************************/
798

799
/**
800
 * @ingroup meos_internal_setspan_transf
801
 * @brief Return in the last argument a float span with the precision set to a
802
 * number of decimal places
803
 * @param[in] sp Span
804
 * @param[in] maxdd Maximum number of decimal digits
805
 * @param[out] result Result span
806
 */
807
void
808
floatspan_round_set(const Span *sp, int maxdd, Span *result)
1,589✔
809
{
810
  assert(sp); assert(sp->spantype == T_FLOATSPAN); assert(result);
811
  /* Set precision of bounds */
812
  double lower = float8_round(DatumGetFloat8(sp->lower), maxdd);
1,589✔
813
  double upper = float8_round(DatumGetFloat8(sp->upper), maxdd);
1,589✔
814
  /* Fix the bounds */
815
  bool lower_inc, upper_inc;
816
  if (float8_eq(lower, upper))
×
817
  {
818
    lower_inc = upper_inc = true;
819
  }
820
  else
821
  {
822
    lower_inc = sp->lower_inc; upper_inc = sp->upper_inc;
1,587✔
823
  }
824
  /* Set resulting span */
825
  span_set(Float8GetDatum(lower), Float8GetDatum(upper), lower_inc, upper_inc,
1,589✔
826
    sp->basetype, sp->spantype, result);
1,589✔
827
  return;
1,589✔
828
}
829

830
/**
831
 * @ingroup meos_setspan_transf
832
 * @brief Return a float span with the precision of the bounds set to a
833
 * number of decimal places
834
 * @param[in] sp Span
835
 * @param[in] maxdd Maximum number of decimal digits
836
 * @return On error return @p NULL
837
 */
838
Span *
839
floatspan_round(const Span *sp, int maxdd)
106✔
840
{
841
  /* Ensure the validity of the arguments */
842
  VALIDATE_FLOATSPAN(sp, NULL);
843
  if (! ensure_not_negative(maxdd))
106✔
844
    return NULL;
845

846
  Span *result = palloc(sizeof(Span));
106✔
847
  floatspan_round_set(sp, maxdd, result);
106✔
848
  return result;
106✔
849
}
850

851
/*****************************************************************************/
852

853
/**
854
 * @brief Round down a span to the nearest integer
855
 */
856
void
857
floatspan_floor_ceil_iter(Span *sp, datum_func1 func)
10✔
858
{
859
  assert(sp);
860
  Datum lower = func(sp->lower);
10✔
861
  Datum upper = func(sp->upper);
10✔
862
  bool lower_inc = sp->lower_inc;
10✔
863
  bool upper_inc = sp->upper_inc;
10✔
864
  if (datum_eq(lower, upper, sp->basetype))
10✔
865
    lower_inc = upper_inc = true;
866
  span_set(lower, upper, lower_inc, upper_inc, sp->basetype, sp->spantype, sp);
10✔
867
  return;
10✔
868
}
869

870
/**
871
 * @ingroup meos_setspan_transf
872
 * @brief Return a float span rounded down to the nearest integer
873
 * @csqlfn #Floatspan_floor()
874
 */
875
Span *
876
floatspan_floor(const Span *sp)
2✔
877
{
878
  /* Ensure the validity of the arguments */
879
  VALIDATE_FLOATSPAN(sp, NULL);
880
  Span *result = span_copy(sp);
2✔
881
  floatspan_floor_ceil_iter(result, &datum_floor);
2✔
882
  return result;
2✔
883
}
884

885
/**
886
 * @ingroup meos_setspan_transf
887
 * @brief Return a float span rounded up to the nearest integer
888
 * @csqlfn #Floatspan_ceil()
889
 */
890
Span *
891
floatspan_ceil(const Span *sp)
2✔
892
{
893
  /* Ensure the validity of the arguments */
894
  VALIDATE_FLOATSPAN(sp, NULL);
895
  Span *result = span_copy(sp);
2✔
896
  floatspan_floor_ceil_iter(result, &datum_ceil);
2✔
897
  return result;
2✔
898
}
899

900
/**
901
 * @ingroup meos_setspan_transf
902
 * @brief Return a float span with the values converted to degrees
903
 * @param[in] sp Span
904
 * @param[in] normalize True when the result must be normalized
905
 * @csqlfn #Floatspan_degrees()
906
 */
907
Span *
908
floatspan_degrees(const Span *sp, bool normalize)
198✔
909
{
910
  /* Ensure the validity of the arguments */
911
  VALIDATE_FLOATSPAN(sp, NULL);
912
  Span *result = span_copy(sp);
198✔
913
  result->lower = datum_degrees(sp->lower, normalize);
198✔
914
  result->upper = datum_degrees(sp->upper, normalize);
198✔
915
  return result;
198✔
916
}
917

918
/**
919
 * @ingroup meos_setspan_transf
920
 * @brief Return a float span with the values converted to radians
921
 * @param[in] sp Span
922
 * @csqlfn #Floatspan_radians()
923
 */
924
Span *
925
floatspan_radians(const Span *sp)
99✔
926
{
927
  /* Ensure the validity of the arguments */
928
  VALIDATE_FLOATSPAN(sp, NULL);
929
  Span *result = span_copy(sp);
99✔
930
  result->lower = datum_radians(sp->lower);
99✔
931
  result->upper = datum_radians(sp->upper);
99✔
932
  return result;
99✔
933
}
934

935
/*****************************************************************************/
936

937
/**
938
 * @ingroup meos_internal_setspan_transf
939
 * @brief Return the second span expanded with the first one
940
 * @param[in] sp1,sp2 Spans
941
 */
942
void
943
span_expand(const Span *sp1, Span *sp2)
9,119,491✔
944
{
945
  assert(sp1); assert(sp2); assert(sp1->spantype == sp2->spantype);
946

947
  int cmp1 = datum_cmp(sp2->lower, sp1->lower, sp1->basetype);
9,119,491✔
948
  int cmp2 = datum_cmp(sp2->upper, sp1->upper, sp1->basetype);
9,119,491✔
949
  bool lower1 = cmp1 < 0 || (cmp1 == 0 && (sp2->lower_inc || ! sp1->lower_inc));
9,119,491✔
950
  bool upper1 = cmp2 > 0 || (cmp2 == 0 && (sp2->upper_inc || ! sp1->upper_inc));
9,119,491✔
951
  sp2->lower = lower1 ? sp2->lower : sp1->lower;
9,119,491✔
952
  sp2->lower_inc = lower1 ? sp2->lower_inc : sp1->lower_inc;
9,119,491✔
953
  sp2->upper = upper1 ? sp2->upper : sp1->upper;
9,119,491✔
954
  sp2->upper_inc = upper1 ? sp2->upper_inc : sp1->upper_inc;
9,119,491✔
955
  return;
9,119,491✔
956
}
957

958
/*****************************************************************************/
959

960
/**
961
 * @ingroup meos_internal_setspan_transf
962
 * @brief Return a number span with its bounds expanded/decreased by a value
963
 * @param[in] sp Span
964
 * @param[in] value Value
965
 * @csqlfn #Numspan_expand()
966
 * @note This function can be seen as a 1-dimensional version of the PostGIS
967
 * function `ST_Buffer`
968
 */
969
Span *
970
numspan_expand(const Span *sp, Datum value)
6✔
971
{
972
  /* Ensure the validity of the arguments */
973
  VALIDATE_NUMSPAN(sp, NULL);
974
  /* When the value is negative, return NULL if the span resulting by
975
   * shifting the bounds with the value is empty */ 
976
  if (datum_cmp(value, (Datum) 0, sp->basetype) <= 0)
6✔
977
  {
978
    Datum width = numspan_width(sp);
4✔
979
    Datum value2 = datum_add(value, value, sp->basetype);
4✔
980
    /* We avoid taking the absolute value by adding the two values */
981
    Datum add = datum_add(value2, width, sp->basetype);
4✔
982
    int cmp = datum_cmp(add, (Datum) 0, sp->basetype);
4✔
983
    if (cmp < 0 || (cmp == 0 && (! sp->lower_inc || ! sp->upper_inc)))
4✔
984
      return NULL;
985
  }
986
  Span *result = span_copy(sp);
4✔
987
  result->lower = datum_sub(sp->lower, value, sp->basetype);
4✔
988
  result->upper = datum_add(sp->upper, value, sp->basetype);
4✔
989
  return result;
4✔
990
}
991

992
#if MEOS
993
/**
994
 * @ingroup meos_setspan_transf
995
 * @brief Return an integer span with its bounds expanded/decreased by a value
996
 * @param[in] sp Span
997
 * @param[in] i Value
998
 * @csqlfn #Numspan_expand()
999
 */
1000
Span *
1001
intspan_expand(const Span *sp, int i)
1002
{
1003
  return numspan_expand(sp, Int32GetDatum(i));
1004
}
1005

1006
/**
1007
 * @ingroup meos_setspan_transf
1008
 * @brief Return a big integer span with its bounds expanded/decreased by a
1009
 * value
1010
 * @param[in] sp Span
1011
 * @param[in] i Value
1012
 * @csqlfn #Numspan_expand()
1013
 */
1014
Span *
1015
bigintspan_expand(const Span *sp, int64 i)
1016
{
1017
  return numspan_expand(sp, Int64GetDatum(i));
1018
}
1019

1020
/**
1021
 * @ingroup meos_setspan_transf
1022
 * @brief Return a float span with its bounds expanded/decreased by a value
1023
 * @param[in] sp Span
1024
 * @param[in] d Value
1025
 * @csqlfn #Numspan_expand()
1026
 */
1027
Span *
1028
floatspan_expand(const Span *sp, double d)
1029
{
1030
  return numspan_expand(sp, Float8GetDatum(d));
1031
}
1032
#endif /* MEOS */
1033

1034
/**
1035
 * @ingroup meos_setspan_transf
1036
 * @brief Return a timestamptz span with its bounds expanded/decreased by an
1037
 * interval
1038
 * @param[in] sp Span
1039
 * @param[in] interv Interval
1040
 * @csqlfn #Tstzspan_expand()
1041
 * @note This function can be seen as a 1-dimensional version of the PostGIS
1042
 * function `ST_Buffer`
1043
 */
1044
Span *
1045
tstzspan_expand(const Span *sp, const Interval *interv)
22✔
1046
{
1047
  /* Ensure the validity of the arguments */
1048
  VALIDATE_NOT_NULL(sp, NULL); VALIDATE_NOT_NULL(interv, NULL);
1049
  /* When the interval is negative, return NULL if the span resulting by
1050
   * shifting the bounds with the interval is empty */ 
1051
  Interval intervalzero;
1052
  memset(&intervalzero, 0, sizeof(Interval));
1053
  bool negative = pg_interval_cmp(interv, &intervalzero) <= 0;
22✔
1054
  Interval *interv_neg;
1055
  if (negative)
22✔
1056
  {
1057
    Interval *duration = tstzspan_duration(sp);
15✔
1058
    /* Negate the interval */
1059
    interv_neg = interval_negate(interv);
15✔
1060
    Interval *interv_neg2 = mul_interval_float8(interv_neg, 2.0);
15✔
1061
    int cmp = pg_interval_cmp(duration, interv_neg2);
15✔
1062
    pfree(duration); pfree(interv_neg2);
15✔
1063
    if (cmp < 0 || (cmp == 0 && (! sp->lower_inc || ! sp->upper_inc)))
15✔
1064
    {
1065
      pfree(interv_neg);
8✔
1066
      return NULL;
8✔
1067
    }
1068
  }
1069

1070
  Span *result = span_copy(sp);
14✔
1071
  TimestampTz tmin = negative ?
1072
    add_timestamptz_interval(DatumGetTimestampTz(sp->lower), interv_neg) :
14✔
1073
    minus_timestamptz_interval(DatumGetTimestampTz(sp->lower),
7✔
1074
      (Interval *) interv);
1075
  TimestampTz tmax = add_timestamptz_interval(DatumGetTimestampTz(sp->upper),
14✔
1076
    (Interval *) interv);
1077
  result->lower = TimestampTzGetDatum(tmin);
14✔
1078
  result->upper = TimestampTzGetDatum(tmax);
14✔
1079
  if (negative)
14✔
1080
    pfree(interv_neg);
7✔
1081
  return result;
1082
}
1083

1084
/*****************************************************************************/
1085

1086
/**
1087
 * @brief Shift and/or scale the span bounds by two values
1088
 * @param[in] shift Value for shifting the bounds
1089
 * @param[in] width Width of the result
1090
 * @param[in] basetype Type of the values
1091
 * @param[in] hasshift True when the shift argument is given
1092
 * @param[in] haswidth True when the width argument is given
1093
 * @param[in,out] lower,upper Bounds of the period
1094
 */
1095
void
1096
span_bounds_shift_scale_value(Datum shift, Datum width, meosType basetype,
3,999,678✔
1097
  bool hasshift, bool haswidth, Datum *lower, Datum *upper)
1098
{
1099
  assert(hasshift || haswidth); assert(lower); assert(upper);
1100
  assert(! haswidth || positive_datum(width, basetype));
1101

1102
  bool instant = datum_eq(*lower, *upper, basetype);
3,999,678✔
1103
  if (hasshift)
3,999,678✔
1104
  {
1105
    *lower = datum_add(*lower, shift, basetype);
3,943,916✔
1106
    if (instant)
3,943,916✔
1107
      *upper = *lower;
2,182✔
1108
    else
1109
      *upper = datum_add(*upper, shift, basetype);
3,941,734✔
1110
  }
1111
  if (haswidth && ! instant)
3,999,678✔
1112
  {
1113
    /* Integer and date spans have exclusive upper bound */
1114
    if (span_canon_basetype(basetype))
3,941,633✔
1115
      width = datum_add(width, 1, basetype);
2,949,054✔
1116
    *upper = datum_add(*lower, width, basetype);
3,941,633✔
1117
  }
1118
  return;
3,999,678✔
1119
}
1120

1121
/**
1122
 * @brief Shift and/or scale period bounds by two intervals
1123
 * @param[in] shift Interval to shift the bounds, may be NULL
1124
 * @param[in] duration Interval for the duration of the result, may be NULL
1125
 * @param[in,out] lower,upper Bounds of the period
1126
 */
1127
void
1128
span_bounds_shift_scale_time(const Interval *shift, const Interval *duration,
1,262,091✔
1129
  TimestampTz *lower, TimestampTz *upper)
1130
{
1131
  assert(shift || duration); assert(lower); assert(upper);
1132
  assert(! duration || positive_duration(duration));
1133

1134
  bool instant = (*lower == *upper);
1,262,091✔
1135
  if (shift)
1,262,091✔
1136
  {
1137
    *lower = add_timestamptz_interval(*lower, (Interval *) shift);
1,164,134✔
1138
    if (instant)
1,164,134✔
1139
      *upper = *lower;
106,472✔
1140
    else
1141
      *upper = add_timestamptz_interval(*upper, (Interval *) shift);
1,057,662✔
1142
  }
1143
  if (duration && ! instant)
1,262,091✔
1144
    *upper = add_timestamptz_interval(*lower, (Interval *) duration);
1,050,945✔
1145
  return;
1,262,091✔
1146
}
1147

1148
/**
1149
 * @brief Shift and/or scale a span by a delta and a scale (iterator function)
1150
 */
1151
void
1152
numspan_delta_scale_iter(Span *sp, Datum origin, Datum delta, bool hasdelta,
79,668✔
1153
  double scale)
1154
{
1155
  assert(sp);
1156

1157
  meosType type = sp->basetype;
79,668✔
1158
  /* The default value when shift is not given is 0 */
1159
  if (hasdelta)
79,668✔
1160
  {
1161
    sp->lower = datum_add(sp->lower, delta, type);
53,113✔
1162
    sp->upper = datum_add(sp->upper, delta, type);
53,113✔
1163
  }
1164
  /* Shifted lower and upper */
1165
  Datum lower = sp->lower;
79,668✔
1166
  Datum upper = sp->upper;
79,668✔
1167
  /* The default value when scale is not given is 1.0 */
1168
  if (scale != 1.0)
79,668✔
1169
  {
1170
    /* The potential shift has been already taken care in the previous if */
1171
    sp->lower = datum_add(origin, double_datum(
52,992✔
1172
      datum_double(datum_sub(lower, origin, type), type) * scale, type), type);
52,992✔
1173
    if (datum_eq(lower, upper, type))
52,992✔
1174
      sp->upper = sp->lower;
2,772✔
1175
    else
1176
    {
1177
      /* Integer spans have exclusive upper bound */
1178
      Datum upper1 = span_decr_bound(sp->upper, sp->basetype);
50,220✔
1179
      sp->upper = datum_add(origin,
50,220✔
1180
        double_datum(
1181
          datum_double(datum_sub(upper1, origin, type), type) * scale,
50,220✔
1182
          type), type);
1183
      /* Integer spans have exclusive upper bound */
1184
      if (span_canon_basetype(type))
50,220✔
1185
        sp->upper = datum_add(sp->upper, 1, type);
23,998✔
1186
    }
1187
  }
1188
  return;
79,668✔
1189
}
1190

1191
/**
1192
 * @brief Shift and/or scale a timestamptz span by a delta and a scale
1193
 */
1194
void
1195
tstzspan_delta_scale_iter(Span *sp, TimestampTz origin, TimestampTz delta,
487,916✔
1196
  double scale)
1197
{
1198
  assert(sp);
1199

1200
  TimestampTz lower = DatumGetTimestampTz(sp->lower);
487,916✔
1201
  TimestampTz upper = DatumGetTimestampTz(sp->upper);
487,916✔
1202
  /* The default value when there is not shift is 0 */
1203
  if (delta != 0)
487,916✔
1204
  {
1205
    sp->lower = TimestampTzGetDatum(lower + delta);
329,199✔
1206
    sp->upper = TimestampTzGetDatum(upper + delta);
329,199✔
1207
  }
1208
  /* Shifted lower and upper */
1209
  lower = DatumGetTimestampTz(sp->lower);
487,916✔
1210
  upper = DatumGetTimestampTz(sp->upper);
487,916✔
1211
  /* The default value when there is not scale is 1.0 */
1212
  if (scale != 1.0)
487,916✔
1213
  {
1214
    /* The potential shift has been already taken care in the previous if */
1215
    sp->lower = TimestampTzGetDatum(
316,783✔
1216
      origin + (TimestampTz) ((lower - origin) * scale));
316,783✔
1217
    if (lower == upper)
316,783✔
1218
      sp->upper = sp->lower;
30,807✔
1219
    else
1220
      sp->upper = TimestampTzGetDatum(
285,976✔
1221
        origin + (TimestampTz) ((upper - origin) * scale));
285,976✔
1222
  }
1223
  return;
487,916✔
1224
}
1225

1226
/**
1227
 * @brief Return a number span shifted and/or scaled by two values (iterator
1228
 * function)
1229
 * @param[in] sp Span
1230
 * @param[in] shift Value for shifting the bounds
1231
 * @param[in] width Width of the result
1232
 * @param[in] hasshift True when the shift argument is given
1233
 * @param[in] haswidth True when the width argument is given
1234
 * @param[out] delta,scale Delta and scale of the transformation
1235
 */
1236
void
1237
numspan_shift_scale_iter(Span *sp, Datum shift, Datum width, bool hasshift,
43,666✔
1238
  bool haswidth, Datum *delta, double *scale)
1239
{
1240
  assert(sp); assert(delta); assert(scale);
1241
  Datum lower = sp->lower;
43,666✔
1242
  Datum upper = sp->upper;
43,666✔
1243
  meosType type = sp->basetype;
43,666✔
1244
  span_bounds_shift_scale_value(shift, width, type, hasshift, haswidth,
43,666✔
1245
    &lower, &upper);
1246
  /* Compute delta and scale before overwriting sp->lower and sp->upper */
1247
  *delta = 0;   /* Default value when shift is not given */
43,666✔
1248
  *scale = 1.0; /* Default value when width is not given */
43,666✔
1249
  if (hasshift)
43,666✔
1250
    *delta = datum_sub(lower, sp->lower, type);
29,111✔
1251
  /* If the period is instantaneous we cannot scale */
1252
  if (haswidth && ! datum_eq(sp->lower, sp->upper, type))
43,666✔
1253
  {
1254
    /* Integer spans have exclusive upper bound */
1255
    Datum upper1, upper2;
1256
    if (span_canon_basetype(type))
26,930✔
1257
    {
1258
      upper1 = datum_sub(upper, 1, type);
14,652✔
1259
      upper2 = datum_sub(sp->upper, 1, type);
14,652✔
1260
    }
1261
    else
1262
    {
1263
      upper1 = upper;
12,278✔
1264
      upper2 = sp->upper;
12,278✔
1265
    }
1266
    *scale = datum_double(datum_sub(upper1, lower, type), type) /
53,860✔
1267
      datum_double(datum_sub(upper2, sp->lower, type), type);
26,930✔
1268
  }
1269
  sp->lower = lower;
43,666✔
1270
  sp->upper = upper;
43,666✔
1271
  return;
43,666✔
1272
}
1273

1274
/**
1275
 * @brief Return a timestamptz span shifted and/or scaled by two intervals
1276
 * @note Return the delta and scale of the transformation
1277
 */
1278
void
1279
tstzspan_shift_scale1(Span *sp, const Interval *shift, const Interval *duration,
271,662✔
1280
  TimestampTz *delta, double *scale)
1281
{
1282
  assert(sp); assert(delta); assert(scale);
1283
  TimestampTz lower = DatumGetTimestampTz(sp->lower);
271,662✔
1284
  TimestampTz upper = DatumGetTimestampTz(sp->upper);
271,662✔
1285
  span_bounds_shift_scale_time(shift, duration, &lower, &upper);
271,662✔
1286
  /* Compute delta and scale before overwriting sp->lower and sp->upper */
1287
  *delta = 0;   /* Default value when shift == NULL */
271,662✔
1288
  *scale = 1.0; /* Default value when duration == NULL */
271,662✔
1289
  if (shift)
271,662✔
1290
    *delta = lower - DatumGetTimestampTz(sp->lower);
183,616✔
1291
  /* If the period is instantaneous we cannot scale */
1292
  if (duration && sp->lower != sp->upper)
271,662✔
1293
    *scale = (double) (upper - lower) /
159,749✔
1294
      (double) (DatumGetTimestampTz(sp->upper) - DatumGetTimestampTz(sp->lower));
159,749✔
1295
  sp->lower = TimestampTzGetDatum(lower);
271,662✔
1296
  sp->upper = TimestampTzGetDatum(upper);
271,662✔
1297
  return;
271,662✔
1298
}
1299

1300
/**
1301
 * @ingroup meos_internal_setspan_transf
1302
 * @brief Return a number span shifted and/or scaled by two values
1303
 * @param[in] sp Span
1304
 * @param[in] shift Value for shifting the bounds
1305
 * @param[in] width Width of the result
1306
 * @param[in] hasshift True when the shift argument is given
1307
 * @param[in] haswidth True when the width argument is given
1308
 * @csqlfn #Numspan_shift(), #Numspan_scale(), #Numspan_shift_scale()
1309
 */
1310
Span *
1311
numspan_shift_scale(const Span *sp, Datum shift, Datum width, bool hasshift,
3,949,710✔
1312
  bool haswidth)
1313
{
1314
  /* Ensure the validity of the arguments */
1315
  VALIDATE_NOT_NULL(sp, NULL);
1316
  if (! ensure_one_true(hasshift, haswidth) ||
3,949,710✔
1317
      (haswidth && ! ensure_positive_datum(width, sp->basetype)))
3,910,504✔
1318
    return NULL;
×
1319

1320
  /* Copy the input span to the result */
1321
  Span *result = span_copy(sp);
3,949,710✔
1322
  /* Shift and/or scale the resulting span */
1323
  span_bounds_shift_scale_value(shift, width, sp->basetype, hasshift, haswidth,
3,949,710✔
1324
    &result->lower, &result->upper);
1325
  return result;
3,949,710✔
1326
}
1327

1328
/**
1329
 * @ingroup meos_base_timestamp
1330
 * @brief Return a timestamptz shifted by an interval
1331
 * @param[in] t Timestamp
1332
 * @param[in] interv Interval to shift the instant
1333
 * @return On error return `DT_NOEND`
1334
 * @csqlfn #Timestamptz_shift()
1335
 */
1336
TimestampTz
1337
timestamptz_shift(TimestampTz t, const Interval *interv)
1338
{
1339
  /* Ensure the validity of the arguments */
1340
  VALIDATE_NOT_NULL(interv, DT_NOEND);
1341
  return add_timestamptz_interval(t, (Interval *) interv);
99✔
1342
}
1343

1344
/**
1345
 * @ingroup meos_setspan_transf
1346
 * @brief Return a timestamptz span shifted and/or scaled by two intervals
1347
 * @param[in] sp Span
1348
 * @param[in] shift Interval to shift the bounds, may be NULL
1349
 * @param[in] duration Duation of the result, may be NULL
1350
 * @csqlfn #Tstzspan_shift(), #Tstzspan_scale(), #Tstzspan_shift_scale()
1351
 */
1352
Span *
1353
tstzspan_shift_scale(const Span *sp, const Interval *shift,
990,114✔
1354
  const Interval *duration)
1355
{
1356
  /* Ensure the validity of the arguments */
1357
  VALIDATE_TSTZSPAN(sp, NULL);
1358
  if (! ensure_one_not_null((void *) shift, (void *) duration) ||
990,114✔
1359
      (duration && ! ensure_positive_duration(duration)))
980,110✔
1360
    return NULL;
×
1361

1362
  /* Copy the input period to the result */
1363
  Span *result = span_copy(sp);
990,114✔
1364
  /* Shift and/or scale the resulting period */
1365
  TimestampTz lower = DatumGetTimestampTz(sp->lower);
990,114✔
1366
  TimestampTz upper = DatumGetTimestampTz(sp->upper);
990,114✔
1367
  span_bounds_shift_scale_time(shift, duration, &lower, &upper);
990,114✔
1368
  result->lower = TimestampTzGetDatum(lower);
990,114✔
1369
  result->upper = TimestampTzGetDatum(upper);
990,114✔
1370
  return result;
990,114✔
1371
}
1372

1373
/*****************************************************************************
1374
 * Spans function
1375
 *****************************************************************************/
1376

1377
/**
1378
 * @ingroup meos_setspan_bbox_split
1379
 * @brief Return an array of spans from the values of a set
1380
 * @param[in] s Set
1381
 * @return On error return @p NULL
1382
 * @csqlfn #Set_spans()
1383
 */
1384
Span *
1385
set_spans(const Set *s)
1✔
1386
{
1387
  /* Ensure the validity of the arguments */
1388
  VALIDATE_NOT_NULL(s, NULL);
1389
  /* Output the composing spans */
1390
  Span *result = palloc(sizeof(Span) * s->count);
1✔
1391
  for (int i = 0; i < s->count; i++)
11✔
1392
    set_set_subspan(s, i, i, &result[i]);
10✔
1393
  return result;
1✔
1394
}
1395

1396
/**
1397
 * @ingroup meos_setspan_bbox_split
1398
 * @brief Return an array of N spans from the values of a set
1399
 * @param[in] s Set
1400
 * @param[in] span_count Number of spans
1401
 * @param[out] count Number of elements in the output array
1402
 * @return On error return @p NULL
1403
 * @csqlfn #Set_split_n_spans()
1404
 */
1405
Span *
1406
set_split_n_spans(const Set *s, int span_count, int *count)
8✔
1407
{
1408
  /* Ensure the validity of the arguments */
1409
  VALIDATE_NUMSET(s, NULL); VALIDATE_NOT_NULL(count, NULL);
1410
  if (! ensure_positive(span_count))
8✔
1411
    return NULL;
1412

1413
  Span *result = palloc(sizeof(Span) * s->count);
7✔
1414
  /* Output the composing spans */
1415
  if (s->count <= span_count)
7✔
1416
  {
1417
    for (int i = 0; i < s->count; i++)
11✔
1418
      set_set_subspan(s, i, i, &result[i]);
10✔
1419
    *count = s->count;
1✔
1420
    return result;
1✔
1421
  }
1422

1423
  /* Merge consecutive values to reach the maximum number of span */
1424
  /* Minimum number of values merged together in an output span */
1425
  int size = s->count / span_count;
6✔
1426
  /* Number of output spans that result from merging (size + 1) values */
1427
  int remainder = s->count % span_count;
6✔
1428
  int i = 0; /* Loop variable for input values */
1429
  for (int k = 0; k < span_count; k++)
27✔
1430
  {
1431
    int j = i + size;
21✔
1432
    if (k < remainder)
21✔
1433
      j++;
7✔
1434
    set_set_subspan(s, i, j - 1, &result[k]);
21✔
1435
    i = j;
1436
  }
1437
  assert(i == s->count);
1438
  *count = span_count;
6✔
1439
  return result;
6✔
1440
}
1441

1442
/**
1443
 * @ingroup meos_setspan_bbox_split
1444
 * @brief Return an array of spans from a set obtained by merging consecutive
1445
 * elements
1446
 * @param[in] s Set
1447
 * @param[in] elems_per_span Number of elements merged into an ouput span
1448
 * @param[out] count Number of elements in the output array
1449
 * @return On error return @p NULL
1450
 * @csqlfn #Set_split_each_n_spans()
1451
 */
1452
Span *
1453
set_split_each_n_spans(const Set *s, int elems_per_span, int *count)
7✔
1454
{
1455
  /* Ensure the validity of the arguments */
1456
  VALIDATE_NUMSET(s, NULL); VALIDATE_NOT_NULL(count, NULL);
1457
  if (! ensure_positive(elems_per_span))
7✔
1458
    return NULL;
1459

1460
  int nspans = ceil((double) s->count / (double) elems_per_span);
6✔
1461
  Span *result = palloc(sizeof(Span) * nspans);
6✔
1462
  int k = 0;
1463
  for (int i = 0; i < s->count; ++i)
66✔
1464
  {
1465
    if (i % elems_per_span == 0)
60✔
1466
      value_set_span(SET_VAL_N(s, i), s->basetype, &result[k++]);
26✔
1467
    else
1468
    {
1469
      Span span;
1470
      value_set_span(SET_VAL_N(s, i), s->basetype, &span);
34✔
1471
      span_expand(&span, &result[k - 1]);
34✔
1472
    }
1473
  }
1474
  assert(k == nspans);
1475
  *count = k;
6✔
1476
  return result;
6✔
1477
}
1478

1479
/*****************************************************************************
1480
 * Comparison functions for defining B-tree indexes
1481
 *****************************************************************************/
1482

1483
/**
1484
 * @ingroup meos_setspan_comp
1485
 * @brief Return true if two spans are equal
1486
 * @note The function #span_cmp() is not used to increase efficiency
1487
 * @param[in] sp1,sp2 Sets
1488
 * @csqlfn #Span_eq()
1489
 */
1490
bool
1491
span_eq(const Span *sp1, const Span *sp2)
4,751,732✔
1492
{
1493
  /* Ensure the validity of the arguments */
1494
  if (! ensure_valid_span_span(sp1, sp2))
4,751,732✔
1495
    return false;
1496

1497
  if (sp1->lower != sp2->lower || sp1->upper != sp2->upper ||
4,751,732✔
1498
    sp1->lower_inc != sp2->lower_inc || sp1->upper_inc != sp2->upper_inc)
2,387,115✔
1499
    return false;
2,364,620✔
1500
  return true;
1501
}
1502

1503
/**
1504
 * @ingroup meos_setspan_comp
1505
 * @brief Return true if the first span is different from the second one
1506
 * @param[in] sp1,sp2 Sets
1507
 * @csqlfn #Span_ne()
1508
 */
1509
inline bool
1510
span_ne(const Span *sp1, const Span *sp2)
2,377✔
1511
{
1512
  return (! span_eq(sp1, sp2));
2,377✔
1513
}
1514

1515
/**
1516
 * @ingroup meos_setspan_comp
1517
 * @brief Return -1, 0, or 1 depending on whether the first span is less than,
1518
 * equal to, or greater than the second one
1519
 * @param[in] sp1,sp2 Sets
1520
 * @return On error return INT_MAX
1521
 * @note Function used for B-tree comparison
1522
 * @csqlfn #Span_cmp()
1523
 */
1524
int
1525
span_cmp(const Span *sp1, const Span *sp2)
16,519,222✔
1526
{
1527
  /* Ensure the validity of the arguments */
1528
  if (! ensure_valid_span_span(sp1, sp2))
16,519,222✔
1529
    return INT_MAX;
1530

1531
  int cmp = datum_cmp(sp1->lower, sp2->lower, sp1->basetype);
16,519,222✔
1532
  if (cmp != 0)
16,519,222✔
1533
    return cmp;
1534
  if (sp1->lower_inc != sp2->lower_inc)
1,492,021✔
1535
    return sp1->lower_inc ? -1 : 1;
431,486✔
1536
  cmp = datum_cmp(sp1->upper, sp2->upper, sp1->basetype);
1,060,535✔
1537
  if (cmp != 0)
1,060,535✔
1538
    return cmp;
1539
  if (sp1->upper_inc != sp2->upper_inc)
1,053,211✔
1540
    return sp1->upper_inc ? 1 : -1;
2✔
1541
  return 0;
1542
}
1543

1544
/* Inequality operators using the span_cmp function */
1545

1546
/**
1547
 * @ingroup meos_setspan_comp
1548
 * @brief Return true if the first span is less than the second one
1549
 * @param[in] sp1,sp2 Sets
1550
 * @csqlfn #Span_lt()
1551
 */
1552
inline bool
1553
span_lt(const Span *sp1, const Span *sp2)
19,801✔
1554
{
1555
  return span_cmp(sp1, sp2) < 0;
19,801✔
1556
}
1557

1558
/**
1559
 * @ingroup meos_setspan_comp
1560
 * @brief Return true if the first span is less than or equal to the second one
1561
 * @param[in] sp1,sp2 Sets
1562
 * @csqlfn #Span_le()
1563
 */
1564
inline bool
1565
span_le(const Span *sp1, const Span *sp2)
19,801✔
1566
{
1567
  return span_cmp(sp1, sp2) <= 0;
19,801✔
1568
}
1569

1570
/**
1571
 * @ingroup meos_setspan_comp
1572
 * @brief Return true if the first span is greater than or equal to the second
1573
 * one
1574
 * @param[in] sp1,sp2 Sets
1575
 * @csqlfn #Span_gt()
1576
 */
1577
inline bool
1578
span_ge(const Span *sp1, const Span *sp2)
19,801✔
1579
{
1580
  return span_cmp(sp1, sp2) >= 0;
19,801✔
1581
}
1582

1583
/**
1584
 * @ingroup meos_setspan_comp
1585
 * @brief Return true if the first span is greater than the second one
1586
 * @param[in] sp1,sp2 Sets
1587
 * @csqlfn #Span_ge()
1588
 */
1589
inline bool
1590
span_gt(const Span *sp1, const Span *sp2)
19,801✔
1591
{
1592
  return span_cmp(sp1, sp2) > 0;
19,801✔
1593
}
1594

1595
/*****************************************************************************
1596
 * Functions for defining hash indexes
1597
 *****************************************************************************/
1598

1599
/**
1600
 * @ingroup meos_setspan_accessor
1601
 * @brief Return the 32-bit hash of a span
1602
 * @param[in] sp Span
1603
 * @return On error return @p INT_MAX
1604
 * @csqlfn #Span_hash()
1605
 */
1606
uint32
1607
span_hash(const Span *sp)
7,609✔
1608
{
1609
  /* Ensure the validity of the arguments */
1610
  VALIDATE_NOT_NULL(sp, INT_MAX);
1611

1612
  /* Create flags from the lower_inc and upper_inc values */
1613
  char flags = '\0';
1614
  if (sp->lower_inc)
7,609✔
1615
    flags |= 0x01;
1616
  if (sp->upper_inc)
7,609✔
1617
    flags |= 0x02;
343✔
1618

1619
  /* Create type from the spantype and basetype values */
1620
  uint16 type = ((uint16) (sp->spantype) << 8) | (uint16) (sp->basetype);
7,609✔
1621
  uint32 type_hash = hash_bytes_uint32((int32) type);
7,609✔
1622

1623
  /* Apply the hash function to each bound */
1624
  uint32 lower_hash = datum_hash(sp->lower, sp->basetype);
7,609✔
1625
  uint32 upper_hash = datum_hash(sp->upper, sp->basetype);
7,609✔
1626

1627
  /* Merge hashes of flags, type, and bounds */
1628
  uint32 result = hash_bytes_uint32((uint32) flags);
7,609✔
1629
  result ^= type_hash;
7,609✔
1630
#if POSTGRESQL_VERSION_NUMBER >= 150000
1631
  result = pg_rotate_left32(result, 1);
1632
#else
1633
  result =  (result << 1) | (result >> 31);
1634
#endif
1635
  result ^= lower_hash;
7,609✔
1636
#if POSTGRESQL_VERSION_NUMBER >= 150000
1637
  result = pg_rotate_left32(result, 1);
1638
#else
1639
  result =  (result << 1) | (result >> 31);
1640
#endif
1641
  result ^= upper_hash;
7,609✔
1642

1643
  return result;
7,609✔
1644
}
1645

1646
/**
1647
 * @ingroup meos_setspan_accessor
1648
 * @brief Return the 64-bit hash of a span using a seed
1649
 * @param[in] sp Span
1650
 * @param[in] seed Seed
1651
 * @return On error return @p LONG_MAX
1652
 * @csqlfn #Span_hash_extended()
1653
 */
1654
uint64_t
1655
span_hash_extended(const Span *sp, uint64_t seed)
605✔
1656
{
1657
  /* Ensure the validity of the arguments */
1658
  VALIDATE_NOT_NULL(sp, LONG_MAX);
1659

1660
  char flags = '\0';
1661
  /* Create flags from the lower_inc and upper_inc values */
1662
  if (sp->lower_inc)
605✔
1663
    flags |= 0x01;
1664
  if (sp->upper_inc)
605✔
1665
    flags |= 0x02;
343✔
1666

1667
  /* Create type from the spantype and basetype values */
1668
  uint16 type = ((uint16) (sp->spantype) << 8) | (uint16) (sp->basetype);
605✔
1669
  uint64_t type_hash = hash_uint32_extended((uint32) type, seed);
605✔
1670

1671
  /* Apply the hash function to each bound */
1672
  uint64_t lower_hash = int64_hash_extended(sp->lower, seed);
605✔
1673
  uint64_t upper_hash = int64_hash_extended(sp->upper, seed);
605✔
1674

1675
  /* Merge hashes of flags and bounds */
1676
  uint64_t result = hash_uint32_extended((uint32) flags, seed);
605✔
1677
  result ^= type_hash;
605✔
1678
  result = ROTATE_HIGH_AND_LOW_32BITS(result);
605✔
1679
  result ^= lower_hash;
605✔
1680
  result = ROTATE_HIGH_AND_LOW_32BITS(result);
605✔
1681
  result ^= upper_hash;
605✔
1682

1683
  return result;
605✔
1684
}
1685

1686
/******************************************************************************/
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