1 /*
2 * PostRR - src/rrtimeslice.c
3 * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 /*
29 * A PostgreSQL data-type providing a timeslice implementing round-robin
30 * features.
31 */
33 #include "postrr.h"
34 #include "utils/pg_spi.h"
36 #include <string.h>
38 #include <postgres.h>
39 #include <fmgr.h>
41 /* Postgres utilities */
42 #include <access/hash.h>
43 #include <executor/spi.h>
44 #include <utils/array.h>
45 #include <utils/datetime.h>
46 #include <utils/timestamp.h>
47 #include <miscadmin.h> /* DateStyle */
49 #ifdef HAVE_INT64_TIMESTAMP
50 # define TSTAMP_TO_INT64(t) (t)
51 # define INT64_TO_TSTAMP(i) (i)
52 #else /* ! HAVE_INT64_TIMESTAMP */
53 # define TSTAMP_TO_INT64(t) (int64)((t) * (double)USECS_PER_SEC)
54 # define INT64_TO_TSTAMP(i) ((double)(i) / (double)USECS_PER_SEC)
55 #endif
57 /*
58 * data type
59 */
61 struct rrtimeslice {
62 TimestampTz tstamp;
63 int32 tsid;
64 uint32 seq;
65 };
67 /*
68 * internal helper functions
69 */
71 static int32
72 rrtimeslice_set_spec(int32 len, int32 num)
73 {
74 int spi_rc;
76 char query[256];
77 int32 typmod = 0;
79 if ((len <= 0) || (num <= 0))
80 ereport(ERROR, (
81 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
82 errmsg("rrtimeslice(%i, %i) "
83 "length/num may not be less than zero",
84 len, num)
85 ));
87 if ((spi_rc = SPI_connect()) != SPI_OK_CONNECT)
88 ereport(ERROR, (
89 errmsg("failed to store rrtimeslice spec: "
90 "could not connect to SPI manager: %s",
91 SPI_result_code_string(spi_rc))
92 ));
94 snprintf(query, sizeof(query),
95 "SELECT tsid FROM postrr.rrtimeslices "
96 "WHERE tslen = %d AND tsnum = %d "
97 "LIMIT 1", len, num);
98 query[sizeof(query) - 1] = '\0';
100 spi_rc = pg_spi_get_int(query, 1, &typmod);
101 if (spi_rc == PG_SPI_OK) {
102 SPI_finish();
103 return typmod;
104 }
105 else if (spi_rc != PG_SPI_ERROR_NO_VALUES)
106 pg_spi_ereport(ERROR, "store rrtimeslice spec", spi_rc);
108 snprintf(query, sizeof(query),
109 "SELECT nextval('postrr.tsid'::regclass)");
110 query[sizeof(query) - 1] = '\0';
112 spi_rc = pg_spi_get_int(query, 1, &typmod);
113 if ((spi_rc != PG_SPI_OK) || (typmod <= 0))
114 pg_spi_ereport(ERROR, "retrieve nextval(postrr.tsid)", spi_rc);
116 snprintf(query, sizeof(query),
117 "INSERT INTO postrr.rrtimeslices(tsid, tslen, tsnum) "
118 "VALUES (%d, %d, %d)", typmod, len, num);
119 query[sizeof(query) - 1] = '\0';
121 spi_rc = SPI_exec(query, /* max num rows = */ 1);
122 if (spi_rc != SPI_OK_INSERT)
123 ereport(ERROR, (
124 errmsg("failed to store rrtimeslice spec: "
125 "failed to execute query: %s",
126 SPI_result_code_string(spi_rc))
127 ));
129 SPI_finish();
130 return typmod;
131 } /* rrtimeslice_set_spec */
133 static int
134 rrtimeslice_get_spec(int32 typmod, int32 *len, int32 *num)
135 {
136 int spi_rc;
138 char query[256];
140 if (typmod <= 0)
141 return -1;
143 if ((spi_rc = SPI_connect()) != SPI_OK_CONNECT)
144 ereport(ERROR, (
145 errmsg("failed to determine rrtimeslice spec: "
146 "could not connect to SPI manager: %s",
147 SPI_result_code_string(spi_rc))
148 ));
150 snprintf(query, sizeof(query),
151 "SELECT tslen, tsnum FROM postrr.rrtimeslices "
152 "WHERE tsid = %d", typmod);
153 query[sizeof(query) - 1] = '\0';
155 spi_rc = pg_spi_get_int(query, 2, len, num);
156 if (spi_rc != PG_SPI_OK)
157 pg_spi_ereport(ERROR, "determine rrtimeslice spec", spi_rc);
159 SPI_finish();
160 return 0;
161 } /* rrtimeslice_get_spec */
163 static int
164 rrtimeslice_apply_typmod(rrtimeslice_t *tslice, int32 typmod)
165 {
166 int64 tstamp;
167 int64 length;
168 int64 seq;
170 int32 len = 0;
171 int32 num = 0;
173 if (rrtimeslice_get_spec(typmod, &len, &num))
174 return -1;
176 if ((len <= 0) || (num <= 0))
177 ereport(ERROR, (
178 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
179 errmsg("rrtimeslice(%i, %i) "
180 "length/num may not be less than zero",
181 len, num)
182 ));
184 tstamp = TSTAMP_TO_INT64(tslice->tstamp);
186 length = len * USECS_PER_SEC;
187 if (tstamp % length != 0)
188 tstamp = tstamp - (tstamp % length) + length;
189 seq = tstamp % (length * num) / length;
190 seq = seq % num;
192 tslice->tstamp = INT64_TO_TSTAMP(tstamp);
193 tslice->tsid = typmod;
194 tslice->seq = (uint32)seq;
195 return 0;
196 } /* rrtimeslice_apply_typmod */
198 /*
199 * rrtimeslice_cmp_unify:
200 * Unify two RRTimeslices in order to prepare them for comparison. That is, if
201 * either one of the arguments does not have any typmod applied, then apply
202 * the typmod of the other argument. Throws an error if the typmods don't
203 * match.
204 *
205 * Returns:
206 * - 0 if the arguments could be unified
207 * - 1 if only the first argument is NULL
208 * - 2 if both arguments are NULL
209 * - 3 if only the second argument is NULL
210 */
211 static int
212 rrtimeslice_cmp_unify(rrtimeslice_t *ts1, rrtimeslice_t *ts2)
213 {
214 if ((! ts1) && (! ts2))
215 return 0;
216 else if (! ts1)
217 return -1;
218 else if (! ts2)
219 return 1;
221 if (ts1->tsid && (! ts2->tsid))
222 rrtimeslice_apply_typmod(ts2, ts1->tsid);
223 else if ((! ts1->tsid) && ts2->tsid)
224 rrtimeslice_apply_typmod(ts1, ts2->tsid);
226 if (ts1->tsid != ts2->tsid) /* XXX: compare len/num */
227 ereport(ERROR, (
228 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
229 errmsg("invalid comparison: cannot compare "
230 "rrtimeslices with different typmods (yet)")
231 ));
232 return 0;
233 } /* rrtimeslice_cmp_unify */
235 /*
236 * prototypes for PostgreSQL functions
237 */
239 PG_FUNCTION_INFO_V1(rrtimeslice_validate);
241 PG_FUNCTION_INFO_V1(rrtimeslice_in);
242 PG_FUNCTION_INFO_V1(rrtimeslice_out);
243 PG_FUNCTION_INFO_V1(rrtimeslice_typmodin);
244 PG_FUNCTION_INFO_V1(rrtimeslice_typmodout);
246 PG_FUNCTION_INFO_V1(rrtimeslice_to_rrtimeslice);
247 PG_FUNCTION_INFO_V1(timestamptz_to_rrtimeslice);
248 PG_FUNCTION_INFO_V1(rrtimeslice_to_timestamptz);
250 PG_FUNCTION_INFO_V1(rrtimeslice_cmp);
252 PG_FUNCTION_INFO_V1(rrtimeslice_seq_eq);
253 PG_FUNCTION_INFO_V1(rrtimeslice_seq_ne);
254 PG_FUNCTION_INFO_V1(rrtimeslice_seq_lt);
255 PG_FUNCTION_INFO_V1(rrtimeslice_seq_gt);
256 PG_FUNCTION_INFO_V1(rrtimeslice_seq_le);
257 PG_FUNCTION_INFO_V1(rrtimeslice_seq_ge);
258 PG_FUNCTION_INFO_V1(rrtimeslice_seq_cmp);
259 PG_FUNCTION_INFO_V1(rrtimeslice_seq_hash);
261 /*
262 * public API
263 */
265 Datum
266 rrtimeslice_validate(PG_FUNCTION_ARGS)
267 {
268 char type_info[1024];
269 char *result;
270 size_t req_len;
271 size_t len;
273 if (PG_NARGS() != 1)
274 ereport(ERROR, (
275 errmsg("rrtimeslice_validate() expect one argument"),
276 errhint("Usage rrtimeslice_validate(expected_size)")
277 ));
279 req_len = (size_t)PG_GETARG_UINT32(0);
280 len = sizeof(rrtimeslice_t);
282 if (req_len != len)
283 ereport(ERROR, (
284 errmsg("length of the rrtimeslice type "
285 "does not match the expected length"),
286 errhint("Please report a bug against PostRR")
287 ));
289 snprintf(type_info, sizeof(type_info),
290 "rrtimeslice validated successfully; type length = %zu", len);
291 type_info[sizeof(type_info) - 1] = '\0';
293 result = pstrdup(type_info);
294 PG_RETURN_CSTRING(result);
295 } /* rrtimeslice_validate */
297 Datum
298 rrtimeslice_in(PG_FUNCTION_ARGS)
299 {
300 rrtimeslice_t *tslice;
302 TimestampTz tstamp = 0;
303 int32 typmod;
305 struct pg_tm tm;
306 fsec_t fsec = 0;
307 int tz = 0;
309 char *time_str;
310 int pg_dt_err;
311 char buf[MAXDATELEN + MAXDATEFIELDS];
312 char *field[MAXDATEFIELDS];
313 int ftype[MAXDATEFIELDS];
314 int num_fields = 0;
315 int dtype = 0;
317 if (PG_NARGS() != 3)
318 ereport(ERROR, (
319 errmsg("rrtimeslice_in() expects three arguments"),
320 errhint("Usage: rrtimeslice_in(col_name, oid, typmod)")
321 ));
323 tslice = (rrtimeslice_t *)palloc0(sizeof(*tslice));
324 time_str = PG_GETARG_CSTRING(0);
325 typmod = PG_GETARG_INT32(2);
327 pg_dt_err = ParseDateTime(time_str, buf, sizeof(buf),
328 field, ftype, MAXDATEFIELDS, &num_fields);
330 if (! pg_dt_err)
331 pg_dt_err = DecodeDateTime(field, ftype, num_fields,
332 &dtype, &tm, &fsec, &tz);
333 if (pg_dt_err)
334 DateTimeParseError(pg_dt_err, time_str, "rrtimeslice");
336 switch (dtype) {
337 case DTK_DATE:
338 if (tm2timestamp(&tm, fsec, &tz, &tstamp))
339 ereport(ERROR, (
340 errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
341 errmsg("timestamp out of range: %s", time_str)
342 ));
343 break;
345 case DTK_EPOCH:
346 tstamp = SetEpochTimestamp();
347 break;
349 default:
350 ereport(ERROR, (
351 errmsg("unexpected dtype %d while "
352 "parsing rrtimeslice: %s", dtype, time_str)
353 ));
354 }
356 tslice->tstamp = tstamp;
358 /* most likely, this won't happen … coerce_type
359 * (src/backend/parser/parse_coerce.c) does not pass that information to
360 * the input function but rather lets a length conversion cast do that
361 * work */
362 if (typmod > 0)
363 rrtimeslice_apply_typmod(tslice, typmod);
365 PG_RETURN_RRTIMESLICE_P(tslice);
366 } /* rrtimeslice_in */
368 Datum
369 rrtimeslice_out(PG_FUNCTION_ARGS)
370 {
371 rrtimeslice_t *tslice;
373 struct pg_tm tm;
374 fsec_t fsec = 0;
375 int tz = 0;
377 char *tz_str = NULL;
379 char ts_str[MAXDATELEN + 1];
380 char buf_l[MAXDATELEN + 1];
381 char buf_u[MAXDATELEN + 1];
382 char *result;
384 int32 len = 0;
385 int32 num = 0;
387 if (PG_NARGS() != 1)
388 ereport(ERROR, (
389 errmsg("rrtimeslice_out() expects one argument"),
390 errhint("Usage: rrtimeslice_out(rrtimeslice)")
391 ));
393 tslice = PG_GETARG_RRTIMESLICE_P(0);
395 if (TIMESTAMP_NOT_FINITE(tslice->tstamp)
396 || timestamp2tm(tslice->tstamp, &tz, &tm, &fsec, &tz_str, NULL))
397 ereport(ERROR, (
398 errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
399 errmsg("invalid (non-finite) timestamp")
400 ));
402 EncodeDateTime(&tm, fsec, &tz, &tz_str, DateStyle, buf_u);
404 if (! rrtimeslice_get_spec(tslice->tsid, &len, &num)) {
405 TimestampTz lower = tslice->tstamp - (len * USECS_PER_SEC);
407 if (timestamp2tm(lower, &tz, &tm, &fsec, &tz_str, NULL))
408 ereport(ERROR, (
409 errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
410 errmsg("invalid (non-finite) lower timestamp")
411 ));
413 EncodeDateTime(&tm, fsec, &tz, &tz_str, DateStyle, buf_l);
414 }
415 else {
416 strncpy(buf_l, "ERR", sizeof(buf_l));
417 buf_l[sizeof(buf_l) - 1] = '\0';
418 }
420 snprintf(ts_str, sizeof(ts_str), "(\"%s\", \"%s\"] #%i/%i",
421 buf_l, buf_u, tslice->seq, num);
423 result = pstrdup(ts_str);
424 PG_RETURN_CSTRING(result);
425 } /* rrtimeslice_out */
427 Datum
428 rrtimeslice_typmodin(PG_FUNCTION_ARGS)
429 {
430 ArrayType *tm_array;
432 int32 *spec;
433 int spec_elems = 0;
434 int32 typmod;
436 if (PG_NARGS() != 1)
437 ereport(ERROR, (
438 errmsg("rrtimeslice_typmodin() expects one argument"),
439 errhint("Usage: rrtimeslice_typmodin(array)")
440 ));
442 tm_array = PG_GETARG_ARRAYTYPE_P(0);
444 spec = ArrayGetIntegerTypmods(tm_array, &spec_elems);
445 if (spec_elems != 2)
446 ereport(ERROR, (
447 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
448 errmsg("invalid rrtimeslice type modifier"),
449 errhint("Usage: rrtimeslice(<slice_len>, <num>)")
450 ));
452 typmod = rrtimeslice_set_spec(spec[0], spec[1]);
453 PG_RETURN_INT32(typmod);
454 } /* rrtimeslice_typmodin */
456 Datum
457 rrtimeslice_typmodout(PG_FUNCTION_ARGS)
458 {
459 int32 typmod;
460 char tm_str[1024];
461 char *result;
463 int32 len = 0;
464 int32 num = 0;
466 if (PG_NARGS() != 1)
467 ereport(ERROR, (
468 errmsg("rrtimeslice_typmodout() expects one argument"),
469 errhint("Usage: rrtimeslice_typmodout(typmod)")
470 ));
472 typmod = PG_GETARG_INT32(0);
473 if (rrtimeslice_get_spec(typmod, &len, &num))
474 tm_str[0] = '\0';
475 else if ((len <= 0) || (num <= 0))
476 snprintf(tm_str, sizeof(tm_str), "(#ERR, #ERR)");
477 else
478 snprintf(tm_str, sizeof(tm_str), "(%d, %d)", len, num);
480 result = pstrdup(tm_str);
481 PG_RETURN_CSTRING(result);
482 } /* rrtimeslice_typmodout */
484 Datum
485 rrtimeslice_to_rrtimeslice(PG_FUNCTION_ARGS)
486 {
487 rrtimeslice_t *tslice;
488 int32 typmod;
490 if (PG_NARGS() != 3)
491 ereport(ERROR, (
492 errmsg("rrtimeslice_to_rrtimeslice() "
493 "expects three arguments"),
494 errhint("Usage: rrtimeslice_to_rrtimeslice"
495 "(rrtimeslice, typmod, is_explicit)")
496 ));
498 tslice = PG_GETARG_RRTIMESLICE_P(0);
499 typmod = PG_GETARG_INT32(1);
501 if (typmod > 0) {
502 if ((! tslice->tsid) && (! tslice->seq))
503 rrtimeslice_apply_typmod(tslice, typmod);
504 else
505 ereport(ERROR, (
506 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
507 errmsg("invalid cast: cannot cast rrtimeslices "
508 "with different typmod (yet)")
509 ));
510 }
512 PG_RETURN_RRTIMESLICE_P(tslice);
513 } /* rrtimeslice_to_rrtimeslice */
515 Datum
516 timestamptz_to_rrtimeslice(PG_FUNCTION_ARGS)
517 {
518 TimestampTz tstamp;
519 int32 typmod;
521 rrtimeslice_t *tslice;
523 if (PG_NARGS() != 3)
524 ereport(ERROR, (
525 errmsg("timestamptz_to_rrtimeslice() "
526 "expects three arguments"),
527 errhint("Usage: timestamptz_to_rrtimeslice"
528 "(timestamptz, typmod, is_explicit)")
529 ));
531 tstamp = PG_GETARG_TIMESTAMPTZ(0);
532 typmod = PG_GETARG_INT32(1);
534 tslice = (rrtimeslice_t *)palloc0(sizeof(*tslice));
536 tslice->tstamp = tstamp;
537 if (typmod >= 0)
538 rrtimeslice_apply_typmod(tslice, typmod);
540 PG_RETURN_RRTIMESLICE_P(tslice);
541 } /* timestamptz_to_rrtimeslice */
543 Datum
544 rrtimeslice_to_timestamptz(PG_FUNCTION_ARGS)
545 {
546 rrtimeslice_t *tslice;
548 if (PG_NARGS() != 1)
549 ereport(ERROR, (
550 errmsg("rrtimeslice_to_timestamptz() "
551 "expects one argument"),
552 errhint("Usage: rrtimeslice_to_timestamptz"
553 "(rrtimeslice)")
554 ));
556 tslice = PG_GETARG_RRTIMESLICE_P(0);
557 PG_RETURN_TIMESTAMPTZ(tslice->tstamp);
558 } /* rrtimeslice_to_timestamptz */
560 int
561 rrtimeslice_cmp_internal(rrtimeslice_t *ts1, rrtimeslice_t *ts2)
562 {
563 int status;
565 status = rrtimeslice_cmp_unify(ts1, ts2);
566 if (status) /* [1, 3] -> [-1, 1] */
567 return status - 2;
569 if (ts1->tstamp == ts2->tstamp)
570 return 0;
571 else if ((ts1->seq == ts2->seq) && (ts1->tstamp < ts2->tstamp))
572 return -1;
573 else if (ts1->tstamp < ts2->tstamp)
574 return -2;
575 else if ((ts1->seq == ts2->seq) && (ts1->tstamp > ts2->tstamp))
576 return 1;
577 else
578 return 2;
579 } /* rrtimeslice_cmp_internal */
581 Datum
582 rrtimeslice_cmp(PG_FUNCTION_ARGS)
583 {
584 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
585 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
587 PG_RETURN_INT32(rrtimeslice_cmp_internal(ts1, ts2));
588 } /* rrtimeslice_cmp */
590 int
591 rrtimeslice_seq_cmp_internal(rrtimeslice_t *ts1, rrtimeslice_t *ts2)
592 {
593 int status;
595 status = rrtimeslice_cmp_unify(ts1, ts2);
596 if (status) /* [1, 3] -> [-1, 1] */
597 return status - 2;
599 if (ts1->seq < ts2->seq)
600 return -1;
601 else if (ts1->seq == ts2->seq)
602 return 0;
603 else
604 return 1;
605 } /* rrtimeslice_seq_cmp_internal */
607 Datum
608 rrtimeslice_seq_eq(PG_FUNCTION_ARGS)
609 {
610 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
611 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
613 PG_RETURN_BOOL(rrtimeslice_seq_cmp_internal(ts1, ts2) == 0);
614 } /* rrtimeslice_seq_eq */
616 Datum
617 rrtimeslice_seq_ne(PG_FUNCTION_ARGS)
618 {
619 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
620 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
622 PG_RETURN_BOOL(rrtimeslice_seq_cmp_internal(ts1, ts2) != 0);
623 } /* rrtimeslice_seq_ne */
625 Datum
626 rrtimeslice_seq_lt(PG_FUNCTION_ARGS)
627 {
628 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
629 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
631 PG_RETURN_BOOL(rrtimeslice_seq_cmp_internal(ts1, ts2) < 0);
632 } /* rrtimeslice_seq_lt */
634 Datum
635 rrtimeslice_seq_le(PG_FUNCTION_ARGS)
636 {
637 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
638 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
640 PG_RETURN_BOOL(rrtimeslice_seq_cmp_internal(ts1, ts2) <= 0);
641 } /* rrtimeslice_seq_le */
643 Datum
644 rrtimeslice_seq_gt(PG_FUNCTION_ARGS)
645 {
646 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
647 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
649 PG_RETURN_BOOL(rrtimeslice_seq_cmp_internal(ts1, ts2) > 0);
650 } /* rrtimeslice_seq_gt */
652 Datum
653 rrtimeslice_seq_ge(PG_FUNCTION_ARGS)
654 {
655 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
656 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
658 PG_RETURN_BOOL(rrtimeslice_seq_cmp_internal(ts1, ts2) >= 0);
659 } /* rrtimeslice_seq_ge */
661 Datum
662 rrtimeslice_seq_cmp(PG_FUNCTION_ARGS)
663 {
664 rrtimeslice_t *ts1 = PG_GETARG_RRTIMESLICE_P(0);
665 rrtimeslice_t *ts2 = PG_GETARG_RRTIMESLICE_P(1);
667 PG_RETURN_INT32(rrtimeslice_seq_cmp_internal(ts1, ts2));
668 } /* rrtimeslice_seq_cmp */
670 Datum
671 rrtimeslice_seq_hash(PG_FUNCTION_ARGS)
672 {
673 rrtimeslice_t *ts = PG_GETARG_RRTIMESLICE_P(0);
674 return hash_uint32(ts->seq);
675 } /* rrtimeslice_seq_hash */
677 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */