1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Add generate_series(numeric, numeric).

Платон Малюгин
Reviewed by Michael Paquier, Ali Akbar and Marti Raudsepp
This commit is contained in:
Fujii Masao
2014-11-11 21:44:46 +09:00
parent a1b395b6a2
commit 1871c89202
7 changed files with 212 additions and 5 deletions

View 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().