packages feed

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;
}