| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * timestamp.c |
| 4 | * Functions for the built-in SQL types "timestamp" and "interval". |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, 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/int128.h" |
| 26 | #include "funcapi.h" |
| 27 | #include "libpq/pqformat.h" |
| 28 | #include "miscadmin.h" |
| 29 | #include "nodes/makefuncs.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/datetime.h" |
| 36 | #include "utils/float.h" |
| 37 | |
| 38 | /* |
| 39 | * gcc's -ffast-math switch breaks routines that expect exact results from |
| 40 | * expressions like timeval / SECS_PER_HOUR, where timeval is double. |
| 41 | */ |
| 42 | #ifdef __FAST_MATH__ |
| 43 | #error -ffast-math is known to break this code |
| 44 | #endif |
| 45 | |
| 46 | #define SAMESIGN(a,b) (((a) < 0) == ((b) < 0)) |
| 47 | |
| 48 | /* Set at postmaster start */ |
| 49 | TimestampTz PgStartTime; |
| 50 | |
| 51 | /* Set at configuration reload */ |
| 52 | TimestampTz PgReloadTime; |
| 53 | |
| 54 | typedef struct |
| 55 | { |
| 56 | Timestamp current; |
| 57 | Timestamp finish; |
| 58 | Interval step; |
| 59 | int step_sign; |
| 60 | } generate_series_timestamp_fctx; |
| 61 | |
| 62 | typedef struct |
| 63 | { |
| 64 | TimestampTz current; |
| 65 | TimestampTz finish; |
| 66 | Interval step; |
| 67 | int step_sign; |
| 68 | } generate_series_timestamptz_fctx; |
| 69 | |
| 70 | |
| 71 | static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec); |
| 72 | static Timestamp dt2local(Timestamp dt, int timezone); |
| 73 | static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod); |
| 74 | static void AdjustIntervalForTypmod(Interval *interval, int32 typmod); |
| 75 | static TimestampTz timestamp2timestamptz(Timestamp timestamp); |
| 76 | static Timestamp timestamptz2timestamp(TimestampTz timestamp); |
| 77 | |
| 78 | |
| 79 | /* common code for timestamptypmodin and timestamptztypmodin */ |
| 80 | static int32 |
| 81 | anytimestamp_typmodin(bool istz, ArrayType *ta) |
| 82 | { |
| 83 | int32 *tl; |
| 84 | int n; |
| 85 | |
| 86 | tl = ArrayGetIntegerTypmods(ta, &n); |
| 87 | |
| 88 | /* |
| 89 | * we're not too tense about good error message here because grammar |
| 90 | * shouldn't allow wrong number of modifiers for TIMESTAMP |
| 91 | */ |
| 92 | if (n != 1) |
| 93 | ereport(ERROR, |
| 94 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 95 | errmsg("invalid type modifier" ))); |
| 96 | |
| 97 | return anytimestamp_typmod_check(istz, tl[0]); |
| 98 | } |
| 99 | |
| 100 | /* exported so parse_expr.c can use it */ |
| 101 | int32 |
| 102 | anytimestamp_typmod_check(bool istz, int32 typmod) |
| 103 | { |
| 104 | if (typmod < 0) |
| 105 | ereport(ERROR, |
| 106 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 107 | errmsg("TIMESTAMP(%d)%s precision must not be negative" , |
| 108 | typmod, (istz ? " WITH TIME ZONE" : "" )))); |
| 109 | if (typmod > MAX_TIMESTAMP_PRECISION) |
| 110 | { |
| 111 | ereport(WARNING, |
| 112 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 113 | errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d" , |
| 114 | typmod, (istz ? " WITH TIME ZONE" : "" ), |
| 115 | MAX_TIMESTAMP_PRECISION))); |
| 116 | typmod = MAX_TIMESTAMP_PRECISION; |
| 117 | } |
| 118 | |
| 119 | return typmod; |
| 120 | } |
| 121 | |
| 122 | /* common code for timestamptypmodout and timestamptztypmodout */ |
| 123 | static char * |
| 124 | anytimestamp_typmodout(bool istz, int32 typmod) |
| 125 | { |
| 126 | const char *tz = istz ? " with time zone" : " without time zone" ; |
| 127 | |
| 128 | if (typmod >= 0) |
| 129 | return psprintf("(%d)%s" , (int) typmod, tz); |
| 130 | else |
| 131 | return psprintf("%s" , tz); |
| 132 | } |
| 133 | |
| 134 | |
| 135 | /***************************************************************************** |
| 136 | * USER I/O ROUTINES * |
| 137 | *****************************************************************************/ |
| 138 | |
| 139 | /* timestamp_in() |
| 140 | * Convert a string to internal form. |
| 141 | */ |
| 142 | Datum |
| 143 | timestamp_in(PG_FUNCTION_ARGS) |
| 144 | { |
| 145 | char *str = PG_GETARG_CSTRING(0); |
| 146 | |
| 147 | #ifdef NOT_USED |
| 148 | Oid typelem = PG_GETARG_OID(1); |
| 149 | #endif |
| 150 | int32 typmod = PG_GETARG_INT32(2); |
| 151 | Timestamp result; |
| 152 | fsec_t fsec; |
| 153 | struct pg_tm tt, |
| 154 | *tm = &tt; |
| 155 | int tz; |
| 156 | int dtype; |
| 157 | int nf; |
| 158 | int dterr; |
| 159 | char *field[MAXDATEFIELDS]; |
| 160 | int ftype[MAXDATEFIELDS]; |
| 161 | char workbuf[MAXDATELEN + MAXDATEFIELDS]; |
| 162 | |
| 163 | dterr = ParseDateTime(str, workbuf, sizeof(workbuf), |
| 164 | field, ftype, MAXDATEFIELDS, &nf); |
| 165 | if (dterr == 0) |
| 166 | dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); |
| 167 | if (dterr != 0) |
| 168 | DateTimeParseError(dterr, str, "timestamp" ); |
| 169 | |
| 170 | switch (dtype) |
| 171 | { |
| 172 | case DTK_DATE: |
| 173 | if (tm2timestamp(tm, fsec, NULL, &result) != 0) |
| 174 | ereport(ERROR, |
| 175 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 176 | errmsg("timestamp out of range: \"%s\"" , str))); |
| 177 | break; |
| 178 | |
| 179 | case DTK_EPOCH: |
| 180 | result = SetEpochTimestamp(); |
| 181 | break; |
| 182 | |
| 183 | case DTK_LATE: |
| 184 | TIMESTAMP_NOEND(result); |
| 185 | break; |
| 186 | |
| 187 | case DTK_EARLY: |
| 188 | TIMESTAMP_NOBEGIN(result); |
| 189 | break; |
| 190 | |
| 191 | default: |
| 192 | elog(ERROR, "unexpected dtype %d while parsing timestamp \"%s\"" , |
| 193 | dtype, str); |
| 194 | TIMESTAMP_NOEND(result); |
| 195 | } |
| 196 | |
| 197 | AdjustTimestampForTypmod(&result, typmod); |
| 198 | |
| 199 | PG_RETURN_TIMESTAMP(result); |
| 200 | } |
| 201 | |
| 202 | /* timestamp_out() |
| 203 | * Convert a timestamp to external form. |
| 204 | */ |
| 205 | Datum |
| 206 | timestamp_out(PG_FUNCTION_ARGS) |
| 207 | { |
| 208 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 209 | char *result; |
| 210 | struct pg_tm tt, |
| 211 | *tm = &tt; |
| 212 | fsec_t fsec; |
| 213 | char buf[MAXDATELEN + 1]; |
| 214 | |
| 215 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 216 | EncodeSpecialTimestamp(timestamp, buf); |
| 217 | else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) |
| 218 | EncodeDateTime(tm, fsec, false, 0, NULL, DateStyle, buf); |
| 219 | else |
| 220 | ereport(ERROR, |
| 221 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 222 | errmsg("timestamp out of range" ))); |
| 223 | |
| 224 | result = pstrdup(buf); |
| 225 | PG_RETURN_CSTRING(result); |
| 226 | } |
| 227 | |
| 228 | /* |
| 229 | * timestamp_recv - converts external binary format to timestamp |
| 230 | */ |
| 231 | Datum |
| 232 | timestamp_recv(PG_FUNCTION_ARGS) |
| 233 | { |
| 234 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
| 235 | |
| 236 | #ifdef NOT_USED |
| 237 | Oid typelem = PG_GETARG_OID(1); |
| 238 | #endif |
| 239 | int32 typmod = PG_GETARG_INT32(2); |
| 240 | Timestamp timestamp; |
| 241 | struct pg_tm tt, |
| 242 | *tm = &tt; |
| 243 | fsec_t fsec; |
| 244 | |
| 245 | timestamp = (Timestamp) pq_getmsgint64(buf); |
| 246 | |
| 247 | /* range check: see if timestamp_out would like it */ |
| 248 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 249 | /* ok */ ; |
| 250 | else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0 || |
| 251 | !IS_VALID_TIMESTAMP(timestamp)) |
| 252 | ereport(ERROR, |
| 253 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 254 | errmsg("timestamp out of range" ))); |
| 255 | |
| 256 | AdjustTimestampForTypmod(×tamp, typmod); |
| 257 | |
| 258 | PG_RETURN_TIMESTAMP(timestamp); |
| 259 | } |
| 260 | |
| 261 | /* |
| 262 | * timestamp_send - converts timestamp to binary format |
| 263 | */ |
| 264 | Datum |
| 265 | timestamp_send(PG_FUNCTION_ARGS) |
| 266 | { |
| 267 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 268 | StringInfoData buf; |
| 269 | |
| 270 | pq_begintypsend(&buf); |
| 271 | pq_sendint64(&buf, timestamp); |
| 272 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
| 273 | } |
| 274 | |
| 275 | Datum |
| 276 | timestamptypmodin(PG_FUNCTION_ARGS) |
| 277 | { |
| 278 | ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); |
| 279 | |
| 280 | PG_RETURN_INT32(anytimestamp_typmodin(false, ta)); |
| 281 | } |
| 282 | |
| 283 | Datum |
| 284 | timestamptypmodout(PG_FUNCTION_ARGS) |
| 285 | { |
| 286 | int32 typmod = PG_GETARG_INT32(0); |
| 287 | |
| 288 | PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod)); |
| 289 | } |
| 290 | |
| 291 | |
| 292 | /* |
| 293 | * timestamp_support() |
| 294 | * |
| 295 | * Planner support function for the timestamp_scale() and timestamptz_scale() |
| 296 | * length coercion functions (we need not distinguish them here). |
| 297 | */ |
| 298 | Datum |
| 299 | timestamp_support(PG_FUNCTION_ARGS) |
| 300 | { |
| 301 | Node *rawreq = (Node *) PG_GETARG_POINTER(0); |
| 302 | Node *ret = NULL; |
| 303 | |
| 304 | if (IsA(rawreq, SupportRequestSimplify)) |
| 305 | { |
| 306 | SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; |
| 307 | |
| 308 | ret = TemporalSimplify(MAX_TIMESTAMP_PRECISION, (Node *) req->fcall); |
| 309 | } |
| 310 | |
| 311 | PG_RETURN_POINTER(ret); |
| 312 | } |
| 313 | |
| 314 | /* timestamp_scale() |
| 315 | * Adjust time type for specified scale factor. |
| 316 | * Used by PostgreSQL type system to stuff columns. |
| 317 | */ |
| 318 | Datum |
| 319 | timestamp_scale(PG_FUNCTION_ARGS) |
| 320 | { |
| 321 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 322 | int32 typmod = PG_GETARG_INT32(1); |
| 323 | Timestamp result; |
| 324 | |
| 325 | result = timestamp; |
| 326 | |
| 327 | AdjustTimestampForTypmod(&result, typmod); |
| 328 | |
| 329 | PG_RETURN_TIMESTAMP(result); |
| 330 | } |
| 331 | |
| 332 | /* |
| 333 | * AdjustTimestampForTypmod --- round off a timestamp to suit given typmod |
| 334 | * Works for either timestamp or timestamptz. |
| 335 | */ |
| 336 | static void |
| 337 | AdjustTimestampForTypmod(Timestamp *time, int32 typmod) |
| 338 | { |
| 339 | static const int64 TimestampScales[MAX_TIMESTAMP_PRECISION + 1] = { |
| 340 | INT64CONST(1000000), |
| 341 | INT64CONST(100000), |
| 342 | INT64CONST(10000), |
| 343 | INT64CONST(1000), |
| 344 | INT64CONST(100), |
| 345 | INT64CONST(10), |
| 346 | INT64CONST(1) |
| 347 | }; |
| 348 | |
| 349 | static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = { |
| 350 | INT64CONST(500000), |
| 351 | INT64CONST(50000), |
| 352 | INT64CONST(5000), |
| 353 | INT64CONST(500), |
| 354 | INT64CONST(50), |
| 355 | INT64CONST(5), |
| 356 | INT64CONST(0) |
| 357 | }; |
| 358 | |
| 359 | if (!TIMESTAMP_NOT_FINITE(*time) |
| 360 | && (typmod != -1) && (typmod != MAX_TIMESTAMP_PRECISION)) |
| 361 | { |
| 362 | if (typmod < 0 || typmod > MAX_TIMESTAMP_PRECISION) |
| 363 | ereport(ERROR, |
| 364 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 365 | errmsg("timestamp(%d) precision must be between %d and %d" , |
| 366 | typmod, 0, MAX_TIMESTAMP_PRECISION))); |
| 367 | |
| 368 | if (*time >= INT64CONST(0)) |
| 369 | { |
| 370 | *time = ((*time + TimestampOffsets[typmod]) / TimestampScales[typmod]) * |
| 371 | TimestampScales[typmod]; |
| 372 | } |
| 373 | else |
| 374 | { |
| 375 | *time = -((((-*time) + TimestampOffsets[typmod]) / TimestampScales[typmod]) |
| 376 | * TimestampScales[typmod]); |
| 377 | } |
| 378 | } |
| 379 | } |
| 380 | |
| 381 | |
| 382 | /* timestamptz_in() |
| 383 | * Convert a string to internal form. |
| 384 | */ |
| 385 | Datum |
| 386 | timestamptz_in(PG_FUNCTION_ARGS) |
| 387 | { |
| 388 | char *str = PG_GETARG_CSTRING(0); |
| 389 | |
| 390 | #ifdef NOT_USED |
| 391 | Oid typelem = PG_GETARG_OID(1); |
| 392 | #endif |
| 393 | int32 typmod = PG_GETARG_INT32(2); |
| 394 | TimestampTz result; |
| 395 | fsec_t fsec; |
| 396 | struct pg_tm tt, |
| 397 | *tm = &tt; |
| 398 | int tz; |
| 399 | int dtype; |
| 400 | int nf; |
| 401 | int dterr; |
| 402 | char *field[MAXDATEFIELDS]; |
| 403 | int ftype[MAXDATEFIELDS]; |
| 404 | char workbuf[MAXDATELEN + MAXDATEFIELDS]; |
| 405 | |
| 406 | dterr = ParseDateTime(str, workbuf, sizeof(workbuf), |
| 407 | field, ftype, MAXDATEFIELDS, &nf); |
| 408 | if (dterr == 0) |
| 409 | dterr = DecodeDateTime(field, ftype, nf, &dtype, tm, &fsec, &tz); |
| 410 | if (dterr != 0) |
| 411 | DateTimeParseError(dterr, str, "timestamp with time zone" ); |
| 412 | |
| 413 | switch (dtype) |
| 414 | { |
| 415 | case DTK_DATE: |
| 416 | if (tm2timestamp(tm, fsec, &tz, &result) != 0) |
| 417 | ereport(ERROR, |
| 418 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 419 | errmsg("timestamp out of range: \"%s\"" , str))); |
| 420 | break; |
| 421 | |
| 422 | case DTK_EPOCH: |
| 423 | result = SetEpochTimestamp(); |
| 424 | break; |
| 425 | |
| 426 | case DTK_LATE: |
| 427 | TIMESTAMP_NOEND(result); |
| 428 | break; |
| 429 | |
| 430 | case DTK_EARLY: |
| 431 | TIMESTAMP_NOBEGIN(result); |
| 432 | break; |
| 433 | |
| 434 | default: |
| 435 | elog(ERROR, "unexpected dtype %d while parsing timestamptz \"%s\"" , |
| 436 | dtype, str); |
| 437 | TIMESTAMP_NOEND(result); |
| 438 | } |
| 439 | |
| 440 | AdjustTimestampForTypmod(&result, typmod); |
| 441 | |
| 442 | PG_RETURN_TIMESTAMPTZ(result); |
| 443 | } |
| 444 | |
| 445 | /* |
| 446 | * Try to parse a timezone specification, and return its timezone offset value |
| 447 | * if it's acceptable. Otherwise, an error is thrown. |
| 448 | * |
| 449 | * Note: some code paths update tm->tm_isdst, and some don't; current callers |
| 450 | * don't care, so we don't bother being consistent. |
| 451 | */ |
| 452 | static int |
| 453 | parse_sane_timezone(struct pg_tm *tm, text *zone) |
| 454 | { |
| 455 | char tzname[TZ_STRLEN_MAX + 1]; |
| 456 | int rt; |
| 457 | int tz; |
| 458 | |
| 459 | text_to_cstring_buffer(zone, tzname, sizeof(tzname)); |
| 460 | |
| 461 | /* |
| 462 | * Look up the requested timezone. First we try to interpret it as a |
| 463 | * numeric timezone specification; if DecodeTimezone decides it doesn't |
| 464 | * like the format, we look in the timezone abbreviation table (to handle |
| 465 | * cases like "EST"), and if that also fails, we look in the timezone |
| 466 | * database (to handle cases like "America/New_York"). (This matches the |
| 467 | * order in which timestamp input checks the cases; it's important because |
| 468 | * the timezone database unwisely uses a few zone names that are identical |
| 469 | * to offset abbreviations.) |
| 470 | * |
| 471 | * Note pg_tzset happily parses numeric input that DecodeTimezone would |
| 472 | * reject. To avoid having it accept input that would otherwise be seen |
| 473 | * as invalid, it's enough to disallow having a digit in the first |
| 474 | * position of our input string. |
| 475 | */ |
| 476 | if (isdigit((unsigned char) *tzname)) |
| 477 | ereport(ERROR, |
| 478 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 479 | errmsg("invalid input syntax for type %s: \"%s\"" , |
| 480 | "numeric time zone" , tzname), |
| 481 | errhint("Numeric time zones must have \"-\" or \"+\" as first character." ))); |
| 482 | |
| 483 | rt = DecodeTimezone(tzname, &tz); |
| 484 | if (rt != 0) |
| 485 | { |
| 486 | char *lowzone; |
| 487 | int type, |
| 488 | val; |
| 489 | pg_tz *tzp; |
| 490 | |
| 491 | if (rt == DTERR_TZDISP_OVERFLOW) |
| 492 | ereport(ERROR, |
| 493 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 494 | errmsg("numeric time zone \"%s\" out of range" , tzname))); |
| 495 | else if (rt != DTERR_BAD_FORMAT) |
| 496 | ereport(ERROR, |
| 497 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 498 | errmsg("time zone \"%s\" not recognized" , tzname))); |
| 499 | |
| 500 | /* DecodeTimezoneAbbrev requires lowercase input */ |
| 501 | lowzone = downcase_truncate_identifier(tzname, |
| 502 | strlen(tzname), |
| 503 | false); |
| 504 | type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); |
| 505 | |
| 506 | if (type == TZ || type == DTZ) |
| 507 | { |
| 508 | /* fixed-offset abbreviation */ |
| 509 | tz = -val; |
| 510 | } |
| 511 | else if (type == DYNTZ) |
| 512 | { |
| 513 | /* dynamic-offset abbreviation, resolve using specified time */ |
| 514 | tz = DetermineTimeZoneAbbrevOffset(tm, tzname, tzp); |
| 515 | } |
| 516 | else |
| 517 | { |
| 518 | /* try it as a full zone name */ |
| 519 | tzp = pg_tzset(tzname); |
| 520 | if (tzp) |
| 521 | tz = DetermineTimeZoneOffset(tm, tzp); |
| 522 | else |
| 523 | ereport(ERROR, |
| 524 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 525 | errmsg("time zone \"%s\" not recognized" , tzname))); |
| 526 | } |
| 527 | } |
| 528 | |
| 529 | return tz; |
| 530 | } |
| 531 | |
| 532 | /* |
| 533 | * make_timestamp_internal |
| 534 | * workhorse for make_timestamp and make_timestamptz |
| 535 | */ |
| 536 | static Timestamp |
| 537 | make_timestamp_internal(int year, int month, int day, |
| 538 | int hour, int min, double sec) |
| 539 | { |
| 540 | struct pg_tm tm; |
| 541 | TimeOffset date; |
| 542 | TimeOffset time; |
| 543 | int dterr; |
| 544 | Timestamp result; |
| 545 | |
| 546 | tm.tm_year = year; |
| 547 | tm.tm_mon = month; |
| 548 | tm.tm_mday = day; |
| 549 | |
| 550 | /* |
| 551 | * Note: we'll reject zero or negative year values. Perhaps negatives |
| 552 | * should be allowed to represent BC years? |
| 553 | */ |
| 554 | dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm); |
| 555 | |
| 556 | if (dterr != 0) |
| 557 | ereport(ERROR, |
| 558 | (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), |
| 559 | errmsg("date field value out of range: %d-%02d-%02d" , |
| 560 | year, month, day))); |
| 561 | |
| 562 | if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday)) |
| 563 | ereport(ERROR, |
| 564 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 565 | errmsg("date out of range: %d-%02d-%02d" , |
| 566 | year, month, day))); |
| 567 | |
| 568 | date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE; |
| 569 | |
| 570 | /* |
| 571 | * This should match the checks in DecodeTimeOnly, except that since we're |
| 572 | * dealing with a float "sec" value, we also explicitly reject NaN. (An |
| 573 | * infinity input should get rejected by the range comparisons, but we |
| 574 | * can't be sure how those will treat a NaN.) |
| 575 | */ |
| 576 | if (hour < 0 || min < 0 || min > MINS_PER_HOUR - 1 || |
| 577 | isnan(sec) || |
| 578 | sec < 0 || sec > SECS_PER_MINUTE || |
| 579 | hour > HOURS_PER_DAY || |
| 580 | /* test for > 24:00:00 */ |
| 581 | (hour == HOURS_PER_DAY && (min > 0 || sec > 0))) |
| 582 | ereport(ERROR, |
| 583 | (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW), |
| 584 | errmsg("time field value out of range: %d:%02d:%02g" , |
| 585 | hour, min, sec))); |
| 586 | |
| 587 | /* This should match tm2time */ |
| 588 | time = (((hour * MINS_PER_HOUR + min) * SECS_PER_MINUTE) |
| 589 | * USECS_PER_SEC) + rint(sec * USECS_PER_SEC); |
| 590 | |
| 591 | result = date * USECS_PER_DAY + time; |
| 592 | /* check for major overflow */ |
| 593 | if ((result - time) / USECS_PER_DAY != date) |
| 594 | ereport(ERROR, |
| 595 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 596 | errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g" , |
| 597 | year, month, day, |
| 598 | hour, min, sec))); |
| 599 | |
| 600 | /* check for just-barely overflow (okay except time-of-day wraps) */ |
| 601 | /* caution: we want to allow 1999-12-31 24:00:00 */ |
| 602 | if ((result < 0 && date > 0) || |
| 603 | (result > 0 && date < -1)) |
| 604 | ereport(ERROR, |
| 605 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 606 | errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g" , |
| 607 | year, month, day, |
| 608 | hour, min, sec))); |
| 609 | |
| 610 | /* final range check catches just-out-of-range timestamps */ |
| 611 | if (!IS_VALID_TIMESTAMP(result)) |
| 612 | ereport(ERROR, |
| 613 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 614 | errmsg("timestamp out of range: %d-%02d-%02d %d:%02d:%02g" , |
| 615 | year, month, day, |
| 616 | hour, min, sec))); |
| 617 | |
| 618 | return result; |
| 619 | } |
| 620 | |
| 621 | /* |
| 622 | * make_timestamp() - timestamp constructor |
| 623 | */ |
| 624 | Datum |
| 625 | make_timestamp(PG_FUNCTION_ARGS) |
| 626 | { |
| 627 | int32 year = PG_GETARG_INT32(0); |
| 628 | int32 month = PG_GETARG_INT32(1); |
| 629 | int32 mday = PG_GETARG_INT32(2); |
| 630 | int32 hour = PG_GETARG_INT32(3); |
| 631 | int32 min = PG_GETARG_INT32(4); |
| 632 | float8 sec = PG_GETARG_FLOAT8(5); |
| 633 | Timestamp result; |
| 634 | |
| 635 | result = make_timestamp_internal(year, month, mday, |
| 636 | hour, min, sec); |
| 637 | |
| 638 | PG_RETURN_TIMESTAMP(result); |
| 639 | } |
| 640 | |
| 641 | /* |
| 642 | * make_timestamptz() - timestamp with time zone constructor |
| 643 | */ |
| 644 | Datum |
| 645 | make_timestamptz(PG_FUNCTION_ARGS) |
| 646 | { |
| 647 | int32 year = PG_GETARG_INT32(0); |
| 648 | int32 month = PG_GETARG_INT32(1); |
| 649 | int32 mday = PG_GETARG_INT32(2); |
| 650 | int32 hour = PG_GETARG_INT32(3); |
| 651 | int32 min = PG_GETARG_INT32(4); |
| 652 | float8 sec = PG_GETARG_FLOAT8(5); |
| 653 | Timestamp result; |
| 654 | |
| 655 | result = make_timestamp_internal(year, month, mday, |
| 656 | hour, min, sec); |
| 657 | |
| 658 | PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(result)); |
| 659 | } |
| 660 | |
| 661 | /* |
| 662 | * Construct a timestamp with time zone. |
| 663 | * As above, but the time zone is specified as seventh argument. |
| 664 | */ |
| 665 | Datum |
| 666 | make_timestamptz_at_timezone(PG_FUNCTION_ARGS) |
| 667 | { |
| 668 | int32 year = PG_GETARG_INT32(0); |
| 669 | int32 month = PG_GETARG_INT32(1); |
| 670 | int32 mday = PG_GETARG_INT32(2); |
| 671 | int32 hour = PG_GETARG_INT32(3); |
| 672 | int32 min = PG_GETARG_INT32(4); |
| 673 | float8 sec = PG_GETARG_FLOAT8(5); |
| 674 | text *zone = PG_GETARG_TEXT_PP(6); |
| 675 | TimestampTz result; |
| 676 | Timestamp timestamp; |
| 677 | struct pg_tm tt; |
| 678 | int tz; |
| 679 | fsec_t fsec; |
| 680 | |
| 681 | timestamp = make_timestamp_internal(year, month, mday, |
| 682 | hour, min, sec); |
| 683 | |
| 684 | if (timestamp2tm(timestamp, NULL, &tt, &fsec, NULL, NULL) != 0) |
| 685 | ereport(ERROR, |
| 686 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 687 | errmsg("timestamp out of range" ))); |
| 688 | |
| 689 | tz = parse_sane_timezone(&tt, zone); |
| 690 | |
| 691 | result = dt2local(timestamp, -tz); |
| 692 | |
| 693 | if (!IS_VALID_TIMESTAMP(result)) |
| 694 | ereport(ERROR, |
| 695 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 696 | errmsg("timestamp out of range" ))); |
| 697 | |
| 698 | PG_RETURN_TIMESTAMPTZ(result); |
| 699 | } |
| 700 | |
| 701 | /* |
| 702 | * to_timestamp(double precision) |
| 703 | * Convert UNIX epoch to timestamptz. |
| 704 | */ |
| 705 | Datum |
| 706 | float8_timestamptz(PG_FUNCTION_ARGS) |
| 707 | { |
| 708 | float8 seconds = PG_GETARG_FLOAT8(0); |
| 709 | TimestampTz result; |
| 710 | |
| 711 | /* Deal with NaN and infinite inputs ... */ |
| 712 | if (isnan(seconds)) |
| 713 | ereport(ERROR, |
| 714 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 715 | errmsg("timestamp cannot be NaN" ))); |
| 716 | |
| 717 | if (isinf(seconds)) |
| 718 | { |
| 719 | if (seconds < 0) |
| 720 | TIMESTAMP_NOBEGIN(result); |
| 721 | else |
| 722 | TIMESTAMP_NOEND(result); |
| 723 | } |
| 724 | else |
| 725 | { |
| 726 | /* Out of range? */ |
| 727 | if (seconds < |
| 728 | (float8) SECS_PER_DAY * (DATETIME_MIN_JULIAN - UNIX_EPOCH_JDATE) |
| 729 | || seconds >= |
| 730 | (float8) SECS_PER_DAY * (TIMESTAMP_END_JULIAN - UNIX_EPOCH_JDATE)) |
| 731 | ereport(ERROR, |
| 732 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 733 | errmsg("timestamp out of range: \"%g\"" , seconds))); |
| 734 | |
| 735 | /* Convert UNIX epoch to Postgres epoch */ |
| 736 | seconds -= ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); |
| 737 | |
| 738 | seconds = rint(seconds * USECS_PER_SEC); |
| 739 | result = (int64) seconds; |
| 740 | |
| 741 | /* Recheck in case roundoff produces something just out of range */ |
| 742 | if (!IS_VALID_TIMESTAMP(result)) |
| 743 | ereport(ERROR, |
| 744 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 745 | errmsg("timestamp out of range: \"%g\"" , |
| 746 | PG_GETARG_FLOAT8(0)))); |
| 747 | } |
| 748 | |
| 749 | PG_RETURN_TIMESTAMP(result); |
| 750 | } |
| 751 | |
| 752 | /* timestamptz_out() |
| 753 | * Convert a timestamp to external form. |
| 754 | */ |
| 755 | Datum |
| 756 | timestamptz_out(PG_FUNCTION_ARGS) |
| 757 | { |
| 758 | TimestampTz dt = PG_GETARG_TIMESTAMPTZ(0); |
| 759 | char *result; |
| 760 | int tz; |
| 761 | struct pg_tm tt, |
| 762 | *tm = &tt; |
| 763 | fsec_t fsec; |
| 764 | const char *tzn; |
| 765 | char buf[MAXDATELEN + 1]; |
| 766 | |
| 767 | if (TIMESTAMP_NOT_FINITE(dt)) |
| 768 | EncodeSpecialTimestamp(dt, buf); |
| 769 | else if (timestamp2tm(dt, &tz, tm, &fsec, &tzn, NULL) == 0) |
| 770 | EncodeDateTime(tm, fsec, true, tz, tzn, DateStyle, buf); |
| 771 | else |
| 772 | ereport(ERROR, |
| 773 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 774 | errmsg("timestamp out of range" ))); |
| 775 | |
| 776 | result = pstrdup(buf); |
| 777 | PG_RETURN_CSTRING(result); |
| 778 | } |
| 779 | |
| 780 | /* |
| 781 | * timestamptz_recv - converts external binary format to timestamptz |
| 782 | */ |
| 783 | Datum |
| 784 | timestamptz_recv(PG_FUNCTION_ARGS) |
| 785 | { |
| 786 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
| 787 | |
| 788 | #ifdef NOT_USED |
| 789 | Oid typelem = PG_GETARG_OID(1); |
| 790 | #endif |
| 791 | int32 typmod = PG_GETARG_INT32(2); |
| 792 | TimestampTz timestamp; |
| 793 | int tz; |
| 794 | struct pg_tm tt, |
| 795 | *tm = &tt; |
| 796 | fsec_t fsec; |
| 797 | |
| 798 | timestamp = (TimestampTz) pq_getmsgint64(buf); |
| 799 | |
| 800 | /* range check: see if timestamptz_out would like it */ |
| 801 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 802 | /* ok */ ; |
| 803 | else if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0 || |
| 804 | !IS_VALID_TIMESTAMP(timestamp)) |
| 805 | ereport(ERROR, |
| 806 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 807 | errmsg("timestamp out of range" ))); |
| 808 | |
| 809 | AdjustTimestampForTypmod(×tamp, typmod); |
| 810 | |
| 811 | PG_RETURN_TIMESTAMPTZ(timestamp); |
| 812 | } |
| 813 | |
| 814 | /* |
| 815 | * timestamptz_send - converts timestamptz to binary format |
| 816 | */ |
| 817 | Datum |
| 818 | timestamptz_send(PG_FUNCTION_ARGS) |
| 819 | { |
| 820 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); |
| 821 | StringInfoData buf; |
| 822 | |
| 823 | pq_begintypsend(&buf); |
| 824 | pq_sendint64(&buf, timestamp); |
| 825 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
| 826 | } |
| 827 | |
| 828 | Datum |
| 829 | timestamptztypmodin(PG_FUNCTION_ARGS) |
| 830 | { |
| 831 | ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); |
| 832 | |
| 833 | PG_RETURN_INT32(anytimestamp_typmodin(true, ta)); |
| 834 | } |
| 835 | |
| 836 | Datum |
| 837 | timestamptztypmodout(PG_FUNCTION_ARGS) |
| 838 | { |
| 839 | int32 typmod = PG_GETARG_INT32(0); |
| 840 | |
| 841 | PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod)); |
| 842 | } |
| 843 | |
| 844 | |
| 845 | /* timestamptz_scale() |
| 846 | * Adjust time type for specified scale factor. |
| 847 | * Used by PostgreSQL type system to stuff columns. |
| 848 | */ |
| 849 | Datum |
| 850 | timestamptz_scale(PG_FUNCTION_ARGS) |
| 851 | { |
| 852 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); |
| 853 | int32 typmod = PG_GETARG_INT32(1); |
| 854 | TimestampTz result; |
| 855 | |
| 856 | result = timestamp; |
| 857 | |
| 858 | AdjustTimestampForTypmod(&result, typmod); |
| 859 | |
| 860 | PG_RETURN_TIMESTAMPTZ(result); |
| 861 | } |
| 862 | |
| 863 | |
| 864 | /* interval_in() |
| 865 | * Convert a string to internal form. |
| 866 | * |
| 867 | * External format(s): |
| 868 | * Uses the generic date/time parsing and decoding routines. |
| 869 | */ |
| 870 | Datum |
| 871 | interval_in(PG_FUNCTION_ARGS) |
| 872 | { |
| 873 | char *str = PG_GETARG_CSTRING(0); |
| 874 | |
| 875 | #ifdef NOT_USED |
| 876 | Oid typelem = PG_GETARG_OID(1); |
| 877 | #endif |
| 878 | int32 typmod = PG_GETARG_INT32(2); |
| 879 | Interval *result; |
| 880 | fsec_t fsec; |
| 881 | struct pg_tm tt, |
| 882 | *tm = &tt; |
| 883 | int dtype; |
| 884 | int nf; |
| 885 | int range; |
| 886 | int dterr; |
| 887 | char *field[MAXDATEFIELDS]; |
| 888 | int ftype[MAXDATEFIELDS]; |
| 889 | char workbuf[256]; |
| 890 | |
| 891 | tm->tm_year = 0; |
| 892 | tm->tm_mon = 0; |
| 893 | tm->tm_mday = 0; |
| 894 | tm->tm_hour = 0; |
| 895 | tm->tm_min = 0; |
| 896 | tm->tm_sec = 0; |
| 897 | fsec = 0; |
| 898 | |
| 899 | if (typmod >= 0) |
| 900 | range = INTERVAL_RANGE(typmod); |
| 901 | else |
| 902 | range = INTERVAL_FULL_RANGE; |
| 903 | |
| 904 | dterr = ParseDateTime(str, workbuf, sizeof(workbuf), field, |
| 905 | ftype, MAXDATEFIELDS, &nf); |
| 906 | if (dterr == 0) |
| 907 | dterr = DecodeInterval(field, ftype, nf, range, |
| 908 | &dtype, tm, &fsec); |
| 909 | |
| 910 | /* if those functions think it's a bad format, try ISO8601 style */ |
| 911 | if (dterr == DTERR_BAD_FORMAT) |
| 912 | dterr = DecodeISO8601Interval(str, |
| 913 | &dtype, tm, &fsec); |
| 914 | |
| 915 | if (dterr != 0) |
| 916 | { |
| 917 | if (dterr == DTERR_FIELD_OVERFLOW) |
| 918 | dterr = DTERR_INTERVAL_OVERFLOW; |
| 919 | DateTimeParseError(dterr, str, "interval" ); |
| 920 | } |
| 921 | |
| 922 | result = (Interval *) palloc(sizeof(Interval)); |
| 923 | |
| 924 | switch (dtype) |
| 925 | { |
| 926 | case DTK_DELTA: |
| 927 | if (tm2interval(tm, fsec, result) != 0) |
| 928 | ereport(ERROR, |
| 929 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 930 | errmsg("interval out of range" ))); |
| 931 | break; |
| 932 | |
| 933 | default: |
| 934 | elog(ERROR, "unexpected dtype %d while parsing interval \"%s\"" , |
| 935 | dtype, str); |
| 936 | } |
| 937 | |
| 938 | AdjustIntervalForTypmod(result, typmod); |
| 939 | |
| 940 | PG_RETURN_INTERVAL_P(result); |
| 941 | } |
| 942 | |
| 943 | /* interval_out() |
| 944 | * Convert a time span to external form. |
| 945 | */ |
| 946 | Datum |
| 947 | interval_out(PG_FUNCTION_ARGS) |
| 948 | { |
| 949 | Interval *span = PG_GETARG_INTERVAL_P(0); |
| 950 | char *result; |
| 951 | struct pg_tm tt, |
| 952 | *tm = &tt; |
| 953 | fsec_t fsec; |
| 954 | char buf[MAXDATELEN + 1]; |
| 955 | |
| 956 | if (interval2tm(*span, tm, &fsec) != 0) |
| 957 | elog(ERROR, "could not convert interval to tm" ); |
| 958 | |
| 959 | EncodeInterval(tm, fsec, IntervalStyle, buf); |
| 960 | |
| 961 | result = pstrdup(buf); |
| 962 | PG_RETURN_CSTRING(result); |
| 963 | } |
| 964 | |
| 965 | /* |
| 966 | * interval_recv - converts external binary format to interval |
| 967 | */ |
| 968 | Datum |
| 969 | interval_recv(PG_FUNCTION_ARGS) |
| 970 | { |
| 971 | StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); |
| 972 | |
| 973 | #ifdef NOT_USED |
| 974 | Oid typelem = PG_GETARG_OID(1); |
| 975 | #endif |
| 976 | int32 typmod = PG_GETARG_INT32(2); |
| 977 | Interval *interval; |
| 978 | |
| 979 | interval = (Interval *) palloc(sizeof(Interval)); |
| 980 | |
| 981 | interval->time = pq_getmsgint64(buf); |
| 982 | interval->day = pq_getmsgint(buf, sizeof(interval->day)); |
| 983 | interval->month = pq_getmsgint(buf, sizeof(interval->month)); |
| 984 | |
| 985 | AdjustIntervalForTypmod(interval, typmod); |
| 986 | |
| 987 | PG_RETURN_INTERVAL_P(interval); |
| 988 | } |
| 989 | |
| 990 | /* |
| 991 | * interval_send - converts interval to binary format |
| 992 | */ |
| 993 | Datum |
| 994 | interval_send(PG_FUNCTION_ARGS) |
| 995 | { |
| 996 | Interval *interval = PG_GETARG_INTERVAL_P(0); |
| 997 | StringInfoData buf; |
| 998 | |
| 999 | pq_begintypsend(&buf); |
| 1000 | pq_sendint64(&buf, interval->time); |
| 1001 | pq_sendint32(&buf, interval->day); |
| 1002 | pq_sendint32(&buf, interval->month); |
| 1003 | PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
| 1004 | } |
| 1005 | |
| 1006 | /* |
| 1007 | * The interval typmod stores a "range" in its high 16 bits and a "precision" |
| 1008 | * in its low 16 bits. Both contribute to defining the resolution of the |
| 1009 | * type. Range addresses resolution granules larger than one second, and |
| 1010 | * precision specifies resolution below one second. This representation can |
| 1011 | * express all SQL standard resolutions, but we implement them all in terms of |
| 1012 | * truncating rightward from some position. Range is a bitmap of permitted |
| 1013 | * fields, but only the temporally-smallest such field is significant to our |
| 1014 | * calculations. Precision is a count of sub-second decimal places to retain. |
| 1015 | * Setting all bits (INTERVAL_FULL_PRECISION) gives the same truncation |
| 1016 | * semantics as choosing MAX_INTERVAL_PRECISION. |
| 1017 | */ |
| 1018 | Datum |
| 1019 | intervaltypmodin(PG_FUNCTION_ARGS) |
| 1020 | { |
| 1021 | ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); |
| 1022 | int32 *tl; |
| 1023 | int n; |
| 1024 | int32 typmod; |
| 1025 | |
| 1026 | tl = ArrayGetIntegerTypmods(ta, &n); |
| 1027 | |
| 1028 | /* |
| 1029 | * tl[0] - interval range (fields bitmask) tl[1] - precision (optional) |
| 1030 | * |
| 1031 | * Note we must validate tl[0] even though it's normally guaranteed |
| 1032 | * correct by the grammar --- consider SELECT 'foo'::"interval"(1000). |
| 1033 | */ |
| 1034 | if (n > 0) |
| 1035 | { |
| 1036 | switch (tl[0]) |
| 1037 | { |
| 1038 | case INTERVAL_MASK(YEAR): |
| 1039 | case INTERVAL_MASK(MONTH): |
| 1040 | case INTERVAL_MASK(DAY): |
| 1041 | case INTERVAL_MASK(HOUR): |
| 1042 | case INTERVAL_MASK(MINUTE): |
| 1043 | case INTERVAL_MASK(SECOND): |
| 1044 | case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): |
| 1045 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): |
| 1046 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): |
| 1047 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1048 | case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): |
| 1049 | case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1050 | case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1051 | case INTERVAL_FULL_RANGE: |
| 1052 | /* all OK */ |
| 1053 | break; |
| 1054 | default: |
| 1055 | ereport(ERROR, |
| 1056 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 1057 | errmsg("invalid INTERVAL type modifier" ))); |
| 1058 | } |
| 1059 | } |
| 1060 | |
| 1061 | if (n == 1) |
| 1062 | { |
| 1063 | if (tl[0] != INTERVAL_FULL_RANGE) |
| 1064 | typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]); |
| 1065 | else |
| 1066 | typmod = -1; |
| 1067 | } |
| 1068 | else if (n == 2) |
| 1069 | { |
| 1070 | if (tl[1] < 0) |
| 1071 | ereport(ERROR, |
| 1072 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 1073 | errmsg("INTERVAL(%d) precision must not be negative" , |
| 1074 | tl[1]))); |
| 1075 | if (tl[1] > MAX_INTERVAL_PRECISION) |
| 1076 | { |
| 1077 | ereport(WARNING, |
| 1078 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 1079 | errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d" , |
| 1080 | tl[1], MAX_INTERVAL_PRECISION))); |
| 1081 | typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]); |
| 1082 | } |
| 1083 | else |
| 1084 | typmod = INTERVAL_TYPMOD(tl[1], tl[0]); |
| 1085 | } |
| 1086 | else |
| 1087 | { |
| 1088 | ereport(ERROR, |
| 1089 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 1090 | errmsg("invalid INTERVAL type modifier" ))); |
| 1091 | typmod = 0; /* keep compiler quiet */ |
| 1092 | } |
| 1093 | |
| 1094 | PG_RETURN_INT32(typmod); |
| 1095 | } |
| 1096 | |
| 1097 | Datum |
| 1098 | intervaltypmodout(PG_FUNCTION_ARGS) |
| 1099 | { |
| 1100 | int32 typmod = PG_GETARG_INT32(0); |
| 1101 | char *res = (char *) palloc(64); |
| 1102 | int fields; |
| 1103 | int precision; |
| 1104 | const char *fieldstr; |
| 1105 | |
| 1106 | if (typmod < 0) |
| 1107 | { |
| 1108 | *res = '\0'; |
| 1109 | PG_RETURN_CSTRING(res); |
| 1110 | } |
| 1111 | |
| 1112 | fields = INTERVAL_RANGE(typmod); |
| 1113 | precision = INTERVAL_PRECISION(typmod); |
| 1114 | |
| 1115 | switch (fields) |
| 1116 | { |
| 1117 | case INTERVAL_MASK(YEAR): |
| 1118 | fieldstr = " year" ; |
| 1119 | break; |
| 1120 | case INTERVAL_MASK(MONTH): |
| 1121 | fieldstr = " month" ; |
| 1122 | break; |
| 1123 | case INTERVAL_MASK(DAY): |
| 1124 | fieldstr = " day" ; |
| 1125 | break; |
| 1126 | case INTERVAL_MASK(HOUR): |
| 1127 | fieldstr = " hour" ; |
| 1128 | break; |
| 1129 | case INTERVAL_MASK(MINUTE): |
| 1130 | fieldstr = " minute" ; |
| 1131 | break; |
| 1132 | case INTERVAL_MASK(SECOND): |
| 1133 | fieldstr = " second" ; |
| 1134 | break; |
| 1135 | case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): |
| 1136 | fieldstr = " year to month" ; |
| 1137 | break; |
| 1138 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): |
| 1139 | fieldstr = " day to hour" ; |
| 1140 | break; |
| 1141 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): |
| 1142 | fieldstr = " day to minute" ; |
| 1143 | break; |
| 1144 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1145 | fieldstr = " day to second" ; |
| 1146 | break; |
| 1147 | case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): |
| 1148 | fieldstr = " hour to minute" ; |
| 1149 | break; |
| 1150 | case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1151 | fieldstr = " hour to second" ; |
| 1152 | break; |
| 1153 | case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1154 | fieldstr = " minute to second" ; |
| 1155 | break; |
| 1156 | case INTERVAL_FULL_RANGE: |
| 1157 | fieldstr = "" ; |
| 1158 | break; |
| 1159 | default: |
| 1160 | elog(ERROR, "invalid INTERVAL typmod: 0x%x" , typmod); |
| 1161 | fieldstr = "" ; |
| 1162 | break; |
| 1163 | } |
| 1164 | |
| 1165 | if (precision != INTERVAL_FULL_PRECISION) |
| 1166 | snprintf(res, 64, "%s(%d)" , fieldstr, precision); |
| 1167 | else |
| 1168 | snprintf(res, 64, "%s" , fieldstr); |
| 1169 | |
| 1170 | PG_RETURN_CSTRING(res); |
| 1171 | } |
| 1172 | |
| 1173 | /* |
| 1174 | * Given an interval typmod value, return a code for the least-significant |
| 1175 | * field that the typmod allows to be nonzero, for instance given |
| 1176 | * INTERVAL DAY TO HOUR we want to identify "hour". |
| 1177 | * |
| 1178 | * The results should be ordered by field significance, which means |
| 1179 | * we can't use the dt.h macros YEAR etc, because for some odd reason |
| 1180 | * they aren't ordered that way. Instead, arbitrarily represent |
| 1181 | * SECOND = 0, MINUTE = 1, HOUR = 2, DAY = 3, MONTH = 4, YEAR = 5. |
| 1182 | */ |
| 1183 | static int |
| 1184 | intervaltypmodleastfield(int32 typmod) |
| 1185 | { |
| 1186 | if (typmod < 0) |
| 1187 | return 0; /* SECOND */ |
| 1188 | |
| 1189 | switch (INTERVAL_RANGE(typmod)) |
| 1190 | { |
| 1191 | case INTERVAL_MASK(YEAR): |
| 1192 | return 5; /* YEAR */ |
| 1193 | case INTERVAL_MASK(MONTH): |
| 1194 | return 4; /* MONTH */ |
| 1195 | case INTERVAL_MASK(DAY): |
| 1196 | return 3; /* DAY */ |
| 1197 | case INTERVAL_MASK(HOUR): |
| 1198 | return 2; /* HOUR */ |
| 1199 | case INTERVAL_MASK(MINUTE): |
| 1200 | return 1; /* MINUTE */ |
| 1201 | case INTERVAL_MASK(SECOND): |
| 1202 | return 0; /* SECOND */ |
| 1203 | case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): |
| 1204 | return 4; /* MONTH */ |
| 1205 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): |
| 1206 | return 2; /* HOUR */ |
| 1207 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): |
| 1208 | return 1; /* MINUTE */ |
| 1209 | case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1210 | return 0; /* SECOND */ |
| 1211 | case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): |
| 1212 | return 1; /* MINUTE */ |
| 1213 | case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1214 | return 0; /* SECOND */ |
| 1215 | case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): |
| 1216 | return 0; /* SECOND */ |
| 1217 | case INTERVAL_FULL_RANGE: |
| 1218 | return 0; /* SECOND */ |
| 1219 | default: |
| 1220 | elog(ERROR, "invalid INTERVAL typmod: 0x%x" , typmod); |
| 1221 | break; |
| 1222 | } |
| 1223 | return 0; /* can't get here, but keep compiler quiet */ |
| 1224 | } |
| 1225 | |
| 1226 | |
| 1227 | /* |
| 1228 | * interval_support() |
| 1229 | * |
| 1230 | * Planner support function for interval_scale(). |
| 1231 | * |
| 1232 | * Flatten superfluous calls to interval_scale(). The interval typmod is |
| 1233 | * complex to permit accepting and regurgitating all SQL standard variations. |
| 1234 | * For truncation purposes, it boils down to a single, simple granularity. |
| 1235 | */ |
| 1236 | Datum |
| 1237 | interval_support(PG_FUNCTION_ARGS) |
| 1238 | { |
| 1239 | Node *rawreq = (Node *) PG_GETARG_POINTER(0); |
| 1240 | Node *ret = NULL; |
| 1241 | |
| 1242 | if (IsA(rawreq, SupportRequestSimplify)) |
| 1243 | { |
| 1244 | SupportRequestSimplify *req = (SupportRequestSimplify *) rawreq; |
| 1245 | FuncExpr *expr = req->fcall; |
| 1246 | Node *typmod; |
| 1247 | |
| 1248 | Assert(list_length(expr->args) >= 2); |
| 1249 | |
| 1250 | typmod = (Node *) lsecond(expr->args); |
| 1251 | |
| 1252 | if (IsA(typmod, Const) &&!((Const *) typmod)->constisnull) |
| 1253 | { |
| 1254 | Node *source = (Node *) linitial(expr->args); |
| 1255 | int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue); |
| 1256 | bool noop; |
| 1257 | |
| 1258 | if (new_typmod < 0) |
| 1259 | noop = true; |
| 1260 | else |
| 1261 | { |
| 1262 | int32 old_typmod = exprTypmod(source); |
| 1263 | int old_least_field; |
| 1264 | int new_least_field; |
| 1265 | int old_precis; |
| 1266 | int new_precis; |
| 1267 | |
| 1268 | old_least_field = intervaltypmodleastfield(old_typmod); |
| 1269 | new_least_field = intervaltypmodleastfield(new_typmod); |
| 1270 | if (old_typmod < 0) |
| 1271 | old_precis = INTERVAL_FULL_PRECISION; |
| 1272 | else |
| 1273 | old_precis = INTERVAL_PRECISION(old_typmod); |
| 1274 | new_precis = INTERVAL_PRECISION(new_typmod); |
| 1275 | |
| 1276 | /* |
| 1277 | * Cast is a no-op if least field stays the same or decreases |
| 1278 | * while precision stays the same or increases. But |
| 1279 | * precision, which is to say, sub-second precision, only |
| 1280 | * affects ranges that include SECOND. |
| 1281 | */ |
| 1282 | noop = (new_least_field <= old_least_field) && |
| 1283 | (old_least_field > 0 /* SECOND */ || |
| 1284 | new_precis >= MAX_INTERVAL_PRECISION || |
| 1285 | new_precis >= old_precis); |
| 1286 | } |
| 1287 | if (noop) |
| 1288 | ret = relabel_to_typmod(source, new_typmod); |
| 1289 | } |
| 1290 | } |
| 1291 | |
| 1292 | PG_RETURN_POINTER(ret); |
| 1293 | } |
| 1294 | |
| 1295 | /* interval_scale() |
| 1296 | * Adjust interval type for specified fields. |
| 1297 | * Used by PostgreSQL type system to stuff columns. |
| 1298 | */ |
| 1299 | Datum |
| 1300 | interval_scale(PG_FUNCTION_ARGS) |
| 1301 | { |
| 1302 | Interval *interval = PG_GETARG_INTERVAL_P(0); |
| 1303 | int32 typmod = PG_GETARG_INT32(1); |
| 1304 | Interval *result; |
| 1305 | |
| 1306 | result = palloc(sizeof(Interval)); |
| 1307 | *result = *interval; |
| 1308 | |
| 1309 | AdjustIntervalForTypmod(result, typmod); |
| 1310 | |
| 1311 | PG_RETURN_INTERVAL_P(result); |
| 1312 | } |
| 1313 | |
| 1314 | /* |
| 1315 | * Adjust interval for specified precision, in both YEAR to SECOND |
| 1316 | * range and sub-second precision. |
| 1317 | */ |
| 1318 | static void |
| 1319 | AdjustIntervalForTypmod(Interval *interval, int32 typmod) |
| 1320 | { |
| 1321 | static const int64 IntervalScales[MAX_INTERVAL_PRECISION + 1] = { |
| 1322 | INT64CONST(1000000), |
| 1323 | INT64CONST(100000), |
| 1324 | INT64CONST(10000), |
| 1325 | INT64CONST(1000), |
| 1326 | INT64CONST(100), |
| 1327 | INT64CONST(10), |
| 1328 | INT64CONST(1) |
| 1329 | }; |
| 1330 | |
| 1331 | static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = { |
| 1332 | INT64CONST(500000), |
| 1333 | INT64CONST(50000), |
| 1334 | INT64CONST(5000), |
| 1335 | INT64CONST(500), |
| 1336 | INT64CONST(50), |
| 1337 | INT64CONST(5), |
| 1338 | INT64CONST(0) |
| 1339 | }; |
| 1340 | |
| 1341 | /* |
| 1342 | * Unspecified range and precision? Then not necessary to adjust. Setting |
| 1343 | * typmod to -1 is the convention for all data types. |
| 1344 | */ |
| 1345 | if (typmod >= 0) |
| 1346 | { |
| 1347 | int range = INTERVAL_RANGE(typmod); |
| 1348 | int precision = INTERVAL_PRECISION(typmod); |
| 1349 | |
| 1350 | /* |
| 1351 | * Our interpretation of intervals with a limited set of fields is |
| 1352 | * that fields to the right of the last one specified are zeroed out, |
| 1353 | * but those to the left of it remain valid. Thus for example there |
| 1354 | * is no operational difference between INTERVAL YEAR TO MONTH and |
| 1355 | * INTERVAL MONTH. In some cases we could meaningfully enforce that |
| 1356 | * higher-order fields are zero; for example INTERVAL DAY could reject |
| 1357 | * nonzero "month" field. However that seems a bit pointless when we |
| 1358 | * can't do it consistently. (We cannot enforce a range limit on the |
| 1359 | * highest expected field, since we do not have any equivalent of |
| 1360 | * SQL's <interval leading field precision>.) If we ever decide to |
| 1361 | * revisit this, interval_support will likely require adjusting. |
| 1362 | * |
| 1363 | * Note: before PG 8.4 we interpreted a limited set of fields as |
| 1364 | * actually causing a "modulo" operation on a given value, potentially |
| 1365 | * losing high-order as well as low-order information. But there is |
| 1366 | * no support for such behavior in the standard, and it seems fairly |
| 1367 | * undesirable on data consistency grounds anyway. Now we only |
| 1368 | * perform truncation or rounding of low-order fields. |
| 1369 | */ |
| 1370 | if (range == INTERVAL_FULL_RANGE) |
| 1371 | { |
| 1372 | /* Do nothing... */ |
| 1373 | } |
| 1374 | else if (range == INTERVAL_MASK(YEAR)) |
| 1375 | { |
| 1376 | interval->month = (interval->month / MONTHS_PER_YEAR) * MONTHS_PER_YEAR; |
| 1377 | interval->day = 0; |
| 1378 | interval->time = 0; |
| 1379 | } |
| 1380 | else if (range == INTERVAL_MASK(MONTH)) |
| 1381 | { |
| 1382 | interval->day = 0; |
| 1383 | interval->time = 0; |
| 1384 | } |
| 1385 | /* YEAR TO MONTH */ |
| 1386 | else if (range == (INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH))) |
| 1387 | { |
| 1388 | interval->day = 0; |
| 1389 | interval->time = 0; |
| 1390 | } |
| 1391 | else if (range == INTERVAL_MASK(DAY)) |
| 1392 | { |
| 1393 | interval->time = 0; |
| 1394 | } |
| 1395 | else if (range == INTERVAL_MASK(HOUR)) |
| 1396 | { |
| 1397 | interval->time = (interval->time / USECS_PER_HOUR) * |
| 1398 | USECS_PER_HOUR; |
| 1399 | } |
| 1400 | else if (range == INTERVAL_MASK(MINUTE)) |
| 1401 | { |
| 1402 | interval->time = (interval->time / USECS_PER_MINUTE) * |
| 1403 | USECS_PER_MINUTE; |
| 1404 | } |
| 1405 | else if (range == INTERVAL_MASK(SECOND)) |
| 1406 | { |
| 1407 | /* fractional-second rounding will be dealt with below */ |
| 1408 | } |
| 1409 | /* DAY TO HOUR */ |
| 1410 | else if (range == (INTERVAL_MASK(DAY) | |
| 1411 | INTERVAL_MASK(HOUR))) |
| 1412 | { |
| 1413 | interval->time = (interval->time / USECS_PER_HOUR) * |
| 1414 | USECS_PER_HOUR; |
| 1415 | } |
| 1416 | /* DAY TO MINUTE */ |
| 1417 | else if (range == (INTERVAL_MASK(DAY) | |
| 1418 | INTERVAL_MASK(HOUR) | |
| 1419 | INTERVAL_MASK(MINUTE))) |
| 1420 | { |
| 1421 | interval->time = (interval->time / USECS_PER_MINUTE) * |
| 1422 | USECS_PER_MINUTE; |
| 1423 | } |
| 1424 | /* DAY TO SECOND */ |
| 1425 | else if (range == (INTERVAL_MASK(DAY) | |
| 1426 | INTERVAL_MASK(HOUR) | |
| 1427 | INTERVAL_MASK(MINUTE) | |
| 1428 | INTERVAL_MASK(SECOND))) |
| 1429 | { |
| 1430 | /* fractional-second rounding will be dealt with below */ |
| 1431 | } |
| 1432 | /* HOUR TO MINUTE */ |
| 1433 | else if (range == (INTERVAL_MASK(HOUR) | |
| 1434 | INTERVAL_MASK(MINUTE))) |
| 1435 | { |
| 1436 | interval->time = (interval->time / USECS_PER_MINUTE) * |
| 1437 | USECS_PER_MINUTE; |
| 1438 | } |
| 1439 | /* HOUR TO SECOND */ |
| 1440 | else if (range == (INTERVAL_MASK(HOUR) | |
| 1441 | INTERVAL_MASK(MINUTE) | |
| 1442 | INTERVAL_MASK(SECOND))) |
| 1443 | { |
| 1444 | /* fractional-second rounding will be dealt with below */ |
| 1445 | } |
| 1446 | /* MINUTE TO SECOND */ |
| 1447 | else if (range == (INTERVAL_MASK(MINUTE) | |
| 1448 | INTERVAL_MASK(SECOND))) |
| 1449 | { |
| 1450 | /* fractional-second rounding will be dealt with below */ |
| 1451 | } |
| 1452 | else |
| 1453 | elog(ERROR, "unrecognized interval typmod: %d" , typmod); |
| 1454 | |
| 1455 | /* Need to adjust sub-second precision? */ |
| 1456 | if (precision != INTERVAL_FULL_PRECISION) |
| 1457 | { |
| 1458 | if (precision < 0 || precision > MAX_INTERVAL_PRECISION) |
| 1459 | ereport(ERROR, |
| 1460 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 1461 | errmsg("interval(%d) precision must be between %d and %d" , |
| 1462 | precision, 0, MAX_INTERVAL_PRECISION))); |
| 1463 | |
| 1464 | if (interval->time >= INT64CONST(0)) |
| 1465 | { |
| 1466 | interval->time = ((interval->time + |
| 1467 | IntervalOffsets[precision]) / |
| 1468 | IntervalScales[precision]) * |
| 1469 | IntervalScales[precision]; |
| 1470 | } |
| 1471 | else |
| 1472 | { |
| 1473 | interval->time = -(((-interval->time + |
| 1474 | IntervalOffsets[precision]) / |
| 1475 | IntervalScales[precision]) * |
| 1476 | IntervalScales[precision]); |
| 1477 | } |
| 1478 | } |
| 1479 | } |
| 1480 | } |
| 1481 | |
| 1482 | /* |
| 1483 | * make_interval - numeric Interval constructor |
| 1484 | */ |
| 1485 | Datum |
| 1486 | make_interval(PG_FUNCTION_ARGS) |
| 1487 | { |
| 1488 | int32 years = PG_GETARG_INT32(0); |
| 1489 | int32 months = PG_GETARG_INT32(1); |
| 1490 | int32 weeks = PG_GETARG_INT32(2); |
| 1491 | int32 days = PG_GETARG_INT32(3); |
| 1492 | int32 hours = PG_GETARG_INT32(4); |
| 1493 | int32 mins = PG_GETARG_INT32(5); |
| 1494 | double secs = PG_GETARG_FLOAT8(6); |
| 1495 | Interval *result; |
| 1496 | |
| 1497 | /* |
| 1498 | * Reject out-of-range inputs. We really ought to check the integer |
| 1499 | * inputs as well, but it's not entirely clear what limits to apply. |
| 1500 | */ |
| 1501 | if (isinf(secs) || isnan(secs)) |
| 1502 | ereport(ERROR, |
| 1503 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 1504 | errmsg("interval out of range" ))); |
| 1505 | |
| 1506 | result = (Interval *) palloc(sizeof(Interval)); |
| 1507 | result->month = years * MONTHS_PER_YEAR + months; |
| 1508 | result->day = weeks * 7 + days; |
| 1509 | |
| 1510 | secs = rint(secs * USECS_PER_SEC); |
| 1511 | result->time = hours * ((int64) SECS_PER_HOUR * USECS_PER_SEC) + |
| 1512 | mins * ((int64) SECS_PER_MINUTE * USECS_PER_SEC) + |
| 1513 | (int64) secs; |
| 1514 | |
| 1515 | PG_RETURN_INTERVAL_P(result); |
| 1516 | } |
| 1517 | |
| 1518 | /* EncodeSpecialTimestamp() |
| 1519 | * Convert reserved timestamp data type to string. |
| 1520 | */ |
| 1521 | void |
| 1522 | EncodeSpecialTimestamp(Timestamp dt, char *str) |
| 1523 | { |
| 1524 | if (TIMESTAMP_IS_NOBEGIN(dt)) |
| 1525 | strcpy(str, EARLY); |
| 1526 | else if (TIMESTAMP_IS_NOEND(dt)) |
| 1527 | strcpy(str, LATE); |
| 1528 | else /* shouldn't happen */ |
| 1529 | elog(ERROR, "invalid argument for EncodeSpecialTimestamp" ); |
| 1530 | } |
| 1531 | |
| 1532 | Datum |
| 1533 | now(PG_FUNCTION_ARGS) |
| 1534 | { |
| 1535 | PG_RETURN_TIMESTAMPTZ(GetCurrentTransactionStartTimestamp()); |
| 1536 | } |
| 1537 | |
| 1538 | Datum |
| 1539 | statement_timestamp(PG_FUNCTION_ARGS) |
| 1540 | { |
| 1541 | PG_RETURN_TIMESTAMPTZ(GetCurrentStatementStartTimestamp()); |
| 1542 | } |
| 1543 | |
| 1544 | Datum |
| 1545 | clock_timestamp(PG_FUNCTION_ARGS) |
| 1546 | { |
| 1547 | PG_RETURN_TIMESTAMPTZ(GetCurrentTimestamp()); |
| 1548 | } |
| 1549 | |
| 1550 | Datum |
| 1551 | pg_postmaster_start_time(PG_FUNCTION_ARGS) |
| 1552 | { |
| 1553 | PG_RETURN_TIMESTAMPTZ(PgStartTime); |
| 1554 | } |
| 1555 | |
| 1556 | Datum |
| 1557 | pg_conf_load_time(PG_FUNCTION_ARGS) |
| 1558 | { |
| 1559 | PG_RETURN_TIMESTAMPTZ(PgReloadTime); |
| 1560 | } |
| 1561 | |
| 1562 | /* |
| 1563 | * GetCurrentTimestamp -- get the current operating system time |
| 1564 | * |
| 1565 | * Result is in the form of a TimestampTz value, and is expressed to the |
| 1566 | * full precision of the gettimeofday() syscall |
| 1567 | */ |
| 1568 | TimestampTz |
| 1569 | GetCurrentTimestamp(void) |
| 1570 | { |
| 1571 | TimestampTz result; |
| 1572 | struct timeval tp; |
| 1573 | |
| 1574 | gettimeofday(&tp, NULL); |
| 1575 | |
| 1576 | result = (TimestampTz) tp.tv_sec - |
| 1577 | ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); |
| 1578 | result = (result * USECS_PER_SEC) + tp.tv_usec; |
| 1579 | |
| 1580 | return result; |
| 1581 | } |
| 1582 | |
| 1583 | /* |
| 1584 | * GetSQLCurrentTimestamp -- implements CURRENT_TIMESTAMP, CURRENT_TIMESTAMP(n) |
| 1585 | */ |
| 1586 | TimestampTz |
| 1587 | GetSQLCurrentTimestamp(int32 typmod) |
| 1588 | { |
| 1589 | TimestampTz ts; |
| 1590 | |
| 1591 | ts = GetCurrentTransactionStartTimestamp(); |
| 1592 | if (typmod >= 0) |
| 1593 | AdjustTimestampForTypmod(&ts, typmod); |
| 1594 | return ts; |
| 1595 | } |
| 1596 | |
| 1597 | /* |
| 1598 | * GetSQLLocalTimestamp -- implements LOCALTIMESTAMP, LOCALTIMESTAMP(n) |
| 1599 | */ |
| 1600 | Timestamp |
| 1601 | GetSQLLocalTimestamp(int32 typmod) |
| 1602 | { |
| 1603 | Timestamp ts; |
| 1604 | |
| 1605 | ts = timestamptz2timestamp(GetCurrentTransactionStartTimestamp()); |
| 1606 | if (typmod >= 0) |
| 1607 | AdjustTimestampForTypmod(&ts, typmod); |
| 1608 | return ts; |
| 1609 | } |
| 1610 | |
| 1611 | /* |
| 1612 | * timeofday(*) -- returns the current time as a text. |
| 1613 | */ |
| 1614 | Datum |
| 1615 | timeofday(PG_FUNCTION_ARGS) |
| 1616 | { |
| 1617 | struct timeval tp; |
| 1618 | char templ[128]; |
| 1619 | char buf[128]; |
| 1620 | pg_time_t tt; |
| 1621 | |
| 1622 | gettimeofday(&tp, NULL); |
| 1623 | tt = (pg_time_t) tp.tv_sec; |
| 1624 | pg_strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%06d %Y %Z" , |
| 1625 | pg_localtime(&tt, session_timezone)); |
| 1626 | snprintf(buf, sizeof(buf), templ, tp.tv_usec); |
| 1627 | |
| 1628 | PG_RETURN_TEXT_P(cstring_to_text(buf)); |
| 1629 | } |
| 1630 | |
| 1631 | /* |
| 1632 | * TimestampDifference -- convert the difference between two timestamps |
| 1633 | * into integer seconds and microseconds |
| 1634 | * |
| 1635 | * Both inputs must be ordinary finite timestamps (in current usage, |
| 1636 | * they'll be results from GetCurrentTimestamp()). |
| 1637 | * |
| 1638 | * We expect start_time <= stop_time. If not, we return zeros; for current |
| 1639 | * callers there is no need to be tense about which way division rounds on |
| 1640 | * negative inputs. |
| 1641 | */ |
| 1642 | void |
| 1643 | TimestampDifference(TimestampTz start_time, TimestampTz stop_time, |
| 1644 | long *secs, int *microsecs) |
| 1645 | { |
| 1646 | TimestampTz diff = stop_time - start_time; |
| 1647 | |
| 1648 | if (diff <= 0) |
| 1649 | { |
| 1650 | *secs = 0; |
| 1651 | *microsecs = 0; |
| 1652 | } |
| 1653 | else |
| 1654 | { |
| 1655 | *secs = (long) (diff / USECS_PER_SEC); |
| 1656 | *microsecs = (int) (diff % USECS_PER_SEC); |
| 1657 | } |
| 1658 | } |
| 1659 | |
| 1660 | /* |
| 1661 | * TimestampDifferenceExceeds -- report whether the difference between two |
| 1662 | * timestamps is >= a threshold (expressed in milliseconds) |
| 1663 | * |
| 1664 | * Both inputs must be ordinary finite timestamps (in current usage, |
| 1665 | * they'll be results from GetCurrentTimestamp()). |
| 1666 | */ |
| 1667 | bool |
| 1668 | TimestampDifferenceExceeds(TimestampTz start_time, |
| 1669 | TimestampTz stop_time, |
| 1670 | int msec) |
| 1671 | { |
| 1672 | TimestampTz diff = stop_time - start_time; |
| 1673 | |
| 1674 | return (diff >= msec * INT64CONST(1000)); |
| 1675 | } |
| 1676 | |
| 1677 | /* |
| 1678 | * Convert a time_t to TimestampTz. |
| 1679 | * |
| 1680 | * We do not use time_t internally in Postgres, but this is provided for use |
| 1681 | * by functions that need to interpret, say, a stat(2) result. |
| 1682 | * |
| 1683 | * To avoid having the function's ABI vary depending on the width of time_t, |
| 1684 | * we declare the argument as pg_time_t, which is cast-compatible with |
| 1685 | * time_t but always 64 bits wide (unless the platform has no 64-bit type). |
| 1686 | * This detail should be invisible to callers, at least at source code level. |
| 1687 | */ |
| 1688 | TimestampTz |
| 1689 | time_t_to_timestamptz(pg_time_t tm) |
| 1690 | { |
| 1691 | TimestampTz result; |
| 1692 | |
| 1693 | result = (TimestampTz) tm - |
| 1694 | ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY); |
| 1695 | result *= USECS_PER_SEC; |
| 1696 | |
| 1697 | return result; |
| 1698 | } |
| 1699 | |
| 1700 | /* |
| 1701 | * Convert a TimestampTz to time_t. |
| 1702 | * |
| 1703 | * This too is just marginally useful, but some places need it. |
| 1704 | * |
| 1705 | * To avoid having the function's ABI vary depending on the width of time_t, |
| 1706 | * we declare the result as pg_time_t, which is cast-compatible with |
| 1707 | * time_t but always 64 bits wide (unless the platform has no 64-bit type). |
| 1708 | * This detail should be invisible to callers, at least at source code level. |
| 1709 | */ |
| 1710 | pg_time_t |
| 1711 | timestamptz_to_time_t(TimestampTz t) |
| 1712 | { |
| 1713 | pg_time_t result; |
| 1714 | |
| 1715 | result = (pg_time_t) (t / USECS_PER_SEC + |
| 1716 | ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY)); |
| 1717 | |
| 1718 | return result; |
| 1719 | } |
| 1720 | |
| 1721 | /* |
| 1722 | * Produce a C-string representation of a TimestampTz. |
| 1723 | * |
| 1724 | * This is mostly for use in emitting messages. The primary difference |
| 1725 | * from timestamptz_out is that we force the output format to ISO. Note |
| 1726 | * also that the result is in a static buffer, not pstrdup'd. |
| 1727 | */ |
| 1728 | const char * |
| 1729 | timestamptz_to_str(TimestampTz t) |
| 1730 | { |
| 1731 | static char buf[MAXDATELEN + 1]; |
| 1732 | int tz; |
| 1733 | struct pg_tm tt, |
| 1734 | *tm = &tt; |
| 1735 | fsec_t fsec; |
| 1736 | const char *tzn; |
| 1737 | |
| 1738 | if (TIMESTAMP_NOT_FINITE(t)) |
| 1739 | EncodeSpecialTimestamp(t, buf); |
| 1740 | else if (timestamp2tm(t, &tz, tm, &fsec, &tzn, NULL) == 0) |
| 1741 | EncodeDateTime(tm, fsec, true, tz, tzn, USE_ISO_DATES, buf); |
| 1742 | else |
| 1743 | strlcpy(buf, "(timestamp out of range)" , sizeof(buf)); |
| 1744 | |
| 1745 | return buf; |
| 1746 | } |
| 1747 | |
| 1748 | |
| 1749 | void |
| 1750 | dt2time(Timestamp jd, int *hour, int *min, int *sec, fsec_t *fsec) |
| 1751 | { |
| 1752 | TimeOffset time; |
| 1753 | |
| 1754 | time = jd; |
| 1755 | |
| 1756 | *hour = time / USECS_PER_HOUR; |
| 1757 | time -= (*hour) * USECS_PER_HOUR; |
| 1758 | *min = time / USECS_PER_MINUTE; |
| 1759 | time -= (*min) * USECS_PER_MINUTE; |
| 1760 | *sec = time / USECS_PER_SEC; |
| 1761 | *fsec = time - (*sec * USECS_PER_SEC); |
| 1762 | } /* dt2time() */ |
| 1763 | |
| 1764 | |
| 1765 | /* |
| 1766 | * timestamp2tm() - Convert timestamp data type to POSIX time structure. |
| 1767 | * |
| 1768 | * Note that year is _not_ 1900-based, but is an explicit full value. |
| 1769 | * Also, month is one-based, _not_ zero-based. |
| 1770 | * Returns: |
| 1771 | * 0 on success |
| 1772 | * -1 on out of range |
| 1773 | * |
| 1774 | * If attimezone is NULL, the global timezone setting will be used. |
| 1775 | */ |
| 1776 | int |
| 1777 | timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, const char **tzn, pg_tz *attimezone) |
| 1778 | { |
| 1779 | Timestamp date; |
| 1780 | Timestamp time; |
| 1781 | pg_time_t utime; |
| 1782 | |
| 1783 | /* Use session timezone if caller asks for default */ |
| 1784 | if (attimezone == NULL) |
| 1785 | attimezone = session_timezone; |
| 1786 | |
| 1787 | time = dt; |
| 1788 | TMODULO(time, date, USECS_PER_DAY); |
| 1789 | |
| 1790 | if (time < INT64CONST(0)) |
| 1791 | { |
| 1792 | time += USECS_PER_DAY; |
| 1793 | date -= 1; |
| 1794 | } |
| 1795 | |
| 1796 | /* add offset to go from J2000 back to standard Julian date */ |
| 1797 | date += POSTGRES_EPOCH_JDATE; |
| 1798 | |
| 1799 | /* Julian day routine does not work for negative Julian days */ |
| 1800 | if (date < 0 || date > (Timestamp) INT_MAX) |
| 1801 | return -1; |
| 1802 | |
| 1803 | j2date((int) date, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); |
| 1804 | dt2time(time, &tm->tm_hour, &tm->tm_min, &tm->tm_sec, fsec); |
| 1805 | |
| 1806 | /* Done if no TZ conversion wanted */ |
| 1807 | if (tzp == NULL) |
| 1808 | { |
| 1809 | tm->tm_isdst = -1; |
| 1810 | tm->tm_gmtoff = 0; |
| 1811 | tm->tm_zone = NULL; |
| 1812 | if (tzn != NULL) |
| 1813 | *tzn = NULL; |
| 1814 | return 0; |
| 1815 | } |
| 1816 | |
| 1817 | /* |
| 1818 | * If the time falls within the range of pg_time_t, use pg_localtime() to |
| 1819 | * rotate to the local time zone. |
| 1820 | * |
| 1821 | * First, convert to an integral timestamp, avoiding possibly |
| 1822 | * platform-specific roundoff-in-wrong-direction errors, and adjust to |
| 1823 | * Unix epoch. Then see if we can convert to pg_time_t without loss. This |
| 1824 | * coding avoids hardwiring any assumptions about the width of pg_time_t, |
| 1825 | * so it should behave sanely on machines without int64. |
| 1826 | */ |
| 1827 | dt = (dt - *fsec) / USECS_PER_SEC + |
| 1828 | (POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY; |
| 1829 | utime = (pg_time_t) dt; |
| 1830 | if ((Timestamp) utime == dt) |
| 1831 | { |
| 1832 | struct pg_tm *tx = pg_localtime(&utime, attimezone); |
| 1833 | |
| 1834 | tm->tm_year = tx->tm_year + 1900; |
| 1835 | tm->tm_mon = tx->tm_mon + 1; |
| 1836 | tm->tm_mday = tx->tm_mday; |
| 1837 | tm->tm_hour = tx->tm_hour; |
| 1838 | tm->tm_min = tx->tm_min; |
| 1839 | tm->tm_sec = tx->tm_sec; |
| 1840 | tm->tm_isdst = tx->tm_isdst; |
| 1841 | tm->tm_gmtoff = tx->tm_gmtoff; |
| 1842 | tm->tm_zone = tx->tm_zone; |
| 1843 | *tzp = -tm->tm_gmtoff; |
| 1844 | if (tzn != NULL) |
| 1845 | *tzn = tm->tm_zone; |
| 1846 | } |
| 1847 | else |
| 1848 | { |
| 1849 | /* |
| 1850 | * When out of range of pg_time_t, treat as GMT |
| 1851 | */ |
| 1852 | *tzp = 0; |
| 1853 | /* Mark this as *no* time zone available */ |
| 1854 | tm->tm_isdst = -1; |
| 1855 | tm->tm_gmtoff = 0; |
| 1856 | tm->tm_zone = NULL; |
| 1857 | if (tzn != NULL) |
| 1858 | *tzn = NULL; |
| 1859 | } |
| 1860 | |
| 1861 | return 0; |
| 1862 | } |
| 1863 | |
| 1864 | |
| 1865 | /* tm2timestamp() |
| 1866 | * Convert a tm structure to a timestamp data type. |
| 1867 | * Note that year is _not_ 1900-based, but is an explicit full value. |
| 1868 | * Also, month is one-based, _not_ zero-based. |
| 1869 | * |
| 1870 | * Returns -1 on failure (value out of range). |
| 1871 | */ |
| 1872 | int |
| 1873 | tm2timestamp(struct pg_tm *tm, fsec_t fsec, int *tzp, Timestamp *result) |
| 1874 | { |
| 1875 | TimeOffset date; |
| 1876 | TimeOffset time; |
| 1877 | |
| 1878 | /* Prevent overflow in Julian-day routines */ |
| 1879 | if (!IS_VALID_JULIAN(tm->tm_year, tm->tm_mon, tm->tm_mday)) |
| 1880 | { |
| 1881 | *result = 0; /* keep compiler quiet */ |
| 1882 | return -1; |
| 1883 | } |
| 1884 | |
| 1885 | date = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; |
| 1886 | time = time2t(tm->tm_hour, tm->tm_min, tm->tm_sec, fsec); |
| 1887 | |
| 1888 | *result = date * USECS_PER_DAY + time; |
| 1889 | /* check for major overflow */ |
| 1890 | if ((*result - time) / USECS_PER_DAY != date) |
| 1891 | { |
| 1892 | *result = 0; /* keep compiler quiet */ |
| 1893 | return -1; |
| 1894 | } |
| 1895 | /* check for just-barely overflow (okay except time-of-day wraps) */ |
| 1896 | /* caution: we want to allow 1999-12-31 24:00:00 */ |
| 1897 | if ((*result < 0 && date > 0) || |
| 1898 | (*result > 0 && date < -1)) |
| 1899 | { |
| 1900 | *result = 0; /* keep compiler quiet */ |
| 1901 | return -1; |
| 1902 | } |
| 1903 | if (tzp != NULL) |
| 1904 | *result = dt2local(*result, -(*tzp)); |
| 1905 | |
| 1906 | /* final range check catches just-out-of-range timestamps */ |
| 1907 | if (!IS_VALID_TIMESTAMP(*result)) |
| 1908 | { |
| 1909 | *result = 0; /* keep compiler quiet */ |
| 1910 | return -1; |
| 1911 | } |
| 1912 | |
| 1913 | return 0; |
| 1914 | } |
| 1915 | |
| 1916 | |
| 1917 | /* interval2tm() |
| 1918 | * Convert an interval data type to a tm structure. |
| 1919 | */ |
| 1920 | int |
| 1921 | interval2tm(Interval span, struct pg_tm *tm, fsec_t *fsec) |
| 1922 | { |
| 1923 | TimeOffset time; |
| 1924 | TimeOffset tfrac; |
| 1925 | |
| 1926 | tm->tm_year = span.month / MONTHS_PER_YEAR; |
| 1927 | tm->tm_mon = span.month % MONTHS_PER_YEAR; |
| 1928 | tm->tm_mday = span.day; |
| 1929 | time = span.time; |
| 1930 | |
| 1931 | tfrac = time / USECS_PER_HOUR; |
| 1932 | time -= tfrac * USECS_PER_HOUR; |
| 1933 | tm->tm_hour = tfrac; |
| 1934 | if (!SAMESIGN(tm->tm_hour, tfrac)) |
| 1935 | ereport(ERROR, |
| 1936 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 1937 | errmsg("interval out of range" ))); |
| 1938 | tfrac = time / USECS_PER_MINUTE; |
| 1939 | time -= tfrac * USECS_PER_MINUTE; |
| 1940 | tm->tm_min = tfrac; |
| 1941 | tfrac = time / USECS_PER_SEC; |
| 1942 | *fsec = time - (tfrac * USECS_PER_SEC); |
| 1943 | tm->tm_sec = tfrac; |
| 1944 | |
| 1945 | return 0; |
| 1946 | } |
| 1947 | |
| 1948 | int |
| 1949 | tm2interval(struct pg_tm *tm, fsec_t fsec, Interval *span) |
| 1950 | { |
| 1951 | double total_months = (double) tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon; |
| 1952 | |
| 1953 | if (total_months > INT_MAX || total_months < INT_MIN) |
| 1954 | return -1; |
| 1955 | span->month = total_months; |
| 1956 | span->day = tm->tm_mday; |
| 1957 | span->time = (((((tm->tm_hour * INT64CONST(60)) + |
| 1958 | tm->tm_min) * INT64CONST(60)) + |
| 1959 | tm->tm_sec) * USECS_PER_SEC) + fsec; |
| 1960 | |
| 1961 | return 0; |
| 1962 | } |
| 1963 | |
| 1964 | static TimeOffset |
| 1965 | time2t(const int hour, const int min, const int sec, const fsec_t fsec) |
| 1966 | { |
| 1967 | return (((((hour * MINS_PER_HOUR) + min) * SECS_PER_MINUTE) + sec) * USECS_PER_SEC) + fsec; |
| 1968 | } |
| 1969 | |
| 1970 | static Timestamp |
| 1971 | dt2local(Timestamp dt, int tz) |
| 1972 | { |
| 1973 | dt -= (tz * USECS_PER_SEC); |
| 1974 | return dt; |
| 1975 | } |
| 1976 | |
| 1977 | |
| 1978 | /***************************************************************************** |
| 1979 | * PUBLIC ROUTINES * |
| 1980 | *****************************************************************************/ |
| 1981 | |
| 1982 | |
| 1983 | Datum |
| 1984 | timestamp_finite(PG_FUNCTION_ARGS) |
| 1985 | { |
| 1986 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 1987 | |
| 1988 | PG_RETURN_BOOL(!TIMESTAMP_NOT_FINITE(timestamp)); |
| 1989 | } |
| 1990 | |
| 1991 | Datum |
| 1992 | interval_finite(PG_FUNCTION_ARGS) |
| 1993 | { |
| 1994 | PG_RETURN_BOOL(true); |
| 1995 | } |
| 1996 | |
| 1997 | |
| 1998 | /*---------------------------------------------------------- |
| 1999 | * Relational operators for timestamp. |
| 2000 | *---------------------------------------------------------*/ |
| 2001 | |
| 2002 | void |
| 2003 | GetEpochTime(struct pg_tm *tm) |
| 2004 | { |
| 2005 | struct pg_tm *t0; |
| 2006 | pg_time_t epoch = 0; |
| 2007 | |
| 2008 | t0 = pg_gmtime(&epoch); |
| 2009 | |
| 2010 | if (t0 == NULL) |
| 2011 | elog(ERROR, "could not convert epoch to timestamp: %m" ); |
| 2012 | |
| 2013 | tm->tm_year = t0->tm_year; |
| 2014 | tm->tm_mon = t0->tm_mon; |
| 2015 | tm->tm_mday = t0->tm_mday; |
| 2016 | tm->tm_hour = t0->tm_hour; |
| 2017 | tm->tm_min = t0->tm_min; |
| 2018 | tm->tm_sec = t0->tm_sec; |
| 2019 | |
| 2020 | tm->tm_year += 1900; |
| 2021 | tm->tm_mon++; |
| 2022 | } |
| 2023 | |
| 2024 | Timestamp |
| 2025 | SetEpochTimestamp(void) |
| 2026 | { |
| 2027 | Timestamp dt; |
| 2028 | struct pg_tm tt, |
| 2029 | *tm = &tt; |
| 2030 | |
| 2031 | GetEpochTime(tm); |
| 2032 | /* we don't bother to test for failure ... */ |
| 2033 | tm2timestamp(tm, 0, NULL, &dt); |
| 2034 | |
| 2035 | return dt; |
| 2036 | } /* SetEpochTimestamp() */ |
| 2037 | |
| 2038 | /* |
| 2039 | * We are currently sharing some code between timestamp and timestamptz. |
| 2040 | * The comparison functions are among them. - thomas 2001-09-25 |
| 2041 | * |
| 2042 | * timestamp_relop - is timestamp1 relop timestamp2 |
| 2043 | */ |
| 2044 | int |
| 2045 | timestamp_cmp_internal(Timestamp dt1, Timestamp dt2) |
| 2046 | { |
| 2047 | return (dt1 < dt2) ? -1 : ((dt1 > dt2) ? 1 : 0); |
| 2048 | } |
| 2049 | |
| 2050 | Datum |
| 2051 | timestamp_eq(PG_FUNCTION_ARGS) |
| 2052 | { |
| 2053 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2054 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2055 | |
| 2056 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); |
| 2057 | } |
| 2058 | |
| 2059 | Datum |
| 2060 | timestamp_ne(PG_FUNCTION_ARGS) |
| 2061 | { |
| 2062 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2063 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2064 | |
| 2065 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); |
| 2066 | } |
| 2067 | |
| 2068 | Datum |
| 2069 | timestamp_lt(PG_FUNCTION_ARGS) |
| 2070 | { |
| 2071 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2072 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2073 | |
| 2074 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); |
| 2075 | } |
| 2076 | |
| 2077 | Datum |
| 2078 | timestamp_gt(PG_FUNCTION_ARGS) |
| 2079 | { |
| 2080 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2081 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2082 | |
| 2083 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); |
| 2084 | } |
| 2085 | |
| 2086 | Datum |
| 2087 | timestamp_le(PG_FUNCTION_ARGS) |
| 2088 | { |
| 2089 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2090 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2091 | |
| 2092 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); |
| 2093 | } |
| 2094 | |
| 2095 | Datum |
| 2096 | timestamp_ge(PG_FUNCTION_ARGS) |
| 2097 | { |
| 2098 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2099 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2100 | |
| 2101 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); |
| 2102 | } |
| 2103 | |
| 2104 | Datum |
| 2105 | timestamp_cmp(PG_FUNCTION_ARGS) |
| 2106 | { |
| 2107 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2108 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2109 | |
| 2110 | PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); |
| 2111 | } |
| 2112 | |
| 2113 | /* note: this is used for timestamptz also */ |
| 2114 | static int |
| 2115 | timestamp_fastcmp(Datum x, Datum y, SortSupport ssup) |
| 2116 | { |
| 2117 | Timestamp a = DatumGetTimestamp(x); |
| 2118 | Timestamp b = DatumGetTimestamp(y); |
| 2119 | |
| 2120 | return timestamp_cmp_internal(a, b); |
| 2121 | } |
| 2122 | |
| 2123 | Datum |
| 2124 | timestamp_sortsupport(PG_FUNCTION_ARGS) |
| 2125 | { |
| 2126 | SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0); |
| 2127 | |
| 2128 | ssup->comparator = timestamp_fastcmp; |
| 2129 | PG_RETURN_VOID(); |
| 2130 | } |
| 2131 | |
| 2132 | Datum |
| 2133 | timestamp_hash(PG_FUNCTION_ARGS) |
| 2134 | { |
| 2135 | return hashint8(fcinfo); |
| 2136 | } |
| 2137 | |
| 2138 | Datum |
| 2139 | timestamp_hash_extended(PG_FUNCTION_ARGS) |
| 2140 | { |
| 2141 | return hashint8extended(fcinfo); |
| 2142 | } |
| 2143 | |
| 2144 | /* |
| 2145 | * Cross-type comparison functions for timestamp vs timestamptz |
| 2146 | */ |
| 2147 | |
| 2148 | Datum |
| 2149 | timestamp_eq_timestamptz(PG_FUNCTION_ARGS) |
| 2150 | { |
| 2151 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2152 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2153 | TimestampTz dt1; |
| 2154 | |
| 2155 | dt1 = timestamp2timestamptz(timestampVal); |
| 2156 | |
| 2157 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); |
| 2158 | } |
| 2159 | |
| 2160 | Datum |
| 2161 | timestamp_ne_timestamptz(PG_FUNCTION_ARGS) |
| 2162 | { |
| 2163 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2164 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2165 | TimestampTz dt1; |
| 2166 | |
| 2167 | dt1 = timestamp2timestamptz(timestampVal); |
| 2168 | |
| 2169 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); |
| 2170 | } |
| 2171 | |
| 2172 | Datum |
| 2173 | timestamp_lt_timestamptz(PG_FUNCTION_ARGS) |
| 2174 | { |
| 2175 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2176 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2177 | TimestampTz dt1; |
| 2178 | |
| 2179 | dt1 = timestamp2timestamptz(timestampVal); |
| 2180 | |
| 2181 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); |
| 2182 | } |
| 2183 | |
| 2184 | Datum |
| 2185 | timestamp_gt_timestamptz(PG_FUNCTION_ARGS) |
| 2186 | { |
| 2187 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2188 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2189 | TimestampTz dt1; |
| 2190 | |
| 2191 | dt1 = timestamp2timestamptz(timestampVal); |
| 2192 | |
| 2193 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); |
| 2194 | } |
| 2195 | |
| 2196 | Datum |
| 2197 | timestamp_le_timestamptz(PG_FUNCTION_ARGS) |
| 2198 | { |
| 2199 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2200 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2201 | TimestampTz dt1; |
| 2202 | |
| 2203 | dt1 = timestamp2timestamptz(timestampVal); |
| 2204 | |
| 2205 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); |
| 2206 | } |
| 2207 | |
| 2208 | Datum |
| 2209 | timestamp_ge_timestamptz(PG_FUNCTION_ARGS) |
| 2210 | { |
| 2211 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2212 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2213 | TimestampTz dt1; |
| 2214 | |
| 2215 | dt1 = timestamp2timestamptz(timestampVal); |
| 2216 | |
| 2217 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); |
| 2218 | } |
| 2219 | |
| 2220 | Datum |
| 2221 | timestamp_cmp_timestamptz(PG_FUNCTION_ARGS) |
| 2222 | { |
| 2223 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(0); |
| 2224 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 2225 | TimestampTz dt1; |
| 2226 | |
| 2227 | dt1 = timestamp2timestamptz(timestampVal); |
| 2228 | |
| 2229 | PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); |
| 2230 | } |
| 2231 | |
| 2232 | Datum |
| 2233 | timestamptz_eq_timestamp(PG_FUNCTION_ARGS) |
| 2234 | { |
| 2235 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2236 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2237 | TimestampTz dt2; |
| 2238 | |
| 2239 | dt2 = timestamp2timestamptz(timestampVal); |
| 2240 | |
| 2241 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) == 0); |
| 2242 | } |
| 2243 | |
| 2244 | Datum |
| 2245 | timestamptz_ne_timestamp(PG_FUNCTION_ARGS) |
| 2246 | { |
| 2247 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2248 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2249 | TimestampTz dt2; |
| 2250 | |
| 2251 | dt2 = timestamp2timestamptz(timestampVal); |
| 2252 | |
| 2253 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) != 0); |
| 2254 | } |
| 2255 | |
| 2256 | Datum |
| 2257 | timestamptz_lt_timestamp(PG_FUNCTION_ARGS) |
| 2258 | { |
| 2259 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2260 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2261 | TimestampTz dt2; |
| 2262 | |
| 2263 | dt2 = timestamp2timestamptz(timestampVal); |
| 2264 | |
| 2265 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) < 0); |
| 2266 | } |
| 2267 | |
| 2268 | Datum |
| 2269 | timestamptz_gt_timestamp(PG_FUNCTION_ARGS) |
| 2270 | { |
| 2271 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2272 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2273 | TimestampTz dt2; |
| 2274 | |
| 2275 | dt2 = timestamp2timestamptz(timestampVal); |
| 2276 | |
| 2277 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) > 0); |
| 2278 | } |
| 2279 | |
| 2280 | Datum |
| 2281 | timestamptz_le_timestamp(PG_FUNCTION_ARGS) |
| 2282 | { |
| 2283 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2284 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2285 | TimestampTz dt2; |
| 2286 | |
| 2287 | dt2 = timestamp2timestamptz(timestampVal); |
| 2288 | |
| 2289 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) <= 0); |
| 2290 | } |
| 2291 | |
| 2292 | Datum |
| 2293 | timestamptz_ge_timestamp(PG_FUNCTION_ARGS) |
| 2294 | { |
| 2295 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2296 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2297 | TimestampTz dt2; |
| 2298 | |
| 2299 | dt2 = timestamp2timestamptz(timestampVal); |
| 2300 | |
| 2301 | PG_RETURN_BOOL(timestamp_cmp_internal(dt1, dt2) >= 0); |
| 2302 | } |
| 2303 | |
| 2304 | Datum |
| 2305 | timestamptz_cmp_timestamp(PG_FUNCTION_ARGS) |
| 2306 | { |
| 2307 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 2308 | Timestamp timestampVal = PG_GETARG_TIMESTAMP(1); |
| 2309 | TimestampTz dt2; |
| 2310 | |
| 2311 | dt2 = timestamp2timestamptz(timestampVal); |
| 2312 | |
| 2313 | PG_RETURN_INT32(timestamp_cmp_internal(dt1, dt2)); |
| 2314 | } |
| 2315 | |
| 2316 | |
| 2317 | /* |
| 2318 | * interval_relop - is interval1 relop interval2 |
| 2319 | * |
| 2320 | * Interval comparison is based on converting interval values to a linear |
| 2321 | * representation expressed in the units of the time field (microseconds, |
| 2322 | * in the case of integer timestamps) with days assumed to be always 24 hours |
| 2323 | * and months assumed to be always 30 days. To avoid overflow, we need a |
| 2324 | * wider-than-int64 datatype for the linear representation, so use INT128. |
| 2325 | */ |
| 2326 | |
| 2327 | static inline INT128 |
| 2328 | interval_cmp_value(const Interval *interval) |
| 2329 | { |
| 2330 | INT128 span; |
| 2331 | int64 dayfraction; |
| 2332 | int64 days; |
| 2333 | |
| 2334 | /* |
| 2335 | * Separate time field into days and dayfraction, then add the month and |
| 2336 | * day fields to the days part. We cannot overflow int64 days here. |
| 2337 | */ |
| 2338 | dayfraction = interval->time % USECS_PER_DAY; |
| 2339 | days = interval->time / USECS_PER_DAY; |
| 2340 | days += interval->month * INT64CONST(30); |
| 2341 | days += interval->day; |
| 2342 | |
| 2343 | /* Widen dayfraction to 128 bits */ |
| 2344 | span = int64_to_int128(dayfraction); |
| 2345 | |
| 2346 | /* Scale up days to microseconds, forming a 128-bit product */ |
| 2347 | int128_add_int64_mul_int64(&span, days, USECS_PER_DAY); |
| 2348 | |
| 2349 | return span; |
| 2350 | } |
| 2351 | |
| 2352 | static int |
| 2353 | interval_cmp_internal(Interval *interval1, Interval *interval2) |
| 2354 | { |
| 2355 | INT128 span1 = interval_cmp_value(interval1); |
| 2356 | INT128 span2 = interval_cmp_value(interval2); |
| 2357 | |
| 2358 | return int128_compare(span1, span2); |
| 2359 | } |
| 2360 | |
| 2361 | Datum |
| 2362 | interval_eq(PG_FUNCTION_ARGS) |
| 2363 | { |
| 2364 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2365 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2366 | |
| 2367 | PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) == 0); |
| 2368 | } |
| 2369 | |
| 2370 | Datum |
| 2371 | interval_ne(PG_FUNCTION_ARGS) |
| 2372 | { |
| 2373 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2374 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2375 | |
| 2376 | PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) != 0); |
| 2377 | } |
| 2378 | |
| 2379 | Datum |
| 2380 | interval_lt(PG_FUNCTION_ARGS) |
| 2381 | { |
| 2382 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2383 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2384 | |
| 2385 | PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) < 0); |
| 2386 | } |
| 2387 | |
| 2388 | Datum |
| 2389 | interval_gt(PG_FUNCTION_ARGS) |
| 2390 | { |
| 2391 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2392 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2393 | |
| 2394 | PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) > 0); |
| 2395 | } |
| 2396 | |
| 2397 | Datum |
| 2398 | interval_le(PG_FUNCTION_ARGS) |
| 2399 | { |
| 2400 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2401 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2402 | |
| 2403 | PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) <= 0); |
| 2404 | } |
| 2405 | |
| 2406 | Datum |
| 2407 | interval_ge(PG_FUNCTION_ARGS) |
| 2408 | { |
| 2409 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2410 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2411 | |
| 2412 | PG_RETURN_BOOL(interval_cmp_internal(interval1, interval2) >= 0); |
| 2413 | } |
| 2414 | |
| 2415 | Datum |
| 2416 | interval_cmp(PG_FUNCTION_ARGS) |
| 2417 | { |
| 2418 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 2419 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 2420 | |
| 2421 | PG_RETURN_INT32(interval_cmp_internal(interval1, interval2)); |
| 2422 | } |
| 2423 | |
| 2424 | /* |
| 2425 | * Hashing for intervals |
| 2426 | * |
| 2427 | * We must produce equal hashvals for values that interval_cmp_internal() |
| 2428 | * considers equal. So, compute the net span the same way it does, |
| 2429 | * and then hash that. |
| 2430 | */ |
| 2431 | Datum |
| 2432 | interval_hash(PG_FUNCTION_ARGS) |
| 2433 | { |
| 2434 | Interval *interval = PG_GETARG_INTERVAL_P(0); |
| 2435 | INT128 span = interval_cmp_value(interval); |
| 2436 | int64 span64; |
| 2437 | |
| 2438 | /* |
| 2439 | * Use only the least significant 64 bits for hashing. The upper 64 bits |
| 2440 | * seldom add any useful information, and besides we must do it like this |
| 2441 | * for compatibility with hashes calculated before use of INT128 was |
| 2442 | * introduced. |
| 2443 | */ |
| 2444 | span64 = int128_to_int64(span); |
| 2445 | |
| 2446 | return DirectFunctionCall1(hashint8, Int64GetDatumFast(span64)); |
| 2447 | } |
| 2448 | |
| 2449 | Datum |
| 2450 | interval_hash_extended(PG_FUNCTION_ARGS) |
| 2451 | { |
| 2452 | Interval *interval = PG_GETARG_INTERVAL_P(0); |
| 2453 | INT128 span = interval_cmp_value(interval); |
| 2454 | int64 span64; |
| 2455 | |
| 2456 | /* Same approach as interval_hash */ |
| 2457 | span64 = int128_to_int64(span); |
| 2458 | |
| 2459 | return DirectFunctionCall2(hashint8extended, Int64GetDatumFast(span64), |
| 2460 | PG_GETARG_DATUM(1)); |
| 2461 | } |
| 2462 | |
| 2463 | /* overlaps_timestamp() --- implements the SQL OVERLAPS operator. |
| 2464 | * |
| 2465 | * Algorithm is per SQL spec. This is much harder than you'd think |
| 2466 | * because the spec requires us to deliver a non-null answer in some cases |
| 2467 | * where some of the inputs are null. |
| 2468 | */ |
| 2469 | Datum |
| 2470 | overlaps_timestamp(PG_FUNCTION_ARGS) |
| 2471 | { |
| 2472 | /* |
| 2473 | * The arguments are Timestamps, but we leave them as generic Datums to |
| 2474 | * avoid unnecessary conversions between value and reference forms --- not |
| 2475 | * to mention possible dereferences of null pointers. |
| 2476 | */ |
| 2477 | Datum ts1 = PG_GETARG_DATUM(0); |
| 2478 | Datum te1 = PG_GETARG_DATUM(1); |
| 2479 | Datum ts2 = PG_GETARG_DATUM(2); |
| 2480 | Datum te2 = PG_GETARG_DATUM(3); |
| 2481 | bool ts1IsNull = PG_ARGISNULL(0); |
| 2482 | bool te1IsNull = PG_ARGISNULL(1); |
| 2483 | bool ts2IsNull = PG_ARGISNULL(2); |
| 2484 | bool te2IsNull = PG_ARGISNULL(3); |
| 2485 | |
| 2486 | #define TIMESTAMP_GT(t1,t2) \ |
| 2487 | DatumGetBool(DirectFunctionCall2(timestamp_gt,t1,t2)) |
| 2488 | #define TIMESTAMP_LT(t1,t2) \ |
| 2489 | DatumGetBool(DirectFunctionCall2(timestamp_lt,t1,t2)) |
| 2490 | |
| 2491 | /* |
| 2492 | * If both endpoints of interval 1 are null, the result is null (unknown). |
| 2493 | * If just one endpoint is null, take ts1 as the non-null one. Otherwise, |
| 2494 | * take ts1 as the lesser endpoint. |
| 2495 | */ |
| 2496 | if (ts1IsNull) |
| 2497 | { |
| 2498 | if (te1IsNull) |
| 2499 | PG_RETURN_NULL(); |
| 2500 | /* swap null for non-null */ |
| 2501 | ts1 = te1; |
| 2502 | te1IsNull = true; |
| 2503 | } |
| 2504 | else if (!te1IsNull) |
| 2505 | { |
| 2506 | if (TIMESTAMP_GT(ts1, te1)) |
| 2507 | { |
| 2508 | Datum tt = ts1; |
| 2509 | |
| 2510 | ts1 = te1; |
| 2511 | te1 = tt; |
| 2512 | } |
| 2513 | } |
| 2514 | |
| 2515 | /* Likewise for interval 2. */ |
| 2516 | if (ts2IsNull) |
| 2517 | { |
| 2518 | if (te2IsNull) |
| 2519 | PG_RETURN_NULL(); |
| 2520 | /* swap null for non-null */ |
| 2521 | ts2 = te2; |
| 2522 | te2IsNull = true; |
| 2523 | } |
| 2524 | else if (!te2IsNull) |
| 2525 | { |
| 2526 | if (TIMESTAMP_GT(ts2, te2)) |
| 2527 | { |
| 2528 | Datum tt = ts2; |
| 2529 | |
| 2530 | ts2 = te2; |
| 2531 | te2 = tt; |
| 2532 | } |
| 2533 | } |
| 2534 | |
| 2535 | /* |
| 2536 | * At this point neither ts1 nor ts2 is null, so we can consider three |
| 2537 | * cases: ts1 > ts2, ts1 < ts2, ts1 = ts2 |
| 2538 | */ |
| 2539 | if (TIMESTAMP_GT(ts1, ts2)) |
| 2540 | { |
| 2541 | /* |
| 2542 | * This case is ts1 < te2 OR te1 < te2, which may look redundant but |
| 2543 | * in the presence of nulls it's not quite completely so. |
| 2544 | */ |
| 2545 | if (te2IsNull) |
| 2546 | PG_RETURN_NULL(); |
| 2547 | if (TIMESTAMP_LT(ts1, te2)) |
| 2548 | PG_RETURN_BOOL(true); |
| 2549 | if (te1IsNull) |
| 2550 | PG_RETURN_NULL(); |
| 2551 | |
| 2552 | /* |
| 2553 | * If te1 is not null then we had ts1 <= te1 above, and we just found |
| 2554 | * ts1 >= te2, hence te1 >= te2. |
| 2555 | */ |
| 2556 | PG_RETURN_BOOL(false); |
| 2557 | } |
| 2558 | else if (TIMESTAMP_LT(ts1, ts2)) |
| 2559 | { |
| 2560 | /* This case is ts2 < te1 OR te2 < te1 */ |
| 2561 | if (te1IsNull) |
| 2562 | PG_RETURN_NULL(); |
| 2563 | if (TIMESTAMP_LT(ts2, te1)) |
| 2564 | PG_RETURN_BOOL(true); |
| 2565 | if (te2IsNull) |
| 2566 | PG_RETURN_NULL(); |
| 2567 | |
| 2568 | /* |
| 2569 | * If te2 is not null then we had ts2 <= te2 above, and we just found |
| 2570 | * ts2 >= te1, hence te2 >= te1. |
| 2571 | */ |
| 2572 | PG_RETURN_BOOL(false); |
| 2573 | } |
| 2574 | else |
| 2575 | { |
| 2576 | /* |
| 2577 | * For ts1 = ts2 the spec says te1 <> te2 OR te1 = te2, which is a |
| 2578 | * rather silly way of saying "true if both are non-null, else null". |
| 2579 | */ |
| 2580 | if (te1IsNull || te2IsNull) |
| 2581 | PG_RETURN_NULL(); |
| 2582 | PG_RETURN_BOOL(true); |
| 2583 | } |
| 2584 | |
| 2585 | #undef TIMESTAMP_GT |
| 2586 | #undef TIMESTAMP_LT |
| 2587 | } |
| 2588 | |
| 2589 | |
| 2590 | /*---------------------------------------------------------- |
| 2591 | * "Arithmetic" operators on date/times. |
| 2592 | *---------------------------------------------------------*/ |
| 2593 | |
| 2594 | Datum |
| 2595 | timestamp_smaller(PG_FUNCTION_ARGS) |
| 2596 | { |
| 2597 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2598 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2599 | Timestamp result; |
| 2600 | |
| 2601 | /* use timestamp_cmp_internal to be sure this agrees with comparisons */ |
| 2602 | if (timestamp_cmp_internal(dt1, dt2) < 0) |
| 2603 | result = dt1; |
| 2604 | else |
| 2605 | result = dt2; |
| 2606 | PG_RETURN_TIMESTAMP(result); |
| 2607 | } |
| 2608 | |
| 2609 | Datum |
| 2610 | timestamp_larger(PG_FUNCTION_ARGS) |
| 2611 | { |
| 2612 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2613 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2614 | Timestamp result; |
| 2615 | |
| 2616 | if (timestamp_cmp_internal(dt1, dt2) > 0) |
| 2617 | result = dt1; |
| 2618 | else |
| 2619 | result = dt2; |
| 2620 | PG_RETURN_TIMESTAMP(result); |
| 2621 | } |
| 2622 | |
| 2623 | |
| 2624 | Datum |
| 2625 | timestamp_mi(PG_FUNCTION_ARGS) |
| 2626 | { |
| 2627 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 2628 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 2629 | Interval *result; |
| 2630 | |
| 2631 | result = (Interval *) palloc(sizeof(Interval)); |
| 2632 | |
| 2633 | if (TIMESTAMP_NOT_FINITE(dt1) || TIMESTAMP_NOT_FINITE(dt2)) |
| 2634 | ereport(ERROR, |
| 2635 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2636 | errmsg("cannot subtract infinite timestamps" ))); |
| 2637 | |
| 2638 | result->time = dt1 - dt2; |
| 2639 | |
| 2640 | result->month = 0; |
| 2641 | result->day = 0; |
| 2642 | |
| 2643 | /*---------- |
| 2644 | * This is wrong, but removing it breaks a lot of regression tests. |
| 2645 | * For example: |
| 2646 | * |
| 2647 | * test=> SET timezone = 'EST5EDT'; |
| 2648 | * test=> SELECT |
| 2649 | * test-> ('2005-10-30 13:22:00-05'::timestamptz - |
| 2650 | * test(> '2005-10-29 13:22:00-04'::timestamptz); |
| 2651 | * ?column? |
| 2652 | * ---------------- |
| 2653 | * 1 day 01:00:00 |
| 2654 | * (1 row) |
| 2655 | * |
| 2656 | * so adding that to the first timestamp gets: |
| 2657 | * |
| 2658 | * test=> SELECT |
| 2659 | * test-> ('2005-10-29 13:22:00-04'::timestamptz + |
| 2660 | * test(> ('2005-10-30 13:22:00-05'::timestamptz - |
| 2661 | * test(> '2005-10-29 13:22:00-04'::timestamptz)) at time zone 'EST'; |
| 2662 | * timezone |
| 2663 | * -------------------- |
| 2664 | * 2005-10-30 14:22:00 |
| 2665 | * (1 row) |
| 2666 | *---------- |
| 2667 | */ |
| 2668 | result = DatumGetIntervalP(DirectFunctionCall1(interval_justify_hours, |
| 2669 | IntervalPGetDatum(result))); |
| 2670 | |
| 2671 | PG_RETURN_INTERVAL_P(result); |
| 2672 | } |
| 2673 | |
| 2674 | /* |
| 2675 | * interval_justify_interval() |
| 2676 | * |
| 2677 | * Adjust interval so 'month', 'day', and 'time' portions are within |
| 2678 | * customary bounds. Specifically: |
| 2679 | * |
| 2680 | * 0 <= abs(time) < 24 hours |
| 2681 | * 0 <= abs(day) < 30 days |
| 2682 | * |
| 2683 | * Also, the sign bit on all three fields is made equal, so either |
| 2684 | * all three fields are negative or all are positive. |
| 2685 | */ |
| 2686 | Datum |
| 2687 | interval_justify_interval(PG_FUNCTION_ARGS) |
| 2688 | { |
| 2689 | Interval *span = PG_GETARG_INTERVAL_P(0); |
| 2690 | Interval *result; |
| 2691 | TimeOffset wholeday; |
| 2692 | int32 wholemonth; |
| 2693 | |
| 2694 | result = (Interval *) palloc(sizeof(Interval)); |
| 2695 | result->month = span->month; |
| 2696 | result->day = span->day; |
| 2697 | result->time = span->time; |
| 2698 | |
| 2699 | TMODULO(result->time, wholeday, USECS_PER_DAY); |
| 2700 | result->day += wholeday; /* could overflow... */ |
| 2701 | |
| 2702 | wholemonth = result->day / DAYS_PER_MONTH; |
| 2703 | result->day -= wholemonth * DAYS_PER_MONTH; |
| 2704 | result->month += wholemonth; |
| 2705 | |
| 2706 | if (result->month > 0 && |
| 2707 | (result->day < 0 || (result->day == 0 && result->time < 0))) |
| 2708 | { |
| 2709 | result->day += DAYS_PER_MONTH; |
| 2710 | result->month--; |
| 2711 | } |
| 2712 | else if (result->month < 0 && |
| 2713 | (result->day > 0 || (result->day == 0 && result->time > 0))) |
| 2714 | { |
| 2715 | result->day -= DAYS_PER_MONTH; |
| 2716 | result->month++; |
| 2717 | } |
| 2718 | |
| 2719 | if (result->day > 0 && result->time < 0) |
| 2720 | { |
| 2721 | result->time += USECS_PER_DAY; |
| 2722 | result->day--; |
| 2723 | } |
| 2724 | else if (result->day < 0 && result->time > 0) |
| 2725 | { |
| 2726 | result->time -= USECS_PER_DAY; |
| 2727 | result->day++; |
| 2728 | } |
| 2729 | |
| 2730 | PG_RETURN_INTERVAL_P(result); |
| 2731 | } |
| 2732 | |
| 2733 | /* |
| 2734 | * interval_justify_hours() |
| 2735 | * |
| 2736 | * Adjust interval so 'time' contains less than a whole day, adding |
| 2737 | * the excess to 'day'. This is useful for |
| 2738 | * situations (such as non-TZ) where '1 day' = '24 hours' is valid, |
| 2739 | * e.g. interval subtraction and division. |
| 2740 | */ |
| 2741 | Datum |
| 2742 | interval_justify_hours(PG_FUNCTION_ARGS) |
| 2743 | { |
| 2744 | Interval *span = PG_GETARG_INTERVAL_P(0); |
| 2745 | Interval *result; |
| 2746 | TimeOffset wholeday; |
| 2747 | |
| 2748 | result = (Interval *) palloc(sizeof(Interval)); |
| 2749 | result->month = span->month; |
| 2750 | result->day = span->day; |
| 2751 | result->time = span->time; |
| 2752 | |
| 2753 | TMODULO(result->time, wholeday, USECS_PER_DAY); |
| 2754 | result->day += wholeday; /* could overflow... */ |
| 2755 | |
| 2756 | if (result->day > 0 && result->time < 0) |
| 2757 | { |
| 2758 | result->time += USECS_PER_DAY; |
| 2759 | result->day--; |
| 2760 | } |
| 2761 | else if (result->day < 0 && result->time > 0) |
| 2762 | { |
| 2763 | result->time -= USECS_PER_DAY; |
| 2764 | result->day++; |
| 2765 | } |
| 2766 | |
| 2767 | PG_RETURN_INTERVAL_P(result); |
| 2768 | } |
| 2769 | |
| 2770 | /* |
| 2771 | * interval_justify_days() |
| 2772 | * |
| 2773 | * Adjust interval so 'day' contains less than 30 days, adding |
| 2774 | * the excess to 'month'. |
| 2775 | */ |
| 2776 | Datum |
| 2777 | interval_justify_days(PG_FUNCTION_ARGS) |
| 2778 | { |
| 2779 | Interval *span = PG_GETARG_INTERVAL_P(0); |
| 2780 | Interval *result; |
| 2781 | int32 wholemonth; |
| 2782 | |
| 2783 | result = (Interval *) palloc(sizeof(Interval)); |
| 2784 | result->month = span->month; |
| 2785 | result->day = span->day; |
| 2786 | result->time = span->time; |
| 2787 | |
| 2788 | wholemonth = result->day / DAYS_PER_MONTH; |
| 2789 | result->day -= wholemonth * DAYS_PER_MONTH; |
| 2790 | result->month += wholemonth; |
| 2791 | |
| 2792 | if (result->month > 0 && result->day < 0) |
| 2793 | { |
| 2794 | result->day += DAYS_PER_MONTH; |
| 2795 | result->month--; |
| 2796 | } |
| 2797 | else if (result->month < 0 && result->day > 0) |
| 2798 | { |
| 2799 | result->day -= DAYS_PER_MONTH; |
| 2800 | result->month++; |
| 2801 | } |
| 2802 | |
| 2803 | PG_RETURN_INTERVAL_P(result); |
| 2804 | } |
| 2805 | |
| 2806 | /* timestamp_pl_interval() |
| 2807 | * Add an interval to a timestamp data type. |
| 2808 | * Note that interval has provisions for qualitative year/month and day |
| 2809 | * units, so try to do the right thing with them. |
| 2810 | * To add a month, increment the month, and use the same day of month. |
| 2811 | * Then, if the next month has fewer days, set the day of month |
| 2812 | * to the last day of month. |
| 2813 | * To add a day, increment the mday, and use the same time of day. |
| 2814 | * Lastly, add in the "quantitative time". |
| 2815 | */ |
| 2816 | Datum |
| 2817 | timestamp_pl_interval(PG_FUNCTION_ARGS) |
| 2818 | { |
| 2819 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 2820 | Interval *span = PG_GETARG_INTERVAL_P(1); |
| 2821 | Timestamp result; |
| 2822 | |
| 2823 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 2824 | result = timestamp; |
| 2825 | else |
| 2826 | { |
| 2827 | if (span->month != 0) |
| 2828 | { |
| 2829 | struct pg_tm tt, |
| 2830 | *tm = &tt; |
| 2831 | fsec_t fsec; |
| 2832 | |
| 2833 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 2834 | ereport(ERROR, |
| 2835 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2836 | errmsg("timestamp out of range" ))); |
| 2837 | |
| 2838 | tm->tm_mon += span->month; |
| 2839 | if (tm->tm_mon > MONTHS_PER_YEAR) |
| 2840 | { |
| 2841 | tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR; |
| 2842 | tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1; |
| 2843 | } |
| 2844 | else if (tm->tm_mon < 1) |
| 2845 | { |
| 2846 | tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1; |
| 2847 | tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR; |
| 2848 | } |
| 2849 | |
| 2850 | /* adjust for end of month boundary problems... */ |
| 2851 | if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) |
| 2852 | tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); |
| 2853 | |
| 2854 | if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) |
| 2855 | ereport(ERROR, |
| 2856 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2857 | errmsg("timestamp out of range" ))); |
| 2858 | } |
| 2859 | |
| 2860 | if (span->day != 0) |
| 2861 | { |
| 2862 | struct pg_tm tt, |
| 2863 | *tm = &tt; |
| 2864 | fsec_t fsec; |
| 2865 | int julian; |
| 2866 | |
| 2867 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 2868 | ereport(ERROR, |
| 2869 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2870 | errmsg("timestamp out of range" ))); |
| 2871 | |
| 2872 | /* Add days by converting to and from Julian */ |
| 2873 | julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day; |
| 2874 | j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); |
| 2875 | |
| 2876 | if (tm2timestamp(tm, fsec, NULL, ×tamp) != 0) |
| 2877 | ereport(ERROR, |
| 2878 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2879 | errmsg("timestamp out of range" ))); |
| 2880 | } |
| 2881 | |
| 2882 | timestamp += span->time; |
| 2883 | |
| 2884 | if (!IS_VALID_TIMESTAMP(timestamp)) |
| 2885 | ereport(ERROR, |
| 2886 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2887 | errmsg("timestamp out of range" ))); |
| 2888 | |
| 2889 | result = timestamp; |
| 2890 | } |
| 2891 | |
| 2892 | PG_RETURN_TIMESTAMP(result); |
| 2893 | } |
| 2894 | |
| 2895 | Datum |
| 2896 | timestamp_mi_interval(PG_FUNCTION_ARGS) |
| 2897 | { |
| 2898 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 2899 | Interval *span = PG_GETARG_INTERVAL_P(1); |
| 2900 | Interval tspan; |
| 2901 | |
| 2902 | tspan.month = -span->month; |
| 2903 | tspan.day = -span->day; |
| 2904 | tspan.time = -span->time; |
| 2905 | |
| 2906 | return DirectFunctionCall2(timestamp_pl_interval, |
| 2907 | TimestampGetDatum(timestamp), |
| 2908 | PointerGetDatum(&tspan)); |
| 2909 | } |
| 2910 | |
| 2911 | |
| 2912 | /* timestamptz_pl_interval() |
| 2913 | * Add an interval to a timestamp with time zone data type. |
| 2914 | * Note that interval has provisions for qualitative year/month |
| 2915 | * units, so try to do the right thing with them. |
| 2916 | * To add a month, increment the month, and use the same day of month. |
| 2917 | * Then, if the next month has fewer days, set the day of month |
| 2918 | * to the last day of month. |
| 2919 | * Lastly, add in the "quantitative time". |
| 2920 | */ |
| 2921 | Datum |
| 2922 | timestamptz_pl_interval(PG_FUNCTION_ARGS) |
| 2923 | { |
| 2924 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); |
| 2925 | Interval *span = PG_GETARG_INTERVAL_P(1); |
| 2926 | TimestampTz result; |
| 2927 | int tz; |
| 2928 | |
| 2929 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 2930 | result = timestamp; |
| 2931 | else |
| 2932 | { |
| 2933 | if (span->month != 0) |
| 2934 | { |
| 2935 | struct pg_tm tt, |
| 2936 | *tm = &tt; |
| 2937 | fsec_t fsec; |
| 2938 | |
| 2939 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
| 2940 | ereport(ERROR, |
| 2941 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2942 | errmsg("timestamp out of range" ))); |
| 2943 | |
| 2944 | tm->tm_mon += span->month; |
| 2945 | if (tm->tm_mon > MONTHS_PER_YEAR) |
| 2946 | { |
| 2947 | tm->tm_year += (tm->tm_mon - 1) / MONTHS_PER_YEAR; |
| 2948 | tm->tm_mon = ((tm->tm_mon - 1) % MONTHS_PER_YEAR) + 1; |
| 2949 | } |
| 2950 | else if (tm->tm_mon < 1) |
| 2951 | { |
| 2952 | tm->tm_year += tm->tm_mon / MONTHS_PER_YEAR - 1; |
| 2953 | tm->tm_mon = tm->tm_mon % MONTHS_PER_YEAR + MONTHS_PER_YEAR; |
| 2954 | } |
| 2955 | |
| 2956 | /* adjust for end of month boundary problems... */ |
| 2957 | if (tm->tm_mday > day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]) |
| 2958 | tm->tm_mday = (day_tab[isleap(tm->tm_year)][tm->tm_mon - 1]); |
| 2959 | |
| 2960 | tz = DetermineTimeZoneOffset(tm, session_timezone); |
| 2961 | |
| 2962 | if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0) |
| 2963 | ereport(ERROR, |
| 2964 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2965 | errmsg("timestamp out of range" ))); |
| 2966 | } |
| 2967 | |
| 2968 | if (span->day != 0) |
| 2969 | { |
| 2970 | struct pg_tm tt, |
| 2971 | *tm = &tt; |
| 2972 | fsec_t fsec; |
| 2973 | int julian; |
| 2974 | |
| 2975 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
| 2976 | ereport(ERROR, |
| 2977 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2978 | errmsg("timestamp out of range" ))); |
| 2979 | |
| 2980 | /* Add days by converting to and from Julian */ |
| 2981 | julian = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) + span->day; |
| 2982 | j2date(julian, &tm->tm_year, &tm->tm_mon, &tm->tm_mday); |
| 2983 | |
| 2984 | tz = DetermineTimeZoneOffset(tm, session_timezone); |
| 2985 | |
| 2986 | if (tm2timestamp(tm, fsec, &tz, ×tamp) != 0) |
| 2987 | ereport(ERROR, |
| 2988 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2989 | errmsg("timestamp out of range" ))); |
| 2990 | } |
| 2991 | |
| 2992 | timestamp += span->time; |
| 2993 | |
| 2994 | if (!IS_VALID_TIMESTAMP(timestamp)) |
| 2995 | ereport(ERROR, |
| 2996 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 2997 | errmsg("timestamp out of range" ))); |
| 2998 | |
| 2999 | result = timestamp; |
| 3000 | } |
| 3001 | |
| 3002 | PG_RETURN_TIMESTAMP(result); |
| 3003 | } |
| 3004 | |
| 3005 | Datum |
| 3006 | timestamptz_mi_interval(PG_FUNCTION_ARGS) |
| 3007 | { |
| 3008 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); |
| 3009 | Interval *span = PG_GETARG_INTERVAL_P(1); |
| 3010 | Interval tspan; |
| 3011 | |
| 3012 | tspan.month = -span->month; |
| 3013 | tspan.day = -span->day; |
| 3014 | tspan.time = -span->time; |
| 3015 | |
| 3016 | return DirectFunctionCall2(timestamptz_pl_interval, |
| 3017 | TimestampGetDatum(timestamp), |
| 3018 | PointerGetDatum(&tspan)); |
| 3019 | } |
| 3020 | |
| 3021 | |
| 3022 | Datum |
| 3023 | interval_um(PG_FUNCTION_ARGS) |
| 3024 | { |
| 3025 | Interval *interval = PG_GETARG_INTERVAL_P(0); |
| 3026 | Interval *result; |
| 3027 | |
| 3028 | result = (Interval *) palloc(sizeof(Interval)); |
| 3029 | |
| 3030 | result->time = -interval->time; |
| 3031 | /* overflow check copied from int4um */ |
| 3032 | if (interval->time != 0 && SAMESIGN(result->time, interval->time)) |
| 3033 | ereport(ERROR, |
| 3034 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3035 | errmsg("interval out of range" ))); |
| 3036 | result->day = -interval->day; |
| 3037 | if (interval->day != 0 && SAMESIGN(result->day, interval->day)) |
| 3038 | ereport(ERROR, |
| 3039 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3040 | errmsg("interval out of range" ))); |
| 3041 | result->month = -interval->month; |
| 3042 | if (interval->month != 0 && SAMESIGN(result->month, interval->month)) |
| 3043 | ereport(ERROR, |
| 3044 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3045 | errmsg("interval out of range" ))); |
| 3046 | |
| 3047 | PG_RETURN_INTERVAL_P(result); |
| 3048 | } |
| 3049 | |
| 3050 | |
| 3051 | Datum |
| 3052 | interval_smaller(PG_FUNCTION_ARGS) |
| 3053 | { |
| 3054 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 3055 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 3056 | Interval *result; |
| 3057 | |
| 3058 | /* use interval_cmp_internal to be sure this agrees with comparisons */ |
| 3059 | if (interval_cmp_internal(interval1, interval2) < 0) |
| 3060 | result = interval1; |
| 3061 | else |
| 3062 | result = interval2; |
| 3063 | PG_RETURN_INTERVAL_P(result); |
| 3064 | } |
| 3065 | |
| 3066 | Datum |
| 3067 | interval_larger(PG_FUNCTION_ARGS) |
| 3068 | { |
| 3069 | Interval *interval1 = PG_GETARG_INTERVAL_P(0); |
| 3070 | Interval *interval2 = PG_GETARG_INTERVAL_P(1); |
| 3071 | Interval *result; |
| 3072 | |
| 3073 | if (interval_cmp_internal(interval1, interval2) > 0) |
| 3074 | result = interval1; |
| 3075 | else |
| 3076 | result = interval2; |
| 3077 | PG_RETURN_INTERVAL_P(result); |
| 3078 | } |
| 3079 | |
| 3080 | Datum |
| 3081 | interval_pl(PG_FUNCTION_ARGS) |
| 3082 | { |
| 3083 | Interval *span1 = PG_GETARG_INTERVAL_P(0); |
| 3084 | Interval *span2 = PG_GETARG_INTERVAL_P(1); |
| 3085 | Interval *result; |
| 3086 | |
| 3087 | result = (Interval *) palloc(sizeof(Interval)); |
| 3088 | |
| 3089 | result->month = span1->month + span2->month; |
| 3090 | /* overflow check copied from int4pl */ |
| 3091 | if (SAMESIGN(span1->month, span2->month) && |
| 3092 | !SAMESIGN(result->month, span1->month)) |
| 3093 | ereport(ERROR, |
| 3094 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3095 | errmsg("interval out of range" ))); |
| 3096 | |
| 3097 | result->day = span1->day + span2->day; |
| 3098 | if (SAMESIGN(span1->day, span2->day) && |
| 3099 | !SAMESIGN(result->day, span1->day)) |
| 3100 | ereport(ERROR, |
| 3101 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3102 | errmsg("interval out of range" ))); |
| 3103 | |
| 3104 | result->time = span1->time + span2->time; |
| 3105 | if (SAMESIGN(span1->time, span2->time) && |
| 3106 | !SAMESIGN(result->time, span1->time)) |
| 3107 | ereport(ERROR, |
| 3108 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3109 | errmsg("interval out of range" ))); |
| 3110 | |
| 3111 | PG_RETURN_INTERVAL_P(result); |
| 3112 | } |
| 3113 | |
| 3114 | Datum |
| 3115 | interval_mi(PG_FUNCTION_ARGS) |
| 3116 | { |
| 3117 | Interval *span1 = PG_GETARG_INTERVAL_P(0); |
| 3118 | Interval *span2 = PG_GETARG_INTERVAL_P(1); |
| 3119 | Interval *result; |
| 3120 | |
| 3121 | result = (Interval *) palloc(sizeof(Interval)); |
| 3122 | |
| 3123 | result->month = span1->month - span2->month; |
| 3124 | /* overflow check copied from int4mi */ |
| 3125 | if (!SAMESIGN(span1->month, span2->month) && |
| 3126 | !SAMESIGN(result->month, span1->month)) |
| 3127 | ereport(ERROR, |
| 3128 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3129 | errmsg("interval out of range" ))); |
| 3130 | |
| 3131 | result->day = span1->day - span2->day; |
| 3132 | if (!SAMESIGN(span1->day, span2->day) && |
| 3133 | !SAMESIGN(result->day, span1->day)) |
| 3134 | ereport(ERROR, |
| 3135 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3136 | errmsg("interval out of range" ))); |
| 3137 | |
| 3138 | result->time = span1->time - span2->time; |
| 3139 | if (!SAMESIGN(span1->time, span2->time) && |
| 3140 | !SAMESIGN(result->time, span1->time)) |
| 3141 | ereport(ERROR, |
| 3142 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3143 | errmsg("interval out of range" ))); |
| 3144 | |
| 3145 | PG_RETURN_INTERVAL_P(result); |
| 3146 | } |
| 3147 | |
| 3148 | /* |
| 3149 | * There is no interval_abs(): it is unclear what value to return: |
| 3150 | * http://archives.postgresql.org/pgsql-general/2009-10/msg01031.php |
| 3151 | * http://archives.postgresql.org/pgsql-general/2009-11/msg00041.php |
| 3152 | */ |
| 3153 | |
| 3154 | Datum |
| 3155 | interval_mul(PG_FUNCTION_ARGS) |
| 3156 | { |
| 3157 | Interval *span = PG_GETARG_INTERVAL_P(0); |
| 3158 | float8 factor = PG_GETARG_FLOAT8(1); |
| 3159 | double month_remainder_days, |
| 3160 | sec_remainder, |
| 3161 | result_double; |
| 3162 | int32 orig_month = span->month, |
| 3163 | orig_day = span->day; |
| 3164 | Interval *result; |
| 3165 | |
| 3166 | result = (Interval *) palloc(sizeof(Interval)); |
| 3167 | |
| 3168 | result_double = span->month * factor; |
| 3169 | if (isnan(result_double) || |
| 3170 | result_double > INT_MAX || result_double < INT_MIN) |
| 3171 | ereport(ERROR, |
| 3172 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3173 | errmsg("interval out of range" ))); |
| 3174 | result->month = (int32) result_double; |
| 3175 | |
| 3176 | result_double = span->day * factor; |
| 3177 | if (isnan(result_double) || |
| 3178 | result_double > INT_MAX || result_double < INT_MIN) |
| 3179 | ereport(ERROR, |
| 3180 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3181 | errmsg("interval out of range" ))); |
| 3182 | result->day = (int32) result_double; |
| 3183 | |
| 3184 | /* |
| 3185 | * The above correctly handles the whole-number part of the month and day |
| 3186 | * products, but we have to do something with any fractional part |
| 3187 | * resulting when the factor is non-integral. We cascade the fractions |
| 3188 | * down to lower units using the conversion factors DAYS_PER_MONTH and |
| 3189 | * SECS_PER_DAY. Note we do NOT cascade up, since we are not forced to do |
| 3190 | * so by the representation. The user can choose to cascade up later, |
| 3191 | * using justify_hours and/or justify_days. |
| 3192 | */ |
| 3193 | |
| 3194 | /* |
| 3195 | * Fractional months full days into days. |
| 3196 | * |
| 3197 | * Floating point calculation are inherently imprecise, so these |
| 3198 | * calculations are crafted to produce the most reliable result possible. |
| 3199 | * TSROUND() is needed to more accurately produce whole numbers where |
| 3200 | * appropriate. |
| 3201 | */ |
| 3202 | month_remainder_days = (orig_month * factor - result->month) * DAYS_PER_MONTH; |
| 3203 | month_remainder_days = TSROUND(month_remainder_days); |
| 3204 | sec_remainder = (orig_day * factor - result->day + |
| 3205 | month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY; |
| 3206 | sec_remainder = TSROUND(sec_remainder); |
| 3207 | |
| 3208 | /* |
| 3209 | * Might have 24:00:00 hours due to rounding, or >24 hours because of time |
| 3210 | * cascade from months and days. It might still be >24 if the combination |
| 3211 | * of cascade and the seconds factor operation itself. |
| 3212 | */ |
| 3213 | if (Abs(sec_remainder) >= SECS_PER_DAY) |
| 3214 | { |
| 3215 | result->day += (int) (sec_remainder / SECS_PER_DAY); |
| 3216 | sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY; |
| 3217 | } |
| 3218 | |
| 3219 | /* cascade units down */ |
| 3220 | result->day += (int32) month_remainder_days; |
| 3221 | result_double = rint(span->time * factor + sec_remainder * USECS_PER_SEC); |
| 3222 | if (result_double > PG_INT64_MAX || result_double < PG_INT64_MIN) |
| 3223 | ereport(ERROR, |
| 3224 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3225 | errmsg("interval out of range" ))); |
| 3226 | result->time = (int64) result_double; |
| 3227 | |
| 3228 | PG_RETURN_INTERVAL_P(result); |
| 3229 | } |
| 3230 | |
| 3231 | Datum |
| 3232 | mul_d_interval(PG_FUNCTION_ARGS) |
| 3233 | { |
| 3234 | /* Args are float8 and Interval *, but leave them as generic Datum */ |
| 3235 | Datum factor = PG_GETARG_DATUM(0); |
| 3236 | Datum span = PG_GETARG_DATUM(1); |
| 3237 | |
| 3238 | return DirectFunctionCall2(interval_mul, span, factor); |
| 3239 | } |
| 3240 | |
| 3241 | Datum |
| 3242 | interval_div(PG_FUNCTION_ARGS) |
| 3243 | { |
| 3244 | Interval *span = PG_GETARG_INTERVAL_P(0); |
| 3245 | float8 factor = PG_GETARG_FLOAT8(1); |
| 3246 | double month_remainder_days, |
| 3247 | sec_remainder; |
| 3248 | int32 orig_month = span->month, |
| 3249 | orig_day = span->day; |
| 3250 | Interval *result; |
| 3251 | |
| 3252 | result = (Interval *) palloc(sizeof(Interval)); |
| 3253 | |
| 3254 | if (factor == 0.0) |
| 3255 | ereport(ERROR, |
| 3256 | (errcode(ERRCODE_DIVISION_BY_ZERO), |
| 3257 | errmsg("division by zero" ))); |
| 3258 | |
| 3259 | result->month = (int32) (span->month / factor); |
| 3260 | result->day = (int32) (span->day / factor); |
| 3261 | |
| 3262 | /* |
| 3263 | * Fractional months full days into days. See comment in interval_mul(). |
| 3264 | */ |
| 3265 | month_remainder_days = (orig_month / factor - result->month) * DAYS_PER_MONTH; |
| 3266 | month_remainder_days = TSROUND(month_remainder_days); |
| 3267 | sec_remainder = (orig_day / factor - result->day + |
| 3268 | month_remainder_days - (int) month_remainder_days) * SECS_PER_DAY; |
| 3269 | sec_remainder = TSROUND(sec_remainder); |
| 3270 | if (Abs(sec_remainder) >= SECS_PER_DAY) |
| 3271 | { |
| 3272 | result->day += (int) (sec_remainder / SECS_PER_DAY); |
| 3273 | sec_remainder -= (int) (sec_remainder / SECS_PER_DAY) * SECS_PER_DAY; |
| 3274 | } |
| 3275 | |
| 3276 | /* cascade units down */ |
| 3277 | result->day += (int32) month_remainder_days; |
| 3278 | result->time = rint(span->time / factor + sec_remainder * USECS_PER_SEC); |
| 3279 | |
| 3280 | PG_RETURN_INTERVAL_P(result); |
| 3281 | } |
| 3282 | |
| 3283 | |
| 3284 | /* |
| 3285 | * in_range support functions for timestamps and intervals. |
| 3286 | * |
| 3287 | * Per SQL spec, we support these with interval as the offset type. |
| 3288 | * The spec's restriction that the offset not be negative is a bit hard to |
| 3289 | * decipher for intervals, but we choose to interpret it the same as our |
| 3290 | * interval comparison operators would. |
| 3291 | */ |
| 3292 | |
| 3293 | Datum |
| 3294 | in_range_timestamptz_interval(PG_FUNCTION_ARGS) |
| 3295 | { |
| 3296 | TimestampTz val = PG_GETARG_TIMESTAMPTZ(0); |
| 3297 | TimestampTz base = PG_GETARG_TIMESTAMPTZ(1); |
| 3298 | Interval *offset = PG_GETARG_INTERVAL_P(2); |
| 3299 | bool sub = PG_GETARG_BOOL(3); |
| 3300 | bool less = PG_GETARG_BOOL(4); |
| 3301 | TimestampTz sum; |
| 3302 | |
| 3303 | if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0) |
| 3304 | ereport(ERROR, |
| 3305 | (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), |
| 3306 | errmsg("invalid preceding or following size in window function" ))); |
| 3307 | |
| 3308 | /* We don't currently bother to avoid overflow hazards here */ |
| 3309 | if (sub) |
| 3310 | sum = DatumGetTimestampTz(DirectFunctionCall2(timestamptz_mi_interval, |
| 3311 | TimestampTzGetDatum(base), |
| 3312 | IntervalPGetDatum(offset))); |
| 3313 | else |
| 3314 | sum = DatumGetTimestampTz(DirectFunctionCall2(timestamptz_pl_interval, |
| 3315 | TimestampTzGetDatum(base), |
| 3316 | IntervalPGetDatum(offset))); |
| 3317 | |
| 3318 | if (less) |
| 3319 | PG_RETURN_BOOL(val <= sum); |
| 3320 | else |
| 3321 | PG_RETURN_BOOL(val >= sum); |
| 3322 | } |
| 3323 | |
| 3324 | Datum |
| 3325 | in_range_timestamp_interval(PG_FUNCTION_ARGS) |
| 3326 | { |
| 3327 | Timestamp val = PG_GETARG_TIMESTAMP(0); |
| 3328 | Timestamp base = PG_GETARG_TIMESTAMP(1); |
| 3329 | Interval *offset = PG_GETARG_INTERVAL_P(2); |
| 3330 | bool sub = PG_GETARG_BOOL(3); |
| 3331 | bool less = PG_GETARG_BOOL(4); |
| 3332 | Timestamp sum; |
| 3333 | |
| 3334 | if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0) |
| 3335 | ereport(ERROR, |
| 3336 | (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), |
| 3337 | errmsg("invalid preceding or following size in window function" ))); |
| 3338 | |
| 3339 | /* We don't currently bother to avoid overflow hazards here */ |
| 3340 | if (sub) |
| 3341 | sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_mi_interval, |
| 3342 | TimestampGetDatum(base), |
| 3343 | IntervalPGetDatum(offset))); |
| 3344 | else |
| 3345 | sum = DatumGetTimestamp(DirectFunctionCall2(timestamp_pl_interval, |
| 3346 | TimestampGetDatum(base), |
| 3347 | IntervalPGetDatum(offset))); |
| 3348 | |
| 3349 | if (less) |
| 3350 | PG_RETURN_BOOL(val <= sum); |
| 3351 | else |
| 3352 | PG_RETURN_BOOL(val >= sum); |
| 3353 | } |
| 3354 | |
| 3355 | Datum |
| 3356 | in_range_interval_interval(PG_FUNCTION_ARGS) |
| 3357 | { |
| 3358 | Interval *val = PG_GETARG_INTERVAL_P(0); |
| 3359 | Interval *base = PG_GETARG_INTERVAL_P(1); |
| 3360 | Interval *offset = PG_GETARG_INTERVAL_P(2); |
| 3361 | bool sub = PG_GETARG_BOOL(3); |
| 3362 | bool less = PG_GETARG_BOOL(4); |
| 3363 | Interval *sum; |
| 3364 | |
| 3365 | if (int128_compare(interval_cmp_value(offset), int64_to_int128(0)) < 0) |
| 3366 | ereport(ERROR, |
| 3367 | (errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE), |
| 3368 | errmsg("invalid preceding or following size in window function" ))); |
| 3369 | |
| 3370 | /* We don't currently bother to avoid overflow hazards here */ |
| 3371 | if (sub) |
| 3372 | sum = DatumGetIntervalP(DirectFunctionCall2(interval_mi, |
| 3373 | IntervalPGetDatum(base), |
| 3374 | IntervalPGetDatum(offset))); |
| 3375 | else |
| 3376 | sum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, |
| 3377 | IntervalPGetDatum(base), |
| 3378 | IntervalPGetDatum(offset))); |
| 3379 | |
| 3380 | if (less) |
| 3381 | PG_RETURN_BOOL(interval_cmp_internal(val, sum) <= 0); |
| 3382 | else |
| 3383 | PG_RETURN_BOOL(interval_cmp_internal(val, sum) >= 0); |
| 3384 | } |
| 3385 | |
| 3386 | |
| 3387 | /* |
| 3388 | * interval_accum, interval_accum_inv, and interval_avg implement the |
| 3389 | * AVG(interval) aggregate. |
| 3390 | * |
| 3391 | * The transition datatype for this aggregate is a 2-element array of |
| 3392 | * intervals, where the first is the running sum and the second contains |
| 3393 | * the number of values so far in its 'time' field. This is a bit ugly |
| 3394 | * but it beats inventing a specialized datatype for the purpose. |
| 3395 | */ |
| 3396 | |
| 3397 | Datum |
| 3398 | interval_accum(PG_FUNCTION_ARGS) |
| 3399 | { |
| 3400 | ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); |
| 3401 | Interval *newval = PG_GETARG_INTERVAL_P(1); |
| 3402 | Datum *transdatums; |
| 3403 | int ndatums; |
| 3404 | Interval sumX, |
| 3405 | N; |
| 3406 | Interval *newsum; |
| 3407 | ArrayType *result; |
| 3408 | |
| 3409 | deconstruct_array(transarray, |
| 3410 | INTERVALOID, sizeof(Interval), false, 'd', |
| 3411 | &transdatums, NULL, &ndatums); |
| 3412 | if (ndatums != 2) |
| 3413 | elog(ERROR, "expected 2-element interval array" ); |
| 3414 | |
| 3415 | sumX = *(DatumGetIntervalP(transdatums[0])); |
| 3416 | N = *(DatumGetIntervalP(transdatums[1])); |
| 3417 | |
| 3418 | newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, |
| 3419 | IntervalPGetDatum(&sumX), |
| 3420 | IntervalPGetDatum(newval))); |
| 3421 | N.time += 1; |
| 3422 | |
| 3423 | transdatums[0] = IntervalPGetDatum(newsum); |
| 3424 | transdatums[1] = IntervalPGetDatum(&N); |
| 3425 | |
| 3426 | result = construct_array(transdatums, 2, |
| 3427 | INTERVALOID, sizeof(Interval), false, 'd'); |
| 3428 | |
| 3429 | PG_RETURN_ARRAYTYPE_P(result); |
| 3430 | } |
| 3431 | |
| 3432 | Datum |
| 3433 | interval_combine(PG_FUNCTION_ARGS) |
| 3434 | { |
| 3435 | ArrayType *transarray1 = PG_GETARG_ARRAYTYPE_P(0); |
| 3436 | ArrayType *transarray2 = PG_GETARG_ARRAYTYPE_P(1); |
| 3437 | Datum *transdatums1; |
| 3438 | Datum *transdatums2; |
| 3439 | int ndatums1; |
| 3440 | int ndatums2; |
| 3441 | Interval sum1, |
| 3442 | N1; |
| 3443 | Interval sum2, |
| 3444 | N2; |
| 3445 | |
| 3446 | Interval *newsum; |
| 3447 | ArrayType *result; |
| 3448 | |
| 3449 | deconstruct_array(transarray1, |
| 3450 | INTERVALOID, sizeof(Interval), false, 'd', |
| 3451 | &transdatums1, NULL, &ndatums1); |
| 3452 | if (ndatums1 != 2) |
| 3453 | elog(ERROR, "expected 2-element interval array" ); |
| 3454 | |
| 3455 | sum1 = *(DatumGetIntervalP(transdatums1[0])); |
| 3456 | N1 = *(DatumGetIntervalP(transdatums1[1])); |
| 3457 | |
| 3458 | deconstruct_array(transarray2, |
| 3459 | INTERVALOID, sizeof(Interval), false, 'd', |
| 3460 | &transdatums2, NULL, &ndatums2); |
| 3461 | if (ndatums2 != 2) |
| 3462 | elog(ERROR, "expected 2-element interval array" ); |
| 3463 | |
| 3464 | sum2 = *(DatumGetIntervalP(transdatums2[0])); |
| 3465 | N2 = *(DatumGetIntervalP(transdatums2[1])); |
| 3466 | |
| 3467 | newsum = DatumGetIntervalP(DirectFunctionCall2(interval_pl, |
| 3468 | IntervalPGetDatum(&sum1), |
| 3469 | IntervalPGetDatum(&sum2))); |
| 3470 | N1.time += N2.time; |
| 3471 | |
| 3472 | transdatums1[0] = IntervalPGetDatum(newsum); |
| 3473 | transdatums1[1] = IntervalPGetDatum(&N1); |
| 3474 | |
| 3475 | result = construct_array(transdatums1, 2, |
| 3476 | INTERVALOID, sizeof(Interval), false, 'd'); |
| 3477 | |
| 3478 | PG_RETURN_ARRAYTYPE_P(result); |
| 3479 | } |
| 3480 | |
| 3481 | Datum |
| 3482 | interval_accum_inv(PG_FUNCTION_ARGS) |
| 3483 | { |
| 3484 | ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); |
| 3485 | Interval *newval = PG_GETARG_INTERVAL_P(1); |
| 3486 | Datum *transdatums; |
| 3487 | int ndatums; |
| 3488 | Interval sumX, |
| 3489 | N; |
| 3490 | Interval *newsum; |
| 3491 | ArrayType *result; |
| 3492 | |
| 3493 | deconstruct_array(transarray, |
| 3494 | INTERVALOID, sizeof(Interval), false, 'd', |
| 3495 | &transdatums, NULL, &ndatums); |
| 3496 | if (ndatums != 2) |
| 3497 | elog(ERROR, "expected 2-element interval array" ); |
| 3498 | |
| 3499 | sumX = *(DatumGetIntervalP(transdatums[0])); |
| 3500 | N = *(DatumGetIntervalP(transdatums[1])); |
| 3501 | |
| 3502 | newsum = DatumGetIntervalP(DirectFunctionCall2(interval_mi, |
| 3503 | IntervalPGetDatum(&sumX), |
| 3504 | IntervalPGetDatum(newval))); |
| 3505 | N.time -= 1; |
| 3506 | |
| 3507 | transdatums[0] = IntervalPGetDatum(newsum); |
| 3508 | transdatums[1] = IntervalPGetDatum(&N); |
| 3509 | |
| 3510 | result = construct_array(transdatums, 2, |
| 3511 | INTERVALOID, sizeof(Interval), false, 'd'); |
| 3512 | |
| 3513 | PG_RETURN_ARRAYTYPE_P(result); |
| 3514 | } |
| 3515 | |
| 3516 | Datum |
| 3517 | interval_avg(PG_FUNCTION_ARGS) |
| 3518 | { |
| 3519 | ArrayType *transarray = PG_GETARG_ARRAYTYPE_P(0); |
| 3520 | Datum *transdatums; |
| 3521 | int ndatums; |
| 3522 | Interval sumX, |
| 3523 | N; |
| 3524 | |
| 3525 | deconstruct_array(transarray, |
| 3526 | INTERVALOID, sizeof(Interval), false, 'd', |
| 3527 | &transdatums, NULL, &ndatums); |
| 3528 | if (ndatums != 2) |
| 3529 | elog(ERROR, "expected 2-element interval array" ); |
| 3530 | |
| 3531 | sumX = *(DatumGetIntervalP(transdatums[0])); |
| 3532 | N = *(DatumGetIntervalP(transdatums[1])); |
| 3533 | |
| 3534 | /* SQL defines AVG of no values to be NULL */ |
| 3535 | if (N.time == 0) |
| 3536 | PG_RETURN_NULL(); |
| 3537 | |
| 3538 | return DirectFunctionCall2(interval_div, |
| 3539 | IntervalPGetDatum(&sumX), |
| 3540 | Float8GetDatum((double) N.time)); |
| 3541 | } |
| 3542 | |
| 3543 | |
| 3544 | /* timestamp_age() |
| 3545 | * Calculate time difference while retaining year/month fields. |
| 3546 | * Note that this does not result in an accurate absolute time span |
| 3547 | * since year and month are out of context once the arithmetic |
| 3548 | * is done. |
| 3549 | */ |
| 3550 | Datum |
| 3551 | timestamp_age(PG_FUNCTION_ARGS) |
| 3552 | { |
| 3553 | Timestamp dt1 = PG_GETARG_TIMESTAMP(0); |
| 3554 | Timestamp dt2 = PG_GETARG_TIMESTAMP(1); |
| 3555 | Interval *result; |
| 3556 | fsec_t fsec, |
| 3557 | fsec1, |
| 3558 | fsec2; |
| 3559 | struct pg_tm tt, |
| 3560 | *tm = &tt; |
| 3561 | struct pg_tm tt1, |
| 3562 | *tm1 = &tt1; |
| 3563 | struct pg_tm tt2, |
| 3564 | *tm2 = &tt2; |
| 3565 | |
| 3566 | result = (Interval *) palloc(sizeof(Interval)); |
| 3567 | |
| 3568 | if (timestamp2tm(dt1, NULL, tm1, &fsec1, NULL, NULL) == 0 && |
| 3569 | timestamp2tm(dt2, NULL, tm2, &fsec2, NULL, NULL) == 0) |
| 3570 | { |
| 3571 | /* form the symbolic difference */ |
| 3572 | fsec = fsec1 - fsec2; |
| 3573 | tm->tm_sec = tm1->tm_sec - tm2->tm_sec; |
| 3574 | tm->tm_min = tm1->tm_min - tm2->tm_min; |
| 3575 | tm->tm_hour = tm1->tm_hour - tm2->tm_hour; |
| 3576 | tm->tm_mday = tm1->tm_mday - tm2->tm_mday; |
| 3577 | tm->tm_mon = tm1->tm_mon - tm2->tm_mon; |
| 3578 | tm->tm_year = tm1->tm_year - tm2->tm_year; |
| 3579 | |
| 3580 | /* flip sign if necessary... */ |
| 3581 | if (dt1 < dt2) |
| 3582 | { |
| 3583 | fsec = -fsec; |
| 3584 | tm->tm_sec = -tm->tm_sec; |
| 3585 | tm->tm_min = -tm->tm_min; |
| 3586 | tm->tm_hour = -tm->tm_hour; |
| 3587 | tm->tm_mday = -tm->tm_mday; |
| 3588 | tm->tm_mon = -tm->tm_mon; |
| 3589 | tm->tm_year = -tm->tm_year; |
| 3590 | } |
| 3591 | |
| 3592 | /* propagate any negative fields into the next higher field */ |
| 3593 | while (fsec < 0) |
| 3594 | { |
| 3595 | fsec += USECS_PER_SEC; |
| 3596 | tm->tm_sec--; |
| 3597 | } |
| 3598 | |
| 3599 | while (tm->tm_sec < 0) |
| 3600 | { |
| 3601 | tm->tm_sec += SECS_PER_MINUTE; |
| 3602 | tm->tm_min--; |
| 3603 | } |
| 3604 | |
| 3605 | while (tm->tm_min < 0) |
| 3606 | { |
| 3607 | tm->tm_min += MINS_PER_HOUR; |
| 3608 | tm->tm_hour--; |
| 3609 | } |
| 3610 | |
| 3611 | while (tm->tm_hour < 0) |
| 3612 | { |
| 3613 | tm->tm_hour += HOURS_PER_DAY; |
| 3614 | tm->tm_mday--; |
| 3615 | } |
| 3616 | |
| 3617 | while (tm->tm_mday < 0) |
| 3618 | { |
| 3619 | if (dt1 < dt2) |
| 3620 | { |
| 3621 | tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1]; |
| 3622 | tm->tm_mon--; |
| 3623 | } |
| 3624 | else |
| 3625 | { |
| 3626 | tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1]; |
| 3627 | tm->tm_mon--; |
| 3628 | } |
| 3629 | } |
| 3630 | |
| 3631 | while (tm->tm_mon < 0) |
| 3632 | { |
| 3633 | tm->tm_mon += MONTHS_PER_YEAR; |
| 3634 | tm->tm_year--; |
| 3635 | } |
| 3636 | |
| 3637 | /* recover sign if necessary... */ |
| 3638 | if (dt1 < dt2) |
| 3639 | { |
| 3640 | fsec = -fsec; |
| 3641 | tm->tm_sec = -tm->tm_sec; |
| 3642 | tm->tm_min = -tm->tm_min; |
| 3643 | tm->tm_hour = -tm->tm_hour; |
| 3644 | tm->tm_mday = -tm->tm_mday; |
| 3645 | tm->tm_mon = -tm->tm_mon; |
| 3646 | tm->tm_year = -tm->tm_year; |
| 3647 | } |
| 3648 | |
| 3649 | if (tm2interval(tm, fsec, result) != 0) |
| 3650 | ereport(ERROR, |
| 3651 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3652 | errmsg("interval out of range" ))); |
| 3653 | } |
| 3654 | else |
| 3655 | ereport(ERROR, |
| 3656 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3657 | errmsg("timestamp out of range" ))); |
| 3658 | |
| 3659 | PG_RETURN_INTERVAL_P(result); |
| 3660 | } |
| 3661 | |
| 3662 | |
| 3663 | /* timestamptz_age() |
| 3664 | * Calculate time difference while retaining year/month fields. |
| 3665 | * Note that this does not result in an accurate absolute time span |
| 3666 | * since year and month are out of context once the arithmetic |
| 3667 | * is done. |
| 3668 | */ |
| 3669 | Datum |
| 3670 | timestamptz_age(PG_FUNCTION_ARGS) |
| 3671 | { |
| 3672 | TimestampTz dt1 = PG_GETARG_TIMESTAMPTZ(0); |
| 3673 | TimestampTz dt2 = PG_GETARG_TIMESTAMPTZ(1); |
| 3674 | Interval *result; |
| 3675 | fsec_t fsec, |
| 3676 | fsec1, |
| 3677 | fsec2; |
| 3678 | struct pg_tm tt, |
| 3679 | *tm = &tt; |
| 3680 | struct pg_tm tt1, |
| 3681 | *tm1 = &tt1; |
| 3682 | struct pg_tm tt2, |
| 3683 | *tm2 = &tt2; |
| 3684 | int tz1; |
| 3685 | int tz2; |
| 3686 | |
| 3687 | result = (Interval *) palloc(sizeof(Interval)); |
| 3688 | |
| 3689 | if (timestamp2tm(dt1, &tz1, tm1, &fsec1, NULL, NULL) == 0 && |
| 3690 | timestamp2tm(dt2, &tz2, tm2, &fsec2, NULL, NULL) == 0) |
| 3691 | { |
| 3692 | /* form the symbolic difference */ |
| 3693 | fsec = fsec1 - fsec2; |
| 3694 | tm->tm_sec = tm1->tm_sec - tm2->tm_sec; |
| 3695 | tm->tm_min = tm1->tm_min - tm2->tm_min; |
| 3696 | tm->tm_hour = tm1->tm_hour - tm2->tm_hour; |
| 3697 | tm->tm_mday = tm1->tm_mday - tm2->tm_mday; |
| 3698 | tm->tm_mon = tm1->tm_mon - tm2->tm_mon; |
| 3699 | tm->tm_year = tm1->tm_year - tm2->tm_year; |
| 3700 | |
| 3701 | /* flip sign if necessary... */ |
| 3702 | if (dt1 < dt2) |
| 3703 | { |
| 3704 | fsec = -fsec; |
| 3705 | tm->tm_sec = -tm->tm_sec; |
| 3706 | tm->tm_min = -tm->tm_min; |
| 3707 | tm->tm_hour = -tm->tm_hour; |
| 3708 | tm->tm_mday = -tm->tm_mday; |
| 3709 | tm->tm_mon = -tm->tm_mon; |
| 3710 | tm->tm_year = -tm->tm_year; |
| 3711 | } |
| 3712 | |
| 3713 | /* propagate any negative fields into the next higher field */ |
| 3714 | while (fsec < 0) |
| 3715 | { |
| 3716 | fsec += USECS_PER_SEC; |
| 3717 | tm->tm_sec--; |
| 3718 | } |
| 3719 | |
| 3720 | while (tm->tm_sec < 0) |
| 3721 | { |
| 3722 | tm->tm_sec += SECS_PER_MINUTE; |
| 3723 | tm->tm_min--; |
| 3724 | } |
| 3725 | |
| 3726 | while (tm->tm_min < 0) |
| 3727 | { |
| 3728 | tm->tm_min += MINS_PER_HOUR; |
| 3729 | tm->tm_hour--; |
| 3730 | } |
| 3731 | |
| 3732 | while (tm->tm_hour < 0) |
| 3733 | { |
| 3734 | tm->tm_hour += HOURS_PER_DAY; |
| 3735 | tm->tm_mday--; |
| 3736 | } |
| 3737 | |
| 3738 | while (tm->tm_mday < 0) |
| 3739 | { |
| 3740 | if (dt1 < dt2) |
| 3741 | { |
| 3742 | tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1]; |
| 3743 | tm->tm_mon--; |
| 3744 | } |
| 3745 | else |
| 3746 | { |
| 3747 | tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1]; |
| 3748 | tm->tm_mon--; |
| 3749 | } |
| 3750 | } |
| 3751 | |
| 3752 | while (tm->tm_mon < 0) |
| 3753 | { |
| 3754 | tm->tm_mon += MONTHS_PER_YEAR; |
| 3755 | tm->tm_year--; |
| 3756 | } |
| 3757 | |
| 3758 | /* |
| 3759 | * Note: we deliberately ignore any difference between tz1 and tz2. |
| 3760 | */ |
| 3761 | |
| 3762 | /* recover sign if necessary... */ |
| 3763 | if (dt1 < dt2) |
| 3764 | { |
| 3765 | fsec = -fsec; |
| 3766 | tm->tm_sec = -tm->tm_sec; |
| 3767 | tm->tm_min = -tm->tm_min; |
| 3768 | tm->tm_hour = -tm->tm_hour; |
| 3769 | tm->tm_mday = -tm->tm_mday; |
| 3770 | tm->tm_mon = -tm->tm_mon; |
| 3771 | tm->tm_year = -tm->tm_year; |
| 3772 | } |
| 3773 | |
| 3774 | if (tm2interval(tm, fsec, result) != 0) |
| 3775 | ereport(ERROR, |
| 3776 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3777 | errmsg("interval out of range" ))); |
| 3778 | } |
| 3779 | else |
| 3780 | ereport(ERROR, |
| 3781 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3782 | errmsg("timestamp out of range" ))); |
| 3783 | |
| 3784 | PG_RETURN_INTERVAL_P(result); |
| 3785 | } |
| 3786 | |
| 3787 | |
| 3788 | /*---------------------------------------------------------- |
| 3789 | * Conversion operators. |
| 3790 | *---------------------------------------------------------*/ |
| 3791 | |
| 3792 | |
| 3793 | /* timestamp_trunc() |
| 3794 | * Truncate timestamp to specified units. |
| 3795 | */ |
| 3796 | Datum |
| 3797 | timestamp_trunc(PG_FUNCTION_ARGS) |
| 3798 | { |
| 3799 | text *units = PG_GETARG_TEXT_PP(0); |
| 3800 | Timestamp timestamp = PG_GETARG_TIMESTAMP(1); |
| 3801 | Timestamp result; |
| 3802 | int type, |
| 3803 | val; |
| 3804 | char *lowunits; |
| 3805 | fsec_t fsec; |
| 3806 | struct pg_tm tt, |
| 3807 | *tm = &tt; |
| 3808 | |
| 3809 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 3810 | PG_RETURN_TIMESTAMP(timestamp); |
| 3811 | |
| 3812 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
| 3813 | VARSIZE_ANY_EXHDR(units), |
| 3814 | false); |
| 3815 | |
| 3816 | type = DecodeUnits(0, lowunits, &val); |
| 3817 | |
| 3818 | if (type == UNITS) |
| 3819 | { |
| 3820 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 3821 | ereport(ERROR, |
| 3822 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3823 | errmsg("timestamp out of range" ))); |
| 3824 | |
| 3825 | switch (val) |
| 3826 | { |
| 3827 | case DTK_WEEK: |
| 3828 | { |
| 3829 | int woy; |
| 3830 | |
| 3831 | woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 3832 | |
| 3833 | /* |
| 3834 | * If it is week 52/53 and the month is January, then the |
| 3835 | * week must belong to the previous year. Also, some |
| 3836 | * December dates belong to the next year. |
| 3837 | */ |
| 3838 | if (woy >= 52 && tm->tm_mon == 1) |
| 3839 | --tm->tm_year; |
| 3840 | if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR) |
| 3841 | ++tm->tm_year; |
| 3842 | isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); |
| 3843 | tm->tm_hour = 0; |
| 3844 | tm->tm_min = 0; |
| 3845 | tm->tm_sec = 0; |
| 3846 | fsec = 0; |
| 3847 | break; |
| 3848 | } |
| 3849 | case DTK_MILLENNIUM: |
| 3850 | /* see comments in timestamptz_trunc */ |
| 3851 | if (tm->tm_year > 0) |
| 3852 | tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999; |
| 3853 | else |
| 3854 | tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1; |
| 3855 | /* FALL THRU */ |
| 3856 | case DTK_CENTURY: |
| 3857 | /* see comments in timestamptz_trunc */ |
| 3858 | if (tm->tm_year > 0) |
| 3859 | tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99; |
| 3860 | else |
| 3861 | tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1; |
| 3862 | /* FALL THRU */ |
| 3863 | case DTK_DECADE: |
| 3864 | /* see comments in timestamptz_trunc */ |
| 3865 | if (val != DTK_MILLENNIUM && val != DTK_CENTURY) |
| 3866 | { |
| 3867 | if (tm->tm_year > 0) |
| 3868 | tm->tm_year = (tm->tm_year / 10) * 10; |
| 3869 | else |
| 3870 | tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10; |
| 3871 | } |
| 3872 | /* FALL THRU */ |
| 3873 | case DTK_YEAR: |
| 3874 | tm->tm_mon = 1; |
| 3875 | /* FALL THRU */ |
| 3876 | case DTK_QUARTER: |
| 3877 | tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1; |
| 3878 | /* FALL THRU */ |
| 3879 | case DTK_MONTH: |
| 3880 | tm->tm_mday = 1; |
| 3881 | /* FALL THRU */ |
| 3882 | case DTK_DAY: |
| 3883 | tm->tm_hour = 0; |
| 3884 | /* FALL THRU */ |
| 3885 | case DTK_HOUR: |
| 3886 | tm->tm_min = 0; |
| 3887 | /* FALL THRU */ |
| 3888 | case DTK_MINUTE: |
| 3889 | tm->tm_sec = 0; |
| 3890 | /* FALL THRU */ |
| 3891 | case DTK_SECOND: |
| 3892 | fsec = 0; |
| 3893 | break; |
| 3894 | |
| 3895 | case DTK_MILLISEC: |
| 3896 | fsec = (fsec / 1000) * 1000; |
| 3897 | break; |
| 3898 | |
| 3899 | case DTK_MICROSEC: |
| 3900 | break; |
| 3901 | |
| 3902 | default: |
| 3903 | ereport(ERROR, |
| 3904 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 3905 | errmsg("timestamp units \"%s\" not supported" , |
| 3906 | lowunits))); |
| 3907 | result = 0; |
| 3908 | } |
| 3909 | |
| 3910 | if (tm2timestamp(tm, fsec, NULL, &result) != 0) |
| 3911 | ereport(ERROR, |
| 3912 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3913 | errmsg("timestamp out of range" ))); |
| 3914 | } |
| 3915 | else |
| 3916 | { |
| 3917 | ereport(ERROR, |
| 3918 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 3919 | errmsg("timestamp units \"%s\" not recognized" , |
| 3920 | lowunits))); |
| 3921 | result = 0; |
| 3922 | } |
| 3923 | |
| 3924 | PG_RETURN_TIMESTAMP(result); |
| 3925 | } |
| 3926 | |
| 3927 | /* |
| 3928 | * Common code for timestamptz_trunc() and timestamptz_trunc_zone(). |
| 3929 | * |
| 3930 | * tzp identifies the zone to truncate with respect to. We assume |
| 3931 | * infinite timestamps have already been rejected. |
| 3932 | */ |
| 3933 | static TimestampTz |
| 3934 | timestamptz_trunc_internal(text *units, TimestampTz timestamp, pg_tz *tzp) |
| 3935 | { |
| 3936 | TimestampTz result; |
| 3937 | int tz; |
| 3938 | int type, |
| 3939 | val; |
| 3940 | bool redotz = false; |
| 3941 | char *lowunits; |
| 3942 | fsec_t fsec; |
| 3943 | struct pg_tm tt, |
| 3944 | *tm = &tt; |
| 3945 | |
| 3946 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
| 3947 | VARSIZE_ANY_EXHDR(units), |
| 3948 | false); |
| 3949 | |
| 3950 | type = DecodeUnits(0, lowunits, &val); |
| 3951 | |
| 3952 | if (type == UNITS) |
| 3953 | { |
| 3954 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, tzp) != 0) |
| 3955 | ereport(ERROR, |
| 3956 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 3957 | errmsg("timestamp out of range" ))); |
| 3958 | |
| 3959 | switch (val) |
| 3960 | { |
| 3961 | case DTK_WEEK: |
| 3962 | { |
| 3963 | int woy; |
| 3964 | |
| 3965 | woy = date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 3966 | |
| 3967 | /* |
| 3968 | * If it is week 52/53 and the month is January, then the |
| 3969 | * week must belong to the previous year. Also, some |
| 3970 | * December dates belong to the next year. |
| 3971 | */ |
| 3972 | if (woy >= 52 && tm->tm_mon == 1) |
| 3973 | --tm->tm_year; |
| 3974 | if (woy <= 1 && tm->tm_mon == MONTHS_PER_YEAR) |
| 3975 | ++tm->tm_year; |
| 3976 | isoweek2date(woy, &(tm->tm_year), &(tm->tm_mon), &(tm->tm_mday)); |
| 3977 | tm->tm_hour = 0; |
| 3978 | tm->tm_min = 0; |
| 3979 | tm->tm_sec = 0; |
| 3980 | fsec = 0; |
| 3981 | redotz = true; |
| 3982 | break; |
| 3983 | } |
| 3984 | /* one may consider DTK_THOUSAND and DTK_HUNDRED... */ |
| 3985 | case DTK_MILLENNIUM: |
| 3986 | |
| 3987 | /* |
| 3988 | * truncating to the millennium? what is this supposed to |
| 3989 | * mean? let us put the first year of the millennium... i.e. |
| 3990 | * -1000, 1, 1001, 2001... |
| 3991 | */ |
| 3992 | if (tm->tm_year > 0) |
| 3993 | tm->tm_year = ((tm->tm_year + 999) / 1000) * 1000 - 999; |
| 3994 | else |
| 3995 | tm->tm_year = -((999 - (tm->tm_year - 1)) / 1000) * 1000 + 1; |
| 3996 | /* FALL THRU */ |
| 3997 | case DTK_CENTURY: |
| 3998 | /* truncating to the century? as above: -100, 1, 101... */ |
| 3999 | if (tm->tm_year > 0) |
| 4000 | tm->tm_year = ((tm->tm_year + 99) / 100) * 100 - 99; |
| 4001 | else |
| 4002 | tm->tm_year = -((99 - (tm->tm_year - 1)) / 100) * 100 + 1; |
| 4003 | /* FALL THRU */ |
| 4004 | case DTK_DECADE: |
| 4005 | |
| 4006 | /* |
| 4007 | * truncating to the decade? first year of the decade. must |
| 4008 | * not be applied if year was truncated before! |
| 4009 | */ |
| 4010 | if (val != DTK_MILLENNIUM && val != DTK_CENTURY) |
| 4011 | { |
| 4012 | if (tm->tm_year > 0) |
| 4013 | tm->tm_year = (tm->tm_year / 10) * 10; |
| 4014 | else |
| 4015 | tm->tm_year = -((8 - (tm->tm_year - 1)) / 10) * 10; |
| 4016 | } |
| 4017 | /* FALL THRU */ |
| 4018 | case DTK_YEAR: |
| 4019 | tm->tm_mon = 1; |
| 4020 | /* FALL THRU */ |
| 4021 | case DTK_QUARTER: |
| 4022 | tm->tm_mon = (3 * ((tm->tm_mon - 1) / 3)) + 1; |
| 4023 | /* FALL THRU */ |
| 4024 | case DTK_MONTH: |
| 4025 | tm->tm_mday = 1; |
| 4026 | /* FALL THRU */ |
| 4027 | case DTK_DAY: |
| 4028 | tm->tm_hour = 0; |
| 4029 | redotz = true; /* for all cases >= DAY */ |
| 4030 | /* FALL THRU */ |
| 4031 | case DTK_HOUR: |
| 4032 | tm->tm_min = 0; |
| 4033 | /* FALL THRU */ |
| 4034 | case DTK_MINUTE: |
| 4035 | tm->tm_sec = 0; |
| 4036 | /* FALL THRU */ |
| 4037 | case DTK_SECOND: |
| 4038 | fsec = 0; |
| 4039 | break; |
| 4040 | case DTK_MILLISEC: |
| 4041 | fsec = (fsec / 1000) * 1000; |
| 4042 | break; |
| 4043 | case DTK_MICROSEC: |
| 4044 | break; |
| 4045 | |
| 4046 | default: |
| 4047 | ereport(ERROR, |
| 4048 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4049 | errmsg("timestamp with time zone units \"%s\" not " |
| 4050 | "supported" , lowunits))); |
| 4051 | result = 0; |
| 4052 | } |
| 4053 | |
| 4054 | if (redotz) |
| 4055 | tz = DetermineTimeZoneOffset(tm, tzp); |
| 4056 | |
| 4057 | if (tm2timestamp(tm, fsec, &tz, &result) != 0) |
| 4058 | ereport(ERROR, |
| 4059 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4060 | errmsg("timestamp out of range" ))); |
| 4061 | } |
| 4062 | else |
| 4063 | { |
| 4064 | ereport(ERROR, |
| 4065 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4066 | errmsg("timestamp with time zone units \"%s\" not recognized" , |
| 4067 | lowunits))); |
| 4068 | result = 0; |
| 4069 | } |
| 4070 | |
| 4071 | return result; |
| 4072 | } |
| 4073 | |
| 4074 | /* timestamptz_trunc() |
| 4075 | * Truncate timestamptz to specified units in session timezone. |
| 4076 | */ |
| 4077 | Datum |
| 4078 | timestamptz_trunc(PG_FUNCTION_ARGS) |
| 4079 | { |
| 4080 | text *units = PG_GETARG_TEXT_PP(0); |
| 4081 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); |
| 4082 | TimestampTz result; |
| 4083 | |
| 4084 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 4085 | PG_RETURN_TIMESTAMPTZ(timestamp); |
| 4086 | |
| 4087 | result = timestamptz_trunc_internal(units, timestamp, session_timezone); |
| 4088 | |
| 4089 | PG_RETURN_TIMESTAMPTZ(result); |
| 4090 | } |
| 4091 | |
| 4092 | /* timestamptz_trunc_zone() |
| 4093 | * Truncate timestamptz to specified units in specified timezone. |
| 4094 | */ |
| 4095 | Datum |
| 4096 | timestamptz_trunc_zone(PG_FUNCTION_ARGS) |
| 4097 | { |
| 4098 | text *units = PG_GETARG_TEXT_PP(0); |
| 4099 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); |
| 4100 | text *zone = PG_GETARG_TEXT_PP(2); |
| 4101 | TimestampTz result; |
| 4102 | char tzname[TZ_STRLEN_MAX + 1]; |
| 4103 | char *lowzone; |
| 4104 | int type, |
| 4105 | val; |
| 4106 | pg_tz *tzp; |
| 4107 | |
| 4108 | /* |
| 4109 | * timestamptz_zone() doesn't look up the zone for infinite inputs, so we |
| 4110 | * don't do so here either. |
| 4111 | */ |
| 4112 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 4113 | PG_RETURN_TIMESTAMP(timestamp); |
| 4114 | |
| 4115 | /* |
| 4116 | * Look up the requested timezone (see notes in timestamptz_zone()). |
| 4117 | */ |
| 4118 | text_to_cstring_buffer(zone, tzname, sizeof(tzname)); |
| 4119 | |
| 4120 | /* DecodeTimezoneAbbrev requires lowercase input */ |
| 4121 | lowzone = downcase_truncate_identifier(tzname, |
| 4122 | strlen(tzname), |
| 4123 | false); |
| 4124 | |
| 4125 | type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); |
| 4126 | |
| 4127 | if (type == TZ || type == DTZ) |
| 4128 | { |
| 4129 | /* fixed-offset abbreviation, get a pg_tz descriptor for that */ |
| 4130 | tzp = pg_tzset_offset(-val); |
| 4131 | } |
| 4132 | else if (type == DYNTZ) |
| 4133 | { |
| 4134 | /* dynamic-offset abbreviation, use its referenced timezone */ |
| 4135 | } |
| 4136 | else |
| 4137 | { |
| 4138 | /* try it as a full zone name */ |
| 4139 | tzp = pg_tzset(tzname); |
| 4140 | if (!tzp) |
| 4141 | ereport(ERROR, |
| 4142 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4143 | errmsg("time zone \"%s\" not recognized" , tzname))); |
| 4144 | } |
| 4145 | |
| 4146 | result = timestamptz_trunc_internal(units, timestamp, tzp); |
| 4147 | |
| 4148 | PG_RETURN_TIMESTAMPTZ(result); |
| 4149 | } |
| 4150 | |
| 4151 | /* interval_trunc() |
| 4152 | * Extract specified field from interval. |
| 4153 | */ |
| 4154 | Datum |
| 4155 | interval_trunc(PG_FUNCTION_ARGS) |
| 4156 | { |
| 4157 | text *units = PG_GETARG_TEXT_PP(0); |
| 4158 | Interval *interval = PG_GETARG_INTERVAL_P(1); |
| 4159 | Interval *result; |
| 4160 | int type, |
| 4161 | val; |
| 4162 | char *lowunits; |
| 4163 | fsec_t fsec; |
| 4164 | struct pg_tm tt, |
| 4165 | *tm = &tt; |
| 4166 | |
| 4167 | result = (Interval *) palloc(sizeof(Interval)); |
| 4168 | |
| 4169 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
| 4170 | VARSIZE_ANY_EXHDR(units), |
| 4171 | false); |
| 4172 | |
| 4173 | type = DecodeUnits(0, lowunits, &val); |
| 4174 | |
| 4175 | if (type == UNITS) |
| 4176 | { |
| 4177 | if (interval2tm(*interval, tm, &fsec) == 0) |
| 4178 | { |
| 4179 | switch (val) |
| 4180 | { |
| 4181 | case DTK_MILLENNIUM: |
| 4182 | /* caution: C division may have negative remainder */ |
| 4183 | tm->tm_year = (tm->tm_year / 1000) * 1000; |
| 4184 | /* FALL THRU */ |
| 4185 | case DTK_CENTURY: |
| 4186 | /* caution: C division may have negative remainder */ |
| 4187 | tm->tm_year = (tm->tm_year / 100) * 100; |
| 4188 | /* FALL THRU */ |
| 4189 | case DTK_DECADE: |
| 4190 | /* caution: C division may have negative remainder */ |
| 4191 | tm->tm_year = (tm->tm_year / 10) * 10; |
| 4192 | /* FALL THRU */ |
| 4193 | case DTK_YEAR: |
| 4194 | tm->tm_mon = 0; |
| 4195 | /* FALL THRU */ |
| 4196 | case DTK_QUARTER: |
| 4197 | tm->tm_mon = 3 * (tm->tm_mon / 3); |
| 4198 | /* FALL THRU */ |
| 4199 | case DTK_MONTH: |
| 4200 | tm->tm_mday = 0; |
| 4201 | /* FALL THRU */ |
| 4202 | case DTK_DAY: |
| 4203 | tm->tm_hour = 0; |
| 4204 | /* FALL THRU */ |
| 4205 | case DTK_HOUR: |
| 4206 | tm->tm_min = 0; |
| 4207 | /* FALL THRU */ |
| 4208 | case DTK_MINUTE: |
| 4209 | tm->tm_sec = 0; |
| 4210 | /* FALL THRU */ |
| 4211 | case DTK_SECOND: |
| 4212 | fsec = 0; |
| 4213 | break; |
| 4214 | case DTK_MILLISEC: |
| 4215 | fsec = (fsec / 1000) * 1000; |
| 4216 | break; |
| 4217 | case DTK_MICROSEC: |
| 4218 | break; |
| 4219 | |
| 4220 | default: |
| 4221 | if (val == DTK_WEEK) |
| 4222 | ereport(ERROR, |
| 4223 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4224 | errmsg("interval units \"%s\" not supported " |
| 4225 | "because months usually have fractional weeks" , |
| 4226 | lowunits))); |
| 4227 | else |
| 4228 | ereport(ERROR, |
| 4229 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4230 | errmsg("interval units \"%s\" not supported" , |
| 4231 | lowunits))); |
| 4232 | } |
| 4233 | |
| 4234 | if (tm2interval(tm, fsec, result) != 0) |
| 4235 | ereport(ERROR, |
| 4236 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4237 | errmsg("interval out of range" ))); |
| 4238 | } |
| 4239 | else |
| 4240 | elog(ERROR, "could not convert interval to tm" ); |
| 4241 | } |
| 4242 | else |
| 4243 | { |
| 4244 | ereport(ERROR, |
| 4245 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4246 | errmsg("interval units \"%s\" not recognized" , |
| 4247 | lowunits))); |
| 4248 | } |
| 4249 | |
| 4250 | PG_RETURN_INTERVAL_P(result); |
| 4251 | } |
| 4252 | |
| 4253 | /* isoweek2j() |
| 4254 | * |
| 4255 | * Return the Julian day which corresponds to the first day (Monday) of the given ISO 8601 year and week. |
| 4256 | * Julian days are used to convert between ISO week dates and Gregorian dates. |
| 4257 | */ |
| 4258 | int |
| 4259 | isoweek2j(int year, int week) |
| 4260 | { |
| 4261 | int day0, |
| 4262 | day4; |
| 4263 | |
| 4264 | /* fourth day of current year */ |
| 4265 | day4 = date2j(year, 1, 4); |
| 4266 | |
| 4267 | /* day0 == offset to first day of week (Monday) */ |
| 4268 | day0 = j2day(day4 - 1); |
| 4269 | |
| 4270 | return ((week - 1) * 7) + (day4 - day0); |
| 4271 | } |
| 4272 | |
| 4273 | /* isoweek2date() |
| 4274 | * Convert ISO week of year number to date. |
| 4275 | * The year field must be specified with the ISO year! |
| 4276 | * karel 2000/08/07 |
| 4277 | */ |
| 4278 | void |
| 4279 | isoweek2date(int woy, int *year, int *mon, int *mday) |
| 4280 | { |
| 4281 | j2date(isoweek2j(*year, woy), year, mon, mday); |
| 4282 | } |
| 4283 | |
| 4284 | /* isoweekdate2date() |
| 4285 | * |
| 4286 | * Convert an ISO 8601 week date (ISO year, ISO week) into a Gregorian date. |
| 4287 | * Gregorian day of week sent so weekday strings can be supplied. |
| 4288 | * Populates year, mon, and mday with the correct Gregorian values. |
| 4289 | * year must be passed in as the ISO year. |
| 4290 | */ |
| 4291 | void |
| 4292 | isoweekdate2date(int isoweek, int wday, int *year, int *mon, int *mday) |
| 4293 | { |
| 4294 | int jday; |
| 4295 | |
| 4296 | jday = isoweek2j(*year, isoweek); |
| 4297 | /* convert Gregorian week start (Sunday=1) to ISO week start (Monday=1) */ |
| 4298 | if (wday > 1) |
| 4299 | jday += wday - 2; |
| 4300 | else |
| 4301 | jday += 6; |
| 4302 | j2date(jday, year, mon, mday); |
| 4303 | } |
| 4304 | |
| 4305 | /* date2isoweek() |
| 4306 | * |
| 4307 | * Returns ISO week number of year. |
| 4308 | */ |
| 4309 | int |
| 4310 | date2isoweek(int year, int mon, int mday) |
| 4311 | { |
| 4312 | float8 result; |
| 4313 | int day0, |
| 4314 | day4, |
| 4315 | dayn; |
| 4316 | |
| 4317 | /* current day */ |
| 4318 | dayn = date2j(year, mon, mday); |
| 4319 | |
| 4320 | /* fourth day of current year */ |
| 4321 | day4 = date2j(year, 1, 4); |
| 4322 | |
| 4323 | /* day0 == offset to first day of week (Monday) */ |
| 4324 | day0 = j2day(day4 - 1); |
| 4325 | |
| 4326 | /* |
| 4327 | * We need the first week containing a Thursday, otherwise this day falls |
| 4328 | * into the previous year for purposes of counting weeks |
| 4329 | */ |
| 4330 | if (dayn < day4 - day0) |
| 4331 | { |
| 4332 | day4 = date2j(year - 1, 1, 4); |
| 4333 | |
| 4334 | /* day0 == offset to first day of week (Monday) */ |
| 4335 | day0 = j2day(day4 - 1); |
| 4336 | } |
| 4337 | |
| 4338 | result = (dayn - (day4 - day0)) / 7 + 1; |
| 4339 | |
| 4340 | /* |
| 4341 | * Sometimes the last few days in a year will fall into the first week of |
| 4342 | * the next year, so check for this. |
| 4343 | */ |
| 4344 | if (result >= 52) |
| 4345 | { |
| 4346 | day4 = date2j(year + 1, 1, 4); |
| 4347 | |
| 4348 | /* day0 == offset to first day of week (Monday) */ |
| 4349 | day0 = j2day(day4 - 1); |
| 4350 | |
| 4351 | if (dayn >= day4 - day0) |
| 4352 | result = (dayn - (day4 - day0)) / 7 + 1; |
| 4353 | } |
| 4354 | |
| 4355 | return (int) result; |
| 4356 | } |
| 4357 | |
| 4358 | |
| 4359 | /* date2isoyear() |
| 4360 | * |
| 4361 | * Returns ISO 8601 year number. |
| 4362 | */ |
| 4363 | int |
| 4364 | date2isoyear(int year, int mon, int mday) |
| 4365 | { |
| 4366 | float8 result; |
| 4367 | int day0, |
| 4368 | day4, |
| 4369 | dayn; |
| 4370 | |
| 4371 | /* current day */ |
| 4372 | dayn = date2j(year, mon, mday); |
| 4373 | |
| 4374 | /* fourth day of current year */ |
| 4375 | day4 = date2j(year, 1, 4); |
| 4376 | |
| 4377 | /* day0 == offset to first day of week (Monday) */ |
| 4378 | day0 = j2day(day4 - 1); |
| 4379 | |
| 4380 | /* |
| 4381 | * We need the first week containing a Thursday, otherwise this day falls |
| 4382 | * into the previous year for purposes of counting weeks |
| 4383 | */ |
| 4384 | if (dayn < day4 - day0) |
| 4385 | { |
| 4386 | day4 = date2j(year - 1, 1, 4); |
| 4387 | |
| 4388 | /* day0 == offset to first day of week (Monday) */ |
| 4389 | day0 = j2day(day4 - 1); |
| 4390 | |
| 4391 | year--; |
| 4392 | } |
| 4393 | |
| 4394 | result = (dayn - (day4 - day0)) / 7 + 1; |
| 4395 | |
| 4396 | /* |
| 4397 | * Sometimes the last few days in a year will fall into the first week of |
| 4398 | * the next year, so check for this. |
| 4399 | */ |
| 4400 | if (result >= 52) |
| 4401 | { |
| 4402 | day4 = date2j(year + 1, 1, 4); |
| 4403 | |
| 4404 | /* day0 == offset to first day of week (Monday) */ |
| 4405 | day0 = j2day(day4 - 1); |
| 4406 | |
| 4407 | if (dayn >= day4 - day0) |
| 4408 | year++; |
| 4409 | } |
| 4410 | |
| 4411 | return year; |
| 4412 | } |
| 4413 | |
| 4414 | |
| 4415 | /* date2isoyearday() |
| 4416 | * |
| 4417 | * Returns the ISO 8601 day-of-year, given a Gregorian year, month and day. |
| 4418 | * Possible return values are 1 through 371 (364 in non-leap years). |
| 4419 | */ |
| 4420 | int |
| 4421 | date2isoyearday(int year, int mon, int mday) |
| 4422 | { |
| 4423 | return date2j(year, mon, mday) - isoweek2j(date2isoyear(year, mon, mday), 1) + 1; |
| 4424 | } |
| 4425 | |
| 4426 | /* |
| 4427 | * NonFiniteTimestampTzPart |
| 4428 | * |
| 4429 | * Used by timestamp_part and timestamptz_part when extracting from infinite |
| 4430 | * timestamp[tz]. Returns +/-Infinity if that is the appropriate result, |
| 4431 | * otherwise returns zero (which should be taken as meaning to return NULL). |
| 4432 | * |
| 4433 | * Errors thrown here for invalid units should exactly match those that |
| 4434 | * would be thrown in the calling functions, else there will be unexpected |
| 4435 | * discrepancies between finite- and infinite-input cases. |
| 4436 | */ |
| 4437 | static float8 |
| 4438 | NonFiniteTimestampTzPart(int type, int unit, char *lowunits, |
| 4439 | bool isNegative, bool isTz) |
| 4440 | { |
| 4441 | if ((type != UNITS) && (type != RESERV)) |
| 4442 | { |
| 4443 | if (isTz) |
| 4444 | ereport(ERROR, |
| 4445 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4446 | errmsg("timestamp with time zone units \"%s\" not recognized" , |
| 4447 | lowunits))); |
| 4448 | else |
| 4449 | ereport(ERROR, |
| 4450 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4451 | errmsg("timestamp units \"%s\" not recognized" , |
| 4452 | lowunits))); |
| 4453 | } |
| 4454 | |
| 4455 | switch (unit) |
| 4456 | { |
| 4457 | /* Oscillating units */ |
| 4458 | case DTK_MICROSEC: |
| 4459 | case DTK_MILLISEC: |
| 4460 | case DTK_SECOND: |
| 4461 | case DTK_MINUTE: |
| 4462 | case DTK_HOUR: |
| 4463 | case DTK_DAY: |
| 4464 | case DTK_MONTH: |
| 4465 | case DTK_QUARTER: |
| 4466 | case DTK_WEEK: |
| 4467 | case DTK_DOW: |
| 4468 | case DTK_ISODOW: |
| 4469 | case DTK_DOY: |
| 4470 | case DTK_TZ: |
| 4471 | case DTK_TZ_MINUTE: |
| 4472 | case DTK_TZ_HOUR: |
| 4473 | return 0.0; |
| 4474 | |
| 4475 | /* Monotonically-increasing units */ |
| 4476 | case DTK_YEAR: |
| 4477 | case DTK_DECADE: |
| 4478 | case DTK_CENTURY: |
| 4479 | case DTK_MILLENNIUM: |
| 4480 | case DTK_JULIAN: |
| 4481 | case DTK_ISOYEAR: |
| 4482 | case DTK_EPOCH: |
| 4483 | if (isNegative) |
| 4484 | return -get_float8_infinity(); |
| 4485 | else |
| 4486 | return get_float8_infinity(); |
| 4487 | |
| 4488 | default: |
| 4489 | if (isTz) |
| 4490 | ereport(ERROR, |
| 4491 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4492 | errmsg("timestamp with time zone units \"%s\" not supported" , |
| 4493 | lowunits))); |
| 4494 | else |
| 4495 | ereport(ERROR, |
| 4496 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4497 | errmsg("timestamp units \"%s\" not supported" , |
| 4498 | lowunits))); |
| 4499 | return 0.0; /* keep compiler quiet */ |
| 4500 | } |
| 4501 | } |
| 4502 | |
| 4503 | /* timestamp_part() |
| 4504 | * Extract specified field from timestamp. |
| 4505 | */ |
| 4506 | Datum |
| 4507 | timestamp_part(PG_FUNCTION_ARGS) |
| 4508 | { |
| 4509 | text *units = PG_GETARG_TEXT_PP(0); |
| 4510 | Timestamp timestamp = PG_GETARG_TIMESTAMP(1); |
| 4511 | float8 result; |
| 4512 | Timestamp epoch; |
| 4513 | int type, |
| 4514 | val; |
| 4515 | char *lowunits; |
| 4516 | fsec_t fsec; |
| 4517 | struct pg_tm tt, |
| 4518 | *tm = &tt; |
| 4519 | |
| 4520 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
| 4521 | VARSIZE_ANY_EXHDR(units), |
| 4522 | false); |
| 4523 | |
| 4524 | type = DecodeUnits(0, lowunits, &val); |
| 4525 | if (type == UNKNOWN_FIELD) |
| 4526 | type = DecodeSpecial(0, lowunits, &val); |
| 4527 | |
| 4528 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 4529 | { |
| 4530 | result = NonFiniteTimestampTzPart(type, val, lowunits, |
| 4531 | TIMESTAMP_IS_NOBEGIN(timestamp), |
| 4532 | false); |
| 4533 | if (result) |
| 4534 | PG_RETURN_FLOAT8(result); |
| 4535 | else |
| 4536 | PG_RETURN_NULL(); |
| 4537 | } |
| 4538 | |
| 4539 | if (type == UNITS) |
| 4540 | { |
| 4541 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 4542 | ereport(ERROR, |
| 4543 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4544 | errmsg("timestamp out of range" ))); |
| 4545 | |
| 4546 | switch (val) |
| 4547 | { |
| 4548 | case DTK_MICROSEC: |
| 4549 | result = tm->tm_sec * 1000000.0 + fsec; |
| 4550 | break; |
| 4551 | |
| 4552 | case DTK_MILLISEC: |
| 4553 | result = tm->tm_sec * 1000.0 + fsec / 1000.0; |
| 4554 | break; |
| 4555 | |
| 4556 | case DTK_SECOND: |
| 4557 | result = tm->tm_sec + fsec / 1000000.0; |
| 4558 | break; |
| 4559 | |
| 4560 | case DTK_MINUTE: |
| 4561 | result = tm->tm_min; |
| 4562 | break; |
| 4563 | |
| 4564 | case DTK_HOUR: |
| 4565 | result = tm->tm_hour; |
| 4566 | break; |
| 4567 | |
| 4568 | case DTK_DAY: |
| 4569 | result = tm->tm_mday; |
| 4570 | break; |
| 4571 | |
| 4572 | case DTK_MONTH: |
| 4573 | result = tm->tm_mon; |
| 4574 | break; |
| 4575 | |
| 4576 | case DTK_QUARTER: |
| 4577 | result = (tm->tm_mon - 1) / 3 + 1; |
| 4578 | break; |
| 4579 | |
| 4580 | case DTK_WEEK: |
| 4581 | result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 4582 | break; |
| 4583 | |
| 4584 | case DTK_YEAR: |
| 4585 | if (tm->tm_year > 0) |
| 4586 | result = tm->tm_year; |
| 4587 | else |
| 4588 | /* there is no year 0, just 1 BC and 1 AD */ |
| 4589 | result = tm->tm_year - 1; |
| 4590 | break; |
| 4591 | |
| 4592 | case DTK_DECADE: |
| 4593 | |
| 4594 | /* |
| 4595 | * what is a decade wrt dates? let us assume that decade 199 |
| 4596 | * is 1990 thru 1999... decade 0 starts on year 1 BC, and -1 |
| 4597 | * is 11 BC thru 2 BC... |
| 4598 | */ |
| 4599 | if (tm->tm_year >= 0) |
| 4600 | result = tm->tm_year / 10; |
| 4601 | else |
| 4602 | result = -((8 - (tm->tm_year - 1)) / 10); |
| 4603 | break; |
| 4604 | |
| 4605 | case DTK_CENTURY: |
| 4606 | |
| 4607 | /* ---- |
| 4608 | * centuries AD, c>0: year in [ (c-1)* 100 + 1 : c*100 ] |
| 4609 | * centuries BC, c<0: year in [ c*100 : (c+1) * 100 - 1] |
| 4610 | * there is no number 0 century. |
| 4611 | * ---- |
| 4612 | */ |
| 4613 | if (tm->tm_year > 0) |
| 4614 | result = (tm->tm_year + 99) / 100; |
| 4615 | else |
| 4616 | /* caution: C division may have negative remainder */ |
| 4617 | result = -((99 - (tm->tm_year - 1)) / 100); |
| 4618 | break; |
| 4619 | |
| 4620 | case DTK_MILLENNIUM: |
| 4621 | /* see comments above. */ |
| 4622 | if (tm->tm_year > 0) |
| 4623 | result = (tm->tm_year + 999) / 1000; |
| 4624 | else |
| 4625 | result = -((999 - (tm->tm_year - 1)) / 1000); |
| 4626 | break; |
| 4627 | |
| 4628 | case DTK_JULIAN: |
| 4629 | result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 4630 | result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + |
| 4631 | tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY; |
| 4632 | break; |
| 4633 | |
| 4634 | case DTK_ISOYEAR: |
| 4635 | result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 4636 | break; |
| 4637 | |
| 4638 | case DTK_DOW: |
| 4639 | case DTK_ISODOW: |
| 4640 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 4641 | ereport(ERROR, |
| 4642 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4643 | errmsg("timestamp out of range" ))); |
| 4644 | result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); |
| 4645 | if (val == DTK_ISODOW && result == 0) |
| 4646 | result = 7; |
| 4647 | break; |
| 4648 | |
| 4649 | case DTK_DOY: |
| 4650 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 4651 | ereport(ERROR, |
| 4652 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4653 | errmsg("timestamp out of range" ))); |
| 4654 | result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) |
| 4655 | - date2j(tm->tm_year, 1, 1) + 1); |
| 4656 | break; |
| 4657 | |
| 4658 | case DTK_TZ: |
| 4659 | case DTK_TZ_MINUTE: |
| 4660 | case DTK_TZ_HOUR: |
| 4661 | default: |
| 4662 | ereport(ERROR, |
| 4663 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4664 | errmsg("timestamp units \"%s\" not supported" , |
| 4665 | lowunits))); |
| 4666 | result = 0; |
| 4667 | } |
| 4668 | } |
| 4669 | else if (type == RESERV) |
| 4670 | { |
| 4671 | switch (val) |
| 4672 | { |
| 4673 | case DTK_EPOCH: |
| 4674 | epoch = SetEpochTimestamp(); |
| 4675 | /* try to avoid precision loss in subtraction */ |
| 4676 | if (timestamp < (PG_INT64_MAX + epoch)) |
| 4677 | result = (timestamp - epoch) / 1000000.0; |
| 4678 | else |
| 4679 | result = ((float8) timestamp - epoch) / 1000000.0; |
| 4680 | break; |
| 4681 | |
| 4682 | default: |
| 4683 | ereport(ERROR, |
| 4684 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4685 | errmsg("timestamp units \"%s\" not supported" , |
| 4686 | lowunits))); |
| 4687 | result = 0; |
| 4688 | } |
| 4689 | |
| 4690 | } |
| 4691 | else |
| 4692 | { |
| 4693 | ereport(ERROR, |
| 4694 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4695 | errmsg("timestamp units \"%s\" not recognized" , lowunits))); |
| 4696 | result = 0; |
| 4697 | } |
| 4698 | |
| 4699 | PG_RETURN_FLOAT8(result); |
| 4700 | } |
| 4701 | |
| 4702 | /* timestamptz_part() |
| 4703 | * Extract specified field from timestamp with time zone. |
| 4704 | */ |
| 4705 | Datum |
| 4706 | timestamptz_part(PG_FUNCTION_ARGS) |
| 4707 | { |
| 4708 | text *units = PG_GETARG_TEXT_PP(0); |
| 4709 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); |
| 4710 | float8 result; |
| 4711 | Timestamp epoch; |
| 4712 | int tz; |
| 4713 | int type, |
| 4714 | val; |
| 4715 | char *lowunits; |
| 4716 | double dummy; |
| 4717 | fsec_t fsec; |
| 4718 | struct pg_tm tt, |
| 4719 | *tm = &tt; |
| 4720 | |
| 4721 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
| 4722 | VARSIZE_ANY_EXHDR(units), |
| 4723 | false); |
| 4724 | |
| 4725 | type = DecodeUnits(0, lowunits, &val); |
| 4726 | if (type == UNKNOWN_FIELD) |
| 4727 | type = DecodeSpecial(0, lowunits, &val); |
| 4728 | |
| 4729 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 4730 | { |
| 4731 | result = NonFiniteTimestampTzPart(type, val, lowunits, |
| 4732 | TIMESTAMP_IS_NOBEGIN(timestamp), |
| 4733 | true); |
| 4734 | if (result) |
| 4735 | PG_RETURN_FLOAT8(result); |
| 4736 | else |
| 4737 | PG_RETURN_NULL(); |
| 4738 | } |
| 4739 | |
| 4740 | if (type == UNITS) |
| 4741 | { |
| 4742 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
| 4743 | ereport(ERROR, |
| 4744 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4745 | errmsg("timestamp out of range" ))); |
| 4746 | |
| 4747 | switch (val) |
| 4748 | { |
| 4749 | case DTK_TZ: |
| 4750 | result = -tz; |
| 4751 | break; |
| 4752 | |
| 4753 | case DTK_TZ_MINUTE: |
| 4754 | result = -tz; |
| 4755 | result /= MINS_PER_HOUR; |
| 4756 | FMODULO(result, dummy, (double) MINS_PER_HOUR); |
| 4757 | break; |
| 4758 | |
| 4759 | case DTK_TZ_HOUR: |
| 4760 | dummy = -tz; |
| 4761 | FMODULO(dummy, result, (double) SECS_PER_HOUR); |
| 4762 | break; |
| 4763 | |
| 4764 | case DTK_MICROSEC: |
| 4765 | result = tm->tm_sec * 1000000.0 + fsec; |
| 4766 | break; |
| 4767 | |
| 4768 | case DTK_MILLISEC: |
| 4769 | result = tm->tm_sec * 1000.0 + fsec / 1000.0; |
| 4770 | break; |
| 4771 | |
| 4772 | case DTK_SECOND: |
| 4773 | result = tm->tm_sec + fsec / 1000000.0; |
| 4774 | break; |
| 4775 | |
| 4776 | case DTK_MINUTE: |
| 4777 | result = tm->tm_min; |
| 4778 | break; |
| 4779 | |
| 4780 | case DTK_HOUR: |
| 4781 | result = tm->tm_hour; |
| 4782 | break; |
| 4783 | |
| 4784 | case DTK_DAY: |
| 4785 | result = tm->tm_mday; |
| 4786 | break; |
| 4787 | |
| 4788 | case DTK_MONTH: |
| 4789 | result = tm->tm_mon; |
| 4790 | break; |
| 4791 | |
| 4792 | case DTK_QUARTER: |
| 4793 | result = (tm->tm_mon - 1) / 3 + 1; |
| 4794 | break; |
| 4795 | |
| 4796 | case DTK_WEEK: |
| 4797 | result = (float8) date2isoweek(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 4798 | break; |
| 4799 | |
| 4800 | case DTK_YEAR: |
| 4801 | if (tm->tm_year > 0) |
| 4802 | result = tm->tm_year; |
| 4803 | else |
| 4804 | /* there is no year 0, just 1 BC and 1 AD */ |
| 4805 | result = tm->tm_year - 1; |
| 4806 | break; |
| 4807 | |
| 4808 | case DTK_DECADE: |
| 4809 | /* see comments in timestamp_part */ |
| 4810 | if (tm->tm_year > 0) |
| 4811 | result = tm->tm_year / 10; |
| 4812 | else |
| 4813 | result = -((8 - (tm->tm_year - 1)) / 10); |
| 4814 | break; |
| 4815 | |
| 4816 | case DTK_CENTURY: |
| 4817 | /* see comments in timestamp_part */ |
| 4818 | if (tm->tm_year > 0) |
| 4819 | result = (tm->tm_year + 99) / 100; |
| 4820 | else |
| 4821 | result = -((99 - (tm->tm_year - 1)) / 100); |
| 4822 | break; |
| 4823 | |
| 4824 | case DTK_MILLENNIUM: |
| 4825 | /* see comments in timestamp_part */ |
| 4826 | if (tm->tm_year > 0) |
| 4827 | result = (tm->tm_year + 999) / 1000; |
| 4828 | else |
| 4829 | result = -((999 - (tm->tm_year - 1)) / 1000); |
| 4830 | break; |
| 4831 | |
| 4832 | case DTK_JULIAN: |
| 4833 | result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 4834 | result += ((((tm->tm_hour * MINS_PER_HOUR) + tm->tm_min) * SECS_PER_MINUTE) + |
| 4835 | tm->tm_sec + (fsec / 1000000.0)) / (double) SECS_PER_DAY; |
| 4836 | break; |
| 4837 | |
| 4838 | case DTK_ISOYEAR: |
| 4839 | result = date2isoyear(tm->tm_year, tm->tm_mon, tm->tm_mday); |
| 4840 | break; |
| 4841 | |
| 4842 | case DTK_DOW: |
| 4843 | case DTK_ISODOW: |
| 4844 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
| 4845 | ereport(ERROR, |
| 4846 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4847 | errmsg("timestamp out of range" ))); |
| 4848 | result = j2day(date2j(tm->tm_year, tm->tm_mon, tm->tm_mday)); |
| 4849 | if (val == DTK_ISODOW && result == 0) |
| 4850 | result = 7; |
| 4851 | break; |
| 4852 | |
| 4853 | case DTK_DOY: |
| 4854 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
| 4855 | ereport(ERROR, |
| 4856 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 4857 | errmsg("timestamp out of range" ))); |
| 4858 | result = (date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) |
| 4859 | - date2j(tm->tm_year, 1, 1) + 1); |
| 4860 | break; |
| 4861 | |
| 4862 | default: |
| 4863 | ereport(ERROR, |
| 4864 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4865 | errmsg("timestamp with time zone units \"%s\" not supported" , |
| 4866 | lowunits))); |
| 4867 | result = 0; |
| 4868 | } |
| 4869 | |
| 4870 | } |
| 4871 | else if (type == RESERV) |
| 4872 | { |
| 4873 | switch (val) |
| 4874 | { |
| 4875 | case DTK_EPOCH: |
| 4876 | epoch = SetEpochTimestamp(); |
| 4877 | /* try to avoid precision loss in subtraction */ |
| 4878 | if (timestamp < (PG_INT64_MAX + epoch)) |
| 4879 | result = (timestamp - epoch) / 1000000.0; |
| 4880 | else |
| 4881 | result = ((float8) timestamp - epoch) / 1000000.0; |
| 4882 | break; |
| 4883 | |
| 4884 | default: |
| 4885 | ereport(ERROR, |
| 4886 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4887 | errmsg("timestamp with time zone units \"%s\" not supported" , |
| 4888 | lowunits))); |
| 4889 | result = 0; |
| 4890 | } |
| 4891 | } |
| 4892 | else |
| 4893 | { |
| 4894 | ereport(ERROR, |
| 4895 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 4896 | errmsg("timestamp with time zone units \"%s\" not recognized" , |
| 4897 | lowunits))); |
| 4898 | |
| 4899 | result = 0; |
| 4900 | } |
| 4901 | |
| 4902 | PG_RETURN_FLOAT8(result); |
| 4903 | } |
| 4904 | |
| 4905 | |
| 4906 | /* interval_part() |
| 4907 | * Extract specified field from interval. |
| 4908 | */ |
| 4909 | Datum |
| 4910 | interval_part(PG_FUNCTION_ARGS) |
| 4911 | { |
| 4912 | text *units = PG_GETARG_TEXT_PP(0); |
| 4913 | Interval *interval = PG_GETARG_INTERVAL_P(1); |
| 4914 | float8 result; |
| 4915 | int type, |
| 4916 | val; |
| 4917 | char *lowunits; |
| 4918 | fsec_t fsec; |
| 4919 | struct pg_tm tt, |
| 4920 | *tm = &tt; |
| 4921 | |
| 4922 | lowunits = downcase_truncate_identifier(VARDATA_ANY(units), |
| 4923 | VARSIZE_ANY_EXHDR(units), |
| 4924 | false); |
| 4925 | |
| 4926 | type = DecodeUnits(0, lowunits, &val); |
| 4927 | if (type == UNKNOWN_FIELD) |
| 4928 | type = DecodeSpecial(0, lowunits, &val); |
| 4929 | |
| 4930 | if (type == UNITS) |
| 4931 | { |
| 4932 | if (interval2tm(*interval, tm, &fsec) == 0) |
| 4933 | { |
| 4934 | switch (val) |
| 4935 | { |
| 4936 | case DTK_MICROSEC: |
| 4937 | result = tm->tm_sec * 1000000.0 + fsec; |
| 4938 | break; |
| 4939 | |
| 4940 | case DTK_MILLISEC: |
| 4941 | result = tm->tm_sec * 1000.0 + fsec / 1000.0; |
| 4942 | break; |
| 4943 | |
| 4944 | case DTK_SECOND: |
| 4945 | result = tm->tm_sec + fsec / 1000000.0; |
| 4946 | break; |
| 4947 | |
| 4948 | case DTK_MINUTE: |
| 4949 | result = tm->tm_min; |
| 4950 | break; |
| 4951 | |
| 4952 | case DTK_HOUR: |
| 4953 | result = tm->tm_hour; |
| 4954 | break; |
| 4955 | |
| 4956 | case DTK_DAY: |
| 4957 | result = tm->tm_mday; |
| 4958 | break; |
| 4959 | |
| 4960 | case DTK_MONTH: |
| 4961 | result = tm->tm_mon; |
| 4962 | break; |
| 4963 | |
| 4964 | case DTK_QUARTER: |
| 4965 | result = (tm->tm_mon / 3) + 1; |
| 4966 | break; |
| 4967 | |
| 4968 | case DTK_YEAR: |
| 4969 | result = tm->tm_year; |
| 4970 | break; |
| 4971 | |
| 4972 | case DTK_DECADE: |
| 4973 | /* caution: C division may have negative remainder */ |
| 4974 | result = tm->tm_year / 10; |
| 4975 | break; |
| 4976 | |
| 4977 | case DTK_CENTURY: |
| 4978 | /* caution: C division may have negative remainder */ |
| 4979 | result = tm->tm_year / 100; |
| 4980 | break; |
| 4981 | |
| 4982 | case DTK_MILLENNIUM: |
| 4983 | /* caution: C division may have negative remainder */ |
| 4984 | result = tm->tm_year / 1000; |
| 4985 | break; |
| 4986 | |
| 4987 | default: |
| 4988 | ereport(ERROR, |
| 4989 | (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 4990 | errmsg("interval units \"%s\" not supported" , |
| 4991 | lowunits))); |
| 4992 | result = 0; |
| 4993 | } |
| 4994 | |
| 4995 | } |
| 4996 | else |
| 4997 | { |
| 4998 | elog(ERROR, "could not convert interval to tm" ); |
| 4999 | result = 0; |
| 5000 | } |
| 5001 | } |
| 5002 | else if (type == RESERV && val == DTK_EPOCH) |
| 5003 | { |
| 5004 | result = interval->time / 1000000.0; |
| 5005 | result += ((double) DAYS_PER_YEAR * SECS_PER_DAY) * (interval->month / MONTHS_PER_YEAR); |
| 5006 | result += ((double) DAYS_PER_MONTH * SECS_PER_DAY) * (interval->month % MONTHS_PER_YEAR); |
| 5007 | result += ((double) SECS_PER_DAY) * interval->day; |
| 5008 | } |
| 5009 | else |
| 5010 | { |
| 5011 | ereport(ERROR, |
| 5012 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5013 | errmsg("interval units \"%s\" not recognized" , |
| 5014 | lowunits))); |
| 5015 | result = 0; |
| 5016 | } |
| 5017 | |
| 5018 | PG_RETURN_FLOAT8(result); |
| 5019 | } |
| 5020 | |
| 5021 | |
| 5022 | /* timestamp_zone() |
| 5023 | * Encode timestamp type with specified time zone. |
| 5024 | * This function is just timestamp2timestamptz() except instead of |
| 5025 | * shifting to the global timezone, we shift to the specified timezone. |
| 5026 | * This is different from the other AT TIME ZONE cases because instead |
| 5027 | * of shifting _to_ a new time zone, it sets the time to _be_ the |
| 5028 | * specified timezone. |
| 5029 | */ |
| 5030 | Datum |
| 5031 | timestamp_zone(PG_FUNCTION_ARGS) |
| 5032 | { |
| 5033 | text *zone = PG_GETARG_TEXT_PP(0); |
| 5034 | Timestamp timestamp = PG_GETARG_TIMESTAMP(1); |
| 5035 | TimestampTz result; |
| 5036 | int tz; |
| 5037 | char tzname[TZ_STRLEN_MAX + 1]; |
| 5038 | char *lowzone; |
| 5039 | int type, |
| 5040 | val; |
| 5041 | pg_tz *tzp; |
| 5042 | struct pg_tm tm; |
| 5043 | fsec_t fsec; |
| 5044 | |
| 5045 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 5046 | PG_RETURN_TIMESTAMPTZ(timestamp); |
| 5047 | |
| 5048 | /* |
| 5049 | * Look up the requested timezone. First we look in the timezone |
| 5050 | * abbreviation table (to handle cases like "EST"), and if that fails, we |
| 5051 | * look in the timezone database (to handle cases like |
| 5052 | * "America/New_York"). (This matches the order in which timestamp input |
| 5053 | * checks the cases; it's important because the timezone database unwisely |
| 5054 | * uses a few zone names that are identical to offset abbreviations.) |
| 5055 | */ |
| 5056 | text_to_cstring_buffer(zone, tzname, sizeof(tzname)); |
| 5057 | |
| 5058 | /* DecodeTimezoneAbbrev requires lowercase input */ |
| 5059 | lowzone = downcase_truncate_identifier(tzname, |
| 5060 | strlen(tzname), |
| 5061 | false); |
| 5062 | |
| 5063 | type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); |
| 5064 | |
| 5065 | if (type == TZ || type == DTZ) |
| 5066 | { |
| 5067 | /* fixed-offset abbreviation */ |
| 5068 | tz = val; |
| 5069 | result = dt2local(timestamp, tz); |
| 5070 | } |
| 5071 | else if (type == DYNTZ) |
| 5072 | { |
| 5073 | /* dynamic-offset abbreviation, resolve using specified time */ |
| 5074 | if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0) |
| 5075 | ereport(ERROR, |
| 5076 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5077 | errmsg("timestamp out of range" ))); |
| 5078 | tz = -DetermineTimeZoneAbbrevOffset(&tm, tzname, tzp); |
| 5079 | result = dt2local(timestamp, tz); |
| 5080 | } |
| 5081 | else |
| 5082 | { |
| 5083 | /* try it as a full zone name */ |
| 5084 | tzp = pg_tzset(tzname); |
| 5085 | if (tzp) |
| 5086 | { |
| 5087 | /* Apply the timezone change */ |
| 5088 | if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, tzp) != 0) |
| 5089 | ereport(ERROR, |
| 5090 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5091 | errmsg("timestamp out of range" ))); |
| 5092 | tz = DetermineTimeZoneOffset(&tm, tzp); |
| 5093 | if (tm2timestamp(&tm, fsec, &tz, &result) != 0) |
| 5094 | ereport(ERROR, |
| 5095 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5096 | errmsg("timestamp out of range" ))); |
| 5097 | } |
| 5098 | else |
| 5099 | { |
| 5100 | ereport(ERROR, |
| 5101 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5102 | errmsg("time zone \"%s\" not recognized" , tzname))); |
| 5103 | result = 0; /* keep compiler quiet */ |
| 5104 | } |
| 5105 | } |
| 5106 | |
| 5107 | if (!IS_VALID_TIMESTAMP(result)) |
| 5108 | ereport(ERROR, |
| 5109 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5110 | errmsg("timestamp out of range" ))); |
| 5111 | |
| 5112 | PG_RETURN_TIMESTAMPTZ(result); |
| 5113 | } |
| 5114 | |
| 5115 | /* timestamp_izone() |
| 5116 | * Encode timestamp type with specified time interval as time zone. |
| 5117 | */ |
| 5118 | Datum |
| 5119 | timestamp_izone(PG_FUNCTION_ARGS) |
| 5120 | { |
| 5121 | Interval *zone = PG_GETARG_INTERVAL_P(0); |
| 5122 | Timestamp timestamp = PG_GETARG_TIMESTAMP(1); |
| 5123 | TimestampTz result; |
| 5124 | int tz; |
| 5125 | |
| 5126 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 5127 | PG_RETURN_TIMESTAMPTZ(timestamp); |
| 5128 | |
| 5129 | if (zone->month != 0 || zone->day != 0) |
| 5130 | ereport(ERROR, |
| 5131 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5132 | errmsg("interval time zone \"%s\" must not include months or days" , |
| 5133 | DatumGetCString(DirectFunctionCall1(interval_out, |
| 5134 | PointerGetDatum(zone)))))); |
| 5135 | |
| 5136 | tz = zone->time / USECS_PER_SEC; |
| 5137 | |
| 5138 | result = dt2local(timestamp, tz); |
| 5139 | |
| 5140 | if (!IS_VALID_TIMESTAMP(result)) |
| 5141 | ereport(ERROR, |
| 5142 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5143 | errmsg("timestamp out of range" ))); |
| 5144 | |
| 5145 | PG_RETURN_TIMESTAMPTZ(result); |
| 5146 | } /* timestamp_izone() */ |
| 5147 | |
| 5148 | /* TimestampTimestampTzRequiresRewrite() |
| 5149 | * |
| 5150 | * Returns false if the TimeZone GUC setting causes timestamp_timestamptz and |
| 5151 | * timestamptz_timestamp to be no-ops, where the return value has the same |
| 5152 | * bits as the argument. Since project convention is to assume a GUC changes |
| 5153 | * no more often than STABLE functions change, the answer is valid that long. |
| 5154 | */ |
| 5155 | bool |
| 5156 | TimestampTimestampTzRequiresRewrite(void) |
| 5157 | { |
| 5158 | long offset; |
| 5159 | |
| 5160 | if (pg_get_timezone_offset(session_timezone, &offset) && offset == 0) |
| 5161 | return false; |
| 5162 | return true; |
| 5163 | } |
| 5164 | |
| 5165 | /* timestamp_timestamptz() |
| 5166 | * Convert local timestamp to timestamp at GMT |
| 5167 | */ |
| 5168 | Datum |
| 5169 | timestamp_timestamptz(PG_FUNCTION_ARGS) |
| 5170 | { |
| 5171 | Timestamp timestamp = PG_GETARG_TIMESTAMP(0); |
| 5172 | |
| 5173 | PG_RETURN_TIMESTAMPTZ(timestamp2timestamptz(timestamp)); |
| 5174 | } |
| 5175 | |
| 5176 | static TimestampTz |
| 5177 | timestamp2timestamptz(Timestamp timestamp) |
| 5178 | { |
| 5179 | TimestampTz result; |
| 5180 | struct pg_tm tt, |
| 5181 | *tm = &tt; |
| 5182 | fsec_t fsec; |
| 5183 | int tz; |
| 5184 | |
| 5185 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 5186 | result = timestamp; |
| 5187 | else |
| 5188 | { |
| 5189 | if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0) |
| 5190 | ereport(ERROR, |
| 5191 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5192 | errmsg("timestamp out of range" ))); |
| 5193 | |
| 5194 | tz = DetermineTimeZoneOffset(tm, session_timezone); |
| 5195 | |
| 5196 | if (tm2timestamp(tm, fsec, &tz, &result) != 0) |
| 5197 | ereport(ERROR, |
| 5198 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5199 | errmsg("timestamp out of range" ))); |
| 5200 | } |
| 5201 | |
| 5202 | return result; |
| 5203 | } |
| 5204 | |
| 5205 | /* timestamptz_timestamp() |
| 5206 | * Convert timestamp at GMT to local timestamp |
| 5207 | */ |
| 5208 | Datum |
| 5209 | timestamptz_timestamp(PG_FUNCTION_ARGS) |
| 5210 | { |
| 5211 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(0); |
| 5212 | |
| 5213 | PG_RETURN_TIMESTAMP(timestamptz2timestamp(timestamp)); |
| 5214 | } |
| 5215 | |
| 5216 | static Timestamp |
| 5217 | timestamptz2timestamp(TimestampTz timestamp) |
| 5218 | { |
| 5219 | Timestamp result; |
| 5220 | struct pg_tm tt, |
| 5221 | *tm = &tt; |
| 5222 | fsec_t fsec; |
| 5223 | int tz; |
| 5224 | |
| 5225 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 5226 | result = timestamp; |
| 5227 | else |
| 5228 | { |
| 5229 | if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) |
| 5230 | ereport(ERROR, |
| 5231 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5232 | errmsg("timestamp out of range" ))); |
| 5233 | if (tm2timestamp(tm, fsec, NULL, &result) != 0) |
| 5234 | ereport(ERROR, |
| 5235 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5236 | errmsg("timestamp out of range" ))); |
| 5237 | } |
| 5238 | return result; |
| 5239 | } |
| 5240 | |
| 5241 | /* timestamptz_zone() |
| 5242 | * Evaluate timestamp with time zone type at the specified time zone. |
| 5243 | * Returns a timestamp without time zone. |
| 5244 | */ |
| 5245 | Datum |
| 5246 | timestamptz_zone(PG_FUNCTION_ARGS) |
| 5247 | { |
| 5248 | text *zone = PG_GETARG_TEXT_PP(0); |
| 5249 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); |
| 5250 | Timestamp result; |
| 5251 | int tz; |
| 5252 | char tzname[TZ_STRLEN_MAX + 1]; |
| 5253 | char *lowzone; |
| 5254 | int type, |
| 5255 | val; |
| 5256 | pg_tz *tzp; |
| 5257 | |
| 5258 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 5259 | PG_RETURN_TIMESTAMP(timestamp); |
| 5260 | |
| 5261 | /* |
| 5262 | * Look up the requested timezone. First we look in the timezone |
| 5263 | * abbreviation table (to handle cases like "EST"), and if that fails, we |
| 5264 | * look in the timezone database (to handle cases like |
| 5265 | * "America/New_York"). (This matches the order in which timestamp input |
| 5266 | * checks the cases; it's important because the timezone database unwisely |
| 5267 | * uses a few zone names that are identical to offset abbreviations.) |
| 5268 | */ |
| 5269 | text_to_cstring_buffer(zone, tzname, sizeof(tzname)); |
| 5270 | |
| 5271 | /* DecodeTimezoneAbbrev requires lowercase input */ |
| 5272 | lowzone = downcase_truncate_identifier(tzname, |
| 5273 | strlen(tzname), |
| 5274 | false); |
| 5275 | |
| 5276 | type = DecodeTimezoneAbbrev(0, lowzone, &val, &tzp); |
| 5277 | |
| 5278 | if (type == TZ || type == DTZ) |
| 5279 | { |
| 5280 | /* fixed-offset abbreviation */ |
| 5281 | tz = -val; |
| 5282 | result = dt2local(timestamp, tz); |
| 5283 | } |
| 5284 | else if (type == DYNTZ) |
| 5285 | { |
| 5286 | /* dynamic-offset abbreviation, resolve using specified time */ |
| 5287 | int isdst; |
| 5288 | |
| 5289 | tz = DetermineTimeZoneAbbrevOffsetTS(timestamp, tzname, tzp, &isdst); |
| 5290 | result = dt2local(timestamp, tz); |
| 5291 | } |
| 5292 | else |
| 5293 | { |
| 5294 | /* try it as a full zone name */ |
| 5295 | tzp = pg_tzset(tzname); |
| 5296 | if (tzp) |
| 5297 | { |
| 5298 | /* Apply the timezone change */ |
| 5299 | struct pg_tm tm; |
| 5300 | fsec_t fsec; |
| 5301 | |
| 5302 | if (timestamp2tm(timestamp, &tz, &tm, &fsec, NULL, tzp) != 0) |
| 5303 | ereport(ERROR, |
| 5304 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5305 | errmsg("timestamp out of range" ))); |
| 5306 | if (tm2timestamp(&tm, fsec, NULL, &result) != 0) |
| 5307 | ereport(ERROR, |
| 5308 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5309 | errmsg("timestamp out of range" ))); |
| 5310 | } |
| 5311 | else |
| 5312 | { |
| 5313 | ereport(ERROR, |
| 5314 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5315 | errmsg("time zone \"%s\" not recognized" , tzname))); |
| 5316 | result = 0; /* keep compiler quiet */ |
| 5317 | } |
| 5318 | } |
| 5319 | |
| 5320 | if (!IS_VALID_TIMESTAMP(result)) |
| 5321 | ereport(ERROR, |
| 5322 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5323 | errmsg("timestamp out of range" ))); |
| 5324 | |
| 5325 | PG_RETURN_TIMESTAMP(result); |
| 5326 | } |
| 5327 | |
| 5328 | /* timestamptz_izone() |
| 5329 | * Encode timestamp with time zone type with specified time interval as time zone. |
| 5330 | * Returns a timestamp without time zone. |
| 5331 | */ |
| 5332 | Datum |
| 5333 | timestamptz_izone(PG_FUNCTION_ARGS) |
| 5334 | { |
| 5335 | Interval *zone = PG_GETARG_INTERVAL_P(0); |
| 5336 | TimestampTz timestamp = PG_GETARG_TIMESTAMPTZ(1); |
| 5337 | Timestamp result; |
| 5338 | int tz; |
| 5339 | |
| 5340 | if (TIMESTAMP_NOT_FINITE(timestamp)) |
| 5341 | PG_RETURN_TIMESTAMP(timestamp); |
| 5342 | |
| 5343 | if (zone->month != 0 || zone->day != 0) |
| 5344 | ereport(ERROR, |
| 5345 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5346 | errmsg("interval time zone \"%s\" must not include months or days" , |
| 5347 | DatumGetCString(DirectFunctionCall1(interval_out, |
| 5348 | PointerGetDatum(zone)))))); |
| 5349 | |
| 5350 | tz = -(zone->time / USECS_PER_SEC); |
| 5351 | |
| 5352 | result = dt2local(timestamp, tz); |
| 5353 | |
| 5354 | if (!IS_VALID_TIMESTAMP(result)) |
| 5355 | ereport(ERROR, |
| 5356 | (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), |
| 5357 | errmsg("timestamp out of range" ))); |
| 5358 | |
| 5359 | PG_RETURN_TIMESTAMP(result); |
| 5360 | } |
| 5361 | |
| 5362 | /* generate_series_timestamp() |
| 5363 | * Generate the set of timestamps from start to finish by step |
| 5364 | */ |
| 5365 | Datum |
| 5366 | generate_series_timestamp(PG_FUNCTION_ARGS) |
| 5367 | { |
| 5368 | FuncCallContext *funcctx; |
| 5369 | generate_series_timestamp_fctx *fctx; |
| 5370 | Timestamp result; |
| 5371 | |
| 5372 | /* stuff done only on the first call of the function */ |
| 5373 | if (SRF_IS_FIRSTCALL()) |
| 5374 | { |
| 5375 | Timestamp start = PG_GETARG_TIMESTAMP(0); |
| 5376 | Timestamp finish = PG_GETARG_TIMESTAMP(1); |
| 5377 | Interval *step = PG_GETARG_INTERVAL_P(2); |
| 5378 | MemoryContext oldcontext; |
| 5379 | Interval interval_zero; |
| 5380 | |
| 5381 | /* create a function context for cross-call persistence */ |
| 5382 | funcctx = SRF_FIRSTCALL_INIT(); |
| 5383 | |
| 5384 | /* |
| 5385 | * switch to memory context appropriate for multiple function calls |
| 5386 | */ |
| 5387 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 5388 | |
| 5389 | /* allocate memory for user context */ |
| 5390 | fctx = (generate_series_timestamp_fctx *) |
| 5391 | palloc(sizeof(generate_series_timestamp_fctx)); |
| 5392 | |
| 5393 | /* |
| 5394 | * Use fctx to keep state from call to call. Seed current with the |
| 5395 | * original start value |
| 5396 | */ |
| 5397 | fctx->current = start; |
| 5398 | fctx->finish = finish; |
| 5399 | fctx->step = *step; |
| 5400 | |
| 5401 | /* Determine sign of the interval */ |
| 5402 | MemSet(&interval_zero, 0, sizeof(Interval)); |
| 5403 | fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); |
| 5404 | |
| 5405 | if (fctx->step_sign == 0) |
| 5406 | ereport(ERROR, |
| 5407 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5408 | errmsg("step size cannot equal zero" ))); |
| 5409 | |
| 5410 | funcctx->user_fctx = fctx; |
| 5411 | MemoryContextSwitchTo(oldcontext); |
| 5412 | } |
| 5413 | |
| 5414 | /* stuff done on every call of the function */ |
| 5415 | funcctx = SRF_PERCALL_SETUP(); |
| 5416 | |
| 5417 | /* |
| 5418 | * get the saved state and use current as the result for this iteration |
| 5419 | */ |
| 5420 | fctx = funcctx->user_fctx; |
| 5421 | result = fctx->current; |
| 5422 | |
| 5423 | if (fctx->step_sign > 0 ? |
| 5424 | timestamp_cmp_internal(result, fctx->finish) <= 0 : |
| 5425 | timestamp_cmp_internal(result, fctx->finish) >= 0) |
| 5426 | { |
| 5427 | /* increment current in preparation for next iteration */ |
| 5428 | fctx->current = DatumGetTimestamp( |
| 5429 | DirectFunctionCall2(timestamp_pl_interval, |
| 5430 | TimestampGetDatum(fctx->current), |
| 5431 | PointerGetDatum(&fctx->step))); |
| 5432 | |
| 5433 | /* do when there is more left to send */ |
| 5434 | SRF_RETURN_NEXT(funcctx, TimestampGetDatum(result)); |
| 5435 | } |
| 5436 | else |
| 5437 | { |
| 5438 | /* do when there is no more left */ |
| 5439 | SRF_RETURN_DONE(funcctx); |
| 5440 | } |
| 5441 | } |
| 5442 | |
| 5443 | /* generate_series_timestamptz() |
| 5444 | * Generate the set of timestamps from start to finish by step |
| 5445 | */ |
| 5446 | Datum |
| 5447 | generate_series_timestamptz(PG_FUNCTION_ARGS) |
| 5448 | { |
| 5449 | FuncCallContext *funcctx; |
| 5450 | generate_series_timestamptz_fctx *fctx; |
| 5451 | TimestampTz result; |
| 5452 | |
| 5453 | /* stuff done only on the first call of the function */ |
| 5454 | if (SRF_IS_FIRSTCALL()) |
| 5455 | { |
| 5456 | TimestampTz start = PG_GETARG_TIMESTAMPTZ(0); |
| 5457 | TimestampTz finish = PG_GETARG_TIMESTAMPTZ(1); |
| 5458 | Interval *step = PG_GETARG_INTERVAL_P(2); |
| 5459 | MemoryContext oldcontext; |
| 5460 | Interval interval_zero; |
| 5461 | |
| 5462 | /* create a function context for cross-call persistence */ |
| 5463 | funcctx = SRF_FIRSTCALL_INIT(); |
| 5464 | |
| 5465 | /* |
| 5466 | * switch to memory context appropriate for multiple function calls |
| 5467 | */ |
| 5468 | oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 5469 | |
| 5470 | /* allocate memory for user context */ |
| 5471 | fctx = (generate_series_timestamptz_fctx *) |
| 5472 | palloc(sizeof(generate_series_timestamptz_fctx)); |
| 5473 | |
| 5474 | /* |
| 5475 | * Use fctx to keep state from call to call. Seed current with the |
| 5476 | * original start value |
| 5477 | */ |
| 5478 | fctx->current = start; |
| 5479 | fctx->finish = finish; |
| 5480 | fctx->step = *step; |
| 5481 | |
| 5482 | /* Determine sign of the interval */ |
| 5483 | MemSet(&interval_zero, 0, sizeof(Interval)); |
| 5484 | fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); |
| 5485 | |
| 5486 | if (fctx->step_sign == 0) |
| 5487 | ereport(ERROR, |
| 5488 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
| 5489 | errmsg("step size cannot equal zero" ))); |
| 5490 | |
| 5491 | funcctx->user_fctx = fctx; |
| 5492 | MemoryContextSwitchTo(oldcontext); |
| 5493 | } |
| 5494 | |
| 5495 | /* stuff done on every call of the function */ |
| 5496 | funcctx = SRF_PERCALL_SETUP(); |
| 5497 | |
| 5498 | /* |
| 5499 | * get the saved state and use current as the result for this iteration |
| 5500 | */ |
| 5501 | fctx = funcctx->user_fctx; |
| 5502 | result = fctx->current; |
| 5503 | |
| 5504 | if (fctx->step_sign > 0 ? |
| 5505 | timestamp_cmp_internal(result, fctx->finish) <= 0 : |
| 5506 | timestamp_cmp_internal(result, fctx->finish) >= 0) |
| 5507 | { |
| 5508 | /* increment current in preparation for next iteration */ |
| 5509 | fctx->current = DatumGetTimestampTz( |
| 5510 | DirectFunctionCall2(timestamptz_pl_interval, |
| 5511 | TimestampTzGetDatum(fctx->current), |
| 5512 | PointerGetDatum(&fctx->step))); |
| 5513 | |
| 5514 | /* do when there is more left to send */ |
| 5515 | SRF_RETURN_NEXT(funcctx, TimestampTzGetDatum(result)); |
| 5516 | } |
| 5517 | else |
| 5518 | { |
| 5519 | /* do when there is no more left */ |
| 5520 | SRF_RETURN_DONE(funcctx); |
| 5521 | } |
| 5522 | } |
| 5523 | |