Implement array_send/array_recv (binary I/O for arrays). This exposed

the folly of not passing element type to typsend/typreceive, so fix that.
This commit is contained in:
Tom Lane 2003-05-09 23:01:45 +00:00
parent b1ee615a7f
commit ba1e066e46
7 changed files with 316 additions and 35 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v 1.42 2003/05/08 22:19:56 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v 1.43 2003/05/09 23:01:44 tgl Exp $
PostgreSQL documentation
-->
@ -117,15 +117,20 @@ CREATE TYPE <replaceable class="parameter">typename</replaceable> (
representation is in the machine's native byte order.) The receive
function should perform adequate checking to ensure that the value is
valid.
The receive function should be declared as taking one argument of type
<type>internal</type> and returning a value of the data type itself.
(The argument actually supplied is a pointer to a StringInfo buffer
holding the received byte string.) Similarly, the optional
The receive function may be declared as taking one argument of type
<type>internal</type>, or two arguments of types <type>internal</type>
and <type>oid</type>. It must return a value of the data type itself.
(The first argument is a pointer to a StringInfo buffer
holding the received byte string; the optional second argument is the
element type in case this is an array type.) Similarly, the optional
<replaceable class="parameter">send_function</replaceable> converts
from the internal representation to the external binary representation.
If this function is not supplied, the type cannot participate in binary
output. The send function should be declared as taking one argument of the
new data type and returning type <type>bytea</type>.
output. The send function may be
declared as taking one argument of the new data type, or as taking
two arguments of which the second is type <type>oid</type>.
The second argument is again the array element type for array types.
The send function must return type <type>bytea</type>.
</para>
<para>

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.35 2003/05/08 22:19:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.36 2003/05/09 23:01:45 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
@ -911,7 +911,8 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
Oid procOid;
/*
* Receive functions take a single argument of type INTERNAL.
* Receive functions can take a single argument of type INTERNAL, or
* two arguments (internal, oid).
*/
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
@ -921,6 +922,12 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
procOid = LookupFuncName(procname, 2, argList);
if (OidIsValid(procOid))
return procOid;
func_error("TypeCreate", procname, 1, argList, NULL);
return InvalidOid; /* keep compiler quiet */
@ -933,7 +940,8 @@ findTypeSendFunction(List *procname, Oid typeOid)
Oid procOid;
/*
* Send functions take a single argument of the type.
* Send functions can take a single argument of the type, or two
* arguments (data value, element OID).
*/
MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
@ -943,6 +951,12 @@ findTypeSendFunction(List *procname, Oid typeOid)
if (OidIsValid(procOid))
return procOid;
argList[1] = OIDOID;
procOid = LookupFuncName(procname, 2, argList);
if (OidIsValid(procOid))
return procOid;
func_error("TypeCreate", procname, 1, argList, NULL);
return InvalidOid; /* keep compiler quiet */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.88 2003/05/08 22:19:56 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.89 2003/05/09 23:01:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,6 +19,7 @@
#include "access/tupmacs.h"
#include "catalog/catalog.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
@ -69,6 +70,15 @@
#define RETURN_NULL(type) do { *isNull = true; return (type) 0; } while (0)
/* I/O function selector for system_cache_lookup */
typedef enum IOFuncSelector
{
IOFunc_input,
IOFunc_output,
IOFunc_receive,
IOFunc_send
} IOFuncSelector;
static int ArrayCount(char *str, int *dim, char typdelim);
static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
@ -76,12 +86,17 @@ static Datum *ReadArrayStr(char *arrayStr, int nitems, int ndim, int *dim,
char typdelim,
int typlen, bool typbyval, char typalign,
int *nbytes);
static Datum *ReadArrayBinary(StringInfo buf, int nitems,
FmgrInfo *receiveproc, Oid typelem,
int typlen, bool typbyval, char typalign,
int *nbytes);
static void CopyArrayEls(char *p, Datum *values, int nitems,
int typlen, bool typbyval, char typalign,
bool freedata);
static void system_cache_lookup(Oid element_type, bool input, int *typlen,
bool *typbyval, char *typdelim, Oid *typelem,
Oid *proc, char *typalign);
static void system_cache_lookup(Oid element_type, IOFuncSelector which_func,
int *typlen, bool *typbyval,
char *typdelim, Oid *typelem,
Oid *proc, char *typalign);
static Datum ArrayCast(char *value, bool byval, int len);
static int ArrayCastAndSet(Datum src,
int typlen, bool typbyval, char typalign,
@ -141,7 +156,8 @@ array_in(PG_FUNCTION_ARGS)
char typalign;
/* Get info about element type, including its input conversion proc */
system_cache_lookup(element_type, true, &typlen, &typbyval, &typdelim,
system_cache_lookup(element_type, IOFunc_input,
&typlen, &typbyval, &typdelim,
&typelem, &typinput, &typalign);
fmgr_info(typinput, &inputproc);
@ -622,8 +638,9 @@ array_out(PG_FUNCTION_ARGS)
*dim;
element_type = ARR_ELEMTYPE(v);
system_cache_lookup(element_type, false, &typlen, &typbyval,
&typdelim, &typelem, &typoutput, &typalign);
system_cache_lookup(element_type, IOFunc_output,
&typlen, &typbyval, &typdelim,
&typelem, &typoutput, &typalign);
fmgr_info(typoutput, &outputproc);
ndim = ARR_NDIM(v);
@ -763,10 +780,178 @@ array_out(PG_FUNCTION_ARGS)
Datum
array_recv(PG_FUNCTION_ARGS)
{
elog(ERROR, "array_recv: not implemented yet");
return 0;
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
Oid spec_element_type = PG_GETARG_OID(1); /* type of an array
* element */
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typreceive;
Oid typelem;
FmgrInfo receiveproc;
int i,
nitems;
int32 nbytes;
Datum *dataPtr;
ArrayType *retval;
int ndim,
flags,
dim[MAXDIM],
lBound[MAXDIM];
char typalign;
/* Get the array header information */
ndim = pq_getmsgint(buf, 4);
if (ndim < 0 || ndim > MAXDIM)
elog(ERROR, "array_recv: invalid number of dimensions");
flags = pq_getmsgint(buf, 4);
if (flags != 0)
elog(ERROR, "array_recv: invalid array flags");
element_type = pq_getmsgint(buf, sizeof(Oid));
if (element_type != spec_element_type)
{
/* XXX Can we allow taking the input element type in any cases? */
elog(ERROR, "array_recv: wrong element type");
}
for (i = 0; i < ndim; i++)
{
dim[i] = pq_getmsgint(buf, 4);
lBound[i] = pq_getmsgint(buf, 4);
}
nitems = ArrayGetNItems(ndim, dim);
if (nitems == 0)
{
/* Return empty array */
retval = (ArrayType *) palloc0(sizeof(ArrayType));
retval->size = sizeof(ArrayType);
retval->elemtype = element_type;
PG_RETURN_ARRAYTYPE_P(retval);
}
/* Get info about element type, including its receive conversion proc */
system_cache_lookup(element_type, IOFunc_receive,
&typlen, &typbyval, &typdelim,
&typelem, &typreceive, &typalign);
if (!OidIsValid(typreceive))
elog(ERROR, "No binary input function available for type %s",
format_type_be(element_type));
fmgr_info(typreceive, &receiveproc);
dataPtr = ReadArrayBinary(buf, nitems, &receiveproc, typelem,
typlen, typbyval, typalign,
&nbytes);
nbytes += ARR_OVERHEAD(ndim);
retval = (ArrayType *) palloc0(nbytes);
retval->size = nbytes;
retval->ndim = ndim;
retval->elemtype = element_type;
memcpy((char *) ARR_DIMS(retval), (char *) dim,
ndim * sizeof(int));
memcpy((char *) ARR_LBOUND(retval), (char *) lBound,
ndim * sizeof(int));
CopyArrayEls(ARR_DATA_PTR(retval), dataPtr, nitems,
typlen, typbyval, typalign, true);
pfree(dataPtr);
PG_RETURN_ARRAYTYPE_P(retval);
}
/*---------------------------------------------------------------------------
* ReadArrayBinary:
* collect the data elements of an array being read in binary style.
* result :
* returns a palloc'd array of Datum representations of the array elements.
* If element type is pass-by-ref, the Datums point to palloc'd values.
* *nbytes is set to the amount of data space needed for the array,
* including alignment padding but not including array header overhead.
*---------------------------------------------------------------------------
*/
static Datum *
ReadArrayBinary(StringInfo buf,
int nitems,
FmgrInfo *receiveproc,
Oid typelem,
int typlen,
bool typbyval,
char typalign,
int *nbytes)
{
Datum *values;
int i;
values = (Datum *) palloc(nitems * sizeof(Datum));
for (i = 0; i < nitems; i++)
{
int itemlen;
StringInfoData elem_buf;
char csave;
/* Get and check the item length */
itemlen = pq_getmsgint(buf, 4);
if (itemlen < 0 || itemlen > (buf->len - buf->cursor))
elog(ERROR, "insufficient data left in message");
/*
* Rather than copying data around, we just set up a phony
* StringInfo pointing to the correct portion of the input
* buffer. We assume we can scribble on the input buffer
* so as to maintain the convention that StringInfos have
* a trailing null.
*/
elem_buf.data = &buf->data[buf->cursor];
elem_buf.maxlen = itemlen + 1;
elem_buf.len = itemlen;
elem_buf.cursor = 0;
buf->cursor += itemlen;
csave = buf->data[buf->cursor];
buf->data[buf->cursor] = '\0';
/* Now call the element's receiveproc */
values[i] = FunctionCall2(receiveproc,
PointerGetDatum(&elem_buf),
ObjectIdGetDatum(typelem));
/* Trouble if it didn't eat the whole buffer */
if (elem_buf.cursor != itemlen)
elog(ERROR, "Improper binary format in array element %d",
i + 1);
buf->data[buf->cursor] = csave;
}
/*
* Compute total data space needed
*/
if (typlen > 0)
{
*nbytes = nitems * att_align(typlen, typalign);
}
else
{
Assert(!typbyval);
*nbytes = 0;
for (i = 0; i < nitems; i++)
{
/* let's just make sure data is not toasted */
if (typlen == -1)
values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
*nbytes = att_addlength(*nbytes, typlen, values[i]);
*nbytes = att_align(*nbytes, typalign);
}
}
return values;
}
/*-------------------------------------------------------------------------
* array_send :
* takes the internal representation of an array and returns a bytea
@ -776,8 +961,70 @@ array_recv(PG_FUNCTION_ARGS)
Datum
array_send(PG_FUNCTION_ARGS)
{
elog(ERROR, "array_send: not implemented yet");
return 0;
ArrayType *v = PG_GETARG_ARRAYTYPE_P(0);
Oid element_type;
int typlen;
bool typbyval;
char typdelim;
Oid typsend,
typelem;
FmgrInfo sendproc;
char typalign;
char *p;
int nitems,
i;
int ndim,
*dim;
StringInfoData buf;
/* Get information about the element type and the array dimensions */
element_type = ARR_ELEMTYPE(v);
system_cache_lookup(element_type, IOFunc_send, &typlen, &typbyval,
&typdelim, &typelem, &typsend, &typalign);
if (!OidIsValid(typsend))
elog(ERROR, "No binary output function available for type %s",
format_type_be(element_type));
fmgr_info(typsend, &sendproc);
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
nitems = ArrayGetNItems(ndim, dim);
pq_begintypsend(&buf);
/* Send the array header information */
pq_sendint(&buf, ndim, 4);
pq_sendint(&buf, v->flags, 4);
pq_sendint(&buf, element_type, sizeof(Oid));
for (i = 0; i < ndim; i++)
{
pq_sendint(&buf, ARR_DIMS(v)[i], 4);
pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
}
/* Send the array elements using the element's own sendproc */
p = ARR_DATA_PTR(v);
for (i = 0; i < nitems; i++)
{
Datum itemvalue;
bytea *outputbytes;
itemvalue = fetch_att(p, typbyval, typlen);
outputbytes = DatumGetByteaP(FunctionCall2(&sendproc,
itemvalue,
ObjectIdGetDatum(typelem)));
/* We assume the result will not have been toasted */
pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
pq_sendbytes(&buf, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
pfree(outputbytes);
p = att_addlength(p, typlen, PointerGetDatum(p));
p = (char *) att_align(p, typalign);
}
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*-------------------------------------------------------------------------
@ -1583,9 +1830,9 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
PG_RETURN_ARRAYTYPE_P(v);
/* Lookup source and result types. Unneeded variables are reused. */
system_cache_lookup(inpType, false, &inp_typlen, &inp_typbyval,
system_cache_lookup(inpType, IOFunc_input, &inp_typlen, &inp_typbyval,
&typdelim, &typelem, &proc, &inp_typalign);
system_cache_lookup(retType, false, &typlen, &typbyval,
system_cache_lookup(retType, IOFunc_input, &typlen, &typbyval,
&typdelim, &typelem, &proc, &typalign);
/* Allocate temporary array for new values */
@ -1832,7 +2079,7 @@ array_eq(PG_FUNCTION_ARGS)
static void
system_cache_lookup(Oid element_type,
bool input,
IOFuncSelector which_func,
int *typlen,
bool *typbyval,
char *typdelim,
@ -1855,10 +2102,21 @@ system_cache_lookup(Oid element_type,
*typdelim = typeStruct->typdelim;
*typelem = typeStruct->typelem;
*typalign = typeStruct->typalign;
if (input)
*proc = typeStruct->typinput;
else
*proc = typeStruct->typoutput;
switch (which_func)
{
case IOFunc_input:
*proc = typeStruct->typinput;
break;
case IOFunc_output:
*proc = typeStruct->typoutput;
break;
case IOFunc_receive:
*proc = typeStruct->typreceive;
break;
case IOFunc_send:
*proc = typeStruct->typsend;
break;
}
ReleaseSysCache(typeTuple);
}

View File

@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: catversion.h,v 1.189 2003/05/09 21:19:49 tgl Exp $
* $Id: catversion.h,v 1.190 2003/05/09 23:01:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200305092
#define CATALOG_VERSION_NO 200305093
#endif

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_proc.h,v 1.296 2003/05/09 21:19:49 tgl Exp $
* $Id: pg_proc.h,v 1.297 2003/05/09 23:01:45 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
@ -3153,9 +3153,9 @@ DATA(insert OID = 2311 ( md5 PGNSP PGUID 12 f f t f i 1 25 "25" md5_text -
DESCR("calculates md5 hash");
DATA(insert OID = 2400 ( array_recv PGNSP PGUID 12 f f t f s 1 2277 "2281" array_recv - _null_ ));
DATA(insert OID = 2400 ( array_recv PGNSP PGUID 12 f f t f s 2 2277 "2281 26" array_recv - _null_ ));
DESCR("I/O");
DATA(insert OID = 2401 ( array_send PGNSP PGUID 12 f f t f s 1 17 "2277" array_send - _null_ ));
DATA(insert OID = 2401 ( array_send PGNSP PGUID 12 f f t f s 2 17 "2277 26" array_send - _null_ ));
DESCR("I/O");
DATA(insert OID = 2402 ( record_recv PGNSP PGUID 12 f f t f i 1 2249 "2281" record_recv - _null_ ));
DESCR("I/O");

View File

@ -143,7 +143,9 @@ WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype);
((p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype) OR
(p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND
p2.proargtypes[1] = 'oid'::regtype));
oid | typname | oid | proname
-----+---------+-----+---------
(0 rows)

View File

@ -114,7 +114,9 @@ WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype);
((p2.pronargs = 1 AND p2.proargtypes[0] = 'internal'::regtype) OR
(p2.pronargs = 2 AND p2.proargtypes[0] = 'internal'::regtype AND
p2.proargtypes[1] = 'oid'::regtype));
-- As of 7.4, this check finds refcursor, which is borrowing
-- other types' I/O routines