
94985c210 added code to detect when WindowFuncs were monotonic and allowed additional quals to be "pushed down" into the subquery to be used as WindowClause runConditions in order to short-circuit execution in nodeWindowAgg.c. The Node representation of runConditions wasn't well selected and because we do qual pushdown before planning the subquery, the planning of the subquery could perform subquery pull-up of nested subqueries. For WindowFuncs with args, the arguments could be changed after pushing the qual down to the subquery. This was made more difficult by the fact that the code duplicated the WindowFunc inside an OpExpr to include in the WindowClauses runCondition field. This could result in duplication of subqueries and a pull-up of such a subquery could result in another initplan parameter being issued for the 2nd version of the subplan. This could result in errors such as: ERROR: WindowFunc not found in subplan target lists Here in the backbranches, we don't have the flexibility to improve the Node representation to resolve this, so instead we just disable the runCondition optimization for ntile() unless the argument is a Const, (v16 only) and likewise for count(expr) (both v15 and v16). count(*) is unaffected. All other window functions which support this optimization all take zero arguments and therefore are unaffected. Bug: #18170 Reported-by: Zuming Jiang Discussion: https://postgr.es/m/18170-f1d17bf9a0d58b24@postgresql.org Backpatch-through 15 (master will be fixed independently)
1540 lines
32 KiB
C
1540 lines
32 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* int8.c
|
|
* Internal 64-bit integer operations
|
|
*
|
|
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/utils/adt/int8.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <math.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/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
|
typedef struct
|
|
{
|
|
int64 current;
|
|
int64 finish;
|
|
int64 step;
|
|
} generate_series_fctx;
|
|
|
|
|
|
/***********************************************************************
|
|
**
|
|
** Routines for 64-bit integers.
|
|
**
|
|
***********************************************************************/
|
|
|
|
/*----------------------------------------------------------
|
|
* Formatting and conversion routines.
|
|
*---------------------------------------------------------*/
|
|
|
|
/* int8in()
|
|
*/
|
|
Datum
|
|
int8in(PG_FUNCTION_ARGS)
|
|
{
|
|
char *num = PG_GETARG_CSTRING(0);
|
|
|
|
PG_RETURN_INT64(pg_strtoint64(num));
|
|
}
|
|
|
|
|
|
/* int8out()
|
|
*/
|
|
Datum
|
|
int8out(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val = PG_GETARG_INT64(0);
|
|
char buf[MAXINT8LEN + 1];
|
|
char *result;
|
|
int len;
|
|
|
|
len = pg_lltoa(val, buf) + 1;
|
|
|
|
/*
|
|
* Since the length is already known, we do a manual palloc() and memcpy()
|
|
* to avoid the strlen() call that would otherwise be done in pstrdup().
|
|
*/
|
|
result = palloc(len);
|
|
memcpy(result, buf, len);
|
|
PG_RETURN_CSTRING(result);
|
|
}
|
|
|
|
/*
|
|
* int8recv - converts external binary format to int8
|
|
*/
|
|
Datum
|
|
int8recv(PG_FUNCTION_ARGS)
|
|
{
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
PG_RETURN_INT64(pq_getmsgint64(buf));
|
|
}
|
|
|
|
/*
|
|
* int8send - converts int8 to binary format
|
|
*/
|
|
Datum
|
|
int8send(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
StringInfoData buf;
|
|
|
|
pq_begintypsend(&buf);
|
|
pq_sendint64(&buf, arg1);
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
* Relational operators for int8s, including cross-data-type comparisons.
|
|
*---------------------------------------------------------*/
|
|
|
|
/* int8relop()
|
|
* Is val1 relop val2?
|
|
*/
|
|
Datum
|
|
int8eq(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 == val2);
|
|
}
|
|
|
|
Datum
|
|
int8ne(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 != val2);
|
|
}
|
|
|
|
Datum
|
|
int8lt(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 < val2);
|
|
}
|
|
|
|
Datum
|
|
int8gt(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 > val2);
|
|
}
|
|
|
|
Datum
|
|
int8le(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 <= val2);
|
|
}
|
|
|
|
Datum
|
|
int8ge(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 >= val2);
|
|
}
|
|
|
|
/* int84relop()
|
|
* Is 64-bit val1 relop 32-bit val2?
|
|
*/
|
|
Datum
|
|
int84eq(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int32 val2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_BOOL(val1 == val2);
|
|
}
|
|
|
|
Datum
|
|
int84ne(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int32 val2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_BOOL(val1 != val2);
|
|
}
|
|
|
|
Datum
|
|
int84lt(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int32 val2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_BOOL(val1 < val2);
|
|
}
|
|
|
|
Datum
|
|
int84gt(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int32 val2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_BOOL(val1 > val2);
|
|
}
|
|
|
|
Datum
|
|
int84le(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int32 val2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_BOOL(val1 <= val2);
|
|
}
|
|
|
|
Datum
|
|
int84ge(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int32 val2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_BOOL(val1 >= val2);
|
|
}
|
|
|
|
/* int48relop()
|
|
* Is 32-bit val1 relop 64-bit val2?
|
|
*/
|
|
Datum
|
|
int48eq(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 val1 = PG_GETARG_INT32(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 == val2);
|
|
}
|
|
|
|
Datum
|
|
int48ne(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 val1 = PG_GETARG_INT32(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 != val2);
|
|
}
|
|
|
|
Datum
|
|
int48lt(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 val1 = PG_GETARG_INT32(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 < val2);
|
|
}
|
|
|
|
Datum
|
|
int48gt(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 val1 = PG_GETARG_INT32(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 > val2);
|
|
}
|
|
|
|
Datum
|
|
int48le(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 val1 = PG_GETARG_INT32(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 <= val2);
|
|
}
|
|
|
|
Datum
|
|
int48ge(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 val1 = PG_GETARG_INT32(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 >= val2);
|
|
}
|
|
|
|
/* int82relop()
|
|
* Is 64-bit val1 relop 16-bit val2?
|
|
*/
|
|
Datum
|
|
int82eq(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int16 val2 = PG_GETARG_INT16(1);
|
|
|
|
PG_RETURN_BOOL(val1 == val2);
|
|
}
|
|
|
|
Datum
|
|
int82ne(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int16 val2 = PG_GETARG_INT16(1);
|
|
|
|
PG_RETURN_BOOL(val1 != val2);
|
|
}
|
|
|
|
Datum
|
|
int82lt(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int16 val2 = PG_GETARG_INT16(1);
|
|
|
|
PG_RETURN_BOOL(val1 < val2);
|
|
}
|
|
|
|
Datum
|
|
int82gt(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int16 val2 = PG_GETARG_INT16(1);
|
|
|
|
PG_RETURN_BOOL(val1 > val2);
|
|
}
|
|
|
|
Datum
|
|
int82le(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int16 val2 = PG_GETARG_INT16(1);
|
|
|
|
PG_RETURN_BOOL(val1 <= val2);
|
|
}
|
|
|
|
Datum
|
|
int82ge(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val1 = PG_GETARG_INT64(0);
|
|
int16 val2 = PG_GETARG_INT16(1);
|
|
|
|
PG_RETURN_BOOL(val1 >= val2);
|
|
}
|
|
|
|
/* int28relop()
|
|
* Is 16-bit val1 relop 64-bit val2?
|
|
*/
|
|
Datum
|
|
int28eq(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 val1 = PG_GETARG_INT16(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 == val2);
|
|
}
|
|
|
|
Datum
|
|
int28ne(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 val1 = PG_GETARG_INT16(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 != val2);
|
|
}
|
|
|
|
Datum
|
|
int28lt(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 val1 = PG_GETARG_INT16(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 < val2);
|
|
}
|
|
|
|
Datum
|
|
int28gt(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 val1 = PG_GETARG_INT16(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 > val2);
|
|
}
|
|
|
|
Datum
|
|
int28le(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 val1 = PG_GETARG_INT16(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 <= val2);
|
|
}
|
|
|
|
Datum
|
|
int28ge(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 val1 = PG_GETARG_INT16(0);
|
|
int64 val2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_BOOL(val1 >= val2);
|
|
}
|
|
|
|
/*
|
|
* in_range support function for int8.
|
|
*
|
|
* Note: we needn't supply int8_int4 or int8_int2 variants, as implicit
|
|
* coercion of the offset value takes care of those scenarios just as well.
|
|
*/
|
|
Datum
|
|
in_range_int8_int8(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 val = PG_GETARG_INT64(0);
|
|
int64 base = PG_GETARG_INT64(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),
|
|
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);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------
|
|
* Arithmetic operators on 64-bit integers.
|
|
*---------------------------------------------------------*/
|
|
|
|
Datum
|
|
int8um(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
int64 result;
|
|
|
|
if (unlikely(arg == PG_INT64_MIN))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
result = -arg;
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int8up(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
|
|
PG_RETURN_INT64(arg);
|
|
}
|
|
|
|
Datum
|
|
int8pl(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_add_s64_overflow(arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int8mi(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_sub_s64_overflow(arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int8mul(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_mul_s64_overflow(arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int8div(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 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();
|
|
}
|
|
|
|
/*
|
|
* INT64_MIN / -1 is problematic, since the result can't be represented on
|
|
* a two's-complement machine. Some machines produce INT64_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_INT64_MIN))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
result = -arg1;
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
/* No overflow is possible */
|
|
|
|
result = arg1 / arg2;
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
/* int8abs()
|
|
* Absolute value
|
|
*/
|
|
Datum
|
|
int8abs(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 result;
|
|
|
|
if (unlikely(arg1 == PG_INT64_MIN))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
result = (arg1 < 0) ? -arg1 : arg1;
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
/* int8mod()
|
|
* Modulo operation.
|
|
*/
|
|
Datum
|
|
int8mod(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(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 INT64_MIN % -1,
|
|
* which is a bit silly since the correct answer is perfectly
|
|
* well-defined, namely zero.
|
|
*/
|
|
if (arg2 == -1)
|
|
PG_RETURN_INT64(0);
|
|
|
|
/* No overflow is possible */
|
|
|
|
PG_RETURN_INT64(arg1 % arg2);
|
|
}
|
|
|
|
/*
|
|
* 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 INT64_MIN ---
|
|
* gcd(0, INT64_MIN), gcd(INT64_MIN, 0) and gcd(INT64_MIN, INT64_MIN) are
|
|
* all equal to abs(INT64_MIN), which cannot be represented as a 64-bit signed
|
|
* integer.
|
|
*/
|
|
static int64
|
|
int8gcd_internal(int64 arg1, int64 arg2)
|
|
{
|
|
int64 swap;
|
|
int64 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 INT64_MIN below.
|
|
*
|
|
* We do this in negative space in order to handle INT64_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 INT64_MIN. See comments above. */
|
|
if (arg1 == PG_INT64_MIN)
|
|
{
|
|
if (arg2 == 0 || arg2 == PG_INT64_MIN)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
/*
|
|
* Some machines throw a floating-point exception for INT64_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(INT64_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 INT64_MIN
|
|
* anymore).
|
|
*/
|
|
if (arg1 < 0)
|
|
arg1 = -arg1;
|
|
|
|
return arg1;
|
|
}
|
|
|
|
Datum
|
|
int8gcd(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
result = int8gcd_internal(arg1, arg2);
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
/*
|
|
* Least Common Multiple
|
|
*/
|
|
Datum
|
|
int8lcm(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 gcd;
|
|
int64 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 = INT64_MIN.
|
|
*/
|
|
if (arg1 == 0 || arg2 == 0)
|
|
PG_RETURN_INT64(0);
|
|
|
|
/* lcm(x, y) = abs(x / gcd(x, y) * y) */
|
|
gcd = int8gcd_internal(arg1, arg2);
|
|
arg1 = arg1 / gcd;
|
|
|
|
if (unlikely(pg_mul_s64_overflow(arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
/* If the result is INT64_MIN, it cannot be represented. */
|
|
if (unlikely(result == PG_INT64_MIN))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
if (result < 0)
|
|
result = -result;
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int8inc(PG_FUNCTION_ARGS)
|
|
{
|
|
/*
|
|
* When int8 is pass-by-reference, we provide this special case to avoid
|
|
* palloc overhead for COUNT(): when called as an aggregate, we know that
|
|
* the argument is modifiable local storage, so just update it in-place.
|
|
* (If int8 is pass-by-value, then of course this is useless as well as
|
|
* incorrect, so just ifdef it out.)
|
|
*/
|
|
#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
|
|
if (AggCheckCallContext(fcinfo, NULL))
|
|
{
|
|
int64 *arg = (int64 *) PG_GETARG_POINTER(0);
|
|
|
|
if (unlikely(pg_add_s64_overflow(*arg, 1, arg)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
PG_RETURN_POINTER(arg);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Not called as an aggregate, so just do it the dumb way */
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_add_s64_overflow(arg, 1, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
}
|
|
|
|
Datum
|
|
int8dec(PG_FUNCTION_ARGS)
|
|
{
|
|
/*
|
|
* When int8 is pass-by-reference, we provide this special case to avoid
|
|
* palloc overhead for COUNT(): when called as an aggregate, we know that
|
|
* the argument is modifiable local storage, so just update it in-place.
|
|
* (If int8 is pass-by-value, then of course this is useless as well as
|
|
* incorrect, so just ifdef it out.)
|
|
*/
|
|
#ifndef USE_FLOAT8_BYVAL /* controls int8 too */
|
|
if (AggCheckCallContext(fcinfo, NULL))
|
|
{
|
|
int64 *arg = (int64 *) PG_GETARG_POINTER(0);
|
|
|
|
if (unlikely(pg_sub_s64_overflow(*arg, 1, arg)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_POINTER(arg);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Not called as an aggregate, so just do it the dumb way */
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_sub_s64_overflow(arg, 1, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* These functions are exactly like int8inc/int8dec but are used for
|
|
* aggregates that count only non-null values. Since the functions are
|
|
* declared strict, the null checks happen before we ever get here, and all we
|
|
* need do is increment the state value. We could actually make these pg_proc
|
|
* entries point right at int8inc/int8dec, but then the opr_sanity regression
|
|
* test would complain about mismatched entries for a built-in function.
|
|
*/
|
|
|
|
Datum
|
|
int8inc_any(PG_FUNCTION_ARGS)
|
|
{
|
|
return int8inc(fcinfo);
|
|
}
|
|
|
|
Datum
|
|
int8inc_float8_float8(PG_FUNCTION_ARGS)
|
|
{
|
|
return int8inc(fcinfo);
|
|
}
|
|
|
|
Datum
|
|
int8dec_any(PG_FUNCTION_ARGS)
|
|
{
|
|
return int8dec(fcinfo);
|
|
}
|
|
|
|
/*
|
|
* int8inc_support
|
|
* prosupport function for int8inc() and int8inc_any()
|
|
*/
|
|
Datum
|
|
int8inc_support(PG_FUNCTION_ARGS)
|
|
{
|
|
Node *rawreq = (Node *) PG_GETARG_POINTER(0);
|
|
|
|
if (IsA(rawreq, SupportRequestWFuncMonotonic))
|
|
{
|
|
SupportRequestWFuncMonotonic *req = (SupportRequestWFuncMonotonic *) rawreq;
|
|
MonotonicFunction monotonic = MONOTONICFUNC_NONE;
|
|
int frameOptions = req->window_clause->frameOptions;
|
|
WindowFunc *wfunc = req->window_func;
|
|
|
|
if (list_length(wfunc->args) == 1)
|
|
{
|
|
Node *expr = eval_const_expressions(NULL, linitial(wfunc->args));
|
|
|
|
/*
|
|
* Due to the Node representation of WindowClause runConditions in
|
|
* version prior to v17, we need to insist that the count arg is
|
|
* Const to allow safe application of the runCondition
|
|
* optimization.
|
|
*/
|
|
if (!IsA(expr, Const))
|
|
PG_RETURN_POINTER(NULL);
|
|
}
|
|
|
|
/* No ORDER BY clause then all rows are peers */
|
|
if (req->window_clause->orderClause == NIL)
|
|
monotonic = MONOTONICFUNC_BOTH;
|
|
else
|
|
{
|
|
/*
|
|
* Otherwise take into account the frame options. When the frame
|
|
* bound is the start of the window then the resulting value can
|
|
* never decrease, therefore is monotonically increasing
|
|
*/
|
|
if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
|
|
monotonic |= MONOTONICFUNC_INCREASING;
|
|
|
|
/*
|
|
* Likewise, if the frame bound is the end of the window then the
|
|
* resulting value can never decrease.
|
|
*/
|
|
if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
|
|
monotonic |= MONOTONICFUNC_DECREASING;
|
|
}
|
|
|
|
req->monotonic = monotonic;
|
|
PG_RETURN_POINTER(req);
|
|
}
|
|
|
|
PG_RETURN_POINTER(NULL);
|
|
}
|
|
|
|
|
|
Datum
|
|
int8larger(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
result = ((arg1 > arg2) ? arg1 : arg2);
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int8smaller(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
result = ((arg1 < arg2) ? arg1 : arg2);
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int84pl(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_add_s64_overflow(arg1, (int64) arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int84mi(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_sub_s64_overflow(arg1, (int64) arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int84mul(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_mul_s64_overflow(arg1, (int64) arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int84div(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
int64 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();
|
|
}
|
|
|
|
/*
|
|
* INT64_MIN / -1 is problematic, since the result can't be represented on
|
|
* a two's-complement machine. Some machines produce INT64_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_INT64_MIN))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
result = -arg1;
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
/* No overflow is possible */
|
|
|
|
result = arg1 / arg2;
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int48pl(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_add_s64_overflow((int64) arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int48mi(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_sub_s64_overflow((int64) arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int48mul(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_mul_s64_overflow((int64) arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int48div(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 arg1 = PG_GETARG_INT32(0);
|
|
int64 arg2 = PG_GETARG_INT64(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_INT64((int64) arg1 / arg2);
|
|
}
|
|
|
|
Datum
|
|
int82pl(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_add_s64_overflow(arg1, (int64) arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int82mi(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_sub_s64_overflow(arg1, (int64) arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int82mul(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_mul_s64_overflow(arg1, (int64) arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int82div(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int16 arg2 = PG_GETARG_INT16(1);
|
|
int64 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();
|
|
}
|
|
|
|
/*
|
|
* INT64_MIN / -1 is problematic, since the result can't be represented on
|
|
* a two's-complement machine. Some machines produce INT64_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_INT64_MIN))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
result = -arg1;
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
/* No overflow is possible */
|
|
|
|
result = arg1 / arg2;
|
|
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int28pl(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_add_s64_overflow((int64) arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int28mi(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_sub_s64_overflow((int64) arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int28mul(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
int64 result;
|
|
|
|
if (unlikely(pg_mul_s64_overflow((int64) arg1, arg2, &result)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
PG_RETURN_INT64(result);
|
|
}
|
|
|
|
Datum
|
|
int28div(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 arg1 = PG_GETARG_INT16(0);
|
|
int64 arg2 = PG_GETARG_INT64(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_INT64((int64) arg1 / arg2);
|
|
}
|
|
|
|
/* Binary arithmetics
|
|
*
|
|
* int8and - returns arg1 & arg2
|
|
* int8or - returns arg1 | arg2
|
|
* int8xor - returns arg1 # arg2
|
|
* int8not - returns ~arg1
|
|
* int8shl - returns arg1 << arg2
|
|
* int8shr - returns arg1 >> arg2
|
|
*/
|
|
|
|
Datum
|
|
int8and(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_INT64(arg1 & arg2);
|
|
}
|
|
|
|
Datum
|
|
int8or(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_INT64(arg1 | arg2);
|
|
}
|
|
|
|
Datum
|
|
int8xor(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int64 arg2 = PG_GETARG_INT64(1);
|
|
|
|
PG_RETURN_INT64(arg1 ^ arg2);
|
|
}
|
|
|
|
Datum
|
|
int8not(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
|
|
PG_RETURN_INT64(~arg1);
|
|
}
|
|
|
|
Datum
|
|
int8shl(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_INT64(arg1 << arg2);
|
|
}
|
|
|
|
Datum
|
|
int8shr(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg1 = PG_GETARG_INT64(0);
|
|
int32 arg2 = PG_GETARG_INT32(1);
|
|
|
|
PG_RETURN_INT64(arg1 >> arg2);
|
|
}
|
|
|
|
/*----------------------------------------------------------
|
|
* Conversion operators.
|
|
*---------------------------------------------------------*/
|
|
|
|
Datum
|
|
int48(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 arg = PG_GETARG_INT32(0);
|
|
|
|
PG_RETURN_INT64((int64) arg);
|
|
}
|
|
|
|
Datum
|
|
int84(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
|
|
if (unlikely(arg < PG_INT32_MIN) || unlikely(arg > PG_INT32_MAX))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("integer out of range")));
|
|
|
|
PG_RETURN_INT32((int32) arg);
|
|
}
|
|
|
|
Datum
|
|
int28(PG_FUNCTION_ARGS)
|
|
{
|
|
int16 arg = PG_GETARG_INT16(0);
|
|
|
|
PG_RETURN_INT64((int64) arg);
|
|
}
|
|
|
|
Datum
|
|
int82(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
|
|
if (unlikely(arg < PG_INT16_MIN) || unlikely(arg > PG_INT16_MAX))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("smallint out of range")));
|
|
|
|
PG_RETURN_INT16((int16) arg);
|
|
}
|
|
|
|
Datum
|
|
i8tod(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
float8 result;
|
|
|
|
result = arg;
|
|
|
|
PG_RETURN_FLOAT8(result);
|
|
}
|
|
|
|
/* dtoi8()
|
|
* Convert float8 to 8-byte integer.
|
|
*/
|
|
Datum
|
|
dtoi8(PG_FUNCTION_ARGS)
|
|
{
|
|
float8 num = PG_GETARG_FLOAT8(0);
|
|
|
|
/*
|
|
* Get rid of any fractional part in the input. This is so we don't fail
|
|
* on just-out-of-range values that would round into range. Note
|
|
* assumption that rint() will pass through a NaN or Inf unchanged.
|
|
*/
|
|
num = rint(num);
|
|
|
|
/* Range check */
|
|
if (unlikely(isnan(num) || !FLOAT8_FITS_IN_INT64(num)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
PG_RETURN_INT64((int64) num);
|
|
}
|
|
|
|
Datum
|
|
i8tof(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
float4 result;
|
|
|
|
result = arg;
|
|
|
|
PG_RETURN_FLOAT4(result);
|
|
}
|
|
|
|
/* ftoi8()
|
|
* Convert float4 to 8-byte integer.
|
|
*/
|
|
Datum
|
|
ftoi8(PG_FUNCTION_ARGS)
|
|
{
|
|
float4 num = PG_GETARG_FLOAT4(0);
|
|
|
|
/*
|
|
* Get rid of any fractional part in the input. This is so we don't fail
|
|
* on just-out-of-range values that would round into range. Note
|
|
* assumption that rint() will pass through a NaN or Inf unchanged.
|
|
*/
|
|
num = rint(num);
|
|
|
|
/* Range check */
|
|
if (unlikely(isnan(num) || !FLOAT4_FITS_IN_INT64(num)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("bigint out of range")));
|
|
|
|
PG_RETURN_INT64((int64) num);
|
|
}
|
|
|
|
Datum
|
|
i8tooid(PG_FUNCTION_ARGS)
|
|
{
|
|
int64 arg = PG_GETARG_INT64(0);
|
|
|
|
if (unlikely(arg < 0) || unlikely(arg > PG_UINT32_MAX))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("OID out of range")));
|
|
|
|
PG_RETURN_OID((Oid) arg);
|
|
}
|
|
|
|
Datum
|
|
oidtoi8(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg = PG_GETARG_OID(0);
|
|
|
|
PG_RETURN_INT64((int64) arg);
|
|
}
|
|
|
|
/*
|
|
* non-persistent numeric series generator
|
|
*/
|
|
Datum
|
|
generate_series_int8(PG_FUNCTION_ARGS)
|
|
{
|
|
return generate_series_step_int8(fcinfo);
|
|
}
|
|
|
|
Datum
|
|
generate_series_step_int8(PG_FUNCTION_ARGS)
|
|
{
|
|
FuncCallContext *funcctx;
|
|
generate_series_fctx *fctx;
|
|
int64 result;
|
|
MemoryContext oldcontext;
|
|
|
|
/* stuff done only on the first call of the function */
|
|
if (SRF_IS_FIRSTCALL())
|
|
{
|
|
int64 start = PG_GETARG_INT64(0);
|
|
int64 finish = PG_GETARG_INT64(1);
|
|
int64 step = 1;
|
|
|
|
/* see if we were given an explicit step size */
|
|
if (PG_NARGS() == 3)
|
|
step = PG_GETARG_INT64(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_s64_overflow(fctx->current, fctx->step, &fctx->current))
|
|
fctx->step = 0;
|
|
|
|
/* do when there is more left to send */
|
|
SRF_RETURN_NEXT(funcctx, Int64GetDatum(result));
|
|
}
|
|
else
|
|
/* do when there is no more left */
|
|
SRF_RETURN_DONE(funcctx);
|
|
}
|
|
|
|
/*
|
|
* Planner support function for generate_series(int8, int8 [, int8])
|
|
*/
|
|
Datum
|
|
generate_series_int8_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 = DatumGetInt64(((Const *) arg1)->constvalue);
|
|
finish = DatumGetInt64(((Const *) arg2)->constvalue);
|
|
step = arg3 ? DatumGetInt64(((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);
|
|
}
|