mirror of
https://github.com/postgres/postgres.git
synced 2025-12-01 12:18:01 +03:00
Modify all callers of datatype input and receive functions so that if these
functions are not strict, they will be called (passing a NULL first parameter) during any attempt to input a NULL value of their datatype. Currently, all our input functions are strict and so this commit does not change any behavior. However, this will make it possible to build domain input functions that centralize checking of domain constraints, thereby closing numerous holes in our domain support, as per previous discussion. While at it, I took the opportunity to introduce convenience functions InputFunctionCall, OutputFunctionCall, etc to use in code that calls I/O functions. This eliminates a lot of grotty-looking casts, but the main motivation is to make it easier to grep for these places if we ever need to touch them again.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.127 2006/03/05 15:58:41 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.128 2006/04/04 19:35:35 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -787,14 +787,14 @@ ReadArrayStr(char *arrayStr,
|
||||
pg_strcasecmp(itemstart, "NULL") == 0)
|
||||
{
|
||||
/* it's a NULL item */
|
||||
values[i] = InputFunctionCall(inputproc, NULL,
|
||||
typioparam, typmod);
|
||||
nulls[i] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
values[i] = FunctionCall3(inputproc,
|
||||
CStringGetDatum(itemstart),
|
||||
ObjectIdGetDatum(typioparam),
|
||||
Int32GetDatum(typmod));
|
||||
values[i] = InputFunctionCall(inputproc, itemstart,
|
||||
typioparam, typmod);
|
||||
nulls[i] = false;
|
||||
}
|
||||
}
|
||||
@@ -1018,8 +1018,7 @@ array_out(PG_FUNCTION_ARGS)
|
||||
Datum itemvalue;
|
||||
|
||||
itemvalue = fetch_att(p, typbyval, typlen);
|
||||
values[i] = DatumGetCString(FunctionCall1(&my_extra->proc,
|
||||
itemvalue));
|
||||
values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
|
||||
p = att_addlength(p, typlen, PointerGetDatum(p));
|
||||
p = (char *) att_align(p, typalign);
|
||||
|
||||
@@ -1357,6 +1356,8 @@ ReadArrayBinary(StringInfo buf,
|
||||
if (itemlen == -1)
|
||||
{
|
||||
/* -1 length means NULL */
|
||||
values[i] = ReceiveFunctionCall(receiveproc, NULL,
|
||||
typioparam, typmod);
|
||||
nulls[i] = true;
|
||||
continue;
|
||||
}
|
||||
@@ -1378,10 +1379,8 @@ ReadArrayBinary(StringInfo buf,
|
||||
buf->data[buf->cursor] = '\0';
|
||||
|
||||
/* Now call the element's receiveproc */
|
||||
values[i] = FunctionCall3(receiveproc,
|
||||
PointerGetDatum(&elem_buf),
|
||||
ObjectIdGetDatum(typioparam),
|
||||
Int32GetDatum(typmod));
|
||||
values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
|
||||
typioparam, typmod);
|
||||
nulls[i] = false;
|
||||
|
||||
/* Trouble if it didn't eat the whole buffer */
|
||||
@@ -1515,10 +1514,7 @@ array_send(PG_FUNCTION_ARGS)
|
||||
bytea *outputbytes;
|
||||
|
||||
itemvalue = fetch_att(p, typbyval, typlen);
|
||||
|
||||
outputbytes = DatumGetByteaP(FunctionCall1(&my_extra->proc,
|
||||
itemvalue));
|
||||
/* We assume the result will not have been toasted */
|
||||
outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
|
||||
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
|
||||
pq_sendbytes(&buf, VARDATA(outputbytes),
|
||||
VARSIZE(outputbytes) - VARHDRSZ);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.14 2006/03/05 15:58:43 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.15 2006/04/04 19:35:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -136,6 +136,7 @@ record_in(PG_FUNCTION_ARGS)
|
||||
{
|
||||
ColumnIOData *column_info = &my_extra->columns[i];
|
||||
Oid column_type = tupdesc->attrs[i]->atttypid;
|
||||
char *column_data;
|
||||
|
||||
/* Ignore dropped columns in datatype, but fill with nulls */
|
||||
if (tupdesc->attrs[i]->attisdropped)
|
||||
@@ -161,7 +162,7 @@ record_in(PG_FUNCTION_ARGS)
|
||||
/* Check for null: completely empty input means null */
|
||||
if (*ptr == ',' || *ptr == ')')
|
||||
{
|
||||
values[i] = (Datum) 0;
|
||||
column_data = NULL;
|
||||
nulls[i] = 'n';
|
||||
}
|
||||
else
|
||||
@@ -207,26 +208,28 @@ record_in(PG_FUNCTION_ARGS)
|
||||
appendStringInfoChar(&buf, ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the column value
|
||||
*/
|
||||
if (column_info->column_type != column_type)
|
||||
{
|
||||
getTypeInputInfo(column_type,
|
||||
&column_info->typiofunc,
|
||||
&column_info->typioparam);
|
||||
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
||||
fcinfo->flinfo->fn_mcxt);
|
||||
column_info->column_type = column_type;
|
||||
}
|
||||
|
||||
values[i] = FunctionCall3(&column_info->proc,
|
||||
CStringGetDatum(buf.data),
|
||||
ObjectIdGetDatum(column_info->typioparam),
|
||||
Int32GetDatum(tupdesc->attrs[i]->atttypmod));
|
||||
column_data = buf.data;
|
||||
nulls[i] = ' ';
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the column value
|
||||
*/
|
||||
if (column_info->column_type != column_type)
|
||||
{
|
||||
getTypeInputInfo(column_type,
|
||||
&column_info->typiofunc,
|
||||
&column_info->typioparam);
|
||||
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
||||
fcinfo->flinfo->fn_mcxt);
|
||||
column_info->column_type = column_type;
|
||||
}
|
||||
|
||||
values[i] = InputFunctionCall(&column_info->proc,
|
||||
column_data,
|
||||
column_info->typioparam,
|
||||
tupdesc->attrs[i]->atttypmod);
|
||||
|
||||
/*
|
||||
* Prep for next column
|
||||
*/
|
||||
@@ -372,8 +375,7 @@ record_out(PG_FUNCTION_ARGS)
|
||||
column_info->column_type = column_type;
|
||||
}
|
||||
|
||||
value = DatumGetCString(FunctionCall1(&column_info->proc,
|
||||
values[i]));
|
||||
value = OutputFunctionCall(&column_info->proc, values[i]);
|
||||
|
||||
/* Detect whether we need double quotes for this value */
|
||||
nq = (value[0] == '\0'); /* force quotes for empty string */
|
||||
@@ -505,6 +507,9 @@ record_recv(PG_FUNCTION_ARGS)
|
||||
Oid column_type = tupdesc->attrs[i]->atttypid;
|
||||
Oid coltypoid;
|
||||
int itemlen;
|
||||
StringInfoData item_buf;
|
||||
StringInfo bufptr;
|
||||
char csave;
|
||||
|
||||
/* Ignore dropped columns in datatype, but fill with nulls */
|
||||
if (tupdesc->attrs[i]->attisdropped)
|
||||
@@ -532,8 +537,9 @@ record_recv(PG_FUNCTION_ARGS)
|
||||
if (itemlen == -1)
|
||||
{
|
||||
/* -1 length means NULL */
|
||||
values[i] = (Datum) 0;
|
||||
bufptr = NULL;
|
||||
nulls[i] = 'n';
|
||||
csave = 0; /* keep compiler quiet */
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -543,9 +549,6 @@ record_recv(PG_FUNCTION_ARGS)
|
||||
* We assume we can scribble on the input buffer so as to maintain
|
||||
* the convention that StringInfos have a trailing null.
|
||||
*/
|
||||
StringInfoData item_buf;
|
||||
char csave;
|
||||
|
||||
item_buf.data = &buf->data[buf->cursor];
|
||||
item_buf.maxlen = itemlen + 1;
|
||||
item_buf.len = itemlen;
|
||||
@@ -556,23 +559,28 @@ record_recv(PG_FUNCTION_ARGS)
|
||||
csave = buf->data[buf->cursor];
|
||||
buf->data[buf->cursor] = '\0';
|
||||
|
||||
/* Now call the column's receiveproc */
|
||||
if (column_info->column_type != column_type)
|
||||
{
|
||||
getTypeBinaryInputInfo(column_type,
|
||||
&column_info->typiofunc,
|
||||
&column_info->typioparam);
|
||||
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
||||
fcinfo->flinfo->fn_mcxt);
|
||||
column_info->column_type = column_type;
|
||||
}
|
||||
|
||||
values[i] = FunctionCall3(&column_info->proc,
|
||||
PointerGetDatum(&item_buf),
|
||||
ObjectIdGetDatum(column_info->typioparam),
|
||||
Int32GetDatum(tupdesc->attrs[i]->atttypmod));
|
||||
bufptr = &item_buf;
|
||||
nulls[i] = ' ';
|
||||
}
|
||||
|
||||
/* Now call the column's receiveproc */
|
||||
if (column_info->column_type != column_type)
|
||||
{
|
||||
getTypeBinaryInputInfo(column_type,
|
||||
&column_info->typiofunc,
|
||||
&column_info->typioparam);
|
||||
fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
|
||||
fcinfo->flinfo->fn_mcxt);
|
||||
column_info->column_type = column_type;
|
||||
}
|
||||
|
||||
values[i] = ReceiveFunctionCall(&column_info->proc,
|
||||
bufptr,
|
||||
column_info->typioparam,
|
||||
tupdesc->attrs[i]->atttypmod);
|
||||
|
||||
if (bufptr)
|
||||
{
|
||||
/* Trouble if it didn't eat the whole buffer */
|
||||
if (item_buf.cursor != itemlen)
|
||||
ereport(ERROR,
|
||||
@@ -712,8 +720,7 @@ record_send(PG_FUNCTION_ARGS)
|
||||
column_info->column_type = column_type;
|
||||
}
|
||||
|
||||
outputbytes = DatumGetByteaP(FunctionCall1(&column_info->proc,
|
||||
values[i]));
|
||||
outputbytes = SendFunctionCall(&column_info->proc, values[i]);
|
||||
|
||||
/* We assume the result will not have been toasted */
|
||||
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* ruleutils.c - Functions to convert stored expressions/querytrees
|
||||
* back to source text
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.217 2006/03/16 00:31:55 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.218 2006/04/04 19:35:36 tgl Exp $
|
||||
**********************************************************************/
|
||||
|
||||
#include "postgres.h"
|
||||
@@ -3921,8 +3921,7 @@ get_const_expr(Const *constval, deparse_context *context)
|
||||
getTypeOutputInfo(constval->consttype,
|
||||
&typoutput, &typIsVarlena);
|
||||
|
||||
extval = DatumGetCString(OidFunctionCall1(typoutput,
|
||||
constval->constvalue));
|
||||
extval = OidOutputFunctionCall(typoutput, constval->constvalue);
|
||||
|
||||
switch (constval->consttype)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.145 2006/03/05 15:58:44 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.146 2006/04/04 19:35:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2580,8 +2580,7 @@ array_to_text(PG_FUNCTION_ARGS)
|
||||
{
|
||||
itemvalue = fetch_att(p, typbyval, typlen);
|
||||
|
||||
value = DatumGetCString(FunctionCall1(&my_extra->proc,
|
||||
itemvalue));
|
||||
value = OutputFunctionCall(&my_extra->proc, itemvalue);
|
||||
|
||||
if (printed)
|
||||
appendStringInfo(&buf, "%s%s", fldsep, value);
|
||||
|
||||
8
src/backend/utils/cache/lsyscache.c
vendored
8
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.132 2006/03/05 15:58:45 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.133 2006/04/04 19:35:36 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -1554,10 +1554,8 @@ get_typdefault(Oid typid)
|
||||
strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
|
||||
datum));
|
||||
/* Convert C string to a value of the given type */
|
||||
datum = OidFunctionCall3(type->typinput,
|
||||
CStringGetDatum(strDefaultVal),
|
||||
ObjectIdGetDatum(getTypeIOParam(typeTuple)),
|
||||
Int32GetDatum(-1));
|
||||
datum = OidInputFunctionCall(type->typinput, strDefaultVal,
|
||||
getTypeIOParam(typeTuple), -1);
|
||||
/* Build a Const node containing the value */
|
||||
expr = (Node *) makeConst(typid,
|
||||
type->typlen,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.99 2006/03/05 15:58:46 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.100 2006/04/04 19:35:36 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1682,6 +1682,172 @@ OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Special cases for convenient invocation of datatype I/O functions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Call a previously-looked-up datatype input function.
|
||||
*
|
||||
* "str" may be NULL to indicate we are reading a NULL. In this case
|
||||
* the caller should assume the result is NULL, but we'll call the input
|
||||
* function anyway if it's not strict. So this is almost but not quite
|
||||
* the same as FunctionCall3.
|
||||
*/
|
||||
Datum
|
||||
InputFunctionCall(FmgrInfo *flinfo, char *str, Oid typioparam, int32 typmod)
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
Datum result;
|
||||
|
||||
if (str == NULL && flinfo->fn_strict)
|
||||
return (Datum) 0; /* just return null result */
|
||||
|
||||
InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL);
|
||||
|
||||
fcinfo.arg[0] = CStringGetDatum(str);
|
||||
fcinfo.arg[1] = ObjectIdGetDatum(typioparam);
|
||||
fcinfo.arg[2] = Int32GetDatum(typmod);
|
||||
fcinfo.argnull[0] = (str == NULL);
|
||||
fcinfo.argnull[1] = false;
|
||||
fcinfo.argnull[2] = false;
|
||||
|
||||
result = FunctionCallInvoke(&fcinfo);
|
||||
|
||||
/* Should get null result if and only if str is NULL */
|
||||
if (str == NULL)
|
||||
{
|
||||
if (!fcinfo.isnull)
|
||||
elog(ERROR, "input function %u returned non-NULL",
|
||||
fcinfo.flinfo->fn_oid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fcinfo.isnull)
|
||||
elog(ERROR, "input function %u returned NULL",
|
||||
fcinfo.flinfo->fn_oid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a previously-looked-up datatype output function.
|
||||
*
|
||||
* Do not call this on NULL datums.
|
||||
*
|
||||
* This is mere window dressing for FunctionCall1, but its use is recommended
|
||||
* anyway so that code invoking output functions can be identified easily.
|
||||
*/
|
||||
char *
|
||||
OutputFunctionCall(FmgrInfo *flinfo, Datum val)
|
||||
{
|
||||
return DatumGetCString(FunctionCall1(flinfo, val));
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a previously-looked-up datatype binary-input function.
|
||||
*
|
||||
* "buf" may be NULL to indicate we are reading a NULL. In this case
|
||||
* the caller should assume the result is NULL, but we'll call the receive
|
||||
* function anyway if it's not strict. So this is almost but not quite
|
||||
* the same as FunctionCall3.
|
||||
*/
|
||||
Datum
|
||||
ReceiveFunctionCall(FmgrInfo *flinfo, StringInfo buf,
|
||||
Oid typioparam, int32 typmod)
|
||||
{
|
||||
FunctionCallInfoData fcinfo;
|
||||
Datum result;
|
||||
|
||||
if (buf == NULL && flinfo->fn_strict)
|
||||
return (Datum) 0; /* just return null result */
|
||||
|
||||
InitFunctionCallInfoData(fcinfo, flinfo, 3, NULL, NULL);
|
||||
|
||||
fcinfo.arg[0] = PointerGetDatum(buf);
|
||||
fcinfo.arg[1] = ObjectIdGetDatum(typioparam);
|
||||
fcinfo.arg[2] = Int32GetDatum(typmod);
|
||||
fcinfo.argnull[0] = (buf == NULL);
|
||||
fcinfo.argnull[1] = false;
|
||||
fcinfo.argnull[2] = false;
|
||||
|
||||
result = FunctionCallInvoke(&fcinfo);
|
||||
|
||||
/* Should get null result if and only if buf is NULL */
|
||||
if (buf == NULL)
|
||||
{
|
||||
if (!fcinfo.isnull)
|
||||
elog(ERROR, "receive function %u returned non-NULL",
|
||||
fcinfo.flinfo->fn_oid);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fcinfo.isnull)
|
||||
elog(ERROR, "receive function %u returned NULL",
|
||||
fcinfo.flinfo->fn_oid);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call a previously-looked-up datatype binary-output function.
|
||||
*
|
||||
* Do not call this on NULL datums.
|
||||
*
|
||||
* This is little more than window dressing for FunctionCall1, but its use is
|
||||
* recommended anyway so that code invoking output functions can be identified
|
||||
* easily. Note however that it does guarantee a non-toasted result.
|
||||
*/
|
||||
bytea *
|
||||
SendFunctionCall(FmgrInfo *flinfo, Datum val)
|
||||
{
|
||||
return DatumGetByteaP(FunctionCall1(flinfo, val));
|
||||
}
|
||||
|
||||
/*
|
||||
* As above, for I/O functions identified by OID. These are only to be used
|
||||
* in seldom-executed code paths. They are not only slow but leak memory.
|
||||
*/
|
||||
Datum
|
||||
OidInputFunctionCall(Oid functionId, char *str, Oid typioparam, int32 typmod)
|
||||
{
|
||||
FmgrInfo flinfo;
|
||||
|
||||
fmgr_info(functionId, &flinfo);
|
||||
return InputFunctionCall(&flinfo, str, typioparam, typmod);
|
||||
}
|
||||
|
||||
char *
|
||||
OidOutputFunctionCall(Oid functionId, Datum val)
|
||||
{
|
||||
FmgrInfo flinfo;
|
||||
|
||||
fmgr_info(functionId, &flinfo);
|
||||
return OutputFunctionCall(&flinfo, val);
|
||||
}
|
||||
|
||||
Datum
|
||||
OidReceiveFunctionCall(Oid functionId, StringInfo buf,
|
||||
Oid typioparam, int32 typmod)
|
||||
{
|
||||
FmgrInfo flinfo;
|
||||
|
||||
fmgr_info(functionId, &flinfo);
|
||||
return ReceiveFunctionCall(&flinfo, buf, typioparam, typmod);
|
||||
}
|
||||
|
||||
bytea *
|
||||
OidSendFunctionCall(Oid functionId, Datum val)
|
||||
{
|
||||
FmgrInfo flinfo;
|
||||
|
||||
fmgr_info(functionId, &flinfo);
|
||||
return SendFunctionCall(&flinfo, val);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* !!! OLD INTERFACE !!!
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user