From ba1e066e4637d64886b6ba12706b18ca35a6e258 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 9 May 2003 23:01:45 +0000 Subject: 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. --- src/backend/utils/adt/arrayfuncs.c | 294 ++++++++++++++++++++++++++++++++++--- 1 file changed, 276 insertions(+), 18 deletions(-) (limited to 'src/backend/utils/adt/arrayfuncs.c') diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index f713fda7d57..b53c896e431 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -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); } -- cgit v1.2.3