2014-10-06 22:51 GMT+07:00 Marti Raudsepp <[email protected]>:
> That's fine I think, it's just for tracking who made the changes in
> the CommitFest app. What actually matters is what you write in the
> "Author" field, which could contain all 3 names separated by commas.
>
Ok. Added to commitfest:
https://commitfest.postgresql.org/action/patch_view?id=1591
> > the one that tests values just before numeric overflow
>
> Actually I don't know if that's too useful. I think you should add a
> test case that causes an error to be thrown.
>
Actually i added the test case because in the code, when adding step into
current for the last value, i expected it to overflow:
/* increment current in preparation for next iteration */
add_var(&fctx->current, &fctx->step, &fctx->current);
where in the last calculation, current is 9 * 10^131071. Plus 10^131071, it
will be 10^131072, which i expected to overflow numeric type (in the doc,
numeric's range is "up to 131072 digits before the decimal point").
In attached patch, i narrowed the test case to produce smaller result.
Also, I noticed that there are a few trailing spaces in the patch that
> should be removed:
>
> +generate_series_numeric(PG_FUNCTION_ARGS)
> ...
> + if (NUMERIC_IS_NAN(start_num) ||
> NUMERIC_IS_NAN(finish_num))
> ...
> + if (PG_NARGS() == 3)
> ...
> + if (NUMERIC_IS_NAN(step_num))
>
Ah, didn't see it. Thanks. Fixed in this patch.
Regards,
--
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,1477 ----
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, 5.0::numeric);
+ generate_series
+ -----------------
+ 0.1
+ 1.1
+ 2.1
+ 3.1
+ 4.1
+ (5 rows)
+
+ select * from generate_series(0.1::numeric, 1.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
+ (10 rows)
+
+ select * from generate_series(0.1::numeric, -2.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
+ (9 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) - 1,
+ 9*(10::numeric ^ 131071),
+ (10::numeric ^ 131071))
+ as a(i);
+ numeric
+ ---------
+ 9
+ (1 row)
+
*** 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,858 ----
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, 5.0::numeric);
+ select * from generate_series(0.1::numeric, 1.0::numeric, 0.1::numeric);
+ select * from generate_series(0.1::numeric, -2.0::numeric, -0.25::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) - 1,
+ 9*(10::numeric ^ 131071),
+ (10::numeric ^ 131071))
+ as a(i);
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers