From b6d15590f7269536c4da5b4cd835bc1e4f2cdf43 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 4 May 2008 23:19:24 +0000 Subject: [PATCH] Add timestamp and timestamptz versions of generate_series(). Hitoshi Harada --- doc/src/sgml/func.sgml | 48 ++++++-- src/backend/utils/adt/timestamp.c | 181 +++++++++++++++++++++++++++++- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_proc.h | 6 +- src/include/utils/timestamp.h | 5 +- 5 files changed, 229 insertions(+), 15 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 47727a1f41..d168891d4e 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -10650,6 +10650,16 @@ AND + + generate_series(start, stop, step interval) + timestamp or timestamp with time zone + setof timestamp or setof timestamp with time zone (same as argument type) + + Generate a series of values, from start to stop + with a step size of step + + + @@ -10683,6 +10693,7 @@ select * from generate_series(4,3); ----------------- (0 rows) +-- this example relies on the date-plus-integer operator select current_date + s.a as dates from generate_series(0,14,7) as s(a); dates ------------ @@ -10690,16 +10701,26 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a); 2004-02-12 2004-02-19 (3 rows) + +select * from generate_series('2008-03-01 00:00'::timestamp, + '2008-03-04 12:00', '10 hours'); + generate_series +--------------------- + 2008-03-01 00:00:00 + 2008-03-01 10:00:00 + 2008-03-01 20:00:00 + 2008-03-02 06:00:00 + 2008-03-02 16:00:00 + 2008-03-03 02:00:00 + 2008-03-03 12:00:00 + 2008-03-03 22:00:00 + 2008-03-04 08:00:00 +(9 rows) - - - generate_subscripts - - - Subscripts Generating Functions + Subscript Generating Functions @@ -10711,7 +10732,7 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a); - generate_subscripts(array annyarray, dim int) + generate_subscripts(array anyarray, dim int) setof int Generate a series comprising the given array's subscripts. @@ -10719,7 +10740,7 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a); - generate_subscripts(array annyarray, dim int, reverse boolean) + generate_subscripts(array anyarray, dim int, reverse boolean) setof int Generate a series comprising the given array's subscripts. When @@ -10732,10 +10753,17 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a);
+ + generate_subscripts + + + generate_subscripts is a convenience function that generates + the set of valid subscripts for the specified dimension of the given + array. Zero rows are returned for arrays that do not have the requested dimension, or for NULL arrays (but valid subscripts are returned for NULL array - elements.) Some examples follow: + elements). Some examples follow: -- basic usage select generate_subscripts('{NULL,1,NULL,2}'::int[], 1) as s; diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 25d95b60ef..c266ed0f38 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.188 2008/05/04 21:13:35 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.189 2008/05/04 23:19:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "access/hash.h" #include "access/xact.h" #include "catalog/pg_type.h" +#include "funcapi.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "parser/scansup.h" @@ -45,6 +46,22 @@ TimestampTz PgStartTime; /* Set at configuration reload */ TimestampTz PgReloadTime; +typedef struct +{ + Timestamp current; + Timestamp finish; + Interval step; + int step_sign; +} generate_series_timestamp_fctx; + +typedef struct +{ + TimestampTz current; + TimestampTz finish; + Interval step; + int step_sign; +} generate_series_timestamptz_fctx; + static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec); static int EncodeSpecialTimestamp(Timestamp dt, char *str); @@ -4651,3 +4668,165 @@ timestamptz_izone(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMP(result); } + +/* generate_series_timestamp() + * Generate the set of timestamps from start to finish by step + */ +Datum +generate_series_timestamp(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + generate_series_timestamp_fctx *fctx; + Timestamp result; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + Timestamp start = PG_GETARG_TIMESTAMP(0); + Timestamp finish = PG_GETARG_TIMESTAMP(1); + Interval *step = PG_GETARG_INTERVAL_P(2); + MemoryContext oldcontext; + Interval interval_zero; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (generate_series_timestamp_fctx *) + palloc(sizeof(generate_series_timestamp_fctx)); + + /* + * Use fctx to keep state from call to call. Seed current with the + * original start value + */ + fctx->current = start; + fctx->finish = finish; + fctx->step = *step; + + /* Determine sign of the interval */ + MemSet(&interval_zero, 0, sizeof(Interval)); + fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); + + if (fctx->step_sign == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot equal zero"))); + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + /* + * get the saved state and use current as the result for this iteration + */ + fctx = funcctx->user_fctx; + result = fctx->current; + + if (fctx->step_sign > 0 ? + timestamp_cmp_internal(result, fctx->finish) <= 0 : + timestamp_cmp_internal(result, fctx->finish) >= 0) + { + /* increment current in preparation for next iteration */ + fctx->current = DatumGetTimestamp( + DirectFunctionCall2(timestamp_pl_interval, + TimestampGetDatum(fctx->current), + PointerGetDatum(&fctx->step))); + + /* do when there is more left to send */ + SRF_RETURN_NEXT(funcctx, TimestampGetDatum(result)); + } + else + { + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); + } +} + +/* generate_series_timestamptz() + * Generate the set of timestamps from start to finish by step + */ +Datum +generate_series_timestamptz(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + generate_series_timestamptz_fctx *fctx; + TimestampTz result; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + TimestampTz start = PG_GETARG_TIMESTAMPTZ(0); + TimestampTz finish = PG_GETARG_TIMESTAMPTZ(1); + Interval *step = PG_GETARG_INTERVAL_P(2); + MemoryContext oldcontext; + Interval interval_zero; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (generate_series_timestamptz_fctx *) + palloc(sizeof(generate_series_timestamptz_fctx)); + + /* + * Use fctx to keep state from call to call. Seed current with the + * original start value + */ + fctx->current = start; + fctx->finish = finish; + fctx->step = *step; + + /* Determine sign of the interval */ + MemSet(&interval_zero, 0, sizeof(Interval)); + fctx->step_sign = interval_cmp_internal(&fctx->step, &interval_zero); + + if (fctx->step_sign == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot equal zero"))); + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + + /* + * get the saved state and use current as the result for this iteration + */ + fctx = funcctx->user_fctx; + result = fctx->current; + + if (fctx->step_sign > 0 ? + timestamp_cmp_internal(result, fctx->finish) <= 0 : + timestamp_cmp_internal(result, fctx->finish) >= 0) + { + /* increment current in preparation for next iteration */ + fctx->current = DatumGetTimestampTz( + DirectFunctionCall2(timestamptz_pl_interval, + TimestampTzGetDatum(fctx->current), + PointerGetDatum(&fctx->step))); + + /* do when there is more left to send */ + SRF_RETURN_NEXT(funcctx, TimestampTzGetDatum(result)); + } + else + { + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); + } +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 751cb8e8f7..cea905dbd2 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.454 2008/05/04 21:13:35 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.455 2008/05/04 23:19:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200805041 +#define CATALOG_VERSION_NO 200805042 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 8f9954692b..75a0b5900d 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.495 2008/05/04 21:13:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.496 2008/05/04 23:19:23 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -3872,6 +3872,10 @@ DATA(insert OID = 1068 ( generate_series PGNSP PGUID 12 1 1000 f f t t i 3 20 " DESCR("non-persistent series generator"); DATA(insert OID = 1069 ( generate_series PGNSP PGUID 12 1 1000 f f t t i 2 20 "20 20" _null_ _null_ _null_ generate_series_int8 - _null_ _null_ )); DESCR("non-persistent series generator"); +DATA(insert OID = 938 ( generate_series PGNSP PGUID 12 1 1000 f f t t i 3 1114 "1114 1114 1186" _null_ _null_ _null_ generate_series_timestamp - _null_ _null_ )); +DESCR("non-persistent series generator"); +DATA(insert OID = 939 ( generate_series PGNSP PGUID 12 1 1000 f f t t s 3 1184 "1184 1184 1186" _null_ _null_ _null_ generate_series_timestamptz - _null_ _null_ )); +DESCR("non-persistent series generator"); /* boolean aggregates */ DATA(insert OID = 2515 ( booland_statefunc PGNSP PGUID 12 1 0 f f t f i 2 16 "16 16" _null_ _null_ _null_ booland_statefunc - _null_ _null_ )); diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h index 31ad788ebf..6b2efaf8d7 100644 --- a/src/include/utils/timestamp.h +++ b/src/include/utils/timestamp.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.77 2008/05/04 21:13:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.78 2008/05/04 23:19:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -308,6 +308,9 @@ extern Datum clock_timestamp(PG_FUNCTION_ARGS); extern Datum pg_postmaster_start_time(PG_FUNCTION_ARGS); extern Datum pg_conf_load_time(PG_FUNCTION_ARGS); +extern Datum generate_series_timestamp(PG_FUNCTION_ARGS); +extern Datum generate_series_timestamptz(PG_FUNCTION_ARGS); + /* Internal routines (not fmgr-callable) */ extern TimestampTz GetCurrentTimestamp(void);