Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * timestamp.c
4 : : * Functions for the built-in SQL types "timestamp" and "interval".
5 : : *
6 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/utils/adt/timestamp.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #include "postgres.h"
17 : :
18 : : #include <ctype.h>
19 : : #include <math.h>
20 : : #include <limits.h>
21 : : #include <sys/time.h>
22 : :
23 : : #include "access/xact.h"
24 : : #include "catalog/pg_type.h"
25 : : #include "common/int.h"
26 : : #include "common/int128.h"
27 : : #include "funcapi.h"
28 : : #include "libpq/pqformat.h"
29 : : #include "miscadmin.h"
30 : : #include "nodes/nodeFuncs.h"
31 : : #include "nodes/supportnodes.h"
32 : : #include "parser/scansup.h"
33 : : #include "utils/array.h"
34 : : #include "utils/builtins.h"
35 : : #include "utils/date.h"
36 : : #include "utils/datetime.h"
37 : : #include "utils/float.h"
38 : : #include "utils/numeric.h"
39 : : #include "utils/sortsupport.h"
40 : :
41 : : /*
42 : : * gcc's -ffast-math switch breaks routines that expect exact results from
43 : : * expressions like timeval / SECS_PER_HOUR, where timeval is double.
44 : : */
45 : : #ifdef __FAST_MATH__
46 : : #error -ffast-math is known to break this code
47 : : #endif
48 : :
49 : : #define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
50 : :
51 : : /* Set at postmaster start */
52 : : TimestampTz PgStartTime;
53 : :
54 : : /* Set at configuration reload */
55 : : TimestampTz PgReloadTime;
56 : :
57 : : typedef struct
58 : : {
59 : : Timestamp current;
60 : : Timestamp finish;
61 : : Interval step;
62 : : int step_sign;
63 : : } generate_series_timestamp_fctx;
64 : :
65 : : typedef struct
66 : : {
67 : : TimestampTz current;
68 : : TimestampTz finish;
69 : : Interval step;
70 : : int step_sign;
71 : : pg_tz *attimezone;
72 : : } generate_series_timestamptz_fctx;
73 : :
74 : : /*
75 : : * The transition datatype for interval aggregates is declared as internal.
76 : : * It's a pointer to an IntervalAggState allocated in the aggregate context.
77 : : */
78 : : typedef struct IntervalAggState
79 : : {
80 : : int64 N; /* count of finite intervals processed */
81 : : Interval sumX; /* sum of finite intervals processed */
82 : : /* These counts are *not* included in N! Use IA_TOTAL_COUNT() as needed */
83 : : int64 pInfcount; /* count of +infinity intervals */
84 : : int64 nInfcount; /* count of -infinity intervals */
85 : : } IntervalAggState;
86 : :
87 : : #define IA_TOTAL_COUNT(ia) \
88 : : ((ia)->N + (ia)->pInfcount + (ia)->nInfcount)
89 : :
90 : : static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
91 : : static Timestamp dt2local(Timestamp dt, int timezone);
92 : : static bool AdjustIntervalForTypmod(Interval *interval, int32 typmod,
93 : : Node *escontext);
94 : : static TimestampTz timestamp2timestamptz(Timestamp timestamp);
95 : : static Timestamp timestamptz2timestamp(TimestampTz timestamp);
96 : :
97 : : static void EncodeSpecialInterval(const Interval *interval, char *str);
98 : : static void interval_um_internal(const Interval *interval, Interval *result);
99 : :
100 : : /* common code for timestamptypmodin and timestamptztypmodin */
101 : : static int32
333 michael@paquier.xyz 102 :CBC 66 : anytimestamp_typmodin(bool istz, ArrayType *ta)
103 : : {
104 : : int32 *tl;
105 : : int n;
106 : :
107 : 66 : tl = ArrayGetIntegerTypmods(ta, &n);
108 : :
109 : : /*
110 : : * we're not too tense about good error message here because grammar
111 : : * shouldn't allow wrong number of modifiers for TIMESTAMP
112 : : */
113 [ - + ]: 66 : if (n != 1)
333 michael@paquier.xyz 114 [ # # ]:UBC 0 : ereport(ERROR,
115 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
116 : : errmsg("invalid type modifier")));
117 : :
333 michael@paquier.xyz 118 :CBC 66 : return anytimestamp_typmod_check(istz, tl[0]);
119 : : }
120 : :
121 : : /* exported so parse_expr.c can use it */
122 : : int32
2798 tgl@sss.pgh.pa.us 123 : 340 : anytimestamp_typmod_check(bool istz, int32 typmod)
124 : : {
125 [ - + ]: 340 : if (typmod < 0)
6315 tgl@sss.pgh.pa.us 126 [ # # # # ]:UBC 0 : ereport(ERROR,
127 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
128 : : errmsg("TIMESTAMP(%d)%s precision must not be negative",
129 : : typmod, (istz ? " WITH TIME ZONE" : ""))));
2798 tgl@sss.pgh.pa.us 130 [ + + ]:CBC 340 : if (typmod > MAX_TIMESTAMP_PRECISION)
131 : : {
6315 132 [ + - + + ]: 18 : ereport(WARNING,
133 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
134 : : errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
135 : : typmod, (istz ? " WITH TIME ZONE" : ""),
136 : : MAX_TIMESTAMP_PRECISION)));
137 : 18 : typmod = MAX_TIMESTAMP_PRECISION;
138 : : }
139 : :
140 : 340 : return typmod;
141 : : }
142 : :
143 : : /* common code for timestamptypmodout and timestamptztypmodout */
144 : : static char *
145 : 10 : anytimestamp_typmodout(bool istz, int32 typmod)
146 : : {
147 [ + + ]: 10 : const char *tz = istz ? " with time zone" : " without time zone";
148 : :
149 [ + - ]: 10 : if (typmod >= 0)
3751 peter_e@gmx.net 150 : 10 : return psprintf("(%d)%s", (int) typmod, tz);
151 : : else
586 drowley@postgresql.o 152 :UBC 0 : return pstrdup(tz);
153 : : }
154 : :
155 : :
156 : : /*****************************************************************************
157 : : * USER I/O ROUTINES *
158 : : *****************************************************************************/
159 : :
160 : : /* timestamp_in()
161 : : * Convert a string to internal form.
162 : : */
163 : : Datum
8710 tgl@sss.pgh.pa.us 164 :CBC 8252 : timestamp_in(PG_FUNCTION_ARGS)
165 : : {
166 : 8252 : char *str = PG_GETARG_CSTRING(0);
167 : : #ifdef NOT_USED
168 : : Oid typelem = PG_GETARG_OID(1);
169 : : #endif
8229 lockhart@fourpalms.o 170 : 8252 : int32 typmod = PG_GETARG_INT32(2);
492 tgl@sss.pgh.pa.us 171 : 8252 : Node *escontext = fcinfo->context;
172 : : Timestamp result;
173 : : fsec_t fsec;
174 : : struct pg_tm tt,
8824 lockhart@fourpalms.o 175 : 8252 : *tm = &tt;
176 : : int tz;
177 : : int dtype;
178 : : int nf;
179 : : int dterr;
180 : : char *field[MAXDATEFIELDS];
181 : : int ftype[MAXDATEFIELDS];
182 : : char workbuf[MAXDATELEN + MAXDATEFIELDS];
183 : : DateTimeErrorExtra extra;
184 : :
6898 neilc@samurai.com 185 : 8252 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
186 : : field, ftype, MAXDATEFIELDS, &nf);
7536 tgl@sss.pgh.pa.us 187 [ + - ]: 8252 : if (dterr == 0)
492 188 : 8252 : dterr = DecodeDateTime(field, ftype, nf,
189 : : &dtype, tm, &fsec, &tz, &extra);
7536 190 [ + + ]: 8252 : if (dterr != 0)
191 : : {
492 192 : 57 : DateTimeParseError(dterr, &extra, str, "timestamp", escontext);
193 : 12 : PG_RETURN_NULL();
194 : : }
195 : :
8824 lockhart@fourpalms.o 196 [ + + + + : 8195 : switch (dtype)
- ]
197 : : {
198 : 8027 : case DTK_DATE:
8234 199 [ + + ]: 8027 : if (tm2timestamp(tm, fsec, NULL, &result) != 0)
492 tgl@sss.pgh.pa.us 200 [ + - ]: 9 : ereturn(escontext, (Datum) 0,
201 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
202 : : errmsg("timestamp out of range: \"%s\"", str)));
8824 lockhart@fourpalms.o 203 : 8018 : break;
204 : :
205 : 12 : case DTK_EPOCH:
8234 206 : 12 : result = SetEpochTimestamp();
8824 207 : 12 : break;
208 : :
209 : 88 : case DTK_LATE:
8710 tgl@sss.pgh.pa.us 210 : 88 : TIMESTAMP_NOEND(result);
8824 lockhart@fourpalms.o 211 : 88 : break;
212 : :
213 : 68 : case DTK_EARLY:
8710 tgl@sss.pgh.pa.us 214 : 68 : TIMESTAMP_NOBEGIN(result);
8824 lockhart@fourpalms.o 215 : 68 : break;
216 : :
8824 lockhart@fourpalms.o 217 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 218 [ # # ]: 0 : elog(ERROR, "unexpected dtype %d while parsing timestamp \"%s\"",
219 : : dtype, str);
220 : : TIMESTAMP_NOEND(result);
221 : : }
222 : :
492 tgl@sss.pgh.pa.us 223 :CBC 8186 : AdjustTimestampForTypmod(&result, typmod, escontext);
224 : :
8710 225 : 8186 : PG_RETURN_TIMESTAMP(result);
226 : : }
227 : :
228 : : /* timestamp_out()
229 : : * Convert a timestamp to external form.
230 : : */
231 : : Datum
232 : 20969 : timestamp_out(PG_FUNCTION_ARGS)
233 : : {
6756 bruce@momjian.us 234 : 20969 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
235 : : char *result;
236 : : struct pg_tm tt,
8234 lockhart@fourpalms.o 237 : 20969 : *tm = &tt;
238 : : fsec_t fsec;
239 : : char buf[MAXDATELEN + 1];
240 : :
8229 241 [ + + + + ]: 20969 : if (TIMESTAMP_NOT_FINITE(timestamp))
242 : 236 : EncodeSpecialTimestamp(timestamp, buf);
6878 bruce@momjian.us 243 [ + - ]: 20733 : else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
4414 peter_e@gmx.net 244 : 20733 : EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf);
245 : : else
7567 tgl@sss.pgh.pa.us 246 [ # # ]:UBC 0 : ereport(ERROR,
247 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
248 : : errmsg("timestamp out of range")));
249 : :
8234 lockhart@fourpalms.o 250 :CBC 20969 : result = pstrdup(buf);
251 : 20969 : PG_RETURN_CSTRING(result);
252 : : }
253 : :
254 : : /*
255 : : * timestamp_recv - converts external binary format to timestamp
256 : : */
257 : : Datum
7643 tgl@sss.pgh.pa.us 258 :UBC 0 : timestamp_recv(PG_FUNCTION_ARGS)
259 : : {
260 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
261 : :
262 : : #ifdef NOT_USED
263 : : Oid typelem = PG_GETARG_OID(1);
264 : : #endif
6853 265 : 0 : int32 typmod = PG_GETARG_INT32(2);
266 : : Timestamp timestamp;
267 : : struct pg_tm tt,
7255 268 : 0 : *tm = &tt;
269 : : fsec_t fsec;
270 : :
271 : 0 : timestamp = (Timestamp) pq_getmsgint64(buf);
272 : :
273 : : /* range check: see if timestamp_out would like it */
274 [ # # # # ]: 0 : if (TIMESTAMP_NOT_FINITE(timestamp))
275 : : /* ok */ ;
2951 276 [ # # ]: 0 : else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0 ||
277 [ # # # # ]: 0 : !IS_VALID_TIMESTAMP(timestamp))
7255 278 [ # # ]: 0 : ereport(ERROR,
279 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
280 : : errmsg("timestamp out of range")));
281 : :
492 282 : 0 : AdjustTimestampForTypmod(×tamp, typmod, NULL);
283 : :
7255 284 : 0 : PG_RETURN_TIMESTAMP(timestamp);
285 : : }
286 : :
287 : : /*
288 : : * timestamp_send - converts timestamp to binary format
289 : : */
290 : : Datum
7643 291 : 0 : timestamp_send(PG_FUNCTION_ARGS)
292 : : {
6756 bruce@momjian.us 293 : 0 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
294 : : StringInfoData buf;
295 : :
7643 tgl@sss.pgh.pa.us 296 : 0 : pq_begintypsend(&buf);
297 : 0 : pq_sendint64(&buf, timestamp);
298 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
299 : : }
300 : :
301 : : Datum
6315 tgl@sss.pgh.pa.us 302 :CBC 18 : timestamptypmodin(PG_FUNCTION_ARGS)
303 : : {
5995 bruce@momjian.us 304 : 18 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
305 : :
6315 tgl@sss.pgh.pa.us 306 : 18 : PG_RETURN_INT32(anytimestamp_typmodin(false, ta));
307 : : }
308 : :
309 : : Datum
310 : 5 : timestamptypmodout(PG_FUNCTION_ARGS)
311 : : {
5995 bruce@momjian.us 312 : 5 : int32 typmod = PG_GETARG_INT32(0);
313 : :
6315 tgl@sss.pgh.pa.us 314 : 5 : PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod));
315 : : }
316 : :
317 : :
318 : : /*
319 : : * timestamp_support()
320 : : *
321 : : * Planner support function for the timestamp_scale() and timestamptz_scale()
322 : : * length coercion functions (we need not distinguish them here).
323 : : */
324 : : Datum
1891 325 : 12 : timestamp_support(PG_FUNCTION_ARGS)
326 : : {
327 : 12 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
328 : 12 : Node *ret = NULL;
329 : :
330 [ + + ]: 12 : if (IsA(rawreq, SupportRequestSimplify))
331 : : {
332 : 6 : SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
333 : :
334 : 6 : ret = TemporalSimplify(MAX_TIMESTAMP_PRECISION, (Node *) req->fcall);
335 : : }
336 : :
337 : 12 : PG_RETURN_POINTER(ret);
338 : : }
339 : :
340 : : /* timestamp_scale()
341 : : * Adjust time type for specified scale factor.
342 : : * Used by PostgreSQL type system to stuff columns.
343 : : */
344 : : Datum
8229 lockhart@fourpalms.o 345 : 31086 : timestamp_scale(PG_FUNCTION_ARGS)
346 : : {
6756 bruce@momjian.us 347 : 31086 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
8229 lockhart@fourpalms.o 348 : 31086 : int32 typmod = PG_GETARG_INT32(1);
349 : : Timestamp result;
350 : :
351 : 31086 : result = timestamp;
352 : :
492 tgl@sss.pgh.pa.us 353 : 31086 : AdjustTimestampForTypmod(&result, typmod, NULL);
354 : :
8229 lockhart@fourpalms.o 355 : 31086 : PG_RETURN_TIMESTAMP(result);
356 : : }
357 : :
358 : : /*
359 : : * AdjustTimestampForTypmod --- round off a timestamp to suit given typmod
360 : : * Works for either timestamp or timestamptz.
361 : : *
362 : : * Returns true on success, false on failure (if escontext points to an
363 : : * ErrorSaveContext; otherwise errors are thrown).
364 : : */
365 : : bool
492 tgl@sss.pgh.pa.us 366 : 62086 : AdjustTimestampForTypmod(Timestamp *time, int32 typmod, Node *escontext)
367 : : {
368 : : static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = {
369 : : INT64CONST(1000000),
370 : : INT64CONST(100000),
371 : : INT64CONST(10000),
372 : : INT64CONST(1000),
373 : : INT64CONST(100),
374 : : INT64CONST(10),
375 : : INT64CONST(1)
376 : : };
377 : :
378 : : static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
379 : : INT64CONST(500000),
380 : : INT64CONST(50000),
381 : : INT64CONST(5000),
382 : : INT64CONST(500),
383 : : INT64CONST(50),
384 : : INT64CONST(5),
385 : : INT64CONST(0)
386 : : };
387 : :
8029 lockhart@fourpalms.o 388 [ + + + + ]: 62086 : if (!TIMESTAMP_NOT_FINITE(*time)
389 [ + + + + ]: 61757 : && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION))
390 : : {
6901 bruce@momjian.us 391 [ + - - + ]: 31600 : if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION)
492 tgl@sss.pgh.pa.us 392 [ # # ]:UBC 0 : ereturn(escontext, false,
393 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
394 : : errmsg("timestamp(%d) precision must be between %d and %d",
395 : : typmod, 0, MAX_TIMESTAMP_PRECISION)));
396 : :
8029 lockhart@fourpalms.o 397 [ + + ]:CBC 31600 : if (*time >= INT64CONST(0))
398 : : {
6900 bruce@momjian.us 399 : 31279 : *time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) *
6756 400 : 31279 : TimestampScales[typmod];
401 : : }
402 : : else
403 : : {
7559 404 : 321 : *time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
405 : 321 : * TimestampScales[typmod]);
406 : : }
407 : : }
408 : :
1663 akorotkov@postgresql 409 : 62086 : return true;
410 : : }
411 : :
412 : : /* timestamptz_in()
413 : : * Convert a string to internal form.
414 : : */
415 : : Datum
8234 lockhart@fourpalms.o 416 : 20459 : timestamptz_in(PG_FUNCTION_ARGS)
417 : : {
418 : 20459 : char *str = PG_GETARG_CSTRING(0);
419 : : #ifdef NOT_USED
420 : : Oid typelem = PG_GETARG_OID(1);
421 : : #endif
8229 422 : 20459 : int32 typmod = PG_GETARG_INT32(2);
492 tgl@sss.pgh.pa.us 423 : 20459 : Node *escontext = fcinfo->context;
424 : : TimestampTz result;
425 : : fsec_t fsec;
426 : : struct pg_tm tt,
8234 lockhart@fourpalms.o 427 : 20459 : *tm = &tt;
428 : : int tz;
429 : : int dtype;
430 : : int nf;
431 : : int dterr;
432 : : char *field[MAXDATEFIELDS];
433 : : int ftype[MAXDATEFIELDS];
434 : : char workbuf[MAXDATELEN + MAXDATEFIELDS];
435 : : DateTimeErrorExtra extra;
436 : :
6898 neilc@samurai.com 437 : 20459 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf),
438 : : field, ftype, MAXDATEFIELDS, &nf);
7536 tgl@sss.pgh.pa.us 439 [ + - ]: 20459 : if (dterr == 0)
492 440 : 20459 : dterr = DecodeDateTime(field, ftype, nf,
441 : : &dtype, tm, &fsec, &tz, &extra);
7536 442 [ + + ]: 20459 : if (dterr != 0)
443 : : {
492 444 : 54 : DateTimeParseError(dterr, &extra, str, "timestamp with time zone",
445 : : escontext);
446 : 12 : PG_RETURN_NULL();
447 : : }
448 : :
8234 lockhart@fourpalms.o 449 [ + + + + : 20405 : switch (dtype)
- ]
450 : : {
451 : 20238 : case DTK_DATE:
452 [ + + ]: 20238 : if (tm2timestamp(tm, fsec, &tz, &result) != 0)
492 tgl@sss.pgh.pa.us 453 [ + - ]: 12 : ereturn(escontext, (Datum) 0,
454 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
455 : : errmsg("timestamp out of range: \"%s\"", str)));
8234 lockhart@fourpalms.o 456 : 20226 : break;
457 : :
458 : 6 : case DTK_EPOCH:
459 : 6 : result = SetEpochTimestamp();
460 : 6 : break;
461 : :
462 : 93 : case DTK_LATE:
463 : 93 : TIMESTAMP_NOEND(result);
464 : 93 : break;
465 : :
466 : 68 : case DTK_EARLY:
467 : 68 : TIMESTAMP_NOBEGIN(result);
468 : 68 : break;
469 : :
8234 lockhart@fourpalms.o 470 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 471 [ # # ]: 0 : elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"",
472 : : dtype, str);
473 : : TIMESTAMP_NOEND(result);
474 : : }
475 : :
492 tgl@sss.pgh.pa.us 476 :CBC 20393 : AdjustTimestampForTypmod(&result, typmod, escontext);
477 : :
8234 lockhart@fourpalms.o 478 : 20393 : PG_RETURN_TIMESTAMPTZ(result);
479 : : }
480 : :
481 : : /*
482 : : * Try to parse a timezone specification, and return its timezone offset value
483 : : * if it's acceptable. Otherwise, an error is thrown.
484 : : *
485 : : * Note: some code paths update tm->tm_isdst, and some don't; current callers
486 : : * don't care, so we don't bother being consistent.
487 : : */
488 : : static int
2489 tgl@sss.pgh.pa.us 489 : 93 : parse_sane_timezone(struct pg_tm *tm, text *zone)
490 : : {
491 : : char tzname[TZ_STRLEN_MAX + 1];
492 : : int dterr;
493 : : int tz;
494 : :
3694 alvherre@alvh.no-ip. 495 : 93 : text_to_cstring_buffer(zone, tzname, sizeof(tzname));
496 : :
497 : : /*
498 : : * Look up the requested timezone. First we try to interpret it as a
499 : : * numeric timezone specification; if DecodeTimezone decides it doesn't
500 : : * like the format, we try timezone abbreviations and names.
501 : : *
502 : : * Note pg_tzset happily parses numeric input that DecodeTimezone would
503 : : * reject. To avoid having it accept input that would otherwise be seen
504 : : * as invalid, it's enough to disallow having a digit in the first
505 : : * position of our input string.
506 : : */
3692 heikki.linnakangas@i 507 [ + + ]: 93 : if (isdigit((unsigned char) *tzname))
3694 alvherre@alvh.no-ip. 508 [ + - ]: 3 : ereport(ERROR,
509 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
510 : : errmsg("invalid input syntax for type %s: \"%s\"",
511 : : "numeric time zone", tzname),
512 : : errhint("Numeric time zones must have \"-\" or \"+\" as first character.")));
513 : :
492 tgl@sss.pgh.pa.us 514 : 90 : dterr = DecodeTimezone(tzname, &tz);
515 [ + + ]: 90 : if (dterr != 0)
516 : : {
517 : : int type,
518 : : val;
519 : : pg_tz *tzp;
520 : :
521 [ + + ]: 36 : if (dterr == DTERR_TZDISP_OVERFLOW)
3694 alvherre@alvh.no-ip. 522 [ + - ]: 6 : ereport(ERROR,
523 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
524 : : errmsg("numeric time zone \"%s\" out of range", tzname)));
492 tgl@sss.pgh.pa.us 525 [ - + ]: 30 : else if (dterr != DTERR_BAD_FORMAT)
3694 alvherre@alvh.no-ip. 526 [ # # ]:UBC 0 : ereport(ERROR,
527 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
528 : : errmsg("time zone \"%s\" not recognized", tzname)));
529 : :
394 tgl@sss.pgh.pa.us 530 :CBC 30 : type = DecodeTimezoneName(tzname, &val, &tzp);
531 : :
532 [ + + ]: 27 : if (type == TZNAME_FIXED_OFFSET)
533 : : {
534 : : /* fixed-offset abbreviation */
3468 535 : 6 : tz = -val;
536 : : }
394 537 [ + + ]: 21 : else if (type == TZNAME_DYNTZ)
538 : : {
539 : : /* dynamic-offset abbreviation, resolve using specified time */
3468 540 : 6 : tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp);
541 : : }
542 : : else
543 : : {
544 : : /* full zone name */
394 545 : 15 : tz = DetermineTimeZoneOffset(tm, tzp);
546 : : }
547 : : }
548 : :
3694 alvherre@alvh.no-ip. 549 : 81 : return tz;
550 : : }
551 : :
552 : : /*
553 : : * Look up the requested timezone, returning a pg_tz struct.
554 : : *
555 : : * This is the same as DecodeTimezoneNameToTz, but starting with a text Datum.
556 : : */
557 : : static pg_tz *
393 tgl@sss.pgh.pa.us 558 : 36 : lookup_timezone(text *zone)
559 : : {
560 : : char tzname[TZ_STRLEN_MAX + 1];
561 : :
562 : 36 : text_to_cstring_buffer(zone, tzname, sizeof(tzname));
563 : :
564 : 36 : return DecodeTimezoneNameToTz(tzname);
565 : : }
566 : :
567 : : /*
568 : : * make_timestamp_internal
569 : : * workhorse for make_timestamp and make_timestamptz
570 : : */
571 : : static Timestamp
3694 alvherre@alvh.no-ip. 572 : 105 : make_timestamp_internal(int year, int month, int day,
573 : : int hour, int min, double sec)
574 : : {
575 : : struct pg_tm tm;
576 : : TimeOffset date;
577 : : TimeOffset time;
578 : : int dterr;
1293 tgl@sss.pgh.pa.us 579 : 105 : bool bc = false;
580 : : Timestamp result;
581 : :
3694 alvherre@alvh.no-ip. 582 : 105 : tm.tm_year = year;
583 : 105 : tm.tm_mon = month;
584 : 105 : tm.tm_mday = day;
585 : :
586 : : /* Handle negative years as BC */
1293 tgl@sss.pgh.pa.us 587 [ + + ]: 105 : if (tm.tm_year < 0)
588 : : {
589 : 3 : bc = true;
590 : 3 : tm.tm_year = -tm.tm_year;
591 : : }
592 : :
593 : 105 : dterr = ValidateDate(DTK_DATE_M, false, false, bc, &tm);
594 : :
3694 alvherre@alvh.no-ip. 595 [ + + ]: 105 : if (dterr != 0)
596 [ + - ]: 3 : ereport(ERROR,
597 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
598 : : errmsg("date field value out of range: %d-%02d-%02d",
599 : : year, month, day)));
600 : :
601 [ - + - - : 102 : if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
- - - + -
- - - ]
3694 alvherre@alvh.no-ip. 602 [ # # ]:UBC 0 : ereport(ERROR,
603 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
604 : : errmsg("date out of range: %d-%02d-%02d",
605 : : year, month, day)));
606 : :
3694 alvherre@alvh.no-ip. 607 :CBC 102 : date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
608 : :
609 : : /* Check for time overflow */
1410 tgl@sss.pgh.pa.us 610 [ - + ]: 102 : if (float_time_overflows(hour, min, sec))
3694 alvherre@alvh.no-ip. 611 [ # # ]:UBC 0 : ereport(ERROR,
612 : : (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
613 : : errmsg("time field value out of range: %d:%02d:%02g",
614 : : hour, min, sec)));
615 : :
616 : : /* This should match tm2time */
3694 alvherre@alvh.no-ip. 617 :CBC 102 : time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE)
1410 tgl@sss.pgh.pa.us 618 : 102 : * USECS_PER_SEC) + (int64) rint(sec * USECS_PER_SEC);
619 : :
3694 alvherre@alvh.no-ip. 620 : 102 : result = date * USECS_PER_DAY + time;
621 : : /* check for major overflow */
622 [ - + ]: 102 : if ((result - time) / USECS_PER_DAY != date)
3694 alvherre@alvh.no-ip. 623 [ # # ]:UBC 0 : ereport(ERROR,
624 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
625 : : errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
626 : : year, month, day,
627 : : hour, min, sec)));
628 : :
629 : : /* check for just-barely overflow (okay except time-of-day wraps) */
630 : : /* caution: we want to allow 1999-12-31 24:00:00 */
3694 alvherre@alvh.no-ip. 631 [ + + + - :CBC 102 : if ((result < 0 && date > 0) ||
+ + ]
632 [ - + ]: 75 : (result > 0 && date < -1))
3694 alvherre@alvh.no-ip. 633 [ # # ]:UBC 0 : ereport(ERROR,
634 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
635 : : errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
636 : : year, month, day,
637 : : hour, min, sec)));
638 : :
639 : : /* final range check catches just-out-of-range timestamps */
2951 tgl@sss.pgh.pa.us 640 [ + - - + ]:CBC 102 : if (!IS_VALID_TIMESTAMP(result))
2951 tgl@sss.pgh.pa.us 641 [ # # ]:UBC 0 : ereport(ERROR,
642 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
643 : : errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g",
644 : : year, month, day,
645 : : hour, min, sec)));
646 : :
3694 alvherre@alvh.no-ip. 647 :CBC 102 : return result;
648 : : }
649 : :
650 : : /*
651 : : * make_timestamp() - timestamp constructor
652 : : */
653 : : Datum
654 : 9 : make_timestamp(PG_FUNCTION_ARGS)
655 : : {
656 : 9 : int32 year = PG_GETARG_INT32(0);
657 : 9 : int32 month = PG_GETARG_INT32(1);
658 : 9 : int32 mday = PG_GETARG_INT32(2);
659 : 9 : int32 hour = PG_GETARG_INT32(3);
660 : 9 : int32 min = PG_GETARG_INT32(4);
661 : 9 : float8 sec = PG_GETARG_FLOAT8(5);
662 : : Timestamp result;
663 : :
664 : 9 : result = make_timestamp_internal(year, month, mday,
665 : : hour, min, sec);
666 : :
667 : 6 : PG_RETURN_TIMESTAMP(result);
668 : : }
669 : :
670 : : /*
671 : : * make_timestamptz() - timestamp with time zone constructor
672 : : */
673 : : Datum
674 : 3 : make_timestamptz(PG_FUNCTION_ARGS)
675 : : {
676 : 3 : int32 year = PG_GETARG_INT32(0);
677 : 3 : int32 month = PG_GETARG_INT32(1);
678 : 3 : int32 mday = PG_GETARG_INT32(2);
679 : 3 : int32 hour = PG_GETARG_INT32(3);
680 : 3 : int32 min = PG_GETARG_INT32(4);
681 : 3 : float8 sec = PG_GETARG_FLOAT8(5);
682 : : Timestamp result;
683 : :
684 : 3 : result = make_timestamp_internal(year, month, mday,
685 : : hour, min, sec);
686 : :
687 : 3 : PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result));
688 : : }
689 : :
690 : : /*
691 : : * Construct a timestamp with time zone.
692 : : * As above, but the time zone is specified as seventh argument.
693 : : */
694 : : Datum
695 : 93 : make_timestamptz_at_timezone(PG_FUNCTION_ARGS)
696 : : {
697 : 93 : int32 year = PG_GETARG_INT32(0);
698 : 93 : int32 month = PG_GETARG_INT32(1);
699 : 93 : int32 mday = PG_GETARG_INT32(2);
700 : 93 : int32 hour = PG_GETARG_INT32(3);
701 : 93 : int32 min = PG_GETARG_INT32(4);
702 : 93 : float8 sec = PG_GETARG_FLOAT8(5);
703 : 93 : text *zone = PG_GETARG_TEXT_PP(6);
704 : : TimestampTz result;
705 : : Timestamp timestamp;
706 : : struct pg_tm tt;
707 : : int tz;
708 : : fsec_t fsec;
709 : :
710 : 93 : timestamp = make_timestamp_internal(year, month, mday,
711 : : hour, min, sec);
712 : :
713 [ - + ]: 93 : if (timestamp2tm(timestamp, NULL, &tt, &fsec, NULL, NULL) != 0)
3694 alvherre@alvh.no-ip. 714 [ # # ]:UBC 0 : ereport(ERROR,
715 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
716 : : errmsg("timestamp out of range")));
717 : :
3694 alvherre@alvh.no-ip. 718 :CBC 93 : tz = parse_sane_timezone(&tt, zone);
719 : :
2951 tgl@sss.pgh.pa.us 720 : 81 : result = dt2local(timestamp, -tz);
721 : :
722 [ + - - + ]: 81 : if (!IS_VALID_TIMESTAMP(result))
2951 tgl@sss.pgh.pa.us 723 [ # # ]:UBC 0 : ereport(ERROR,
724 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
725 : : errmsg("timestamp out of range")));
726 : :
2951 tgl@sss.pgh.pa.us 727 :CBC 81 : PG_RETURN_TIMESTAMPTZ(result);
728 : : }
729 : :
730 : : /*
731 : : * to_timestamp(double precision)
732 : : * Convert UNIX epoch to timestamptz.
733 : : */
734 : : Datum
2938 735 : 27 : float8_timestamptz(PG_FUNCTION_ARGS)
736 : : {
737 : 27 : float8 seconds = PG_GETARG_FLOAT8(0);
738 : : TimestampTz result;
739 : :
740 : : /* Deal with NaN and infinite inputs ... */
741 [ + + ]: 27 : if (isnan(seconds))
742 [ + - ]: 3 : ereport(ERROR,
743 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
744 : : errmsg("timestamp cannot be NaN")));
745 : :
746 [ + + ]: 24 : if (isinf(seconds))
747 : : {
748 [ + + ]: 6 : if (seconds < 0)
749 : 3 : TIMESTAMP_NOBEGIN(result);
750 : : else
751 : 3 : TIMESTAMP_NOEND(result);
752 : : }
753 : : else
754 : : {
755 : : /* Out of range? */
756 [ + - ]: 18 : if (seconds <
757 : : (float8) SECS_PER_DAY * (DATETIME_MIN_JULIAN - UNIX_EPOCH_JDATE)
2621 758 [ - + ]: 18 : || seconds >=
759 : : (float8) SECS_PER_DAY * (TIMESTAMP_END_JULIAN - UNIX_EPOCH_JDATE))
2938 tgl@sss.pgh.pa.us 760 [ # # ]:UBC 0 : ereport(ERROR,
761 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
762 : : errmsg("timestamp out of range: \"%g\"", seconds)));
763 : :
764 : : /* Convert UNIX epoch to Postgres epoch */
2938 tgl@sss.pgh.pa.us 765 :CBC 18 : seconds -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
766 : :
2621 767 : 18 : seconds = rint(seconds * USECS_PER_SEC);
768 : 18 : result = (int64) seconds;
769 : :
770 : : /* Recheck in case roundoff produces something just out of range */
2938 771 [ + - - + ]: 18 : if (!IS_VALID_TIMESTAMP(result))
2938 tgl@sss.pgh.pa.us 772 [ # # ]:UBC 0 : ereport(ERROR,
773 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
774 : : errmsg("timestamp out of range: \"%g\"",
775 : : PG_GETARG_FLOAT8(0))));
776 : : }
777 : :
2938 tgl@sss.pgh.pa.us 778 :CBC 24 : PG_RETURN_TIMESTAMP(result);
779 : : }
780 : :
781 : : /* timestamptz_out()
782 : : * Convert a timestamp to external form.
783 : : */
784 : : Datum
8234 lockhart@fourpalms.o 785 : 34753 : timestamptz_out(PG_FUNCTION_ARGS)
786 : : {
7643 tgl@sss.pgh.pa.us 787 : 34753 : TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0);
788 : : char *result;
789 : : int tz;
790 : : struct pg_tm tt,
9715 bruce@momjian.us 791 : 34753 : *tm = &tt;
792 : : fsec_t fsec;
793 : : const char *tzn;
794 : : char buf[MAXDATELEN + 1];
795 : :
8234 lockhart@fourpalms.o 796 [ + + + + ]: 34753 : if (TIMESTAMP_NOT_FINITE(dt))
8710 tgl@sss.pgh.pa.us 797 : 364 : EncodeSpecialTimestamp(dt, buf);
6878 bruce@momjian.us 798 [ + - ]: 34389 : else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0)
4414 peter_e@gmx.net 799 : 34389 : EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, buf);
800 : : else
7567 tgl@sss.pgh.pa.us 801 [ # # ]:UBC 0 : ereport(ERROR,
802 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
803 : : errmsg("timestamp out of range")));
804 : :
8710 tgl@sss.pgh.pa.us 805 :CBC 34753 : result = pstrdup(buf);
806 : 34753 : PG_RETURN_CSTRING(result);
807 : : }
808 : :
809 : : /*
810 : : * timestamptz_recv - converts external binary format to timestamptz
811 : : */
812 : : Datum
7643 tgl@sss.pgh.pa.us 813 :UBC 0 : timestamptz_recv(PG_FUNCTION_ARGS)
814 : : {
815 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
816 : :
817 : : #ifdef NOT_USED
818 : : Oid typelem = PG_GETARG_OID(1);
819 : : #endif
6853 820 : 0 : int32 typmod = PG_GETARG_INT32(2);
821 : : TimestampTz timestamp;
822 : : int tz;
823 : : struct pg_tm tt,
7255 824 : 0 : *tm = &tt;
825 : : fsec_t fsec;
826 : :
827 : 0 : timestamp = (TimestampTz) pq_getmsgint64(buf);
828 : :
829 : : /* range check: see if timestamptz_out would like it */
830 [ # # # # ]: 0 : if (TIMESTAMP_NOT_FINITE(timestamp))
831 : : /* ok */ ;
2951 832 [ # # ]: 0 : else if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0 ||
833 [ # # # # ]: 0 : !IS_VALID_TIMESTAMP(timestamp))
7255 834 [ # # ]: 0 : ereport(ERROR,
835 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
836 : : errmsg("timestamp out of range")));
837 : :
492 838 : 0 : AdjustTimestampForTypmod(×tamp, typmod, NULL);
839 : :
7255 840 : 0 : PG_RETURN_TIMESTAMPTZ(timestamp);
841 : : }
842 : :
843 : : /*
844 : : * timestamptz_send - converts timestamptz to binary format
845 : : */
846 : : Datum
7643 847 : 0 : timestamptz_send(PG_FUNCTION_ARGS)
848 : : {
7559 bruce@momjian.us 849 : 0 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
850 : : StringInfoData buf;
851 : :
7643 tgl@sss.pgh.pa.us 852 : 0 : pq_begintypsend(&buf);
853 : 0 : pq_sendint64(&buf, timestamp);
854 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
855 : : }
856 : :
857 : : Datum
6315 tgl@sss.pgh.pa.us 858 :CBC 48 : timestamptztypmodin(PG_FUNCTION_ARGS)
859 : : {
5995 bruce@momjian.us 860 : 48 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
861 : :
6315 tgl@sss.pgh.pa.us 862 : 48 : PG_RETURN_INT32(anytimestamp_typmodin(true, ta));
863 : : }
864 : :
865 : : Datum
866 : 5 : timestamptztypmodout(PG_FUNCTION_ARGS)
867 : : {
5995 bruce@momjian.us 868 : 5 : int32 typmod = PG_GETARG_INT32(0);
869 : :
6315 tgl@sss.pgh.pa.us 870 : 5 : PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod));
871 : : }
872 : :
873 : :
874 : : /* timestamptz_scale()
875 : : * Adjust time type for specified scale factor.
876 : : * Used by PostgreSQL type system to stuff columns.
877 : : */
878 : : Datum
8229 lockhart@fourpalms.o 879 : 228 : timestamptz_scale(PG_FUNCTION_ARGS)
880 : : {
7643 tgl@sss.pgh.pa.us 881 : 228 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
8229 lockhart@fourpalms.o 882 : 228 : int32 typmod = PG_GETARG_INT32(1);
883 : : TimestampTz result;
884 : :
885 : 228 : result = timestamp;
886 : :
492 tgl@sss.pgh.pa.us 887 : 228 : AdjustTimestampForTypmod(&result, typmod, NULL);
888 : :
8229 lockhart@fourpalms.o 889 : 228 : PG_RETURN_TIMESTAMPTZ(result);
890 : : }
891 : :
892 : :
893 : : /* interval_in()
894 : : * Convert a string to internal form.
895 : : *
896 : : * External format(s):
897 : : * Uses the generic date/time parsing and decoding routines.
898 : : */
899 : : Datum
8710 tgl@sss.pgh.pa.us 900 : 5851 : interval_in(PG_FUNCTION_ARGS)
901 : : {
902 : 5851 : char *str = PG_GETARG_CSTRING(0);
903 : : #ifdef NOT_USED
904 : : Oid typelem = PG_GETARG_OID(1);
905 : : #endif
8214 lockhart@fourpalms.o 906 : 5851 : int32 typmod = PG_GETARG_INT32(2);
492 tgl@sss.pgh.pa.us 907 : 5851 : Node *escontext = fcinfo->context;
908 : : Interval *result;
909 : : struct pg_itm_in tt,
743 910 : 5851 : *itm_in = &tt;
911 : : int dtype;
912 : : int nf;
913 : : int range;
914 : : int dterr;
915 : : char *field[MAXDATEFIELDS];
916 : : int ftype[MAXDATEFIELDS];
917 : : char workbuf[256];
918 : : DateTimeErrorExtra extra;
919 : :
920 : 5851 : itm_in->tm_year = 0;
921 : 5851 : itm_in->tm_mon = 0;
922 : 5851 : itm_in->tm_mday = 0;
923 : 5851 : itm_in->tm_usec = 0;
924 : :
5695 925 [ + + ]: 5851 : if (typmod >= 0)
926 : 168 : range = INTERVAL_RANGE(typmod);
927 : : else
928 : 5683 : range = INTERVAL_FULL_RANGE;
929 : :
6898 neilc@samurai.com 930 : 5851 : dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field,
931 : : ftype, MAXDATEFIELDS, &nf);
7536 tgl@sss.pgh.pa.us 932 [ + - ]: 5851 : if (dterr == 0)
5633 933 : 5851 : dterr = DecodeInterval(field, ftype, nf, range,
934 : : &dtype, itm_in);
935 : :
936 : : /* if those functions think it's a bad format, try ISO8601 style */
937 [ + + ]: 5851 : if (dterr == DTERR_BAD_FORMAT)
5421 bruce@momjian.us 938 : 306 : dterr = DecodeISO8601Interval(str,
939 : : &dtype, itm_in);
940 : :
7536 tgl@sss.pgh.pa.us 941 [ + + ]: 5851 : if (dterr != 0)
942 : : {
943 [ + + ]: 477 : if (dterr == DTERR_FIELD_OVERFLOW)
944 : 360 : dterr = DTERR_INTERVAL_OVERFLOW;
492 945 : 477 : DateTimeParseError(dterr, &extra, str, "interval", escontext);
946 : 12 : PG_RETURN_NULL();
947 : : }
948 : :
8234 lockhart@fourpalms.o 949 : 5374 : result = (Interval *) palloc(sizeof(Interval));
950 : :
8824 951 [ + + + - ]: 5374 : switch (dtype)
952 : : {
953 : 4888 : case DTK_DELTA:
743 tgl@sss.pgh.pa.us 954 [ + + ]: 4888 : if (itmin2interval(itm_in, result) != 0)
492 955 [ + - ]: 9 : ereturn(escontext, (Datum) 0,
956 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
957 : : errmsg("interval out of range")));
8234 lockhart@fourpalms.o 958 : 4879 : break;
959 : :
152 dean.a.rasheed@gmail 960 :GNC 285 : case DTK_LATE:
961 : 285 : INTERVAL_NOEND(result);
962 : 285 : break;
963 : :
964 : 201 : case DTK_EARLY:
965 : 201 : INTERVAL_NOBEGIN(result);
966 : 201 : break;
967 : :
9684 lockhart@fourpalms.o 968 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 969 [ # # ]: 0 : elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"",
970 : : dtype, str);
971 : : }
972 : :
492 tgl@sss.pgh.pa.us 973 :CBC 5365 : AdjustIntervalForTypmod(result, typmod, escontext);
974 : :
8234 lockhart@fourpalms.o 975 : 5359 : PG_RETURN_INTERVAL_P(result);
976 : : }
977 : :
978 : : /* interval_out()
979 : : * Convert a time span to external form.
980 : : */
981 : : Datum
8710 tgl@sss.pgh.pa.us 982 : 7708 : interval_out(PG_FUNCTION_ARGS)
983 : : {
984 : 7708 : Interval *span = PG_GETARG_INTERVAL_P(0);
985 : : char *result;
986 : : struct pg_itm tt,
743 987 : 7708 : *itm = &tt;
988 : : char buf[MAXDATELEN + 1];
989 : :
152 dean.a.rasheed@gmail 990 [ + + + + :GNC 7708 : if (INTERVAL_NOT_FINITE(span))
+ + + + +
+ + + ]
991 : 1006 : EncodeSpecialInterval(span, buf);
992 : : else
993 : : {
994 : 6702 : interval2itm(*span, itm);
995 : 6702 : EncodeInterval(itm, IntervalStyle, buf);
996 : : }
997 : :
8710 tgl@sss.pgh.pa.us 998 :CBC 7708 : result = pstrdup(buf);
999 : 7708 : PG_RETURN_CSTRING(result);
1000 : : }
1001 : :
1002 : : /*
1003 : : * interval_recv - converts external binary format to interval
1004 : : */
1005 : : Datum
7643 tgl@sss.pgh.pa.us 1006 :UBC 0 : interval_recv(PG_FUNCTION_ARGS)
1007 : : {
1008 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
1009 : :
1010 : : #ifdef NOT_USED
1011 : : Oid typelem = PG_GETARG_OID(1);
1012 : : #endif
6853 1013 : 0 : int32 typmod = PG_GETARG_INT32(2);
1014 : : Interval *interval;
1015 : :
7643 1016 : 0 : interval = (Interval *) palloc(sizeof(Interval));
1017 : :
6901 bruce@momjian.us 1018 : 0 : interval->time = pq_getmsgint64(buf);
6843 1019 : 0 : interval->day = pq_getmsgint(buf, sizeof(interval->day));
6901 1020 : 0 : interval->month = pq_getmsgint(buf, sizeof(interval->month));
1021 : :
492 tgl@sss.pgh.pa.us 1022 : 0 : AdjustIntervalForTypmod(interval, typmod, NULL);
1023 : :
7643 1024 : 0 : PG_RETURN_INTERVAL_P(interval);
1025 : : }
1026 : :
1027 : : /*
1028 : : * interval_send - converts interval to binary format
1029 : : */
1030 : : Datum
1031 : 0 : interval_send(PG_FUNCTION_ARGS)
1032 : : {
1033 : 0 : Interval *interval = PG_GETARG_INTERVAL_P(0);
1034 : : StringInfoData buf;
1035 : :
1036 : 0 : pq_begintypsend(&buf);
1037 : 0 : pq_sendint64(&buf, interval->time);
2377 andres@anarazel.de 1038 : 0 : pq_sendint32(&buf, interval->day);
1039 : 0 : pq_sendint32(&buf, interval->month);
7643 tgl@sss.pgh.pa.us 1040 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1041 : : }
1042 : :
1043 : : /*
1044 : : * The interval typmod stores a "range" in its high 16 bits and a "precision"
1045 : : * in its low 16 bits. Both contribute to defining the resolution of the
1046 : : * type. Range addresses resolution granules larger than one second, and
1047 : : * precision specifies resolution below one second. This representation can
1048 : : * express all SQL standard resolutions, but we implement them all in terms of
1049 : : * truncating rightward from some position. Range is a bitmap of permitted
1050 : : * fields, but only the temporally-smallest such field is significant to our
1051 : : * calculations. Precision is a count of sub-second decimal places to retain.
1052 : : * Setting all bits (INTERVAL_FULL_PRECISION) gives the same truncation
1053 : : * semantics as choosing MAX_INTERVAL_PRECISION.
1054 : : */
1055 : : Datum
6315 tgl@sss.pgh.pa.us 1056 :CBC 177 : intervaltypmodin(PG_FUNCTION_ARGS)
1057 : : {
5995 bruce@momjian.us 1058 : 177 : ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
1059 : : int32 *tl;
1060 : : int n;
1061 : : int32 typmod;
1062 : :
6148 tgl@sss.pgh.pa.us 1063 : 177 : tl = ArrayGetIntegerTypmods(ta, &n);
1064 : :
1065 : : /*
1066 : : * tl[0] - interval range (fields bitmask) tl[1] - precision (optional)
1067 : : *
1068 : : * Note we must validate tl[0] even though it's normally guaranteed
1069 : : * correct by the grammar --- consider SELECT 'foo'::"interval"(1000).
1070 : : */
6315 1071 [ + - ]: 177 : if (n > 0)
1072 : : {
1073 [ + - ]: 177 : switch (tl[0])
1074 : : {
1075 : 177 : case INTERVAL_MASK(YEAR):
1076 : : case INTERVAL_MASK(MONTH):
1077 : : case INTERVAL_MASK(DAY):
1078 : : case INTERVAL_MASK(HOUR):
1079 : : case INTERVAL_MASK(MINUTE):
1080 : : case INTERVAL_MASK(SECOND):
1081 : : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
1082 : : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
1083 : : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1084 : : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1085 : : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1086 : : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1087 : : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1088 : : case INTERVAL_FULL_RANGE:
1089 : : /* all OK */
1090 : 177 : break;
6315 tgl@sss.pgh.pa.us 1091 :UBC 0 : default:
1092 [ # # ]: 0 : ereport(ERROR,
1093 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1094 : : errmsg("invalid INTERVAL type modifier")));
1095 : : }
1096 : : }
1097 : :
6315 tgl@sss.pgh.pa.us 1098 [ + + ]:CBC 177 : if (n == 1)
1099 : : {
1100 [ + - ]: 129 : if (tl[0] != INTERVAL_FULL_RANGE)
1101 : 129 : typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]);
1102 : : else
6315 tgl@sss.pgh.pa.us 1103 :UBC 0 : typmod = -1;
1104 : : }
6315 tgl@sss.pgh.pa.us 1105 [ + - ]:CBC 48 : else if (n == 2)
1106 : : {
1107 [ - + ]: 48 : if (tl[1] < 0)
6315 tgl@sss.pgh.pa.us 1108 [ # # ]:UBC 0 : ereport(ERROR,
1109 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1110 : : errmsg("INTERVAL(%d) precision must not be negative",
1111 : : tl[1])));
6315 tgl@sss.pgh.pa.us 1112 [ - + ]:CBC 48 : if (tl[1] > MAX_INTERVAL_PRECISION)
1113 : : {
6315 tgl@sss.pgh.pa.us 1114 [ # # ]:UBC 0 : ereport(WARNING,
1115 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1116 : : errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
1117 : : tl[1], MAX_INTERVAL_PRECISION)));
1118 : 0 : typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]);
1119 : : }
1120 : : else
6315 tgl@sss.pgh.pa.us 1121 :CBC 48 : typmod = INTERVAL_TYPMOD(tl[1], tl[0]);
1122 : : }
1123 : : else
1124 : : {
6315 tgl@sss.pgh.pa.us 1125 [ # # ]:UBC 0 : ereport(ERROR,
1126 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1127 : : errmsg("invalid INTERVAL type modifier")));
1128 : : typmod = 0; /* keep compiler quiet */
1129 : : }
1130 : :
6315 tgl@sss.pgh.pa.us 1131 :CBC 177 : PG_RETURN_INT32(typmod);
1132 : : }
1133 : :
1134 : : Datum
6315 tgl@sss.pgh.pa.us 1135 :UBC 0 : intervaltypmodout(PG_FUNCTION_ARGS)
1136 : : {
5995 bruce@momjian.us 1137 : 0 : int32 typmod = PG_GETARG_INT32(0);
6315 tgl@sss.pgh.pa.us 1138 : 0 : char *res = (char *) palloc(64);
1139 : : int fields;
1140 : : int precision;
1141 : : const char *fieldstr;
1142 : :
1143 [ # # ]: 0 : if (typmod < 0)
1144 : : {
1145 : 0 : *res = '\0';
1146 : 0 : PG_RETURN_CSTRING(res);
1147 : : }
1148 : :
1149 : 0 : fields = INTERVAL_RANGE(typmod);
1150 : 0 : precision = INTERVAL_PRECISION(typmod);
1151 : :
1152 [ # # # # : 0 : switch (fields)
# # # # #
# # # # #
# ]
1153 : : {
1154 : 0 : case INTERVAL_MASK(YEAR):
1155 : 0 : fieldstr = " year";
1156 : 0 : break;
1157 : 0 : case INTERVAL_MASK(MONTH):
1158 : 0 : fieldstr = " month";
1159 : 0 : break;
1160 : 0 : case INTERVAL_MASK(DAY):
1161 : 0 : fieldstr = " day";
1162 : 0 : break;
1163 : 0 : case INTERVAL_MASK(HOUR):
1164 : 0 : fieldstr = " hour";
1165 : 0 : break;
1166 : 0 : case INTERVAL_MASK(MINUTE):
1167 : 0 : fieldstr = " minute";
1168 : 0 : break;
1169 : 0 : case INTERVAL_MASK(SECOND):
1170 : 0 : fieldstr = " second";
1171 : 0 : break;
1172 : 0 : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
1173 : 0 : fieldstr = " year to month";
1174 : 0 : break;
1175 : 0 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
1176 : 0 : fieldstr = " day to hour";
1177 : 0 : break;
1178 : 0 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1179 : 0 : fieldstr = " day to minute";
1180 : 0 : break;
1181 : 0 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1182 : 0 : fieldstr = " day to second";
1183 : 0 : break;
1184 : 0 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1185 : 0 : fieldstr = " hour to minute";
1186 : 0 : break;
1187 : 0 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1188 : 0 : fieldstr = " hour to second";
1189 : 0 : break;
1190 : 0 : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1191 : 0 : fieldstr = " minute to second";
1192 : 0 : break;
1193 : 0 : case INTERVAL_FULL_RANGE:
1194 : 0 : fieldstr = "";
1195 : 0 : break;
1196 : 0 : default:
1197 [ # # ]: 0 : elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod);
1198 : : fieldstr = "";
1199 : : break;
1200 : : }
1201 : :
1202 [ # # ]: 0 : if (precision != INTERVAL_FULL_PRECISION)
5694 1203 : 0 : snprintf(res, 64, "%s(%d)", fieldstr, precision);
1204 : : else
6315 1205 : 0 : snprintf(res, 64, "%s", fieldstr);
1206 : :
1207 : 0 : PG_RETURN_CSTRING(res);
1208 : : }
1209 : :
1210 : : /*
1211 : : * Given an interval typmod value, return a code for the least-significant
1212 : : * field that the typmod allows to be nonzero, for instance given
1213 : : * INTERVAL DAY TO HOUR we want to identify "hour".
1214 : : *
1215 : : * The results should be ordered by field significance, which means
1216 : : * we can't use the dt.h macros YEAR etc, because for some odd reason
1217 : : * they aren't ordered that way. Instead, arbitrarily represent
1218 : : * SECOND = 0, MINUTE = 1, HOUR = 2, DAY = 3, MONTH = 4, YEAR = 5.
1219 : : */
1220 : : static int
2665 tgl@sss.pgh.pa.us 1221 :CBC 18 : intervaltypmodleastfield(int32 typmod)
1222 : : {
1223 [ + + ]: 18 : if (typmod < 0)
1224 : 6 : return 0; /* SECOND */
1225 : :
1226 [ + + - - : 12 : switch (INTERVAL_RANGE(typmod))
- - - - +
- - - - -
- ]
1227 : : {
1228 : 3 : case INTERVAL_MASK(YEAR):
1229 : 3 : return 5; /* YEAR */
1230 : 6 : case INTERVAL_MASK(MONTH):
1231 : 6 : return 4; /* MONTH */
2665 tgl@sss.pgh.pa.us 1232 :UBC 0 : case INTERVAL_MASK(DAY):
1233 : 0 : return 3; /* DAY */
1234 : 0 : case INTERVAL_MASK(HOUR):
1235 : 0 : return 2; /* HOUR */
1236 : 0 : case INTERVAL_MASK(MINUTE):
1237 : 0 : return 1; /* MINUTE */
1238 : 0 : case INTERVAL_MASK(SECOND):
1239 : 0 : return 0; /* SECOND */
1240 : 0 : case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
1241 : 0 : return 4; /* MONTH */
1242 : 0 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
1243 : 0 : return 2; /* HOUR */
2665 tgl@sss.pgh.pa.us 1244 :CBC 3 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1245 : 3 : return 1; /* MINUTE */
2665 tgl@sss.pgh.pa.us 1246 :UBC 0 : case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1247 : 0 : return 0; /* SECOND */
1248 : 0 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
1249 : 0 : return 1; /* MINUTE */
1250 : 0 : case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1251 : 0 : return 0; /* SECOND */
1252 : 0 : case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
1253 : 0 : return 0; /* SECOND */
1254 : 0 : case INTERVAL_FULL_RANGE:
1255 : 0 : return 0; /* SECOND */
1256 : 0 : default:
1257 [ # # ]: 0 : elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod);
1258 : : break;
1259 : : }
1260 : : return 0; /* can't get here, but keep compiler quiet */
1261 : : }
1262 : :
1263 : :
1264 : : /*
1265 : : * interval_support()
1266 : : *
1267 : : * Planner support function for interval_scale().
1268 : : *
1269 : : * Flatten superfluous calls to interval_scale(). The interval typmod is
1270 : : * complex to permit accepting and regurgitating all SQL standard variations.
1271 : : * For truncation purposes, it boils down to a single, simple granularity.
1272 : : */
1273 : : Datum
1891 tgl@sss.pgh.pa.us 1274 :CBC 18 : interval_support(PG_FUNCTION_ARGS)
1275 : : {
1276 : 18 : Node *rawreq = (Node *) PG_GETARG_POINTER(0);
4449 rhaas@postgresql.org 1277 : 18 : Node *ret = NULL;
1278 : :
1891 tgl@sss.pgh.pa.us 1279 [ + + ]: 18 : if (IsA(rawreq, SupportRequestSimplify))
1280 : : {
1281 : 9 : SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq;
1282 : 9 : FuncExpr *expr = req->fcall;
1283 : : Node *typmod;
1284 : :
1285 [ - + ]: 9 : Assert(list_length(expr->args) >= 2);
1286 : :
1287 : 9 : typmod = (Node *) lsecond(expr->args);
1288 : :
1429 1289 [ + - + - ]: 9 : if (IsA(typmod, Const) && !((Const *) typmod)->constisnull)
1290 : : {
1891 1291 : 9 : Node *source = (Node *) linitial(expr->args);
1292 : 9 : int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
1293 : : bool noop;
1294 : :
1295 [ - + ]: 9 : if (new_typmod < 0)
1891 tgl@sss.pgh.pa.us 1296 :UBC 0 : noop = true;
1297 : : else
1298 : : {
1891 tgl@sss.pgh.pa.us 1299 :CBC 9 : int32 old_typmod = exprTypmod(source);
1300 : : int old_least_field;
1301 : : int new_least_field;
1302 : : int old_precis;
1303 : : int new_precis;
1304 : :
1305 : 9 : old_least_field = intervaltypmodleastfield(old_typmod);
1306 : 9 : new_least_field = intervaltypmodleastfield(new_typmod);
1307 [ + + ]: 9 : if (old_typmod < 0)
1308 : 6 : old_precis = INTERVAL_FULL_PRECISION;
1309 : : else
1310 : 3 : old_precis = INTERVAL_PRECISION(old_typmod);
1311 : 9 : new_precis = INTERVAL_PRECISION(new_typmod);
1312 : :
1313 : : /*
1314 : : * Cast is a no-op if least field stays the same or decreases
1315 : : * while precision stays the same or increases. But
1316 : : * precision, which is to say, sub-second precision, only
1317 : : * affects ranges that include SECOND.
1318 : : */
1319 [ - + - - ]: 9 : noop = (new_least_field <= old_least_field) &&
1891 tgl@sss.pgh.pa.us 1320 [ # # ]:UBC 0 : (old_least_field > 0 /* SECOND */ ||
1321 [ # # ]: 0 : new_precis >= MAX_INTERVAL_PRECISION ||
1322 : : new_precis >= old_precis);
1323 : : }
1891 tgl@sss.pgh.pa.us 1324 [ - + ]:CBC 9 : if (noop)
1891 tgl@sss.pgh.pa.us 1325 :UBC 0 : ret = relabel_to_typmod(source, new_typmod);
1326 : : }
1327 : : }
1328 : :
4449 rhaas@postgresql.org 1329 :CBC 18 : PG_RETURN_POINTER(ret);
1330 : : }
1331 : :
1332 : : /* interval_scale()
1333 : : * Adjust interval type for specified fields.
1334 : : * Used by PostgreSQL type system to stuff columns.
1335 : : */
1336 : : Datum
8214 lockhart@fourpalms.o 1337 : 108 : interval_scale(PG_FUNCTION_ARGS)
1338 : : {
1339 : 108 : Interval *interval = PG_GETARG_INTERVAL_P(0);
1340 : 108 : int32 typmod = PG_GETARG_INT32(1);
1341 : : Interval *result;
1342 : :
1343 : 108 : result = palloc(sizeof(Interval));
1344 : 108 : *result = *interval;
1345 : :
492 tgl@sss.pgh.pa.us 1346 : 108 : AdjustIntervalForTypmod(result, typmod, NULL);
1347 : :
8214 lockhart@fourpalms.o 1348 : 108 : PG_RETURN_INTERVAL_P(result);
1349 : : }
1350 : :
1351 : : /*
1352 : : * Adjust interval for specified precision, in both YEAR to SECOND
1353 : : * range and sub-second precision.
1354 : : *
1355 : : * Returns true on success, false on failure (if escontext points to an
1356 : : * ErrorSaveContext; otherwise errors are thrown).
1357 : : */
1358 : : static bool
492 tgl@sss.pgh.pa.us 1359 : 5473 : AdjustIntervalForTypmod(Interval *interval, int32 typmod,
1360 : : Node *escontext)
1361 : : {
1362 : : static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = {
1363 : : INT64CONST(1000000),
1364 : : INT64CONST(100000),
1365 : : INT64CONST(10000),
1366 : : INT64CONST(1000),
1367 : : INT64CONST(100),
1368 : : INT64CONST(10),
1369 : : INT64CONST(1)
1370 : : };
1371 : :
1372 : : static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
1373 : : INT64CONST(500000),
1374 : : INT64CONST(50000),
1375 : : INT64CONST(5000),
1376 : : INT64CONST(500),
1377 : : INT64CONST(50),
1378 : : INT64CONST(5),
1379 : : INT64CONST(0)
1380 : : };
1381 : :
1382 : : /* Typmod has no effect on infinite intervals */
152 dean.a.rasheed@gmail 1383 [ + + + + :GNC 5473 : if (INTERVAL_NOT_FINITE(interval))
+ + + + +
+ + + ]
1384 : 510 : return true;
1385 : :
1386 : : /*
1387 : : * Unspecified range and precision? Then not necessary to adjust. Setting
1388 : : * typmod to -1 is the convention for all data types.
1389 : : */
5695 tgl@sss.pgh.pa.us 1390 [ + + ]:CBC 4963 : if (typmod >= 0)
1391 : : {
7924 lockhart@fourpalms.o 1392 : 231 : int range = INTERVAL_RANGE(typmod);
1393 : 231 : int precision = INTERVAL_PRECISION(typmod);
1394 : :
1395 : : /*
1396 : : * Our interpretation of intervals with a limited set of fields is
1397 : : * that fields to the right of the last one specified are zeroed out,
1398 : : * but those to the left of it remain valid. Thus for example there
1399 : : * is no operational difference between INTERVAL YEAR TO MONTH and
1400 : : * INTERVAL MONTH. In some cases we could meaningfully enforce that
1401 : : * higher-order fields are zero; for example INTERVAL DAY could reject
1402 : : * nonzero "month" field. However that seems a bit pointless when we
1403 : : * can't do it consistently. (We cannot enforce a range limit on the
1404 : : * highest expected field, since we do not have any equivalent of
1405 : : * SQL's <interval leading field precision>.) If we ever decide to
1406 : : * revisit this, interval_support will likely require adjusting.
1407 : : *
1408 : : * Note: before PG 8.4 we interpreted a limited set of fields as
1409 : : * actually causing a "modulo" operation on a given value, potentially
1410 : : * losing high-order as well as low-order information. But there is
1411 : : * no support for such behavior in the standard, and it seems fairly
1412 : : * undesirable on data consistency grounds anyway. Now we only
1413 : : * perform truncation or rounding of low-order fields.
1414 : : */
1415 [ + + ]: 231 : if (range == INTERVAL_FULL_RANGE)
1416 : : {
1417 : : /* Do nothing... */
1418 : : }
1419 [ + + ]: 225 : else if (range == INTERVAL_MASK(YEAR))
1420 : : {
6842 bruce@momjian.us 1421 : 33 : interval->month = (interval->month / MONTHS_PER_YEAR) * MONTHS_PER_YEAR;
6843 1422 : 33 : interval->day = 0;
6901 1423 : 33 : interval->time = 0;
1424 : : }
7924 lockhart@fourpalms.o 1425 [ + + ]: 192 : else if (range == INTERVAL_MASK(MONTH))
1426 : : {
6843 bruce@momjian.us 1427 : 36 : interval->day = 0;
6901 1428 : 36 : interval->time = 0;
1429 : : }
1430 : : /* YEAR TO MONTH */
7924 lockhart@fourpalms.o 1431 [ + + ]: 156 : else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH)))
1432 : : {
6843 bruce@momjian.us 1433 : 9 : interval->day = 0;
6901 1434 : 9 : interval->time = 0;
1435 : : }
7924 lockhart@fourpalms.o 1436 [ + + ]: 147 : else if (range == INTERVAL_MASK(DAY))
1437 : : {
6843 bruce@momjian.us 1438 : 6 : interval->time = 0;
1439 : : }
7924 lockhart@fourpalms.o 1440 [ + + ]: 141 : else if (range == INTERVAL_MASK(HOUR))
1441 : : {
6901 bruce@momjian.us 1442 : 6 : interval->time = (interval->time / USECS_PER_HOUR) *
1443 : : USECS_PER_HOUR;
1444 : : }
7924 lockhart@fourpalms.o 1445 [ + + ]: 135 : else if (range == INTERVAL_MASK(MINUTE))
1446 : : {
6901 bruce@momjian.us 1447 : 6 : interval->time = (interval->time / USECS_PER_MINUTE) *
1448 : : USECS_PER_MINUTE;
1449 : : }
7924 lockhart@fourpalms.o 1450 [ + + ]: 129 : else if (range == INTERVAL_MASK(SECOND))
1451 : : {
1452 : : /* fractional-second rounding will be dealt with below */
1453 : : }
1454 : : /* DAY TO HOUR */
1455 [ + + ]: 111 : else if (range == (INTERVAL_MASK(DAY) |
1456 : : INTERVAL_MASK(HOUR)))
1457 : : {
6901 bruce@momjian.us 1458 : 12 : interval->time = (interval->time / USECS_PER_HOUR) *
1459 : : USECS_PER_HOUR;
1460 : : }
1461 : : /* DAY TO MINUTE */
7924 lockhart@fourpalms.o 1462 [ + + ]: 99 : else if (range == (INTERVAL_MASK(DAY) |
1463 : : INTERVAL_MASK(HOUR) |
1464 : : INTERVAL_MASK(MINUTE)))
1465 : : {
6901 bruce@momjian.us 1466 : 36 : interval->time = (interval->time / USECS_PER_MINUTE) *
1467 : : USECS_PER_MINUTE;
1468 : : }
1469 : : /* DAY TO SECOND */
7924 lockhart@fourpalms.o 1470 [ + + ]: 63 : else if (range == (INTERVAL_MASK(DAY) |
1471 : : INTERVAL_MASK(HOUR) |
1472 : : INTERVAL_MASK(MINUTE) |
1473 : : INTERVAL_MASK(SECOND)))
1474 : : {
1475 : : /* fractional-second rounding will be dealt with below */
1476 : : }
1477 : : /* HOUR TO MINUTE */
1478 [ + + ]: 45 : else if (range == (INTERVAL_MASK(HOUR) |
1479 : : INTERVAL_MASK(MINUTE)))
1480 : : {
6901 bruce@momjian.us 1481 : 6 : interval->time = (interval->time / USECS_PER_MINUTE) *
1482 : : USECS_PER_MINUTE;
1483 : : }
1484 : : /* HOUR TO SECOND */
7924 lockhart@fourpalms.o 1485 [ + + ]: 39 : else if (range == (INTERVAL_MASK(HOUR) |
1486 : : INTERVAL_MASK(MINUTE) |
1487 : : INTERVAL_MASK(SECOND)))
1488 : : {
1489 : : /* fractional-second rounding will be dealt with below */
1490 : : }
1491 : : /* MINUTE TO SECOND */
1492 [ - + ]: 27 : else if (range == (INTERVAL_MASK(MINUTE) |
1493 : : INTERVAL_MASK(SECOND)))
1494 : : {
1495 : : /* fractional-second rounding will be dealt with below */
1496 : : }
1497 : : else
7567 tgl@sss.pgh.pa.us 1498 [ # # ]:UBC 0 : elog(ERROR, "unrecognized interval typmod: %d", typmod);
1499 : :
1500 : : /* Need to adjust sub-second precision? */
7924 lockhart@fourpalms.o 1501 [ + + ]:CBC 231 : if (precision != INTERVAL_FULL_PRECISION)
1502 : : {
6901 bruce@momjian.us 1503 [ + - - + ]: 39 : if (precision < 0 || precision > MAX_INTERVAL_PRECISION)
492 tgl@sss.pgh.pa.us 1504 [ # # ]:UBC 0 : ereturn(escontext, false,
1505 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1506 : : errmsg("interval(%d) precision must be between %d and %d",
1507 : : precision, 0, MAX_INTERVAL_PRECISION)));
1508 : :
8029 lockhart@fourpalms.o 1509 [ + + ]:CBC 39 : if (interval->time >= INT64CONST(0))
1510 : : {
61 tgl@sss.pgh.pa.us 1511 [ + + ]:GNC 36 : if (pg_add_s64_overflow(interval->time,
1512 : 36 : IntervalOffsets[precision],
1513 : 36 : &interval->time))
1514 [ + - ]: 3 : ereturn(escontext, false,
1515 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1516 : : errmsg("interval out of range")));
1517 : 33 : interval->time -= interval->time % IntervalScales[precision];
1518 : : }
1519 : : else
1520 : : {
1521 [ + - ]: 3 : if (pg_sub_s64_overflow(interval->time,
1522 : 3 : IntervalOffsets[precision],
1523 : 3 : &interval->time))
1524 [ + - ]: 3 : ereturn(escontext, false,
1525 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1526 : : errmsg("interval out of range")));
61 tgl@sss.pgh.pa.us 1527 :UNC 0 : interval->time -= interval->time % IntervalScales[precision];
1528 : : }
1529 : : }
1530 : : }
1531 : :
492 tgl@sss.pgh.pa.us 1532 :CBC 4957 : return true;
1533 : : }
1534 : :
1535 : : /*
1536 : : * make_interval - numeric Interval constructor
1537 : : */
1538 : : Datum
3694 alvherre@alvh.no-ip. 1539 : 66 : make_interval(PG_FUNCTION_ARGS)
1540 : : {
1541 : 66 : int32 years = PG_GETARG_INT32(0);
1542 : 66 : int32 months = PG_GETARG_INT32(1);
1543 : 66 : int32 weeks = PG_GETARG_INT32(2);
1544 : 66 : int32 days = PG_GETARG_INT32(3);
1545 : 66 : int32 hours = PG_GETARG_INT32(4);
1546 : 66 : int32 mins = PG_GETARG_INT32(5);
1547 : 66 : double secs = PG_GETARG_FLOAT8(6);
1548 : : Interval *result;
1549 : :
1550 : : /*
1551 : : * Reject out-of-range inputs. We reject any input values that cause
1552 : : * integer overflow of the corresponding interval fields.
1553 : : */
3693 tgl@sss.pgh.pa.us 1554 [ + + + + ]: 66 : if (isinf(secs) || isnan(secs))
168 dean.a.rasheed@gmail 1555 :GNC 6 : goto out_of_range;
1556 : :
3694 alvherre@alvh.no-ip. 1557 :CBC 60 : result = (Interval *) palloc(sizeof(Interval));
1558 : :
1559 : : /* years and months -> months */
168 dean.a.rasheed@gmail 1560 [ + + + + ]:GNC 114 : if (pg_mul_s32_overflow(years, MONTHS_PER_YEAR, &result->month) ||
1561 : 54 : pg_add_s32_overflow(result->month, months, &result->month))
1562 : 12 : goto out_of_range;
1563 : :
1564 : : /* weeks and days -> days */
1565 [ + + + + ]: 90 : if (pg_mul_s32_overflow(weeks, DAYS_PER_WEEK, &result->day) ||
1566 : 42 : pg_add_s32_overflow(result->day, days, &result->day))
1567 : 12 : goto out_of_range;
1568 : :
1569 : : /* hours and mins -> usecs (cannot overflow 64-bit) */
1570 : 36 : result->time = hours * USECS_PER_HOUR + mins * USECS_PER_MINUTE;
1571 : :
1572 : : /* secs -> usecs */
1573 : 36 : secs = rint(float8_mul(secs, USECS_PER_SEC));
1574 [ + + + + : 60 : if (!FLOAT8_FITS_IN_INT64(secs) ||
+ + ]
1575 : 27 : pg_add_s64_overflow(result->time, (int64) secs, &result->time))
1576 : 12 : goto out_of_range;
1577 : :
1578 : : /* make sure that the result is finite */
152 1579 [ - + - - : 21 : if (INTERVAL_NOT_FINITE(result))
- - - + -
- - - ]
152 dean.a.rasheed@gmail 1580 :UNC 0 : goto out_of_range;
1581 : :
3694 alvherre@alvh.no-ip. 1582 :CBC 21 : PG_RETURN_INTERVAL_P(result);
1583 : :
168 dean.a.rasheed@gmail 1584 :GNC 42 : out_of_range:
1585 [ + - ]: 42 : ereport(ERROR,
1586 : : errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
1587 : : errmsg("interval out of range"));
1588 : :
1589 : : PG_RETURN_NULL(); /* keep compiler quiet */
1590 : : }
1591 : :
1592 : : /* EncodeSpecialTimestamp()
1593 : : * Convert reserved timestamp data type to string.
1594 : : */
1595 : : void
8824 lockhart@fourpalms.o 1596 :CBC 624 : EncodeSpecialTimestamp(Timestamp dt, char *str)
1597 : : {
8234 1598 [ + + ]: 624 : if (TIMESTAMP_IS_NOBEGIN(dt))
1599 : 308 : strcpy(str, EARLY);
1600 [ + - ]: 316 : else if (TIMESTAMP_IS_NOEND(dt))
1601 : 316 : strcpy(str, LATE);
1602 : : else /* shouldn't happen */
5661 tgl@sss.pgh.pa.us 1603 [ # # ]:UBC 0 : elog(ERROR, "invalid argument for EncodeSpecialTimestamp");
5661 tgl@sss.pgh.pa.us 1604 :CBC 624 : }
1605 : :
1606 : : static void
152 dean.a.rasheed@gmail 1607 :GNC 1006 : EncodeSpecialInterval(const Interval *interval, char *str)
1608 : : {
1609 [ + + + - : 1006 : if (INTERVAL_IS_NOBEGIN(interval))
+ - ]
1610 : 494 : strcpy(str, EARLY);
1611 [ + - + - : 512 : else if (INTERVAL_IS_NOEND(interval))
+ - ]
1612 : 512 : strcpy(str, LATE);
1613 : : else /* shouldn't happen */
152 dean.a.rasheed@gmail 1614 [ # # ]:UNC 0 : elog(ERROR, "invalid argument for EncodeSpecialInterval");
152 dean.a.rasheed@gmail 1615 :GNC 1006 : }
1616 : :
1617 : : Datum
8710 tgl@sss.pgh.pa.us 1618 :CBC 35278 : now(PG_FUNCTION_ARGS)
1619 : : {
6864 1620 : 35278 : PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp());
1621 : : }
1622 : :
1623 : : Datum
6564 bruce@momjian.us 1624 : 3 : statement_timestamp(PG_FUNCTION_ARGS)
1625 : : {
1626 : 3 : PG_RETURN_TIMESTAMPTZ(GetCurrentStatementStartTimestamp());
1627 : : }
1628 : :
1629 : : Datum
1630 : 16 : clock_timestamp(PG_FUNCTION_ARGS)
1631 : : {
1632 : 16 : PG_RETURN_TIMESTAMPTZ(GetCurrentTimestamp());
1633 : : }
1634 : :
1635 : : Datum
5824 tgl@sss.pgh.pa.us 1636 :UBC 0 : pg_postmaster_start_time(PG_FUNCTION_ARGS)
1637 : : {
6864 1638 : 0 : PG_RETURN_TIMESTAMPTZ(PgStartTime);
1639 : : }
1640 : :
1641 : : Datum
5824 1642 : 0 : pg_conf_load_time(PG_FUNCTION_ARGS)
1643 : : {
1644 : 0 : PG_RETURN_TIMESTAMPTZ(PgReloadTime);
1645 : : }
1646 : :
1647 : : /*
1648 : : * GetCurrentTimestamp -- get the current operating system time
1649 : : *
1650 : : * Result is in the form of a TimestampTz value, and is expressed to the
1651 : : * full precision of the gettimeofday() syscall
1652 : : */
1653 : : TimestampTz
6864 tgl@sss.pgh.pa.us 1654 :CBC 3735632 : GetCurrentTimestamp(void)
1655 : : {
1656 : : TimestampTz result;
1657 : : struct timeval tp;
1658 : :
1659 : 3735632 : gettimeofday(&tp, NULL);
1660 : :
6820 1661 : 3735632 : result = (TimestampTz) tp.tv_sec -
1662 : : ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
4176 heikki.linnakangas@i 1663 : 3735632 : result = (result * USECS_PER_SEC) + tp.tv_usec;
1664 : :
1665 : 3735632 : return result;
1666 : : }
1667 : :
1668 : : /*
1669 : : * GetSQLCurrentTimestamp -- implements CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(n)
1670 : : */
1671 : : TimestampTz
333 michael@paquier.xyz 1672 : 174 : GetSQLCurrentTimestamp(int32 typmod)
1673 : : {
1674 : : TimestampTz ts;
1675 : :
2798 tgl@sss.pgh.pa.us 1676 : 174 : ts = GetCurrentTransactionStartTimestamp();
1677 [ + + ]: 174 : if (typmod >= 0)
492 1678 : 36 : AdjustTimestampForTypmod(&ts, typmod, NULL);
333 michael@paquier.xyz 1679 : 174 : return ts;
1680 : : }
1681 : :
1682 : : /*
1683 : : * GetSQLLocalTimestamp -- implements LOCALTIMESTAMP, LOCALTIMESTAMP(n)
1684 : : */
1685 : : Timestamp
1686 : 33 : GetSQLLocalTimestamp(int32 typmod)
1687 : : {
1688 : : Timestamp ts;
1689 : :
2798 tgl@sss.pgh.pa.us 1690 : 33 : ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp());
1691 [ + + ]: 33 : if (typmod >= 0)
492 1692 : 3 : AdjustTimestampForTypmod(&ts, typmod, NULL);
333 michael@paquier.xyz 1693 : 33 : return ts;
1694 : : }
1695 : :
1696 : : /*
1697 : : * timeofday(*) -- returns the current time as a text.
1698 : : */
1699 : : Datum
2012 andres@anarazel.de 1700 : 400 : timeofday(PG_FUNCTION_ARGS)
1701 : : {
1702 : : struct timeval tp;
1703 : : char templ[128];
1704 : : char buf[128];
1705 : : pg_time_t tt;
1706 : :
1707 : 400 : gettimeofday(&tp, NULL);
1708 : 400 : tt = (pg_time_t) tp.tv_sec;
1709 : 400 : pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z",
1710 : 400 : pg_localtime(&tt, session_timezone));
1711 : 400 : snprintf(buf, sizeof(buf), templ, tp.tv_usec);
1712 : :
1713 : 400 : PG_RETURN_TEXT_P(cstring_to_text(buf));
1714 : : }
1715 : :
1716 : : /*
1717 : : * TimestampDifference -- convert the difference between two timestamps
1718 : : * into integer seconds and microseconds
1719 : : *
1720 : : * This is typically used to calculate a wait timeout for select(2),
1721 : : * which explains the otherwise-odd choice of output format.
1722 : : *
1723 : : * Both inputs must be ordinary finite timestamps (in current usage,
1724 : : * they'll be results from GetCurrentTimestamp()).
1725 : : *
1726 : : * We expect start_time <= stop_time. If not, we return zeros,
1727 : : * since then we're already past the previously determined stop_time.
1728 : : */
1729 : : void
6508 tgl@sss.pgh.pa.us 1730 : 530449 : TimestampDifference(TimestampTz start_time, TimestampTz stop_time,
1731 : : long *secs, int *microsecs)
1732 : : {
1733 : 530449 : TimestampTz diff = stop_time - start_time;
1734 : :
1735 [ + + ]: 530449 : if (diff <= 0)
1736 : : {
1737 : 3 : *secs = 0;
1738 : 3 : *microsecs = 0;
1739 : : }
1740 : : else
1741 : : {
1742 : 530446 : *secs = (long) (diff / USECS_PER_SEC);
1743 : 530446 : *microsecs = (int) (diff % USECS_PER_SEC);
1744 : : }
1745 : 530449 : }
1746 : :
1747 : : /*
1748 : : * TimestampDifferenceMilliseconds -- convert the difference between two
1749 : : * timestamps into integer milliseconds
1750 : : *
1751 : : * This is typically used to calculate a wait timeout for WaitLatch()
1752 : : * or a related function. The choice of "long" as the result type
1753 : : * is to harmonize with that; furthermore, we clamp the result to at most
1754 : : * INT_MAX milliseconds, because that's all that WaitLatch() allows.
1755 : : *
1756 : : * We expect start_time <= stop_time. If not, we return zero,
1757 : : * since then we're already past the previously determined stop_time.
1758 : : *
1759 : : * Subtracting finite and infinite timestamps works correctly, returning
1760 : : * zero or INT_MAX as appropriate.
1761 : : *
1762 : : * Note we round up any fractional millisecond, since waiting for just
1763 : : * less than the intended timeout is undesirable.
1764 : : */
1765 : : long
1251 1766 : 154500 : TimestampDifferenceMilliseconds(TimestampTz start_time, TimestampTz stop_time)
1767 : : {
1768 : : TimestampTz diff;
1769 : :
1770 : : /* Deal with zero or negative elapsed time quickly. */
444 1771 [ + + ]: 154500 : if (start_time >= stop_time)
1251 1772 : 11 : return 0;
1773 : : /* To not fail with timestamp infinities, we must detect overflow. */
444 1774 [ - + ]: 154489 : if (pg_sub_s64_overflow(stop_time, start_time, &diff))
444 tgl@sss.pgh.pa.us 1775 :UBC 0 : return (long) INT_MAX;
444 tgl@sss.pgh.pa.us 1776 [ - + ]:CBC 154489 : if (diff >= (INT_MAX * INT64CONST(1000) - 999))
444 tgl@sss.pgh.pa.us 1777 :UBC 0 : return (long) INT_MAX;
1778 : : else
1251 tgl@sss.pgh.pa.us 1779 :CBC 154489 : return (long) ((diff + 999) / 1000);
1780 : : }
1781 : :
1782 : : /*
1783 : : * TimestampDifferenceExceeds -- report whether the difference between two
1784 : : * timestamps is >= a threshold (expressed in milliseconds)
1785 : : *
1786 : : * Both inputs must be ordinary finite timestamps (in current usage,
1787 : : * they'll be results from GetCurrentTimestamp()).
1788 : : */
1789 : : bool
6194 1790 : 486656 : TimestampDifferenceExceeds(TimestampTz start_time,
1791 : : TimestampTz stop_time,
1792 : : int msec)
1793 : : {
1794 : 486656 : TimestampTz diff = stop_time - start_time;
1795 : :
1796 : 486656 : return (diff >= msec * INT64CONST(1000));
1797 : : }
1798 : :
1799 : : /*
1800 : : * Convert a time_t to TimestampTz.
1801 : : *
1802 : : * We do not use time_t internally in Postgres, but this is provided for use
1803 : : * by functions that need to interpret, say, a stat(2) result.
1804 : : *
1805 : : * To avoid having the function's ABI vary depending on the width of time_t,
1806 : : * we declare the argument as pg_time_t, which is cast-compatible with
1807 : : * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1808 : : * This detail should be invisible to callers, at least at source code level.
1809 : : */
1810 : : TimestampTz
5901 1811 : 21309 : time_t_to_timestamptz(pg_time_t tm)
1812 : : {
1813 : : TimestampTz result;
1814 : :
6820 1815 : 21309 : result = (TimestampTz) tm -
1816 : : ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
1817 : 21309 : result *= USECS_PER_SEC;
1818 : :
1819 : 21309 : return result;
1820 : : }
1821 : :
1822 : : /*
1823 : : * Convert a TimestampTz to time_t.
1824 : : *
1825 : : * This too is just marginally useful, but some places need it.
1826 : : *
1827 : : * To avoid having the function's ABI vary depending on the width of time_t,
1828 : : * we declare the result as pg_time_t, which is cast-compatible with
1829 : : * time_t but always 64 bits wide (unless the platform has no 64-bit type).
1830 : : * This detail should be invisible to callers, at least at source code level.
1831 : : */
1832 : : pg_time_t
6508 1833 : 21141 : timestamptz_to_time_t(TimestampTz t)
1834 : : {
1835 : : pg_time_t result;
1836 : :
5901 1837 : 21141 : result = (pg_time_t) (t / USECS_PER_SEC +
1838 : : ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY));
1839 : :
6508 1840 : 21141 : return result;
1841 : : }
1842 : :
1843 : : /*
1844 : : * Produce a C-string representation of a TimestampTz.
1845 : : *
1846 : : * This is mostly for use in emitting messages. The primary difference
1847 : : * from timestamptz_out is that we force the output format to ISO. Note
1848 : : * also that the result is in a static buffer, not pstrdup'd.
1849 : : *
1850 : : * See also pg_strftime.
1851 : : */
1852 : : const char *
6194 1853 : 1352 : timestamptz_to_str(TimestampTz t)
1854 : : {
1855 : : static char buf[MAXDATELEN + 1];
1856 : : int tz;
1857 : : struct pg_tm tt,
1858 : 1352 : *tm = &tt;
1859 : : fsec_t fsec;
1860 : : const char *tzn;
1861 : :
1862 [ + - - + ]: 1352 : if (TIMESTAMP_NOT_FINITE(t))
6194 tgl@sss.pgh.pa.us 1863 :UBC 0 : EncodeSpecialTimestamp(t, buf);
6194 tgl@sss.pgh.pa.us 1864 [ + - ]:CBC 1352 : else if (timestamp2tm(t, &tz, tm, &fsec, &tzn, NULL) == 0)
4414 peter_e@gmx.net 1865 : 1352 : EncodeDateTime(tm, fsec, true, tz, tzn, USE_ISO_DATES, buf);
1866 : : else
6194 tgl@sss.pgh.pa.us 1867 :UBC 0 : strlcpy(buf, "(timestamp out of range)", sizeof(buf));
1868 : :
6194 tgl@sss.pgh.pa.us 1869 :CBC 1352 : return buf;
1870 : : }
1871 : :
1872 : :
1873 : : void
8029 lockhart@fourpalms.o 1874 : 126836 : dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec)
1875 : : {
1876 : : TimeOffset time;
1877 : :
8824 1878 : 126836 : time = jd;
1879 : :
6901 bruce@momjian.us 1880 : 126836 : *hour = time / USECS_PER_HOUR;
1881 : 126836 : time -= (*hour) * USECS_PER_HOUR;
1882 : 126836 : *min = time / USECS_PER_MINUTE;
1883 : 126836 : time -= (*min) * USECS_PER_MINUTE;
1884 : 126836 : *sec = time / USECS_PER_SEC;
1885 : 126836 : *fsec = time - (*sec * USECS_PER_SEC);
2489 tgl@sss.pgh.pa.us 1886 : 126836 : } /* dt2time() */
1887 : :
1888 : :
1889 : : /*
1890 : : * timestamp2tm() - Convert timestamp data type to POSIX time structure.
1891 : : *
1892 : : * Note that year is _not_ 1900-based, but is an explicit full value.
1893 : : * Also, month is one-based, _not_ zero-based.
1894 : : * Returns:
1895 : : * 0 on success
1896 : : * -1 on out of range
1897 : : *
1898 : : * If attimezone is NULL, the global timezone setting will be used.
1899 : : */
1900 : : int
1901 : 126830 : timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, const char **tzn, pg_tz *attimezone)
1902 : : {
1903 : : Timestamp date;
1904 : : Timestamp time;
1905 : : pg_time_t utime;
1906 : :
1907 : : /* Use session timezone if caller asks for default */
3817 1908 [ + + ]: 126830 : if (attimezone == NULL)
1909 : 117653 : attimezone = session_timezone;
1910 : :
6762 1911 : 126830 : time = dt;
6901 bruce@momjian.us 1912 [ + + ]: 126830 : TMODULO(time, date, USECS_PER_DAY);
1913 : :
8029 lockhart@fourpalms.o 1914 [ + + ]: 126830 : if (time < INT64CONST(0))
1915 : : {
6901 bruce@momjian.us 1916 : 50086 : time += USECS_PER_DAY;
1917 : 50086 : date -= 1;
1918 : : }
1919 : :
1920 : : /* add offset to go from J2000 back to standard Julian date */
6762 tgl@sss.pgh.pa.us 1921 : 126830 : date += POSTGRES_EPOCH_JDATE;
1922 : :
1923 : : /* Julian day routine does not work for negative Julian days */
1924 [ + - - + ]: 126830 : if (date < 0 || date > (Timestamp) INT_MAX)
6762 tgl@sss.pgh.pa.us 1925 :UBC 0 : return -1;
1926 : :
6762 tgl@sss.pgh.pa.us 1927 :CBC 126830 : j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
1928 : 126830 : dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec);
1929 : :
1930 : : /* Done if no TZ conversion wanted */
7255 1931 [ + + ]: 126830 : if (tzp == NULL)
1932 : : {
1933 : 41734 : tm->tm_isdst = -1;
1934 : 41734 : tm->tm_gmtoff = 0;
1935 : 41734 : tm->tm_zone = NULL;
1936 [ - + ]: 41734 : if (tzn != NULL)
7255 tgl@sss.pgh.pa.us 1937 :UBC 0 : *tzn = NULL;
7255 tgl@sss.pgh.pa.us 1938 :CBC 41734 : return 0;
1939 : : }
1940 : :
1941 : : /*
1942 : : * If the time falls within the range of pg_time_t, use pg_localtime() to
1943 : : * rotate to the local time zone.
1944 : : *
1945 : : * First, convert to an integral timestamp, avoiding possibly
1946 : : * platform-specific roundoff-in-wrong-direction errors, and adjust to
1947 : : * Unix epoch. Then see if we can convert to pg_time_t without loss. This
1948 : : * coding avoids hardwiring any assumptions about the width of pg_time_t,
1949 : : * so it should behave sanely on machines without int64.
1950 : : */
6901 bruce@momjian.us 1951 : 85096 : dt = (dt - *fsec) / USECS_PER_SEC +
1952 : : (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY;
7255 tgl@sss.pgh.pa.us 1953 : 85096 : utime = (pg_time_t) dt;
1954 [ + - ]: 85096 : if ((Timestamp) utime == dt)
1955 : : {
3817 1956 : 85096 : struct pg_tm *tx = pg_localtime(&utime, attimezone);
1957 : :
7255 1958 : 85096 : tm->tm_year = tx->tm_year + 1900;
1959 : 85096 : tm->tm_mon = tx->tm_mon + 1;
1960 : 85096 : tm->tm_mday = tx->tm_mday;
1961 : 85096 : tm->tm_hour = tx->tm_hour;
1962 : 85096 : tm->tm_min = tx->tm_min;
1963 : 85096 : tm->tm_sec = tx->tm_sec;
1964 : 85096 : tm->tm_isdst = tx->tm_isdst;
1965 : 85096 : tm->tm_gmtoff = tx->tm_gmtoff;
1966 : 85096 : tm->tm_zone = tx->tm_zone;
6841 bruce@momjian.us 1967 : 85096 : *tzp = -tm->tm_gmtoff;
7255 tgl@sss.pgh.pa.us 1968 [ + + ]: 85096 : if (tzn != NULL)
4413 peter_e@gmx.net 1969 : 42836 : *tzn = tm->tm_zone;
1970 : : }
1971 : : else
1972 : : {
1973 : : /*
1974 : : * When out of range of pg_time_t, treat as GMT
1975 : : */
7255 tgl@sss.pgh.pa.us 1976 :UBC 0 : *tzp = 0;
1977 : : /* Mark this as *no* time zone available */
8212 lockhart@fourpalms.o 1978 : 0 : tm->tm_isdst = -1;
7255 tgl@sss.pgh.pa.us 1979 : 0 : tm->tm_gmtoff = 0;
1980 : 0 : tm->tm_zone = NULL;
8824 lockhart@fourpalms.o 1981 [ # # ]: 0 : if (tzn != NULL)
1982 : 0 : *tzn = NULL;
1983 : : }
1984 : :
8824 lockhart@fourpalms.o 1985 :CBC 85096 : return 0;
1986 : : }
1987 : :
1988 : :
1989 : : /* tm2timestamp()
1990 : : * Convert a tm structure to a timestamp data type.
1991 : : * Note that year is _not_ 1900-based, but is an explicit full value.
1992 : : * Also, month is one-based, _not_ zero-based.
1993 : : *
1994 : : * Returns -1 on failure (value out of range).
1995 : : */
1996 : : int
2489 tgl@sss.pgh.pa.us 1997 : 82652 : tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result)
1998 : : {
1999 : : TimeOffset date;
2000 : : TimeOffset time;
2001 : :
2002 : : /* Prevent overflow in Julian-day routines */
8824 lockhart@fourpalms.o 2003 [ + + + + : 82652 : if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday))
+ - - + -
- - - ]
2004 : : {
6364 tgl@sss.pgh.pa.us 2005 : 6 : *result = 0; /* keep compiler quiet */
8824 lockhart@fourpalms.o 2006 : 6 : return -1;
2007 : : }
2008 : :
7681 tgl@sss.pgh.pa.us 2009 : 82646 : date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
8029 lockhart@fourpalms.o 2010 : 82646 : time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec);
2011 : :
6901 bruce@momjian.us 2012 : 82646 : *result = date * USECS_PER_DAY + time;
2013 : : /* check for major overflow */
2014 [ + + ]: 82646 : if ((*result - time) / USECS_PER_DAY != date)
2015 : : {
6364 tgl@sss.pgh.pa.us 2016 : 3 : *result = 0; /* keep compiler quiet */
7590 2017 : 3 : return -1;
2018 : : }
2019 : : /* check for just-barely overflow (okay except time-of-day wraps) */
2020 : : /* caution: we want to allow 1999-12-31 24:00:00 */
4059 2021 [ + + + - ]: 82643 : if ((*result < 0 && date > 0) ||
2022 [ + + - + ]: 82643 : (*result > 0 && date < -1))
2023 : : {
6364 tgl@sss.pgh.pa.us 2024 :UBC 0 : *result = 0; /* keep compiler quiet */
7590 2025 : 0 : return -1;
2026 : : }
8824 lockhart@fourpalms.o 2027 [ + + ]:CBC 82643 : if (tzp != NULL)
2028 : 25060 : *result = dt2local(*result, -(*tzp));
2029 : :
2030 : : /* final range check catches just-out-of-range timestamps */
2951 tgl@sss.pgh.pa.us 2031 [ + + + + ]: 82643 : if (!IS_VALID_TIMESTAMP(*result))
2032 : : {
2033 : 12 : *result = 0; /* keep compiler quiet */
2034 : 12 : return -1;
2035 : : }
2036 : :
8824 lockhart@fourpalms.o 2037 : 82631 : return 0;
2038 : : }
2039 : :
2040 : :
2041 : : /* interval2itm()
2042 : : * Convert an Interval to a pg_itm structure.
2043 : : * Note: overflow is not possible, because the pg_itm fields are
2044 : : * wide enough for all possible conversion results.
2045 : : */
2046 : : void
743 tgl@sss.pgh.pa.us 2047 : 7342 : interval2itm(Interval span, struct pg_itm *itm)
2048 : : {
2049 : : TimeOffset time;
2050 : : TimeOffset tfrac;
2051 : :
2052 : 7342 : itm->tm_year = span.month / MONTHS_PER_YEAR;
2053 : 7342 : itm->tm_mon = span.month % MONTHS_PER_YEAR;
2054 : 7342 : itm->tm_mday = span.day;
8824 lockhart@fourpalms.o 2055 : 7342 : time = span.time;
2056 : :
6746 tgl@sss.pgh.pa.us 2057 : 7342 : tfrac = time / USECS_PER_HOUR;
2058 : 7342 : time -= tfrac * USECS_PER_HOUR;
743 2059 : 7342 : itm->tm_hour = tfrac;
6746 2060 : 7342 : tfrac = time / USECS_PER_MINUTE;
2061 : 7342 : time -= tfrac * USECS_PER_MINUTE;
743 2062 : 7342 : itm->tm_min = (int) tfrac;
6746 2063 : 7342 : tfrac = time / USECS_PER_SEC;
743 2064 : 7342 : time -= tfrac * USECS_PER_SEC;
2065 : 7342 : itm->tm_sec = (int) tfrac;
2066 : 7342 : itm->tm_usec = (int) time;
2067 : 7342 : }
2068 : :
2069 : : /* itm2interval()
2070 : : * Convert a pg_itm structure to an Interval.
2071 : : * Returns 0 if OK, -1 on overflow.
2072 : : *
2073 : : * This is for use in computations expected to produce finite results. Any
2074 : : * inputs that lead to infinite results are treated as overflows.
2075 : : */
2076 : : int
743 tgl@sss.pgh.pa.us 2077 :UBC 0 : itm2interval(struct pg_itm *itm, Interval *span)
2078 : : {
2079 : 0 : int64 total_months = (int64) itm->tm_year * MONTHS_PER_YEAR + itm->tm_mon;
2080 : :
2081 [ # # # # ]: 0 : if (total_months > INT_MAX || total_months < INT_MIN)
2082 : 0 : return -1;
2083 : 0 : span->month = (int32) total_months;
2084 : 0 : span->day = itm->tm_mday;
2085 [ # # ]: 0 : if (pg_mul_s64_overflow(itm->tm_hour, USECS_PER_HOUR,
2086 : 0 : &span->time))
2087 : 0 : return -1;
2088 : : /* tm_min, tm_sec are 32 bits, so intermediate products can't overflow */
2089 [ # # ]: 0 : if (pg_add_s64_overflow(span->time, itm->tm_min * USECS_PER_MINUTE,
2090 : 0 : &span->time))
2091 : 0 : return -1;
2092 [ # # ]: 0 : if (pg_add_s64_overflow(span->time, itm->tm_sec * USECS_PER_SEC,
2093 : 0 : &span->time))
2094 : 0 : return -1;
2095 [ # # ]: 0 : if (pg_add_s64_overflow(span->time, itm->tm_usec,
2096 : 0 : &span->time))
2097 : 0 : return -1;
152 dean.a.rasheed@gmail 2098 [ # # # # :UNC 0 : if (INTERVAL_NOT_FINITE(span))
# # # # #
# # # ]
2099 : 0 : return -1;
8824 lockhart@fourpalms.o 2100 :UBC 0 : return 0;
2101 : : }
2102 : :
2103 : : /* itmin2interval()
2104 : : * Convert a pg_itm_in structure to an Interval.
2105 : : * Returns 0 if OK, -1 on overflow.
2106 : : *
2107 : : * Note: if the result is infinite, it is not treated as an overflow. This
2108 : : * avoids any dump/reload hazards from pre-17 databases that do not support
2109 : : * infinite intervals, but do allow finite intervals with all fields set to
2110 : : * INT_MIN/INT_MAX (outside the documented range). Such intervals will be
2111 : : * silently converted to +/-infinity. This may not be ideal, but seems
2112 : : * preferable to failure, and ought to be pretty unlikely in practice.
2113 : : */
2114 : : int
743 tgl@sss.pgh.pa.us 2115 :CBC 11425 : itmin2interval(struct pg_itm_in *itm_in, Interval *span)
2116 : : {
2117 : 11425 : int64 total_months = (int64) itm_in->tm_year * MONTHS_PER_YEAR + itm_in->tm_mon;
2118 : :
3727 bruce@momjian.us 2119 [ + + + + ]: 11425 : if (total_months > INT_MAX || total_months < INT_MIN)
2120 : 9 : return -1;
743 tgl@sss.pgh.pa.us 2121 : 11416 : span->month = (int32) total_months;
2122 : 11416 : span->day = itm_in->tm_mday;
2123 : 11416 : span->time = itm_in->tm_usec;
8824 lockhart@fourpalms.o 2124 : 11416 : return 0;
2125 : : }
2126 : :
2127 : : static TimeOffset
8029 2128 : 82646 : time2t(const int hour, const int min, const int sec, const fsec_t fsec)
2129 : : {
6842 bruce@momjian.us 2130 : 82646 : return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec;
2131 : : }
2132 : :
2133 : : static Timestamp
572 pg@bowt.ie 2134 : 33301 : dt2local(Timestamp dt, int timezone)
2135 : : {
2136 : 33301 : dt -= (timezone * USECS_PER_SEC);
8824 lockhart@fourpalms.o 2137 : 33301 : return dt;
2138 : : }
2139 : :
2140 : :
2141 : : /*****************************************************************************
2142 : : * PUBLIC ROUTINES *
2143 : : *****************************************************************************/
2144 : :
2145 : :
2146 : : Datum
8710 tgl@sss.pgh.pa.us 2147 :LBC (48) : timestamp_finite(PG_FUNCTION_ARGS)
2148 : : {
6756 bruce@momjian.us 2149 : (48) : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
2150 : :
8424 2151 [ # # # # ]: (48) : PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp));
2152 : : }
2153 : :
2154 : : Datum
8710 tgl@sss.pgh.pa.us 2155 :GBC 153 : interval_finite(PG_FUNCTION_ARGS)
2156 : : {
152 dean.a.rasheed@gmail 2157 :GNC 153 : Interval *interval = PG_GETARG_INTERVAL_P(0);
2158 : :
2159 [ + + + - : 153 : PG_RETURN_BOOL(!INTERVAL_NOT_FINITE(interval));
- + + + +
- - + ]
2160 : : }
2161 : :
2162 : :
2163 : : /*----------------------------------------------------------
2164 : : * Relational operators for timestamp.
2165 : : *---------------------------------------------------------*/
2166 : :
2167 : : void
2489 tgl@sss.pgh.pa.us 2168 :CBC 14190 : GetEpochTime(struct pg_tm *tm)
2169 : : {
2170 : : struct pg_tm *t0;
7168 bruce@momjian.us 2171 : 14190 : pg_time_t epoch = 0;
2172 : :
7268 tgl@sss.pgh.pa.us 2173 : 14190 : t0 = pg_gmtime(&epoch);
2174 : :
2007 2175 [ - + ]: 14190 : if (t0 == NULL)
2007 tgl@sss.pgh.pa.us 2176 [ # # ]:UBC 0 : elog(ERROR, "could not convert epoch to timestamp: %m");
2177 : :
8824 lockhart@fourpalms.o 2178 :CBC 14190 : tm->tm_year = t0->tm_year;
2179 : 14190 : tm->tm_mon = t0->tm_mon;
2180 : 14190 : tm->tm_mday = t0->tm_mday;
2181 : 14190 : tm->tm_hour = t0->tm_hour;
2182 : 14190 : tm->tm_min = t0->tm_min;
2183 : 14190 : tm->tm_sec = t0->tm_sec;
2184 : :
7255 tgl@sss.pgh.pa.us 2185 : 14190 : tm->tm_year += 1900;
8824 lockhart@fourpalms.o 2186 : 14190 : tm->tm_mon++;
7255 tgl@sss.pgh.pa.us 2187 : 14190 : }
2188 : :
2189 : : Timestamp
8234 lockhart@fourpalms.o 2190 : 14187 : SetEpochTimestamp(void)
2191 : : {
2192 : : Timestamp dt;
2193 : : struct pg_tm tt,
2194 : 14187 : *tm = &tt;
2195 : :
2196 : 14187 : GetEpochTime(tm);
2197 : : /* we don't bother to test for failure ... */
2198 : 14187 : tm2timestamp(tm, 0, NULL, &dt);
2199 : :
8824 2200 : 14187 : return dt;
2201 : : } /* SetEpochTimestamp() */
2202 : :
2203 : : /*
2204 : : * We are currently sharing some code between timestamp and timestamptz.
2205 : : * The comparison functions are among them. - thomas 2001-09-25
2206 : : *
2207 : : * timestamp_relop - is timestamp1 relop timestamp2
2208 : : */
2209 : : int
8382 tgl@sss.pgh.pa.us 2210 : 343579 : timestamp_cmp_internal(Timestamp dt1, Timestamp dt2)
2211 : : {
6900 bruce@momjian.us 2212 [ + + ]: 343579 : return (dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0);
2213 : : }
2214 : :
2215 : : Datum
8710 tgl@sss.pgh.pa.us 2216 : 54700 : timestamp_eq(PG_FUNCTION_ARGS)
2217 : : {
2218 : 54700 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2219 : 54700 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2220 : :
8382 2221 : 54700 : PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0);
2222 : : }
2223 : :
2224 : : Datum
8710 2225 : 393 : timestamp_ne(PG_FUNCTION_ARGS)
2226 : : {
2227 : 393 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2228 : 393 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2229 : :
8382 2230 : 393 : PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0);
2231 : : }
2232 : :
2233 : : Datum
8710 2234 : 163769 : timestamp_lt(PG_FUNCTION_ARGS)
2235 : : {
2236 : 163769 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2237 : 163769 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2238 : :
8382 2239 : 163769 : PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0);
2240 : : }
2241 : :
2242 : : Datum
8710 2243 : 49246 : timestamp_gt(PG_FUNCTION_ARGS)
2244 : : {
2245 : 49246 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2246 : 49246 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2247 : :
8382 2248 : 49246 : PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0);
2249 : : }
2250 : :
2251 : : Datum
8710 2252 : 9264 : timestamp_le(PG_FUNCTION_ARGS)
2253 : : {
2254 : 9264 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2255 : 9264 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2256 : :
8382 2257 : 9264 : PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0);
2258 : : }
2259 : :
2260 : : Datum
8710 2261 : 9281 : timestamp_ge(PG_FUNCTION_ARGS)
2262 : : {
2263 : 9281 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2264 : 9281 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2265 : :
8382 2266 : 9281 : PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0);
2267 : : }
2268 : :
2269 : : Datum
8710 2270 : 17506 : timestamp_cmp(PG_FUNCTION_ARGS)
2271 : : {
2272 : 17506 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2273 : 17506 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2274 : :
8382 2275 : 17506 : PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2));
2276 : : }
2277 : :
2278 : : #if SIZEOF_DATUM < 8
2279 : : /* note: this is used for timestamptz also */
2280 : : static int
2281 : : timestamp_fastcmp(Datum x, Datum y, SortSupport ssup)
2282 : : {
2283 : : Timestamp a = DatumGetTimestamp(x);
2284 : : Timestamp b = DatumGetTimestamp(y);
2285 : :
2286 : : return timestamp_cmp_internal(a, b);
2287 : : }
2288 : : #endif
2289 : :
2290 : : Datum
4512 2291 : 244 : timestamp_sortsupport(PG_FUNCTION_ARGS)
2292 : : {
4326 bruce@momjian.us 2293 : 244 : SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
2294 : :
2295 : : #if SIZEOF_DATUM >= 8
2296 : :
2297 : : /*
2298 : : * If this build has pass-by-value timestamps, then we can use a standard
2299 : : * comparator function.
2300 : : */
743 john.naylor@postgres 2301 : 244 : ssup->comparator = ssup_datum_signed_cmp;
2302 : : #else
2303 : : ssup->comparator = timestamp_fastcmp;
2304 : : #endif
4512 tgl@sss.pgh.pa.us 2305 : 244 : PG_RETURN_VOID();
2306 : : }
2307 : :
2308 : : Datum
6127 2309 : 3255 : timestamp_hash(PG_FUNCTION_ARGS)
2310 : : {
2311 : 3255 : return hashint8(fcinfo);
2312 : : }
2313 : :
2314 : : Datum
2418 rhaas@postgresql.org 2315 : 30 : timestamp_hash_extended(PG_FUNCTION_ARGS)
2316 : : {
2317 : 30 : return hashint8extended(fcinfo);
2318 : : }
2319 : :
2320 : : /*
2321 : : * Cross-type comparison functions for timestamp vs timestamptz
2322 : : */
2323 : :
2324 : : int32
1285 tgl@sss.pgh.pa.us 2325 : 7953 : timestamp_cmp_timestamptz_internal(Timestamp timestampVal, TimestampTz dt2)
2326 : : {
2327 : : TimestampTz dt1;
2328 : : int overflow;
2329 : :
2330 : 7953 : dt1 = timestamp2timestamptz_opt_overflow(timestampVal, &overflow);
2331 [ - + ]: 7953 : if (overflow > 0)
2332 : : {
2333 : : /* dt1 is larger than any finite timestamp, but less than infinity */
1285 tgl@sss.pgh.pa.us 2334 [ # # ]:UBC 0 : return TIMESTAMP_IS_NOEND(dt2) ? -1 : +1;
2335 : : }
1285 tgl@sss.pgh.pa.us 2336 [ + + ]:CBC 7953 : if (overflow < 0)
2337 : : {
2338 : : /* dt1 is less than any finite timestamp, but more than -infinity */
2339 [ - + ]: 6 : return TIMESTAMP_IS_NOBEGIN(dt2) ? +1 : -1;
2340 : : }
2341 : :
2342 : 7947 : return timestamptz_cmp_internal(dt1, dt2);
2343 : : }
2344 : :
2345 : : Datum
7328 2346 : 906 : timestamp_eq_timestamptz(PG_FUNCTION_ARGS)
2347 : : {
2348 : 906 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2349 : 906 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2350 : :
1285 tgl@sss.pgh.pa.us 2351 : 906 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) == 0);
2352 : : }
2353 : :
2354 : : Datum
7328 tgl@sss.pgh.pa.us 2355 :UBC 0 : timestamp_ne_timestamptz(PG_FUNCTION_ARGS)
2356 : : {
2357 : 0 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2358 : 0 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2359 : :
1285 tgl@sss.pgh.pa.us 2360 : 0 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) != 0);
2361 : : }
2362 : :
2363 : : Datum
7328 tgl@sss.pgh.pa.us 2364 :CBC 1602 : timestamp_lt_timestamptz(PG_FUNCTION_ARGS)
2365 : : {
2366 : 1602 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2367 : 1602 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2368 : :
1285 tgl@sss.pgh.pa.us 2369 : 1602 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) < 0);
2370 : : }
2371 : :
2372 : : Datum
7328 2373 : 1599 : timestamp_gt_timestamptz(PG_FUNCTION_ARGS)
2374 : : {
2375 : 1599 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2376 : 1599 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2377 : :
1285 tgl@sss.pgh.pa.us 2378 : 1599 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) > 0);
2379 : : }
2380 : :
2381 : : Datum
7328 2382 : 1899 : timestamp_le_timestamptz(PG_FUNCTION_ARGS)
2383 : : {
2384 : 1899 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2385 : 1899 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2386 : :
1285 tgl@sss.pgh.pa.us 2387 : 1899 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) <= 0);
2388 : : }
2389 : :
2390 : : Datum
7328 2391 : 1752 : timestamp_ge_timestamptz(PG_FUNCTION_ARGS)
2392 : : {
2393 : 1752 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2394 : 1752 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2395 : :
1285 tgl@sss.pgh.pa.us 2396 : 1752 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt2) >= 0);
2397 : : }
2398 : :
2399 : : Datum
7328 2400 : 36 : timestamp_cmp_timestamptz(PG_FUNCTION_ARGS)
2401 : : {
2402 : 36 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(0);
7168 bruce@momjian.us 2403 : 36 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
2404 : :
1285 tgl@sss.pgh.pa.us 2405 : 36 : PG_RETURN_INT32(timestamp_cmp_timestamptz_internal(timestampVal, dt2));
2406 : : }
2407 : :
2408 : : Datum
7328 tgl@sss.pgh.pa.us 2409 :UBC 0 : timestamptz_eq_timestamp(PG_FUNCTION_ARGS)
2410 : : {
7168 bruce@momjian.us 2411 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2412 : 0 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2413 : :
1285 2414 : 0 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) == 0);
2415 : : }
2416 : :
2417 : : Datum
7328 tgl@sss.pgh.pa.us 2418 :CBC 48 : timestamptz_ne_timestamp(PG_FUNCTION_ARGS)
2419 : : {
7168 bruce@momjian.us 2420 : 48 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2421 : 48 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2422 : :
1285 2423 : 48 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) != 0);
2424 : : }
2425 : :
2426 : : Datum
7328 tgl@sss.pgh.pa.us 2427 :UBC 0 : timestamptz_lt_timestamp(PG_FUNCTION_ARGS)
2428 : : {
7168 bruce@momjian.us 2429 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2430 : 0 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2431 : :
1285 2432 : 0 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) > 0);
2433 : : }
2434 : :
2435 : : Datum
7328 2436 : 0 : timestamptz_gt_timestamp(PG_FUNCTION_ARGS)
2437 : : {
7168 bruce@momjian.us 2438 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2439 : 0 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2440 : :
1285 2441 : 0 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) < 0);
2442 : : }
2443 : :
2444 : : Datum
7328 2445 : 0 : timestamptz_le_timestamp(PG_FUNCTION_ARGS)
2446 : : {
7168 bruce@momjian.us 2447 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2448 : 0 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2449 : :
1285 2450 : 0 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) >= 0);
2451 : : }
2452 : :
2453 : : Datum
7328 tgl@sss.pgh.pa.us 2454 :CBC 3 : timestamptz_ge_timestamp(PG_FUNCTION_ARGS)
2455 : : {
7168 bruce@momjian.us 2456 : 3 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2457 : 3 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2458 : :
1285 2459 : 3 : PG_RETURN_BOOL(timestamp_cmp_timestamptz_internal(timestampVal, dt1) <= 0);
2460 : : }
2461 : :
2462 : : Datum
7328 tgl@sss.pgh.pa.us 2463 :UBC 0 : timestamptz_cmp_timestamp(PG_FUNCTION_ARGS)
2464 : : {
7168 bruce@momjian.us 2465 : 0 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
7328 tgl@sss.pgh.pa.us 2466 : 0 : Timestamp timestampVal = PG_GETARG_TIMESTAMP(1);
2467 : :
1285 2468 : 0 : PG_RETURN_INT32(-timestamp_cmp_timestamptz_internal(timestampVal, dt1));
2469 : : }
2470 : :
2471 : :
2472 : : /*
2473 : : * interval_relop - is interval1 relop interval2
2474 : : *
2475 : : * Interval comparison is based on converting interval values to a linear
2476 : : * representation expressed in the units of the time field (microseconds,
2477 : : * in the case of integer timestamps) with days assumed to be always 24 hours
2478 : : * and months assumed to be always 30 days. To avoid overflow, we need a
2479 : : * wider-than-int64 datatype for the linear representation, so use INT128.
2480 : : */
2481 : :
2482 : : static inline INT128
5489 tgl@sss.pgh.pa.us 2483 :CBC 288907 : interval_cmp_value(const Interval *interval)
2484 : : {
2485 : : INT128 span;
2486 : : int64 days;
2487 : :
2488 : : /*
2489 : : * Combine the month and day fields into an integral number of days.
2490 : : * Because the inputs are int32, int64 arithmetic suffices here.
2491 : : */
931 2492 : 288907 : days = interval->month * INT64CONST(30);
2566 2493 : 288907 : days += interval->day;
2494 : :
2495 : : /* Widen time field to 128 bits */
931 2496 : 288907 : span = int64_to_int128(interval->time);
2497 : :
2498 : : /* Scale up days to microseconds, forming a 128-bit product */
2566 2499 : 288907 : int128_add_int64_mul_int64(&span, days, USECS_PER_DAY);
2500 : :
5489 2501 : 288907 : return span;
2502 : : }
2503 : :
2504 : : static int
638 peter@eisentraut.org 2505 : 142656 : interval_cmp_internal(const Interval *interval1, const Interval *interval2)
2506 : : {
2566 tgl@sss.pgh.pa.us 2507 : 142656 : INT128 span1 = interval_cmp_value(interval1);
2508 : 142656 : INT128 span2 = interval_cmp_value(interval2);
2509 : :
2510 : 142656 : return int128_compare(span1, span2);
2511 : : }
2512 : :
2513 : : static int
152 dean.a.rasheed@gmail 2514 :GNC 2422 : interval_sign(const Interval *interval)
2515 : : {
2516 : 2422 : INT128 span = interval_cmp_value(interval);
2517 : 2422 : INT128 zero = int64_to_int128(0);
2518 : :
2519 : 2422 : return int128_compare(span, zero);
2520 : : }
2521 : :
2522 : : Datum
8710 tgl@sss.pgh.pa.us 2523 :CBC 29351 : interval_eq(PG_FUNCTION_ARGS)
2524 : : {
2525 : 29351 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2526 : 29351 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2527 : :
8382 2528 : 29351 : PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0);
2529 : : }
2530 : :
2531 : : Datum
8710 2532 : 54 : interval_ne(PG_FUNCTION_ARGS)
2533 : : {
2534 : 54 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2535 : 54 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2536 : :
8382 2537 : 54 : PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0);
2538 : : }
2539 : :
2540 : : Datum
8710 2541 : 70210 : interval_lt(PG_FUNCTION_ARGS)
2542 : : {
2543 : 70210 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2544 : 70210 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2545 : :
8382 2546 : 70210 : PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0);
2547 : : }
2548 : :
2549 : : Datum
8710 2550 : 4974 : interval_gt(PG_FUNCTION_ARGS)
2551 : : {
2552 : 4974 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2553 : 4974 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2554 : :
8382 2555 : 4974 : PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0);
2556 : : }
2557 : :
2558 : : Datum
8710 2559 : 3179 : interval_le(PG_FUNCTION_ARGS)
2560 : : {
2561 : 3179 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2562 : 3179 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2563 : :
8382 2564 : 3179 : PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0);
2565 : : }
2566 : :
2567 : : Datum
8710 2568 : 2942 : interval_ge(PG_FUNCTION_ARGS)
2569 : : {
2570 : 2942 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2571 : 2942 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2572 : :
8382 2573 : 2942 : PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0);
2574 : : }
2575 : :
2576 : : Datum
8710 2577 : 31502 : interval_cmp(PG_FUNCTION_ARGS)
2578 : : {
2579 : 31502 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
2580 : 31502 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
2581 : :
8382 2582 : 31502 : PG_RETURN_INT32(interval_cmp_internal(interval1, interval2));
2583 : : }
2584 : :
2585 : : /*
2586 : : * Hashing for intervals
2587 : : *
2588 : : * We must produce equal hashvals for values that interval_cmp_internal()
2589 : : * considers equal. So, compute the net span the same way it does,
2590 : : * and then hash that.
2591 : : */
2592 : : Datum
8700 2593 : 1143 : interval_hash(PG_FUNCTION_ARGS)
2594 : : {
5489 2595 : 1143 : Interval *interval = PG_GETARG_INTERVAL_P(0);
2566 2596 : 1143 : INT128 span = interval_cmp_value(interval);
2597 : : int64 span64;
2598 : :
2599 : : /*
2600 : : * Use only the least significant 64 bits for hashing. The upper 64 bits
2601 : : * seldom add any useful information, and besides we must do it like this
2602 : : * for compatibility with hashes calculated before use of INT128 was
2603 : : * introduced.
2604 : : */
2605 : 1143 : span64 = int128_to_int64(span);
2606 : :
2607 : 1143 : return DirectFunctionCall1(hashint8, Int64GetDatumFast(span64));
2608 : : }
2609 : :
2610 : : Datum
2418 rhaas@postgresql.org 2611 : 30 : interval_hash_extended(PG_FUNCTION_ARGS)
2612 : : {
2613 : 30 : Interval *interval = PG_GETARG_INTERVAL_P(0);
2614 : 30 : INT128 span = interval_cmp_value(interval);
2615 : : int64 span64;
2616 : :
2617 : : /* Same approach as interval_hash */
2618 : 30 : span64 = int128_to_int64(span);
2619 : :
2620 : 30 : return DirectFunctionCall2(hashint8extended, Int64GetDatumFast(span64),
2621 : : PG_GETARG_DATUM(1));
2622 : : }
2623 : :
2624 : : /* overlaps_timestamp() --- implements the SQL OVERLAPS operator.
2625 : : *
2626 : : * Algorithm is per SQL spec. This is much harder than you'd think
2627 : : * because the spec requires us to deliver a non-null answer in some cases
2628 : : * where some of the inputs are null.
2629 : : */
2630 : : Datum
8710 tgl@sss.pgh.pa.us 2631 : 36 : overlaps_timestamp(PG_FUNCTION_ARGS)
2632 : : {
2633 : : /*
2634 : : * The arguments are Timestamps, but we leave them as generic Datums to
2635 : : * avoid unnecessary conversions between value and reference forms --- not
2636 : : * to mention possible dereferences of null pointers.
2637 : : */
2638 : 36 : Datum ts1 = PG_GETARG_DATUM(0);
2639 : 36 : Datum te1 = PG_GETARG_DATUM(1);
2640 : 36 : Datum ts2 = PG_GETARG_DATUM(2);
2641 : 36 : Datum te2 = PG_GETARG_DATUM(3);
8529 2642 : 36 : bool ts1IsNull = PG_ARGISNULL(0);
2643 : 36 : bool te1IsNull = PG_ARGISNULL(1);
2644 : 36 : bool ts2IsNull = PG_ARGISNULL(2);
2645 : 36 : bool te2IsNull = PG_ARGISNULL(3);
2646 : :
2647 : : #define TIMESTAMP_GT(t1,t2) \
2648 : : DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2))
2649 : : #define TIMESTAMP_LT(t1,t2) \
2650 : : DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2))
2651 : :
2652 : : /*
2653 : : * If both endpoints of interval 1 are null, the result is null (unknown).
2654 : : * If just one endpoint is null, take ts1 as the non-null one. Otherwise,
2655 : : * take ts1 as the lesser endpoint.
2656 : : */
2657 [ - + ]: 36 : if (ts1IsNull)
2658 : : {
8529 tgl@sss.pgh.pa.us 2659 [ # # ]:UBC 0 : if (te1IsNull)
2660 : 0 : PG_RETURN_NULL();
2661 : : /* swap null for non-null */
8797 lockhart@fourpalms.o 2662 : 0 : ts1 = te1;
8529 tgl@sss.pgh.pa.us 2663 : 0 : te1IsNull = true;
2664 : : }
8529 tgl@sss.pgh.pa.us 2665 [ + - ]:CBC 36 : else if (!te1IsNull)
2666 : : {
2667 [ - + ]: 36 : if (TIMESTAMP_GT(ts1, te1))
2668 : : {
8424 bruce@momjian.us 2669 :UBC 0 : Datum tt = ts1;
2670 : :
8529 tgl@sss.pgh.pa.us 2671 : 0 : ts1 = te1;
2672 : 0 : te1 = tt;
2673 : : }
2674 : : }
2675 : :
2676 : : /* Likewise for interval 2. */
8529 tgl@sss.pgh.pa.us 2677 [ - + ]:CBC 36 : if (ts2IsNull)
2678 : : {
8529 tgl@sss.pgh.pa.us 2679 [ # # ]:UBC 0 : if (te2IsNull)
2680 : 0 : PG_RETURN_NULL();
2681 : : /* swap null for non-null */
8797 lockhart@fourpalms.o 2682 : 0 : ts2 = te2;
8529 tgl@sss.pgh.pa.us 2683 : 0 : te2IsNull = true;
2684 : : }
8529 tgl@sss.pgh.pa.us 2685 [ + - ]:CBC 36 : else if (!te2IsNull)
2686 : : {
2687 [ - + ]: 36 : if (TIMESTAMP_GT(ts2, te2))
2688 : : {
8424 bruce@momjian.us 2689 :UBC 0 : Datum tt = ts2;
2690 : :
8529 tgl@sss.pgh.pa.us 2691 : 0 : ts2 = te2;
2692 : 0 : te2 = tt;
2693 : : }
2694 : : }
2695 : :
2696 : : /*
2697 : : * At this point neither ts1 nor ts2 is null, so we can consider three
2698 : : * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2
2699 : : */
8529 tgl@sss.pgh.pa.us 2700 [ - + ]:CBC 36 : if (TIMESTAMP_GT(ts1, ts2))
2701 : : {
2702 : : /*
2703 : : * This case is ts1 < te2 OR te1 < te2, which may look redundant but
2704 : : * in the presence of nulls it's not quite completely so.
2705 : : */
8529 tgl@sss.pgh.pa.us 2706 [ # # ]:UBC 0 : if (te2IsNull)
2707 : 0 : PG_RETURN_NULL();
2708 [ # # ]: 0 : if (TIMESTAMP_LT(ts1, te2))
2709 : 0 : PG_RETURN_BOOL(true);
2710 [ # # ]: 0 : if (te1IsNull)
2711 : 0 : PG_RETURN_NULL();
2712 : :
2713 : : /*
2714 : : * If te1 is not null then we had ts1 <= te1 above, and we just found
2715 : : * ts1 >= te2, hence te1 >= te2.
2716 : : */
2717 : 0 : PG_RETURN_BOOL(false);
2718 : : }
8529 tgl@sss.pgh.pa.us 2719 [ + + ]:CBC 36 : else if (TIMESTAMP_LT(ts1, ts2))
2720 : : {
2721 : : /* This case is ts2 < te1 OR te2 < te1 */
2722 [ - + ]: 30 : if (te1IsNull)
8529 tgl@sss.pgh.pa.us 2723 :UBC 0 : PG_RETURN_NULL();
8529 tgl@sss.pgh.pa.us 2724 [ + + ]:CBC 30 : if (TIMESTAMP_LT(ts2, te1))
2725 : 12 : PG_RETURN_BOOL(true);
2726 [ - + ]: 18 : if (te2IsNull)
8529 tgl@sss.pgh.pa.us 2727 :UBC 0 : PG_RETURN_NULL();
2728 : :
2729 : : /*
2730 : : * If te2 is not null then we had ts2 <= te2 above, and we just found
2731 : : * ts2 >= te1, hence te2 >= te1.
2732 : : */
8529 tgl@sss.pgh.pa.us 2733 :CBC 18 : PG_RETURN_BOOL(false);
2734 : : }
2735 : : else
2736 : : {
2737 : : /*
2738 : : * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a
2739 : : * rather silly way of saying "true if both are non-null, else null".
2740 : : */
2741 [ + - - + ]: 6 : if (te1IsNull || te2IsNull)
8529 tgl@sss.pgh.pa.us 2742 :UBC 0 : PG_RETURN_NULL();
8529 tgl@sss.pgh.pa.us 2743 :CBC 6 : PG_RETURN_BOOL(true);
2744 : : }
2745 : :
2746 : : #undef TIMESTAMP_GT
2747 : : #undef TIMESTAMP_LT
2748 : : }
2749 : :
2750 : :
2751 : : /*----------------------------------------------------------
2752 : : * "Arithmetic" operators on date/times.
2753 : : *---------------------------------------------------------*/
2754 : :
2755 : : Datum
8710 tgl@sss.pgh.pa.us 2756 :UBC 0 : timestamp_smaller(PG_FUNCTION_ARGS)
2757 : : {
2758 : 0 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2759 : 0 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2760 : : Timestamp result;
2761 : :
2762 : : /* use timestamp_cmp_internal to be sure this agrees with comparisons */
7555 2763 [ # # ]: 0 : if (timestamp_cmp_internal(dt1, dt2) < 0)
2764 : 0 : result = dt1;
2765 : : else
2766 : 0 : result = dt2;
8710 2767 : 0 : PG_RETURN_TIMESTAMP(result);
2768 : : }
2769 : :
2770 : : Datum
8710 tgl@sss.pgh.pa.us 2771 :GBC 42 : timestamp_larger(PG_FUNCTION_ARGS)
2772 : : {
2773 : 42 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2774 : 42 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2775 : : Timestamp result;
2776 : :
7555 2777 [ - + ]: 42 : if (timestamp_cmp_internal(dt1, dt2) > 0)
7555 tgl@sss.pgh.pa.us 2778 :UBC 0 : result = dt1;
2779 : : else
7555 tgl@sss.pgh.pa.us 2780 :GBC 42 : result = dt2;
8710 2781 : 42 : PG_RETURN_TIMESTAMP(result);
2782 : : }
2783 : :
2784 : :
2785 : : Datum
8710 tgl@sss.pgh.pa.us 2786 :CBC 3117 : timestamp_mi(PG_FUNCTION_ARGS)
2787 : : {
2788 : 3117 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
2789 : 3117 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
2790 : : Interval *result;
2791 : :
2792 : 3117 : result = (Interval *) palloc(sizeof(Interval));
2793 : :
2794 : : /*
2795 : : * Handle infinities.
2796 : : *
2797 : : * We treat anything that amounts to "infinity - infinity" as an error,
2798 : : * since the interval type has nothing equivalent to NaN.
2799 : : */
8234 lockhart@fourpalms.o 2800 [ + + + + : 3117 : if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2))
+ - - + ]
2801 : : {
152 dean.a.rasheed@gmail 2802 [ + + ]:GNC 42 : if (TIMESTAMP_IS_NOBEGIN(dt1))
2803 : : {
2804 [ + + ]: 18 : if (TIMESTAMP_IS_NOBEGIN(dt2))
2805 [ + - ]: 6 : ereport(ERROR,
2806 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2807 : : errmsg("interval out of range")));
2808 : : else
2809 : 12 : INTERVAL_NOBEGIN(result);
2810 : : }
2811 [ + - ]: 24 : else if (TIMESTAMP_IS_NOEND(dt1))
2812 : : {
2813 [ + + ]: 24 : if (TIMESTAMP_IS_NOEND(dt2))
2814 [ + - ]: 6 : ereport(ERROR,
2815 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2816 : : errmsg("interval out of range")));
2817 : : else
2818 : 18 : INTERVAL_NOEND(result);
2819 : : }
152 dean.a.rasheed@gmail 2820 [ # # ]:UNC 0 : else if (TIMESTAMP_IS_NOBEGIN(dt2))
2821 : 0 : INTERVAL_NOEND(result);
2822 : : else /* TIMESTAMP_IS_NOEND(dt2) */
2823 : 0 : INTERVAL_NOBEGIN(result);
2824 : :
152 dean.a.rasheed@gmail 2825 :GNC 30 : PG_RETURN_INTERVAL_P(result);
2826 : : }
2827 : :
419 tgl@sss.pgh.pa.us 2828 [ + + ]:CBC 3075 : if (unlikely(pg_sub_s64_overflow(dt1, dt2, &result->time)))
2829 [ + - ]: 6 : ereport(ERROR,
2830 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2831 : : errmsg("interval out of range")));
2832 : :
8824 lockhart@fourpalms.o 2833 : 3069 : result->month = 0;
6843 bruce@momjian.us 2834 : 3069 : result->day = 0;
2835 : :
2836 : : /*----------
2837 : : * This is wrong, but removing it breaks a lot of regression tests.
2838 : : * For example:
2839 : : *
2840 : : * test=> SET timezone = 'EST5EDT';
2841 : : * test=> SELECT
2842 : : * test-> ('2005-10-30 13:22:00-05'::timestamptz -
2843 : : * test(> '2005-10-29 13:22:00-04'::timestamptz);
2844 : : * ?column?
2845 : : * ----------------
2846 : : * 1 day 01:00:00
2847 : : * (1 row)
2848 : : *
2849 : : * so adding that to the first timestamp gets:
2850 : : *
2851 : : * test=> SELECT
2852 : : * test-> ('2005-10-29 13:22:00-04'::timestamptz +
2853 : : * test(> ('2005-10-30 13:22:00-05'::timestamptz -
2854 : : * test(> '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST';
2855 : : * timezone
2856 : : * --------------------
2857 : : * 2005-10-30 14:22:00
2858 : : * (1 row)
2859 : : *----------
2860 : : */
6718 2861 : 3069 : result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours,
2862 : : IntervalPGetDatum(result)));
2863 : :
6807 2864 : 3069 : PG_RETURN_INTERVAL_P(result);
2865 : : }
2866 : :
2867 : : /*
2868 : : * interval_justify_interval()
2869 : : *
2870 : : * Adjust interval so 'month', 'day', and 'time' portions are within
2871 : : * customary bounds. Specifically:
2872 : : *
2873 : : * 0 <= abs(time) < 24 hours
2874 : : * 0 <= abs(day) < 30 days
2875 : : *
2876 : : * Also, the sign bit on all three fields is made equal, so either
2877 : : * all three fields are negative or all are positive.
2878 : : */
2879 : : Datum
6614 2880 : 33 : interval_justify_interval(PG_FUNCTION_ARGS)
2881 : : {
2882 : 33 : Interval *span = PG_GETARG_INTERVAL_P(0);
2883 : : Interval *result;
2884 : : TimeOffset wholeday;
2885 : : int32 wholemonth;
2886 : :
2887 : 33 : result = (Interval *) palloc(sizeof(Interval));
2888 : 33 : result->month = span->month;
2889 : 33 : result->day = span->day;
2890 : 33 : result->time = span->time;
2891 : :
2892 : : /* do nothing for infinite intervals */
152 dean.a.rasheed@gmail 2893 [ + + + + :GNC 33 : if (INTERVAL_NOT_FINITE(result))
- + + + +
+ + - ]
2894 : 6 : PG_RETURN_INTERVAL_P(result);
2895 : :
2896 : : /* pre-justify days if it might prevent overflow */
776 tgl@sss.pgh.pa.us 2897 [ + + + + ]:CBC 27 : if ((result->day > 0 && result->time > 0) ||
2898 [ + + + + ]: 24 : (result->day < 0 && result->time < 0))
2899 : : {
2900 : 6 : wholemonth = result->day / DAYS_PER_MONTH;
2901 : 6 : result->day -= wholemonth * DAYS_PER_MONTH;
2902 [ - + ]: 6 : if (pg_add_s32_overflow(result->month, wholemonth, &result->month))
776 tgl@sss.pgh.pa.us 2903 [ # # ]:UBC 0 : ereport(ERROR,
2904 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2905 : : errmsg("interval out of range")));
2906 : : }
2907 : :
2908 : : /*
2909 : : * Since TimeOffset is int64, abs(wholeday) can't exceed about 1.07e8. If
2910 : : * we pre-justified then abs(result->day) is less than DAYS_PER_MONTH, so
2911 : : * this addition can't overflow. If we didn't pre-justify, then day and
2912 : : * time are of different signs, so it still can't overflow.
2913 : : */
6614 bruce@momjian.us 2914 [ + + ]:CBC 27 : TMODULO(result->time, wholeday, USECS_PER_DAY);
776 tgl@sss.pgh.pa.us 2915 : 27 : result->day += wholeday;
2916 : :
6614 bruce@momjian.us 2917 : 27 : wholemonth = result->day / DAYS_PER_MONTH;
2918 : 27 : result->day -= wholemonth * DAYS_PER_MONTH;
776 tgl@sss.pgh.pa.us 2919 [ + + ]: 27 : if (pg_add_s32_overflow(result->month, wholemonth, &result->month))
2920 [ + - ]: 12 : ereport(ERROR,
2921 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2922 : : errmsg("interval out of range")));
2923 : :
6614 bruce@momjian.us 2924 [ + + ]: 15 : if (result->month > 0 &&
2925 [ + - + + : 9 : (result->day < 0 || (result->day == 0 && result->time < 0)))
+ - ]
2926 : : {
2927 : 3 : result->day += DAYS_PER_MONTH;
2928 : 3 : result->month--;
2929 : : }
2930 [ + + ]: 12 : else if (result->month < 0 &&
6402 2931 [ + - - + : 6 : (result->day > 0 || (result->day == 0 && result->time > 0)))
- - ]
2932 : : {
6614 bruce@momjian.us 2933 :UBC 0 : result->day -= DAYS_PER_MONTH;
2934 : 0 : result->month++;
2935 : : }
2936 : :
6614 bruce@momjian.us 2937 [ + + + + ]:CBC 15 : if (result->day > 0 && result->time < 0)
2938 : : {
2939 : 3 : result->time += USECS_PER_DAY;
2940 : 3 : result->day--;
2941 : : }
2942 [ + + - + ]: 12 : else if (result->day < 0 && result->time > 0)
2943 : : {
6614 bruce@momjian.us 2944 :UBC 0 : result->time -= USECS_PER_DAY;
2945 : 0 : result->day++;
2946 : : }
2947 : :
6614 bruce@momjian.us 2948 :CBC 15 : PG_RETURN_INTERVAL_P(result);
2949 : : }
2950 : :
2951 : : /*
2952 : : * interval_justify_hours()
2953 : : *
2954 : : * Adjust interval so 'time' contains less than a whole day, adding
2955 : : * the excess to 'day'. This is useful for
2956 : : * situations (such as non-TZ) where '1 day' = '24 hours' is valid,
2957 : : * e.g. interval subtraction and division.
2958 : : */
2959 : : Datum
6843 2960 : 4071 : interval_justify_hours(PG_FUNCTION_ARGS)
2961 : : {
6756 2962 : 4071 : Interval *span = PG_GETARG_INTERVAL_P(0);
2963 : : Interval *result;
2964 : : TimeOffset wholeday;
2965 : :
6843 2966 : 4071 : result = (Interval *) palloc(sizeof(Interval));
2967 : 4071 : result->month = span->month;
6746 tgl@sss.pgh.pa.us 2968 : 4071 : result->day = span->day;
6843 bruce@momjian.us 2969 : 4071 : result->time = span->time;
2970 : :
2971 : : /* do nothing for infinite intervals */
152 dean.a.rasheed@gmail 2972 [ + + + - :GNC 4071 : if (INTERVAL_NOT_FINITE(result))
- + + + +
- + - ]
2973 : 6 : PG_RETURN_INTERVAL_P(result);
2974 : :
6746 tgl@sss.pgh.pa.us 2975 [ + + ]:CBC 4065 : TMODULO(result->time, wholeday, USECS_PER_DAY);
776 2976 [ + + ]: 4065 : if (pg_add_s32_overflow(result->day, wholeday, &result->day))
2977 [ + - ]: 3 : ereport(ERROR,
2978 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
2979 : : errmsg("interval out of range")));
2980 : :
6614 bruce@momjian.us 2981 [ + + - + ]: 4062 : if (result->day > 0 && result->time < 0)
2982 : : {
6614 bruce@momjian.us 2983 :UBC 0 : result->time += USECS_PER_DAY;
2984 : 0 : result->day--;
2985 : : }
6614 bruce@momjian.us 2986 [ + + - + ]:CBC 4062 : else if (result->day < 0 && result->time > 0)
2987 : : {
6614 bruce@momjian.us 2988 :UBC 0 : result->time -= USECS_PER_DAY;
2989 : 0 : result->day++;
2990 : : }
2991 : :
8710 tgl@sss.pgh.pa.us 2992 :CBC 4062 : PG_RETURN_INTERVAL_P(result);
2993 : : }
2994 : :
2995 : : /*
2996 : : * interval_justify_days()
2997 : : *
2998 : : * Adjust interval so 'day' contains less than 30 days, adding
2999 : : * the excess to 'month'.
3000 : : */
3001 : : Datum
6843 bruce@momjian.us 3002 : 1002 : interval_justify_days(PG_FUNCTION_ARGS)
3003 : : {
6756 3004 : 1002 : Interval *span = PG_GETARG_INTERVAL_P(0);
3005 : : Interval *result;
3006 : : int32 wholemonth;
3007 : :
6843 3008 : 1002 : result = (Interval *) palloc(sizeof(Interval));
6746 tgl@sss.pgh.pa.us 3009 : 1002 : result->month = span->month;
6843 bruce@momjian.us 3010 : 1002 : result->day = span->day;
3011 : 1002 : result->time = span->time;
3012 : :
3013 : : /* do nothing for infinite intervals */
152 dean.a.rasheed@gmail 3014 [ + + + - :GNC 1002 : if (INTERVAL_NOT_FINITE(result))
- + + + +
+ + - ]
3015 : 6 : PG_RETURN_INTERVAL_P(result);
3016 : :
6746 tgl@sss.pgh.pa.us 3017 :CBC 996 : wholemonth = result->day / DAYS_PER_MONTH;
3018 : 996 : result->day -= wholemonth * DAYS_PER_MONTH;
776 3019 [ + + ]: 996 : if (pg_add_s32_overflow(result->month, wholemonth, &result->month))
3020 [ + - ]: 3 : ereport(ERROR,
3021 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3022 : : errmsg("interval out of range")));
3023 : :
6614 bruce@momjian.us 3024 [ + + - + ]: 993 : if (result->month > 0 && result->day < 0)
3025 : : {
6614 bruce@momjian.us 3026 :UBC 0 : result->day += DAYS_PER_MONTH;
3027 : 0 : result->month--;
3028 : : }
6614 bruce@momjian.us 3029 [ - + - - ]:CBC 993 : else if (result->month < 0 && result->day > 0)
3030 : : {
6614 bruce@momjian.us 3031 :UBC 0 : result->day -= DAYS_PER_MONTH;
3032 : 0 : result->month++;
3033 : : }
3034 : :
6843 bruce@momjian.us 3035 :CBC 993 : PG_RETURN_INTERVAL_P(result);
3036 : : }
3037 : :
3038 : : /* timestamp_pl_interval()
3039 : : * Add an interval to a timestamp data type.
3040 : : * Note that interval has provisions for qualitative year/month and day
3041 : : * units, so try to do the right thing with them.
3042 : : * To add a month, increment the month, and use the same day of month.
3043 : : * Then, if the next month has fewer days, set the day of month
3044 : : * to the last day of month.
3045 : : * To add a day, increment the mday, and use the same time of day.
3046 : : * Lastly, add in the "quantitative time".
3047 : : */
3048 : : Datum
7365 tgl@sss.pgh.pa.us 3049 : 4798 : timestamp_pl_interval(PG_FUNCTION_ARGS)
3050 : : {
6756 bruce@momjian.us 3051 : 4798 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
8710 tgl@sss.pgh.pa.us 3052 : 4798 : Interval *span = PG_GETARG_INTERVAL_P(1);
3053 : : Timestamp result;
3054 : :
3055 : : /*
3056 : : * Handle infinities.
3057 : : *
3058 : : * We treat anything that amounts to "infinity - infinity" as an error,
3059 : : * since the timestamp type has nothing equivalent to NaN.
3060 : : */
152 dean.a.rasheed@gmail 3061 [ + + + - :GNC 4798 : if (INTERVAL_IS_NOBEGIN(span))
+ - ]
3062 : : {
3063 [ + + ]: 138 : if (TIMESTAMP_IS_NOEND(timestamp))
3064 [ + - ]: 12 : ereport(ERROR,
3065 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3066 : : errmsg("timestamp out of range")));
3067 : : else
3068 : 126 : TIMESTAMP_NOBEGIN(result);
3069 : : }
3070 [ + + + - : 4660 : else if (INTERVAL_IS_NOEND(span))
+ - ]
3071 : : {
3072 [ + + ]: 102 : if (TIMESTAMP_IS_NOBEGIN(timestamp))
3073 [ + - ]: 12 : ereport(ERROR,
3074 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3075 : : errmsg("timestamp out of range")));
3076 : : else
3077 : 90 : TIMESTAMP_NOEND(result);
3078 : : }
3079 [ + + + + ]: 4558 : else if (TIMESTAMP_NOT_FINITE(timestamp))
8234 lockhart@fourpalms.o 3080 :CBC 57 : result = timestamp;
3081 : : else
3082 : : {
3083 [ + + ]: 4501 : if (span->month != 0)
3084 : : {
3085 : : struct pg_tm tt,
3086 : 1314 : *tm = &tt;
3087 : : fsec_t fsec;
3088 : :
6842 bruce@momjian.us 3089 [ - + ]: 1314 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
7567 tgl@sss.pgh.pa.us 3090 [ # # ]:UBC 0 : ereport(ERROR,
3091 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3092 : : errmsg("timestamp out of range")));
3093 : :
7567 tgl@sss.pgh.pa.us 3094 :CBC 1314 : tm->tm_mon += span->month;
6842 bruce@momjian.us 3095 [ + + ]: 1314 : if (tm->tm_mon > MONTHS_PER_YEAR)
3096 : : {
3097 : 699 : tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
3098 : 699 : tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
3099 : : }
7567 tgl@sss.pgh.pa.us 3100 [ + + ]: 615 : else if (tm->tm_mon < 1)
3101 : : {
6842 bruce@momjian.us 3102 : 585 : tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
3103 : 585 : tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
3104 : : }
3105 : :
3106 : : /* adjust for end of month boundary problems... */
7567 tgl@sss.pgh.pa.us 3107 [ + + + + : 1314 : if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
+ - + + ]
3108 [ - + - - : 6 : tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
- - ]
3109 : :
6842 bruce@momjian.us 3110 [ - + ]: 1314 : if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0)
7567 tgl@sss.pgh.pa.us 3111 [ # # ]:UBC 0 : ereport(ERROR,
3112 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3113 : : errmsg("timestamp out of range")));
3114 : : }
3115 : :
6843 bruce@momjian.us 3116 [ + + ]:CBC 4501 : if (span->day != 0)
3117 : : {
3118 : : struct pg_tm tt,
3119 : 1452 : *tm = &tt;
3120 : : fsec_t fsec;
3121 : : int julian;
3122 : :
6842 3123 [ - + ]: 1452 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
6843 bruce@momjian.us 3124 [ # # ]:UBC 0 : ereport(ERROR,
3125 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3126 : : errmsg("timestamp out of range")));
3127 : :
3128 : : /*
3129 : : * Add days by converting to and from Julian. We need an overflow
3130 : : * check here since j2date expects a non-negative integer input.
3131 : : */
79 tgl@sss.pgh.pa.us 3132 :CBC 1452 : julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3133 [ + - ]: 1452 : if (pg_add_s32_overflow(julian, span->day, &julian) ||
3134 [ + + ]: 1452 : julian < 0)
3135 [ + - ]: 3 : ereport(ERROR,
3136 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3137 : : errmsg("timestamp out of range")));
6843 bruce@momjian.us 3138 : 1449 : j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3139 : :
6842 3140 [ - + ]: 1449 : if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0)
6843 bruce@momjian.us 3141 [ # # ]:UBC 0 : ereport(ERROR,
3142 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3143 : : errmsg("timestamp out of range")));
3144 : : }
3145 : :
6843 bruce@momjian.us 3146 :CBC 4498 : timestamp += span->time;
3147 : :
2951 tgl@sss.pgh.pa.us 3148 [ + - - + ]: 4498 : if (!IS_VALID_TIMESTAMP(timestamp))
2951 tgl@sss.pgh.pa.us 3149 [ # # ]:UBC 0 : ereport(ERROR,
3150 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3151 : : errmsg("timestamp out of range")));
3152 : :
8234 lockhart@fourpalms.o 3153 :CBC 4498 : result = timestamp;
3154 : : }
3155 : :
3156 : 4771 : PG_RETURN_TIMESTAMP(result);
3157 : : }
3158 : :
3159 : : Datum
7365 tgl@sss.pgh.pa.us 3160 : 1086 : timestamp_mi_interval(PG_FUNCTION_ARGS)
3161 : : {
6756 bruce@momjian.us 3162 : 1086 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
8234 lockhart@fourpalms.o 3163 : 1086 : Interval *span = PG_GETARG_INTERVAL_P(1);
3164 : : Interval tspan;
3165 : :
152 dean.a.rasheed@gmail 3166 :GNC 1086 : interval_um_internal(span, &tspan);
3167 : :
7365 tgl@sss.pgh.pa.us 3168 :CBC 1086 : return DirectFunctionCall2(timestamp_pl_interval,
3169 : : TimestampGetDatum(timestamp),
3170 : : PointerGetDatum(&tspan));
3171 : : }
3172 : :
3173 : :
3174 : : /* timestamptz_pl_interval_internal()
3175 : : * Add an interval to a timestamptz, in the given (or session) timezone.
3176 : : *
3177 : : * Note that interval has provisions for qualitative year/month and day
3178 : : * units, so try to do the right thing with them.
3179 : : * To add a month, increment the month, and use the same day of month.
3180 : : * Then, if the next month has fewer days, set the day of month
3181 : : * to the last day of month.
3182 : : * To add a day, increment the mday, and use the same time of day.
3183 : : * Lastly, add in the "quantitative time".
3184 : : */
3185 : : static TimestampTz
393 3186 : 48944 : timestamptz_pl_interval_internal(TimestampTz timestamp,
3187 : : Interval *span,
3188 : : pg_tz *attimezone)
3189 : : {
3190 : : TimestampTz result;
3191 : : int tz;
3192 : :
3193 : : /*
3194 : : * Handle infinities.
3195 : : *
3196 : : * We treat anything that amounts to "infinity - infinity" as an error,
3197 : : * since the timestamptz type has nothing equivalent to NaN.
3198 : : */
152 dean.a.rasheed@gmail 3199 [ + + + - :GNC 48944 : if (INTERVAL_IS_NOBEGIN(span))
+ - ]
3200 : : {
3201 [ + + ]: 216 : if (TIMESTAMP_IS_NOEND(timestamp))
3202 [ + - ]: 6 : ereport(ERROR,
3203 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3204 : : errmsg("timestamp out of range")));
3205 : : else
3206 : 210 : TIMESTAMP_NOBEGIN(result);
3207 : : }
3208 [ + + + - : 48728 : else if (INTERVAL_IS_NOEND(span))
+ - ]
3209 : : {
3210 [ + + ]: 180 : if (TIMESTAMP_IS_NOBEGIN(timestamp))
3211 [ + - ]: 6 : ereport(ERROR,
3212 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3213 : : errmsg("timestamp out of range")));
3214 : : else
3215 : 174 : TIMESTAMP_NOEND(result);
3216 : : }
3217 [ + + + + ]: 48548 : else if (TIMESTAMP_NOT_FINITE(timestamp))
8710 tgl@sss.pgh.pa.us 3218 :CBC 60 : result = timestamp;
3219 : : else
3220 : : {
3221 : : /* Use session timezone if caller asks for default */
393 3222 [ + + ]: 48488 : if (attimezone == NULL)
3223 : 17469 : attimezone = session_timezone;
3224 : :
8824 lockhart@fourpalms.o 3225 [ + + ]: 48488 : if (span->month != 0)
3226 : : {
3227 : : struct pg_tm tt,
3228 : 1170 : *tm = &tt;
3229 : : fsec_t fsec;
3230 : :
393 tgl@sss.pgh.pa.us 3231 [ - + ]: 1170 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, attimezone) != 0)
7567 tgl@sss.pgh.pa.us 3232 [ # # ]:UBC 0 : ereport(ERROR,
3233 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3234 : : errmsg("timestamp out of range")));
3235 : :
7567 tgl@sss.pgh.pa.us 3236 :CBC 1170 : tm->tm_mon += span->month;
6842 bruce@momjian.us 3237 [ + + ]: 1170 : if (tm->tm_mon > MONTHS_PER_YEAR)
3238 : : {
3239 : 447 : tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR;
3240 : 447 : tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1;
3241 : : }
7567 tgl@sss.pgh.pa.us 3242 [ + + ]: 723 : else if (tm->tm_mon < 1)
3243 : : {
6842 bruce@momjian.us 3244 : 510 : tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1;
3245 : 510 : tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR;
3246 : : }
3247 : :
3248 : : /* adjust for end of month boundary problems... */
7567 tgl@sss.pgh.pa.us 3249 [ + + + + : 1170 : if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1])
+ - + + ]
3250 [ + + + + : 27 : tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]);
+ - ]
3251 : :
393 3252 : 1170 : tz = DetermineTimeZoneOffset(tm, attimezone);
3253 : :
6842 bruce@momjian.us 3254 [ - + ]: 1170 : if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0)
7567 tgl@sss.pgh.pa.us 3255 [ # # ]:UBC 0 : ereport(ERROR,
3256 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3257 : : errmsg("timestamp out of range")));
3258 : : }
3259 : :
6843 bruce@momjian.us 3260 [ + + ]:CBC 48488 : if (span->day != 0)
3261 : : {
3262 : : struct pg_tm tt,
3263 : 1534 : *tm = &tt;
3264 : : fsec_t fsec;
3265 : : int julian;
3266 : :
393 tgl@sss.pgh.pa.us 3267 [ - + ]: 1534 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, attimezone) != 0)
6843 bruce@momjian.us 3268 [ # # ]:UBC 0 : ereport(ERROR,
3269 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3270 : : errmsg("timestamp out of range")));
3271 : :
3272 : : /*
3273 : : * Add days by converting to and from Julian. We need an overflow
3274 : : * check here since j2date expects a non-negative integer input.
3275 : : * In practice though, it will give correct answers for small
3276 : : * negative Julian dates; we should allow -1 to avoid
3277 : : * timezone-dependent failures, as discussed in timestamp.h.
3278 : : */
79 tgl@sss.pgh.pa.us 3279 :CBC 1534 : julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday);
3280 [ + - ]: 1534 : if (pg_add_s32_overflow(julian, span->day, &julian) ||
3281 [ + + ]: 1534 : julian < -1)
3282 [ + - ]: 3 : ereport(ERROR,
3283 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3284 : : errmsg("timestamp out of range")));
6843 bruce@momjian.us 3285 : 1531 : j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday);
3286 : :
393 tgl@sss.pgh.pa.us 3287 : 1531 : tz = DetermineTimeZoneOffset(tm, attimezone);
3288 : :
6842 bruce@momjian.us 3289 [ - + ]: 1531 : if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0)
6843 bruce@momjian.us 3290 [ # # ]:UBC 0 : ereport(ERROR,
3291 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3292 : : errmsg("timestamp out of range")));
3293 : : }
3294 : :
6843 bruce@momjian.us 3295 :CBC 48485 : timestamp += span->time;
3296 : :
2951 tgl@sss.pgh.pa.us 3297 [ + - - + ]: 48485 : if (!IS_VALID_TIMESTAMP(timestamp))
2951 tgl@sss.pgh.pa.us 3298 [ # # ]:UBC 0 : ereport(ERROR,
3299 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3300 : : errmsg("timestamp out of range")));
3301 : :
8234 lockhart@fourpalms.o 3302 :CBC 48485 : result = timestamp;
3303 : : }
3304 : :
393 tgl@sss.pgh.pa.us 3305 : 48929 : return result;
3306 : : }
3307 : :
3308 : : /* timestamptz_mi_interval_internal()
3309 : : * As above, but subtract the interval.
3310 : : */
3311 : : static TimestampTz
3312 : 1059 : timestamptz_mi_interval_internal(TimestampTz timestamp,
3313 : : Interval *span,
3314 : : pg_tz *attimezone)
3315 : : {
3316 : : Interval tspan;
3317 : :
152 dean.a.rasheed@gmail 3318 :GNC 1059 : interval_um_internal(span, &tspan);
3319 : :
393 tgl@sss.pgh.pa.us 3320 :CBC 1059 : return timestamptz_pl_interval_internal(timestamp, &tspan, attimezone);
3321 : : }
3322 : :
3323 : : /* timestamptz_pl_interval()
3324 : : * Add an interval to a timestamptz, in the session timezone.
3325 : : */
3326 : : Datum
3327 : 16662 : timestamptz_pl_interval(PG_FUNCTION_ARGS)
3328 : : {
3329 : 16662 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3330 : 16662 : Interval *span = PG_GETARG_INTERVAL_P(1);
3331 : :
3332 : 16662 : PG_RETURN_TIMESTAMP(timestamptz_pl_interval_internal(timestamp, span, NULL));
3333 : : }
3334 : :
3335 : : Datum
3336 : 816 : timestamptz_mi_interval(PG_FUNCTION_ARGS)
3337 : : {
3338 : 816 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3339 : 816 : Interval *span = PG_GETARG_INTERVAL_P(1);
3340 : :
3341 : 816 : PG_RETURN_TIMESTAMP(timestamptz_mi_interval_internal(timestamp, span, NULL));
3342 : : }
3343 : :
3344 : : /* timestamptz_pl_interval_at_zone()
3345 : : * Add an interval to a timestamptz, in the specified timezone.
3346 : : */
3347 : : Datum
3348 : 3 : timestamptz_pl_interval_at_zone(PG_FUNCTION_ARGS)
3349 : : {
3350 : 3 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3351 : 3 : Interval *span = PG_GETARG_INTERVAL_P(1);
3352 : 3 : text *zone = PG_GETARG_TEXT_PP(2);
3353 : 3 : pg_tz *attimezone = lookup_timezone(zone);
3354 : :
3355 : 3 : PG_RETURN_TIMESTAMP(timestamptz_pl_interval_internal(timestamp, span, attimezone));
3356 : : }
3357 : :
3358 : : Datum
3359 : 3 : timestamptz_mi_interval_at_zone(PG_FUNCTION_ARGS)
3360 : : {
3361 : 3 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
3362 : 3 : Interval *span = PG_GETARG_INTERVAL_P(1);
3363 : 3 : text *zone = PG_GETARG_TEXT_PP(2);
3364 : 3 : pg_tz *attimezone = lookup_timezone(zone);
3365 : :
3366 : 3 : PG_RETURN_TIMESTAMP(timestamptz_mi_interval_internal(timestamp, span, attimezone));
3367 : : }
3368 : :
3369 : : /* interval_um_internal()
3370 : : * Negate an interval.
3371 : : */
3372 : : static void
152 dean.a.rasheed@gmail 3373 :GNC 3480 : interval_um_internal(const Interval *interval, Interval *result)
3374 : : {
3375 [ + + + + : 3480 : if (INTERVAL_IS_NOBEGIN(interval))
+ - ]
3376 : 90 : INTERVAL_NOEND(result);
3377 [ + + + - : 3390 : else if (INTERVAL_IS_NOEND(interval))
+ - ]
3378 : 294 : INTERVAL_NOBEGIN(result);
3379 : : else
3380 : : {
3381 : : /* Negate each field, guarding against overflow */
3382 [ + + + + ]: 6189 : if (pg_sub_s64_overflow(INT64CONST(0), interval->time, &result->time) ||
3383 [ + + ]: 6183 : pg_sub_s32_overflow(0, interval->day, &result->day) ||
3384 : 3090 : pg_sub_s32_overflow(0, interval->month, &result->month) ||
3385 [ - + - - : 3087 : INTERVAL_NOT_FINITE(result))
- - + + +
+ + - ]
3386 [ + - ]: 15 : ereport(ERROR,
3387 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3388 : : errmsg("interval out of range")));
3389 : : }
3390 : 3465 : }
3391 : :
3392 : : Datum
8710 tgl@sss.pgh.pa.us 3393 :CBC 1317 : interval_um(PG_FUNCTION_ARGS)
3394 : : {
3395 : 1317 : Interval *interval = PG_GETARG_INTERVAL_P(0);
3396 : : Interval *result;
3397 : :
3398 : 1317 : result = (Interval *) palloc(sizeof(Interval));
152 dean.a.rasheed@gmail 3399 :GNC 1317 : interval_um_internal(interval, result);
3400 : :
8710 tgl@sss.pgh.pa.us 3401 :CBC 1302 : PG_RETURN_INTERVAL_P(result);
3402 : : }
3403 : :
3404 : :
3405 : : Datum
8710 tgl@sss.pgh.pa.us 3406 :UBC 0 : interval_smaller(PG_FUNCTION_ARGS)
3407 : : {
3408 : 0 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
3409 : 0 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
3410 : : Interval *result;
3411 : :
3412 : : /* use interval_cmp_internal to be sure this agrees with comparisons */
7555 3413 [ # # ]: 0 : if (interval_cmp_internal(interval1, interval2) < 0)
3414 : 0 : result = interval1;
3415 : : else
3416 : 0 : result = interval2;
8710 3417 : 0 : PG_RETURN_INTERVAL_P(result);
3418 : : }
3419 : :
3420 : : Datum
3421 : 0 : interval_larger(PG_FUNCTION_ARGS)
3422 : : {
3423 : 0 : Interval *interval1 = PG_GETARG_INTERVAL_P(0);
3424 : 0 : Interval *interval2 = PG_GETARG_INTERVAL_P(1);
3425 : : Interval *result;
3426 : :
7555 3427 [ # # ]: 0 : if (interval_cmp_internal(interval1, interval2) > 0)
3428 : 0 : result = interval1;
3429 : : else
3430 : 0 : result = interval2;
8710 3431 : 0 : PG_RETURN_INTERVAL_P(result);
3432 : : }
3433 : :
3434 : : static void
152 dean.a.rasheed@gmail 3435 :GNC 264 : finite_interval_pl(const Interval *span1, const Interval *span2, Interval *result)
3436 : : {
3437 [ - + - - : 264 : Assert(!INTERVAL_NOT_FINITE(span1));
- - - + -
- - - ]
3438 [ + + + - : 264 : Assert(!INTERVAL_NOT_FINITE(span2));
+ - + + +
- - + ]
3439 : :
3440 [ + - + - ]: 528 : if (pg_add_s32_overflow(span1->month, span2->month, &result->month) ||
3441 [ + - ]: 528 : pg_add_s32_overflow(span1->day, span2->day, &result->day) ||
3442 : 264 : pg_add_s64_overflow(span1->time, span2->time, &result->time) ||
3443 [ + + + - : 264 : INTERVAL_NOT_FINITE(result))
+ + + + +
- + + ]
3444 [ + - ]: 6 : ereport(ERROR,
3445 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3446 : : errmsg("interval out of range")));
3447 : 258 : }
3448 : :
3449 : : Datum
8710 tgl@sss.pgh.pa.us 3450 :CBC 279 : interval_pl(PG_FUNCTION_ARGS)
3451 : : {
3452 : 279 : Interval *span1 = PG_GETARG_INTERVAL_P(0);
3453 : 279 : Interval *span2 = PG_GETARG_INTERVAL_P(1);
3454 : : Interval *result;
3455 : :
3456 : 279 : result = (Interval *) palloc(sizeof(Interval));
3457 : :
3458 : : /*
3459 : : * Handle infinities.
3460 : : *
3461 : : * We treat anything that amounts to "infinity - infinity" as an error,
3462 : : * since the interval type has nothing equivalent to NaN.
3463 : : */
152 dean.a.rasheed@gmail 3464 [ + + + - :GNC 279 : if (INTERVAL_IS_NOBEGIN(span1))
+ - ]
3465 : : {
3466 [ + + + - : 27 : if (INTERVAL_IS_NOEND(span2))
+ - ]
3467 [ + - ]: 3 : ereport(ERROR,
3468 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3469 : : errmsg("interval out of range")));
3470 : : else
3471 : 24 : INTERVAL_NOBEGIN(result);
3472 : : }
3473 [ + + + - : 252 : else if (INTERVAL_IS_NOEND(span1))
+ - ]
3474 : : {
3475 [ + + + - : 21 : if (INTERVAL_IS_NOBEGIN(span2))
+ - ]
3476 [ + - ]: 3 : ereport(ERROR,
3477 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3478 : : errmsg("interval out of range")));
3479 : : else
3480 : 18 : INTERVAL_NOEND(result);
3481 : : }
3482 [ + + + - : 231 : else if (INTERVAL_NOT_FINITE(span2))
- + + + +
- + - ]
3483 : 69 : memcpy(result, span2, sizeof(Interval));
3484 : : else
3485 : 162 : finite_interval_pl(span1, span2, result);
3486 : :
152 dean.a.rasheed@gmail 3487 :CBC 267 : PG_RETURN_INTERVAL_P(result);
3488 : : }
3489 : :
3490 : : static void
152 dean.a.rasheed@gmail 3491 :GNC 774 : finite_interval_mi(const Interval *span1, const Interval *span2, Interval *result)
3492 : : {
3493 [ + + + + : 774 : Assert(!INTERVAL_NOT_FINITE(span1));
+ - + + +
+ - + ]
3494 [ + + + + : 774 : Assert(!INTERVAL_NOT_FINITE(span2));
+ - + + +
+ - + ]
3495 : :
3496 [ + - + - ]: 1548 : if (pg_sub_s32_overflow(span1->month, span2->month, &result->month) ||
3497 [ + - ]: 1548 : pg_sub_s32_overflow(span1->day, span2->day, &result->day) ||
3498 : 774 : pg_sub_s64_overflow(span1->time, span2->time, &result->time) ||
3499 [ + + + - : 774 : INTERVAL_NOT_FINITE(result))
- + + + +
- + - ]
3727 bruce@momjian.us 3500 [ + - ]: 6 : ereport(ERROR,
3501 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3502 : : errmsg("interval out of range")));
8710 tgl@sss.pgh.pa.us 3503 : 768 : }
3504 : :
3505 : : Datum
8710 tgl@sss.pgh.pa.us 3506 :CBC 897 : interval_mi(PG_FUNCTION_ARGS)
3507 : : {
3508 : 897 : Interval *span1 = PG_GETARG_INTERVAL_P(0);
3509 : 897 : Interval *span2 = PG_GETARG_INTERVAL_P(1);
3510 : : Interval *result;
3511 : :
3512 : 897 : result = (Interval *) palloc(sizeof(Interval));
3513 : :
3514 : : /*
3515 : : * Handle infinities.
3516 : : *
3517 : : * We treat anything that amounts to "infinity - infinity" as an error,
3518 : : * since the interval type has nothing equivalent to NaN.
3519 : : */
152 dean.a.rasheed@gmail 3520 [ + + + + :GNC 897 : if (INTERVAL_IS_NOBEGIN(span1))
+ + ]
3521 : : {
3522 [ + + + - : 27 : if (INTERVAL_IS_NOBEGIN(span2))
+ - ]
3523 [ + - ]: 3 : ereport(ERROR,
3524 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3525 : : errmsg("interval out of range")));
3526 : : else
3527 : 24 : INTERVAL_NOBEGIN(result);
3528 : : }
3529 [ + + + + : 870 : else if (INTERVAL_IS_NOEND(span1))
+ + ]
3530 : : {
3531 [ + + + - : 24 : if (INTERVAL_IS_NOEND(span2))
+ - ]
3532 [ + - ]: 3 : ereport(ERROR,
3533 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3534 : : errmsg("interval out of range")));
3535 : : else
3536 : 21 : INTERVAL_NOEND(result);
3537 : : }
3538 [ + + + + : 846 : else if (INTERVAL_IS_NOBEGIN(span2))
+ + ]
3539 : 3 : INTERVAL_NOEND(result);
3540 [ + + + + : 843 : else if (INTERVAL_IS_NOEND(span2))
+ + ]
3541 : 93 : INTERVAL_NOBEGIN(result);
3542 : : else
3543 : 750 : finite_interval_mi(span1, span2, result);
3544 : :
8710 tgl@sss.pgh.pa.us 3545 :CBC 885 : PG_RETURN_INTERVAL_P(result);
3546 : : }
3547 : :
3548 : : /*
3549 : : * There is no interval_abs(): it is unclear what value to return:
3550 : : * http://archives.postgresql.org/pgsql-general/2009-10/msg01031.php
3551 : : * http://archives.postgresql.org/pgsql-general/2009-11/msg00041.php
3552 : : */
3553 : :
3554 : : Datum
3555 : 5826 : interval_mul(PG_FUNCTION_ARGS)
3556 : : {
6839 bruce@momjian.us 3557 : 5826 : Interval *span = PG_GETARG_INTERVAL_P(0);
8710 tgl@sss.pgh.pa.us 3558 : 5826 : float8 factor = PG_GETARG_FLOAT8(1);
3559 : : double month_remainder_days,
3560 : : sec_remainder,
3561 : : result_double;
6402 bruce@momjian.us 3562 : 5826 : int32 orig_month = span->month,
3563 : 5826 : orig_day = span->day;
3564 : : Interval *result;
3565 : :
8710 tgl@sss.pgh.pa.us 3566 : 5826 : result = (Interval *) palloc(sizeof(Interval));
3567 : :
3568 : : /*
3569 : : * Handle NaN and infinities.
3570 : : *
3571 : : * We treat "0 * infinity" and "infinity * 0" as errors, since the
3572 : : * interval type has nothing equivalent to NaN.
3573 : : */
152 dean.a.rasheed@gmail 3574 [ + + ]:GNC 5826 : if (isnan(factor))
148 3575 : 6 : goto out_of_range;
3576 : :
152 3577 [ + + + - : 5820 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
3578 : : {
3579 [ + + ]: 30 : if (factor == 0.0)
148 3580 : 6 : goto out_of_range;
3581 : :
3582 [ + + ]: 24 : if (factor < 0.0)
152 3583 : 12 : interval_um_internal(span, result);
3584 : : else
3585 : 12 : memcpy(result, span, sizeof(Interval));
3586 : :
3587 : 24 : PG_RETURN_INTERVAL_P(result);
3588 : : }
3589 [ + + ]: 5790 : if (isinf(factor))
3590 : : {
3591 : 12 : int isign = interval_sign(span);
3592 : :
3593 [ + + ]: 12 : if (isign == 0)
148 3594 : 6 : goto out_of_range;
3595 : :
3596 [ + + ]: 6 : if (factor * isign < 0)
152 3597 : 3 : INTERVAL_NOBEGIN(result);
3598 : : else
3599 : 3 : INTERVAL_NOEND(result);
3600 : :
3601 : 6 : PG_RETURN_INTERVAL_P(result);
3602 : : }
3603 : :
3727 bruce@momjian.us 3604 :CBC 5778 : result_double = span->month * factor;
148 dean.a.rasheed@gmail 3605 [ + - + - : 5778 : if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double))
+ + ]
3606 : 3 : goto out_of_range;
3727 bruce@momjian.us 3607 : 5775 : result->month = (int32) result_double;
3608 : :
3609 : 5775 : result_double = span->day * factor;
148 dean.a.rasheed@gmail 3610 [ + - + - : 5775 : if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double))
+ + ]
3611 : 3 : goto out_of_range;
3727 bruce@momjian.us 3612 : 5772 : result->day = (int32) result_double;
3613 : :
3614 : : /*
3615 : : * The above correctly handles the whole-number part of the month and day
3616 : : * products, but we have to do something with any fractional part
3617 : : * resulting when the factor is non-integral. We cascade the fractions
3618 : : * down to lower units using the conversion factors DAYS_PER_MONTH and
3619 : : * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to do
3620 : : * so by the representation. The user can choose to cascade up later,
3621 : : * using justify_hours and/or justify_days.
3622 : : */
3623 : :
3624 : : /*
3625 : : * Fractional months full days into days.
3626 : : *
3627 : : * Floating point calculation are inherently imprecise, so these
3628 : : * calculations are crafted to produce the most reliable result possible.
3629 : : * TSROUND() is needed to more accurately produce whole numbers where
3630 : : * appropriate.
3631 : : */
6431 3632 : 5772 : month_remainder_days = (orig_month * factor - result->month) * DAYS_PER_MONTH;
3633 : 5772 : month_remainder_days = TSROUND(month_remainder_days);
3634 : 5772 : sec_remainder = (orig_day * factor - result->day +
2489 tgl@sss.pgh.pa.us 3635 : 5772 : month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY;
6431 bruce@momjian.us 3636 : 5772 : sec_remainder = TSROUND(sec_remainder);
3637 : :
3638 : : /*
3639 : : * Might have 24:00:00 hours due to rounding, or >24 hours because of time
3640 : : * cascade from months and days. It might still be >24 if the combination
3641 : : * of cascade and the seconds factor operation itself.
3642 : : */
555 peter@eisentraut.org 3643 [ - + ]: 5772 : if (fabs(sec_remainder) >= SECS_PER_DAY)
3644 : : {
148 dean.a.rasheed@gmail 3645 [ # # ]:UBC 0 : if (pg_add_s32_overflow(result->day,
3646 : 0 : (int) (sec_remainder / SECS_PER_DAY),
3647 : : &result->day))
3648 : 0 : goto out_of_range;
6402 bruce@momjian.us 3649 : 0 : sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
3650 : : }
3651 : :
3652 : : /* cascade units down */
148 dean.a.rasheed@gmail 3653 [ + + ]:CBC 5772 : if (pg_add_s32_overflow(result->day, (int32) month_remainder_days,
3654 : : &result->day))
3655 : 3 : goto out_of_range;
3727 bruce@momjian.us 3656 : 5769 : result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC);
1620 tgl@sss.pgh.pa.us 3657 [ + - + - : 5769 : if (isnan(result_double) || !FLOAT8_FITS_IN_INT64(result_double))
+ + ]
148 dean.a.rasheed@gmail 3658 : 3 : goto out_of_range;
3727 bruce@momjian.us 3659 : 5766 : result->time = (int64) result_double;
3660 : :
152 dean.a.rasheed@gmail 3661 [ + + + - :GNC 5766 : if (INTERVAL_NOT_FINITE(result))
- + - + -
- - - ]
148 3662 : 3 : goto out_of_range;
3663 : :
6807 bruce@momjian.us 3664 :CBC 5763 : PG_RETURN_INTERVAL_P(result);
3665 : :
148 dean.a.rasheed@gmail 3666 : 33 : out_of_range:
3667 [ + - ]: 33 : ereport(ERROR,
3668 : : errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3669 : : errmsg("interval out of range"));
3670 : :
3671 : : PG_RETURN_NULL(); /* keep compiler quiet */
3672 : : }
3673 : :
3674 : : Datum
8710 tgl@sss.pgh.pa.us 3675 : 5715 : mul_d_interval(PG_FUNCTION_ARGS)
3676 : : {
3677 : : /* Args are float8 and Interval *, but leave them as generic Datum */
3678 : 5715 : Datum factor = PG_GETARG_DATUM(0);
6839 bruce@momjian.us 3679 : 5715 : Datum span = PG_GETARG_DATUM(1);
3680 : :
3681 : 5715 : return DirectFunctionCall2(interval_mul, span, factor);
3682 : : }
3683 : :
3684 : : Datum
8710 tgl@sss.pgh.pa.us 3685 : 111 : interval_div(PG_FUNCTION_ARGS)
3686 : : {
8029 lockhart@fourpalms.o 3687 : 111 : Interval *span = PG_GETARG_INTERVAL_P(0);
8710 tgl@sss.pgh.pa.us 3688 : 111 : float8 factor = PG_GETARG_FLOAT8(1);
3689 : : double month_remainder_days,
3690 : : sec_remainder,
3691 : : result_double;
6402 bruce@momjian.us 3692 : 111 : int32 orig_month = span->month,
3693 : 111 : orig_day = span->day;
3694 : : Interval *result;
3695 : :
8710 tgl@sss.pgh.pa.us 3696 : 111 : result = (Interval *) palloc(sizeof(Interval));
3697 : :
3698 [ - + ]: 111 : if (factor == 0.0)
7567 tgl@sss.pgh.pa.us 3699 [ # # ]:UBC 0 : ereport(ERROR,
3700 : : (errcode(ERRCODE_DIVISION_BY_ZERO),
3701 : : errmsg("division by zero")));
3702 : :
3703 : : /*
3704 : : * Handle NaN and infinities.
3705 : : *
3706 : : * We treat "infinity / infinity" as an error, since the interval type has
3707 : : * nothing equivalent to NaN. Otherwise, dividing by infinity is handled
3708 : : * by the regular division code, causing all fields to be set to zero.
3709 : : */
152 dean.a.rasheed@gmail 3710 [ + + ]:GNC 111 : if (isnan(factor))
148 3711 : 6 : goto out_of_range;
3712 : :
152 3713 [ + + + - : 105 : if (INTERVAL_NOT_FINITE(span))
- + + + +
- + - ]
3714 : : {
3715 [ + + ]: 24 : if (isinf(factor))
148 3716 : 12 : goto out_of_range;
3717 : :
152 3718 [ + + ]: 12 : if (factor < 0.0)
3719 : 6 : interval_um_internal(span, result);
3720 : : else
3721 : 6 : memcpy(result, span, sizeof(Interval));
3722 : :
3723 : 12 : PG_RETURN_INTERVAL_P(result);
3724 : : }
3725 : :
148 dean.a.rasheed@gmail 3726 :CBC 81 : result_double = span->month / factor;
3727 [ + - + - : 81 : if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double))
+ + ]
3728 : 3 : goto out_of_range;
3729 : 78 : result->month = (int32) result_double;
3730 : :
3731 : 78 : result_double = span->day / factor;
3732 [ + - + - : 78 : if (isnan(result_double) || !FLOAT8_FITS_IN_INT32(result_double))
+ + ]
3733 : 3 : goto out_of_range;
3734 : 75 : result->day = (int32) result_double;
3735 : :
3736 : : /*
3737 : : * Fractional months full days into days. See comment in interval_mul().
3738 : : */
6431 bruce@momjian.us 3739 : 75 : month_remainder_days = (orig_month / factor - result->month) * DAYS_PER_MONTH;
3740 : 75 : month_remainder_days = TSROUND(month_remainder_days);
3741 : 75 : sec_remainder = (orig_day / factor - result->day +
2489 tgl@sss.pgh.pa.us 3742 : 75 : month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY;
6431 bruce@momjian.us 3743 : 75 : sec_remainder = TSROUND(sec_remainder);
555 peter@eisentraut.org 3744 [ + + ]: 75 : if (fabs(sec_remainder) >= SECS_PER_DAY)
3745 : : {
148 dean.a.rasheed@gmail 3746 [ - + ]: 3 : if (pg_add_s32_overflow(result->day,
3747 : 3 : (int) (sec_remainder / SECS_PER_DAY),
3748 : : &result->day))
148 dean.a.rasheed@gmail 3749 :UBC 0 : goto out_of_range;
6402 bruce@momjian.us 3750 :CBC 3 : sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY;
3751 : : }
3752 : :
3753 : : /* cascade units down */
148 dean.a.rasheed@gmail 3754 [ - + ]: 75 : if (pg_add_s32_overflow(result->day, (int32) month_remainder_days,
3755 : : &result->day))
148 dean.a.rasheed@gmail 3756 :UBC 0 : goto out_of_range;
148 dean.a.rasheed@gmail 3757 :CBC 75 : result_double = rint(span->time / factor + sec_remainder * USECS_PER_SEC);
3758 [ + - + - : 75 : if (isnan(result_double) || !FLOAT8_FITS_IN_INT64(result_double))
+ + ]
3759 : 3 : goto out_of_range;
3760 : 72 : result->time = (int64) result_double;
3761 : :
152 dean.a.rasheed@gmail 3762 [ + + + - :GNC 72 : if (INTERVAL_NOT_FINITE(result))
- + - + -
- - - ]
148 3763 : 3 : goto out_of_range;
3764 : :
6807 bruce@momjian.us 3765 :CBC 69 : PG_RETURN_INTERVAL_P(result);
3766 : :
148 dean.a.rasheed@gmail 3767 : 30 : out_of_range:
3768 [ + - ]: 30 : ereport(ERROR,
3769 : : errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
3770 : : errmsg("interval out of range"));
3771 : :
3772 : : PG_RETURN_NULL(); /* keep compiler quiet */
3773 : : }
3774 : :
3775 : :
3776 : : /*
3777 : : * in_range support functions for timestamps and intervals.
3778 : : *
3779 : : * Per SQL spec, we support these with interval as the offset type.
3780 : : * The spec's restriction that the offset not be negative is a bit hard to
3781 : : * decipher for intervals, but we choose to interpret it the same as our
3782 : : * interval comparison operators would.
3783 : : */
3784 : :
3785 : : Datum
2258 tgl@sss.pgh.pa.us 3786 : 567 : in_range_timestamptz_interval(PG_FUNCTION_ARGS)
3787 : : {
3788 : 567 : TimestampTz val = PG_GETARG_TIMESTAMPTZ(0);
3789 : 567 : TimestampTz base = PG_GETARG_TIMESTAMPTZ(1);
3790 : 567 : Interval *offset = PG_GETARG_INTERVAL_P(2);
3791 : 567 : bool sub = PG_GETARG_BOOL(3);
3792 : 567 : bool less = PG_GETARG_BOOL(4);
3793 : : TimestampTz sum;
3794 : :
152 dean.a.rasheed@gmail 3795 [ + + ]:GNC 567 : if (interval_sign(offset) < 0)
2258 tgl@sss.pgh.pa.us 3796 [ + - ]:GBC 6 : ereport(ERROR,
3797 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
3798 : : errmsg("invalid preceding or following size in window function")));
3799 : :
3800 : : /*
3801 : : * Deal with cases where both base and offset are infinite, and computing
3802 : : * base +/- offset would cause an error. As for float and numeric types,
3803 : : * we assume that all values infinitely precede +infinity and infinitely
3804 : : * follow -infinity. See in_range_float8_float8() for reasoning.
3805 : : */
152 dean.a.rasheed@gmail 3806 [ + + + - :GNC 561 : if (INTERVAL_IS_NOEND(offset) &&
+ - + + +
+ ]
3807 : : (sub ? TIMESTAMP_IS_NOEND(base) : TIMESTAMP_IS_NOBEGIN(base)))
3808 : 114 : PG_RETURN_BOOL(true);
3809 : :
3810 : : /* We don't currently bother to avoid overflow hazards here */
2258 tgl@sss.pgh.pa.us 3811 [ + + ]:CBC 447 : if (sub)
393 3812 : 240 : sum = timestamptz_mi_interval_internal(base, offset, NULL);
3813 : : else
3814 : 207 : sum = timestamptz_pl_interval_internal(base, offset, NULL);
3815 : :
2258 3816 [ + + ]: 447 : if (less)
3817 : 177 : PG_RETURN_BOOL(val <= sum);
3818 : : else
3819 : 270 : PG_RETURN_BOOL(val >= sum);
3820 : : }
3821 : :
3822 : : Datum
3823 : 1233 : in_range_timestamp_interval(PG_FUNCTION_ARGS)
3824 : : {
3825 : 1233 : Timestamp val = PG_GETARG_TIMESTAMP(0);
3826 : 1233 : Timestamp base = PG_GETARG_TIMESTAMP(1);
3827 : 1233 : Interval *offset = PG_GETARG_INTERVAL_P(2);
3828 : 1233 : bool sub = PG_GETARG_BOOL(3);
3829 : 1233 : bool less = PG_GETARG_BOOL(4);
3830 : : Timestamp sum;
3831 : :
152 dean.a.rasheed@gmail 3832 [ + + ]:GNC 1233 : if (interval_sign(offset) < 0)
2258 tgl@sss.pgh.pa.us 3833 [ + - ]:CBC 9 : ereport(ERROR,
3834 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
3835 : : errmsg("invalid preceding or following size in window function")));
3836 : :
3837 : : /*
3838 : : * Deal with cases where both base and offset are infinite, and computing
3839 : : * base +/- offset would cause an error. As for float and numeric types,
3840 : : * we assume that all values infinitely precede +infinity and infinitely
3841 : : * follow -infinity. See in_range_float8_float8() for reasoning.
3842 : : */
152 dean.a.rasheed@gmail 3843 [ + + + - :GNC 1224 : if (INTERVAL_IS_NOEND(offset) &&
+ - + + +
+ ]
3844 : : (sub ? TIMESTAMP_IS_NOEND(base) : TIMESTAMP_IS_NOBEGIN(base)))
3845 : 114 : PG_RETURN_BOOL(true);
3846 : :
3847 : : /* We don't currently bother to avoid overflow hazards here */
2258 tgl@sss.pgh.pa.us 3848 [ + + ]:CBC 1110 : if (sub)
3849 : 516 : sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_mi_interval,
3850 : : TimestampGetDatum(base),
3851 : : IntervalPGetDatum(offset)));
3852 : : else
3853 : 594 : sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_pl_interval,
3854 : : TimestampGetDatum(base),
3855 : : IntervalPGetDatum(offset)));
3856 : :
3857 [ + + ]: 1110 : if (less)
3858 : 603 : PG_RETURN_BOOL(val <= sum);
3859 : : else
3860 : 507 : PG_RETURN_BOOL(val >= sum);
3861 : : }
3862 : :
3863 : : Datum
3864 : 564 : in_range_interval_interval(PG_FUNCTION_ARGS)
3865 : : {
3866 : 564 : Interval *val = PG_GETARG_INTERVAL_P(0);
3867 : 564 : Interval *base = PG_GETARG_INTERVAL_P(1);
3868 : 564 : Interval *offset = PG_GETARG_INTERVAL_P(2);
3869 : 564 : bool sub = PG_GETARG_BOOL(3);
3870 : 564 : bool less = PG_GETARG_BOOL(4);
3871 : : Interval *sum;
3872 : :
152 dean.a.rasheed@gmail 3873 [ + + ]:GNC 564 : if (interval_sign(offset) < 0)
2258 tgl@sss.pgh.pa.us 3874 [ + - ]:GBC 6 : ereport(ERROR,
3875 : : (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
3876 : : errmsg("invalid preceding or following size in window function")));
3877 : :
3878 : : /*
3879 : : * Deal with cases where both base and offset are infinite, and computing
3880 : : * base +/- offset would cause an error. As for float and numeric types,
3881 : : * we assume that all values infinitely precede +infinity and infinitely
3882 : : * follow -infinity. See in_range_float8_float8() for reasoning.
3883 : : */
152 dean.a.rasheed@gmail 3884 [ + + + - :GNC 840 : if (INTERVAL_IS_NOEND(offset) &&
+ - + + +
+ ]
3885 [ + + + - : 282 : (sub ? INTERVAL_IS_NOEND(base) : INTERVAL_IS_NOBEGIN(base)))
+ - + + +
- + - ]
3886 : 114 : PG_RETURN_BOOL(true);
3887 : :
3888 : : /* We don't currently bother to avoid overflow hazards here */
2258 tgl@sss.pgh.pa.us 3889 [ + + ]:CBC 444 : if (sub)
3890 : 240 : sum = DatumGetIntervalP(DirectFunctionCall2(interval_mi,
3891 : : IntervalPGetDatum(base),
3892 : : IntervalPGetDatum(offset)));
3893 : : else
3894 : 204 : sum = DatumGetIntervalP(DirectFunctionCall2(interval_pl,
3895 : : IntervalPGetDatum(base),
3896 : : IntervalPGetDatum(offset)));
3897 : :
3898 [ + + ]: 444 : if (less)
3899 : 174 : PG_RETURN_BOOL(interval_cmp_internal(val, sum) <= 0);
3900 : : else
3901 : 270 : PG_RETURN_BOOL(interval_cmp_internal(val, sum) >= 0);
3902 : : }
3903 : :
3904 : :
3905 : : /*
3906 : : * Prepare state data for an interval aggregate function, that needs to compute
3907 : : * sum and count, in the aggregate's memory context.
3908 : : *
3909 : : * The function is used when the state data needs to be allocated in aggregate's
3910 : : * context. When the state data needs to be allocated in the current memory
3911 : : * context, we use palloc0 directly e.g. interval_avg_deserialize().
3912 : : */
3913 : : static IntervalAggState *
152 dean.a.rasheed@gmail 3914 :GNC 27 : makeIntervalAggState(FunctionCallInfo fcinfo)
3915 : : {
3916 : : IntervalAggState *state;
3917 : : MemoryContext agg_context;
3918 : : MemoryContext old_context;
3919 : :
3920 [ - + ]: 27 : if (!AggCheckCallContext(fcinfo, &agg_context))
152 dean.a.rasheed@gmail 3921 [ # # ]:UNC 0 : elog(ERROR, "aggregate function called in non-aggregate context");
3922 : :
152 dean.a.rasheed@gmail 3923 :GNC 27 : old_context = MemoryContextSwitchTo(agg_context);
3924 : :
3925 : 27 : state = (IntervalAggState *) palloc0(sizeof(IntervalAggState));
3926 : :
3927 : 27 : MemoryContextSwitchTo(old_context);
3928 : :
3929 : 27 : return state;
3930 : : }
3931 : :
3932 : : /*
3933 : : * Accumulate a new input value for interval aggregate functions.
3934 : : */
3935 : : static void
3936 : 162 : do_interval_accum(IntervalAggState *state, Interval *newval)
3937 : : {
3938 : : /* Infinite inputs are counted separately, and do not affect "N" */
3939 [ + + + - : 162 : if (INTERVAL_IS_NOBEGIN(newval))
+ + ]
3940 : : {
3941 : 30 : state->nInfcount++;
3942 : 30 : return;
3943 : : }
3944 : :
3945 [ + + + - : 132 : if (INTERVAL_IS_NOEND(newval))
+ + ]
3946 : : {
3947 : 30 : state->pInfcount++;
3948 : 30 : return;
3949 : : }
3950 : :
3951 : 102 : finite_interval_pl(&state->sumX, newval, &state->sumX);
3952 : 102 : state->N++;
3953 : : }
3954 : :
3955 : : /*
3956 : : * Remove the given interval value from the aggregated state.
3957 : : */
3958 : : static void
3959 : 102 : do_interval_discard(IntervalAggState *state, Interval *newval)
3960 : : {
3961 : : /* Infinite inputs are counted separately, and do not affect "N" */
3962 [ + + + - : 102 : if (INTERVAL_IS_NOBEGIN(newval))
+ + ]
3963 : : {
3964 : 12 : state->nInfcount--;
3965 : 12 : return;
3966 : : }
3967 : :
3968 [ + + + - : 90 : if (INTERVAL_IS_NOEND(newval))
+ + ]
3969 : : {
3970 : 24 : state->pInfcount--;
3971 : 24 : return;
3972 : : }
3973 : :
3974 : : /* Handle the to-be-discarded finite value. */
3975 : 66 : state->N--;
3976 [ + + ]: 66 : if (state->N > 0)
3977 : 24 : finite_interval_mi(&state->sumX, newval, &state->sumX);
3978 : : else
3979 : : {
3980 : : /* All values discarded, reset the state */
3981 [ - + ]: 42 : Assert(state->N == 0);
3982 : 42 : memset(&state->sumX, 0, sizeof(state->sumX));
3983 : : }
3984 : : }
3985 : :
3986 : : /*
3987 : : * Transition function for sum() and avg() interval aggregates.
3988 : : */
3989 : : Datum
3990 : 204 : interval_avg_accum(PG_FUNCTION_ARGS)
3991 : : {
3992 : : IntervalAggState *state;
3993 : :
3994 [ + + ]: 204 : state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0);
3995 : :
3996 : : /* Create the state data on the first call */
3997 [ + + ]: 204 : if (state == NULL)
3998 : 27 : state = makeIntervalAggState(fcinfo);
3999 : :
4000 [ + + ]: 204 : if (!PG_ARGISNULL(1))
4001 : 162 : do_interval_accum(state, PG_GETARG_INTERVAL_P(1));
4002 : :
4003 : 204 : PG_RETURN_POINTER(state);
4004 : : }
4005 : :
4006 : : /*
4007 : : * Combine function for sum() and avg() interval aggregates.
4008 : : *
4009 : : * Combine the given internal aggregate states and place the combination in
4010 : : * the first argument.
4011 : : */
4012 : : Datum
152 dean.a.rasheed@gmail 4013 :UNC 0 : interval_avg_combine(PG_FUNCTION_ARGS)
4014 : : {
4015 : : IntervalAggState *state1;
4016 : : IntervalAggState *state2;
4017 : :
4018 [ # # ]: 0 : state1 = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0);
4019 [ # # ]: 0 : state2 = PG_ARGISNULL(1) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(1);
4020 : :
4021 [ # # ]: 0 : if (state2 == NULL)
4022 : 0 : PG_RETURN_POINTER(state1);
4023 : :
4024 [ # # ]: 0 : if (state1 == NULL)
4025 : : {
4026 : : /* manually copy all fields from state2 to state1 */
4027 : 0 : state1 = makeIntervalAggState(fcinfo);
4028 : :
4029 : 0 : state1->N = state2->N;
4030 : 0 : state1->pInfcount = state2->pInfcount;
4031 : 0 : state1->nInfcount = state2->nInfcount;
4032 : :
4033 : 0 : state1->sumX.day = state2->sumX.day;
4034 : 0 : state1->sumX.month = state2->sumX.month;
4035 : 0 : state1->sumX.time = state2->sumX.time;
4036 : :
4037 : 0 : PG_RETURN_POINTER(state1);
4038 : : }
4039 : :
4040 : 0 : state1->N += state2->N;
4041 : 0 : state1->pInfcount += state2->pInfcount;
4042 : 0 : state1->nInfcount += state2->nInfcount;
4043 : :
4044 : : /* Accumulate finite interval values, if any. */
4045 [ # # ]: 0 : if (state2->N > 0)
4046 : 0 : finite_interval_pl(&state1->sumX, &state2->sumX, &state1->sumX);
4047 : :
4048 : 0 : PG_RETURN_POINTER(state1);
4049 : : }
4050 : :
4051 : : /*
4052 : : * interval_avg_serialize
4053 : : * Serialize IntervalAggState for interval aggregates.
4054 : : */
4055 : : Datum
4056 : 0 : interval_avg_serialize(PG_FUNCTION_ARGS)
4057 : : {
4058 : : IntervalAggState *state;
4059 : : StringInfoData buf;
4060 : : bytea *result;
4061 : :
4062 : : /* Ensure we disallow calling when not in aggregate context */
4063 [ # # ]: 0 : if (!AggCheckCallContext(fcinfo, NULL))
4064 [ # # ]: 0 : elog(ERROR, "aggregate function called in non-aggregate context");
4065 : :
4066 : 0 : state = (IntervalAggState *) PG_GETARG_POINTER(0);
4067 : :
4068 : 0 : pq_begintypsend(&buf);
4069 : :
4070 : : /* N */
4071 : 0 : pq_sendint64(&buf, state->N);
4072 : :
4073 : : /* sumX */
4074 : 0 : pq_sendint64(&buf, state->sumX.time);
4075 : 0 : pq_sendint32(&buf, state->sumX.day);
4076 : 0 : pq_sendint32(&buf, state->sumX.month);
4077 : :
4078 : : /* pInfcount */
4079 : 0 : pq_sendint64(&buf, state->pInfcount);
4080 : :
4081 : : /* nInfcount */
4082 : 0 : pq_sendint64(&buf, state->nInfcount);
4083 : :
4084 : 0 : result = pq_endtypsend(&buf);
4085 : :
4086 : 0 : PG_RETURN_BYTEA_P(result);
4087 : : }
4088 : :
4089 : : /*
4090 : : * interval_avg_deserialize
4091 : : * Deserialize bytea into IntervalAggState for interval aggregates.
4092 : : */
4093 : : Datum
4094 : 0 : interval_avg_deserialize(PG_FUNCTION_ARGS)
4095 : : {
4096 : : bytea *sstate;
4097 : : IntervalAggState *result;
4098 : : StringInfoData buf;
4099 : :
4100 [ # # ]: 0 : if (!AggCheckCallContext(fcinfo, NULL))
4101 [ # # ]: 0 : elog(ERROR, "aggregate function called in non-aggregate context");
4102 : :
4103 : 0 : sstate = PG_GETARG_BYTEA_PP(0);
4104 : :
4105 : : /*
4106 : : * Initialize a StringInfo so that we can "receive" it using the standard
4107 : : * recv-function infrastructure.
4108 : : */
4109 [ # # ]: 0 : initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
4110 [ # # # # : 0 : VARSIZE_ANY_EXHDR(sstate));
# # # # #
# ]
4111 : :
4112 : 0 : result = (IntervalAggState *) palloc0(sizeof(IntervalAggState));
4113 : :
4114 : : /* N */
4115 : 0 : result->N = pq_getmsgint64(&buf);
4116 : :
4117 : : /* sumX */
4118 : 0 : result->sumX.time = pq_getmsgint64(&buf);
4119 : 0 : result->sumX.day = pq_getmsgint(&buf, 4);
4120 : 0 : result->sumX.month = pq_getmsgint(&buf, 4);
4121 : :
4122 : : /* pInfcount */
4123 : 0 : result->pInfcount = pq_getmsgint64(&buf);
4124 : :
4125 : : /* nInfcount */
4126 : 0 : result->nInfcount = pq_getmsgint64(&buf);
4127 : :
4128 : 0 : pq_getmsgend(&buf);
4129 : :
4130 : 0 : PG_RETURN_POINTER(result);
4131 : : }
4132 : :
4133 : : /*
4134 : : * Inverse transition function for sum() and avg() interval aggregates.
4135 : : */
4136 : : Datum
152 dean.a.rasheed@gmail 4137 :GNC 132 : interval_avg_accum_inv(PG_FUNCTION_ARGS)
4138 : : {
4139 : : IntervalAggState *state;
4140 : :
4141 [ + - ]: 132 : state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0);
4142 : :
4143 : : /* Should not get here with no state */
4144 [ - + ]: 132 : if (state == NULL)
152 dean.a.rasheed@gmail 4145 [ # # ]:UNC 0 : elog(ERROR, "interval_avg_accum_inv called with NULL state");
4146 : :
152 dean.a.rasheed@gmail 4147 [ + + ]:GNC 132 : if (!PG_ARGISNULL(1))
4148 : 102 : do_interval_discard(state, PG_GETARG_INTERVAL_P(1));
4149 : :
4150 : 132 : PG_RETURN_POINTER(state);
4151 : : }
4152 : :
4153 : : /* avg(interval) aggregate final function */
4154 : : Datum
152 dean.a.rasheed@gmail 4155 :CBC 84 : interval_avg(PG_FUNCTION_ARGS)
4156 : : {
4157 : : IntervalAggState *state;
4158 : :
152 dean.a.rasheed@gmail 4159 [ + - ]:GNC 84 : state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0);
4160 : :
4161 : : /* If there were no non-null inputs, return NULL */
4162 [ + - + + ]: 84 : if (state == NULL || IA_TOTAL_COUNT(state) == 0)
8672 tgl@sss.pgh.pa.us 4163 :CBC 9 : PG_RETURN_NULL();
4164 : :
4165 : : /*
4166 : : * Aggregating infinities that all have the same sign produces infinity
4167 : : * with that sign. Aggregating infinities with different signs results in
4168 : : * an error.
4169 : : */
152 dean.a.rasheed@gmail 4170 [ + + + + ]:GNC 75 : if (state->pInfcount > 0 || state->nInfcount > 0)
4171 : : {
4172 : : Interval *result;
4173 : :
4174 [ + + + + ]: 54 : if (state->pInfcount > 0 && state->nInfcount > 0)
4175 [ + - ]: 3 : ereport(ERROR,
4176 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4177 : : errmsg("interval out of range.")));
4178 : :
4179 : 51 : result = (Interval *) palloc(sizeof(Interval));
4180 [ + + ]: 51 : if (state->pInfcount > 0)
4181 : 30 : INTERVAL_NOEND(result);
4182 : : else
4183 : 21 : INTERVAL_NOBEGIN(result);
4184 : :
4185 : 51 : PG_RETURN_INTERVAL_P(result);
4186 : : }
4187 : :
8672 tgl@sss.pgh.pa.us 4188 :CBC 21 : return DirectFunctionCall2(interval_div,
4189 : : IntervalPGetDatum(&state->sumX),
4190 : : Float8GetDatum((double) state->N));
4191 : : }
4192 : :
4193 : : /* sum(interval) aggregate final function */
4194 : : Datum
152 dean.a.rasheed@gmail 4195 :GNC 81 : interval_sum(PG_FUNCTION_ARGS)
4196 : : {
4197 : : IntervalAggState *state;
4198 : : Interval *result;
4199 : :
4200 [ + - ]: 81 : state = PG_ARGISNULL(0) ? NULL : (IntervalAggState *) PG_GETARG_POINTER(0);
4201 : :
4202 : : /* If there were no non-null inputs, return NULL */
4203 [ + - + + ]: 81 : if (state == NULL || IA_TOTAL_COUNT(state) == 0)
4204 : 9 : PG_RETURN_NULL();
4205 : :
4206 : : /*
4207 : : * Aggregating infinities that all have the same sign produces infinity
4208 : : * with that sign. Aggregating infinities with different signs results in
4209 : : * an error.
4210 : : */
4211 [ + + + + ]: 72 : if (state->pInfcount > 0 && state->nInfcount > 0)
4212 [ + - ]: 3 : ereport(ERROR,
4213 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4214 : : errmsg("interval out of range.")));
4215 : :
4216 : 69 : result = (Interval *) palloc(sizeof(Interval));
4217 : :
4218 [ + + ]: 69 : if (state->pInfcount > 0)
4219 : 30 : INTERVAL_NOEND(result);
4220 [ + + ]: 39 : else if (state->nInfcount > 0)
4221 : 21 : INTERVAL_NOBEGIN(result);
4222 : : else
4223 : 18 : memcpy(result, &state->sumX, sizeof(Interval));
4224 : :
4225 : 69 : PG_RETURN_INTERVAL_P(result);
4226 : : }
4227 : :
4228 : : /* timestamp_age()
4229 : : * Calculate time difference while retaining year/month fields.
4230 : : * Note that this does not result in an accurate absolute time span
4231 : : * since year and month are out of context once the arithmetic
4232 : : * is done.
4233 : : */
4234 : : Datum
8710 tgl@sss.pgh.pa.us 4235 :GBC 18 : timestamp_age(PG_FUNCTION_ARGS)
4236 : : {
4237 : 18 : Timestamp dt1 = PG_GETARG_TIMESTAMP(0);
4238 : 18 : Timestamp dt2 = PG_GETARG_TIMESTAMP(1);
4239 : : Interval *result;
4240 : : fsec_t fsec1,
4241 : : fsec2;
4242 : : struct pg_itm tt,
8824 lockhart@fourpalms.o 4243 : 18 : *tm = &tt;
4244 : : struct pg_tm tt1,
4245 : 18 : *tm1 = &tt1;
4246 : : struct pg_tm tt2,
4247 : 18 : *tm2 = &tt2;
4248 : :
8710 tgl@sss.pgh.pa.us 4249 : 18 : result = (Interval *) palloc(sizeof(Interval));
4250 : :
4251 : : /*
4252 : : * Handle infinities.
4253 : : *
4254 : : * We treat anything that amounts to "infinity - infinity" as an error,
4255 : : * since the interval type has nothing equivalent to NaN.
4256 : : */
152 dean.a.rasheed@gmail 4257 [ + + ]:GNC 18 : if (TIMESTAMP_IS_NOBEGIN(dt1))
4258 : : {
4259 [ + + ]: 6 : if (TIMESTAMP_IS_NOBEGIN(dt2))
4260 [ + - ]: 3 : ereport(ERROR,
4261 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4262 : : errmsg("interval out of range")));
4263 : : else
4264 : 3 : INTERVAL_NOBEGIN(result);
4265 : : }
4266 [ + + ]: 12 : else if (TIMESTAMP_IS_NOEND(dt1))
4267 : : {
4268 [ + + ]: 6 : if (TIMESTAMP_IS_NOEND(dt2))
4269 [ + - ]: 3 : ereport(ERROR,
4270 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4271 : : errmsg("interval out of range")));
4272 : : else
4273 : 3 : INTERVAL_NOEND(result);
4274 : : }
4275 [ + + ]: 6 : else if (TIMESTAMP_IS_NOBEGIN(dt2))
4276 : 3 : INTERVAL_NOEND(result);
4277 [ + - ]: 3 : else if (TIMESTAMP_IS_NOEND(dt2))
4278 : 3 : INTERVAL_NOBEGIN(result);
152 dean.a.rasheed@gmail 4279 [ # # # # ]:UNC 0 : else if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 &&
4280 : 0 : timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0)
4281 : : {
4282 : : /* form the symbolic difference */
743 tgl@sss.pgh.pa.us 4283 :UBC 0 : tm->tm_usec = fsec1 - fsec2;
6900 bruce@momjian.us 4284 : 0 : tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
4285 : 0 : tm->tm_min = tm1->tm_min - tm2->tm_min;
4286 : 0 : tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
4287 : 0 : tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
4288 : 0 : tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
4289 : 0 : tm->tm_year = tm1->tm_year - tm2->tm_year;
4290 : :
4291 : : /* flip sign if necessary... */
8824 lockhart@fourpalms.o 4292 [ # # ]: 0 : if (dt1 < dt2)
4293 : : {
743 tgl@sss.pgh.pa.us 4294 : 0 : tm->tm_usec = -tm->tm_usec;
8824 lockhart@fourpalms.o 4295 : 0 : tm->tm_sec = -tm->tm_sec;
4296 : 0 : tm->tm_min = -tm->tm_min;
4297 : 0 : tm->tm_hour = -tm->tm_hour;
4298 : 0 : tm->tm_mday = -tm->tm_mday;
4299 : 0 : tm->tm_mon = -tm->tm_mon;
4300 : 0 : tm->tm_year = -tm->tm_year;
4301 : : }
4302 : :
4303 : : /* propagate any negative fields into the next higher field */
743 tgl@sss.pgh.pa.us 4304 [ # # ]: 0 : while (tm->tm_usec < 0)
4305 : : {
4306 : 0 : tm->tm_usec += USECS_PER_SEC;
6115 bruce@momjian.us 4307 : 0 : tm->tm_sec--;
4308 : : }
4309 : :
7074 tgl@sss.pgh.pa.us 4310 [ # # ]: 0 : while (tm->tm_sec < 0)
4311 : : {
6842 bruce@momjian.us 4312 : 0 : tm->tm_sec += SECS_PER_MINUTE;
8824 lockhart@fourpalms.o 4313 : 0 : tm->tm_min--;
4314 : : }
4315 : :
7074 tgl@sss.pgh.pa.us 4316 [ # # ]: 0 : while (tm->tm_min < 0)
4317 : : {
6842 bruce@momjian.us 4318 : 0 : tm->tm_min += MINS_PER_HOUR;
8824 lockhart@fourpalms.o 4319 : 0 : tm->tm_hour--;
4320 : : }
4321 : :
7074 tgl@sss.pgh.pa.us 4322 [ # # ]: 0 : while (tm->tm_hour < 0)
4323 : : {
6842 bruce@momjian.us 4324 : 0 : tm->tm_hour += HOURS_PER_DAY;
8824 lockhart@fourpalms.o 4325 : 0 : tm->tm_mday--;
4326 : : }
4327 : :
7074 tgl@sss.pgh.pa.us 4328 [ # # ]: 0 : while (tm->tm_mday < 0)
4329 : : {
8824 lockhart@fourpalms.o 4330 [ # # ]: 0 : if (dt1 < dt2)
4331 : : {
4332 [ # # # # : 0 : tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
# # ]
4333 : 0 : tm->tm_mon--;
4334 : : }
4335 : : else
4336 : : {
4337 [ # # # # : 0 : tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
# # ]
4338 : 0 : tm->tm_mon--;
4339 : : }
4340 : : }
4341 : :
7074 tgl@sss.pgh.pa.us 4342 [ # # ]: 0 : while (tm->tm_mon < 0)
4343 : : {
6842 bruce@momjian.us 4344 : 0 : tm->tm_mon += MONTHS_PER_YEAR;
8824 lockhart@fourpalms.o 4345 : 0 : tm->tm_year--;
4346 : : }
4347 : :
4348 : : /* recover sign if necessary... */
4349 [ # # ]: 0 : if (dt1 < dt2)
4350 : : {
743 tgl@sss.pgh.pa.us 4351 : 0 : tm->tm_usec = -tm->tm_usec;
8824 lockhart@fourpalms.o 4352 : 0 : tm->tm_sec = -tm->tm_sec;
4353 : 0 : tm->tm_min = -tm->tm_min;
4354 : 0 : tm->tm_hour = -tm->tm_hour;
4355 : 0 : tm->tm_mday = -tm->tm_mday;
4356 : 0 : tm->tm_mon = -tm->tm_mon;
4357 : 0 : tm->tm_year = -tm->tm_year;
4358 : : }
4359 : :
743 tgl@sss.pgh.pa.us 4360 [ # # ]: 0 : if (itm2interval(tm, result) != 0)
7567 4361 [ # # ]: 0 : ereport(ERROR,
4362 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4363 : : errmsg("interval out of range")));
4364 : : }
4365 : : else
4366 [ # # ]: 0 : ereport(ERROR,
4367 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4368 : : errmsg("timestamp out of range")));
4369 : :
8710 tgl@sss.pgh.pa.us 4370 :GBC 12 : PG_RETURN_INTERVAL_P(result);
4371 : : }
4372 : :
4373 : :
4374 : : /* timestamptz_age()
4375 : : * Calculate time difference while retaining year/month fields.
4376 : : * Note that this does not result in an accurate absolute time span
4377 : : * since year and month are out of context once the arithmetic
4378 : : * is done.
4379 : : */
4380 : : Datum
8234 lockhart@fourpalms.o 4381 : 18 : timestamptz_age(PG_FUNCTION_ARGS)
4382 : : {
7643 tgl@sss.pgh.pa.us 4383 : 18 : TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0);
4384 : 18 : TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1);
4385 : : Interval *result;
4386 : : fsec_t fsec1,
4387 : : fsec2;
4388 : : struct pg_itm tt,
8234 lockhart@fourpalms.o 4389 : 18 : *tm = &tt;
4390 : : struct pg_tm tt1,
4391 : 18 : *tm1 = &tt1;
4392 : : struct pg_tm tt2,
4393 : 18 : *tm2 = &tt2;
4394 : : int tz1;
4395 : : int tz2;
4396 : :
4397 : 18 : result = (Interval *) palloc(sizeof(Interval));
4398 : :
4399 : : /*
4400 : : * Handle infinities.
4401 : : *
4402 : : * We treat anything that amounts to "infinity - infinity" as an error,
4403 : : * since the interval type has nothing equivalent to NaN.
4404 : : */
152 dean.a.rasheed@gmail 4405 [ + + ]:GNC 18 : if (TIMESTAMP_IS_NOBEGIN(dt1))
4406 : : {
4407 [ + + ]: 6 : if (TIMESTAMP_IS_NOBEGIN(dt2))
4408 [ + - ]: 3 : ereport(ERROR,
4409 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4410 : : errmsg("interval out of range")));
4411 : : else
4412 : 3 : INTERVAL_NOBEGIN(result);
4413 : : }
4414 [ + + ]: 12 : else if (TIMESTAMP_IS_NOEND(dt1))
4415 : : {
4416 [ + + ]: 6 : if (TIMESTAMP_IS_NOEND(dt2))
4417 [ + - ]: 3 : ereport(ERROR,
4418 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4419 : : errmsg("interval out of range")));
4420 : : else
4421 : 3 : INTERVAL_NOEND(result);
4422 : : }
4423 [ + + ]: 6 : else if (TIMESTAMP_IS_NOBEGIN(dt2))
4424 : 3 : INTERVAL_NOEND(result);
4425 [ + - ]: 3 : else if (TIMESTAMP_IS_NOEND(dt2))
4426 : 3 : INTERVAL_NOBEGIN(result);
152 dean.a.rasheed@gmail 4427 [ # # # # ]:UNC 0 : else if (timestamp2tm(dt1, &tz1, tm1, &fsec1, NULL, NULL) == 0 &&
4428 : 0 : timestamp2tm(dt2, &tz2, tm2, &fsec2, NULL, NULL) == 0)
4429 : : {
4430 : : /* form the symbolic difference */
743 tgl@sss.pgh.pa.us 4431 :UBC 0 : tm->tm_usec = fsec1 - fsec2;
6900 bruce@momjian.us 4432 : 0 : tm->tm_sec = tm1->tm_sec - tm2->tm_sec;
4433 : 0 : tm->tm_min = tm1->tm_min - tm2->tm_min;
4434 : 0 : tm->tm_hour = tm1->tm_hour - tm2->tm_hour;
4435 : 0 : tm->tm_mday = tm1->tm_mday - tm2->tm_mday;
4436 : 0 : tm->tm_mon = tm1->tm_mon - tm2->tm_mon;
4437 : 0 : tm->tm_year = tm1->tm_year - tm2->tm_year;
4438 : :
4439 : : /* flip sign if necessary... */
8234 lockhart@fourpalms.o 4440 [ # # ]: 0 : if (dt1 < dt2)
4441 : : {
743 tgl@sss.pgh.pa.us 4442 : 0 : tm->tm_usec = -tm->tm_usec;
8234 lockhart@fourpalms.o 4443 : 0 : tm->tm_sec = -tm->tm_sec;
4444 : 0 : tm->tm_min = -tm->tm_min;
4445 : 0 : tm->tm_hour = -tm->tm_hour;
4446 : 0 : tm->tm_mday = -tm->tm_mday;
4447 : 0 : tm->tm_mon = -tm->tm_mon;
4448 : 0 : tm->tm_year = -tm->tm_year;
4449 : : }
4450 : :
4451 : : /* propagate any negative fields into the next higher field */
743 tgl@sss.pgh.pa.us 4452 [ # # ]: 0 : while (tm->tm_usec < 0)
4453 : : {
4454 : 0 : tm->tm_usec += USECS_PER_SEC;
6115 bruce@momjian.us 4455 : 0 : tm->tm_sec--;
4456 : : }
4457 : :
7074 tgl@sss.pgh.pa.us 4458 [ # # ]: 0 : while (tm->tm_sec < 0)
4459 : : {
6842 bruce@momjian.us 4460 : 0 : tm->tm_sec += SECS_PER_MINUTE;
8234 lockhart@fourpalms.o 4461 : 0 : tm->tm_min--;
4462 : : }
4463 : :
7074 tgl@sss.pgh.pa.us 4464 [ # # ]: 0 : while (tm->tm_min < 0)
4465 : : {
6842 bruce@momjian.us 4466 : 0 : tm->tm_min += MINS_PER_HOUR;
8234 lockhart@fourpalms.o 4467 : 0 : tm->tm_hour--;
4468 : : }
4469 : :
7074 tgl@sss.pgh.pa.us 4470 [ # # ]: 0 : while (tm->tm_hour < 0)
4471 : : {
6842 bruce@momjian.us 4472 : 0 : tm->tm_hour += HOURS_PER_DAY;
8234 lockhart@fourpalms.o 4473 : 0 : tm->tm_mday--;
4474 : : }
4475 : :
7074 tgl@sss.pgh.pa.us 4476 [ # # ]: 0 : while (tm->tm_mday < 0)
4477 : : {
8234 lockhart@fourpalms.o 4478 [ # # ]: 0 : if (dt1 < dt2)
4479 : : {
4480 [ # # # # : 0 : tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
# # ]
4481 : 0 : tm->tm_mon--;
4482 : : }
4483 : : else
4484 : : {
4485 [ # # # # : 0 : tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
# # ]
4486 : 0 : tm->tm_mon--;
4487 : : }
4488 : : }
4489 : :
7074 tgl@sss.pgh.pa.us 4490 [ # # ]: 0 : while (tm->tm_mon < 0)
4491 : : {
6842 bruce@momjian.us 4492 : 0 : tm->tm_mon += MONTHS_PER_YEAR;
8234 lockhart@fourpalms.o 4493 : 0 : tm->tm_year--;
4494 : : }
4495 : :
4496 : : /*
4497 : : * Note: we deliberately ignore any difference between tz1 and tz2.
4498 : : */
4499 : :
4500 : : /* recover sign if necessary... */
4501 [ # # ]: 0 : if (dt1 < dt2)
4502 : : {
743 tgl@sss.pgh.pa.us 4503 : 0 : tm->tm_usec = -tm->tm_usec;
8234 lockhart@fourpalms.o 4504 : 0 : tm->tm_sec = -tm->tm_sec;
4505 : 0 : tm->tm_min = -tm->tm_min;
4506 : 0 : tm->tm_hour = -tm->tm_hour;
4507 : 0 : tm->tm_mday = -tm->tm_mday;
4508 : 0 : tm->tm_mon = -tm->tm_mon;
4509 : 0 : tm->tm_year = -tm->tm_year;
4510 : : }
4511 : :
743 tgl@sss.pgh.pa.us 4512 [ # # ]: 0 : if (itm2interval(tm, result) != 0)
7567 4513 [ # # ]: 0 : ereport(ERROR,
4514 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4515 : : errmsg("interval out of range")));
4516 : : }
4517 : : else
4518 [ # # ]: 0 : ereport(ERROR,
4519 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4520 : : errmsg("timestamp out of range")));
4521 : :
8234 lockhart@fourpalms.o 4522 :GBC 12 : PG_RETURN_INTERVAL_P(result);
4523 : : }
4524 : :
4525 : :
4526 : : /*----------------------------------------------------------
4527 : : * Conversion operators.
4528 : : *---------------------------------------------------------*/
4529 : :
4530 : :
4531 : : /* timestamp_bin()
4532 : : * Bin timestamp into specified interval.
4533 : : */
4534 : : Datum
1117 peter@eisentraut.org 4535 :CBC 138 : timestamp_bin(PG_FUNCTION_ARGS)
4536 : : {
4537 : 138 : Interval *stride = PG_GETARG_INTERVAL_P(0);
4538 : 138 : Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
4539 : 138 : Timestamp origin = PG_GETARG_TIMESTAMP(2);
4540 : : Timestamp result,
4541 : : stride_usecs,
4542 : : tm_diff,
4543 : : tm_modulo,
4544 : : tm_delta;
4545 : :
4546 [ + - - + ]: 138 : if (TIMESTAMP_NOT_FINITE(timestamp))
1117 peter@eisentraut.org 4547 :UBC 0 : PG_RETURN_TIMESTAMP(timestamp);
4548 : :
1117 peter@eisentraut.org 4549 [ + - - + ]:CBC 138 : if (TIMESTAMP_NOT_FINITE(origin))
1117 peter@eisentraut.org 4550 [ # # ]:UBC 0 : ereport(ERROR,
4551 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4552 : : errmsg("origin out of range")));
4553 : :
152 dean.a.rasheed@gmail 4554 [ + + + - :GNC 138 : if (INTERVAL_NOT_FINITE(stride))
- + + + +
- + - ]
4555 [ + - ]: 6 : ereport(ERROR,
4556 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4557 : : errmsg("timestamps cannot be binned into infinite intervals")));
4558 : :
1117 peter@eisentraut.org 4559 [ + + ]:CBC 132 : if (stride->month != 0)
4560 [ + - ]: 6 : ereport(ERROR,
4561 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4562 : : errmsg("timestamps cannot be binned into intervals containing months or years")));
4563 : :
46 tgl@sss.pgh.pa.us 4564 [ + + ]: 126 : if (unlikely(pg_mul_s64_overflow(stride->day, USECS_PER_DAY, &stride_usecs)) ||
4565 [ - + ]: 123 : unlikely(pg_add_s64_overflow(stride_usecs, stride->time, &stride_usecs)))
4566 [ + - ]: 3 : ereport(ERROR,
4567 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4568 : : errmsg("interval out of range")));
4569 : :
991 john.naylor@postgres 4570 [ + + ]: 123 : if (stride_usecs <= 0)
997 4571 [ + - ]: 6 : ereport(ERROR,
4572 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4573 : : errmsg("stride must be greater than zero")));
4574 : :
46 tgl@sss.pgh.pa.us 4575 [ + + ]: 117 : if (unlikely(pg_sub_s64_overflow(timestamp, origin, &tm_diff)))
4576 [ + - ]: 3 : ereport(ERROR,
4577 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4578 : : errmsg("interval out of range")));
4579 : :
4580 : : /* These calculations cannot overflow */
4581 : 114 : tm_modulo = tm_diff % stride_usecs;
4582 : 114 : tm_delta = tm_diff - tm_modulo;
4583 : 114 : result = origin + tm_delta;
4584 : :
4585 : : /*
4586 : : * We want to round towards -infinity, not 0, when tm_diff is negative and
4587 : : * not a multiple of stride_usecs. This adjustment *can* cause overflow,
4588 : : * since the result might now be out of the range origin .. timestamp.
4589 : : */
4590 [ + + ]: 114 : if (tm_modulo < 0)
4591 : : {
4592 [ + - ]: 39 : if (unlikely(pg_sub_s64_overflow(result, stride_usecs, &result)) ||
4593 [ + + - + ]: 39 : !IS_VALID_TIMESTAMP(result))
4594 [ + - ]: 3 : ereport(ERROR,
4595 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4596 : : errmsg("timestamp out of range")));
4597 : : }
4598 : :
1117 peter@eisentraut.org 4599 : 111 : PG_RETURN_TIMESTAMP(result);
4600 : : }
4601 : :
4602 : : /* timestamp_trunc()
4603 : : * Truncate timestamp to specified units.
4604 : : */
4605 : : Datum
8710 tgl@sss.pgh.pa.us 4606 : 693 : timestamp_trunc(PG_FUNCTION_ARGS)
4607 : : {
5864 4608 : 693 : text *units = PG_GETARG_TEXT_PP(0);
6756 bruce@momjian.us 4609 : 693 : Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
4610 : : Timestamp result;
4611 : : int type,
4612 : : val;
4613 : : char *lowunits;
4614 : : fsec_t fsec;
4615 : : struct pg_tm tt,
8824 lockhart@fourpalms.o 4616 : 693 : *tm = &tt;
4617 : :
7282 tgl@sss.pgh.pa.us 4618 [ + - - + ]: 693 : if (TIMESTAMP_NOT_FINITE(timestamp))
7282 tgl@sss.pgh.pa.us 4619 :UBC 0 : PG_RETURN_TIMESTAMP(timestamp);
4620 : :
5864 tgl@sss.pgh.pa.us 4621 [ - + ]:CBC 693 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4622 [ - + - - : 693 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
4623 : : false);
4624 : :
8824 lockhart@fourpalms.o 4625 : 693 : type = DecodeUnits(0, lowunits, &val);
4626 : :
7567 tgl@sss.pgh.pa.us 4627 [ + - ]: 693 : if (type == UNITS)
4628 : : {
6842 bruce@momjian.us 4629 [ - + ]: 693 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
7567 tgl@sss.pgh.pa.us 4630 [ # # ]:UBC 0 : ereport(ERROR,
4631 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4632 : : errmsg("timestamp out of range")));
4633 : :
8234 lockhart@fourpalms.o 4634 [ + + + - :CBC 693 : switch (val)
- - - + +
+ + + +
- ]
4635 : : {
7345 bruce@momjian.us 4636 : 15 : case DTK_WEEK:
4637 : : {
4638 : : int woy;
4639 : :
6756 4640 : 15 : woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
4641 : :
4642 : : /*
4643 : : * If it is week 52/53 and the month is January, then the
4644 : : * week must belong to the previous year. Also, some
4645 : : * December dates belong to the next year.
4646 : : */
4647 [ - + - - ]: 15 : if (woy >= 52 && tm->tm_mon == 1)
6756 bruce@momjian.us 4648 :UBC 0 : --tm->tm_year;
6756 bruce@momjian.us 4649 [ - + - - ]:CBC 15 : if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
6756 bruce@momjian.us 4650 :UBC 0 : ++tm->tm_year;
6756 bruce@momjian.us 4651 :CBC 15 : isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
4652 : 15 : tm->tm_hour = 0;
4653 : 15 : tm->tm_min = 0;
4654 : 15 : tm->tm_sec = 0;
4655 : 15 : fsec = 0;
4656 : 15 : break;
4657 : : }
8234 lockhart@fourpalms.o 4658 : 3 : case DTK_MILLENNIUM:
4659 : : /* see comments in timestamptz_trunc */
7177 bruce@momjian.us 4660 [ + - ]: 3 : if (tm->tm_year > 0)
7168 4661 : 3 : tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
4662 : : else
7168 bruce@momjian.us 4663 :UBC 0 : tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
4664 : : /* FALL THRU */
4665 : : case DTK_CENTURY:
4666 : : /* see comments in timestamptz_trunc */
7177 bruce@momjian.us 4667 [ + - ]:CBC 6 : if (tm->tm_year > 0)
7168 4668 : 6 : tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
4669 : : else
7168 bruce@momjian.us 4670 :UBC 0 : tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
4671 : : /* FALL THRU */
4672 : : case DTK_DECADE:
4673 : : /* see comments in timestamptz_trunc */
7177 bruce@momjian.us 4674 [ + + - + ]:CBC 6 : if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
4675 : : {
7177 bruce@momjian.us 4676 [ # # ]:UBC 0 : if (tm->tm_year > 0)
4677 : 0 : tm->tm_year = (tm->tm_year / 10) * 10;
4678 : : else
7168 4679 : 0 : tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
4680 : : }
4681 : : /* FALL THRU */
4682 : : case DTK_YEAR:
8234 lockhart@fourpalms.o 4683 :CBC 6 : tm->tm_mon = 1;
4684 : : /* FALL THRU */
4685 : 6 : case DTK_QUARTER:
7568 bruce@momjian.us 4686 : 6 : tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
4687 : : /* FALL THRU */
8234 lockhart@fourpalms.o 4688 : 6 : case DTK_MONTH:
4689 : 6 : tm->tm_mday = 1;
4690 : : /* FALL THRU */
4691 : 618 : case DTK_DAY:
4692 : 618 : tm->tm_hour = 0;
4693 : : /* FALL THRU */
4694 : 630 : case DTK_HOUR:
4695 : 630 : tm->tm_min = 0;
4696 : : /* FALL THRU */
4697 : 642 : case DTK_MINUTE:
4698 : 642 : tm->tm_sec = 0;
4699 : : /* FALL THRU */
4700 : 654 : case DTK_SECOND:
4701 : 654 : fsec = 0;
4702 : 654 : break;
4703 : :
4704 : 12 : case DTK_MILLISEC:
6901 bruce@momjian.us 4705 : 12 : fsec = (fsec / 1000) * 1000;
8234 lockhart@fourpalms.o 4706 : 12 : break;
4707 : :
4708 : 12 : case DTK_MICROSEC:
4709 : 12 : break;
4710 : :
8234 lockhart@fourpalms.o 4711 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 4712 [ # # ]: 0 : ereport(ERROR,
4713 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4714 : : errmsg("unit \"%s\" not supported for type %s",
4715 : : lowunits, format_type_be(TIMESTAMPOID))));
4716 : : result = 0;
4717 : : }
4718 : :
8234 lockhart@fourpalms.o 4719 [ - + ]:CBC 693 : if (tm2timestamp(tm, fsec, NULL, &result) != 0)
7567 tgl@sss.pgh.pa.us 4720 [ # # ]:UBC 0 : ereport(ERROR,
4721 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4722 : : errmsg("timestamp out of range")));
4723 : : }
4724 : : else
4725 : : {
4726 [ # # ]: 0 : ereport(ERROR,
4727 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4728 : : errmsg("unit \"%s\" not recognized for type %s",
4729 : : lowunits, format_type_be(TIMESTAMPOID))));
4730 : : result = 0;
4731 : : }
4732 : :
8234 lockhart@fourpalms.o 4733 :CBC 693 : PG_RETURN_TIMESTAMP(result);
4734 : : }
4735 : :
4736 : : /* timestamptz_bin()
4737 : : * Bin timestamptz into specified interval using specified origin.
4738 : : */
4739 : : Datum
1117 peter@eisentraut.org 4740 : 66 : timestamptz_bin(PG_FUNCTION_ARGS)
4741 : : {
4742 : 66 : Interval *stride = PG_GETARG_INTERVAL_P(0);
4743 : 66 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
1104 4744 : 66 : TimestampTz origin = PG_GETARG_TIMESTAMPTZ(2);
4745 : : TimestampTz result,
4746 : : stride_usecs,
4747 : : tm_diff,
4748 : : tm_modulo,
4749 : : tm_delta;
4750 : :
1117 4751 [ + - - + ]: 66 : if (TIMESTAMP_NOT_FINITE(timestamp))
1117 peter@eisentraut.org 4752 :UBC 0 : PG_RETURN_TIMESTAMPTZ(timestamp);
4753 : :
1117 peter@eisentraut.org 4754 [ + - - + ]:CBC 66 : if (TIMESTAMP_NOT_FINITE(origin))
1117 peter@eisentraut.org 4755 [ # # ]:UBC 0 : ereport(ERROR,
4756 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4757 : : errmsg("origin out of range")));
4758 : :
152 dean.a.rasheed@gmail 4759 [ - + - - :GNC 66 : if (INTERVAL_NOT_FINITE(stride))
- - - + -
- - - ]
152 dean.a.rasheed@gmail 4760 [ # # ]:UNC 0 : ereport(ERROR,
4761 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4762 : : errmsg("timestamps cannot be binned into infinite intervals")));
4763 : :
1117 peter@eisentraut.org 4764 [ + + ]:CBC 66 : if (stride->month != 0)
4765 [ + - ]: 6 : ereport(ERROR,
4766 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4767 : : errmsg("timestamps cannot be binned into intervals containing months or years")));
4768 : :
46 tgl@sss.pgh.pa.us 4769 [ + + ]: 60 : if (unlikely(pg_mul_s64_overflow(stride->day, USECS_PER_DAY, &stride_usecs)) ||
4770 [ - + ]: 57 : unlikely(pg_add_s64_overflow(stride_usecs, stride->time, &stride_usecs)))
4771 [ + - ]: 3 : ereport(ERROR,
4772 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4773 : : errmsg("interval out of range")));
4774 : :
991 john.naylor@postgres 4775 [ + + ]: 57 : if (stride_usecs <= 0)
997 4776 [ + - ]: 6 : ereport(ERROR,
4777 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4778 : : errmsg("stride must be greater than zero")));
4779 : :
46 tgl@sss.pgh.pa.us 4780 [ + + ]: 51 : if (unlikely(pg_sub_s64_overflow(timestamp, origin, &tm_diff)))
4781 [ + - ]: 3 : ereport(ERROR,
4782 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4783 : : errmsg("interval out of range")));
4784 : :
4785 : : /* These calculations cannot overflow */
4786 : 48 : tm_modulo = tm_diff % stride_usecs;
4787 : 48 : tm_delta = tm_diff - tm_modulo;
4788 : 48 : result = origin + tm_delta;
4789 : :
4790 : : /*
4791 : : * We want to round towards -infinity, not 0, when tm_diff is negative and
4792 : : * not a multiple of stride_usecs. This adjustment *can* cause overflow,
4793 : : * since the result might now be out of the range origin .. timestamp.
4794 : : */
4795 [ + + ]: 48 : if (tm_modulo < 0)
4796 : : {
4797 [ + - ]: 3 : if (unlikely(pg_sub_s64_overflow(result, stride_usecs, &result)) ||
4798 [ - + - - ]: 3 : !IS_VALID_TIMESTAMP(result))
4799 [ + - ]: 3 : ereport(ERROR,
4800 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4801 : : errmsg("timestamp out of range")));
4802 : : }
4803 : :
1117 peter@eisentraut.org 4804 : 45 : PG_RETURN_TIMESTAMPTZ(result);
4805 : : }
4806 : :
4807 : : /*
4808 : : * Common code for timestamptz_trunc() and timestamptz_trunc_zone().
4809 : : *
4810 : : * tzp identifies the zone to truncate with respect to. We assume
4811 : : * infinite timestamps have already been rejected.
4812 : : */
4813 : : static TimestampTz
1978 tgl@sss.pgh.pa.us 4814 : 654 : timestamptz_trunc_internal(text *units, TimestampTz timestamp, pg_tz *tzp)
4815 : : {
4816 : : TimestampTz result;
4817 : : int tz;
4818 : : int type,
4819 : : val;
7104 4820 : 654 : bool redotz = false;
4821 : : char *lowunits;
4822 : : fsec_t fsec;
4823 : : struct pg_tm tt,
8234 lockhart@fourpalms.o 4824 : 654 : *tm = &tt;
4825 : :
5864 tgl@sss.pgh.pa.us 4826 [ - + ]: 654 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
4827 [ - + - - : 654 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
4828 : : false);
4829 : :
7282 4830 : 654 : type = DecodeUnits(0, lowunits, &val);
4831 : :
7567 4832 [ + - ]: 654 : if (type == UNITS)
4833 : : {
1978 4834 [ - + ]: 654 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, tzp) != 0)
7567 tgl@sss.pgh.pa.us 4835 [ # # ]:UBC 0 : ereport(ERROR,
4836 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4837 : : errmsg("timestamp out of range")));
4838 : :
8234 lockhart@fourpalms.o 4839 [ + + + + :CBC 654 : switch (val)
- - - + +
+ + + +
- ]
4840 : : {
7345 bruce@momjian.us 4841 : 3 : case DTK_WEEK:
4842 : : {
4843 : : int woy;
4844 : :
6756 4845 : 3 : woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
4846 : :
4847 : : /*
4848 : : * If it is week 52/53 and the month is January, then the
4849 : : * week must belong to the previous year. Also, some
4850 : : * December dates belong to the next year.
4851 : : */
4852 [ - + - - ]: 3 : if (woy >= 52 && tm->tm_mon == 1)
6756 bruce@momjian.us 4853 :UBC 0 : --tm->tm_year;
6756 bruce@momjian.us 4854 [ - + - - ]:CBC 3 : if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR)
6756 bruce@momjian.us 4855 :UBC 0 : ++tm->tm_year;
6756 bruce@momjian.us 4856 :CBC 3 : isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday));
4857 : 3 : tm->tm_hour = 0;
4858 : 3 : tm->tm_min = 0;
4859 : 3 : tm->tm_sec = 0;
4860 : 3 : fsec = 0;
4861 : 3 : redotz = true;
4862 : 3 : break;
4863 : : }
4864 : : /* one may consider DTK_THOUSAND and DTK_HUNDRED... */
8234 lockhart@fourpalms.o 4865 : 3 : case DTK_MILLENNIUM:
4866 : :
4867 : : /*
4868 : : * truncating to the millennium? what is this supposed to
4869 : : * mean? let us put the first year of the millennium... i.e.
4870 : : * -1000, 1, 1001, 2001...
4871 : : */
7177 bruce@momjian.us 4872 [ + - ]: 3 : if (tm->tm_year > 0)
7168 4873 : 3 : tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999;
4874 : : else
7168 bruce@momjian.us 4875 :UBC 0 : tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1;
4876 : : /* FALL THRU */
4877 : : case DTK_CENTURY:
4878 : : /* truncating to the century? as above: -100, 1, 101... */
7177 bruce@momjian.us 4879 [ + + ]:CBC 15 : if (tm->tm_year > 0)
7168 4880 : 12 : tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99;
4881 : : else
4882 : 3 : tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1;
4883 : : /* FALL THRU */
4884 : : case DTK_DECADE:
4885 : :
4886 : : /*
4887 : : * truncating to the decade? first year of the decade. must
4888 : : * not be applied if year was truncated before!
4889 : : */
7177 4890 [ + + + + ]: 24 : if (val != DTK_MILLENNIUM && val != DTK_CENTURY)
4891 : : {
4892 [ + + ]: 9 : if (tm->tm_year > 0)
4893 : 6 : tm->tm_year = (tm->tm_year / 10) * 10;
4894 : : else
7168 4895 : 3 : tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10;
4896 : : }
4897 : : /* FALL THRU */
4898 : : case DTK_YEAR:
8234 lockhart@fourpalms.o 4899 : 24 : tm->tm_mon = 1;
4900 : : /* FALL THRU */
4901 : 24 : case DTK_QUARTER:
7568 bruce@momjian.us 4902 : 24 : tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1;
4903 : : /* FALL THRU */
8234 lockhart@fourpalms.o 4904 : 24 : case DTK_MONTH:
4905 : 24 : tm->tm_mday = 1;
4906 : : /* FALL THRU */
4907 : 636 : case DTK_DAY:
4908 : 636 : tm->tm_hour = 0;
7104 tgl@sss.pgh.pa.us 4909 : 636 : redotz = true; /* for all cases >= DAY */
4910 : : /* FALL THRU */
8234 lockhart@fourpalms.o 4911 : 639 : case DTK_HOUR:
4912 : 639 : tm->tm_min = 0;
4913 : : /* FALL THRU */
4914 : 642 : case DTK_MINUTE:
4915 : 642 : tm->tm_sec = 0;
4916 : : /* FALL THRU */
4917 : 645 : case DTK_SECOND:
4918 : 645 : fsec = 0;
4919 : 645 : break;
4920 : 3 : case DTK_MILLISEC:
6841 bruce@momjian.us 4921 : 3 : fsec = (fsec / 1000) * 1000;
8234 lockhart@fourpalms.o 4922 : 3 : break;
4923 : 3 : case DTK_MICROSEC:
4924 : 3 : break;
4925 : :
8234 lockhart@fourpalms.o 4926 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 4927 [ # # ]: 0 : ereport(ERROR,
4928 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4929 : : errmsg("unit \"%s\" not supported for type %s",
4930 : : lowunits, format_type_be(TIMESTAMPTZOID))));
4931 : : result = 0;
4932 : : }
4933 : :
7104 tgl@sss.pgh.pa.us 4934 [ + + ]:CBC 654 : if (redotz)
1978 4935 : 639 : tz = DetermineTimeZoneOffset(tm, tzp);
4936 : :
8234 lockhart@fourpalms.o 4937 [ - + ]: 654 : if (tm2timestamp(tm, fsec, &tz, &result) != 0)
7567 tgl@sss.pgh.pa.us 4938 [ # # ]:UBC 0 : ereport(ERROR,
4939 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
4940 : : errmsg("timestamp out of range")));
4941 : : }
4942 : : else
4943 : : {
4944 [ # # ]: 0 : ereport(ERROR,
4945 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4946 : : errmsg("unit \"%s\" not recognized for type %s",
4947 : : lowunits, format_type_be(TIMESTAMPTZOID))));
4948 : : result = 0;
4949 : : }
4950 : :
1978 tgl@sss.pgh.pa.us 4951 :CBC 654 : return result;
4952 : : }
4953 : :
4954 : : /* timestamptz_trunc()
4955 : : * Truncate timestamptz to specified units in session timezone.
4956 : : */
4957 : : Datum
4958 : 627 : timestamptz_trunc(PG_FUNCTION_ARGS)
4959 : : {
4960 : 627 : text *units = PG_GETARG_TEXT_PP(0);
4961 : 627 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4962 : : TimestampTz result;
4963 : :
4964 [ + - - + ]: 627 : if (TIMESTAMP_NOT_FINITE(timestamp))
1978 tgl@sss.pgh.pa.us 4965 :UBC 0 : PG_RETURN_TIMESTAMPTZ(timestamp);
4966 : :
1978 tgl@sss.pgh.pa.us 4967 :CBC 627 : result = timestamptz_trunc_internal(units, timestamp, session_timezone);
4968 : :
4969 : 627 : PG_RETURN_TIMESTAMPTZ(result);
4970 : : }
4971 : :
4972 : : /* timestamptz_trunc_zone()
4973 : : * Truncate timestamptz to specified units in specified timezone.
4974 : : */
4975 : : Datum
4976 : 27 : timestamptz_trunc_zone(PG_FUNCTION_ARGS)
4977 : : {
4978 : 27 : text *units = PG_GETARG_TEXT_PP(0);
4979 : 27 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
4980 : 27 : text *zone = PG_GETARG_TEXT_PP(2);
4981 : : TimestampTz result;
4982 : : pg_tz *tzp;
4983 : :
4984 : : /*
4985 : : * timestamptz_zone() doesn't look up the zone for infinite inputs, so we
4986 : : * don't do so here either.
4987 : : */
4988 [ + - - + ]: 27 : if (TIMESTAMP_NOT_FINITE(timestamp))
1978 tgl@sss.pgh.pa.us 4989 :UBC 0 : PG_RETURN_TIMESTAMP(timestamp);
4990 : :
4991 : : /*
4992 : : * Look up the requested timezone.
4993 : : */
393 tgl@sss.pgh.pa.us 4994 :CBC 27 : tzp = lookup_timezone(zone);
4995 : :
1978 4996 : 27 : result = timestamptz_trunc_internal(units, timestamp, tzp);
4997 : :
8234 lockhart@fourpalms.o 4998 : 27 : PG_RETURN_TIMESTAMPTZ(result);
4999 : : }
5000 : :
5001 : : /* interval_trunc()
5002 : : * Extract specified field from interval.
5003 : : */
5004 : : Datum
8710 tgl@sss.pgh.pa.us 5005 :GBC 6 : interval_trunc(PG_FUNCTION_ARGS)
5006 : : {
5864 5007 : 6 : text *units = PG_GETARG_TEXT_PP(0);
8710 5008 : 6 : Interval *interval = PG_GETARG_INTERVAL_P(1);
5009 : : Interval *result;
5010 : : int type,
5011 : : val;
5012 : : char *lowunits;
5013 : : struct pg_itm tt,
8824 lockhart@fourpalms.o 5014 : 6 : *tm = &tt;
5015 : :
8710 tgl@sss.pgh.pa.us 5016 : 6 : result = (Interval *) palloc(sizeof(Interval));
5017 : :
152 dean.a.rasheed@gmail 5018 [ + + + - :GNC 6 : if (INTERVAL_NOT_FINITE(interval))
- + + - +
- + - ]
5019 : : {
5020 : 6 : memcpy(result, interval, sizeof(Interval));
5021 : 6 : PG_RETURN_INTERVAL_P(result);
5022 : : }
5023 : :
5864 tgl@sss.pgh.pa.us 5024 [ # # ]:UBC 0 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
5025 [ # # # # : 0 : VARSIZE_ANY_EXHDR(units),
# # # # #
# ]
5026 : : false);
5027 : :
8824 lockhart@fourpalms.o 5028 : 0 : type = DecodeUnits(0, lowunits, &val);
5029 : :
8234 5030 [ # # ]: 0 : if (type == UNITS)
5031 : : {
743 tgl@sss.pgh.pa.us 5032 : 0 : interval2itm(*interval, tm);
703 5033 [ # # # # : 0 : switch (val)
# # # # #
# # # # ]
5034 : : {
5035 : 0 : case DTK_MILLENNIUM:
5036 : : /* caution: C division may have negative remainder */
5037 : 0 : tm->tm_year = (tm->tm_year / 1000) * 1000;
5038 : : /* FALL THRU */
5039 : 0 : case DTK_CENTURY:
5040 : : /* caution: C division may have negative remainder */
5041 : 0 : tm->tm_year = (tm->tm_year / 100) * 100;
5042 : : /* FALL THRU */
5043 : 0 : case DTK_DECADE:
5044 : : /* caution: C division may have negative remainder */
5045 : 0 : tm->tm_year = (tm->tm_year / 10) * 10;
5046 : : /* FALL THRU */
5047 : 0 : case DTK_YEAR:
5048 : 0 : tm->tm_mon = 0;
5049 : : /* FALL THRU */
5050 : 0 : case DTK_QUARTER:
5051 : 0 : tm->tm_mon = 3 * (tm->tm_mon / 3);
5052 : : /* FALL THRU */
5053 : 0 : case DTK_MONTH:
5054 : 0 : tm->tm_mday = 0;
5055 : : /* FALL THRU */
5056 : 0 : case DTK_DAY:
5057 : 0 : tm->tm_hour = 0;
5058 : : /* FALL THRU */
5059 : 0 : case DTK_HOUR:
5060 : 0 : tm->tm_min = 0;
5061 : : /* FALL THRU */
5062 : 0 : case DTK_MINUTE:
5063 : 0 : tm->tm_sec = 0;
5064 : : /* FALL THRU */
5065 : 0 : case DTK_SECOND:
5066 : 0 : tm->tm_usec = 0;
5067 : 0 : break;
5068 : 0 : case DTK_MILLISEC:
5069 : 0 : tm->tm_usec = (tm->tm_usec / 1000) * 1000;
5070 : 0 : break;
5071 : 0 : case DTK_MICROSEC:
5072 : 0 : break;
5073 : :
5074 : 0 : default:
7567 5075 [ # # # # ]: 0 : ereport(ERROR,
5076 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5077 : : errmsg("unit \"%s\" not supported for type %s",
5078 : : lowunits, format_type_be(INTERVALOID)),
5079 : : (val == DTK_WEEK) ? errdetail("Months usually have fractional weeks.") : 0));
5080 : : }
5081 : :
703 5082 [ # # ]: 0 : if (itm2interval(tm, result) != 0)
5083 [ # # ]: 0 : ereport(ERROR,
5084 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5085 : : errmsg("interval out of range")));
5086 : : }
5087 : : else
5088 : : {
7567 5089 [ # # ]: 0 : ereport(ERROR,
5090 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5091 : : errmsg("unit \"%s\" not recognized for type %s",
5092 : : lowunits, format_type_be(INTERVALOID))));
5093 : : }
5094 : :
8710 5095 : 0 : PG_RETURN_INTERVAL_P(result);
5096 : : }
5097 : :
5098 : : /* isoweek2j()
5099 : : *
5100 : : * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week.
5101 : : * Julian days are used to convert between ISO week dates and Gregorian dates.
5102 : : */
5103 : : int
6267 bruce@momjian.us 5104 :CBC 795 : isoweek2j(int year, int week)
5105 : : {
5106 : : int day0,
5107 : : day4;
5108 : :
5109 : : /* fourth day of current year */
5110 : 795 : day4 = date2j(year, 1, 4);
5111 : :
5112 : : /* day0 == offset to first day of week (Monday) */
7681 tgl@sss.pgh.pa.us 5113 : 795 : day0 = j2day(day4 - 1);
5114 : :
6267 bruce@momjian.us 5115 : 795 : return ((week - 1) * 7) + (day4 - day0);
5116 : : }
5117 : :
5118 : : /* isoweek2date()
5119 : : * Convert ISO week of year number to date.
5120 : : * The year field must be specified with the ISO year!
5121 : : * karel 2000/08/07
5122 : : */
5123 : : void
5124 : 18 : isoweek2date(int woy, int *year, int *mon, int *mday)
5125 : : {
5126 : 18 : j2date(isoweek2j(*year, woy), year, mon, mday);
5127 : 18 : }
5128 : :
5129 : : /* isoweekdate2date()
5130 : : *
5131 : : * Convert an ISO 8601 week date (ISO year, ISO week) into a Gregorian date.
5132 : : * Gregorian day of week sent so weekday strings can be supplied.
5133 : : * Populates year, mon, and mday with the correct Gregorian values.
5134 : : * year must be passed in as the ISO year.
5135 : : */
5136 : : void
4241 5137 : 12 : isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday)
5138 : : {
5139 : : int jday;
5140 : :
6267 5141 : 12 : jday = isoweek2j(*year, isoweek);
5142 : : /* convert Gregorian week start (Sunday=1) to ISO week start (Monday=1) */
4241 5143 [ - + ]: 12 : if (wday > 1)
4241 bruce@momjian.us 5144 :UBC 0 : jday += wday - 2;
5145 : : else
4241 bruce@momjian.us 5146 :CBC 12 : jday += 6;
6267 5147 : 12 : j2date(jday, year, mon, mday);
8629 5148 : 12 : }
5149 : :
5150 : : /* date2isoweek()
5151 : : *
5152 : : * Returns ISO week number of year.
5153 : : */
5154 : : int
8424 5155 : 1212 : date2isoweek(int year, int mon, int mday)
5156 : : {
5157 : : float8 result;
5158 : : int day0,
5159 : : day4,
5160 : : dayn;
5161 : :
5162 : : /* current day */
8629 5163 : 1212 : dayn = date2j(year, mon, mday);
5164 : :
5165 : : /* fourth day of current year */
5166 : 1212 : day4 = date2j(year, 1, 4);
5167 : :
5168 : : /* day0 == offset to first day of week (Monday) */
7681 tgl@sss.pgh.pa.us 5169 : 1212 : day0 = j2day(day4 - 1);
5170 : :
5171 : : /*
5172 : : * We need the first week containing a Thursday, otherwise this day falls
5173 : : * into the previous year for purposes of counting weeks
5174 : : */
6901 bruce@momjian.us 5175 [ + + ]: 1212 : if (dayn < day4 - day0)
5176 : : {
7681 tgl@sss.pgh.pa.us 5177 : 18 : day4 = date2j(year - 1, 1, 4);
5178 : :
5179 : : /* day0 == offset to first day of week (Monday) */
5180 : 18 : day0 = j2day(day4 - 1);
5181 : : }
5182 : :
6901 bruce@momjian.us 5183 : 1212 : result = (dayn - (day4 - day0)) / 7 + 1;
5184 : :
5185 : : /*
5186 : : * Sometimes the last few days in a year will fall into the first week of
5187 : : * the next year, so check for this.
5188 : : */
6953 5189 [ + + ]: 1212 : if (result >= 52)
5190 : : {
7681 tgl@sss.pgh.pa.us 5191 : 135 : day4 = date2j(year + 1, 1, 4);
5192 : :
5193 : : /* day0 == offset to first day of week (Monday) */
5194 : 135 : day0 = j2day(day4 - 1);
5195 : :
6901 bruce@momjian.us 5196 [ + + ]: 135 : if (dayn >= day4 - day0)
5197 : 81 : result = (dayn - (day4 - day0)) / 7 + 1;
5198 : : }
5199 : :
8629 5200 : 1212 : return (int) result;
5201 : : }
5202 : :
5203 : :
5204 : : /* date2isoyear()
5205 : : *
5206 : : * Returns ISO 8601 year number.
5207 : : * Note: zero or negative results follow the year-zero-exists convention.
5208 : : */
5209 : : int
7416 5210 : 7293 : date2isoyear(int year, int mon, int mday)
5211 : : {
5212 : : float8 result;
5213 : : int day0,
5214 : : day4,
5215 : : dayn;
5216 : :
5217 : : /* current day */
5218 : 7293 : dayn = date2j(year, mon, mday);
5219 : :
5220 : : /* fourth day of current year */
5221 : 7293 : day4 = date2j(year, 1, 4);
5222 : :
5223 : : /* day0 == offset to first day of week (Monday) */
5224 : 7293 : day0 = j2day(day4 - 1);
5225 : :
5226 : : /*
5227 : : * We need the first week containing a Thursday, otherwise this day falls
5228 : : * into the previous year for purposes of counting weeks
5229 : : */
6901 5230 [ + + ]: 7293 : if (dayn < day4 - day0)
5231 : : {
7416 5232 : 114 : day4 = date2j(year - 1, 1, 4);
5233 : :
5234 : : /* day0 == offset to first day of week (Monday) */
5235 : 114 : day0 = j2day(day4 - 1);
5236 : :
5237 : 114 : year--;
5238 : : }
5239 : :
6901 5240 : 7293 : result = (dayn - (day4 - day0)) / 7 + 1;
5241 : :
5242 : : /*
5243 : : * Sometimes the last few days in a year will fall into the first week of
5244 : : * the next year, so check for this.
5245 : : */
6953 5246 [ + + ]: 7293 : if (result >= 52)
5247 : : {
7416 5248 : 855 : day4 = date2j(year + 1, 1, 4);
5249 : :
5250 : : /* day0 == offset to first day of week (Monday) */
5251 : 855 : day0 = j2day(day4 - 1);
5252 : :
6901 5253 [ + + ]: 855 : if (dayn >= day4 - day0)
7416 5254 : 513 : year++;
5255 : : }
5256 : :
5257 : 7293 : return year;
5258 : : }
5259 : :
5260 : :
5261 : : /* date2isoyearday()
5262 : : *
5263 : : * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day.
5264 : : * Possible return values are 1 through 371 (364 in non-leap years).
5265 : : */
5266 : : int
6267 5267 : 762 : date2isoyearday(int year, int mon, int mday)
5268 : : {
5269 : 762 : return date2j(year, mon, mday) - isoweek2j(date2isoyear(year, mon, mday), 1) + 1;
5270 : : }
5271 : :
5272 : : /*
5273 : : * NonFiniteTimestampTzPart
5274 : : *
5275 : : * Used by timestamp_part and timestamptz_part when extracting from infinite
5276 : : * timestamp[tz]. Returns +/-Infinity if that is the appropriate result,
5277 : : * otherwise returns zero (which should be taken as meaning to return NULL).
5278 : : *
5279 : : * Errors thrown here for invalid units should exactly match those that
5280 : : * would be thrown in the calling functions, else there will be unexpected
5281 : : * discrepancies between finite- and infinite-input cases.
5282 : : */
5283 : : static float8
3006 tgl@sss.pgh.pa.us 5284 : 306 : NonFiniteTimestampTzPart(int type, int unit, char *lowunits,
5285 : : bool isNegative, bool isTz)
5286 : : {
5287 [ + + - + ]: 306 : if ((type != UNITS) && (type != RESERV))
832 tgl@sss.pgh.pa.us 5288 [ # # # # ]:UBC 0 : ereport(ERROR,
5289 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5290 : : errmsg("unit \"%s\" not recognized for type %s",
5291 : : lowunits,
5292 : : format_type_be(isTz ? TIMESTAMPTZOID : TIMESTAMPOID))));
5293 : :
3006 tgl@sss.pgh.pa.us 5294 [ + + - ]:CBC 306 : switch (unit)
5295 : : {
5296 : : /* Oscillating units */
5297 : 198 : case DTK_MICROSEC:
5298 : : case DTK_MILLISEC:
5299 : : case DTK_SECOND:
5300 : : case DTK_MINUTE:
5301 : : case DTK_HOUR:
5302 : : case DTK_DAY:
5303 : : case DTK_MONTH:
5304 : : case DTK_QUARTER:
5305 : : case DTK_WEEK:
5306 : : case DTK_DOW:
5307 : : case DTK_ISODOW:
5308 : : case DTK_DOY:
5309 : : case DTK_TZ:
5310 : : case DTK_TZ_MINUTE:
5311 : : case DTK_TZ_HOUR:
5312 : 198 : return 0.0;
5313 : :
5314 : : /* Monotonically-increasing units */
5315 : 108 : case DTK_YEAR:
5316 : : case DTK_DECADE:
5317 : : case DTK_CENTURY:
5318 : : case DTK_MILLENNIUM:
5319 : : case DTK_JULIAN:
5320 : : case DTK_ISOYEAR:
5321 : : case DTK_EPOCH:
5322 [ + + ]: 108 : if (isNegative)
5323 : 54 : return -get_float8_infinity();
5324 : : else
5325 : 54 : return get_float8_infinity();
5326 : :
3006 tgl@sss.pgh.pa.us 5327 :UBC 0 : default:
832 5328 [ # # # # ]: 0 : ereport(ERROR,
5329 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5330 : : errmsg("unit \"%s\" not supported for type %s",
5331 : : lowunits,
5332 : : format_type_be(isTz ? TIMESTAMPTZOID : TIMESTAMPOID))));
5333 : : return 0.0; /* keep compiler quiet */
5334 : : }
5335 : : }
5336 : :
5337 : : /* timestamp_part() and extract_timestamp()
5338 : : * Extract specified field from timestamp.
5339 : : */
5340 : : static Datum
1104 peter@eisentraut.org 5341 :CBC 5361 : timestamp_part_common(PG_FUNCTION_ARGS, bool retnumeric)
5342 : : {
5864 tgl@sss.pgh.pa.us 5343 : 5361 : text *units = PG_GETARG_TEXT_PP(0);
6756 bruce@momjian.us 5344 : 5361 : Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
5345 : : int64 intresult;
5346 : : Timestamp epoch;
5347 : : int type,
5348 : : val;
5349 : : char *lowunits;
5350 : : fsec_t fsec;
5351 : : struct pg_tm tt,
8824 lockhart@fourpalms.o 5352 : 5361 : *tm = &tt;
5353 : :
5864 tgl@sss.pgh.pa.us 5354 [ - + ]: 5361 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
5355 [ - + - - : 5361 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
5356 : : false);
5357 : :
7282 5358 : 5361 : type = DecodeUnits(0, lowunits, &val);
5359 [ + + ]: 5361 : if (type == UNKNOWN_FIELD)
5360 : 1857 : type = DecodeSpecial(0, lowunits, &val);
5361 : :
3006 5362 [ + + + + ]: 5361 : if (TIMESTAMP_NOT_FINITE(timestamp))
5363 : : {
1104 peter@eisentraut.org 5364 : 144 : double r = NonFiniteTimestampTzPart(type, val, lowunits,
5365 : : TIMESTAMP_IS_NOBEGIN(timestamp),
5366 : : false);
5367 : :
152 dean.a.rasheed@gmail 5368 [ + + ]:GNC 144 : if (r != 0.0)
5369 : : {
1104 peter@eisentraut.org 5370 [ + + ]:CBC 54 : if (retnumeric)
5371 : : {
5372 [ + + ]: 12 : if (r < 0)
5373 : 6 : return DirectFunctionCall3(numeric_in,
5374 : : CStringGetDatum("-Infinity"),
5375 : : ObjectIdGetDatum(InvalidOid),
5376 : : Int32GetDatum(-1));
5377 [ + - ]: 6 : else if (r > 0)
5378 : 6 : return DirectFunctionCall3(numeric_in,
5379 : : CStringGetDatum("Infinity"),
5380 : : ObjectIdGetDatum(InvalidOid),
5381 : : Int32GetDatum(-1));
5382 : : }
5383 : : else
5384 : 42 : PG_RETURN_FLOAT8(r);
5385 : : }
5386 : : else
3006 tgl@sss.pgh.pa.us 5387 : 90 : PG_RETURN_NULL();
5388 : : }
5389 : :
7567 5390 [ + + ]: 5217 : if (type == UNITS)
5391 : : {
6842 bruce@momjian.us 5392 [ - + ]: 4782 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
7567 tgl@sss.pgh.pa.us 5393 [ # # ]:UBC 0 : ereport(ERROR,
5394 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5395 : : errmsg("timestamp out of range")));
5396 : :
8234 lockhart@fourpalms.o 5397 [ + + + + :CBC 4782 : switch (val)
+ + + + +
+ + + + +
+ + + - ]
5398 : : {
5399 : 378 : case DTK_MICROSEC:
1099 tgl@sss.pgh.pa.us 5400 : 378 : intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
8234 lockhart@fourpalms.o 5401 : 378 : break;
5402 : :
5403 : 378 : case DTK_MILLISEC:
1104 peter@eisentraut.org 5404 [ + + ]: 378 : if (retnumeric)
5405 : : /*---
5406 : : * tm->tm_sec * 1000 + fsec / 1000
5407 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
5408 : : */
1099 tgl@sss.pgh.pa.us 5409 : 189 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
5410 : : else
1104 peter@eisentraut.org 5411 : 189 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
5412 : : break;
5413 : :
8234 lockhart@fourpalms.o 5414 : 378 : case DTK_SECOND:
1104 peter@eisentraut.org 5415 [ + + ]: 378 : if (retnumeric)
5416 : : /*---
5417 : : * tm->tm_sec + fsec / 1'000'000
5418 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
5419 : : */
1099 tgl@sss.pgh.pa.us 5420 : 189 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
5421 : : else
1104 peter@eisentraut.org 5422 : 189 : PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
5423 : : break;
5424 : :
8234 lockhart@fourpalms.o 5425 : 189 : case DTK_MINUTE:
1104 peter@eisentraut.org 5426 : 189 : intresult = tm->tm_min;
8234 lockhart@fourpalms.o 5427 : 189 : break;
5428 : :
5429 : 189 : case DTK_HOUR:
1104 peter@eisentraut.org 5430 : 189 : intresult = tm->tm_hour;
8234 lockhart@fourpalms.o 5431 : 189 : break;
5432 : :
5433 : 237 : case DTK_DAY:
1104 peter@eisentraut.org 5434 : 237 : intresult = tm->tm_mday;
8234 lockhart@fourpalms.o 5435 : 237 : break;
5436 : :
5437 : 237 : case DTK_MONTH:
1104 peter@eisentraut.org 5438 : 237 : intresult = tm->tm_mon;
8234 lockhart@fourpalms.o 5439 : 237 : break;
5440 : :
5441 : 237 : case DTK_QUARTER:
1104 peter@eisentraut.org 5442 : 237 : intresult = (tm->tm_mon - 1) / 3 + 1;
8234 lockhart@fourpalms.o 5443 : 237 : break;
5444 : :
5445 : 237 : case DTK_WEEK:
1104 peter@eisentraut.org 5446 : 237 : intresult = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
8234 lockhart@fourpalms.o 5447 : 237 : break;
5448 : :
5449 : 237 : case DTK_YEAR:
7320 bruce@momjian.us 5450 [ + + ]: 237 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5451 : 231 : intresult = tm->tm_year;
5452 : : else
5453 : : /* there is no year 0, just 1 BC and 1 AD */
5454 : 6 : intresult = tm->tm_year - 1;
8234 lockhart@fourpalms.o 5455 : 237 : break;
5456 : :
5457 : 237 : case DTK_DECADE:
5458 : :
5459 : : /*
5460 : : * what is a decade wrt dates? let us assume that decade 199
5461 : : * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1
5462 : : * is 11 BC thru 2 BC...
5463 : : */
7168 bruce@momjian.us 5464 [ + + ]: 237 : if (tm->tm_year >= 0)
1104 peter@eisentraut.org 5465 : 231 : intresult = tm->tm_year / 10;
5466 : : else
5467 : 6 : intresult = -((8 - (tm->tm_year - 1)) / 10);
8234 lockhart@fourpalms.o 5468 : 237 : break;
5469 : :
5470 : 237 : case DTK_CENTURY:
5471 : :
5472 : : /* ----
5473 : : * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ]
5474 : : * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1]
5475 : : * there is no number 0 century.
5476 : : * ----
5477 : : */
7309 bruce@momjian.us 5478 [ + + ]: 237 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5479 : 231 : intresult = (tm->tm_year + 99) / 100;
5480 : : else
5481 : : /* caution: C division may have negative remainder */
5482 : 6 : intresult = -((99 - (tm->tm_year - 1)) / 100);
8234 lockhart@fourpalms.o 5483 : 237 : break;
5484 : :
5485 : 237 : case DTK_MILLENNIUM:
5486 : : /* see comments above. */
7309 bruce@momjian.us 5487 [ + + ]: 237 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5488 : 231 : intresult = (tm->tm_year + 999) / 1000;
5489 : : else
5490 : 6 : intresult = -((999 - (tm->tm_year - 1)) / 1000);
8234 lockhart@fourpalms.o 5491 : 237 : break;
5492 : :
8142 5493 : 426 : case DTK_JULIAN:
1104 peter@eisentraut.org 5494 [ + + ]: 426 : if (retnumeric)
5495 : 189 : PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
5496 : : numeric_div_opt_error(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * INT64CONST(1000000) + fsec),
5497 : : int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
5498 : : NULL),
5499 : : NULL));
5500 : : else
5501 : 237 : PG_RETURN_FLOAT8(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) +
5502 : : ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
5503 : : tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY);
5504 : : break;
5505 : :
6267 bruce@momjian.us 5506 : 237 : case DTK_ISOYEAR:
1104 peter@eisentraut.org 5507 : 237 : intresult = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday);
5508 : : /* Adjust BC years */
5509 [ + + ]: 237 : if (intresult <= 0)
5510 : 6 : intresult -= 1;
6267 bruce@momjian.us 5511 : 237 : break;
5512 : :
3143 stark@mit.edu 5513 : 474 : case DTK_DOW:
5514 : : case DTK_ISODOW:
1104 peter@eisentraut.org 5515 : 474 : intresult = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
5516 [ + + + + ]: 474 : if (val == DTK_ISODOW && intresult == 0)
5517 : 15 : intresult = 7;
3143 stark@mit.edu 5518 : 474 : break;
5519 : :
5520 : 237 : case DTK_DOY:
1104 peter@eisentraut.org 5521 : 237 : intresult = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
5522 : 237 : - date2j(tm->tm_year, 1, 1) + 1);
3143 stark@mit.edu 5523 : 237 : break;
5524 : :
8229 lockhart@fourpalms.o 5525 :UBC 0 : case DTK_TZ:
5526 : : case DTK_TZ_MINUTE:
5527 : : case DTK_TZ_HOUR:
5528 : : default:
7567 tgl@sss.pgh.pa.us 5529 [ # # ]: 0 : ereport(ERROR,
5530 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5531 : : errmsg("unit \"%s\" not supported for type %s",
5532 : : lowunits, format_type_be(TIMESTAMPOID))));
5533 : : intresult = 0;
5534 : : }
5535 : : }
8234 lockhart@fourpalms.o 5536 [ + - ]:CBC 435 : else if (type == RESERV)
5537 : : {
5538 [ + - ]: 435 : switch (val)
5539 : : {
5540 : 435 : case DTK_EPOCH:
2951 tgl@sss.pgh.pa.us 5541 : 435 : epoch = SetEpochTimestamp();
5542 : : /* (timestamp - epoch) / 1000000 */
1104 peter@eisentraut.org 5543 [ + + ]: 435 : if (retnumeric)
5544 : : {
5545 : : Numeric result;
5546 : :
5547 [ + + ]: 195 : if (timestamp < (PG_INT64_MAX + epoch))
5548 : 192 : result = int64_div_fast_to_numeric(timestamp - epoch, 6);
5549 : : else
5550 : : {
5551 : 3 : result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp),
5552 : : int64_to_numeric(epoch),
5553 : : NULL),
5554 : : int64_to_numeric(1000000),
5555 : : NULL);
5556 : 3 : result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
5557 : : NumericGetDatum(result),
5558 : : Int32GetDatum(6)));
5559 : : }
5560 : 195 : PG_RETURN_NUMERIC(result);
5561 : : }
5562 : : else
5563 : : {
5564 : : float8 result;
5565 : :
5566 : : /* try to avoid precision loss in subtraction */
5567 [ + + ]: 240 : if (timestamp < (PG_INT64_MAX + epoch))
5568 : 237 : result = (timestamp - epoch) / 1000000.0;
5569 : : else
5570 : 3 : result = ((float8) timestamp - epoch) / 1000000.0;
5571 : 240 : PG_RETURN_FLOAT8(result);
5572 : : }
5573 : : break;
5574 : :
8234 lockhart@fourpalms.o 5575 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 5576 [ # # ]: 0 : ereport(ERROR,
5577 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5578 : : errmsg("unit \"%s\" not supported for type %s",
5579 : : lowunits, format_type_be(TIMESTAMPOID))));
5580 : : intresult = 0;
5581 : : }
5582 : : }
5583 : : else
5584 : : {
5585 [ # # ]: 0 : ereport(ERROR,
5586 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5587 : : errmsg("unit \"%s\" not recognized for type %s",
5588 : : lowunits, format_type_be(TIMESTAMPOID))));
5589 : : intresult = 0;
5590 : : }
5591 : :
1104 peter@eisentraut.org 5592 [ + + ]:CBC 3600 : if (retnumeric)
5593 : 189 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
5594 : : else
5595 : 3411 : PG_RETURN_FLOAT8(intresult);
5596 : : }
5597 : :
5598 : : Datum
5599 : 4380 : timestamp_part(PG_FUNCTION_ARGS)
5600 : : {
5601 : 4380 : return timestamp_part_common(fcinfo, false);
5602 : : }
5603 : :
5604 : : Datum
5605 : 981 : extract_timestamp(PG_FUNCTION_ARGS)
5606 : : {
5607 : 981 : return timestamp_part_common(fcinfo, true);
5608 : : }
5609 : :
5610 : : /* timestamptz_part() and extract_timestamptz()
5611 : : * Extract specified field from timestamp with time zone.
5612 : : */
5613 : : static Datum
5614 : 18726 : timestamptz_part_common(PG_FUNCTION_ARGS, bool retnumeric)
5615 : : {
5864 tgl@sss.pgh.pa.us 5616 : 18726 : text *units = PG_GETARG_TEXT_PP(0);
7643 5617 : 18726 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
5618 : : int64 intresult;
5619 : : Timestamp epoch;
5620 : : int tz;
5621 : : int type,
5622 : : val;
5623 : : char *lowunits;
5624 : : fsec_t fsec;
5625 : : struct pg_tm tt,
8234 lockhart@fourpalms.o 5626 : 18726 : *tm = &tt;
5627 : :
5864 tgl@sss.pgh.pa.us 5628 [ - + ]: 18726 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
5629 [ - + - - : 18726 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
5630 : : false);
5631 : :
7282 5632 : 18726 : type = DecodeUnits(0, lowunits, &val);
5633 [ + + ]: 18726 : if (type == UNKNOWN_FIELD)
5634 : 14964 : type = DecodeSpecial(0, lowunits, &val);
5635 : :
3006 5636 [ + + + + ]: 18726 : if (TIMESTAMP_NOT_FINITE(timestamp))
5637 : : {
1104 peter@eisentraut.org 5638 : 162 : double r = NonFiniteTimestampTzPart(type, val, lowunits,
5639 : : TIMESTAMP_IS_NOBEGIN(timestamp),
5640 : : true);
5641 : :
152 dean.a.rasheed@gmail 5642 [ + + ]:GNC 162 : if (r != 0.0)
5643 : : {
1104 peter@eisentraut.org 5644 [ + + ]:CBC 54 : if (retnumeric)
5645 : : {
5646 [ + + ]: 12 : if (r < 0)
5647 : 6 : return DirectFunctionCall3(numeric_in,
5648 : : CStringGetDatum("-Infinity"),
5649 : : ObjectIdGetDatum(InvalidOid),
5650 : : Int32GetDatum(-1));
5651 [ + - ]: 6 : else if (r > 0)
5652 : 6 : return DirectFunctionCall3(numeric_in,
5653 : : CStringGetDatum("Infinity"),
5654 : : ObjectIdGetDatum(InvalidOid),
5655 : : Int32GetDatum(-1));
5656 : : }
5657 : : else
5658 : 42 : PG_RETURN_FLOAT8(r);
5659 : : }
5660 : : else
3006 tgl@sss.pgh.pa.us 5661 : 108 : PG_RETURN_NULL();
5662 : : }
5663 : :
7567 5664 [ + + ]: 18564 : if (type == UNITS)
5665 : : {
4413 peter_e@gmx.net 5666 [ - + ]: 4830 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
7567 tgl@sss.pgh.pa.us 5667 [ # # ]:UBC 0 : ereport(ERROR,
5668 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
5669 : : errmsg("timestamp out of range")));
5670 : :
8234 lockhart@fourpalms.o 5671 [ + + + + :CBC 4830 : switch (val)
+ + + + +
+ + + + +
+ + + + +
+ - ]
5672 : : {
5673 : 192 : case DTK_TZ:
1104 peter@eisentraut.org 5674 : 192 : intresult = -tz;
8234 lockhart@fourpalms.o 5675 : 192 : break;
5676 : :
5677 : 192 : case DTK_TZ_MINUTE:
1104 peter@eisentraut.org 5678 : 192 : intresult = (-tz / SECS_PER_MINUTE) % MINS_PER_HOUR;
8234 lockhart@fourpalms.o 5679 : 192 : break;
5680 : :
5681 : 192 : case DTK_TZ_HOUR:
1104 peter@eisentraut.org 5682 : 192 : intresult = -tz / SECS_PER_HOUR;
8234 lockhart@fourpalms.o 5683 : 192 : break;
5684 : :
5685 : 384 : case DTK_MICROSEC:
1099 tgl@sss.pgh.pa.us 5686 : 384 : intresult = tm->tm_sec * INT64CONST(1000000) + fsec;
8234 lockhart@fourpalms.o 5687 : 384 : break;
5688 : :
5689 : 384 : case DTK_MILLISEC:
1104 peter@eisentraut.org 5690 [ + + ]: 384 : if (retnumeric)
5691 : : /*---
5692 : : * tm->tm_sec * 1000 + fsec / 1000
5693 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
5694 : : */
1099 tgl@sss.pgh.pa.us 5695 : 192 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 3));
5696 : : else
1104 peter@eisentraut.org 5697 : 192 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + fsec / 1000.0);
5698 : : break;
5699 : :
8234 lockhart@fourpalms.o 5700 : 384 : case DTK_SECOND:
1104 peter@eisentraut.org 5701 [ + + ]: 384 : if (retnumeric)
5702 : : /*---
5703 : : * tm->tm_sec + fsec / 1'000'000
5704 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
5705 : : */
1099 tgl@sss.pgh.pa.us 5706 : 192 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + fsec, 6));
5707 : : else
1104 peter@eisentraut.org 5708 : 192 : PG_RETURN_FLOAT8(tm->tm_sec + fsec / 1000000.0);
5709 : : break;
5710 : :
8234 lockhart@fourpalms.o 5711 : 192 : case DTK_MINUTE:
1104 peter@eisentraut.org 5712 : 192 : intresult = tm->tm_min;
8234 lockhart@fourpalms.o 5713 : 192 : break;
5714 : :
5715 : 192 : case DTK_HOUR:
1104 peter@eisentraut.org 5716 : 192 : intresult = tm->tm_hour;
8234 lockhart@fourpalms.o 5717 : 192 : break;
5718 : :
5719 : 192 : case DTK_DAY:
1104 peter@eisentraut.org 5720 : 192 : intresult = tm->tm_mday;
8234 lockhart@fourpalms.o 5721 : 192 : break;
5722 : :
5723 : 192 : case DTK_MONTH:
1104 peter@eisentraut.org 5724 : 192 : intresult = tm->tm_mon;
8234 lockhart@fourpalms.o 5725 : 192 : break;
5726 : :
5727 : 192 : case DTK_QUARTER:
1104 peter@eisentraut.org 5728 : 192 : intresult = (tm->tm_mon - 1) / 3 + 1;
8234 lockhart@fourpalms.o 5729 : 192 : break;
5730 : :
5731 : 192 : case DTK_WEEK:
1104 peter@eisentraut.org 5732 : 192 : intresult = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday);
8234 lockhart@fourpalms.o 5733 : 192 : break;
5734 : :
5735 : 192 : case DTK_YEAR:
7085 tgl@sss.pgh.pa.us 5736 [ + + ]: 192 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5737 : 189 : intresult = tm->tm_year;
5738 : : else
5739 : : /* there is no year 0, just 1 BC and 1 AD */
5740 : 3 : intresult = tm->tm_year - 1;
8234 lockhart@fourpalms.o 5741 : 192 : break;
5742 : :
5743 : 192 : case DTK_DECADE:
5744 : : /* see comments in timestamp_part */
7168 bruce@momjian.us 5745 [ + + ]: 192 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5746 : 189 : intresult = tm->tm_year / 10;
5747 : : else
5748 : 3 : intresult = -((8 - (tm->tm_year - 1)) / 10);
8234 lockhart@fourpalms.o 5749 : 192 : break;
5750 : :
5751 : 192 : case DTK_CENTURY:
5752 : : /* see comments in timestamp_part */
7177 bruce@momjian.us 5753 [ + + ]: 192 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5754 : 189 : intresult = (tm->tm_year + 99) / 100;
5755 : : else
5756 : 3 : intresult = -((99 - (tm->tm_year - 1)) / 100);
8234 lockhart@fourpalms.o 5757 : 192 : break;
5758 : :
5759 : 192 : case DTK_MILLENNIUM:
5760 : : /* see comments in timestamp_part */
7177 bruce@momjian.us 5761 [ + + ]: 192 : if (tm->tm_year > 0)
1104 peter@eisentraut.org 5762 : 189 : intresult = (tm->tm_year + 999) / 1000;
5763 : : else
5764 : 3 : intresult = -((999 - (tm->tm_year - 1)) / 1000);
8234 lockhart@fourpalms.o 5765 : 192 : break;
5766 : :
8142 5767 : 384 : case DTK_JULIAN:
1104 peter@eisentraut.org 5768 [ + + ]: 384 : if (retnumeric)
5769 : 192 : PG_RETURN_NUMERIC(numeric_add_opt_error(int64_to_numeric(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)),
5770 : : numeric_div_opt_error(int64_to_numeric(((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + tm->tm_sec) * INT64CONST(1000000) + fsec),
5771 : : int64_to_numeric(SECS_PER_DAY * INT64CONST(1000000)),
5772 : : NULL),
5773 : : NULL));
5774 : : else
5775 : 192 : PG_RETURN_FLOAT8(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) +
5776 : : ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) +
5777 : : tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY);
5778 : : break;
5779 : :
6267 bruce@momjian.us 5780 : 192 : case DTK_ISOYEAR:
1104 peter@eisentraut.org 5781 : 192 : intresult = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday);
5782 : : /* Adjust BC years */
5783 [ + + ]: 192 : if (intresult <= 0)
5784 : 3 : intresult -= 1;
6267 bruce@momjian.us 5785 : 192 : break;
5786 : :
3143 stark@mit.edu 5787 : 414 : case DTK_DOW:
5788 : : case DTK_ISODOW:
1104 peter@eisentraut.org 5789 : 414 : intresult = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday));
5790 [ + + + + ]: 414 : if (val == DTK_ISODOW && intresult == 0)
5791 : 9 : intresult = 7;
3143 stark@mit.edu 5792 : 414 : break;
5793 : :
5794 : 192 : case DTK_DOY:
1104 peter@eisentraut.org 5795 : 192 : intresult = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)
5796 : 192 : - date2j(tm->tm_year, 1, 1) + 1);
3143 stark@mit.edu 5797 : 192 : break;
5798 : :
8234 lockhart@fourpalms.o 5799 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 5800 [ # # ]: 0 : ereport(ERROR,
5801 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5802 : : errmsg("unit \"%s\" not supported for type %s",
5803 : : lowunits, format_type_be(TIMESTAMPTZOID))));
5804 : : intresult = 0;
5805 : : }
5806 : : }
8234 lockhart@fourpalms.o 5807 [ + - ]:CBC 13734 : else if (type == RESERV)
5808 : : {
5809 [ + - ]: 13734 : switch (val)
5810 : : {
5811 : 13734 : case DTK_EPOCH:
2951 tgl@sss.pgh.pa.us 5812 : 13734 : epoch = SetEpochTimestamp();
5813 : : /* (timestamp - epoch) / 1000000 */
1104 peter@eisentraut.org 5814 [ + + ]: 13734 : if (retnumeric)
5815 : : {
5816 : : Numeric result;
5817 : :
5818 [ + + ]: 13539 : if (timestamp < (PG_INT64_MAX + epoch))
5819 : 13536 : result = int64_div_fast_to_numeric(timestamp - epoch, 6);
5820 : : else
5821 : : {
5822 : 3 : result = numeric_div_opt_error(numeric_sub_opt_error(int64_to_numeric(timestamp),
5823 : : int64_to_numeric(epoch),
5824 : : NULL),
5825 : : int64_to_numeric(1000000),
5826 : : NULL);
5827 : 3 : result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
5828 : : NumericGetDatum(result),
5829 : : Int32GetDatum(6)));
5830 : : }
5831 : 13539 : PG_RETURN_NUMERIC(result);
5832 : : }
5833 : : else
5834 : : {
5835 : : float8 result;
5836 : :
5837 : : /* try to avoid precision loss in subtraction */
5838 [ + + ]: 195 : if (timestamp < (PG_INT64_MAX + epoch))
5839 : 192 : result = (timestamp - epoch) / 1000000.0;
5840 : : else
5841 : 3 : result = ((float8) timestamp - epoch) / 1000000.0;
5842 : 195 : PG_RETURN_FLOAT8(result);
5843 : : }
5844 : : break;
5845 : :
8234 lockhart@fourpalms.o 5846 :UBC 0 : default:
7567 tgl@sss.pgh.pa.us 5847 [ # # ]: 0 : ereport(ERROR,
5848 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5849 : : errmsg("unit \"%s\" not supported for type %s",
5850 : : lowunits, format_type_be(TIMESTAMPTZOID))));
5851 : : intresult = 0;
5852 : : }
5853 : : }
5854 : : else
5855 : : {
5856 [ # # ]: 0 : ereport(ERROR,
5857 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5858 : : errmsg("unit \"%s\" not recognized for type %s",
5859 : : lowunits, format_type_be(TIMESTAMPTZOID))));
5860 : :
5861 : : intresult = 0;
5862 : : }
5863 : :
1104 peter@eisentraut.org 5864 [ + + ]:CBC 3678 : if (retnumeric)
5865 : 222 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
5866 : : else
5867 : 3456 : PG_RETURN_FLOAT8(intresult);
5868 : : }
5869 : :
5870 : : Datum
5871 : 4359 : timestamptz_part(PG_FUNCTION_ARGS)
5872 : : {
5873 : 4359 : return timestamptz_part_common(fcinfo, false);
5874 : : }
5875 : :
5876 : : Datum
5877 : 14367 : extract_timestamptz(PG_FUNCTION_ARGS)
5878 : : {
5879 : 14367 : return timestamptz_part_common(fcinfo, true);
5880 : : }
5881 : :
5882 : : /*
5883 : : * NonFiniteIntervalPart
5884 : : *
5885 : : * Used by interval_part when extracting from infinite interval. Returns
5886 : : * +/-Infinity if that is the appropriate result, otherwise returns zero
5887 : : * (which should be taken as meaning to return NULL).
5888 : : *
5889 : : * Errors thrown here for invalid units should exactly match those that
5890 : : * would be thrown in the calling functions, else there will be unexpected
5891 : : * discrepancies between finite- and infinite-input cases.
5892 : : */
5893 : : static float8
152 dean.a.rasheed@gmail 5894 :GNC 102 : NonFiniteIntervalPart(int type, int unit, char *lowunits, bool isNegative)
5895 : : {
5896 [ + + - + ]: 102 : if ((type != UNITS) && (type != RESERV))
152 dean.a.rasheed@gmail 5897 [ # # ]:UNC 0 : ereport(ERROR,
5898 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5899 : : errmsg("unit \"%s\" not recognized for type %s",
5900 : : lowunits, format_type_be(INTERVALOID))));
5901 : :
152 dean.a.rasheed@gmail 5902 [ + + - ]:GNC 102 : switch (unit)
5903 : : {
5904 : : /* Oscillating units */
5905 : 54 : case DTK_MICROSEC:
5906 : : case DTK_MILLISEC:
5907 : : case DTK_SECOND:
5908 : : case DTK_MINUTE:
5909 : : case DTK_MONTH:
5910 : : case DTK_QUARTER:
5911 : 54 : return 0.0;
5912 : :
5913 : : /* Monotonically-increasing units */
5914 : 48 : case DTK_HOUR:
5915 : : case DTK_DAY:
5916 : : case DTK_YEAR:
5917 : : case DTK_DECADE:
5918 : : case DTK_CENTURY:
5919 : : case DTK_MILLENNIUM:
5920 : : case DTK_EPOCH:
5921 [ + + ]: 48 : if (isNegative)
5922 : 24 : return -get_float8_infinity();
5923 : : else
5924 : 24 : return get_float8_infinity();
5925 : :
152 dean.a.rasheed@gmail 5926 :UNC 0 : default:
5927 [ # # ]: 0 : ereport(ERROR,
5928 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5929 : : errmsg("unit \"%s\" not supported for type %s",
5930 : : lowunits, format_type_be(INTERVALOID))));
5931 : : return 0.0; /* keep compiler quiet */
5932 : : }
5933 : : }
5934 : :
5935 : : /* interval_part() and extract_interval()
5936 : : * Extract specified field from interval.
5937 : : */
5938 : : static Datum
1104 peter@eisentraut.org 5939 :CBC 648 : interval_part_common(PG_FUNCTION_ARGS, bool retnumeric)
5940 : : {
5864 tgl@sss.pgh.pa.us 5941 : 648 : text *units = PG_GETARG_TEXT_PP(0);
8710 5942 : 648 : Interval *interval = PG_GETARG_INTERVAL_P(1);
5943 : : int64 intresult;
5944 : : int type,
5945 : : val;
5946 : : char *lowunits;
5947 : : struct pg_itm tt,
8824 lockhart@fourpalms.o 5948 : 648 : *tm = &tt;
5949 : :
5864 tgl@sss.pgh.pa.us 5950 [ - + ]: 648 : lowunits = downcase_truncate_identifier(VARDATA_ANY(units),
5951 [ - + - - : 648 : VARSIZE_ANY_EXHDR(units),
- - - - -
+ ]
5952 : : false);
5953 : :
8824 lockhart@fourpalms.o 5954 : 648 : type = DecodeUnits(0, lowunits, &val);
8214 5955 [ + + ]: 648 : if (type == UNKNOWN_FIELD)
8824 5956 : 81 : type = DecodeSpecial(0, lowunits, &val);
5957 : :
152 dean.a.rasheed@gmail 5958 [ + + + - :GNC 648 : if (INTERVAL_NOT_FINITE(interval))
- + + + +
- + - ]
5959 : : {
5960 : 102 : double r = NonFiniteIntervalPart(type, val, lowunits,
5961 [ + + + - : 102 : INTERVAL_IS_NOBEGIN(interval));
+ - ]
5962 : :
5963 [ + + ]: 102 : if (r != 0.0)
5964 : : {
5965 [ + + ]: 48 : if (retnumeric)
5966 : : {
5967 [ + + ]: 42 : if (r < 0)
5968 : 21 : return DirectFunctionCall3(numeric_in,
5969 : : CStringGetDatum("-Infinity"),
5970 : : ObjectIdGetDatum(InvalidOid),
5971 : : Int32GetDatum(-1));
5972 [ + - ]: 21 : else if (r > 0)
5973 : 21 : return DirectFunctionCall3(numeric_in,
5974 : : CStringGetDatum("Infinity"),
5975 : : ObjectIdGetDatum(InvalidOid),
5976 : : Int32GetDatum(-1));
5977 : : }
5978 : : else
5979 : 6 : PG_RETURN_FLOAT8(r);
5980 : : }
5981 : : else
5982 : 54 : PG_RETURN_NULL();
5983 : : }
5984 : :
8234 lockhart@fourpalms.o 5985 [ + + ]:CBC 546 : if (type == UNITS)
5986 : : {
743 tgl@sss.pgh.pa.us 5987 : 477 : interval2itm(*interval, tm);
703 5988 [ + + + + : 477 : switch (val)
+ + + + +
+ + + + ]
5989 : : {
5990 : 60 : case DTK_MICROSEC:
5991 : 60 : intresult = tm->tm_sec * INT64CONST(1000000) + tm->tm_usec;
5992 : 60 : break;
5993 : :
5994 : 60 : case DTK_MILLISEC:
5995 [ + + ]: 60 : if (retnumeric)
5996 : : /*---
5997 : : * tm->tm_sec * 1000 + fsec / 1000
5998 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1000
5999 : : */
6000 : 30 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + tm->tm_usec, 3));
6001 : : else
6002 : 30 : PG_RETURN_FLOAT8(tm->tm_sec * 1000.0 + tm->tm_usec / 1000.0);
6003 : : break;
6004 : :
6005 : 60 : case DTK_SECOND:
6006 [ + + ]: 60 : if (retnumeric)
6007 : : /*---
6008 : : * tm->tm_sec + fsec / 1'000'000
6009 : : * = (tm->tm_sec * 1'000'000 + fsec) / 1'000'000
6010 : : */
6011 : 30 : PG_RETURN_NUMERIC(int64_div_fast_to_numeric(tm->tm_sec * INT64CONST(1000000) + tm->tm_usec, 6));
6012 : : else
6013 : 30 : PG_RETURN_FLOAT8(tm->tm_sec + tm->tm_usec / 1000000.0);
6014 : : break;
6015 : :
6016 : 30 : case DTK_MINUTE:
6017 : 30 : intresult = tm->tm_min;
6018 : 30 : break;
6019 : :
6020 : 30 : case DTK_HOUR:
6021 : 30 : intresult = tm->tm_hour;
6022 : 30 : break;
6023 : :
6024 : 30 : case DTK_DAY:
6025 : 30 : intresult = tm->tm_mday;
6026 : 30 : break;
6027 : :
6028 : 30 : case DTK_MONTH:
6029 : 30 : intresult = tm->tm_mon;
6030 : 30 : break;
6031 : :
6032 : 30 : case DTK_QUARTER:
6033 : 30 : intresult = (tm->tm_mon / 3) + 1;
6034 : 30 : break;
6035 : :
6036 : 30 : case DTK_YEAR:
6037 : 30 : intresult = tm->tm_year;
6038 : 30 : break;
6039 : :
6040 : 42 : case DTK_DECADE:
6041 : : /* caution: C division may have negative remainder */
6042 : 42 : intresult = tm->tm_year / 10;
6043 : 42 : break;
6044 : :
6045 : 42 : case DTK_CENTURY:
6046 : : /* caution: C division may have negative remainder */
6047 : 42 : intresult = tm->tm_year / 100;
6048 : 42 : break;
6049 : :
6050 : 30 : case DTK_MILLENNIUM:
6051 : : /* caution: C division may have negative remainder */
6052 : 30 : intresult = tm->tm_year / 1000;
6053 : 30 : break;
6054 : :
6055 : 3 : default:
6056 [ + - ]: 3 : ereport(ERROR,
6057 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6058 : : errmsg("unit \"%s\" not supported for type %s",
6059 : : lowunits, format_type_be(INTERVALOID))));
6060 : : intresult = 0;
6061 : : }
6062 : : }
6901 bruce@momjian.us 6063 [ + + + - ]: 69 : else if (type == RESERV && val == DTK_EPOCH)
6064 : : {
1104 peter@eisentraut.org 6065 [ + + ]: 66 : if (retnumeric)
6066 : : {
6067 : : Numeric result;
6068 : : int64 secs_from_day_month;
6069 : : int64 val;
6070 : :
6071 : : /*
6072 : : * To do this calculation in integer arithmetic even though
6073 : : * DAYS_PER_YEAR is fractional, multiply everything by 4 and then
6074 : : * divide by 4 again at the end. This relies on DAYS_PER_YEAR
6075 : : * being a multiple of 0.25 and on SECS_PER_DAY being a multiple
6076 : : * of 4.
6077 : : */
726 6078 : 36 : secs_from_day_month = ((int64) (4 * DAYS_PER_YEAR) * (interval->month / MONTHS_PER_YEAR) +
6079 : 36 : (int64) (4 * DAYS_PER_MONTH) * (interval->month % MONTHS_PER_YEAR) +
6080 : 36 : (int64) 4 * interval->day) * (SECS_PER_DAY / 4);
6081 : :
6082 : : /*---
6083 : : * result = secs_from_day_month + interval->time / 1'000'000
6084 : : * = (secs_from_day_month * 1'000'000 + interval->time) / 1'000'000
6085 : : */
6086 : :
6087 : : /*
6088 : : * Try the computation inside int64; if it overflows, do it in
6089 : : * numeric (slower). This overflow happens around 10^9 days, so
6090 : : * not common in practice.
6091 : : */
1104 6092 [ + + ]: 36 : if (!pg_mul_s64_overflow(secs_from_day_month, 1000000, &val) &&
6093 [ + - ]: 33 : !pg_add_s64_overflow(val, interval->time, &val))
6094 : 33 : result = int64_div_fast_to_numeric(val, 6);
6095 : : else
6096 : : result =
6097 : 3 : numeric_add_opt_error(int64_div_fast_to_numeric(interval->time, 6),
6098 : : int64_to_numeric(secs_from_day_month),
6099 : : NULL);
6100 : :
6101 : 36 : PG_RETURN_NUMERIC(result);
6102 : : }
6103 : : else
6104 : : {
6105 : : float8 result;
6106 : :
6107 : 30 : result = interval->time / 1000000.0;
6108 : 30 : result += ((double) DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR);
6109 : 30 : result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR);
6110 : 30 : result += ((double) SECS_PER_DAY) * interval->day;
6111 : :
6112 : 30 : PG_RETURN_FLOAT8(result);
6113 : : }
6114 : : }
6115 : : else
6116 : : {
7567 tgl@sss.pgh.pa.us 6117 [ + - ]: 3 : ereport(ERROR,
6118 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6119 : : errmsg("unit \"%s\" not recognized for type %s",
6120 : : lowunits, format_type_be(INTERVALOID))));
6121 : : intresult = 0;
6122 : : }
6123 : :
1104 peter@eisentraut.org 6124 [ + + ]: 354 : if (retnumeric)
6125 : 324 : PG_RETURN_NUMERIC(int64_to_numeric(intresult));
6126 : : else
6127 : 30 : PG_RETURN_FLOAT8(intresult);
6128 : : }
6129 : :
6130 : : Datum
6131 : 144 : interval_part(PG_FUNCTION_ARGS)
6132 : : {
6133 : 144 : return interval_part_common(fcinfo, false);
6134 : : }
6135 : :
6136 : : Datum
6137 : 504 : extract_interval(PG_FUNCTION_ARGS)
6138 : : {
6139 : 504 : return interval_part_common(fcinfo, true);
6140 : : }
6141 : :
6142 : :
6143 : : /* timestamp_zone()
6144 : : * Encode timestamp type with specified time zone.
6145 : : * This function is just timestamp2timestamptz() except instead of
6146 : : * shifting to the global timezone, we shift to the specified timezone.
6147 : : * This is different from the other AT TIME ZONE cases because instead
6148 : : * of shifting _to_ a new time zone, it sets the time to _be_ the
6149 : : * specified timezone.
6150 : : */
6151 : : Datum
8710 tgl@sss.pgh.pa.us 6152 : 84 : timestamp_zone(PG_FUNCTION_ARGS)
6153 : : {
5864 6154 : 84 : text *zone = PG_GETARG_TEXT_PP(0);
6792 6155 : 84 : Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
6156 : : TimestampTz result;
6157 : : int tz;
6158 : : char tzname[TZ_STRLEN_MAX + 1];
6159 : : int type,
6160 : : val;
6161 : : pg_tz *tzp;
6162 : : struct pg_tm tm;
6163 : : fsec_t fsec;
6164 : :
8234 lockhart@fourpalms.o 6165 [ + - - + ]: 84 : if (TIMESTAMP_NOT_FINITE(timestamp))
8234 lockhart@fourpalms.o 6166 :UBC 0 : PG_RETURN_TIMESTAMPTZ(timestamp);
6167 : :
6168 : : /*
6169 : : * Look up the requested timezone.
6170 : : */
5864 tgl@sss.pgh.pa.us 6171 :CBC 84 : text_to_cstring_buffer(zone, tzname, sizeof(tzname));
6172 : :
394 6173 : 84 : type = DecodeTimezoneName(tzname, &val, &tzp);
6174 : :
6175 [ - + ]: 84 : if (type == TZNAME_FIXED_OFFSET)
6176 : : {
6177 : : /* fixed-offset abbreviation */
3468 tgl@sss.pgh.pa.us 6178 :UBC 0 : tz = val;
6179 : 0 : result = dt2local(timestamp, tz);
6180 : : }
394 tgl@sss.pgh.pa.us 6181 [ + + ]:CBC 84 : else if (type == TZNAME_DYNTZ)
6182 : : {
6183 : : /* dynamic-offset abbreviation, resolve using specified time */
3468 6184 [ - + ]: 42 : if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
3468 tgl@sss.pgh.pa.us 6185 [ # # ]:UBC 0 : ereport(ERROR,
6186 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6187 : : errmsg("timestamp out of range")));
3468 tgl@sss.pgh.pa.us 6188 :CBC 42 : tz = -DetermineTimeZoneAbbrevOffset(&tm, tzname, tzp);
5760 6189 : 42 : result = dt2local(timestamp, tz);
6190 : : }
6191 : : else
6192 : : {
6193 : : /* full zone name, rotate to that zone */
394 6194 [ - + ]: 42 : if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0)
6792 tgl@sss.pgh.pa.us 6195 [ # # ]:UBC 0 : ereport(ERROR,
6196 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6197 : : errmsg("timestamp out of range")));
394 tgl@sss.pgh.pa.us 6198 :CBC 42 : tz = DetermineTimeZoneOffset(&tm, tzp);
6199 [ - + ]: 42 : if (tm2timestamp(&tm, fsec, &tz, &result) != 0)
394 tgl@sss.pgh.pa.us 6200 [ # # ]:UBC 0 : ereport(ERROR,
6201 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6202 : : errmsg("timestamp out of range")));
6203 : : }
6204 : :
2951 tgl@sss.pgh.pa.us 6205 [ + - - + ]:CBC 84 : if (!IS_VALID_TIMESTAMP(result))
2951 tgl@sss.pgh.pa.us 6206 [ # # ]:UBC 0 : ereport(ERROR,
6207 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6208 : : errmsg("timestamp out of range")));
6209 : :
6841 bruce@momjian.us 6210 :CBC 84 : PG_RETURN_TIMESTAMPTZ(result);
6211 : : }
6212 : :
6213 : : /* timestamp_izone()
6214 : : * Encode timestamp type with specified time interval as time zone.
6215 : : */
6216 : : Datum
8234 lockhart@fourpalms.o 6217 :GBC 6 : timestamp_izone(PG_FUNCTION_ARGS)
6218 : : {
6219 : 6 : Interval *zone = PG_GETARG_INTERVAL_P(0);
6756 bruce@momjian.us 6220 : 6 : Timestamp timestamp = PG_GETARG_TIMESTAMP(1);
6221 : : TimestampTz result;
6222 : : int tz;
6223 : :
8234 lockhart@fourpalms.o 6224 [ + - - + ]: 6 : if (TIMESTAMP_NOT_FINITE(timestamp))
8234 lockhart@fourpalms.o 6225 :UBC 0 : PG_RETURN_TIMESTAMPTZ(timestamp);
6226 : :
152 dean.a.rasheed@gmail 6227 [ + + + - :GNC 6 : if (INTERVAL_NOT_FINITE(zone))
- + + - +
- + - ]
6228 [ + - ]: 6 : ereport(ERROR,
6229 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6230 : : errmsg("interval time zone \"%s\" must be finite",
6231 : : DatumGetCString(DirectFunctionCall1(interval_out,
6232 : : PointerGetDatum(zone))))));
6233 : :
4091 tgl@sss.pgh.pa.us 6234 [ # # # # ]:UBC 0 : if (zone->month != 0 || zone->day != 0)
7567 6235 [ # # ]: 0 : ereport(ERROR,
6236 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6237 : : errmsg("interval time zone \"%s\" must not include months or days",
6238 : : DatumGetCString(DirectFunctionCall1(interval_out,
6239 : : PointerGetDatum(zone))))));
6240 : :
6901 bruce@momjian.us 6241 : 0 : tz = zone->time / USECS_PER_SEC;
6242 : :
8029 lockhart@fourpalms.o 6243 : 0 : result = dt2local(timestamp, tz);
6244 : :
2951 tgl@sss.pgh.pa.us 6245 [ # # # # ]: 0 : if (!IS_VALID_TIMESTAMP(result))
6246 [ # # ]: 0 : ereport(ERROR,
6247 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6248 : : errmsg("timestamp out of range")));
6249 : :
8234 lockhart@fourpalms.o 6250 : 0 : PG_RETURN_TIMESTAMPTZ(result);
6251 : : } /* timestamp_izone() */
6252 : :
6253 : : /* TimestampTimestampTzRequiresRewrite()
6254 : : *
6255 : : * Returns false if the TimeZone GUC setting causes timestamp_timestamptz and
6256 : : * timestamptz_timestamp to be no-ops, where the return value has the same
6257 : : * bits as the argument. Since project convention is to assume a GUC changes
6258 : : * no more often than STABLE functions change, the answer is valid that long.
6259 : : */
6260 : : bool
1864 noah@leadboat.com 6261 :CBC 9 : TimestampTimestampTzRequiresRewrite(void)
6262 : : {
6263 : : long offset;
6264 : :
6265 [ + + + - ]: 9 : if (pg_get_timezone_offset(session_timezone, &offset) && offset == 0)
1757 6266 : 6 : return false;
6267 : 3 : return true;
6268 : : }
6269 : :
6270 : : /* timestamp_timestamptz()
6271 : : * Convert local timestamp to timestamp at GMT
6272 : : */
6273 : : Datum
8234 lockhart@fourpalms.o 6274 : 108 : timestamp_timestamptz(PG_FUNCTION_ARGS)
6275 : : {
6756 bruce@momjian.us 6276 : 108 : Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
6277 : :
7328 tgl@sss.pgh.pa.us 6278 : 108 : PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp));
6279 : : }
6280 : :
6281 : : /*
6282 : : * Convert timestamp to timestamp with time zone.
6283 : : *
6284 : : * On successful conversion, *overflow is set to zero if it's not NULL.
6285 : : *
6286 : : * If the timestamp is finite but out of the valid range for timestamptz, then:
6287 : : * if overflow is NULL, we throw an out-of-range error.
6288 : : * if overflow is not NULL, we store +1 or -1 there to indicate the sign
6289 : : * of the overflow, and return the appropriate timestamptz infinity.
6290 : : */
6291 : : TimestampTz
1637 akorotkov@postgresql 6292 : 8064 : timestamp2timestamptz_opt_overflow(Timestamp timestamp, int *overflow)
6293 : : {
6294 : : TimestampTz result;
6295 : : struct pg_tm tt,
8234 lockhart@fourpalms.o 6296 : 8064 : *tm = &tt;
6297 : : fsec_t fsec;
6298 : : int tz;
6299 : :
1285 tgl@sss.pgh.pa.us 6300 [ + + ]: 8064 : if (overflow)
6301 : 7953 : *overflow = 0;
6302 : :
8234 lockhart@fourpalms.o 6303 [ + - - + ]: 8064 : if (TIMESTAMP_NOT_FINITE(timestamp))
1663 akorotkov@postgresql 6304 :UBC 0 : return timestamp;
6305 : :
6306 : : /* We don't expect this to fail, but check it pro forma */
1285 tgl@sss.pgh.pa.us 6307 [ + - ]:CBC 8064 : if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0)
6308 : : {
6098 6309 : 8064 : tz = DetermineTimeZoneOffset(tm, session_timezone);
6310 : :
1637 akorotkov@postgresql 6311 : 8064 : result = dt2local(timestamp, -tz);
6312 : :
6313 [ + + + - ]: 8064 : if (IS_VALID_TIMESTAMP(result))
6314 : : {
1663 6315 : 8058 : return result;
6316 : : }
1637 6317 [ + - ]: 6 : else if (overflow)
6318 : : {
6319 [ + - ]: 6 : if (result < MIN_TIMESTAMP)
6320 : : {
6321 : 6 : *overflow = -1;
1285 tgl@sss.pgh.pa.us 6322 : 6 : TIMESTAMP_NOBEGIN(result);
6323 : : }
6324 : : else
6325 : : {
1637 akorotkov@postgresql 6326 :UBC 0 : *overflow = 1;
1285 tgl@sss.pgh.pa.us 6327 : 0 : TIMESTAMP_NOEND(result);
6328 : : }
1285 tgl@sss.pgh.pa.us 6329 :CBC 6 : return result;
6330 : : }
6331 : : }
6332 : :
1637 akorotkov@postgresql 6333 [ # # ]:UBC 0 : ereport(ERROR,
6334 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6335 : : errmsg("timestamp out of range")));
6336 : :
6337 : : return 0;
6338 : : }
6339 : :
6340 : : /*
6341 : : * Promote timestamp to timestamptz, throwing error for overflow.
6342 : : */
6343 : : static TimestampTz
1663 akorotkov@postgresql 6344 :CBC 111 : timestamp2timestamptz(Timestamp timestamp)
6345 : : {
1637 6346 : 111 : return timestamp2timestamptz_opt_overflow(timestamp, NULL);
6347 : : }
6348 : :
6349 : : /* timestamptz_timestamp()
6350 : : * Convert timestamp at GMT to local timestamp
6351 : : */
6352 : : Datum
8234 lockhart@fourpalms.o 6353 : 31052 : timestamptz_timestamp(PG_FUNCTION_ARGS)
6354 : : {
7643 tgl@sss.pgh.pa.us 6355 : 31052 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0);
6356 : :
2798 6357 : 31052 : PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp));
6358 : : }
6359 : :
6360 : : static Timestamp
6361 : 31085 : timestamptz2timestamp(TimestampTz timestamp)
6362 : : {
6363 : : Timestamp result;
6364 : : struct pg_tm tt,
8234 lockhart@fourpalms.o 6365 : 31085 : *tm = &tt;
6366 : : fsec_t fsec;
6367 : : int tz;
6368 : :
6369 [ + - - + ]: 31085 : if (TIMESTAMP_NOT_FINITE(timestamp))
8234 lockhart@fourpalms.o 6370 :UBC 0 : result = timestamp;
6371 : : else
6372 : : {
4413 peter_e@gmx.net 6373 [ - + ]:CBC 31085 : if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
7567 tgl@sss.pgh.pa.us 6374 [ # # ]:UBC 0 : ereport(ERROR,
6375 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6376 : : errmsg("timestamp out of range")));
8234 lockhart@fourpalms.o 6377 [ - + ]:CBC 31085 : if (tm2timestamp(tm, fsec, NULL, &result) != 0)
7567 tgl@sss.pgh.pa.us 6378 [ # # ]:UBC 0 : ereport(ERROR,
6379 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6380 : : errmsg("timestamp out of range")));
6381 : : }
2798 tgl@sss.pgh.pa.us 6382 :CBC 31085 : return result;
6383 : : }
6384 : :
6385 : : /* timestamptz_zone()
6386 : : * Evaluate timestamp with time zone type at the specified time zone.
6387 : : * Returns a timestamp without time zone.
6388 : : */
6389 : : Datum
8234 lockhart@fourpalms.o 6390 : 111 : timestamptz_zone(PG_FUNCTION_ARGS)
6391 : : {
5864 tgl@sss.pgh.pa.us 6392 : 111 : text *zone = PG_GETARG_TEXT_PP(0);
7643 6393 : 111 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
6394 : : Timestamp result;
6395 : : int tz;
6396 : : char tzname[TZ_STRLEN_MAX + 1];
6397 : : int type,
6398 : : val;
6399 : : pg_tz *tzp;
6400 : :
8710 6401 [ + + + + ]: 111 : if (TIMESTAMP_NOT_FINITE(timestamp))
6792 tgl@sss.pgh.pa.us 6402 :GBC 12 : PG_RETURN_TIMESTAMP(timestamp);
6403 : :
6404 : : /*
6405 : : * Look up the requested timezone.
6406 : : */
5864 tgl@sss.pgh.pa.us 6407 :CBC 99 : text_to_cstring_buffer(zone, tzname, sizeof(tzname));
6408 : :
394 6409 : 99 : type = DecodeTimezoneName(tzname, &val, &tzp);
6410 : :
6411 [ + + ]: 96 : if (type == TZNAME_FIXED_OFFSET)
6412 : : {
6413 : : /* fixed-offset abbreviation */
3468 6414 : 18 : tz = -val;
6415 : 18 : result = dt2local(timestamp, tz);
6416 : : }
394 6417 [ + + ]: 78 : else if (type == TZNAME_DYNTZ)
6418 : : {
6419 : : /* dynamic-offset abbreviation, resolve using specified time */
6420 : : int isdst;
6421 : :
3468 6422 : 36 : tz = DetermineTimeZoneAbbrevOffsetTS(timestamp, tzname, tzp, &isdst);
5760 6423 : 36 : result = dt2local(timestamp, tz);
6424 : : }
6425 : : else
6426 : : {
6427 : : /* full zone name, rotate from that zone */
6428 : : struct pg_tm tm;
6429 : : fsec_t fsec;
6430 : :
394 6431 [ - + ]: 42 : if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0)
6792 tgl@sss.pgh.pa.us 6432 [ # # ]:UBC 0 : ereport(ERROR,
6433 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6434 : : errmsg("timestamp out of range")));
394 tgl@sss.pgh.pa.us 6435 [ - + ]:CBC 42 : if (tm2timestamp(&tm, fsec, NULL, &result) != 0)
394 tgl@sss.pgh.pa.us 6436 [ # # ]:UBC 0 : ereport(ERROR,
6437 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6438 : : errmsg("timestamp out of range")));
6439 : : }
6440 : :
2951 tgl@sss.pgh.pa.us 6441 [ + - - + ]:CBC 96 : if (!IS_VALID_TIMESTAMP(result))
2951 tgl@sss.pgh.pa.us 6442 [ # # ]:UBC 0 : ereport(ERROR,
6443 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6444 : : errmsg("timestamp out of range")));
6445 : :
8029 lockhart@fourpalms.o 6446 :CBC 96 : PG_RETURN_TIMESTAMP(result);
6447 : : }
6448 : :
6449 : : /* timestamptz_izone()
6450 : : * Encode timestamp with time zone type with specified time interval as time zone.
6451 : : * Returns a timestamp without time zone.
6452 : : */
6453 : : Datum
8234 lockhart@fourpalms.o 6454 :GBC 6 : timestamptz_izone(PG_FUNCTION_ARGS)
6455 : : {
8560 6456 : 6 : Interval *zone = PG_GETARG_INTERVAL_P(0);
7643 tgl@sss.pgh.pa.us 6457 : 6 : TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1);
6458 : : Timestamp result;
6459 : : int tz;
6460 : :
8560 lockhart@fourpalms.o 6461 [ + - - + ]: 6 : if (TIMESTAMP_NOT_FINITE(timestamp))
6792 tgl@sss.pgh.pa.us 6462 :UBC 0 : PG_RETURN_TIMESTAMP(timestamp);
6463 : :
152 dean.a.rasheed@gmail 6464 [ + + + - :GNC 6 : if (INTERVAL_NOT_FINITE(zone))
- + + - +
- + - ]
6465 [ + - ]: 6 : ereport(ERROR,
6466 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6467 : : errmsg("interval time zone \"%s\" must be finite",
6468 : : DatumGetCString(DirectFunctionCall1(interval_out,
6469 : : PointerGetDatum(zone))))));
6470 : :
4091 tgl@sss.pgh.pa.us 6471 [ # # # # ]:UBC 0 : if (zone->month != 0 || zone->day != 0)
7567 6472 [ # # ]: 0 : ereport(ERROR,
6473 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6474 : : errmsg("interval time zone \"%s\" must not include months or days",
6475 : : DatumGetCString(DirectFunctionCall1(interval_out,
6476 : : PointerGetDatum(zone))))));
6477 : :
6901 bruce@momjian.us 6478 : 0 : tz = -(zone->time / USECS_PER_SEC);
6479 : :
8029 lockhart@fourpalms.o 6480 : 0 : result = dt2local(timestamp, tz);
6481 : :
2951 tgl@sss.pgh.pa.us 6482 [ # # # # ]: 0 : if (!IS_VALID_TIMESTAMP(result))
6483 [ # # ]: 0 : ereport(ERROR,
6484 : : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
6485 : : errmsg("timestamp out of range")));
6486 : :
8029 lockhart@fourpalms.o 6487 : 0 : PG_RETURN_TIMESTAMP(result);
6488 : : }
6489 : :
6490 : : /* generate_series_timestamp()
6491 : : * Generate the set of timestamps from start to finish by step
6492 : : */
6493 : : Datum
5824 tgl@sss.pgh.pa.us 6494 :CBC 249 : generate_series_timestamp(PG_FUNCTION_ARGS)
6495 : : {
6496 : : FuncCallContext *funcctx;
6497 : : generate_series_timestamp_fctx *fctx;
6498 : : Timestamp result;
6499 : :
6500 : : /* stuff done only on the first call of the function */
6501 [ + + ]: 249 : if (SRF_IS_FIRSTCALL())
6502 : : {
5421 bruce@momjian.us 6503 : 18 : Timestamp start = PG_GETARG_TIMESTAMP(0);
6504 : 18 : Timestamp finish = PG_GETARG_TIMESTAMP(1);
6505 : 18 : Interval *step = PG_GETARG_INTERVAL_P(2);
6506 : : MemoryContext oldcontext;
6507 : :
6508 : : /* create a function context for cross-call persistence */
5824 tgl@sss.pgh.pa.us 6509 : 18 : funcctx = SRF_FIRSTCALL_INIT();
6510 : :
6511 : : /*
6512 : : * switch to memory context appropriate for multiple function calls
6513 : : */
6514 : 18 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6515 : :
6516 : : /* allocate memory for user context */
6517 : : fctx = (generate_series_timestamp_fctx *)
6518 : 18 : palloc(sizeof(generate_series_timestamp_fctx));
6519 : :
6520 : : /*
6521 : : * Use fctx to keep state from call to call. Seed current with the
6522 : : * original start value
6523 : : */
6524 : 18 : fctx->current = start;
6525 : 18 : fctx->finish = finish;
6526 : 18 : fctx->step = *step;
6527 : :
6528 : : /* Determine sign of the interval */
152 dean.a.rasheed@gmail 6529 :GNC 18 : fctx->step_sign = interval_sign(&fctx->step);
6530 : :
5824 tgl@sss.pgh.pa.us 6531 [ + + ]:CBC 18 : if (fctx->step_sign == 0)
6532 [ + - ]: 3 : ereport(ERROR,
6533 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6534 : : errmsg("step size cannot equal zero")));
6535 : :
152 dean.a.rasheed@gmail 6536 [ + + + - :GNC 15 : if (INTERVAL_NOT_FINITE((&fctx->step)))
- + + + +
- + - ]
6537 [ + - ]: 6 : ereport(ERROR,
6538 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6539 : : errmsg("step size cannot be infinite")));
6540 : :
5824 tgl@sss.pgh.pa.us 6541 :CBC 9 : funcctx->user_fctx = fctx;
6542 : 9 : MemoryContextSwitchTo(oldcontext);
6543 : : }
6544 : :
6545 : : /* stuff done on every call of the function */
6546 : 240 : funcctx = SRF_PERCALL_SETUP();
6547 : :
6548 : : /*
6549 : : * get the saved state and use current as the result for this iteration
6550 : : */
6551 : 240 : fctx = funcctx->user_fctx;
6552 : 240 : result = fctx->current;
6553 : :
6554 [ + - + + ]: 480 : if (fctx->step_sign > 0 ?
6555 : 240 : timestamp_cmp_internal(result, fctx->finish) <= 0 :
5824 tgl@sss.pgh.pa.us 6556 :UBC 0 : timestamp_cmp_internal(result, fctx->finish) >= 0)
6557 : : {
6558 : : /* increment current in preparation for next iteration */
1536 alvherre@alvh.no-ip. 6559 :CBC 234 : fctx->current = DatumGetTimestamp(DirectFunctionCall2(timestamp_pl_interval,
6560 : : TimestampGetDatum(fctx->current),
6561 : : PointerGetDatum(&fctx->step)));
6562 : :
6563 : : /* do when there is more left to send */
5824 tgl@sss.pgh.pa.us 6564 : 234 : SRF_RETURN_NEXT(funcctx, TimestampGetDatum(result));
6565 : : }
6566 : : else
6567 : : {
6568 : : /* do when there is no more left */
6569 : 6 : SRF_RETURN_DONE(funcctx);
6570 : : }
6571 : : }
6572 : :
6573 : : /* generate_series_timestamptz()
6574 : : * Generate the set of timestamps from start to finish by step,
6575 : : * doing arithmetic in the specified or session timezone.
6576 : : */
6577 : : static Datum
393 6578 : 31038 : generate_series_timestamptz_internal(FunctionCallInfo fcinfo)
6579 : : {
6580 : : FuncCallContext *funcctx;
6581 : : generate_series_timestamptz_fctx *fctx;
6582 : : TimestampTz result;
6583 : :
6584 : : /* stuff done only on the first call of the function */
5824 6585 [ + + ]: 31038 : if (SRF_IS_FIRSTCALL())
6586 : : {
6587 : 28 : TimestampTz start = PG_GETARG_TIMESTAMPTZ(0);
6588 : 28 : TimestampTz finish = PG_GETARG_TIMESTAMPTZ(1);
5421 bruce@momjian.us 6589 : 28 : Interval *step = PG_GETARG_INTERVAL_P(2);
393 tgl@sss.pgh.pa.us 6590 [ + + ]: 28 : text *zone = (PG_NARGS() == 4) ? PG_GETARG_TEXT_PP(3) : NULL;
6591 : : MemoryContext oldcontext;
6592 : :
6593 : : /* create a function context for cross-call persistence */
5824 6594 : 28 : funcctx = SRF_FIRSTCALL_INIT();
6595 : :
6596 : : /*
6597 : : * switch to memory context appropriate for multiple function calls
6598 : : */
6599 : 28 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6600 : :
6601 : : /* allocate memory for user context */
6602 : : fctx = (generate_series_timestamptz_fctx *)
6603 : 28 : palloc(sizeof(generate_series_timestamptz_fctx));
6604 : :
6605 : : /*
6606 : : * Use fctx to keep state from call to call. Seed current with the
6607 : : * original start value
6608 : : */
6609 : 28 : fctx->current = start;
6610 : 28 : fctx->finish = finish;
6611 : 28 : fctx->step = *step;
393 6612 [ + + ]: 28 : fctx->attimezone = zone ? lookup_timezone(zone) : session_timezone;
6613 : :
6614 : : /* Determine sign of the interval */
152 dean.a.rasheed@gmail 6615 :GNC 28 : fctx->step_sign = interval_sign(&fctx->step);
6616 : :
5824 tgl@sss.pgh.pa.us 6617 [ + + ]:CBC 28 : if (fctx->step_sign == 0)
6618 [ + - ]: 3 : ereport(ERROR,
6619 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6620 : : errmsg("step size cannot equal zero")));
6621 : :
152 dean.a.rasheed@gmail 6622 [ + + + - :GNC 25 : if (INTERVAL_NOT_FINITE((&fctx->step)))
- + + + +
- + - ]
6623 [ + - ]: 6 : ereport(ERROR,
6624 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6625 : : errmsg("step size cannot be infinite")));
6626 : :
5824 tgl@sss.pgh.pa.us 6627 :CBC 19 : funcctx->user_fctx = fctx;
6628 : 19 : MemoryContextSwitchTo(oldcontext);
6629 : : }
6630 : :
6631 : : /* stuff done on every call of the function */
6632 : 31029 : funcctx = SRF_PERCALL_SETUP();
6633 : :
6634 : : /*
6635 : : * get the saved state and use current as the result for this iteration
6636 : : */
6637 : 31029 : fctx = funcctx->user_fctx;
6638 : 31029 : result = fctx->current;
6639 : :
6640 [ + + + + ]: 62058 : if (fctx->step_sign > 0 ?
6641 : 30987 : timestamp_cmp_internal(result, fctx->finish) <= 0 :
6642 : 42 : timestamp_cmp_internal(result, fctx->finish) >= 0)
6643 : : {
6644 : : /* increment current in preparation for next iteration */
393 6645 : 31013 : fctx->current = timestamptz_pl_interval_internal(fctx->current,
6646 : : &fctx->step,
6647 : : fctx->attimezone);
6648 : :
6649 : : /* do when there is more left to send */
5824 6650 : 31013 : SRF_RETURN_NEXT(funcctx, TimestampTzGetDatum(result));
6651 : : }
6652 : : else
6653 : : {
6654 : : /* do when there is no more left */
6655 : 16 : SRF_RETURN_DONE(funcctx);
6656 : : }
6657 : : }
6658 : :
6659 : : Datum
393 6660 : 30996 : generate_series_timestamptz(PG_FUNCTION_ARGS)
6661 : : {
6662 : 30996 : return generate_series_timestamptz_internal(fcinfo);
6663 : : }
6664 : :
6665 : : Datum
6666 : 42 : generate_series_timestamptz_at_zone(PG_FUNCTION_ARGS)
6667 : : {
6668 : 42 : return generate_series_timestamptz_internal(fcinfo);
6669 : : }
6670 : :
6671 : : /* timestamp_at_local()
6672 : : * timestamptz_at_local()
6673 : : *
6674 : : * The regression tests do not like two functions with the same proargs and
6675 : : * prosrc but different proname, but the grammar for AT LOCAL needs an
6676 : : * overloaded name to handle both types of timestamp, so we make simple
6677 : : * wrappers for it.
6678 : : */
6679 : : Datum
184 michael@paquier.xyz 6680 :GNC 12 : timestamp_at_local(PG_FUNCTION_ARGS)
6681 : : {
6682 : 12 : return timestamp_timestamptz(fcinfo);
6683 : : }
6684 : :
6685 : : Datum
6686 : 12 : timestamptz_at_local(PG_FUNCTION_ARGS)
6687 : : {
6688 : 12 : return timestamptz_timestamp(fcinfo);
6689 : : }
|