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