diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 9c143b2..15ebe47 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -14657,6 +14657,26 @@ AND + + generate_series(start, stop) + date + setof date + + Generate a series of values, from start to stop + with a step size of one day + + + + + generate_series(start, stop, step integer) + date + setof date + + Generate a series of values, from start to stop + with a step size of step + + + @@ -14721,6 +14741,26 @@ SELECT * FROM generate_series('2008-03-01 00:00'::timestamp, 2008-03-03 22:00:00 2008-03-04 08:00:00 (9 rows) + +SELECT d.date_val FROM generate_series('1991-09-24'::date,'1991-10-01'::date) as d(date_val); + date_val +------------ + 1991-09-24 + 1991-09-25 + 1991-09-26 + 1991-09-27 + 1991-09-28 + 1991-09-29 + 1991-09-30 + 1991-10-01 +(8 rows) + +SELECT d.date_val FROM generate_series('1991-09-24'::date,'1991-10-01'::date,7) as d(date_val); + date_val +------------ + 1991-09-24 + 1991-10-01 +(2 rows) diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 332db7e..7404a2f 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -30,6 +30,7 @@ #include "utils/datetime.h" #include "utils/nabstime.h" #include "utils/sortsupport.h" +#include "funcapi.h" /* * gcc's -ffast-math switch breaks routines that expect exact results from @@ -2811,3 +2812,97 @@ timetz_izone(PG_FUNCTION_ARGS) PG_RETURN_TIMETZADT_P(result); } + +/* Corey BEGIN */ +typedef struct +{ + DateADT current; + DateADT finish; + int32 step; +} generate_series_date_fctx; + + +/* generate_series_date() + * Generate the set of dates from start to finish by step + */ +Datum +generate_series_date(PG_FUNCTION_ARGS) +{ + return generate_series_step_date(fcinfo); +} + +Datum +generate_series_step_date(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + generate_series_date_fctx *fctx; + DateADT result; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + { + DateADT start = PG_GETARG_DATEADT(0); + DateADT finish = PG_GETARG_DATEADT(1); + int32 step = 1; + + /* see if we were given an explicit step size */ + if (PG_NARGS() == 3) + step = PG_GETARG_INT32(2); + if (step == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot equal zero"))); + + MemoryContext oldcontext; + + /* 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_date_fctx *) + palloc(sizeof(generate_series_date_fctx)); + + /* + * Use fctx to keep state from call to call. Seed current with the + * original start value + */ + fctx->current = start; + fctx->finish = finish; + /* cannot iterate infinity */ + if (DATE_NOT_FINITE(start) || DATE_NOT_FINITE(finish)) + fctx->step = 0; + else + fctx->step = step; + + 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 > 0 && fctx->current <= fctx->finish) || + (fctx->step < 0 && fctx->current >= fctx->finish)) + { + /* increment current in preparation for next iteration */ + fctx->current += fctx->step; + + /* do when there is more left to send */ + SRF_RETURN_NEXT(funcctx, DateADTGetDatum(result)); + } + else + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); +} + diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 79e92ff..65293eb 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4013,6 +4013,11 @@ DATA(insert OID = 938 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t DESCR("non-persistent series generator"); DATA(insert OID = 939 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t s s 3 0 1184 "1184 1184 1186" _null_ _null_ _null_ _null_ _null_ generate_series_timestamptz _null_ _null_ _null_ )); DESCR("non-persistent series generator"); +DATA(insert OID = 2739 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t s s 3 0 1082 "1082 1082 23" _null_ _null_ "{start,finish,step}" _null_ _null_ generate_series_step_date _null_ _null_ _null_ )); +DESCR("non-persistent series generator"); +DATA(insert OID = 2740 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t s s 2 0 1082 "1082 1082" _null_ _null_ "{start,finish}" _null_ _null_ generate_series_date _null_ _null_ _null_ )); +DESCR("non-persistent series generator"); + /* boolean aggregates */ DATA(insert OID = 2515 ( booland_statefunc PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "16 16" _null_ _null_ _null_ _null_ _null_ booland_statefunc _null_ _null_ _null_ )); diff --git a/src/include/utils/date.h b/src/include/utils/date.h index 1b962af..f427cde 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -205,4 +205,7 @@ extern Datum timetz_izone(PG_FUNCTION_ARGS); extern Datum timetz_pl_interval(PG_FUNCTION_ARGS); extern Datum timetz_mi_interval(PG_FUNCTION_ARGS); +extern Datum generate_series_date(PG_FUNCTION_ARGS); +extern Datum generate_series_step_date(PG_FUNCTION_ARGS); + #endif /* DATE_H */ diff --git a/src/test/regress/expected/date.out b/src/test/regress/expected/date.out index 56c5520..427f3df 100644 --- a/src/test/regress/expected/date.out +++ b/src/test/regress/expected/date.out @@ -1452,3 +1452,42 @@ select make_time(10, 55, 100.1); ERROR: time field value out of range: 10:55:100.1 select make_time(24, 0, 2.1); ERROR: time field value out of range: 24:00:2.1 +SET datestyle TO iso; +SELECT d.date_val FROM generate_series('1991-09-24'::date,'1991-10-01'::date) as d(date_val); + date_val +------------ + 1991-09-24 + 1991-09-25 + 1991-09-26 + 1991-09-27 + 1991-09-28 + 1991-09-29 + 1991-09-30 + 1991-10-01 +(8 rows) + +SELECT d.date_val FROM generate_series('1991-09-24'::date,'1991-10-01'::date,7) as d(date_val); + date_val +------------ + 1991-09-24 + 1991-10-01 +(2 rows) + +SELECT d.date_val FROM generate_series('1999-12-31'::date,'1999-12-29'::date,-1) as d(date_val); + date_val +------------ + 1999-12-31 + 1999-12-30 + 1999-12-29 +(3 rows) + +SELECT d.date_val FROM generate_series('-infinity'::date,'1999-12-29'::date) as d(date_val); + date_val +---------- +(0 rows) + +SELECT d.date_val FROM generate_series('1991-09-24'::date,'infinity'::date) as d(date_val); + date_val +---------- +(0 rows) + diff --git a/src/test/regress/sql/date.sql b/src/test/regress/sql/date.sql index e40b4c4..b790e32 100644 --- a/src/test/regress/sql/date.sql +++ b/src/test/regress/sql/date.sql @@ -340,3 +340,10 @@ select make_date(2013, 11, -1); select make_date(-44, 3, 15); -- perhaps we should allow this sometime? select make_time(10, 55, 100.1); select make_time(24, 0, 2.1); + +SET datestyle TO iso; +SELECT d.date_val FROM generate_series('1991-09-24'::date,'1991-10-01'::date) as d(date_val); +SELECT d.date_val FROM generate_series('1991-09-24'::date,'1991-10-01'::date,7) as d(date_val); +SELECT d.date_val FROM generate_series('1999-12-31'::date,'1999-12-29'::date,-1) as d(date_val); +SELECT d.date_val FROM generate_series('-infinity'::date,'1999-12-29'::date) as d(date_val); +SELECT d.date_val FROM generate_series('1991-09-24'::date,'infinity'::date) as d(date_val);