]> granicus.if.org Git - postgresql/commitdiff
Add generate_series(numeric, numeric).
authorFujii Masao <fujii@postgresql.org>
Tue, 11 Nov 2014 12:44:46 +0000 (21:44 +0900)
committerFujii Masao <fujii@postgresql.org>
Tue, 11 Nov 2014 12:44:46 +0000 (21:44 +0900)
Платон Малюгин
Reviewed by Michael Paquier, Ali Akbar and Marti Raudsepp

doc/src/sgml/func.sgml
src/backend/utils/adt/numeric.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h
src/test/regress/expected/numeric.out
src/test/regress/sql/numeric.sql

index 7e5bcd98ddc5ca7c964553138bed2848b7ee3bc0..b58cfa578d5d2af842198afb1a3d19e3c46e6ea7 100644 (file)
@@ -14076,8 +14076,8 @@ 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><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
@@ -14086,8 +14086,8 @@ 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><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>
@@ -14137,6 +14137,14 @@ SELECT * FROM generate_series(4,3);
 -----------------
 (0 rows)
 
+SELECT generate_series(1.1, 4, 1.3);
+ generate_series 
+-----------------
+             1.1
+             2.4
+             3.7
+(3 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
index 9b3f5b9410786e28230a7c3cf80bf5a83b8a4a06..d61af920cda2dde4ea4ed78ba5fdf684d2be2c3e 100644 (file)
@@ -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,18 @@ typedef struct NumericVar
 } NumericVar;
 
 
+/* ----------
+ * Data for generate_series
+ * ----------
+ */
+typedef struct
+{
+       NumericVar      current;
+       NumericVar      stop;
+       NumericVar      step;
+} generate_series_numeric_fctx;
+
+
 /* ----------
  * Some preinitialized constants
  * ----------
@@ -1229,6 +1242,117 @@ numeric_floor(PG_FUNCTION_ARGS)
        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         stop_num = PG_GETARG_NUMERIC(1);
+               NumericVar      steploc = const_one;
+
+               /* handle NaN in start and stop values */
+               if (NUMERIC_IS_NAN(start_num))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("start value cannot be NaN")));
+
+               if (NUMERIC_IS_NAN(stop_num))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("stop value 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(stop_num, &fctx->stop);
+               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 state as the result of this
+        * iteration.
+        */
+       fctx = funcctx->user_fctx;
+
+       if ((fctx->step.sign == NUMERIC_POS &&
+                cmp_var(&fctx->current, &fctx->stop) <= 0) ||
+               (fctx->step.sign == NUMERIC_NEG &&
+                cmp_var(&fctx->current, &fctx->stop) >= 0))
+       {
+               Numeric result = make_result(&fctx->current);
+
+               /* switch to memory context appropriate for iteration calculation */
+               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().
index b5c5e7aa5e8222ccb7b35146e29310d3536378c1..bdaf4176f6a0ae45ca028b1aa9992d9bde354691 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201411071
+#define CATALOG_VERSION_NO     201411111
 
 #endif
index 497e652674b2221d5501b1685032b634c66ade77..5d4e889af455d61c20d3586d96c9c40a3499506e 100644 (file)
@@ -3952,6 +3952,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 = 3259 (  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 = 3260 (  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_ ));
index 4e74d85d22895b1fa0eed3b10aed87bc4ff98d8c..3ba34f88eec7be1b26c8d481990a8b91ec2d5b2b 100644 (file)
@@ -1029,6 +1029,8 @@ 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);
+extern Datum generate_series_step_numeric(PG_FUNCTION_ARGS);
 
 /* ri_triggers.c */
 extern Datum RI_FKey_check_ins(PG_FUNCTION_ARGS);
index 5fafdaf13f5bd14ea41f21804e794dbe17a49abb..ee6cb50d5a60af688b2e1dc6c17dbce074af51fa 100644 (file)
@@ -1409,3 +1409,55 @@ select 10.0 ^ 2147483647 as overflows;
 ERROR:  value overflows numeric format
 select 117743296169.0 ^ 1000000000 as overflows;
 ERROR:  value overflows numeric format
+--
+-- Tests for generate_series
+--
+select * from generate_series(0.0::numeric, 4.0::numeric);
+ generate_series 
+-----------------
+             0.0
+             1.0
+             2.0
+             3.0
+             4.0
+(5 rows)
+
+select * from generate_series(0.1::numeric, 4.0::numeric, 1.3::numeric);
+ generate_series 
+-----------------
+             0.1
+             1.4
+             2.7
+             4.0
+(4 rows)
+
+select * from generate_series(4.0::numeric, -1.5::numeric, -2.2::numeric);
+ generate_series 
+-----------------
+             4.0
+             1.8
+            -0.4
+(3 rows)
+
+-- Trigger errors
+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, 10::numeric);
+ERROR:  start value cannot be NaN
+select * from generate_series(0::numeric, 'nan'::numeric, 10::numeric);
+ERROR:  stop value cannot be NaN
+-- Checks maximum, output is truncated
+select (i / (10::numeric ^ 131071))::numeric(1,0)
+       from generate_series(6 * (10::numeric ^ 131071),
+                            9 * (10::numeric ^ 131071),
+                            10::numeric ^ 131071) as a(i);
+ numeric 
+---------
+       6
+       7
+       8
+       9
+(4 rows)
+
index 5c08717e7a968380af71ab4ecfaa7af6e2641477..a7e92ac3ccf9dc080def867c49e7685af34c3b00 100644 (file)
@@ -837,3 +837,20 @@ select 10.0 ^ -2147483648 as rounds_to_zero;
 select 10.0 ^ -2147483647 as rounds_to_zero;
 select 10.0 ^ 2147483647 as overflows;
 select 117743296169.0 ^ 1000000000 as overflows;
+
+--
+-- Tests for generate_series
+--
+select * from generate_series(0.0::numeric, 4.0::numeric);
+select * from generate_series(0.1::numeric, 4.0::numeric, 1.3::numeric);
+select * from generate_series(4.0::numeric, -1.5::numeric, -2.2::numeric);
+-- Trigger errors
+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, 10::numeric);
+select * from generate_series(0::numeric, 'nan'::numeric, 10::numeric);
+-- Checks maximum, output is truncated
+select (i / (10::numeric ^ 131071))::numeric(1,0)
+       from generate_series(6 * (10::numeric ^ 131071),
+                            9 * (10::numeric ^ 131071),
+                            10::numeric ^ 131071) as a(i);