| /*------------------------------------------------------------------------- |
| * |
| * int8.c |
| * Internal 64-bit integer operations |
| * |
| * Portions Copyright (c) 1996-2023, 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_safe(num, fcinfo->context)); |
| } |
| |
| |
| /* 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); |
| } |