2014-10-05 15:21 GMT+07:00 Ali Akbar <the.ap...@gmail.com>: > Hi > 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 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 >>> >> > - i think you can use the fctx->current variable without temporary > variable (there's comment in the add_var function: Full version of add > functionality on variable level (handling signs). result might point to one > of the operands too without danger.). But you _must_ switch the context > first because add_var will allocate new array for the data and freeing the > old one. > > - numeric can be NaN. We must reject it as first, finish and last > parameter. > - numeric datatype is large, but there are limitations. According to doc, > the limit is: up to 131072 digits before the decimal point; up to 16383 > digits after the decimal point. How can we check if the next step > overflows? As a comparison, in int.c, generate_series_step_int4 checks if > its overflows, and stop the next call by setting step to 0. Should we do > that? > > ~ will try to fix the patch later > attached the patch. Not checking if it overflows, but testing it with 9 * 10 ^ 131072 works (not resulting in an error). Other modifications: - doc update - regresssion tests - while testing regression test, opr_sanity checks that the generate_series_numeric function is used twice (once for 2 parameter and once for the 3 parameter function), so i changed the name to generate_series_step_numeric and created new function generate_series_numeric that calls generate_series_step_numeric
-- Ali Akbar
*** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** *** 14074,14081 **** AND <tbody> <row> <entry><literal><function>generate_series(<parameter>start</parameter>, <parameter>stop</parameter>)</function></literal></entry> ! <entry><type>int</type> or <type>bigint</type></entry> ! <entry><type>setof int</type> or <type>setof bigint</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 one --- 14074,14081 ---- <tbody> <row> <entry><literal><function>generate_series(<parameter>start</parameter>, <parameter>stop</parameter>)</function></literal></entry> ! <entry><type>int</type>, <type>bigint</type> or <type>numeric</type></entry> ! <entry><type>setof int</type>, <type>setof bigint</type>, or <type>setof numeric</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 one *************** *** 14084,14091 **** AND <row> <entry><literal><function>generate_series(<parameter>start</parameter>, <parameter>stop</parameter>, <parameter>step</parameter>)</function></literal></entry> ! <entry><type>int</type> or <type>bigint</type></entry> ! <entry><type>setof int</type> or <type>setof bigint</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> --- 14084,14091 ---- <row> <entry><literal><function>generate_series(<parameter>start</parameter>, <parameter>stop</parameter>, <parameter>step</parameter>)</function></literal></entry> ! <entry><type>int</type>, <type>bigint</type> or <type>numeric</type></entry> ! <entry><type>setof int</type>, <type>setof bigint</type> or <type>setof numeric</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> *** a/src/backend/utils/adt/numeric.c --- b/src/backend/utils/adt/numeric.c *************** *** 28,33 **** --- 28,34 ---- #include "access/hash.h" #include "catalog/pg_type.h" + #include "funcapi.h" #include "libpq/pqformat.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" *************** *** 261,266 **** typedef struct NumericVar --- 262,279 ---- /* ---------- + * Data for generate series + * ---------- + */ + typedef struct + { + NumericVar current; + NumericVar finish; + NumericVar step; + } generate_series_numeric_fctx; + + + /* ---------- * Some preinitialized constants * ---------- */ *************** *** 1221,1226 **** numeric_floor(PG_FUNCTION_ARGS) --- 1234,1346 ---- PG_RETURN_NUMERIC(res); } + + /* + * generate_series_numeric() - + * + * Generate series of numeric. + */ + Datum + generate_series_numeric(PG_FUNCTION_ARGS) { + return generate_series_step_numeric(fcinfo); + } + + Datum + generate_series_step_numeric(PG_FUNCTION_ARGS) + { + generate_series_numeric_fctx *fctx; + FuncCallContext *funcctx; + MemoryContext oldcontext; + + if (SRF_IS_FIRSTCALL()) + { + Numeric start_num = PG_GETARG_NUMERIC(0); + Numeric finish_num = PG_GETARG_NUMERIC(1); + NumericVar steploc = const_one; + + /* Handle NaN in start & finish */ + if (NUMERIC_IS_NAN(start_num) || NUMERIC_IS_NAN(finish_num)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("start and finish cannot be NaN"))); + } + + /* see if we were given an explicit step size */ + if (PG_NARGS() == 3) { + Numeric step_num = PG_GETARG_NUMERIC(2); + + if (NUMERIC_IS_NAN(step_num)) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot be NaN"))); + } + + init_var_from_num(step_num, &steploc); + + if (cmp_var(&steploc, &const_zero) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("step size cannot equal 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_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; + 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; + + if ((fctx->step.sign == NUMERIC_POS && + cmp_var(&fctx->current, &fctx->finish) <= 0) || + (fctx->step.sign == NUMERIC_NEG && + cmp_var(&fctx->current, &fctx->finish) >= 0)) + { + Numeric result = make_result(&fctx->current); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* increment current in preparation for next iteration */ + add_var(&fctx->current, &fctx->step, &fctx->current); + + + MemoryContextSwitchTo(oldcontext); + /* do when there is more left to send */ + SRF_RETURN_NEXT(funcctx, NumericGetDatum(result)); + } + else + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); + } + + /* * Implements the numeric version of the width_bucket() function * defined by SQL2003. See also width_bucket_float8(). *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** *** 3923,3928 **** DATA(insert OID = 1068 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t --- 3923,3932 ---- 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_step_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_ )); *** a/src/include/utils/builtins.h --- b/src/include/utils/builtins.h *************** *** 1040,1045 **** extern Datum int8_avg(PG_FUNCTION_ARGS); --- 1040,1047 ---- 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); + extern Datum generate_series_step_numeric(PG_FUNCTION_ARGS); /* ri_triggers.c */ extern Datum RI_FKey_check_ins(PG_FUNCTION_ARGS); *** a/src/test/regress/expected/numeric.out --- b/src/test/regress/expected/numeric.out *************** *** 1409,1411 **** select 10.0 ^ 2147483647 as overflows; --- 1409,1627 ---- ERROR: value overflows numeric format select 117743296169.0 ^ 1000000000 as overflows; ERROR: value overflows numeric format + -- + -- Non persistent generate series + -- + select * from generate_series(0.1::numeric, 10.0::numeric); + generate_series + ----------------- + 0.1 + 1.1 + 2.1 + 3.1 + 4.1 + 5.1 + 6.1 + 7.1 + 8.1 + 9.1 + (10 rows) + + select * from generate_series(0.1::numeric, 10.0::numeric, 0.1::numeric); + generate_series + ----------------- + 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 0.6 + 0.7 + 0.8 + 0.9 + 1.0 + 1.1 + 1.2 + 1.3 + 1.4 + 1.5 + 1.6 + 1.7 + 1.8 + 1.9 + 2.0 + 2.1 + 2.2 + 2.3 + 2.4 + 2.5 + 2.6 + 2.7 + 2.8 + 2.9 + 3.0 + 3.1 + 3.2 + 3.3 + 3.4 + 3.5 + 3.6 + 3.7 + 3.8 + 3.9 + 4.0 + 4.1 + 4.2 + 4.3 + 4.4 + 4.5 + 4.6 + 4.7 + 4.8 + 4.9 + 5.0 + 5.1 + 5.2 + 5.3 + 5.4 + 5.5 + 5.6 + 5.7 + 5.8 + 5.9 + 6.0 + 6.1 + 6.2 + 6.3 + 6.4 + 6.5 + 6.6 + 6.7 + 6.8 + 6.9 + 7.0 + 7.1 + 7.2 + 7.3 + 7.4 + 7.5 + 7.6 + 7.7 + 7.8 + 7.9 + 8.0 + 8.1 + 8.2 + 8.3 + 8.4 + 8.5 + 8.6 + 8.7 + 8.8 + 8.9 + 9.0 + 9.1 + 9.2 + 9.3 + 9.4 + 9.5 + 9.6 + 9.7 + 9.8 + 9.9 + 10.0 + (100 rows) + + select * from generate_series(0.1::numeric, -10.0::numeric, -0.25::numeric); + generate_series + ----------------- + 0.1 + -0.15 + -0.40 + -0.65 + -0.90 + -1.15 + -1.40 + -1.65 + -1.90 + -2.15 + -2.40 + -2.65 + -2.90 + -3.15 + -3.40 + -3.65 + -3.90 + -4.15 + -4.40 + -4.65 + -4.90 + -5.15 + -5.40 + -5.65 + -5.90 + -6.15 + -6.40 + -6.65 + -6.90 + -7.15 + -7.40 + -7.65 + -7.90 + -8.15 + -8.40 + -8.65 + -8.90 + -9.15 + -9.40 + -9.65 + -9.90 + (41 rows) + + select * from generate_series(100::numeric, -100::numeric, 10::numeric); + generate_series + ----------------- + (0 rows) + + select * from generate_series(-100::numeric, 100::numeric, -5::numeric); + generate_series + ----------------- + (0 rows) + + select * from generate_series(-100::numeric, 100::numeric, 0::numeric); + ERROR: step size cannot equal zero + select * from generate_series(-100::numeric, 100::numeric, 'nan'::numeric); + ERROR: step size cannot be NaN + select * from generate_series('nan'::numeric, 100::numeric, 0::numeric); + ERROR: start and finish cannot be NaN + select * from generate_series(0::numeric, 'nan'::numeric, 0::numeric); + ERROR: start and finish cannot be NaN + -- big generate (checks the edge of overflow, output truncated) + select (i / (10::numeric ^ 131071))::numeric(1,0) + from generate_series(-9*(10::numeric ^ 131071), + 9*(10::numeric ^ 131071), + (10::numeric ^ 131071)) + as a(i); + numeric + --------- + -9 + -8 + -7 + -6 + -5 + -4 + -3 + -2 + -1 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + (19 rows) + *** a/src/test/regress/sql/numeric.sql --- b/src/test/regress/sql/numeric.sql *************** *** 837,839 **** select 10.0 ^ -2147483648 as rounds_to_zero; --- 837,859 ---- select 10.0 ^ -2147483647 as rounds_to_zero; select 10.0 ^ 2147483647 as overflows; select 117743296169.0 ^ 1000000000 as overflows; + + -- + -- Non persistent generate series + -- + select * from generate_series(0.1::numeric, 10.0::numeric); + select * from generate_series(0.1::numeric, 10.0::numeric, 0.1::numeric); + select * from generate_series(0.1::numeric, -10.0::numeric, -0.25::numeric); + select * from generate_series(100::numeric, -100::numeric, 10::numeric); + select * from generate_series(-100::numeric, 100::numeric, -5::numeric); + select * from generate_series(-100::numeric, 100::numeric, 0::numeric); + select * from generate_series(-100::numeric, 100::numeric, 'nan'::numeric); + select * from generate_series('nan'::numeric, 100::numeric, 0::numeric); + select * from generate_series(0::numeric, 'nan'::numeric, 0::numeric); + + -- big generate (checks the edge of overflow, output truncated) + select (i / (10::numeric ^ 131071))::numeric(1,0) + from generate_series(-9*(10::numeric ^ 131071), + 9*(10::numeric ^ 131071), + (10::numeric ^ 131071)) + as a(i);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers