mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-08-30 13:57:19 +02:00
any amount of leading or trailing whitespace (where "whitespace" is defined by isspace()). This is for SQL conformance, as well as consistency with other numeric types (e.g. oid, numeric). Also refactor pg_atoi() to avoid looking at errno where not necessary, and add a bunch of regression tests for the input to these types.
445 lines
9.6 KiB
C
445 lines
9.6 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* oid.c
|
|
* Functions for the built-in type Oid ... also oidvector.
|
|
*
|
|
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/utils/adt/oid.c,v 1.56 2004/03/11 02:11:13 neilc Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
|
|
#include "libpq/pqformat.h"
|
|
#include "utils/builtins.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
* USER I/O ROUTINES *
|
|
*****************************************************************************/
|
|
|
|
static Oid
|
|
oidin_subr(const char *funcname, const char *s, char **endloc)
|
|
{
|
|
unsigned long cvt;
|
|
char *endptr;
|
|
Oid result;
|
|
|
|
/*
|
|
* In releases prior to 7.5, we accepted an empty string as valid
|
|
* input (yielding an OID of 0). In 7.5, we accept empty strings,
|
|
* but emit a warning noting that the feature is deprecated. In
|
|
* 7.6+, the warning should be replaced by an error.
|
|
*/
|
|
if (*s == '\0')
|
|
ereport(WARNING,
|
|
(errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
|
|
errmsg("deprecated input syntax for type oid: \"\""),
|
|
errdetail("This input will be rejected in "
|
|
"a future release of PostgreSQL.")));
|
|
|
|
errno = 0;
|
|
cvt = strtoul(s, &endptr, 10);
|
|
|
|
/*
|
|
* strtoul() normally only sets ERANGE. On some systems it also may
|
|
* set EINVAL, which simply means it couldn't parse the input string.
|
|
* This is handled by the second "if" consistent across platforms.
|
|
*/
|
|
if (errno && errno != ERANGE && errno != EINVAL)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
errmsg("invalid input syntax for type oid: \"%s\"",
|
|
s)));
|
|
|
|
if (endptr == s && *s != '\0')
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
errmsg("invalid input syntax for type oid: \"%s\"",
|
|
s)));
|
|
|
|
if (errno == ERANGE)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("value \"%s\" is out of range for type oid", s)));
|
|
|
|
if (endloc)
|
|
{
|
|
/* caller wants to deal with rest of string */
|
|
*endloc = endptr;
|
|
}
|
|
else
|
|
{
|
|
/* allow only whitespace after number */
|
|
while (*endptr && isspace((unsigned char) *endptr))
|
|
endptr++;
|
|
if (*endptr)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
|
|
errmsg("invalid input syntax for type oid: \"%s\"",
|
|
s)));
|
|
}
|
|
|
|
result = (Oid) cvt;
|
|
|
|
/*
|
|
* Cope with possibility that unsigned long is wider than Oid, in
|
|
* which case strtoul will not raise an error for some values that are
|
|
* out of the range of Oid.
|
|
*
|
|
* For backwards compatibility, we want to accept inputs that are given
|
|
* with a minus sign, so allow the input value if it matches after
|
|
* either signed or unsigned extension to long.
|
|
*
|
|
* To ensure consistent results on 32-bit and 64-bit platforms, make sure
|
|
* the error message is the same as if strtoul() had returned ERANGE.
|
|
*/
|
|
#if OID_MAX != ULONG_MAX
|
|
if (cvt != (unsigned long) result &&
|
|
cvt != (unsigned long) ((int) result))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
errmsg("value \"%s\" is out of range for type oid", s)));
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
Datum
|
|
oidin(PG_FUNCTION_ARGS)
|
|
{
|
|
char *s = PG_GETARG_CSTRING(0);
|
|
Oid result;
|
|
|
|
result = oidin_subr("oidin", s, NULL);
|
|
PG_RETURN_OID(result);
|
|
}
|
|
|
|
Datum
|
|
oidout(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid o = PG_GETARG_OID(0);
|
|
char *result = (char *) palloc(12);
|
|
|
|
snprintf(result, 12, "%u", o);
|
|
PG_RETURN_CSTRING(result);
|
|
}
|
|
|
|
/*
|
|
* oidrecv - converts external binary format to oid
|
|
*/
|
|
Datum
|
|
oidrecv(PG_FUNCTION_ARGS)
|
|
{
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
|
|
PG_RETURN_OID((Oid) pq_getmsgint(buf, sizeof(Oid)));
|
|
}
|
|
|
|
/*
|
|
* oidsend - converts oid to binary format
|
|
*/
|
|
Datum
|
|
oidsend(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
StringInfoData buf;
|
|
|
|
pq_begintypsend(&buf);
|
|
pq_sendint(&buf, arg1, sizeof(Oid));
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
}
|
|
|
|
|
|
/*
|
|
* oidvectorin - converts "num num ..." to internal form
|
|
*
|
|
* Note:
|
|
* Fills any unsupplied positions with InvalidOid.
|
|
*/
|
|
Datum
|
|
oidvectorin(PG_FUNCTION_ARGS)
|
|
{
|
|
char *oidString = PG_GETARG_CSTRING(0);
|
|
Oid *result = (Oid *) palloc(sizeof(Oid[INDEX_MAX_KEYS]));
|
|
int slot;
|
|
|
|
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
|
{
|
|
while (*oidString && isspace((unsigned char) *oidString))
|
|
oidString++;
|
|
if (*oidString == '\0')
|
|
break;
|
|
result[slot] = oidin_subr("oidvectorin", oidString, &oidString);
|
|
}
|
|
while (*oidString && isspace((unsigned char) *oidString))
|
|
oidString++;
|
|
if (*oidString)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("oidvector has too many elements")));
|
|
while (slot < INDEX_MAX_KEYS)
|
|
result[slot++] = InvalidOid;
|
|
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/*
|
|
* oidvectorout - converts internal form to "num num ..."
|
|
*/
|
|
Datum
|
|
oidvectorout(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *oidArray = (Oid *) PG_GETARG_POINTER(0);
|
|
int num,
|
|
maxnum;
|
|
char *rp;
|
|
char *result;
|
|
|
|
/* find last non-zero value in vector */
|
|
for (maxnum = INDEX_MAX_KEYS - 1; maxnum >= 0; maxnum--)
|
|
if (oidArray[maxnum] != 0)
|
|
break;
|
|
|
|
/* assumes sign, 10 digits, ' ' */
|
|
rp = result = (char *) palloc((maxnum + 1) * 12 + 1);
|
|
for (num = 0; num <= maxnum; num++)
|
|
{
|
|
if (num != 0)
|
|
*rp++ = ' ';
|
|
sprintf(rp, "%u", oidArray[num]);
|
|
while (*++rp != '\0')
|
|
;
|
|
}
|
|
*rp = '\0';
|
|
PG_RETURN_CSTRING(result);
|
|
}
|
|
|
|
/*
|
|
* oidvectorrecv - converts external binary format to oidvector
|
|
*/
|
|
Datum
|
|
oidvectorrecv(PG_FUNCTION_ARGS)
|
|
{
|
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
|
Oid *result = (Oid *) palloc(sizeof(Oid[INDEX_MAX_KEYS]));
|
|
int slot;
|
|
|
|
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
|
result[slot] = (Oid) pq_getmsgint(buf, sizeof(Oid));
|
|
PG_RETURN_POINTER(result);
|
|
}
|
|
|
|
/*
|
|
* oidvectorsend - converts oidvector to binary format
|
|
*/
|
|
Datum
|
|
oidvectorsend(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *oidArray = (Oid *) PG_GETARG_POINTER(0);
|
|
StringInfoData buf;
|
|
int slot;
|
|
|
|
pq_begintypsend(&buf);
|
|
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
|
pq_sendint(&buf, oidArray[slot], sizeof(Oid));
|
|
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* PUBLIC ROUTINES *
|
|
*****************************************************************************/
|
|
|
|
Datum
|
|
oideq(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_BOOL(arg1 == arg2);
|
|
}
|
|
|
|
Datum
|
|
oidne(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_BOOL(arg1 != arg2);
|
|
}
|
|
|
|
Datum
|
|
oidlt(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_BOOL(arg1 < arg2);
|
|
}
|
|
|
|
Datum
|
|
oidle(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_BOOL(arg1 <= arg2);
|
|
}
|
|
|
|
Datum
|
|
oidge(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_BOOL(arg1 >= arg2);
|
|
}
|
|
|
|
Datum
|
|
oidgt(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_BOOL(arg1 > arg2);
|
|
}
|
|
|
|
Datum
|
|
oidlarger(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_OID((arg1 > arg2) ? arg1 : arg2);
|
|
}
|
|
|
|
Datum
|
|
oidsmaller(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid arg1 = PG_GETARG_OID(0);
|
|
Oid arg2 = PG_GETARG_OID(1);
|
|
|
|
PG_RETURN_OID((arg1 < arg2) ? arg1 : arg2);
|
|
}
|
|
|
|
Datum
|
|
oidvectoreq(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
|
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
|
|
|
PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(Oid)) == 0);
|
|
}
|
|
|
|
Datum
|
|
oidvectorne(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
|
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
|
|
|
PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(Oid)) != 0);
|
|
}
|
|
|
|
Datum
|
|
oidvectorlt(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
|
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
|
int i;
|
|
|
|
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
|
if (arg1[i] != arg2[i])
|
|
PG_RETURN_BOOL(arg1[i] < arg2[i]);
|
|
PG_RETURN_BOOL(false);
|
|
}
|
|
|
|
Datum
|
|
oidvectorle(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
|
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
|
int i;
|
|
|
|
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
|
if (arg1[i] != arg2[i])
|
|
PG_RETURN_BOOL(arg1[i] <= arg2[i]);
|
|
PG_RETURN_BOOL(true);
|
|
}
|
|
|
|
Datum
|
|
oidvectorge(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
|
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
|
int i;
|
|
|
|
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
|
if (arg1[i] != arg2[i])
|
|
PG_RETURN_BOOL(arg1[i] >= arg2[i]);
|
|
PG_RETURN_BOOL(true);
|
|
}
|
|
|
|
Datum
|
|
oidvectorgt(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
|
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
|
int i;
|
|
|
|
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
|
if (arg1[i] != arg2[i])
|
|
PG_RETURN_BOOL(arg1[i] > arg2[i]);
|
|
PG_RETURN_BOOL(false);
|
|
}
|
|
|
|
Datum
|
|
oid_text(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid oid = PG_GETARG_OID(0);
|
|
text *result;
|
|
int len;
|
|
char *str;
|
|
|
|
str = DatumGetCString(DirectFunctionCall1(oidout,
|
|
ObjectIdGetDatum(oid)));
|
|
len = strlen(str) + VARHDRSZ;
|
|
|
|
result = (text *) palloc(len);
|
|
|
|
VARATT_SIZEP(result) = len;
|
|
memcpy(VARDATA(result), str, (len - VARHDRSZ));
|
|
pfree(str);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
text_oid(PG_FUNCTION_ARGS)
|
|
{
|
|
text *string = PG_GETARG_TEXT_P(0);
|
|
Oid result;
|
|
int len;
|
|
char *str;
|
|
|
|
len = (VARSIZE(string) - VARHDRSZ);
|
|
|
|
str = palloc(len + 1);
|
|
memcpy(str, VARDATA(string), len);
|
|
*(str + len) = '\0';
|
|
|
|
result = oidin_subr("text_oid", str, NULL);
|
|
|
|
pfree(str);
|
|
|
|
PG_RETURN_OID(result);
|
|
}
|