hpqtypes-1.2.2: libpqtypes/src/array.c
/*
* array.c
* Type handler for the array data type.
*
* Copyright (c) 2011 eSilo, LLC. All rights reserved.
* This is free software; see the source for copying conditions. There is
* NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE.
*/
#include "libpqtypes-int.h"
int
pqt_put_array(PGtypeArgs *args)
{
int i;
int hasnull=0;
int ndims;
int nitems;
int arrsize;
char *out;
int lbound[MAXDIM];
int dims[MAXDIM];
PGarray *arr = va_arg(args->ap, PGarray *);
PUTNULLCHK(args, arr);
if (arr->ndims < 0)
return args->errorf(args, "arr.ndims is invalid - %d", arr->ndims);
/* auto configure when ndims is 0 to 1d array */
if (arr->ndims == 0)
{
ndims = 1;
dims[0] = arr->param->vcnt;
lbound[0] = 1;
}
else
{
ndims = arr->ndims;
memcpy(lbound, arr->lbound, sizeof(lbound));
memcpy(dims, arr->dims, sizeof(dims));
}
nitems = 1;
for (i=0; i < ndims; i++)
nitems *= dims[i];
/* make sure array is on the same page as the param */
if (nitems != arr->param->vcnt)
return args->errorf(args,
"param element count %d is different than array's %d",
arr->param->vcnt, nitems);
/* header: ndims + hasnull + elemtype + ((dims + lbound) * ndims) */
arrsize = 4 + 4 + 4 + (8 * ndims);
/* compute data length, also get the hasnull flag */
for (i=0; i < arr->param->vcnt; i++)
{
if (arr->param->vals[i].format == 0)
return args->errorf(args, "Cannot put array elements in text format");
arrsize += 4;
if (arr->param->vals[i].datal == NULL_LEN)
hasnull = 1;
else
arrsize += arr->param->vals[i].datal;
}
/* make sure args->put.out is large enough */
if (args->put.expandBuffer(args, arrsize) == -1)
return -1;
out = args->put.out;
/* number od dimensions */
pqt_buf_putint4(out, ndims);
out += 4;
/* array hasnull flag */
pqt_buf_putint4(out, hasnull);
out += 4;
/* array element oid */
pqt_buf_putint4(out, args->typhandler->typoid);
out += 4;
/* dims and lbound */
for (i=0; i < ndims; i++)
{
pqt_buf_putint4(out, dims[i]);
out += 4;
pqt_buf_putint4(out, lbound[i]);
out += 4;
}
/* write the element lengths and data */
for (i=0; i < arr->param->vcnt; i++)
{
pqt_buf_putint4(out, arr->param->vals[i].datal);
out += 4;
if (arr->param->vals[i].datal > 0)
{
memcpy(out, arr->param->vals[i].data, arr->param->vals[i].datal);
out += arr->param->vals[i].datal;
}
}
return arrsize;
}
int
pqt_get_array(PGtypeArgs *args)
{
int i,t;
int vlen;
int ntups;
int nattrs;
Oid elemoid;
DECLVALUE(args);
PGresult *res;
int first_tup;
PGarray *arr = va_arg(args->ap, PGarray *);
CHKGETVALS(args, arr);
if (args->format == TEXTFMT)
return args->errorf(args, "array does not support text results");
/* number of dims */
arr->ndims = pqt_buf_getint4(value);
value += 4;
/* skip NULL flag */
value += 4;
/* check the element oid */
elemoid = (Oid)pqt_buf_getint4(value);
if (elemoid != args->typhandler->typoid)
return args->errorf(args,
"array element type %u is different than what server says %u",
args->typhandler->typoid, elemoid);
value += 4;
/* arr dims and lbounds */
first_tup = 1;
for (i=0, ntups=1; i < arr->ndims; i++)
{
arr->dims[i] = pqt_buf_getint4(value);
value += 4;
arr->lbound[i] = pqt_buf_getint4(value);
value += 4;
ntups *= arr->dims[i];
}
/* This means ndims is zero because the above loop never iterated. */
if (i == 0)
ntups = 0;
/* numTuples is the number of array items
* and numAttributes is 1 for non-composites.
*/
nattrs = (args->typhandler->nattrs > 0) ? args->typhandler->nattrs : 1;
if (!(res = pqt_copyresult(args, nattrs)))
RERR_MEM(args);
for (t=0; t < ntups; t++)
{
/* get the value len */
vlen = pqt_buf_getint4(value);
value += 4;
/* Regular Array with 1 attr per tuple */
if (args->typhandler->nattrs == 0)
{
/* set the field value */
if (!PQsetvalue(res, t, 0, value, vlen))
{
PQclear(res);
return -1;
}
if (vlen > 0)
value += vlen;
continue;
}
/* ------------------------
* COMPOSITE/RECORD
*/
/* NULL compsoite array item, fill in attrs with NULL */
if (vlen == NULL_LEN)
{
int x;
for (x=0; x < nattrs; x++)
{
if (!PQsetvalue(res, t, x, NULL, NULL_LEN))
{
PQclear(res);
return -1;
}
}
/* move on to next tuple, done here. */
continue;
}
/* verify that server's attr count matches ours */
if (first_tup)
{
int attcnt = pqt_buf_getint4(value);
/* watch for invalidation issues */
if (attcnt != nattrs)
{
PQclear(res);
return args->errorf(args,
"type handler attribute count is %d but server says it's %d",
args->typhandler->nattrs, attcnt);
}
}
/* skip attr count */
value += 4;
/* composite attributes (record columns) */
for (i=0; i < nattrs; i++)
{
/* watch for invalidation issues */
if (first_tup &&
(Oid) pqt_buf_getint4(value) != args->typhandler->attDescs[i].attoid)
{
Oid server_oid = (Oid) pqt_buf_getint4(value);
args->errorf(args,
"type handler attribute OID is %u but server says it's %u",
args->typhandler->attDescs[i].attoid, server_oid);
PQclear(res);
return -1;
}
/* skip oid */
value += 4;
/* get the value length */
vlen = pqt_buf_getint4(value);
value += 4;
/* set the field value */
if (!PQsetvalue(res, t, i, value, vlen))
{
PQclear(res);
return -1;
}
if (vlen > 0)
value += vlen;
}
first_tup = 0;
}
arr->res = res;
return 0;
}