1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-27 22:56:53 +03:00
Tom Lane 31edbadf4a Downgrade implicit casts to text to be assignment-only, except for the ones
from the other string-category types; this eliminates a lot of surprising
interpretations that the parser could formerly make when there was no directly
applicable operator.

Create a general mechanism that supports casts to and from the standard string
types (text,varchar,bpchar) for *every* datatype, by invoking the datatype's
I/O functions.  These new casts are assignment-only in the to-string direction,
explicit-only in the other, and therefore should create no surprising behavior.
Remove a bunch of thereby-obsoleted datatype-specific casting functions.

The "general mechanism" is a new expression node type CoerceViaIO that can
actually convert between *any* two datatypes if their external text
representations are compatible.  This is more general than needed for the
immediate feature, but might be useful in plpgsql or other places in future.

This commit does nothing about the issue that applying the concatenation
operator || to non-text types will now fail, often with strange error messages
due to misinterpreting the operator as array concatenation.  Since it often
(not always) worked before, we should either make it succeed or at least give
a more user-friendly error; but details are still under debate.

Peter Eisentraut and Tom Lane
2007-06-05 21:31:09 +00:00

422 lines
9.0 KiB
C

/*-------------------------------------------------------------------------
*
* oid.c
* Functions for the built-in type Oid ... also oidvector.
*
* Portions Copyright (c) 1996-2007, 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.72 2007/06/05 21:31:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include <limits.h>
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
#define OidVectorSize(n) (offsetof(oidvector, values) + (n) * sizeof(Oid))
/*****************************************************************************
* USER I/O ROUTINES *
*****************************************************************************/
static Oid
oidin_subr(const char *s, char **endloc)
{
unsigned long cvt;
char *endptr;
Oid result;
if (*s == '\0')
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type oid: \"%s\"",
s)));
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(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));
}
/*
* construct oidvector given a raw array of Oids
*
* If oids is NULL then caller must fill values[] afterward
*/
oidvector *
buildoidvector(const Oid *oids, int n)
{
oidvector *result;
result = (oidvector *) palloc0(OidVectorSize(n));
if (n > 0 && oids)
memcpy(result->values, oids, n * sizeof(Oid));
/*
* Attach standard array header. For historical reasons, we set the index
* lower bound to 0 not 1.
*/
SET_VARSIZE(result, OidVectorSize(n));
result->ndim = 1;
result->dataoffset = 0; /* never any nulls */
result->elemtype = OIDOID;
result->dim1 = n;
result->lbound1 = 0;
return result;
}
/*
* oidvectorin - converts "num num ..." to internal form
*/
Datum
oidvectorin(PG_FUNCTION_ARGS)
{
char *oidString = PG_GETARG_CSTRING(0);
oidvector *result;
int n;
result = (oidvector *) palloc0(OidVectorSize(FUNC_MAX_ARGS));
for (n = 0; n < FUNC_MAX_ARGS; n++)
{
while (*oidString && isspace((unsigned char) *oidString))
oidString++;
if (*oidString == '\0')
break;
result->values[n] = oidin_subr(oidString, &oidString);
}
while (*oidString && isspace((unsigned char) *oidString))
oidString++;
if (*oidString)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("oidvector has too many elements")));
SET_VARSIZE(result, OidVectorSize(n));
result->ndim = 1;
result->dataoffset = 0; /* never any nulls */
result->elemtype = OIDOID;
result->dim1 = n;
result->lbound1 = 0;
PG_RETURN_POINTER(result);
}
/*
* oidvectorout - converts internal form to "num num ..."
*/
Datum
oidvectorout(PG_FUNCTION_ARGS)
{
oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0);
int num,
nnums = oidArray->dim1;
char *rp;
char *result;
/* assumes sign, 10 digits, ' ' */
rp = result = (char *) palloc(nnums * 12 + 1);
for (num = 0; num < nnums; num++)
{
if (num != 0)
*rp++ = ' ';
sprintf(rp, "%u", oidArray->values[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);
FunctionCallInfoData locfcinfo;
oidvector *result;
/*
* Normally one would call array_recv() using DirectFunctionCall3, but
* that does not work since array_recv wants to cache some data using
* fcinfo->flinfo->fn_extra. So we need to pass it our own flinfo
* parameter.
*/
InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 3, NULL, NULL);
locfcinfo.arg[0] = PointerGetDatum(buf);
locfcinfo.arg[1] = ObjectIdGetDatum(OIDOID);
locfcinfo.arg[2] = Int32GetDatum(-1);
locfcinfo.argnull[0] = false;
locfcinfo.argnull[1] = false;
locfcinfo.argnull[2] = false;
result = (oidvector *) DatumGetPointer(array_recv(&locfcinfo));
Assert(!locfcinfo.isnull);
/* sanity checks: oidvector must be 1-D, no nulls */
if (ARR_NDIM(result) != 1 ||
ARR_HASNULL(result) ||
ARR_ELEMTYPE(result) != OIDOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid oidvector data")));
PG_RETURN_POINTER(result);
}
/*
* oidvectorsend - converts oidvector to binary format
*/
Datum
oidvectorsend(PG_FUNCTION_ARGS)
{
return array_send(fcinfo);
}
/*****************************************************************************
* 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)
{
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
PG_RETURN_BOOL(cmp == 0);
}
Datum
oidvectorne(PG_FUNCTION_ARGS)
{
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
PG_RETURN_BOOL(cmp != 0);
}
Datum
oidvectorlt(PG_FUNCTION_ARGS)
{
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
PG_RETURN_BOOL(cmp < 0);
}
Datum
oidvectorle(PG_FUNCTION_ARGS)
{
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
PG_RETURN_BOOL(cmp <= 0);
}
Datum
oidvectorge(PG_FUNCTION_ARGS)
{
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
PG_RETURN_BOOL(cmp >= 0);
}
Datum
oidvectorgt(PG_FUNCTION_ARGS)
{
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
PG_RETURN_BOOL(cmp > 0);
}