From b6d15590f7269536c4da5b4cd835bc1e4f2cdf43 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> 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 47727a1f412..d168891d4e7 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.435 2008/05/04 21:13:35 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.436 2008/05/04 23:19:23 tgl Exp $ --> <chapter id="functions"> <title>Functions and Operators</title> @@ -10650,6 +10650,16 @@ AND </entry> </row> + <row> + <entry><literal><function>generate_series</function>(<parameter>start</parameter>, <parameter>stop</parameter>, <parameter>step</parameter> <type>interval</>)</literal></entry> + <entry><type>timestamp</type> or <type>timestamp with time zone</type></entry> + <entry><type>setof timestamp</type> or <type>setof timestamp with time zone</type> (same as argument type)</entry> + <entry> + Generate a series of values, from <parameter>start</parameter> to <parameter>stop</parameter> + with a step size of <parameter>step</parameter> + </entry> + </row> + </tbody> </tgroup> </table> @@ -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) </programlisting> </para> <table id="functions-srf-subscripts"> - - <indexterm> - <primary>generate_subscripts</primary> - </indexterm> - - <title>Subscripts Generating Functions</title> + <title>Subscript Generating Functions</title> <tgroup cols="3"> <thead> <row> @@ -10711,7 +10732,7 @@ select current_date + s.a as dates from generate_series(0,14,7) as s(a); <tbody> <row> - <entry><literal><function>generate_subscripts</function>(<parameter>array annyarray</parameter>, <parameter>dim int</parameter>)</literal></entry> + <entry><literal><function>generate_subscripts</function>(<parameter>array anyarray</parameter>, <parameter>dim int</parameter>)</literal></entry> <entry><type>setof int</type></entry> <entry> 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); </row> <row> - <entry><literal><function>generate_subscripts</function>(<parameter>array annyarray</parameter>, <parameter>dim int</parameter>, <parameter>reverse boolean</parameter>)</literal></entry> + <entry><literal><function>generate_subscripts</function>(<parameter>array anyarray</parameter>, <parameter>dim int</parameter>, <parameter>reverse boolean</parameter>)</literal></entry> <entry><type>setof int</type></entry> <entry> 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); </tgroup> </table> + <indexterm> + <primary>generate_subscripts</primary> + </indexterm> + <para> + <function>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: <programlisting> -- 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 25d95b60efb..c266ed0f38c 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 751cb8e8f72..cea905dbd28 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 8f9954692b3..75a0b5900d7 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 31ad788ebf1..6b2efaf8d78 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);