diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index 9382395182a9776ef9b677e911c452338a000f02..e3b8b44d8f9ddf5a784fd7815aaff5b0fc7fabab 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.61 2006/02/28 22:37:25 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.62 2006/04/04 19:35:32 tgl Exp $ PostgreSQL documentation --> @@ -97,8 +97,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> <para> The <replaceable class="parameter">input_function</replaceable> converts the type's external textual representation to the internal - representation used by the - operators and functions defined for the type. + representation used by the operators and functions defined for the type. <replaceable class="parameter">output_function</replaceable> performs the reverse transformation. The input function may be declared as taking one argument of type <type>cstring</type>, @@ -110,9 +109,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> and the third is the <literal>typmod</> of the destination column, if known (-1 will be passed if not). The input function must return a value of the data type itself. + Usually, an input function should be declared STRICT; if it is not, + it will be called with a NULL first parameter when reading a NULL + input value. The function must still return NULL in this case, unless + it raises an error. + (This case is mainly meant to support domain input functions, which + may need to reject NULL inputs.) The output function must be declared as taking one argument of the new data type. The output function must return type <type>cstring</type>. + Output functions are not invoked for NULL values. </para> <para> @@ -133,6 +139,12 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> holding the received byte string; the optional arguments are the same as for the text input function. The receive function must return a value of the data type itself. + Usually, a receive function should be declared STRICT; if it is not, + it will be called with a NULL first parameter when reading a NULL + input value. The function must still return NULL in this case, unless + it raises an error. + (This case is mainly meant to support domain receive functions, which + may need to reject NULL inputs.) Similarly, the optional <replaceable class="parameter">send_function</replaceable> converts from the internal representation to the external binary representation. @@ -140,6 +152,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> output. The send function must be declared as taking one argument of the new data type. The send function must return type <type>bytea</type>. + Send functions are not invoked for NULL values. </para> <para> diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c index 3ba606dfaf1cacc8edc52b4737e50e5c06f77115..ba5793b0e7eb3581c39032b5defb8ece5081bb0a 100644 --- a/src/backend/access/common/printtup.c +++ b/src/backend/access/common/printtup.c @@ -9,7 +9,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.94 2006/03/05 15:58:20 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/printtup.c,v 1.95 2006/04/04 19:35:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -331,8 +331,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self) /* Text output */ char *outputstr; - outputstr = DatumGetCString(FunctionCall1(&thisState->finfo, - attr)); + outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); pfree(outputstr); } @@ -341,9 +340,7 @@ printtup(TupleTableSlot *slot, DestReceiver *self) /* Binary output */ bytea *outputbytes; - outputbytes = DatumGetByteaP(FunctionCall1(&thisState->finfo, - attr)); - /* We assume the result will not have been toasted */ + outputbytes = SendFunctionCall(&thisState->finfo, attr); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); @@ -429,8 +426,7 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self) else attr = origattr; - outputstr = DatumGetCString(FunctionCall1(&thisState->finfo, - attr)); + outputstr = OutputFunctionCall(&thisState->finfo, attr); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), true); pfree(outputstr); @@ -542,8 +538,7 @@ debugtup(TupleTableSlot *slot, DestReceiver *self) else attr = origattr; - value = DatumGetCString(OidFunctionCall1(typoutput, - attr)); + value = OidOutputFunctionCall(typoutput, attr); printatt((unsigned) i + 1, typeinfo->attrs[i], value); @@ -632,8 +627,7 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self) else attr = origattr; - outputbytes = DatumGetByteaP(FunctionCall1(&thisState->finfo, - attr)); + outputbytes = SendFunctionCall(&thisState->finfo, attr); /* We assume the result will not have been toasted */ pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 1d48fc44e18524f4900e4bef817729481a61fe63..c1d16b0e8fccd81bf4d8b93921b8cc6aba734774 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.213 2006/03/07 01:03:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootstrap.c,v 1.214 2006/04/04 19:35:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -826,11 +826,11 @@ InsertOneValue(char *value, int i) if (ap == NULL) elog(ERROR, "could not find atttypid %u in Typ list", typoid); - /* XXX this should match getTypeIOParam() */ - if (ap->am_typ.typtype == 'c') - typioparam = typoid; - else + /* XXX this logic should match getTypeIOParam() */ + if (OidIsValid(ap->am_typ.typelem)) typioparam = ap->am_typ.typelem; + else + typioparam = typoid; typinput = ap->am_typ.typinput; typoutput = ap->am_typ.typoutput; @@ -850,19 +850,18 @@ InsertOneValue(char *value, int i) elog(ERROR, "type oid %u not found", typoid); elog(DEBUG5, "Typ == NULL, typeindex = %u", typeindex); - /* XXX there are no composite types in TypInfo */ - typioparam = TypInfo[typeindex].elem; + /* XXX this logic should match getTypeIOParam() */ + if (OidIsValid(TypInfo[typeindex].elem)) + typioparam = TypInfo[typeindex].elem; + else + typioparam = typoid; typinput = TypInfo[typeindex].inproc; typoutput = TypInfo[typeindex].outproc; } - values[i] = OidFunctionCall3(typinput, - CStringGetDatum(value), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); - prt = DatumGetCString(OidFunctionCall1(typoutput, - values[i])); + values[i] = OidInputFunctionCall(typinput, value, typioparam, -1); + prt = OidOutputFunctionCall(typoutput, values[i]); elog(DEBUG4, "inserted -> %s", prt); pfree(prt); } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 23c057af8e08da8d154e59c77239a278823193d0..af21d565f1781bbc392c9234324ca58ad6588a30 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.261 2006/03/23 00:19:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.262 2006/04/04 19:35:33 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1382,8 +1382,8 @@ CopyTo(CopyState cstate) { if (!cstate->binary) { - string = DatumGetCString(FunctionCall1(&out_functions[attnum - 1], - value)); + string = OutputFunctionCall(&out_functions[attnum - 1], + value); if (cstate->csv_mode) CopyAttributeOutCSV(cstate, string, force_quote[attnum - 1], @@ -1395,9 +1395,8 @@ CopyTo(CopyState cstate) { bytea *outputbytes; - outputbytes = DatumGetByteaP(FunctionCall1(&out_functions[attnum - 1], - value)); - /* We assume the result will not have been toasted */ + outputbytes = SendFunctionCall(&out_functions[attnum - 1], + value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); @@ -1459,6 +1458,13 @@ copy_in_error_callback(void *arg) cstate->cur_attname, attval); pfree(attval); } + else if (cstate->cur_attname) + { + /* error is relevant to a particular column, value is NULL */ + errcontext("COPY %s, line %d, column %s: NULL input", + cstate->cur_relname, cstate->cur_lineno, + cstate->cur_attname); + } else { /* error is relevant to a particular line */ @@ -1854,19 +1860,16 @@ CopyFrom(CopyState cstate) string = cstate->null_print; } - /* If we read an SQL NULL, no need to do anything */ + cstate->cur_attname = NameStr(attr[m]->attname); + cstate->cur_attval = string; + values[m] = InputFunctionCall(&in_functions[m], + string, + typioparams[m], + attr[m]->atttypmod); if (string != NULL) - { - cstate->cur_attname = NameStr(attr[m]->attname); - cstate->cur_attval = string; - values[m] = FunctionCall3(&in_functions[m], - CStringGetDatum(string), - ObjectIdGetDatum(typioparams[m]), - Int32GetDatum(attr[m]->atttypmod)); nulls[m] = ' '; - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; } Assert(fieldno == nfields); @@ -2900,7 +2903,7 @@ CopyReadBinaryAttribute(CopyState cstate, if (fld_size == -1) { *isnull = true; - return (Datum) 0; + return ReceiveFunctionCall(flinfo, NULL, typioparam, typmod); } if (fld_size < 0) ereport(ERROR, @@ -2924,10 +2927,8 @@ CopyReadBinaryAttribute(CopyState cstate, cstate->attribute_buf.data[fld_size] = '\0'; /* Call the column type's binary input converter */ - result = FunctionCall3(flinfo, - PointerGetDatum(&cstate->attribute_buf), - ObjectIdGetDatum(typioparam), - Int32GetDatum(typmod)); + result = ReceiveFunctionCall(flinfo, &cstate->attribute_buf, + typioparam, typmod); /* Trouble if it didn't eat the whole buffer */ if (cstate->attribute_buf.cursor != cstate->attribute_buf.len) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index bd50027f776805cc789251cbb8964418b274be6f..81f589fd44b4a81bbbe28d9a9b4cbac4886b765b 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.92 2006/03/05 15:58:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execTuples.c,v 1.93 2006/04/04 19:35:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -928,6 +928,7 @@ TupleDescGetAttInMetadata(TupleDesc tupdesc) /* * BuildTupleFromCStrings - build a HeapTuple given user data in C string form. * values is an array of C strings, one for each attribute of the return tuple. + * A NULL string pointer indicates we want to create a NULL field. */ HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) @@ -937,35 +938,25 @@ BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values) Datum *dvalues; char *nulls; int i; - Oid attioparam; - int32 atttypmod; HeapTuple tuple; dvalues = (Datum *) palloc(natts * sizeof(Datum)); nulls = (char *) palloc(natts * sizeof(char)); - /* Call the "in" function for each non-null, non-dropped attribute */ + /* Call the "in" function for each non-dropped attribute */ for (i = 0; i < natts; i++) { if (!tupdesc->attrs[i]->attisdropped) { /* Non-dropped attributes */ + dvalues[i] = InputFunctionCall(&attinmeta->attinfuncs[i], + values[i], + attinmeta->attioparams[i], + attinmeta->atttypmods[i]); if (values[i] != NULL) - { - attioparam = attinmeta->attioparams[i]; - atttypmod = attinmeta->atttypmods[i]; - - dvalues[i] = FunctionCall3(&attinmeta->attinfuncs[i], - CStringGetDatum(values[i]), - ObjectIdGetDatum(attioparam), - Int32GetDatum(atttypmod)); nulls[i] = ' '; - } else - { - dvalues[i] = (Datum) 0; nulls[i] = 'n'; - } } else { diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 5b48545aac93eeedd52925983e6bc7da9ad53cb3..a242d53968e6a700bada053035ef2732a05efe0d 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -61,7 +61,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.138 2006/03/05 15:58:26 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.139 2006/04/04 19:35:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1407,10 +1407,8 @@ GetAggInitVal(Datum textInitVal, Oid transtype) getTypeInputInfo(transtype, &typinput, &typioparam); strInitVal = DatumGetCString(DirectFunctionCall1(textout, textInitVal)); - initVal = OidFunctionCall3(typinput, - CStringGetDatum(strInitVal), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); + initVal = OidInputFunctionCall(typinput, strInitVal, + typioparam, -1); pfree(strInitVal); return initVal; } diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 183bc89b020843c195332c3037bd1a495b735bb5..3563ba23d3ad2f7cbb8f2fed207402b3714936b9 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.149 2006/03/14 22:48:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.150 2006/04/04 19:35:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -629,9 +629,9 @@ SPI_fname(TupleDesc tupdesc, int fnumber) char * SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) { + char *result; Datum origval, - val, - result; + val; bool isnull; Oid typoid, foutoid; @@ -666,14 +666,13 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) else val = origval; - result = OidFunctionCall1(foutoid, - val); + result = OidOutputFunctionCall(foutoid, val); /* Clean up detoasted copy, if any */ if (val != origval) pfree(DatumGetPointer(val)); - return DatumGetCString(result); + return result; } Datum diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index 18b7535cd06fa1352438ade7c1dc1e32efe66361..c3fbdf242d04895c14591603b12252ac204c9597 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.78 2006/03/05 15:58:28 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/print.c,v 1.79 2006/04/04 19:35:34 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -357,8 +357,7 @@ print_expr(Node *expr, List *rtable) getTypeOutputInfo(c->consttype, &typoutput, &typIsVarlena); - outputstr = DatumGetCString(OidFunctionCall1(typoutput, - c->constvalue)); + outputstr = OidOutputFunctionCall(typoutput, c->constvalue); printf("%s", outputstr); pfree(outputstr); } diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index bcb8e0016dfa0fe0826599bf78f83e9768705f90..5a343e768dda1a9492fb68f45a4c259e1ae88925 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.135 2006/03/05 15:58:33 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.136 2006/04/04 19:35:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -166,26 +166,21 @@ coerce_type(ParseState *pstate, Node *node, newcon->constbyval = typeByVal(targetType); newcon->constisnull = con->constisnull; + /* + * We pass typmod -1 to the input routine, primarily because + * existing input routines follow implicit-coercion semantics for + * length checks, which is not always what we want here. Any + * length constraint will be applied later by our caller. + * + * We assume here that UNKNOWN's internal representation is the + * same as CSTRING. + */ if (!con->constisnull) - { - /* - * We assume here that UNKNOWN's internal representation is the - * same as CSTRING - */ - char *val = DatumGetCString(con->constvalue); - - /* - * We pass typmod -1 to the input routine, primarily because - * existing input routines follow implicit-coercion semantics for - * length checks, which is not always what we want here. Any - * length constraint will be applied later by our caller. - * - * Note that we call stringTypeDatum using the domain's pg_type - * row, if it's a domain. This works because the domain row has - * the same typinput and typelem as the base type --- ugly... - */ - newcon->constvalue = stringTypeDatum(targetType, val, -1); - } + newcon->constvalue = stringTypeDatum(targetType, + DatumGetCString(con->constvalue), + -1); + else + newcon->constvalue = stringTypeDatum(targetType, NULL, -1); result = (Node *) newcon; diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index e88e6c37c1e1276d0364c8c9045a0bd12409a7a2..fec4552c9c4129c9b9ce467244e8072ec4c93ae7 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.79 2006/03/14 22:48:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.80 2006/04/04 19:35:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -326,7 +326,8 @@ typeTypeRelid(Type typ) /* * Given a type structure and a string, returns the internal representation - * of that string + * of that string. The "string" can be NULL to perform conversion of a NULL + * (which might result in failure, if the input function rejects NULLs). */ Datum stringTypeDatum(Type tp, char *string, int32 atttypmod) @@ -336,10 +337,8 @@ stringTypeDatum(Type tp, char *string, int32 atttypmod) typinput = ((Form_pg_type) GETSTRUCT(tp))->typinput; typioparam = getTypeIOParam(tp); - return OidFunctionCall3(typinput, - CStringGetDatum(string), - ObjectIdGetDatum(typioparam), - Int32GetDatum(atttypmod)); + return OidInputFunctionCall(typinput, string, + typioparam, atttypmod); } /* given a typeid, return the type's typrelid (associated relation, if any) */ diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index f9a5d7116b1e685ad3d19a456686fd89323ed9fa..c9fa715600fee72620692f6eb29162c0eb558260 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/fastpath.c,v 1.85 2006/03/05 15:58:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/fastpath.c,v 1.86 2006/04/04 19:35:35 tgl Exp $ * * NOTES * This cruft is the server side of PQfn. @@ -154,8 +154,7 @@ SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format) char *outputstr; getTypeOutputInfo(rettype, &typoutput, &typisvarlena); - outputstr = DatumGetCString(OidFunctionCall1(typoutput, - retval)); + outputstr = OidOutputFunctionCall(typoutput, retval); pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false); pfree(outputstr); } @@ -166,9 +165,7 @@ SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format) bytea *outputbytes; getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena); - outputbytes = DatumGetByteaP(OidFunctionCall1(typsend, - retval)); - /* We assume the result will not have been toasted */ + outputbytes = OidSendFunctionCall(typsend, retval); pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4); pq_sendbytes(&buf, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); @@ -433,23 +430,25 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info * fip, if (argsize == -1) { fcinfo->argnull[i] = true; - continue; } - fcinfo->argnull[i] = false; - if (argsize < 0) - ereport(ERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid argument size %d in function call message", - argsize))); - - /* Reset abuf to empty, and insert raw data into it */ - abuf.len = 0; - abuf.data[0] = '\0'; - abuf.cursor = 0; - - appendBinaryStringInfo(&abuf, - pq_getmsgbytes(msgBuf, argsize), - argsize); + else + { + fcinfo->argnull[i] = false; + if (argsize < 0) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid argument size %d in function call message", + argsize))); + + /* Reset abuf to empty, and insert raw data into it */ + abuf.len = 0; + abuf.data[0] = '\0'; + abuf.cursor = 0; + + appendBinaryStringInfo(&abuf, + pq_getmsgbytes(msgBuf, argsize), + argsize); + } if (numAFormats > 1) aformat = aformats[i]; @@ -472,31 +471,36 @@ parse_fcall_arguments(StringInfo msgBuf, struct fp_info * fip, * have to do encoding conversion before calling the typinput * routine, though. */ - pstring = pg_client_to_server(abuf.data, argsize); - fcinfo->arg[i] = - OidFunctionCall3(typinput, - CStringGetDatum(pstring), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); + if (argsize == -1) + pstring = NULL; + else + pstring = pg_client_to_server(abuf.data, argsize); + + fcinfo->arg[i] = OidInputFunctionCall(typinput, pstring, + typioparam, -1); /* Free result of encoding conversion, if any */ - if (pstring != abuf.data) + if (pstring && pstring != abuf.data) pfree(pstring); } else if (aformat == 1) { Oid typreceive; Oid typioparam; + StringInfo bufptr; /* Call the argument type's binary input converter */ getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam); - fcinfo->arg[i] = OidFunctionCall3(typreceive, - PointerGetDatum(&abuf), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); + if (argsize == -1) + bufptr = NULL; + else + bufptr = &abuf; + + fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, bufptr, + typioparam, -1); /* Trouble if it didn't eat the whole buffer */ - if (abuf.cursor != abuf.len) + if (argsize != -1 && abuf.cursor != abuf.len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format in function argument %d", @@ -552,18 +556,22 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info * fip, Oid typreceive; Oid typioparam; + getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam); + argsize = pq_getmsgint(msgBuf, 4); if (argsize == -1) { fcinfo->argnull[i] = true; + fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, NULL, + typioparam, -1); continue; } fcinfo->argnull[i] = false; if (argsize < 0) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("invalid argument size %d in function call message", - argsize))); + errmsg("invalid argument size %d in function call message", + argsize))); /* Reset abuf to empty, and insert raw data into it */ abuf.len = 0; @@ -574,13 +582,8 @@ parse_fcall_arguments_20(StringInfo msgBuf, struct fp_info * fip, pq_getmsgbytes(msgBuf, argsize), argsize); - /* Call the argument type's binary input converter */ - getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam); - - fcinfo->arg[i] = OidFunctionCall3(typreceive, - PointerGetDatum(&abuf), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); + fcinfo->arg[i] = OidReceiveFunctionCall(typreceive, &abuf, + typioparam, -1); /* Trouble if it didn't eat the whole buffer */ if (abuf.cursor != abuf.len) diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 78cb8c3f811be947ea00f1ca78438ad0e1ec6820..df9ef8983a041145560f67c1523dba3e0cf48560 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.482 2006/03/14 22:48:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.483 2006/04/04 19:35:35 tgl Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -1486,6 +1486,9 @@ exec_bind_message(StringInfo input_message) Oid ptype = lfirst_oid(l); int32 plength; bool isNull; + StringInfoData pbuf; + char csave; + int16 pformat; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); @@ -1493,16 +1496,6 @@ exec_bind_message(StringInfo input_message) if (!isNull) { const char *pvalue = pq_getmsgbytes(input_message, plength); - int16 pformat; - StringInfoData pbuf; - char csave; - - if (numPFormats > 1) - pformat = pformats[i]; - else if (numPFormats > 0) - pformat = pformats[0]; - else - pformat = 0; /* default = text */ /* * Rather than copying data around, we just set up a phony @@ -1519,63 +1512,80 @@ exec_bind_message(StringInfo input_message) csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; + } + else + { + pbuf.data = NULL; /* keep compiler quiet */ + csave = 0; + } - if (pformat == 0) - { - Oid typinput; - Oid typioparam; - char *pstring; + if (numPFormats > 1) + pformat = pformats[i]; + else if (numPFormats > 0) + pformat = pformats[0]; + else + pformat = 0; /* default = text */ - getTypeInputInfo(ptype, &typinput, &typioparam); + if (pformat == 0) + { + Oid typinput; + Oid typioparam; + char *pstring; - /* - * We have to do encoding conversion before calling the - * typinput routine. - */ + getTypeInputInfo(ptype, &typinput, &typioparam); + + /* + * We have to do encoding conversion before calling the + * typinput routine. + */ + if (isNull) + pstring = NULL; + else pstring = pg_client_to_server(pbuf.data, plength); - params[i].value = - OidFunctionCall3(typinput, - CStringGetDatum(pstring), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); - /* Free result of encoding conversion, if any */ - if (pstring != pbuf.data) - pfree(pstring); - } - else if (pformat == 1) - { - Oid typreceive; - Oid typioparam; - /* - * Call the parameter type's binary input converter - */ - getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); - - params[i].value = - OidFunctionCall3(typreceive, - PointerGetDatum(&pbuf), - ObjectIdGetDatum(typioparam), - Int32GetDatum(-1)); - - /* Trouble if it didn't eat the whole buffer */ - if (pbuf.cursor != pbuf.len) - ereport(ERROR, - (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), - errmsg("incorrect binary data format in bind parameter %d", - i + 1))); - } + params[i].value = OidInputFunctionCall(typinput, pstring, + typioparam, -1); + /* Free result of encoding conversion, if any */ + if (pstring && pstring != pbuf.data) + pfree(pstring); + } + else if (pformat == 1) + { + Oid typreceive; + Oid typioparam; + StringInfo bufptr; + + /* + * Call the parameter type's binary input converter + */ + getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); + + if (isNull) + bufptr = NULL; else - { + bufptr = &pbuf; + + params[i].value = OidReceiveFunctionCall(typreceive, bufptr, + typioparam, -1); + + /* Trouble if it didn't eat the whole buffer */ + if (!isNull && pbuf.cursor != pbuf.len) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unsupported format code: %d", - pformat))); - } + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("incorrect binary data format in bind parameter %d", + i + 1))); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unsupported format code: %d", + pformat))); + } - /* Restore message buffer contents */ + /* Restore message buffer contents */ + if (!isNull) pbuf.data[plength] = csave; - } params[i].kind = PARAM_NUM; params[i].id = i + 1; diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 1de3ff13a7dcb82344539a02854cd51f55d9b44b..dba7353ba588152fbc9c5e435d16a1c9793dfd9a 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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); diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c index 9d7fcaa413a12625e92658c7cf7b35abcd6f4696..bb61dc2956840de9073f278a593d31697e4deae5 100644 --- a/src/backend/utils/adt/rowtypes.c +++ b/src/backend/utils/adt/rowtypes.c @@ -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); diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 714140e06179d315ad64b36802612a90d46418bf..f8daab6eeaf234eec3448bd1657a70d15bda14b5 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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) { diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index b936b8219dd52e372ad5c5558672bb4c19fff639..79dc0178a80ee284d2ccfde2a72095d3399687ae 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -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); diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 6eb6ea802bfd2ab5b816988f9cbff304cd349dad..fb24cc623660bcb7fd162af4262b252f35abcc24 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -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, diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index e64ede6f5661c7c13a8d20a112aee1bc8ab72469..4a663135dcbe6d63a9cb11cb5eb90f0cca035e40 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -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 !!! * diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 9f17a24357d9d168ebac69753a34b391868e757e..0d6e72594d2cbc810ddea8311568f19a0e27f44d 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.42 2006/03/05 15:58:52 momjian Exp $ + * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.43 2006/04/04 19:35:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,8 @@ /* We don't want to include primnodes.h here, so make a stub reference */ typedef struct Node *fmNodePtr; +/* Likewise, avoid including stringinfo.h here */ +typedef struct StringInfoData *fmStringInfo; /* @@ -394,6 +396,20 @@ extern Datum OidFunctionCall9(Oid functionId, Datum arg1, Datum arg2, Datum arg6, Datum arg7, Datum arg8, Datum arg9); +/* Special cases for convenient invocation of datatype I/O functions. */ +extern Datum InputFunctionCall(FmgrInfo *flinfo, char *str, + Oid typioparam, int32 typmod); +extern Datum OidInputFunctionCall(Oid functionId, char *str, + Oid typioparam, int32 typmod); +extern char *OutputFunctionCall(FmgrInfo *flinfo, Datum val); +extern char *OidOutputFunctionCall(Oid functionId, Datum val); +extern Datum ReceiveFunctionCall(FmgrInfo *flinfo, fmStringInfo buf, + Oid typioparam, int32 typmod); +extern Datum OidReceiveFunctionCall(Oid functionId, fmStringInfo buf, + Oid typioparam, int32 typmod); +extern bytea *SendFunctionCall(FmgrInfo *flinfo, Datum val); +extern bytea *OidSendFunctionCall(Oid functionId, Datum val); + /* * Routines in fmgr.c diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 0524d9ebe4e4026b92c2a27e2dbca0aca2930ae2..310df025706e5f31714459bdc8fad7fb93d39468 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -1,7 +1,7 @@ /********************************************************************** * plperl.c - perl as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.107 2006/03/19 22:22:56 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.108 2006/04/04 19:35:37 tgl Exp $ * **********************************************************************/ @@ -585,31 +585,35 @@ plperl_modify_tuple(HV *hvTD, TriggerData *tdata, HeapTuple otup) while ((val = hv_iternextsv(hvNew, &key, &klen))) { int attn = SPI_fnumber(tupdesc, key); + Oid typinput; + Oid typioparam; + int32 atttypmod; + FmgrInfo finfo; if (attn <= 0 || tupdesc->attrs[attn - 1]->attisdropped) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("Perl hash contains nonexistent column \"%s\"", key))); + /* XXX would be better to cache these lookups */ + getTypeInputInfo(tupdesc->attrs[attn - 1]->atttypid, + &typinput, &typioparam); + fmgr_info(typinput, &finfo); + atttypmod = tupdesc->attrs[attn - 1]->atttypmod; if (SvOK(val) && SvTYPE(val) != SVt_NULL) { - Oid typinput; - Oid typioparam; - FmgrInfo finfo; - - /* XXX would be better to cache these lookups */ - getTypeInputInfo(tupdesc->attrs[attn - 1]->atttypid, - &typinput, &typioparam); - fmgr_info(typinput, &finfo); - modvalues[slotsused] = FunctionCall3(&finfo, - CStringGetDatum(SvPV(val, PL_na)), - ObjectIdGetDatum(typioparam), - Int32GetDatum(tupdesc->attrs[attn - 1]->atttypmod)); + modvalues[slotsused] = InputFunctionCall(&finfo, + SvPV(val, PL_na), + typioparam, + atttypmod); modnulls[slotsused] = ' '; } else { - modvalues[slotsused] = (Datum) 0; + modvalues[slotsused] = InputFunctionCall(&finfo, + NULL, + typioparam, + atttypmod); modnulls[slotsused] = 'n'; } modattrs[slotsused] = attn; @@ -897,8 +901,8 @@ plperl_call_perl_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo) { char *tmp; - tmp = DatumGetCString(FunctionCall1(&(desc->arg_out_func[i]), - fcinfo->arg[i])); + tmp = OutputFunctionCall(&(desc->arg_out_func[i]), + fcinfo->arg[i]); sv = newSVpv(tmp, 0); #if PERL_BCDVERSION >= 0x5006000L if (GetDatabaseEncoding() == PG_UTF8) @@ -1091,8 +1095,9 @@ plperl_func_handler(PG_FUNCTION_ARGS) /* Return NULL if Perl code returned undef */ if (rsi && IsA(rsi, ReturnSetInfo)) rsi->isDone = ExprEndResult; + retval = InputFunctionCall(&prodesc->result_in_func, NULL, + prodesc->result_typioparam, -1); fcinfo->isnull = true; - retval = (Datum) 0; } else if (prodesc->fn_retistuple) { @@ -1138,10 +1143,8 @@ plperl_func_handler(PG_FUNCTION_ARGS) val = SvPV(perlret, PL_na); - retval = FunctionCall3(&prodesc->result_in_func, - CStringGetDatum(val), - ObjectIdGetDatum(prodesc->result_typioparam), - Int32GetDatum(-1)); + retval = InputFunctionCall(&prodesc->result_in_func, val, + prodesc->result_typioparam, -1); } if (array_ret == NULL) @@ -1534,7 +1537,7 @@ plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) getTypeOutputInfo(tupdesc->attrs[i]->atttypid, &typoutput, &typisvarlena); - outputstr = DatumGetCString(OidFunctionCall1(typoutput, attr)); + outputstr = OidOutputFunctionCall(typoutput, attr); sv = newSVpv(outputstr, 0); #if PERL_BCDVERSION >= 0x5006000L @@ -1750,19 +1753,23 @@ plperl_return_next(SV *sv) } else { - Datum ret = (Datum) 0; - bool isNull = true; + Datum ret; + bool isNull; if (SvOK(sv) && SvTYPE(sv) != SVt_NULL) { char *val = SvPV(sv, PL_na); - ret = FunctionCall3(&prodesc->result_in_func, - PointerGetDatum(val), - ObjectIdGetDatum(prodesc->result_typioparam), - Int32GetDatum(-1)); + ret = InputFunctionCall(&prodesc->result_in_func, val, + prodesc->result_typioparam, -1); isNull = false; } + else + { + ret = InputFunctionCall(&prodesc->result_in_func, NULL, + prodesc->result_typioparam, -1); + isNull = true; + } tuple = heap_form_tuple(current_call_data->ret_tdesc, &ret, &isNull); } @@ -2118,9 +2125,9 @@ plperl_spi_exec_prepared(char* query, HV * attr, int argc, SV ** argv) /************************************************************ * Set up arguments ************************************************************/ - if ( argc > 0) + if (argc > 0) { - nulls = (char *)palloc( argc); + nulls = (char *) palloc(argc); argvalues = (Datum *) palloc(argc * sizeof(Datum)); } else @@ -2129,21 +2136,22 @@ plperl_spi_exec_prepared(char* query, HV * attr, int argc, SV ** argv) argvalues = NULL; } - for ( i = 0; i < argc; i++) + for (i = 0; i < argc; i++) { - if ( SvTYPE( argv[i]) != SVt_NULL) + if (SvTYPE(argv[i]) != SVt_NULL) { - argvalues[i] = - FunctionCall3( &qdesc->arginfuncs[i], - CStringGetDatum( SvPV( argv[i], PL_na)), - ObjectIdGetDatum( qdesc->argtypioparams[i]), - Int32GetDatum(-1) - ); + argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i], + SvPV(argv[i], PL_na), + qdesc->argtypioparams[i], + -1); nulls[i] = ' '; } else { - argvalues[i] = (Datum) 0; + argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i], + NULL, + qdesc->argtypioparams[i], + -1); nulls[i] = 'n'; } } @@ -2247,9 +2255,9 @@ plperl_spi_query_prepared(char* query, int argc, SV ** argv) /************************************************************ * Set up arguments ************************************************************/ - if ( argc > 0) + if (argc > 0) { - nulls = (char *)palloc( argc); + nulls = (char *) palloc(argc); argvalues = (Datum *) palloc(argc * sizeof(Datum)); } else @@ -2258,21 +2266,22 @@ plperl_spi_query_prepared(char* query, int argc, SV ** argv) argvalues = NULL; } - for ( i = 0; i < argc; i++) + for (i = 0; i < argc; i++) { - if ( SvTYPE( argv[i]) != SVt_NULL) + if (SvTYPE(argv[i]) != SVt_NULL) { - argvalues[i] = - FunctionCall3( &qdesc->arginfuncs[i], - CStringGetDatum( SvPV( argv[i], PL_na)), - ObjectIdGetDatum( qdesc->argtypioparams[i]), - Int32GetDatum(-1) - ); + argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i], + SvPV(argv[i], PL_na), + qdesc->argtypioparams[i], + -1); nulls[i] = ' '; } else { - argvalues[i] = (Datum) 0; + argvalues[i] = InputFunctionCall(&qdesc->arginfuncs[i], + NULL, + qdesc->argtypioparams[i], + -1); nulls[i] = 'n'; } } diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index e4fa7003a7ca2210a1a53ba8f0b2c94ffd193d81..8a82a42fbe6be756e8ffc0cf06e8ac5a18a226e6 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.162 2006/03/09 21:29:36 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.163 2006/04/04 19:35:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -4043,6 +4043,8 @@ make_tuple_from_row(PLpgSQL_execstate *estate, * * Note: callers generally assume that the result is a palloc'd string and * should be pfree'd. This is not all that safe an assumption ... + * + * Note: not caching the conversion function lookup is bad for performance. * ---------- */ static char * @@ -4053,7 +4055,7 @@ convert_value_to_string(Datum value, Oid valtype) getTypeOutputInfo(valtype, &typoutput, &typIsVarlena); - return DatumGetCString(OidFunctionCall1(typoutput, value)); + return OidOutputFunctionCall(typoutput, value); } /* ---------- @@ -4068,23 +4070,26 @@ exec_cast_value(Datum value, Oid valtype, int32 reqtypmod, bool isnull) { - if (!isnull) + /* + * If the type of the queries return value isn't that of the variable, + * convert it. + */ + if (valtype != reqtype || reqtypmod != -1) { - /* - * If the type of the queries return value isn't that of the variable, - * convert it. - */ - if (valtype != reqtype || reqtypmod != -1) + if (!isnull) { char *extval; extval = convert_value_to_string(value, valtype); - value = FunctionCall3(reqinput, - CStringGetDatum(extval), - ObjectIdGetDatum(reqtypioparam), - Int32GetDatum(reqtypmod)); + value = InputFunctionCall(reqinput, extval, + reqtypioparam, reqtypmod); pfree(extval); } + else + { + value = InputFunctionCall(reqinput, NULL, + reqtypioparam, reqtypmod); + } } return value; diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index d20a5f72f1301954324ff53377294a1da6d67e5e..bf96db1776df51532ef5084e13c78cc48a5b884d 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,7 +1,7 @@ /********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.76 2006/03/14 22:48:24 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.77 2006/04/04 19:35:37 tgl Exp $ * ********************************************************************* */ @@ -482,17 +482,24 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, modattrs[i] = attn; - if (plval != Py_None && !tupdesc->attrs[atti]->attisdropped) + if (tupdesc->attrs[atti]->attisdropped) + { + modvalues[i] = (Datum) 0; + modnulls[i] = 'n'; + } + else if (plval != Py_None) { plstr = PyObject_Str(plval); if (!plstr) - PLy_elog(ERROR, "function \"%s\" could not modify tuple", proc->proname); + PLy_elog(ERROR, "function \"%s\" could not modify tuple", + proc->proname); src = PyString_AsString(plstr); - modvalues[i] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc, - CStringGetDatum(src), - ObjectIdGetDatum(proc->result.out.r.atts[atti].typioparam), - Int32GetDatum(tupdesc->attrs[atti]->atttypmod)); + modvalues[i] = + InputFunctionCall(&proc->result.out.r.atts[atti].typfunc, + src, + proc->result.out.r.atts[atti].typioparam, + tupdesc->attrs[atti]->atttypmod); modnulls[i] = ' '; Py_DECREF(plstr); @@ -500,7 +507,11 @@ PLy_modify_tuple(PLyProcedure * proc, PyObject * pltd, TriggerData *tdata, } else { - modvalues[i] = PointerGetDatum(NULL); + modvalues[i] = + InputFunctionCall(&proc->result.out.r.atts[atti].typfunc, + NULL, + proc->result.out.r.atts[atti].typioparam, + tupdesc->attrs[atti]->atttypmod); modnulls[i] = 'n'; } @@ -751,7 +762,10 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) else if (plrv == Py_None) { fcinfo->isnull = true; - rv = PointerGetDatum(NULL); + rv = InputFunctionCall(&proc->result.out.d.typfunc, + NULL, + proc->result.out.d.typioparam, + -1); } else { @@ -760,10 +774,10 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc) if (!plrv_so) PLy_elog(ERROR, "function \"%s\" could not create return value", proc->proname); plrv_sc = PyString_AsString(plrv_so); - rv = FunctionCall3(&proc->result.out.d.typfunc, - PointerGetDatum(plrv_sc), - ObjectIdGetDatum(proc->result.out.d.typioparam), - Int32GetDatum(-1)); + rv = InputFunctionCall(&proc->result.out.d.typfunc, + plrv_sc, + proc->result.out.d.typioparam, + -1); } } PG_CATCH(); @@ -861,13 +875,9 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc) else { char *ct; - Datum dt; - dt = FunctionCall3(&(proc->args[i].in.d.typfunc), - fcinfo->arg[i], - ObjectIdGetDatum(proc->args[i].in.d.typioparam), - Int32GetDatum(-1)); - ct = DatumGetCString(dt); + ct = OutputFunctionCall(&(proc->args[i].in.d.typfunc), + fcinfo->arg[i]); arg = (proc->args[i].in.d.func) (ct); pfree(ct); } @@ -1454,8 +1464,7 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc) { char *key, *vsrc; - Datum vattr, - vdat; + Datum vattr; bool is_null; PyObject *value; @@ -1469,11 +1478,8 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc) PyDict_SetItemString(dict, key, Py_None); else { - vdat = FunctionCall3(&info->in.r.atts[i].typfunc, - vattr, - ObjectIdGetDatum(info->in.r.atts[i].typioparam), - Int32GetDatum(desc->attrs[i]->atttypmod)); - vsrc = DatumGetCString(vdat); + vsrc = OutputFunctionCall(&info->in.r.atts[i].typfunc, + vattr); /* * no exceptions allowed @@ -2035,10 +2041,10 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) char *sv = PyString_AsString(so); plan->values[i] = - FunctionCall3(&(plan->args[i].out.d.typfunc), - CStringGetDatum(sv), - ObjectIdGetDatum(plan->args[i].out.d.typioparam), - Int32GetDatum(-1)); + InputFunctionCall(&(plan->args[i].out.d.typfunc), + sv, + plan->args[i].out.d.typioparam, + -1); } PG_CATCH(); { @@ -2053,7 +2059,11 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, long limit) else { Py_DECREF(elem); - plan->values[i] = PointerGetDatum(NULL); + plan->values[i] = + InputFunctionCall(&(plan->args[i].out.d.typfunc), + NULL, + plan->args[i].out.d.typioparam, + -1); nulls[i] = 'n'; } } diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index e1f21109bc5ef259c406b51507a71dbae4ed4bfe..07cbcc2eb315ab643d50fa7b8a6e33dfdfd70b0b 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -2,7 +2,7 @@ * pltcl.c - PostgreSQL support for Tcl as * procedural language (PL) * - * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.101 2006/03/14 22:48:24 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.102 2006/04/04 19:35:37 tgl Exp $ * **********************************************************************/ @@ -524,8 +524,8 @@ pltcl_func_handler(PG_FUNCTION_ARGS) { char *tmp; - tmp = DatumGetCString(FunctionCall1(&prodesc->arg_out_func[i], - fcinfo->arg[i])); + tmp = OutputFunctionCall(&prodesc->arg_out_func[i], + fcinfo->arg[i]); UTF_BEGIN; Tcl_DStringAppendElement(&tcl_cmd, UTF_E2U(tmp)); UTF_END; @@ -578,14 +578,17 @@ pltcl_func_handler(PG_FUNCTION_ARGS) elog(ERROR, "SPI_finish() failed"); if (fcinfo->isnull) - retval = (Datum) 0; + retval = InputFunctionCall(&prodesc->result_in_func, + NULL, + prodesc->result_typioparam, + -1); else { UTF_BEGIN; - retval = FunctionCall3(&prodesc->result_in_func, - PointerGetDatum(UTF_U2E(interp->result)), - ObjectIdGetDatum(prodesc->result_typioparam), - Int32GetDatum(-1)); + retval = InputFunctionCall(&prodesc->result_in_func, + UTF_U2E(interp->result), + prodesc->result_typioparam, + -1); UTF_END; } @@ -805,7 +808,6 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) /* Use a TRY to ensure ret_values will get freed */ PG_TRY(); { - if (ret_numvals % 2 != 0) elog(ERROR, "invalid return list from trigger - must have even # of elements"); @@ -871,11 +873,10 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) modnulls[attnum - 1] = ' '; fmgr_info(typinput, &finfo); UTF_BEGIN; - modvalues[attnum - 1] = - FunctionCall3(&finfo, - CStringGetDatum(UTF_U2E(ret_value)), - ObjectIdGetDatum(typioparam), - Int32GetDatum(tupdesc->attrs[attnum - 1]->atttypmod)); + modvalues[attnum - 1] = InputFunctionCall(&finfo, + (char *) UTF_U2E(ret_value), + typioparam, + tupdesc->attrs[attnum - 1]->atttypmod); UTF_END; } @@ -2041,17 +2042,18 @@ pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, { if (nulls && nulls[j] == 'n') { - /* don't try to convert the input for a null */ - argvalues[j] = (Datum) 0; + argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j], + NULL, + qdesc->argtypioparams[j], + -1); } else { UTF_BEGIN; - argvalues[j] = - FunctionCall3(&qdesc->arginfuncs[j], - CStringGetDatum(UTF_U2E(callargs[j])), - ObjectIdGetDatum(qdesc->argtypioparams[j]), - Int32GetDatum(-1)); + argvalues[j] = InputFunctionCall(&qdesc->arginfuncs[j], + (char *) UTF_U2E(callargs[j]), + qdesc->argtypioparams[j], + -1); UTF_END; } } @@ -2185,8 +2187,7 @@ pltcl_set_tuple_values(Tcl_Interp *interp, CONST84 char *arrayname, ************************************************************/ if (!isnull && OidIsValid(typoutput)) { - outputstr = DatumGetCString(OidFunctionCall1(typoutput, - attr)); + outputstr = OidOutputFunctionCall(typoutput, attr); UTF_BEGIN; Tcl_SetVar2(interp, *arrptr, *nameptr, UTF_E2U(outputstr), 0); UTF_END; @@ -2255,8 +2256,7 @@ pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc, ************************************************************/ if (!isnull && OidIsValid(typoutput)) { - outputstr = DatumGetCString(OidFunctionCall1(typoutput, - attr)); + outputstr = OidOutputFunctionCall(typoutput, attr); Tcl_DStringAppendElement(retval, attname); UTF_BEGIN; Tcl_DStringAppendElement(retval, UTF_E2U(outputstr));