On Tue, Sep 30, 2014 at 10:00 AM, Michael Paquier <michael.paqu...@gmail.com > wrote:
> > > On Mon, Sep 29, 2014 at 4:19 PM, Michael Paquier < > michael.paqu...@gmail.com> wrote: > >> >> Michael >> Btw, while looking at your patch, I actually hacked it a bit and finished >> with the attached: >> - changed process to use NumericVar instead of Numeric >> - addition of custom step values with a function >> generate_series(numeric,numeric,numeric) >> - some cleanup and some comments here and there >> That's still WIP, but feel free to use it for future work. If you are >> able to add documentation and regression tests to this patch, I would >> recommend that you register it to the next commit fest, where it would get >> more review, and hopefully it will get committed. >> > Oops, it seems that I have been too hasty here. With a fresh mind I looked > at my own patch again and found two bugs: > - Incorrect initialization of step variable with const_one > - Incorrect calculation of each step's value, making stuff crash, it is > necessary to switch to the context of the function to perform operations on > a temporary variable first. > And here is the patch... -- Michael
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 2d6a4cb..4c2ff5b 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -28,6 +28,7 @@ #include "access/hash.h" #include "catalog/pg_type.h" +#include "funcapi.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" @@ -260,6 +261,13 @@ typedef struct NumericVar } NumericVar; +typedef struct +{ + NumericVar current; + NumericVar finish; + NumericVar step; +} generate_series_numeric_fctx; + /* ---------- * Some preinitialized constants * ---------- @@ -1221,6 +1229,108 @@ numeric_floor(PG_FUNCTION_ARGS) PG_RETURN_NUMERIC(res); } + +/* + * generate_series_numeric() - + * + * Generate series of numeric. + */ +Datum +generate_series_numeric(PG_FUNCTION_ARGS) +{ + generate_series_numeric_fctx *fctx; + FuncCallContext *funcctx; + Numeric res; + + if (SRF_IS_FIRSTCALL()) + { + Numeric start_num = PG_GETARG_NUMERIC(0); + Numeric finish_num = PG_GETARG_NUMERIC(1); + NumericVar steploc; + MemoryContext oldcontext; + + /* see if we were given an explicit step size */ + if (PG_NARGS() == 3) + { + Numeric step_num; + step_num = PG_GETARG_NUMERIC(2); + + /* Transform step into a variable that can be manipulated */ + init_var_from_num(step_num, &steploc); + } + else + { + init_var(&steploc); + set_var_from_var(&const_one, &steploc); + } + + /* Step cannot be zero */ + if (cmp_var(&steploc, &const_zero) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot equal zero"))); + + /* + * switch to memory context appropriate for multiple function calls + */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* allocate memory for user context */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (generate_series_numeric_fctx *) + palloc(sizeof(generate_series_numeric_fctx)); + + /* + * Use fctx to keep state from call to call. Seed current with the + * original start value + */ + init_var_from_num(start_num, &(fctx->current)); + init_var_from_num(finish_num, &(fctx->finish)); + init_var(&(fctx->step)); + set_var_from_var(&steploc, &(fctx->step)); + funcctx->user_fctx = fctx; + free_var(&steploc); + 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; + res = make_result(&(fctx->current)); + + if ((cmp_var(&(fctx->step), &const_zero) > 0 && + cmp_var(&(fctx->current), &(fctx->finish)) <= 0) || + (cmp_var(&(fctx->step), &const_zero) < 0 && + cmp_var(&(fctx->current), &(fctx->finish)) >= 0)) + { + NumericVar tmp; + MemoryContext oldcontext; + + /* + * Increment for next iteration and perform operation in the context + * of this function. Switch back to memory context of function before + * performing any operations. + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + init_var(&tmp); + add_var(&(fctx->current), &(fctx->step), &tmp); + set_var_from_var(&tmp, &(fctx->current)); + free_var(&tmp); + MemoryContextSwitchTo(oldcontext); + SRF_RETURN_NEXT(funcctx, NumericGetDatum(res)); + } + else + { + SRF_RETURN_DONE(funcctx); + } +} + /* * Implements the numeric version of the width_bucket() function * defined by SQL2003. See also width_bucket_float8(). diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 3ce9849..ccdf3e8 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3923,6 +3923,10 @@ DATA(insert OID = 1068 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t DESCR("non-persistent series generator"); DATA(insert OID = 1069 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t i 2 0 20 "20 20" _null_ _null_ _null_ _null_ generate_series_int8 _null_ _null_ _null_ )); DESCR("non-persistent series generator"); +DATA(insert OID = 6000 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t i 3 0 1700 "1700 1700 1700" _null_ _null_ _null_ _null_ generate_series_numeric _null_ _null_ _null_ )); +DESCR("non-persistent series generator"); +DATA(insert OID = 6001 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t i 2 0 1700 "1700 1700" _null_ _null_ _null_ _null_ generate_series_numeric _null_ _null_ _null_ )); +DESCR("non-persistent series generator"); DATA(insert OID = 938 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t i 3 0 1114 "1114 1114 1186" _null_ _null_ _null_ _null_ generate_series_timestamp _null_ _null_ _null_ )); 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 3 0 1184 "1184 1184 1186" _null_ _null_ _null_ _null_ generate_series_timestamptz _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index d88e7a3..511518d 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1040,6 +1040,7 @@ extern Datum int8_avg(PG_FUNCTION_ARGS); extern Datum int2int4_sum(PG_FUNCTION_ARGS); extern Datum width_bucket_numeric(PG_FUNCTION_ARGS); extern Datum hash_numeric(PG_FUNCTION_ARGS); +extern Datum generate_series_numeric(PG_FUNCTION_ARGS); /* ri_triggers.c */ extern Datum RI_FKey_check_ins(PG_FUNCTION_ARGS);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers