You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
postgres/src/backend/utils/adt/int.c

1654 lines
35 KiB

/*-------------------------------------------------------------------------
*
* 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
*/
27 years ago
#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);
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);
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)
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)
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 != ' ')
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)
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);
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),
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);
}