-<!-- $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>
</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>
-----------------
(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
------------
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>
<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.
</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
</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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
/* 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);
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);
+ }
+}