|
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* int.c
|
|
|
|
|
* Functions for the built-in integer types (except int8).
|
|
|
|
|
*
|
|
|
|
|
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* IDENTIFICATION
|
|
|
|
|
* src/backend/utils/adt/int.c
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
/*
|
|
|
|
|
* OLD COMMENTS
|
|
|
|
|
* I/O routines:
|
|
|
|
|
* int2in, int2out, int2recv, int2send
|
|
|
|
|
* int4in, int4out, int4recv, int4send
|
|
|
|
|
* int2vectorin, int2vectorout, int2vectorrecv, int2vectorsend
|
|
|
|
|
* Boolean operators:
|
|
|
|
|
* inteq, intne, intlt, intle, intgt, intge
|
|
|
|
|
* Arithmetic operators:
|
|
|
|
|
* intpl, intmi, int4mul, intdiv
|
|
|
|
|
*
|
|
|
|
|
* Arithmetic operators:
|
|
|
|
|
* intmod
|
|
|
|
|
*/
|
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
|
#include "common/int.h"
|
|
|
|
|
#include "funcapi.h"
|
|
|
|
|
#include "libpq/pqformat.h"
|
|
|
|
|
#include "nodes/nodeFuncs.h"
|
|
|
|
|
#include "nodes/supportnodes.h"
|
|
|
|
|
#include "optimizer/optimizer.h"
|
|
|
|
|
#include "utils/array.h"
|
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
|
|
|
|
|
|
#define Int2VectorSize(n) (offsetof(int2vector, values) + (n) * sizeof(int16))
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
int32 current;
|
|
|
|
|
int32 finish;
|
|
|
|
|
int32 step;
|
|
|
|
|
} generate_series_fctx;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* USER I/O ROUTINES *
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2in - converts "num" to short
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2in(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
char *num = PG_GETARG_CSTRING(0);
|
|
|
|
|
|
Convert a few datatype input functions to use "soft" error reporting.
This patch converts the input functions for bool, int2, int4, int8,
float4, float8, numeric, and contrib/cube to the new soft-error style.
array_in and record_in are also converted. There's lots more to do,
but this is enough to provide proof-of-concept that the soft-error
API is usable, as well as reference examples for how to convert
input functions.
This patch is mostly by me, but it owes very substantial debt to
earlier work by Nikita Glukhov, Andrew Dunstan, and Amul Sul.
Thanks to Andres Freund for review.
Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
3 years ago
|
|
|
PG_RETURN_INT16(pg_strtoint16_safe(num, fcinfo->context));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2out - converts short to "num"
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2out(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
char *result = (char *) palloc(7); /* sign, 5 digits, '\0' */
|
|
|
|
|
|
|
|
|
|
pg_itoa(arg1, result);
|
|
|
|
|
PG_RETURN_CSTRING(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2recv - converts external binary format to int2
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2recv(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16((int16) pq_getmsgint(buf, sizeof(int16)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2send - converts int2 to binary format
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2send(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
StringInfoData buf;
|
|
|
|
|
|
|
|
|
|
pq_begintypsend(&buf);
|
|
|
|
|
pq_sendint16(&buf, arg1);
|
|
|
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* construct int2vector given a raw array of int2s
|
|
|
|
|
*
|
|
|
|
|
* If int2s is NULL then caller must fill values[] afterward
|
|
|
|
|
*/
|
|
|
|
|
int2vector *
|
|
|
|
|
buildint2vector(const int16 *int2s, int n)
|
|
|
|
|
{
|
|
|
|
|
int2vector *result;
|
|
|
|
|
|
|
|
|
|
result = (int2vector *) palloc0(Int2VectorSize(n));
|
|
|
|
|
|
|
|
|
|
if (n > 0 && int2s)
|
|
|
|
|
memcpy(result->values, int2s, n * sizeof(int16));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Attach standard array header. For historical reasons, we set the index
|
|
|
|
|
* lower bound to 0 not 1.
|
|
|
|
|
*/
|
|
|
|
|
SET_VARSIZE(result, Int2VectorSize(n));
|
|
|
|
|
result->ndim = 1;
|
|
|
|
|
result->dataoffset = 0; /* never any nulls */
|
|
|
|
|
result->elemtype = INT2OID;
|
|
|
|
|
result->dim1 = n;
|
|
|
|
|
result->lbound1 = 0;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2vectorin - converts "num num ..." to internal form
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2vectorin(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
char *intString = PG_GETARG_CSTRING(0);
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
3 years ago
|
|
|
Node *escontext = fcinfo->context;
|
|
|
|
|
int2vector *result;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
result = (int2vector *) palloc0(Int2VectorSize(FUNC_MAX_ARGS));
|
|
|
|
|
|
|
|
|
|
for (n = 0; n < FUNC_MAX_ARGS; n++)
|
|
|
|
|
{
|
|
|
|
|
long l;
|
|
|
|
|
char *endp;
|
|
|
|
|
|
|
|
|
|
while (*intString && isspace((unsigned char) *intString))
|
|
|
|
|
intString++;
|
|
|
|
|
if (*intString == '\0')
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
l = strtol(intString, &endp, 10);
|
|
|
|
|
|
|
|
|
|
if (intString == endp)
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
3 years ago
|
|
|
ereturn(escontext, (Datum) 0,
|
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
|
errmsg("invalid input syntax for type %s: \"%s\"",
|
|
|
|
|
"smallint", intString)));
|
|
|
|
|
|
|
|
|
|
if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
3 years ago
|
|
|
ereturn(escontext, (Datum) 0,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("value \"%s\" is out of range for type %s", intString,
|
|
|
|
|
"smallint")));
|
|
|
|
|
|
|
|
|
|
if (*endp && *endp != ' ')
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
3 years ago
|
|
|
ereturn(escontext, (Datum) 0,
|
|
|
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
|
|
|
errmsg("invalid input syntax for type %s: \"%s\"",
|
|
|
|
|
"integer", intString)));
|
|
|
|
|
|
|
|
|
|
result->values[n] = l;
|
|
|
|
|
intString = endp;
|
|
|
|
|
}
|
|
|
|
|
while (*intString && isspace((unsigned char) *intString))
|
|
|
|
|
intString++;
|
|
|
|
|
if (*intString)
|
Convert a few more datatype input functions to report errors softly.
Convert assorted internal-ish datatypes, namely aclitemin,
int2vectorin, oidin, oidvectorin, pg_lsn_in, pg_snapshot_in,
and tidin to the new style.
(Some others you might expect to find in this group, such as
cidin and xidin, need no changes because they never throw
errors at all. That seems a little cheesy ... but it is not in
the charter of this patch series to add new error conditions.)
Amul Sul, minor mods by me
Discussion: https://postgr.es/m/CAAJ_b97KeDWUdpTKGOaFYPv0OicjOu6EW+QYWj-Ywrgj_aEy1g@mail.gmail.com
3 years ago
|
|
|
ereturn(escontext, (Datum) 0,
|
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
|
errmsg("int2vector has too many elements")));
|
|
|
|
|
|
|
|
|
|
SET_VARSIZE(result, Int2VectorSize(n));
|
|
|
|
|
result->ndim = 1;
|
|
|
|
|
result->dataoffset = 0; /* never any nulls */
|
|
|
|
|
result->elemtype = INT2OID;
|
|
|
|
|
result->dim1 = n;
|
|
|
|
|
result->lbound1 = 0;
|
|
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2vectorout - converts internal form to "num num ..."
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2vectorout(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int2vector *int2Array = (int2vector *) PG_GETARG_POINTER(0);
|
|
|
|
|
int num,
|
|
|
|
|
nnums = int2Array->dim1;
|
|
|
|
|
char *rp;
|
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
|
|
/* assumes sign, 5 digits, ' ' */
|
|
|
|
|
rp = result = (char *) palloc(nnums * 7 + 1);
|
|
|
|
|
for (num = 0; num < nnums; num++)
|
|
|
|
|
{
|
|
|
|
|
if (num != 0)
|
|
|
|
|
*rp++ = ' ';
|
|
|
|
|
rp += pg_itoa(int2Array->values[num], rp);
|
|
|
|
|
}
|
|
|
|
|
*rp = '\0';
|
|
|
|
|
PG_RETURN_CSTRING(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2vectorrecv - converts external binary format to int2vector
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2vectorrecv(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
Change function call information to be variable length.
Before this change FunctionCallInfoData, the struct arguments etc for
V1 function calls are stored in, always had space for
FUNC_MAX_ARGS/100 arguments, storing datums and their nullness in two
arrays. For nearly every function call 100 arguments is far more than
needed, therefore wasting memory. Arg and argnull being two separate
arrays also guarantees that to access a single argument, two
cachelines have to be touched.
Change the layout so there's a single variable-length array with pairs
of value / isnull. That drastically reduces memory consumption for
most function calls (on x86-64 a two argument function now uses
64bytes, previously 936 bytes), and makes it very likely that argument
value and its nullness are on the same cacheline.
Arguments are stored in a new NullableDatum struct, which, due to
padding, needs more memory per argument than before. But as usually
far fewer arguments are stored, and individual arguments are cheaper
to access, that's still a clear win. It's likely that there's other
places where conversion to NullableDatum arrays would make sense,
e.g. TupleTableSlots, but that's for another commit.
Because the function call information is now variable-length
allocations have to take the number of arguments into account. For
heap allocations that can be done with SizeForFunctionCallInfoData(),
for on-stack allocations there's a new LOCAL_FCINFO(name, nargs) macro
that helps to allocate an appropriately sized and aligned variable.
Some places with stack allocation function call information don't know
the number of arguments at compile time, and currently variably sized
stack allocations aren't allowed in postgres. Therefore allow for
FUNC_MAX_ARGS space in these cases. They're not that common, so for
now that seems acceptable.
Because of the need to allocate FunctionCallInfo of the appropriate
size, older extensions may need to update their code. To avoid subtle
breakages, the FunctionCallInfoData struct has been renamed to
FunctionCallInfoBaseData. Most code only references FunctionCallInfo,
so that shouldn't cause much collateral damage.
This change is also a prerequisite for more efficient expression JIT
compilation (by allocating the function call information on the stack,
allowing LLVM to optimize it away); previously the size of the call
information caused problems inside LLVM's optimizer.
Author: Andres Freund
Reviewed-By: Tom Lane
Discussion: https://postgr.es/m/20180605172952.x34m5uz6ju6enaem@alap3.anarazel.de
7 years ago
|
|
|
LOCAL_FCINFO(locfcinfo, 3);
|
|
|
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
|
int2vector *result;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Normally one would call array_recv() using DirectFunctionCall3, but
|
|
|
|
|
* that does not work since array_recv wants to cache some data using
|
|
|
|
|
* fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo
|
|
|
|
|
* parameter.
|
|
|
|
|
*/
|
Change function call information to be variable length.
Before this change FunctionCallInfoData, the struct arguments etc for
V1 function calls are stored in, always had space for
FUNC_MAX_ARGS/100 arguments, storing datums and their nullness in two
arrays. For nearly every function call 100 arguments is far more than
needed, therefore wasting memory. Arg and argnull being two separate
arrays also guarantees that to access a single argument, two
cachelines have to be touched.
Change the layout so there's a single variable-length array with pairs
of value / isnull. That drastically reduces memory consumption for
most function calls (on x86-64 a two argument function now uses
64bytes, previously 936 bytes), and makes it very likely that argument
value and its nullness are on the same cacheline.
Arguments are stored in a new NullableDatum struct, which, due to
padding, needs more memory per argument than before. But as usually
far fewer arguments are stored, and individual arguments are cheaper
to access, that's still a clear win. It's likely that there's other
places where conversion to NullableDatum arrays would make sense,
e.g. TupleTableSlots, but that's for another commit.
Because the function call information is now variable-length
allocations have to take the number of arguments into account. For
heap allocations that can be done with SizeForFunctionCallInfoData(),
for on-stack allocations there's a new LOCAL_FCINFO(name, nargs) macro
that helps to allocate an appropriately sized and aligned variable.
Some places with stack allocation function call information don't know
the number of arguments at compile time, and currently variably sized
stack allocations aren't allowed in postgres. Therefore allow for
FUNC_MAX_ARGS space in these cases. They're not that common, so for
now that seems acceptable.
Because of the need to allocate FunctionCallInfo of the appropriate
size, older extensions may need to update their code. To avoid subtle
breakages, the FunctionCallInfoData struct has been renamed to
FunctionCallInfoBaseData. Most code only references FunctionCallInfo,
so that shouldn't cause much collateral damage.
This change is also a prerequisite for more efficient expression JIT
compilation (by allocating the function call information on the stack,
allowing LLVM to optimize it away); previously the size of the call
information caused problems inside LLVM's optimizer.
Author: Andres Freund
Reviewed-By: Tom Lane
Discussion: https://postgr.es/m/20180605172952.x34m5uz6ju6enaem@alap3.anarazel.de
7 years ago
|
|
|
InitFunctionCallInfoData(*locfcinfo, fcinfo->flinfo, 3,
|
|
|
|
|
InvalidOid, NULL, NULL);
|
|
|
|
|
|
Change function call information to be variable length.
Before this change FunctionCallInfoData, the struct arguments etc for
V1 function calls are stored in, always had space for
FUNC_MAX_ARGS/100 arguments, storing datums and their nullness in two
arrays. For nearly every function call 100 arguments is far more than
needed, therefore wasting memory. Arg and argnull being two separate
arrays also guarantees that to access a single argument, two
cachelines have to be touched.
Change the layout so there's a single variable-length array with pairs
of value / isnull. That drastically reduces memory consumption for
most function calls (on x86-64 a two argument function now uses
64bytes, previously 936 bytes), and makes it very likely that argument
value and its nullness are on the same cacheline.
Arguments are stored in a new NullableDatum struct, which, due to
padding, needs more memory per argument than before. But as usually
far fewer arguments are stored, and individual arguments are cheaper
to access, that's still a clear win. It's likely that there's other
places where conversion to NullableDatum arrays would make sense,
e.g. TupleTableSlots, but that's for another commit.
Because the function call information is now variable-length
allocations have to take the number of arguments into account. For
heap allocations that can be done with SizeForFunctionCallInfoData(),
for on-stack allocations there's a new LOCAL_FCINFO(name, nargs) macro
that helps to allocate an appropriately sized and aligned variable.
Some places with stack allocation function call information don't know
the number of arguments at compile time, and currently variably sized
stack allocations aren't allowed in postgres. Therefore allow for
FUNC_MAX_ARGS space in these cases. They're not that common, so for
now that seems acceptable.
Because of the need to allocate FunctionCallInfo of the appropriate
size, older extensions may need to update their code. To avoid subtle
breakages, the FunctionCallInfoData struct has been renamed to
FunctionCallInfoBaseData. Most code only references FunctionCallInfo,
so that shouldn't cause much collateral damage.
This change is also a prerequisite for more efficient expression JIT
compilation (by allocating the function call information on the stack,
allowing LLVM to optimize it away); previously the size of the call
information caused problems inside LLVM's optimizer.
Author: Andres Freund
Reviewed-By: Tom Lane
Discussion: https://postgr.es/m/20180605172952.x34m5uz6ju6enaem@alap3.anarazel.de
7 years ago
|
|
|
locfcinfo->args[0].value = PointerGetDatum(buf);
|
|
|
|
|
locfcinfo->args[0].isnull = false;
|
|
|
|
|
locfcinfo->args[1].value = ObjectIdGetDatum(INT2OID);
|
|
|
|
|
locfcinfo->args[1].isnull = false;
|
|
|
|
|
locfcinfo->args[2].value = Int32GetDatum(-1);
|
|
|
|
|
locfcinfo->args[2].isnull = false;
|
|
|
|
|
|
Change function call information to be variable length.
Before this change FunctionCallInfoData, the struct arguments etc for
V1 function calls are stored in, always had space for
FUNC_MAX_ARGS/100 arguments, storing datums and their nullness in two
arrays. For nearly every function call 100 arguments is far more than
needed, therefore wasting memory. Arg and argnull being two separate
arrays also guarantees that to access a single argument, two
cachelines have to be touched.
Change the layout so there's a single variable-length array with pairs
of value / isnull. That drastically reduces memory consumption for
most function calls (on x86-64 a two argument function now uses
64bytes, previously 936 bytes), and makes it very likely that argument
value and its nullness are on the same cacheline.
Arguments are stored in a new NullableDatum struct, which, due to
padding, needs more memory per argument than before. But as usually
far fewer arguments are stored, and individual arguments are cheaper
to access, that's still a clear win. It's likely that there's other
places where conversion to NullableDatum arrays would make sense,
e.g. TupleTableSlots, but that's for another commit.
Because the function call information is now variable-length
allocations have to take the number of arguments into account. For
heap allocations that can be done with SizeForFunctionCallInfoData(),
for on-stack allocations there's a new LOCAL_FCINFO(name, nargs) macro
that helps to allocate an appropriately sized and aligned variable.
Some places with stack allocation function call information don't know
the number of arguments at compile time, and currently variably sized
stack allocations aren't allowed in postgres. Therefore allow for
FUNC_MAX_ARGS space in these cases. They're not that common, so for
now that seems acceptable.
Because of the need to allocate FunctionCallInfo of the appropriate
size, older extensions may need to update their code. To avoid subtle
breakages, the FunctionCallInfoData struct has been renamed to
FunctionCallInfoBaseData. Most code only references FunctionCallInfo,
so that shouldn't cause much collateral damage.
This change is also a prerequisite for more efficient expression JIT
compilation (by allocating the function call information on the stack,
allowing LLVM to optimize it away); previously the size of the call
information caused problems inside LLVM's optimizer.
Author: Andres Freund
Reviewed-By: Tom Lane
Discussion: https://postgr.es/m/20180605172952.x34m5uz6ju6enaem@alap3.anarazel.de
7 years ago
|
|
|
result = (int2vector *) DatumGetPointer(array_recv(locfcinfo));
|
|
|
|
|
|
Change function call information to be variable length.
Before this change FunctionCallInfoData, the struct arguments etc for
V1 function calls are stored in, always had space for
FUNC_MAX_ARGS/100 arguments, storing datums and their nullness in two
arrays. For nearly every function call 100 arguments is far more than
needed, therefore wasting memory. Arg and argnull being two separate
arrays also guarantees that to access a single argument, two
cachelines have to be touched.
Change the layout so there's a single variable-length array with pairs
of value / isnull. That drastically reduces memory consumption for
most function calls (on x86-64 a two argument function now uses
64bytes, previously 936 bytes), and makes it very likely that argument
value and its nullness are on the same cacheline.
Arguments are stored in a new NullableDatum struct, which, due to
padding, needs more memory per argument than before. But as usually
far fewer arguments are stored, and individual arguments are cheaper
to access, that's still a clear win. It's likely that there's other
places where conversion to NullableDatum arrays would make sense,
e.g. TupleTableSlots, but that's for another commit.
Because the function call information is now variable-length
allocations have to take the number of arguments into account. For
heap allocations that can be done with SizeForFunctionCallInfoData(),
for on-stack allocations there's a new LOCAL_FCINFO(name, nargs) macro
that helps to allocate an appropriately sized and aligned variable.
Some places with stack allocation function call information don't know
the number of arguments at compile time, and currently variably sized
stack allocations aren't allowed in postgres. Therefore allow for
FUNC_MAX_ARGS space in these cases. They're not that common, so for
now that seems acceptable.
Because of the need to allocate FunctionCallInfo of the appropriate
size, older extensions may need to update their code. To avoid subtle
breakages, the FunctionCallInfoData struct has been renamed to
FunctionCallInfoBaseData. Most code only references FunctionCallInfo,
so that shouldn't cause much collateral damage.
This change is also a prerequisite for more efficient expression JIT
compilation (by allocating the function call information on the stack,
allowing LLVM to optimize it away); previously the size of the call
information caused problems inside LLVM's optimizer.
Author: Andres Freund
Reviewed-By: Tom Lane
Discussion: https://postgr.es/m/20180605172952.x34m5uz6ju6enaem@alap3.anarazel.de
7 years ago
|
|
|
Assert(!locfcinfo->isnull);
|
|
|
|
|
|
|
|
|
|
/* sanity checks: int2vector must be 1-D, 0-based, no nulls */
|
|
|
|
|
if (ARR_NDIM(result) != 1 ||
|
|
|
|
|
ARR_HASNULL(result) ||
|
|
|
|
|
ARR_ELEMTYPE(result) != INT2OID ||
|
|
|
|
|
ARR_LBOUND(result)[0] != 0)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
|
|
|
|
errmsg("invalid int2vector data")));
|
|
|
|
|
|
|
|
|
|
/* check length for consistency with int2vectorin() */
|
|
|
|
|
if (ARR_DIMS(result)[0] > FUNC_MAX_ARGS)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
|
errmsg("oidvector has too many elements")));
|
|
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int2vectorsend - converts int2vector to binary format
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int2vectorsend(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
return array_send(fcinfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
|
* PUBLIC ROUTINES *
|
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int4in - converts "num" to int4
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int4in(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
char *num = PG_GETARG_CSTRING(0);
|
|
|
|
|
|
Convert a few datatype input functions to use "soft" error reporting.
This patch converts the input functions for bool, int2, int4, int8,
float4, float8, numeric, and contrib/cube to the new soft-error style.
array_in and record_in are also converted. There's lots more to do,
but this is enough to provide proof-of-concept that the soft-error
API is usable, as well as reference examples for how to convert
input functions.
This patch is mostly by me, but it owes very substantial debt to
earlier work by Nikita Glukhov, Andrew Dunstan, and Amul Sul.
Thanks to Andres Freund for review.
Discussion: https://postgr.es/m/3bbbb0df-7382-bf87-9737-340ba096e034@postgrespro.ru
3 years ago
|
|
|
PG_RETURN_INT32(pg_strtoint32_safe(num, fcinfo->context));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int4out - converts int4 to "num"
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int4out(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
char *result = (char *) palloc(12); /* sign, 10 digits, '\0' */
|
|
|
|
|
|
|
|
|
|
pg_ltoa(arg1, result);
|
|
|
|
|
PG_RETURN_CSTRING(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int4recv - converts external binary format to int4
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int4recv(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32((int32) pq_getmsgint(buf, sizeof(int32)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int4send - converts int4 to binary format
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int4send(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
StringInfoData buf;
|
|
|
|
|
|
|
|
|
|
pq_begintypsend(&buf);
|
|
|
|
|
pq_sendint32(&buf, arg1);
|
|
|
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ===================
|
|
|
|
|
* CONVERSION ROUTINES
|
|
|
|
|
* ===================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
i2toi4(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32((int32) arg1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
i4toi2(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg1 < SHRT_MIN) || unlikely(arg1 > SHRT_MAX))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16((int16) arg1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Cast int4 -> bool */
|
|
|
|
|
Datum
|
|
|
|
|
int4_bool(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
if (PG_GETARG_INT32(0) == 0)
|
|
|
|
|
PG_RETURN_BOOL(false);
|
|
|
|
|
else
|
|
|
|
|
PG_RETURN_BOOL(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Cast bool -> int4 */
|
|
|
|
|
Datum
|
|
|
|
|
bool_int4(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
if (PG_GETARG_BOOL(0) == false)
|
|
|
|
|
PG_RETURN_INT32(0);
|
|
|
|
|
else
|
|
|
|
|
PG_RETURN_INT32(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ============================
|
|
|
|
|
* COMPARISON OPERATOR ROUTINES
|
|
|
|
|
* ============================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* inteq - returns 1 iff arg1 == arg2
|
|
|
|
|
* intne - returns 1 iff arg1 != arg2
|
|
|
|
|
* intlt - returns 1 iff arg1 < arg2
|
|
|
|
|
* intle - returns 1 iff arg1 <= arg2
|
|
|
|
|
* intgt - returns 1 iff arg1 > arg2
|
|
|
|
|
* intge - returns 1 iff arg1 >= arg2
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4eq(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 == arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4ne(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 != arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4lt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 < arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4le(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 <= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4gt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 > arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4ge(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 >= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2eq(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 == arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2ne(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 != arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2lt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 < arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2le(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 <= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2gt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 > arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2ge(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 >= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24eq(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 == arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24ne(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 != arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24lt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 < arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24le(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 <= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24gt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 > arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24ge(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 >= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42eq(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 == arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42ne(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 != arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42lt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 < arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42le(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 <= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42gt(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 > arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42ge(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_BOOL(arg1 >= arg2);
|
|
|
|
|
}
|
|
|
|
|
|
Support all SQL:2011 options for window frame clauses.
This patch adds the ability to use "RANGE offset PRECEDING/FOLLOWING"
frame boundaries in window functions. We'd punted on that back in the
original patch to add window functions, because it was not clear how to
do it in a reasonably data-type-extensible fashion. That problem is
resolved here by adding the ability for btree operator classes to provide
an "in_range" support function that defines how to add or subtract the
RANGE offset value. Factoring it this way also allows the operator class
to avoid overflow problems near the ends of the datatype's range, if it
wishes to expend effort on that. (In the committed patch, the integer
opclasses handle that issue, but it did not seem worth the trouble to
avoid overflow failures for datetime types.)
The patch includes in_range support for the integer_ops opfamily
(int2/int4/int8) as well as the standard datetime types. Support for
other numeric types has been requested, but that seems like suitable
material for a follow-on patch.
In addition, the patch adds GROUPS mode which counts the offset in
ORDER-BY peer groups rather than rows, and it adds the frame_exclusion
options specified by SQL:2011. As far as I can see, we are now fully
up to spec on window framing options.
Existing behaviors remain unchanged, except that I changed the errcode
for a couple of existing error reports to meet the SQL spec's expectation
that negative "offset" values should be reported as SQLSTATE 22013.
Internally and in relevant parts of the documentation, we now consistently
use the terminology "offset PRECEDING/FOLLOWING" rather than "value
PRECEDING/FOLLOWING", since the term "value" is confusingly vague.
Oliver Ford, reviewed and whacked around some by me
Discussion: https://postgr.es/m/CAGMVOdu9sivPAxbNN0X+q19Sfv9edEPv=HibOJhB14TJv_RCQg@mail.gmail.com
8 years ago
|
|
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
|
|
|
* in_range functions for int4 and int2,
|
|
|
|
|
* including cross-data-type comparisons.
|
|
|
|
|
*
|
|
|
|
|
* Note: we provide separate intN_int8 functions for performance
|
|
|
|
|
* reasons. This forces also providing intN_int2, else cases with a
|
|
|
|
|
* smallint offset value would fail to resolve which function to use.
|
|
|
|
|
* But that's an unlikely situation, so don't duplicate code for it.
|
|
|
|
|
*---------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
in_range_int4_int4(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 val = PG_GETARG_INT32(0);
|
|
|
|
|
int32 base = PG_GETARG_INT32(1);
|
|
|
|
|
int32 offset = PG_GETARG_INT32(2);
|
|
|
|
|
bool sub = PG_GETARG_BOOL(3);
|
|
|
|
|
bool less = PG_GETARG_BOOL(4);
|
|
|
|
|
int32 sum;
|
|
|
|
|
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
|
Support all SQL:2011 options for window frame clauses.
This patch adds the ability to use "RANGE offset PRECEDING/FOLLOWING"
frame boundaries in window functions. We'd punted on that back in the
original patch to add window functions, because it was not clear how to
do it in a reasonably data-type-extensible fashion. That problem is
resolved here by adding the ability for btree operator classes to provide
an "in_range" support function that defines how to add or subtract the
RANGE offset value. Factoring it this way also allows the operator class
to avoid overflow problems near the ends of the datatype's range, if it
wishes to expend effort on that. (In the committed patch, the integer
opclasses handle that issue, but it did not seem worth the trouble to
avoid overflow failures for datetime types.)
The patch includes in_range support for the integer_ops opfamily
(int2/int4/int8) as well as the standard datetime types. Support for
other numeric types has been requested, but that seems like suitable
material for a follow-on patch.
In addition, the patch adds GROUPS mode which counts the offset in
ORDER-BY peer groups rather than rows, and it adds the frame_exclusion
options specified by SQL:2011. As far as I can see, we are now fully
up to spec on window framing options.
Existing behaviors remain unchanged, except that I changed the errcode
for a couple of existing error reports to meet the SQL spec's expectation
that negative "offset" values should be reported as SQLSTATE 22013.
Internally and in relevant parts of the documentation, we now consistently
use the terminology "offset PRECEDING/FOLLOWING" rather than "value
PRECEDING/FOLLOWING", since the term "value" is confusingly vague.
Oliver Ford, reviewed and whacked around some by me
Discussion: https://postgr.es/m/CAGMVOdu9sivPAxbNN0X+q19Sfv9edEPv=HibOJhB14TJv_RCQg@mail.gmail.com
8 years ago
|
|
|
errmsg("invalid preceding or following size in window function")));
|
|
|
|
|
|
|
|
|
|
if (sub)
|
|
|
|
|
offset = -offset; /* cannot overflow */
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s32_overflow(base, offset, &sum)))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If sub is false, the true sum is surely more than val, so correct
|
|
|
|
|
* answer is the same as "less". If sub is true, the true sum is
|
|
|
|
|
* surely less than val, so the answer is "!less".
|
|
|
|
|
*/
|
|
|
|
|
PG_RETURN_BOOL(sub ? !less : less);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (less)
|
|
|
|
|
PG_RETURN_BOOL(val <= sum);
|
|
|
|
|
else
|
|
|
|
|
PG_RETURN_BOOL(val >= sum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
in_range_int4_int2(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
/* Doesn't seem worth duplicating code for, so just invoke int4_int4 */
|
|
|
|
|
return DirectFunctionCall5(in_range_int4_int4,
|
|
|
|
|
PG_GETARG_DATUM(0),
|
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
|
Int32GetDatum((int32) PG_GETARG_INT16(2)),
|
|
|
|
|
PG_GETARG_DATUM(3),
|
|
|
|
|
PG_GETARG_DATUM(4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
in_range_int4_int8(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
/* We must do all the math in int64 */
|
|
|
|
|
int64 val = (int64) PG_GETARG_INT32(0);
|
|
|
|
|
int64 base = (int64) PG_GETARG_INT32(1);
|
|
|
|
|
int64 offset = PG_GETARG_INT64(2);
|
|
|
|
|
bool sub = PG_GETARG_BOOL(3);
|
|
|
|
|
bool less = PG_GETARG_BOOL(4);
|
|
|
|
|
int64 sum;
|
|
|
|
|
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
|
Support all SQL:2011 options for window frame clauses.
This patch adds the ability to use "RANGE offset PRECEDING/FOLLOWING"
frame boundaries in window functions. We'd punted on that back in the
original patch to add window functions, because it was not clear how to
do it in a reasonably data-type-extensible fashion. That problem is
resolved here by adding the ability for btree operator classes to provide
an "in_range" support function that defines how to add or subtract the
RANGE offset value. Factoring it this way also allows the operator class
to avoid overflow problems near the ends of the datatype's range, if it
wishes to expend effort on that. (In the committed patch, the integer
opclasses handle that issue, but it did not seem worth the trouble to
avoid overflow failures for datetime types.)
The patch includes in_range support for the integer_ops opfamily
(int2/int4/int8) as well as the standard datetime types. Support for
other numeric types has been requested, but that seems like suitable
material for a follow-on patch.
In addition, the patch adds GROUPS mode which counts the offset in
ORDER-BY peer groups rather than rows, and it adds the frame_exclusion
options specified by SQL:2011. As far as I can see, we are now fully
up to spec on window framing options.
Existing behaviors remain unchanged, except that I changed the errcode
for a couple of existing error reports to meet the SQL spec's expectation
that negative "offset" values should be reported as SQLSTATE 22013.
Internally and in relevant parts of the documentation, we now consistently
use the terminology "offset PRECEDING/FOLLOWING" rather than "value
PRECEDING/FOLLOWING", since the term "value" is confusingly vague.
Oliver Ford, reviewed and whacked around some by me
Discussion: https://postgr.es/m/CAGMVOdu9sivPAxbNN0X+q19Sfv9edEPv=HibOJhB14TJv_RCQg@mail.gmail.com
8 years ago
|
|
|
errmsg("invalid preceding or following size in window function")));
|
|
|
|
|
|
|
|
|
|
if (sub)
|
|
|
|
|
offset = -offset; /* cannot overflow */
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s64_overflow(base, offset, &sum)))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If sub is false, the true sum is surely more than val, so correct
|
|
|
|
|
* answer is the same as "less". If sub is true, the true sum is
|
|
|
|
|
* surely less than val, so the answer is "!less".
|
|
|
|
|
*/
|
|
|
|
|
PG_RETURN_BOOL(sub ? !less : less);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (less)
|
|
|
|
|
PG_RETURN_BOOL(val <= sum);
|
|
|
|
|
else
|
|
|
|
|
PG_RETURN_BOOL(val >= sum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
in_range_int2_int4(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
/* We must do all the math in int32 */
|
|
|
|
|
int32 val = (int32) PG_GETARG_INT16(0);
|
|
|
|
|
int32 base = (int32) PG_GETARG_INT16(1);
|
|
|
|
|
int32 offset = PG_GETARG_INT32(2);
|
|
|
|
|
bool sub = PG_GETARG_BOOL(3);
|
|
|
|
|
bool less = PG_GETARG_BOOL(4);
|
|
|
|
|
int32 sum;
|
|
|
|
|
|
|
|
|
|
if (offset < 0)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
|
Support all SQL:2011 options for window frame clauses.
This patch adds the ability to use "RANGE offset PRECEDING/FOLLOWING"
frame boundaries in window functions. We'd punted on that back in the
original patch to add window functions, because it was not clear how to
do it in a reasonably data-type-extensible fashion. That problem is
resolved here by adding the ability for btree operator classes to provide
an "in_range" support function that defines how to add or subtract the
RANGE offset value. Factoring it this way also allows the operator class
to avoid overflow problems near the ends of the datatype's range, if it
wishes to expend effort on that. (In the committed patch, the integer
opclasses handle that issue, but it did not seem worth the trouble to
avoid overflow failures for datetime types.)
The patch includes in_range support for the integer_ops opfamily
(int2/int4/int8) as well as the standard datetime types. Support for
other numeric types has been requested, but that seems like suitable
material for a follow-on patch.
In addition, the patch adds GROUPS mode which counts the offset in
ORDER-BY peer groups rather than rows, and it adds the frame_exclusion
options specified by SQL:2011. As far as I can see, we are now fully
up to spec on window framing options.
Existing behaviors remain unchanged, except that I changed the errcode
for a couple of existing error reports to meet the SQL spec's expectation
that negative "offset" values should be reported as SQLSTATE 22013.
Internally and in relevant parts of the documentation, we now consistently
use the terminology "offset PRECEDING/FOLLOWING" rather than "value
PRECEDING/FOLLOWING", since the term "value" is confusingly vague.
Oliver Ford, reviewed and whacked around some by me
Discussion: https://postgr.es/m/CAGMVOdu9sivPAxbNN0X+q19Sfv9edEPv=HibOJhB14TJv_RCQg@mail.gmail.com
8 years ago
|
|
|
errmsg("invalid preceding or following size in window function")));
|
|
|
|
|
|
|
|
|
|
if (sub)
|
|
|
|
|
offset = -offset; /* cannot overflow */
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s32_overflow(base, offset, &sum)))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* If sub is false, the true sum is surely more than val, so correct
|
|
|
|
|
* answer is the same as "less". If sub is true, the true sum is
|
|
|
|
|
* surely less than val, so the answer is "!less".
|
|
|
|
|
*/
|
|
|
|
|
PG_RETURN_BOOL(sub ? !less : less);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (less)
|
|
|
|
|
PG_RETURN_BOOL(val <= sum);
|
|
|
|
|
else
|
|
|
|
|
PG_RETURN_BOOL(val >= sum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
in_range_int2_int2(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
/* Doesn't seem worth duplicating code for, so just invoke int2_int4 */
|
|
|
|
|
return DirectFunctionCall5(in_range_int2_int4,
|
|
|
|
|
PG_GETARG_DATUM(0),
|
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
|
Int32GetDatum((int32) PG_GETARG_INT16(2)),
|
|
|
|
|
PG_GETARG_DATUM(3),
|
|
|
|
|
PG_GETARG_DATUM(4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
in_range_int2_int8(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
/* Doesn't seem worth duplicating code for, so just invoke int4_int8 */
|
|
|
|
|
return DirectFunctionCall5(in_range_int4_int8,
|
|
|
|
|
Int32GetDatum((int32) PG_GETARG_INT16(0)),
|
|
|
|
|
Int32GetDatum((int32) PG_GETARG_INT16(1)),
|
|
|
|
|
PG_GETARG_DATUM(2),
|
|
|
|
|
PG_GETARG_DATUM(3),
|
|
|
|
|
PG_GETARG_DATUM(4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* int[24]pl - returns arg1 + arg2
|
|
|
|
|
* int[24]mi - returns arg1 - arg2
|
|
|
|
|
* int[24]mul - returns arg1 * arg2
|
|
|
|
|
* int[24]div - returns arg1 / arg2
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4um(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg = PG_GETARG_INT32(0);
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg == PG_INT32_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(-arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4up(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg = PG_GETARG_INT32(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4pl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s32_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4mi(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_sub_s32_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4mul(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_mul_s32_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4div(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (arg2 == 0)
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
|
|
|
errmsg("division by zero")));
|
|
|
|
|
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
|
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* INT_MIN / -1 is problematic, since the result can't be represented on a
|
|
|
|
|
* two's-complement machine. Some machines produce INT_MIN, some produce
|
|
|
|
|
* zero, some throw an exception. We can dodge the problem by recognizing
|
|
|
|
|
* that division by -1 is the same as negation.
|
|
|
|
|
*/
|
|
|
|
|
if (arg2 == -1)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(arg1 == PG_INT32_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
result = -arg1;
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No overflow is possible */
|
|
|
|
|
|
|
|
|
|
result = arg1 / arg2;
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4inc(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg = PG_GETARG_INT32(0);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s32_overflow(arg, 1, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2um(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg = PG_GETARG_INT16(0);
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg == PG_INT16_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
PG_RETURN_INT16(-arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2up(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg = PG_GETARG_INT16(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2pl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int16 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s16_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
PG_RETURN_INT16(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2mi(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int16 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_sub_s16_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
PG_RETURN_INT16(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2mul(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int16 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_mul_s16_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2div(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int16 result;
|
|
|
|
|
|
|
|
|
|
if (arg2 == 0)
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
|
|
|
errmsg("division by zero")));
|
|
|
|
|
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
|
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* SHRT_MIN / -1 is problematic, since the result can't be represented on
|
|
|
|
|
* a two's-complement machine. Some machines produce SHRT_MIN, some
|
|
|
|
|
* produce zero, some throw an exception. We can dodge the problem by
|
|
|
|
|
* recognizing that division by -1 is the same as negation.
|
|
|
|
|
*/
|
|
|
|
|
if (arg2 == -1)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(arg1 == PG_INT16_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
result = -arg1;
|
|
|
|
|
PG_RETURN_INT16(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No overflow is possible */
|
|
|
|
|
|
|
|
|
|
result = arg1 / arg2;
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24pl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s32_overflow((int32) arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24mi(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_sub_s32_overflow((int32) arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24mul(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_mul_s32_overflow((int32) arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int24div(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg2 == 0))
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
|
|
|
errmsg("division by zero")));
|
|
|
|
|
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
|
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No overflow is possible */
|
|
|
|
|
PG_RETURN_INT32((int32) arg1 / arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42pl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_add_s32_overflow(arg1, (int32) arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42mi(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_sub_s32_overflow(arg1, (int32) arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42mul(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_mul_s32_overflow(arg1, (int32) arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int42div(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg2 == 0))
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
|
|
|
errmsg("division by zero")));
|
|
|
|
|
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
|
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* INT_MIN / -1 is problematic, since the result can't be represented on a
|
|
|
|
|
* two's-complement machine. Some machines produce INT_MIN, some produce
|
|
|
|
|
* zero, some throw an exception. We can dodge the problem by recognizing
|
|
|
|
|
* that division by -1 is the same as negation.
|
|
|
|
|
*/
|
|
|
|
|
if (arg2 == -1)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(arg1 == PG_INT32_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
result = -arg1;
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No overflow is possible */
|
|
|
|
|
|
|
|
|
|
result = arg1 / arg2;
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4mod(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg2 == 0))
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
|
|
|
errmsg("division by zero")));
|
|
|
|
|
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
|
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some machines throw a floating-point exception for INT_MIN % -1, which
|
|
|
|
|
* is a bit silly since the correct answer is perfectly well-defined,
|
|
|
|
|
* namely zero.
|
|
|
|
|
*/
|
|
|
|
|
if (arg2 == -1)
|
|
|
|
|
PG_RETURN_INT32(0);
|
|
|
|
|
|
|
|
|
|
/* No overflow is possible */
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg1 % arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2mod(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg2 == 0))
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_DIVISION_BY_ZERO),
|
|
|
|
|
errmsg("division by zero")));
|
|
|
|
|
/* ensure compiler realizes we mustn't reach the division (gcc bug) */
|
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some machines throw a floating-point exception for INT_MIN % -1, which
|
|
|
|
|
* is a bit silly since the correct answer is perfectly well-defined,
|
|
|
|
|
* namely zero. (It's not clear this ever happens when dealing with
|
|
|
|
|
* int16, but we might as well have the test for safety.)
|
|
|
|
|
*/
|
|
|
|
|
if (arg2 == -1)
|
|
|
|
|
PG_RETURN_INT16(0);
|
|
|
|
|
|
|
|
|
|
/* No overflow is possible */
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg1 % arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* int[24]abs()
|
|
|
|
|
* Absolute value
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int4abs(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg1 == PG_INT32_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
result = (arg1 < 0) ? -arg1 : arg1;
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2abs(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 result;
|
|
|
|
|
|
|
|
|
|
if (unlikely(arg1 == PG_INT16_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("smallint out of range")));
|
|
|
|
|
result = (arg1 < 0) ? -arg1 : arg1;
|
|
|
|
|
PG_RETURN_INT16(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Greatest Common Divisor
|
|
|
|
|
*
|
|
|
|
|
* Returns the largest positive integer that exactly divides both inputs.
|
|
|
|
|
* Special cases:
|
|
|
|
|
* - gcd(x, 0) = gcd(0, x) = abs(x)
|
|
|
|
|
* because 0 is divisible by anything
|
|
|
|
|
* - gcd(0, 0) = 0
|
|
|
|
|
* complies with the previous definition and is a common convention
|
|
|
|
|
*
|
|
|
|
|
* Special care must be taken if either input is INT_MIN --- gcd(0, INT_MIN),
|
|
|
|
|
* gcd(INT_MIN, 0) and gcd(INT_MIN, INT_MIN) are all equal to abs(INT_MIN),
|
|
|
|
|
* which cannot be represented as a 32-bit signed integer.
|
|
|
|
|
*/
|
|
|
|
|
static int32
|
|
|
|
|
int4gcd_internal(int32 arg1, int32 arg2)
|
|
|
|
|
{
|
|
|
|
|
int32 swap;
|
|
|
|
|
int32 a1,
|
|
|
|
|
a2;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Put the greater absolute value in arg1.
|
|
|
|
|
*
|
|
|
|
|
* This would happen automatically in the loop below, but avoids an
|
|
|
|
|
* expensive modulo operation, and simplifies the special-case handling
|
|
|
|
|
* for INT_MIN below.
|
|
|
|
|
*
|
|
|
|
|
* We do this in negative space in order to handle INT_MIN.
|
|
|
|
|
*/
|
|
|
|
|
a1 = (arg1 < 0) ? arg1 : -arg1;
|
|
|
|
|
a2 = (arg2 < 0) ? arg2 : -arg2;
|
|
|
|
|
if (a1 > a2)
|
|
|
|
|
{
|
|
|
|
|
swap = arg1;
|
|
|
|
|
arg1 = arg2;
|
|
|
|
|
arg2 = swap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Special care needs to be taken with INT_MIN. See comments above. */
|
|
|
|
|
if (arg1 == PG_INT32_MIN)
|
|
|
|
|
{
|
|
|
|
|
if (arg2 == 0 || arg2 == PG_INT32_MIN)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Some machines throw a floating-point exception for INT_MIN % -1,
|
|
|
|
|
* which is a bit silly since the correct answer is perfectly
|
|
|
|
|
* well-defined, namely zero. Guard against this and just return the
|
|
|
|
|
* result, gcd(INT_MIN, -1) = 1.
|
|
|
|
|
*/
|
|
|
|
|
if (arg2 == -1)
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Use the Euclidean algorithm to find the GCD */
|
|
|
|
|
while (arg2 != 0)
|
|
|
|
|
{
|
|
|
|
|
swap = arg2;
|
|
|
|
|
arg2 = arg1 % arg2;
|
|
|
|
|
arg1 = swap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure the result is positive. (We know we don't have INT_MIN
|
|
|
|
|
* anymore).
|
|
|
|
|
*/
|
|
|
|
|
if (arg1 < 0)
|
|
|
|
|
arg1 = -arg1;
|
|
|
|
|
|
|
|
|
|
return arg1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4gcd(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
result = int4gcd_internal(arg1, arg2);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Least Common Multiple
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
int4lcm(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
int32 gcd;
|
|
|
|
|
int32 result;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Handle lcm(x, 0) = lcm(0, x) = 0 as a special case. This prevents a
|
|
|
|
|
* division-by-zero error below when x is zero, and an overflow error from
|
|
|
|
|
* the GCD computation when x = INT_MIN.
|
|
|
|
|
*/
|
|
|
|
|
if (arg1 == 0 || arg2 == 0)
|
|
|
|
|
PG_RETURN_INT32(0);
|
|
|
|
|
|
|
|
|
|
/* lcm(x, y) = abs(x / gcd(x, y) * y) */
|
|
|
|
|
gcd = int4gcd_internal(arg1, arg2);
|
|
|
|
|
arg1 = arg1 / gcd;
|
|
|
|
|
|
|
|
|
|
if (unlikely(pg_mul_s32_overflow(arg1, arg2, &result)))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
|
|
|
|
|
/* If the result is INT_MIN, it cannot be represented. */
|
|
|
|
|
if (unlikely(result == PG_INT32_MIN))
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
|
errmsg("integer out of range")));
|
|
|
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
|
result = -result;
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2larger(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16((arg1 > arg2) ? arg1 : arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2smaller(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16((arg1 < arg2) ? arg1 : arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4larger(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32((arg1 > arg2) ? arg1 : arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4smaller(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32((arg1 < arg2) ? arg1 : arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Bit-pushing operators
|
|
|
|
|
*
|
|
|
|
|
* int[24]and - returns arg1 & arg2
|
|
|
|
|
* int[24]or - returns arg1 | arg2
|
|
|
|
|
* int[24]xor - returns arg1 # arg2
|
|
|
|
|
* int[24]not - returns ~arg1
|
|
|
|
|
* int[24]shl - returns arg1 << arg2
|
|
|
|
|
* int[24]shr - returns arg1 >> arg2
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4and(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg1 & arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4or(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg1 | arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4xor(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg1 ^ arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4shl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg1 << arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4shr(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(arg1 >> arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int4not(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(~arg1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2and(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg1 & arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2or(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg1 | arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2xor(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg1 ^ arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2not(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(~arg1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2shl(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg1 << arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
int2shr(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
|
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT16(arg1 >> arg2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* non-persistent numeric series generator
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
generate_series_int4(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
return generate_series_step_int4(fcinfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Datum
|
|
|
|
|
generate_series_step_int4(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
FuncCallContext *funcctx;
|
|
|
|
|
generate_series_fctx *fctx;
|
|
|
|
|
int32 result;
|
|
|
|
|
MemoryContext oldcontext;
|
|
|
|
|
|
|
|
|
|
/* stuff done only on the first call of the function */
|
|
|
|
|
if (SRF_IS_FIRSTCALL())
|
|
|
|
|
{
|
|
|
|
|
int32 start = PG_GETARG_INT32(0);
|
|
|
|
|
int32 finish = PG_GETARG_INT32(1);
|
|
|
|
|
int32 step = 1;
|
|
|
|
|
|
|
|
|
|
/* see if we were given an explicit step size */
|
|
|
|
|
if (PG_NARGS() == 3)
|
|
|
|
|
step = PG_GETARG_INT32(2);
|
|
|
|
|
if (step == 0)
|
|
|
|
|
ereport(ERROR,
|
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
Wording cleanup for error messages. Also change can't -> cannot.
Standard English uses "may", "can", and "might" in different ways:
may - permission, "You may borrow my rake."
can - ability, "I can lift that log."
might - possibility, "It might rain today."
Unfortunately, in conversational English, their use is often mixed, as
in, "You may use this variable to do X", when in fact, "can" is a better
choice. Similarly, "It may crash" is better stated, "It might crash".
19 years ago
|
|
|
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_fctx *) palloc(sizeof(generate_series_fctx));
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Use fctx to keep state from call to call. Seed current with the
|
|
|
|
|
* original start value
|
|
|
|
|
*/
|
|
|
|
|
fctx->current = start;
|
|
|
|
|
fctx->finish = finish;
|
|
|
|
|
fctx->step = 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;
|
|
|
|
|
result = fctx->current;
|
|
|
|
|
|
|
|
|
|
if ((fctx->step > 0 && fctx->current <= fctx->finish) ||
|
|
|
|
|
(fctx->step < 0 && fctx->current >= fctx->finish))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Increment current in preparation for next iteration. If next-value
|
|
|
|
|
* computation overflows, this is the final result.
|
|
|
|
|
*/
|
|
|
|
|
if (pg_add_s32_overflow(fctx->current, fctx->step, &fctx->current))
|
|
|
|
|
fctx->step = 0;
|
|
|
|
|
|
|
|
|
|
/* do when there is more left to send */
|
|
|
|
|
SRF_RETURN_NEXT(funcctx, Int32GetDatum(result));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
/* do when there is no more left */
|
|
|
|
|
SRF_RETURN_DONE(funcctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Planner support function for generate_series(int4, int4 [, int4])
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
generate_series_int4_support(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
Node *rawreq = (Node *) PG_GETARG_POINTER(0);
|
|
|
|
|
Node *ret = NULL;
|
|
|
|
|
|
|
|
|
|
if (IsA(rawreq, SupportRequestRows))
|
|
|
|
|
{
|
|
|
|
|
/* Try to estimate the number of rows returned */
|
|
|
|
|
SupportRequestRows *req = (SupportRequestRows *) rawreq;
|
|
|
|
|
|
|
|
|
|
if (is_funcclause(req->node)) /* be paranoid */
|
|
|
|
|
{
|
|
|
|
|
List *args = ((FuncExpr *) req->node)->args;
|
|
|
|
|
Node *arg1,
|
|
|
|
|
*arg2,
|
|
|
|
|
*arg3;
|
|
|
|
|
|
|
|
|
|
/* We can use estimated argument values here */
|
|
|
|
|
arg1 = estimate_expression_value(req->root, linitial(args));
|
|
|
|
|
arg2 = estimate_expression_value(req->root, lsecond(args));
|
|
|
|
|
if (list_length(args) >= 3)
|
|
|
|
|
arg3 = estimate_expression_value(req->root, lthird(args));
|
|
|
|
|
else
|
|
|
|
|
arg3 = NULL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If any argument is constant NULL, we can safely assume that
|
|
|
|
|
* zero rows are returned. Otherwise, if they're all non-NULL
|
|
|
|
|
* constants, we can calculate the number of rows that will be
|
|
|
|
|
* returned. Use double arithmetic to avoid overflow hazards.
|
|
|
|
|
*/
|
|
|
|
|
if ((IsA(arg1, Const) &&
|
|
|
|
|
((Const *) arg1)->constisnull) ||
|
|
|
|
|
(IsA(arg2, Const) &&
|
|
|
|
|
((Const *) arg2)->constisnull) ||
|
|
|
|
|
(arg3 != NULL && IsA(arg3, Const) &&
|
|
|
|
|
((Const *) arg3)->constisnull))
|
|
|
|
|
{
|
|
|
|
|
req->rows = 0;
|
|
|
|
|
ret = (Node *) req;
|
|
|
|
|
}
|
|
|
|
|
else if (IsA(arg1, Const) &&
|
|
|
|
|
IsA(arg2, Const) &&
|
|
|
|
|
(arg3 == NULL || IsA(arg3, Const)))
|
|
|
|
|
{
|
|
|
|
|
double start,
|
|
|
|
|
finish,
|
|
|
|
|
step;
|
|
|
|
|
|
|
|
|
|
start = DatumGetInt32(((Const *) arg1)->constvalue);
|
|
|
|
|
finish = DatumGetInt32(((Const *) arg2)->constvalue);
|
|
|
|
|
step = arg3 ? DatumGetInt32(((Const *) arg3)->constvalue) : 1;
|
|
|
|
|
|
|
|
|
|
/* This equation works for either sign of step */
|
|
|
|
|
if (step != 0)
|
|
|
|
|
{
|
|
|
|
|
req->rows = floor((finish - start + step) / step);
|
|
|
|
|
ret = (Node *) req;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PG_RETURN_POINTER(ret);
|
|
|
|
|
}
|