hpqtypes-1.2.2: libpqtypes/src/param.c
/*
* param.c
* Management functions for the PGparam object.
*
* 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"
static int argsExpandBuffer(PGtypeArgs *args, int new_len);
PGparam *
PQparamCreate(const PGconn *conn, PGerror *err)
{
PGparam *param;
PGtypeData *connData;
if (!conn)
{
PQseterror(err, "PGconn cannot be NULL");
return NULL;
}
param = (PGparam *) malloc(sizeof(PGparam));
if (!param)
{
PQseterror(err, PQT_OUTOFMEMORY);
return NULL;
}
memset(param, 0, sizeof(PGparam));
connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc);
if (!connData)
{
PQparamClear(param);
PQseterror(err, "No type data exists for PGconn at %p", conn);
return NULL;
}
/* copy any handlers from conn object */
if (connData->typhcnt > 0)
{
param->typhandlers = pqt_duphandlers(
connData->typhandlers, connData->typhcnt);
if (!param->typhandlers)
{
PQparamClear(param);
PQseterror(err, PQT_OUTOFMEMORY);
return NULL;
}
param->typhcnt = connData->typhcnt;
}
/* copy any typespecs from conn object */
if (connData->typspeccnt > 0)
{
param->typspecs = pqt_dupspecs(
connData->typspecs, connData->typspeccnt);
if (!param->typspecs)
{
PQparamClear(param);
PQseterror(err, PQT_OUTOFMEMORY);
return NULL;
}
param->typspeccnt = connData->typspeccnt;
}
pqt_getfmtinfo(conn, ¶m->fmtinfo);
return param;
}
PQT_EXPORT PGparam *
PQparamDup(PGparam *param, PGerror *err)
{
PGparam *new_param;
if (!param)
{
PQseterror(err, "PGparam to duplicate cannot be NULL");
return NULL;
}
new_param = (PGparam *) malloc(sizeof(PGparam));
if (!new_param)
{
PQseterror(err, PQT_OUTOFMEMORY);
return NULL;
}
memset(new_param, 0, sizeof(PGparam));
/* copy any handlers from conn object */
if (param->typhcnt > 0)
{
new_param->typhandlers = pqt_duphandlers(
param->typhandlers, param->typhcnt);
if (!new_param->typhandlers)
{
PQparamClear(new_param);
PQseterror(err, PQT_OUTOFMEMORY);
return NULL;
}
new_param->typhcnt = param->typhcnt;
}
/* copy any typespecs from conn object */
if (param->typspeccnt > 0)
{
new_param->typspecs = pqt_dupspecs(
param->typspecs, param->typspeccnt);
if (!new_param->typspecs)
{
PQparamClear(new_param);
PQseterror(err, PQT_OUTOFMEMORY);
return NULL;
}
new_param->typspeccnt = param->typspeccnt;
}
memcpy(&new_param->fmtinfo, ¶m->fmtinfo, sizeof(PGtypeFormatInfo));
/* copy any values, don't bother if array is empty. */
if (param->vcnt > 0)
{
int i;
for (i=0; i < param->vcnt; i++)
{
int flags = 0;
PGvalue *val = ¶m->vals[i];
/* Is val->data is direct user pointer? If so, set pointer flag
* so pqt_putparam doesn't deep copy it.
*/
if (val->ptr != val->data)
flags |= TYPFLAG_POINTER;
if (!pqt_putparam(new_param, err, val->data, val->datal, flags,
val->format, val->oid))
{
PQparamClear(new_param);
return NULL;
}
}
}
return new_param;
}
int
PQparamCount(PGparam *param)
{
if (param)
return param->vcnt;
return 0;
}
void
PQparamReset(PGparam *param)
{
if (param)
param->vcnt = 0;
}
void
PQparamClear(PGparam *param)
{
int i;
if (!param)
return;
/* vmax is the size of the vals array. We don't use vcnt because
* someone could of just called PQparamReset, leaving vcnt 0. If
* we find any value pointers that are not NULL, free'm.
*/
for (i=0; i < param->vmax; i++)
if (param->vals[i].ptr)
free(param->vals[i].ptr);
if (param->vals)
free(param->vals);
pqt_freehandlers(param->typhandlers, param->typhcnt);
pqt_freespecs(param->typspecs, param->typspeccnt);
param->vmax = 0;
param->vcnt = 0;
param->vals = NULL;
param->typhcnt = 0;
param->typhandlers = NULL;
param->typspeccnt = 0;
param->typspecs = NULL;
free(param);
}
int
PQputf(PGparam *param, PGerror *err, const char *format, ...)
{
int r;
va_list ap;
/* This function is just a wrapper to PQputvf */
va_start(ap, format);
r = PQputvf(param, err, NULL, 0, format, ap);
va_end(ap);
return r;
}
int
PQputvf(PGparam *param, PGerror *err, char *stmtBuf, size_t stmtBufLen,
const char *format, va_list ap)
{
int n=0;
int flags;
size_t stmtPos = 0;
int save_vcnt;
int typpos = 0;
PGtypeHandler *h;
PGtypeArgs args;
char args_outbuf[4096];
PGtypeSpec *spec = NULL;
if (!param)
{
PQseterror(err, "PGparam cannot be NULL");
return FALSE;
}
if (!format || !*format)
{
PQseterror(err, "param 'format' cannot be NULL or an empty string");
return FALSE;
}
if (stmtBuf && stmtBufLen < 1)
{
PQseterror(err, "Invalid argument: stmtBufLen must be >= 1");
return FALSE;
}
save_vcnt = param->vcnt;
va_copy(args.ap, ap);
/* "@name" format, lookup typeSpec in cache */
if(format && *format == '@')
{
spec = pqt_getspec(param->typspecs, param->typspeccnt, format + 1);
/* If we didn't find a type spec, this is an error. A format string
* with a '@' as its first character is reserved.
*/
if (!spec)
{
PQseterror(err, "No such prepared specifier name: '%s'", format + 1);
return FALSE;
}
}
while (format && *format)
{
if (spec)
{
/* done, no more handlers in cached spec string. */
if (typpos == spec->idcnt)
break;
h = pqt_gethandlerbyid(param->typhandlers, param->typhcnt,
spec->idlist[typpos]);
/* should be an unusual, or a "will never happen", situation */
if (!h)
{
va_end(args.ap);
PQseterror(err, "Unknown type handler id at position %d", typpos+1);
param->vcnt = save_vcnt;
return FALSE;
}
flags = (int) spec->flags[typpos];
typpos++;
}
else
{
format = pqt_parse(err, format, param->typhandlers, param->typhcnt,
stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags);
if (!format)
{
param->vcnt = save_vcnt;
return FALSE;
}
if (!h)
continue;
}
args.is_put = 1;
args.put.param = param;
args.fmtinfo = ¶m->fmtinfo;
args.put.out = args_outbuf;
args.put.__allocated_out = NULL;
args.put.outl = (int) sizeof(args_outbuf);
args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0;
args.err = err;
args.format = BINARYFMT;
args.put.expandBuffer = argsExpandBuffer;
args.typpos = typpos;
args.typhandler = h;
args.errorf = pqt_argserrorf;
args.super = pqt_argssuper;
*args.put.out = 0;
if (flags & TYPFLAG_ARRAY)
n = pqt_put_array(&args);
else
n = h->typput(&args);
if (n == -1)
{
if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
free(args.put.__allocated_out);
param->vcnt = save_vcnt;
return FALSE;
}
if (args.put.out == NULL)
{
args.format = BINARYFMT;
n = -1;
}
n = pqt_putparam(param, err, args.put.out, n, flags, args.format,
(flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid);
if (args.put.__allocated_out && args.put.__allocated_out != args_outbuf)
free(args.put.__allocated_out);
if (!n)
{
param->vcnt = save_vcnt;
return FALSE;
}
}
if (stmtBuf)
stmtBuf[stmtPos] = 0;
return TRUE;
}
/* ----------------------------
* Helper functions
*/
int
pqt_putparam(PGparam *param, PGerror *err, const void *data, int datal,
int flags, int format, Oid typoid)
{
PGvalue *v;
if (!param)
return FALSE;
if (!data)
datal = -1;
/* need to grow param vals array */
if (param->vcnt == param->vmax)
{
PGvalue *vals;
int vmax = param->vmax ? (param->vmax * 3) / 2 : 16;
vals = (PGvalue *) pqt_realloc(param->vals, sizeof(PGvalue) * vmax);
if (!vals)
{
PQseterror(err, PQT_OUTOFMEMORY);
return FALSE;
}
/* zero out the new array elements */
memset(vals + param->vcnt, 0, (vmax - param->vcnt) * sizeof(PGvalue));
param->vmax = vmax;
param->vals = vals;
}
/* grab next param value */
v = ¶m->vals[param->vcnt];
if (datal == -1)
{
v->data = NULL;
}
/* wants to put a direct pointer */
else if (flags & TYPFLAG_POINTER)
{
v->data = (char *) data;
}
else
{
/* allocate more mem for param value ptr or if v->ptr is NULL,
* since then the data will not be copied (memcpy with first
* argument being NULL silently does nothing). it usually happens
* when datal is 0 (eg. empty string was passed).
*/
if (v->ptrl < datal || v->ptr == NULL)
{
/* prevent malloc(0) call which is implementation
* defined and may either return NULL or unique
* pointer (the latter is expected here).
*/
int alloc_size = datal == 0 ? 1 : datal;
char *ptr = (char *) pqt_realloc(v->ptr, alloc_size);
if (!ptr)
{
PQseterror(err, PQT_OUTOFMEMORY);
return FALSE;
}
v->ptrl = datal;
v->ptr = ptr;
}
memcpy(v->ptr, data, datal);
v->data = v->ptr;
}
v->datal = datal;
v->format = format;
v->oid = typoid;
param->vcnt++;
return TRUE;
}
/* returns 0 on success and -1 on error */
static int
argsExpandBuffer(PGtypeArgs *args, int new_len)
{
char *new_out;
if (new_len <= args->put.outl)
return 0;
if (!args->put.__allocated_out)
{
new_out = (char *) malloc(new_len);
if (!new_out)
return args->errorf(args, PQT_OUTOFMEMORY);
/* fully copy existing stack buffer, we don't know what data
* the user has already written and may want to keep.
*/
memcpy(new_out, args->put.out, args->put.outl);
}
else
{
new_out = (char *) realloc(args->put.__allocated_out, new_len);
if (!new_out)
return args->errorf(args, PQT_OUTOFMEMORY);
}
args->put.out = args->put.__allocated_out = new_out;
args->put.outl = new_len;
return 0;
}