mirror of
https://github.com/postgres/postgres.git
synced 2025-06-15 00:01:49 -04:00
Several submitted and even committed patches have run into the problem that C89, our baseline, does not provide minimum/maximum values for various integer datatypes. C99's stdint.h does, but we can't rely on it. Several parts of the code defined limits locally, so instead centralize the definitions to c.h. This patch also changes the more obvious usages of literal limit values; there's more places that could be changed, but it's less clear whether it's beneficial to change those. Author: Andrew Gierth Discussion: 87619tc5wc.fsf@news-spur.riddles.org.uk
230 lines
5.1 KiB
C
230 lines
5.1 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* numutils.c
|
|
* utility functions for I/O of built-in numeric types.
|
|
*
|
|
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/utils/adt/numutils.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
/*
|
|
* pg_atoi: convert string to integer
|
|
*
|
|
* allows any number of leading or trailing whitespace characters.
|
|
*
|
|
* 'size' is the sizeof() the desired integral result (1, 2, or 4 bytes).
|
|
*
|
|
* c, if not 0, is a terminator character that may appear after the
|
|
* integer (plus whitespace). If 0, the string must end after the integer.
|
|
*
|
|
* Unlike plain atoi(), this will throw ereport() upon bad input format or
|
|
* overflow.
|
|
*/
|
|
int32
|
|
pg_atoi(const char *s, int size, int c)
|
|
{
|
|
long l;
|
|
char *badp;
|
|
|
|
/*
|
|
* Some versions of strtol treat the empty string as an error, but some
|
|
* seem not to. Make an explicit test to be sure we catch it.
|
|
*/
|
|
if (s == NULL)
|
|
elog(ERROR, "NULL pointer");
|
|
if (*s == 0)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
errmsg("invalid input syntax for integer: \"%s\"",
|
|
s)));
|
|
|
|
errno = 0;
|
|
l = strtol(s, &badp, 10);
|
|
|
|
/* We made no progress parsing the string, so bail out */
|
|
if (s == badp)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
errmsg("invalid input syntax for integer: \"%s\"",
|
|
s)));
|
|
|
|
switch (size)
|
|
{
|
|
case sizeof(int32):
|
|
if (errno == ERANGE
|
|
#if defined(HAVE_LONG_INT_64)
|
|
/* won't get ERANGE on these with 64-bit longs... */
|
|
|| l < INT_MIN || l > INT_MAX
|
|
#endif
|
|
)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("value \"%s\" is out of range for type integer", s)));
|
|
break;
|
|
case sizeof(int16):
|
|
if (errno == ERANGE || l < SHRT_MIN || l > SHRT_MAX)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("value \"%s\" is out of range for type smallint", s)));
|
|
break;
|
|
case sizeof(int8):
|
|
if (errno == ERANGE || l < SCHAR_MIN || l > SCHAR_MAX)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("value \"%s\" is out of range for 8-bit integer", s)));
|
|
break;
|
|
default:
|
|
elog(ERROR, "unsupported result size: %d", size);
|
|
}
|
|
|
|
/*
|
|
* Skip any trailing whitespace; if anything but whitespace remains before
|
|
* the terminating character, bail out
|
|
*/
|
|
while (*badp && *badp != c && isspace((unsigned char) *badp))
|
|
badp++;
|
|
|
|
if (*badp && *badp != c)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
errmsg("invalid input syntax for integer: \"%s\"",
|
|
s)));
|
|
|
|
return (int32) l;
|
|
}
|
|
|
|
/*
|
|
* pg_itoa: converts a signed 16-bit integer to its string representation
|
|
*
|
|
* Caller must ensure that 'a' points to enough memory to hold the result
|
|
* (at least 7 bytes, counting a leading sign and trailing NUL).
|
|
*
|
|
* It doesn't seem worth implementing this separately.
|
|
*/
|
|
void
|
|
pg_itoa(int16 i, char *a)
|
|
{
|
|
pg_ltoa((int32) i, a);
|
|
}
|
|
|
|
/*
|
|
* pg_ltoa: converts a signed 32-bit integer to its string representation
|
|
*
|
|
* Caller must ensure that 'a' points to enough memory to hold the result
|
|
* (at least 12 bytes, counting a leading sign and trailing NUL).
|
|
*/
|
|
void
|
|
pg_ltoa(int32 value, char *a)
|
|
{
|
|
char *start = a;
|
|
bool neg = false;
|
|
|
|
/*
|
|
* Avoid problems with the most negative integer not being representable
|
|
* as a positive integer.
|
|
*/
|
|
if (value == (-2147483647 - 1))
|
|
{
|
|
memcpy(a, "-2147483648", 12);
|
|
return;
|
|
}
|
|
else if (value < 0)
|
|
{
|
|
value = -value;
|
|
neg = true;
|
|
}
|
|
|
|
/* Compute the result string backwards. */
|
|
do
|
|
{
|
|
int32 remainder;
|
|
int32 oldval = value;
|
|
|
|
value /= 10;
|
|
remainder = oldval - value * 10;
|
|
*a++ = '0' + remainder;
|
|
} while (value != 0);
|
|
|
|
if (neg)
|
|
*a++ = '-';
|
|
|
|
/* Add trailing NUL byte, and back up 'a' to the last character. */
|
|
*a-- = '\0';
|
|
|
|
/* Reverse string. */
|
|
while (start < a)
|
|
{
|
|
char swap = *start;
|
|
|
|
*start++ = *a;
|
|
*a-- = swap;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* pg_lltoa: convert a signed 64-bit integer to its string representation
|
|
*
|
|
* Caller must ensure that 'a' points to enough memory to hold the result
|
|
* (at least MAXINT8LEN+1 bytes, counting a leading sign and trailing NUL).
|
|
*/
|
|
void
|
|
pg_lltoa(int64 value, char *a)
|
|
{
|
|
char *start = a;
|
|
bool neg = false;
|
|
|
|
/*
|
|
* Avoid problems with the most negative integer not being representable
|
|
* as a positive integer.
|
|
*/
|
|
if (value == INT64_MIN)
|
|
{
|
|
memcpy(a, "-9223372036854775808", 21);
|
|
return;
|
|
}
|
|
else if (value < 0)
|
|
{
|
|
value = -value;
|
|
neg = true;
|
|
}
|
|
|
|
/* Compute the result string backwards. */
|
|
do
|
|
{
|
|
int64 remainder;
|
|
int64 oldval = value;
|
|
|
|
value /= 10;
|
|
remainder = oldval - value * 10;
|
|
*a++ = '0' + remainder;
|
|
} while (value != 0);
|
|
|
|
if (neg)
|
|
*a++ = '-';
|
|
|
|
/* Add trailing NUL byte, and back up 'a' to the last character. */
|
|
*a-- = '\0';
|
|
|
|
/* Reverse string. */
|
|
while (start < a)
|
|
{
|
|
char swap = *start;
|
|
|
|
*start++ = *a;
|
|
*a-- = swap;
|
|
}
|
|
}
|