Initial commit:
Version 1.5.2a of libpqtypes which contains a few critical bug fixes on top of official version 1.5.1
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
|
||||
/*
|
||||
* array.c
|
||||
* Type handler for the array data type.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,330 @@
|
||||
|
||||
/*
|
||||
* error.c
|
||||
* The functions in this file represent the libpqtypes error
|
||||
* system. It offers the API user the ability to set/get errors.
|
||||
* The error system uses a per-thread global error, implemented
|
||||
* using TLS (via declspec(thread) or pthread TLS). For
|
||||
* non-PQT_THREAD_SAFE builds, a static buffer is used.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
/* Cannot use allocated memory for the pqterr_t members. This is
|
||||
* because windows has no way of freeing per thread allocated
|
||||
* memory automatically, other than DllMain which won't work for
|
||||
* a third-party library such as libpqtypes. All error messages
|
||||
* and fields are truncated to fit their buffers via pqt_strcpy().
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
/* Room for user message and a full libpq message */
|
||||
char msg[1024 + 8192];
|
||||
|
||||
/* Error Fields (a little less than 8K for all) */
|
||||
char severity[16];
|
||||
char sqlstate[16];
|
||||
char message_primary[2048];
|
||||
char message_detail[1024];
|
||||
char message_hint[512];
|
||||
char stmt_position[16];
|
||||
char internal_position[16];
|
||||
char internal_query[2048];
|
||||
char context[2048];
|
||||
char source_file[256];
|
||||
char source_line[16];
|
||||
char source_function[80];
|
||||
} pqterr_t;
|
||||
|
||||
/* non thread-safe, use a static */
|
||||
#ifndef PQT_THREAD_SAFE
|
||||
static pqterr_t lasterr = {{0}};
|
||||
|
||||
/* MSVC always uses __declspec(thread) */
|
||||
#elif defined(PQT_MSVC)
|
||||
static __declspec(thread) pqterr_t lasterr = {0};
|
||||
|
||||
/* Unix-variant, MinGW or Cygwin. */
|
||||
#else
|
||||
#include <pthread.h>
|
||||
static pthread_key_t tlskey_lasterr;
|
||||
|
||||
/* When the thread dies, this will get called. */
|
||||
static void tls_free_lasterr(void *value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
free(value);
|
||||
pthread_setspecific(tlskey_lasterr, NULL);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* called before main. This attribute is available since gcc 2.7.0.
|
||||
* It replaced the obsolete _init ... destructor replaced _fini.
|
||||
* We used to use pthread_once, but solaris doesn't support this
|
||||
* in older versions (its simply a stub returning 0, ouch!).
|
||||
*/
|
||||
void __attribute__((constructor)) __InItErRoRkEy__(void)
|
||||
{
|
||||
pthread_key_create(&tlskey_lasterr, tls_free_lasterr);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
vseterror(const char *format, va_list ap, int append);
|
||||
|
||||
static pqterr_t *
|
||||
geterr(void)
|
||||
{
|
||||
pqterr_t *err = NULL;
|
||||
|
||||
/* Non thread-safe mode or windows msvc. */
|
||||
#if !defined(PQT_THREAD_SAFE) || defined(PQT_MSVC)
|
||||
err = &lasterr;
|
||||
|
||||
/* systems requiring pthread TLS keys */
|
||||
#else
|
||||
|
||||
/* System is using FSU Threads, like SCO OpenServer 5, which
|
||||
* has minor prototype differences.
|
||||
*/
|
||||
# ifdef PTHREAD_FSU
|
||||
pthread_getspecific(tlskey_lasterr, (void **) &err);
|
||||
# else
|
||||
err = (pqterr_t *) pthread_getspecific(tlskey_lasterr);
|
||||
# endif
|
||||
|
||||
if (!err)
|
||||
{
|
||||
err = (pqterr_t *) malloc(sizeof(pqterr_t));
|
||||
if (!err)
|
||||
return NULL;
|
||||
memset(err, 0, sizeof(pqterr_t));
|
||||
pthread_setspecific(tlskey_lasterr, err);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
char *
|
||||
PQgeterror(void)
|
||||
{
|
||||
static char _empty[1] = {0};
|
||||
pqterr_t *err = geterr();
|
||||
return err ? err->msg : _empty;
|
||||
}
|
||||
|
||||
void
|
||||
PQseterror(const char *format, ...)
|
||||
{
|
||||
/* clear error message by passing in NULL, PQseterror(NULL).*/
|
||||
if (!format)
|
||||
{
|
||||
pqterr_t *err = geterr();
|
||||
|
||||
if (err && *err->msg)
|
||||
{
|
||||
/* do not memset the entire pqterr_t structure, its around 20K and
|
||||
* causes performance problems when putting lots of array elements,
|
||||
* since PQputvf always clears the error PQseterror(NULL) before
|
||||
* attempting to put data.
|
||||
*/
|
||||
*err->msg = 0;
|
||||
*err->severity = 0;
|
||||
*err->sqlstate = 0;
|
||||
*err->message_primary = 0;
|
||||
*err->message_detail = 0;
|
||||
*err->message_hint = 0;
|
||||
*err->stmt_position = 0;
|
||||
*err->internal_position = 0;
|
||||
*err->internal_query = 0;
|
||||
*err->context = 0;
|
||||
*err->source_file = 0;
|
||||
*err->source_line = 0;
|
||||
*err->source_function = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
vseterror(format, ap, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
PQgetErrorField(int fieldcode)
|
||||
{
|
||||
pqterr_t *err = geterr();
|
||||
|
||||
if (!err)
|
||||
return NULL;
|
||||
|
||||
switch (fieldcode)
|
||||
{
|
||||
case PG_DIAG_SEVERITY:
|
||||
return err->severity;
|
||||
|
||||
case PG_DIAG_SQLSTATE:
|
||||
return err->sqlstate;
|
||||
|
||||
case PG_DIAG_MESSAGE_PRIMARY:
|
||||
return err->message_primary;
|
||||
|
||||
case PG_DIAG_MESSAGE_DETAIL:
|
||||
return err->message_detail;
|
||||
|
||||
case PG_DIAG_MESSAGE_HINT:
|
||||
return err->message_hint;
|
||||
|
||||
case PG_DIAG_STATEMENT_POSITION:
|
||||
return err->stmt_position;
|
||||
|
||||
case PG_DIAG_INTERNAL_POSITION:
|
||||
return err->internal_position;
|
||||
|
||||
case PG_DIAG_INTERNAL_QUERY:
|
||||
return err->internal_query;
|
||||
|
||||
case PG_DIAG_CONTEXT:
|
||||
return err->context;
|
||||
|
||||
case PG_DIAG_SOURCE_FILE:
|
||||
return err->source_file;
|
||||
|
||||
case PG_DIAG_SOURCE_LINE:
|
||||
return err->source_line;
|
||||
|
||||
case PG_DIAG_SOURCE_FUNCTION:
|
||||
return err->source_function;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Used by pqt_setresultfields */
|
||||
#define geterrfield(buf, name) do{ \
|
||||
if ((value = PQresultErrorField(res, name))) \
|
||||
pqt_strcpy(buf, sizeof(buf), value); \
|
||||
else \
|
||||
*buf = 0; \
|
||||
}while (0)
|
||||
|
||||
void
|
||||
pqt_setresultfields(const PGresult *res)
|
||||
{
|
||||
char *value;
|
||||
pqterr_t *err = geterr();
|
||||
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
geterrfield(err->severity, PG_DIAG_SEVERITY);
|
||||
geterrfield(err->sqlstate, PG_DIAG_SQLSTATE);
|
||||
geterrfield(err->message_primary, PG_DIAG_MESSAGE_PRIMARY);
|
||||
geterrfield(err->message_detail, PG_DIAG_MESSAGE_DETAIL);
|
||||
geterrfield(err->message_hint, PG_DIAG_MESSAGE_HINT);
|
||||
geterrfield(err->stmt_position, PG_DIAG_STATEMENT_POSITION);
|
||||
geterrfield(err->internal_position, PG_DIAG_INTERNAL_POSITION);
|
||||
geterrfield(err->internal_query, PG_DIAG_INTERNAL_QUERY);
|
||||
geterrfield(err->context, PG_DIAG_CONTEXT);
|
||||
geterrfield(err->source_file, PG_DIAG_SOURCE_FILE);
|
||||
geterrfield(err->source_line, PG_DIAG_SOURCE_LINE);
|
||||
geterrfield(err->source_function, PG_DIAG_SOURCE_FUNCTION);
|
||||
}
|
||||
|
||||
/* errorf() callback for PGtypeArgs, see PQputf() and PQgetf().
|
||||
* Always returns -1.
|
||||
*/
|
||||
int
|
||||
pqt_argserrorf(PGtypeArgs *args, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char fqtn[200];
|
||||
|
||||
if (!args || !format || !*format)
|
||||
return -1;
|
||||
|
||||
pqt_fqtn(fqtn, sizeof(fqtn), args->typhandler->typschema,
|
||||
args->typhandler->typname);
|
||||
|
||||
/* put the header */
|
||||
PQseterror("%s[pos:%d] - ", fqtn, args->typpos);
|
||||
|
||||
/* append message from type handler */
|
||||
va_start(ap, format);
|
||||
vseterror(format, ap, TRUE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
vseterror(const char *format, va_list ap, int append)
|
||||
{
|
||||
int n;
|
||||
int curlen = 0;
|
||||
int size;
|
||||
va_list ap2;
|
||||
char *msg = NULL;
|
||||
pqterr_t *err = geterr();
|
||||
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
if (append)
|
||||
curlen = (int) strlen(err->msg);
|
||||
else
|
||||
*err->msg = 0;
|
||||
|
||||
va_copy(ap2, ap);
|
||||
n = pqt_vsnprintf(err->msg + curlen, sizeof(err->msg) - curlen, format, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
if (n > -1)
|
||||
return;
|
||||
|
||||
/* pqterr_t msg buffer is too small for the complete message. We have
|
||||
* use a temporary buffer to get a successful sprintf so we can
|
||||
* pqt_strcpy() the result; which truncates to fit.
|
||||
*/
|
||||
size = (int) sizeof(err->msg) * 2;
|
||||
if (!(msg = (char *) malloc(size)))
|
||||
return;
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *p;
|
||||
|
||||
va_copy(ap2, ap);
|
||||
n = pqt_vsnprintf(msg + curlen, size - curlen, format, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
/* success */
|
||||
if (n > -1)
|
||||
break;
|
||||
|
||||
/* need more space */
|
||||
n = size * 2;
|
||||
if (!(p = pqt_realloc(msg, n)))
|
||||
{
|
||||
/* we're here because sprintf failed, don't trust buffer contents */
|
||||
*msg = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
msg = p;
|
||||
size = n;
|
||||
}
|
||||
|
||||
pqt_strcpy(err->msg, sizeof(err->msg), msg);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
|
||||
/*
|
||||
* events.c
|
||||
* The libpq PGEventProc implementation.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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 PGtypeData *
|
||||
allocTypeData(PGconn *conn);
|
||||
|
||||
static void
|
||||
freeTypeData(PGtypeData *typeData);
|
||||
|
||||
/* Deprecated, use PQinitTypes instead */
|
||||
int
|
||||
PQtypesRegister(PGconn *conn)
|
||||
{
|
||||
return PQinitTypes(conn);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_eventproc(PGEventId id, void *info, void *passThrough)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case PGEVT_REGISTER:
|
||||
{
|
||||
PGEventRegister *e = (PGEventRegister *) info;
|
||||
void *data = allocTypeData(e->conn);
|
||||
if (!data)
|
||||
return FALSE;
|
||||
PQsetInstanceData((PGconn *) e->conn, pqt_eventproc, data);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGEVT_CONNRESET:
|
||||
{
|
||||
/* No special handling for PGEVT_CONNRESET. Previously, types were
|
||||
* automatically re-registered but this idea fails miserably during
|
||||
* asynchronous resets. Yanked in favor of using the following
|
||||
* call sequence: PQresetXXX, PQclearTypes, PQregisterTypes.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
case PGEVT_CONNDESTROY:
|
||||
{
|
||||
PGEventConnDestroy *e = (PGEventConnDestroy *) info;
|
||||
freeTypeData((PGtypeData *) PQinstanceData(e->conn, pqt_eventproc));
|
||||
break;
|
||||
}
|
||||
|
||||
case PGEVT_RESULTCREATE:
|
||||
{
|
||||
PGtypeData *resData;
|
||||
PGEventResultCreate *e = (PGEventResultCreate *) info;
|
||||
PGtypeData *connData = (PGtypeData *) PQinstanceData(
|
||||
e->conn, pqt_eventproc);
|
||||
|
||||
if (!connData || !(resData = allocTypeData(e->conn)))
|
||||
return FALSE;
|
||||
|
||||
/* copy type handlers from PGconn's typeData */
|
||||
if (connData->typhcnt > 0)
|
||||
{
|
||||
resData->typhandlers = pqt_duphandlers(
|
||||
connData->typhandlers, connData->typhcnt);
|
||||
|
||||
if (resData->typhandlers)
|
||||
resData->typhcnt = connData->typhcnt;
|
||||
}
|
||||
|
||||
/* copy type specs from PGconn's typeData */
|
||||
if (connData->typspeccnt > 0)
|
||||
{
|
||||
resData->typspecs = pqt_dupspecs(
|
||||
connData->typspecs, connData->typspeccnt);
|
||||
|
||||
if (resData->typspecs)
|
||||
resData->typspeccnt = connData->typspeccnt;
|
||||
}
|
||||
|
||||
PQresultSetInstanceData((PGresult *) e->result, pqt_eventproc, resData);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGEVT_RESULTCOPY:
|
||||
{
|
||||
PGtypeData *destData;
|
||||
PGEventResultCopy *e = (PGEventResultCopy *) info;
|
||||
PGtypeData *srcData = (PGtypeData *) PQresultInstanceData(
|
||||
e->src, pqt_eventproc);
|
||||
|
||||
if (!srcData || !(destData = allocTypeData(NULL)))
|
||||
return FALSE;
|
||||
|
||||
memcpy(&destData->fmtinfo, &srcData->fmtinfo, sizeof(PGtypeFormatInfo));
|
||||
|
||||
/* copy type handlers from PGresult's typeData */
|
||||
if (srcData->typhcnt > 0)
|
||||
{
|
||||
destData->typhandlers = pqt_duphandlers(
|
||||
srcData->typhandlers, srcData->typhcnt);
|
||||
|
||||
if (destData->typhandlers)
|
||||
destData->typhcnt = srcData->typhcnt;
|
||||
}
|
||||
|
||||
/* copy type specs from PGresult's typeData */
|
||||
if (srcData->typspeccnt > 0)
|
||||
{
|
||||
destData->typspecs = pqt_dupspecs(
|
||||
srcData->typspecs, srcData->typspeccnt);
|
||||
|
||||
if (destData->typspecs)
|
||||
destData->typspeccnt = srcData->typspeccnt;
|
||||
}
|
||||
|
||||
PQresultSetInstanceData(e->dest, pqt_eventproc, destData);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGEVT_RESULTDESTROY:
|
||||
{
|
||||
PGEventResultDestroy *e = (PGEventResultDestroy *) info;
|
||||
freeTypeData((PGtypeData *) PQresultInstanceData(
|
||||
e->result, pqt_eventproc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static PGtypeData *
|
||||
allocTypeData(PGconn *conn)
|
||||
{
|
||||
PGtypeData *typeData = (PGtypeData *) malloc(sizeof(PGtypeData));
|
||||
|
||||
if (typeData)
|
||||
{
|
||||
memset(typeData, 0, sizeof(PGtypeData));
|
||||
|
||||
/* get type formatting info from conn */
|
||||
if (conn)
|
||||
pqt_getfmtinfo(conn, &typeData->fmtinfo);
|
||||
}
|
||||
|
||||
return typeData;
|
||||
}
|
||||
|
||||
static void
|
||||
freeTypeData(PGtypeData *typeData)
|
||||
{
|
||||
if (typeData)
|
||||
{
|
||||
pqt_cleartypes(typeData);
|
||||
|
||||
pqt_freespecs(typeData->typspecs, typeData->typspeccnt);
|
||||
typeData->typspecs = NULL;
|
||||
typeData->typspeccnt = 0;
|
||||
typeData->typspecmax = 0;
|
||||
|
||||
free(typeData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,562 @@
|
||||
|
||||
/*
|
||||
* exec.c
|
||||
* Query execution and data retrieval functions.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
/*
|
||||
* Each param value requires an oid, ptr and 2 ints (oid, value, length,
|
||||
* format). This makes the maximum size of a param 20 bytes. A stack size
|
||||
* of 4k would allow for 204 query params. If more params are needed,
|
||||
* heap memory is used. Needing more than 204 param values in a query
|
||||
* is very rare, although possible.
|
||||
*/
|
||||
#define PARAM_STACKSIZE 4096
|
||||
|
||||
#define BUILD_ARRAYS(rettype) \
|
||||
rettype r; \
|
||||
char *buf = NULL; \
|
||||
Oid *oids = NULL; \
|
||||
char **vals = NULL; \
|
||||
int *lens = NULL; \
|
||||
int *fmts = NULL; \
|
||||
int vcnt = 0; \
|
||||
char stackbuffer[PARAM_STACKSIZE]; \
|
||||
\
|
||||
PQseterror(NULL); \
|
||||
if (!conn) \
|
||||
{ \
|
||||
PQseterror("PGconn cannot be NULL"); \
|
||||
return (rettype)(0); \
|
||||
} \
|
||||
\
|
||||
if (param) \
|
||||
{ \
|
||||
buf = stackbuffer; \
|
||||
if (!buildArrays(param, &buf, &oids, &vals, &lens, &fmts)) \
|
||||
return (rettype) (0); \
|
||||
vcnt = param->vcnt; \
|
||||
}
|
||||
|
||||
#define RETURN_RESULT \
|
||||
if (param) \
|
||||
{ \
|
||||
if (buf && buf != stackbuffer) \
|
||||
free(buf); \
|
||||
} \
|
||||
return r
|
||||
|
||||
static int
|
||||
buildArrays(PGparam *param, char **buf, Oid **oids,
|
||||
char ***vals, int **lens, int **fmts);
|
||||
|
||||
static PGresult *
|
||||
copyExecError(PGconn *conn, PGresult *r);
|
||||
|
||||
static const char *
|
||||
getCommand(PGconn *conn, PGparam *param, const char *command);
|
||||
|
||||
static int
|
||||
_execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp);
|
||||
|
||||
int
|
||||
PQgetf(const PGresult *res, int tup_num, const char *format, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
n = PQgetvf(res, tup_num, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap)
|
||||
{
|
||||
int r;
|
||||
PGtypeHandler *h;
|
||||
PGtypeArgs args;
|
||||
int typpos = 0;
|
||||
int flags;
|
||||
Oid ftype;
|
||||
PGtypeData *resData;
|
||||
PGtypeSpec *spec = NULL;
|
||||
char tmp[200];
|
||||
|
||||
PQseterror(NULL);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
PQseterror("PGresult cannot be NULL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!(resData = (PGtypeData *) PQresultInstanceData(res, pqt_eventproc)))
|
||||
{
|
||||
PQseterror("PGresult at %p has no event data", res);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
va_copy(args.ap, ap);
|
||||
|
||||
/* "@name" format, lookup typeSpec in cache */
|
||||
if(format && *format == '@')
|
||||
{
|
||||
spec = pqt_getspec(resData->typspecs, resData->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("No such prepared specifier name: '%s'", format + 1);
|
||||
va_end(args.ap);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
while (format && *format)
|
||||
{
|
||||
if (spec)
|
||||
{
|
||||
/* done, no more handlers in cached spec string. */
|
||||
if (typpos == spec->idcnt)
|
||||
break;
|
||||
|
||||
h = pqt_gethandlerbyid(resData->typhandlers, resData->typhcnt,
|
||||
spec->idlist[typpos]);
|
||||
|
||||
/* should be an unusual, or a "will never happen", situation */
|
||||
if (!h)
|
||||
{
|
||||
va_end(args.ap);
|
||||
PQseterror("Unknown type handler id at position %d", typpos+1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
flags = (int) spec->flags[typpos];
|
||||
typpos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = pqt_parse(format, resData->typhandlers, resData->typhcnt,
|
||||
NULL, 0, &h, NULL, &typpos, &flags);
|
||||
|
||||
if (!format)
|
||||
{
|
||||
va_end(args.ap);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!h)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flags & TYPFLAG_BYNAME)
|
||||
args.get.field_num = PQfnumber(res, va_arg(args.ap, const char *));
|
||||
else
|
||||
args.get.field_num = va_arg(args.ap, int);
|
||||
|
||||
/* simplify life for handlers by checking getvalue's return here. */
|
||||
if (args.get.field_num < 0 ||
|
||||
!PQgetvalue(res, tup_num, args.get.field_num))
|
||||
{
|
||||
PQseterror(
|
||||
"Invalid tup_num[%d].field_num[%d] (position %d)",
|
||||
tup_num, args.get.field_num, typpos);
|
||||
va_end(args.ap);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ftype = PQftype(res, args.get.field_num);
|
||||
if (((flags & TYPFLAG_ARRAY) && ftype != h->typoid_array) ||
|
||||
(!(flags & TYPFLAG_ARRAY) && ftype != h->typoid))
|
||||
{
|
||||
Oid oid = (flags & TYPFLAG_ARRAY) ? h->typoid_array : h->typoid;
|
||||
PQseterror(
|
||||
"Trying to get type %u '%s' but server returned %u (position %d)",
|
||||
oid, pqt_fqtn(tmp, sizeof(tmp), h->typschema, h->typname),
|
||||
ftype, typpos);
|
||||
va_end(args.ap);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
args.is_put = 0;
|
||||
args.get.result = (PGresult *) res;
|
||||
args.format = PQfformat(res, args.get.field_num);
|
||||
args.fmtinfo = &resData->fmtinfo;
|
||||
args.get.tup_num = tup_num;
|
||||
args.is_ptr = (flags & TYPFLAG_POINTER) ? 1 : 0;
|
||||
args.typpos = typpos;
|
||||
args.typhandler = h;
|
||||
args.errorf = pqt_argserrorf;
|
||||
args.super = pqt_argssuper;
|
||||
|
||||
if (flags & TYPFLAG_ARRAY)
|
||||
r = pqt_get_array(&args);
|
||||
else
|
||||
r = h->typget(&args);
|
||||
|
||||
if (r == -1)
|
||||
{
|
||||
va_end(args.ap);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args.ap);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* Exec and Send functions
|
||||
*/
|
||||
|
||||
PGresult *
|
||||
PQexecf(PGconn *conn, const char *cmdspec, ...)
|
||||
{
|
||||
va_list ap;
|
||||
PGresult *res;
|
||||
|
||||
va_start(ap, cmdspec);
|
||||
res = PQexecvf(conn, cmdspec, ap);
|
||||
va_end(ap);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
PGresult *
|
||||
PQexecvf(PGconn *conn, const char *cmdspec, va_list ap)
|
||||
{
|
||||
PGresult *res;
|
||||
(void) _execvf(conn, cmdspec, ap, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
PQsendf(PGconn *conn, const char *cmdspec, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, cmdspec);
|
||||
n = PQsendvf(conn, cmdspec, ap);
|
||||
va_end(ap);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
PQsendvf(PGconn *conn, const char *cmdspec, va_list ap)
|
||||
{
|
||||
return _execvf(conn, cmdspec, ap, NULL);
|
||||
}
|
||||
|
||||
PGresult *
|
||||
PQparamExec(PGconn *conn, PGparam *param, const char *command,
|
||||
int resultFormat)
|
||||
{
|
||||
BUILD_ARRAYS(PGresult *);
|
||||
|
||||
command = getCommand(conn, param, command);
|
||||
if (!command)
|
||||
{
|
||||
r = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = PQexecParams(conn, command, vcnt, oids,
|
||||
(const char *const * ) vals, lens, fmts, resultFormat);
|
||||
|
||||
pqt_setresultfields(r);
|
||||
r = copyExecError(conn, r);
|
||||
}
|
||||
|
||||
RETURN_RESULT;
|
||||
}
|
||||
|
||||
int
|
||||
PQparamSendQuery(PGconn *conn, PGparam *param, const char *command,
|
||||
int resultFormat)
|
||||
{
|
||||
BUILD_ARRAYS(int);
|
||||
|
||||
command = getCommand(conn, param, command);
|
||||
if (!command)
|
||||
{
|
||||
r = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = PQsendQueryParams(conn, command, vcnt, oids,
|
||||
(const char *const * ) vals, lens, fmts, resultFormat);
|
||||
|
||||
if (!r)
|
||||
PQseterror("PGconn: %s", PQerrorMessage(conn));
|
||||
}
|
||||
|
||||
RETURN_RESULT;
|
||||
}
|
||||
|
||||
PGresult *
|
||||
PQparamExecPrepared(PGconn *conn, PGparam *param, const char *stmtName,
|
||||
int resultFormat)
|
||||
{
|
||||
BUILD_ARRAYS(PGresult *);
|
||||
|
||||
r = PQexecPrepared(conn, stmtName, vcnt, (const char *const * ) vals,
|
||||
lens, fmts, resultFormat);
|
||||
|
||||
pqt_setresultfields(r);
|
||||
r = copyExecError(conn, r);
|
||||
|
||||
RETURN_RESULT;
|
||||
}
|
||||
|
||||
int
|
||||
PQparamSendQueryPrepared(PGconn *conn, PGparam *param, const char *stmtName,
|
||||
int resultFormat)
|
||||
{
|
||||
BUILD_ARRAYS(int);
|
||||
|
||||
r = PQsendQueryPrepared(conn, stmtName, vcnt,
|
||||
(const char *const * ) vals, lens, fmts, resultFormat);
|
||||
|
||||
if (!r)
|
||||
PQseterror("PGconn: %s", PQerrorMessage(conn));
|
||||
|
||||
RETURN_RESULT;
|
||||
}
|
||||
|
||||
/* Called by PQexecvf and PQsendvf. When resp is NULL, PQparamSendQuery
|
||||
* is used to execute the command. Otherwise, PQparamExec is used. The
|
||||
* return value is always zero when resp is not NULL. When resp is NULL,
|
||||
* the return value is zero for error and non-zero for success (identical
|
||||
* to PQparamSendQuery).
|
||||
*/
|
||||
static int
|
||||
_execvf(PGconn *conn, const char *cmdspec, va_list ap, PGresult **resp)
|
||||
{
|
||||
int retval = 0;
|
||||
size_t stmt_len=0;
|
||||
char buffer[8192]; /* could be larger these days but be conservative */
|
||||
char *stmt = NULL;
|
||||
PGparam *param = NULL;
|
||||
|
||||
if (resp)
|
||||
*resp = NULL;
|
||||
|
||||
if(!conn)
|
||||
{
|
||||
PQseterror("PGconn cannot be NULL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!cmdspec || !*cmdspec)
|
||||
{
|
||||
PQseterror("cmdspec cannot be NULL or an empty string");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* No stmt buf required for preapred type specs */
|
||||
if (*cmdspec != '@')
|
||||
{
|
||||
/* The resulting parameterized command is guarenteed to be smaller
|
||||
* than the cmdspec. When needed, enlarge stmt buf to cmdspec length.
|
||||
*/
|
||||
stmt_len = strlen(cmdspec) + 1;
|
||||
|
||||
/* stack buffer is too small, use heap */
|
||||
if (stmt_len > sizeof(buffer))
|
||||
{
|
||||
if (!(stmt = (char *) malloc(stmt_len)))
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt = buffer;
|
||||
stmt_len = sizeof(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if ((param = PQparamCreate(conn)))
|
||||
{
|
||||
if (PQputvf(param, stmt, stmt_len, cmdspec, ap))
|
||||
{
|
||||
const char *s = stmt ? stmt : cmdspec;
|
||||
|
||||
if (resp)
|
||||
*resp = PQparamExec(conn, param, s, 1);
|
||||
else
|
||||
retval = PQparamSendQuery(conn, param, s, 1);
|
||||
}
|
||||
|
||||
PQparamClear(param);
|
||||
}
|
||||
|
||||
if (stmt && stmt != buffer)
|
||||
free(stmt);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assigns param values to param arrays, for use with postgres
|
||||
* parameter API. 'buf' is expected to be PARAM_STACKSIZE bytes. If more
|
||||
* memory is required, memory is allocated and assigned to *buf, which
|
||||
* must be freed by caller. To determine if *buf was allocated, compare
|
||||
* its address to the initially provided stack address.
|
||||
* Returns 1 on success and 0 on error.
|
||||
*/
|
||||
static int
|
||||
buildArrays(PGparam *param, char **buf, Oid **oids,
|
||||
char ***vals, int **lens, int **fmts)
|
||||
{
|
||||
int n;
|
||||
|
||||
/* no params to assign */
|
||||
if (param->vcnt == 0)
|
||||
return 1;
|
||||
|
||||
/* required memory size for the 4 param arrays */
|
||||
n = (int) ((sizeof(void *) * param->vcnt) + /* values */
|
||||
((sizeof(int) * 2) * param->vcnt) + /* lengths and formats */
|
||||
(sizeof(Oid) * param->vcnt)); /* oids */
|
||||
|
||||
/* required memory is too large for stack buffer, get some heap */
|
||||
if (n > PARAM_STACKSIZE)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (!(p = (char *) malloc(n)))
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*buf = p;
|
||||
}
|
||||
|
||||
/* give arrays memory from buffer, which could be stack or heap. */
|
||||
*vals = (char **) *buf;
|
||||
*lens = (int *) (*buf + (sizeof(void *) * param->vcnt));
|
||||
*fmts = (*lens) + param->vcnt;
|
||||
*oids = (Oid *) ((*fmts) + param->vcnt);
|
||||
|
||||
/* loop param values and assign value, length, format
|
||||
* and oid to arrays.
|
||||
*/
|
||||
for (n=0; n < param->vcnt; n++)
|
||||
{
|
||||
(*oids)[n] = param->vals[n].oid;
|
||||
(*vals)[n] = param->vals[n].data;
|
||||
(*lens)[n] = param->vals[n].datal;
|
||||
(*fmts)[n] = param->vals[n].format;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static PGresult *
|
||||
copyExecError(PGconn *conn, PGresult *r)
|
||||
{
|
||||
if (!r)
|
||||
{
|
||||
PQseterror("PGconn: %s", PQerrorMessage(conn));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (PQresultStatus(r))
|
||||
{
|
||||
case PGRES_COMMAND_OK:
|
||||
case PGRES_TUPLES_OK:
|
||||
case PGRES_EMPTY_QUERY:
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
PQseterror("PGresult: %s", PQresultErrorMessage(r));
|
||||
PQclear(r);
|
||||
r = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Using the param is preferred when both conn and param are provided.
|
||||
* The conn is there in case the exec has no parameters, NULL param.
|
||||
*/
|
||||
static const char *
|
||||
getCommand(PGconn *conn, PGparam *param, const char *command)
|
||||
{
|
||||
PGtypeSpec *spec;
|
||||
int typspeccnt = 0;
|
||||
PGtypeSpec *typspecs = NULL;
|
||||
|
||||
if (!command)
|
||||
{
|
||||
PQseterror("command to execute cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*command != '@')
|
||||
return command;
|
||||
|
||||
if (param)
|
||||
{
|
||||
typspecs = param->typspecs;
|
||||
typspeccnt = param->typspeccnt;
|
||||
}
|
||||
|
||||
/* Try to get instance data from the conn */
|
||||
if (!typspecs || typspeccnt == 0)
|
||||
{
|
||||
PGtypeData *data = PQinstanceData(conn, pqt_eventproc);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
PQseterror("PGconn at %p has no event data", conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typspecs = data->typspecs;
|
||||
typspeccnt = data->typspeccnt;
|
||||
}
|
||||
|
||||
spec = pqt_getspec(typspecs, typspeccnt, command + 1);
|
||||
|
||||
/* If we didn't find a type spec, this is an error. A format string
|
||||
* with an '@' as its first character is reserved.
|
||||
*/
|
||||
if (!spec)
|
||||
{
|
||||
PQseterror("No such prepared specifier name: '%s'", command + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make sure type spec was prepared with a statement */
|
||||
if (!spec->stmt || !*spec->stmt)
|
||||
{
|
||||
PQseterror("Prepared specifier name '%s' has no statement", command + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const char *) spec->stmt;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
|
||||
/*
|
||||
* geo.c
|
||||
* Type handler for the geometric data types.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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 text2point(PGpoint *pt, char *text, char **endptr);
|
||||
static int text2points(PGtypeArgs *args, PGpoint **pts, int *npts);
|
||||
static int bin2points(PGtypeArgs *args, char *valp, int ptcnt,
|
||||
PGpoint **pts, int *npts);
|
||||
static int putpoints(PGtypeArgs *args, int npts, PGpoint *pts,
|
||||
int is_path, int closed);
|
||||
|
||||
int
|
||||
pqt_put_point(PGtypeArgs *args)
|
||||
{
|
||||
unsigned int *buf;
|
||||
PGpoint *pt = va_arg(args->ap, PGpoint *);
|
||||
|
||||
PUTNULLCHK(args, pt);
|
||||
|
||||
buf = (unsigned int *) args->put.out;
|
||||
pqt_swap8(buf, &pt->x, 1);
|
||||
pqt_swap8(buf + 2, &pt->y, 1);
|
||||
return 16;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_point(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGpoint *pt = va_arg(args->ap, PGpoint *);
|
||||
|
||||
CHKGETVALS(args, pt);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
if (!text2point(pt, value, NULL))
|
||||
RERR_STR2INT(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pqt_swap8(&pt->x, (unsigned int *) value, 0);
|
||||
pqt_swap8(&pt->y, ((unsigned int *) (value)) + 2, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_lseg(PGtypeArgs *args)
|
||||
{
|
||||
unsigned int *buf;
|
||||
PGlseg *lseg = va_arg(args->ap, PGlseg *);
|
||||
|
||||
PUTNULLCHK(args, lseg);
|
||||
|
||||
buf = (unsigned int *) args->put.out;
|
||||
pqt_swap8(buf, &lseg->pts[0].x, 1);
|
||||
pqt_swap8(buf + 2, &lseg->pts[0].y, 1);
|
||||
pqt_swap8(buf + 4, &lseg->pts[1].x, 1);
|
||||
pqt_swap8(buf + 6, &lseg->pts[1].y, 1);
|
||||
return 32;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_lseg(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
unsigned int *v;
|
||||
PGlseg *lseg = va_arg(args->ap, PGlseg *);
|
||||
|
||||
CHKGETVALS(args, lseg);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
PGpoint *pts = (PGpoint *)lseg;
|
||||
|
||||
if (*value++ != '[' ||
|
||||
!text2point(pts, value, &value) ||
|
||||
*value++ != ',' ||
|
||||
!text2point(pts + 1, value, &value) ||
|
||||
*value != ']')
|
||||
RERR_STR2INT(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
v = (unsigned int *) value;
|
||||
pqt_swap8(&lseg->pts[0].x, v, 0);
|
||||
pqt_swap8(&lseg->pts[0].y, v + 2, 0);
|
||||
pqt_swap8(&lseg->pts[1].x, v + 4, 0);
|
||||
pqt_swap8(&lseg->pts[1].y, v + 6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_box(PGtypeArgs *args)
|
||||
{
|
||||
unsigned int *buf;
|
||||
PGbox *box = va_arg(args->ap, PGbox *);
|
||||
|
||||
PUTNULLCHK(args, box);
|
||||
|
||||
buf = (unsigned int *) args->put.out;
|
||||
pqt_swap8(buf, &box->high.x, 1);
|
||||
pqt_swap8(buf + 2, &box->high.y, 1);
|
||||
pqt_swap8(buf + 4, &box->low.x, 1);
|
||||
pqt_swap8(buf + 6, &box->low.y, 1);
|
||||
return 32;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_box(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
unsigned int *v;
|
||||
PGbox *box = va_arg(args->ap, PGbox *);
|
||||
|
||||
CHKGETVALS(args, box);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
PGpoint *pts = (PGpoint *)box;
|
||||
|
||||
if (!text2point(pts, value, &value) ||
|
||||
*value++ != ',' ||
|
||||
!text2point(pts + 1, value, NULL))
|
||||
RERR_STR2INT(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
v = (unsigned int *) value;
|
||||
pqt_swap8(&box->high.x, v, 0);
|
||||
pqt_swap8(&box->high.y, v + 2, 0);
|
||||
pqt_swap8(&box->low.x, v + 4, 0);
|
||||
pqt_swap8(&box->low.y, v + 6, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_circle(PGtypeArgs *args)
|
||||
{
|
||||
unsigned int *buf;
|
||||
PGcircle *circle = va_arg(args->ap, PGcircle *);
|
||||
|
||||
PUTNULLCHK(args, circle);
|
||||
|
||||
buf = (unsigned int *) args->put.out;
|
||||
pqt_swap8(buf, &circle->center.x, 1);
|
||||
pqt_swap8(buf + 2, &circle->center.y, 1);
|
||||
pqt_swap8(buf + 4, &circle->radius, 1);
|
||||
return 24;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_circle(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
unsigned int *v;
|
||||
PGcircle *circle = va_arg(args->ap, PGcircle *);
|
||||
|
||||
CHKGETVALS(args, circle);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
if (*value++ != '<' ||
|
||||
!text2point((PGpoint *)circle, value, &value) ||
|
||||
*value++ != ',' ||
|
||||
!pqt_text_to_float8(&circle->radius, value, &value) ||
|
||||
*value != '>')
|
||||
RERR_STR2INT(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
v = (unsigned int *) value;
|
||||
pqt_swap8(&circle->center.x, v, 0);
|
||||
pqt_swap8(&circle->center.y, v + 2, 0);
|
||||
pqt_swap8(&circle->radius, v + 4, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_path(PGtypeArgs *args)
|
||||
{
|
||||
PGpath *path = va_arg(args->ap, PGpath *);
|
||||
PUTNULLCHK(args, path);
|
||||
return putpoints(args, path->npts, path->pts, 1, path->closed ? 1 : 0);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_path(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGpath *path = va_arg(args->ap, PGpath *);
|
||||
|
||||
CHKGETVALS(args, path);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
path->closed = *value == '(' ? 1 : 0;
|
||||
return text2points(args, &path->pts, &path->npts);
|
||||
}
|
||||
|
||||
path->closed = *value ? 1 : 0;
|
||||
value++;
|
||||
|
||||
return bin2points(args,
|
||||
value + sizeof(int), /* beginning of point array */
|
||||
pqt_buf_getint4(value), /* number of points */
|
||||
&path->pts, &path->npts);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_polygon(PGtypeArgs *args)
|
||||
{
|
||||
PGpolygon *polygon = va_arg(args->ap, PGpolygon *);
|
||||
PUTNULLCHK(args, polygon);
|
||||
return putpoints(args, polygon->npts, polygon->pts, 0, 0);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_polygon(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGpolygon *polygon = va_arg(args->ap, PGpolygon *);
|
||||
|
||||
CHKGETVALS(args, polygon);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
return text2points(args, &polygon->pts, &polygon->npts);
|
||||
|
||||
return bin2points(args,
|
||||
value + sizeof(int), /* beginning of point array */
|
||||
pqt_buf_getint4(value), /* number of points */
|
||||
&polygon->pts,
|
||||
&polygon->npts);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
putpoints(PGtypeArgs *args, int npts, PGpoint *pts,
|
||||
int is_path, int closed)
|
||||
{
|
||||
int i;
|
||||
int datal;
|
||||
int hdr = (int) sizeof(int);
|
||||
char *out;
|
||||
|
||||
/* pts is for a path, include 1 byte open/closed flag */
|
||||
if (is_path)
|
||||
hdr++;
|
||||
|
||||
/* length of binary formated path */
|
||||
datal = (npts * sizeof(PGpoint)) + hdr;
|
||||
|
||||
/* make sure out is large enough */
|
||||
if (args->put.expandBuffer(args, datal) == -1)
|
||||
return -1;
|
||||
|
||||
out = args->put.out;
|
||||
if (is_path)
|
||||
*out++ = closed ? 1 : 0; /* path open/closed flag */
|
||||
|
||||
/* write the number of points as an int32 */
|
||||
pqt_buf_putint4(out, npts);
|
||||
out += 4;
|
||||
|
||||
/* assign points to the data 'out' buffer */
|
||||
for (i=0; i < npts; i++)
|
||||
{
|
||||
pqt_swap8(out, &pts[i].x, 1);
|
||||
out += sizeof(double);
|
||||
|
||||
pqt_swap8(out, &pts[i].y, 1);
|
||||
out += sizeof(double);
|
||||
}
|
||||
|
||||
return datal;
|
||||
}
|
||||
|
||||
static int
|
||||
text2points(PGtypeArgs *args, PGpoint **pts, int *npts)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
char *s;
|
||||
int cnt = 0;
|
||||
PGpoint *p = NULL;
|
||||
|
||||
*pts = NULL;
|
||||
*npts = 0;
|
||||
|
||||
if (*value != '(' && *value != '[')
|
||||
RERR(args, "malformed point array");
|
||||
|
||||
/* get the number of points by counting the '(' */
|
||||
for (s=value+1; *s; s++)
|
||||
{
|
||||
if (*s == '(')
|
||||
{
|
||||
if (!(s = strchr(s, ')'))) /* skip point contents */
|
||||
break;
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cnt == 0)
|
||||
return 0; /* empty point list */
|
||||
|
||||
p = (PGpoint *) PQresultAlloc((PGresult *) args->get.result,
|
||||
cnt * sizeof(PGpoint));
|
||||
if (!p)
|
||||
RERR_MEM(args);
|
||||
|
||||
for (cnt=0; *++value; )
|
||||
{
|
||||
if (!text2point(&p[cnt++], value, &value))
|
||||
RERR_STR2INT(args);
|
||||
|
||||
/* done */
|
||||
if (*value != ',')
|
||||
break;
|
||||
}
|
||||
|
||||
*pts = p;
|
||||
*npts = cnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bin2points(PGtypeArgs *args, char *valp, int ptcnt,
|
||||
PGpoint **pts, int *npts)
|
||||
{
|
||||
int i;
|
||||
PGpoint *p;
|
||||
|
||||
*pts = NULL;
|
||||
*npts = 0;
|
||||
|
||||
if (ptcnt == 0)
|
||||
return 0;
|
||||
|
||||
p = (PGpoint *) PQresultAlloc((PGresult *) args->get.result,
|
||||
ptcnt * sizeof(PGpoint));
|
||||
|
||||
if (!p)
|
||||
RERR_MEM(args);
|
||||
|
||||
for (i=0; i < ptcnt; i++)
|
||||
{
|
||||
pqt_swap8(&p[i].x, valp, 0);
|
||||
valp += sizeof(double);
|
||||
|
||||
pqt_swap8(&p[i].y, valp, 0);
|
||||
valp += sizeof(double);
|
||||
}
|
||||
|
||||
*pts = p;
|
||||
*npts = ptcnt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
text2point(PGpoint *pt, char *text, char **endptr)
|
||||
{
|
||||
if (*text++ != '(')
|
||||
return 0;
|
||||
|
||||
if (!pqt_text_to_float8(&pt->x, text, &text) || *text++ != ',')
|
||||
return 0;
|
||||
|
||||
if (!pqt_text_to_float8(&pt->y, text, &text) || *text++ != ')')
|
||||
return 0;
|
||||
|
||||
if (endptr)
|
||||
*endptr = text;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2003-2008, PostgreSQL Global Development Group
|
||||
* $PostgreSQL: pgsql/src/include/getaddrinfo.h
|
||||
*/
|
||||
|
||||
#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
|
||||
!defined(HAVE_GETADDRINFO))
|
||||
|
||||
#ifndef GETADDRINFO_H
|
||||
#define GETADDRINFO_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#define _XOPEN_SOURCE_EXTENDED 1
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
/* Various macros that ought to be in <netdb.h>, but might not be */
|
||||
|
||||
#ifndef EAI_FAIL
|
||||
#define EAI_BADFLAGS (-1)
|
||||
#define EAI_NONAME (-2)
|
||||
#define EAI_AGAIN (-3)
|
||||
#define EAI_FAIL (-4)
|
||||
#define EAI_FAMILY (-6)
|
||||
#define EAI_SOCKTYPE (-7)
|
||||
#define EAI_SERVICE (-8)
|
||||
#define EAI_MEMORY (-10)
|
||||
#define EAI_SYSTEM (-11)
|
||||
#endif /* !EAI_FAIL */
|
||||
|
||||
#ifndef AI_PASSIVE
|
||||
#define AI_PASSIVE 0x0001
|
||||
#endif
|
||||
|
||||
#ifndef AI_NUMERICHOST
|
||||
/*
|
||||
* some platforms don't support AI_NUMERICHOST; define as zero if using
|
||||
* the system version of getaddrinfo...
|
||||
*/
|
||||
#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO)
|
||||
#define AI_NUMERICHOST 0
|
||||
#else
|
||||
#define AI_NUMERICHOST 0x0004
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NI_NUMERICHOST
|
||||
#define NI_NUMERICHOST 1
|
||||
#endif
|
||||
#ifndef NI_NUMERICSERV
|
||||
#define NI_NUMERICSERV 2
|
||||
#endif
|
||||
|
||||
#ifndef NI_MAXHOST
|
||||
#define NI_MAXHOST 1025
|
||||
#endif
|
||||
#ifndef NI_MAXSERV
|
||||
#define NI_MAXSERV 32
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE
|
||||
|
||||
#ifndef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_FAMILY
|
||||
#define ss_family __ss_family
|
||||
#else
|
||||
#error struct sockaddr_storage does not provide an ss_family member
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE___SS_LEN
|
||||
#define ss_len __ss_len
|
||||
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1
|
||||
#endif
|
||||
#else /* !HAVE_STRUCT_SOCKADDR_STORAGE */
|
||||
|
||||
/* Define a struct sockaddr_storage if we don't have one. */
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
struct sockaddr_storage
|
||||
{
|
||||
union
|
||||
{
|
||||
struct sockaddr sa; /* get the system-dependent fields */
|
||||
long long int ss_align; /* ensures struct is properly aligned */
|
||||
char ss_pad[128]; /* ensures struct has desired size */
|
||||
} ss_stuff;
|
||||
};
|
||||
#endif
|
||||
|
||||
#define ss_family ss_stuff.sa.sa_family
|
||||
/* It should have an ss_len field if sockaddr has sa_len. */
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||
#define ss_len ss_stuff.sa.sa_len
|
||||
#define HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN 1
|
||||
#endif
|
||||
#endif /* HAVE_STRUCT_SOCKADDR_STORAGE */
|
||||
|
||||
#ifndef HAVE_STRUCT_ADDRINFO
|
||||
|
||||
struct addrinfo
|
||||
{
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
size_t ai_addrlen;
|
||||
struct sockaddr *ai_addr;
|
||||
char *ai_canonname;
|
||||
struct addrinfo *ai_next;
|
||||
};
|
||||
|
||||
#endif /* HAVE_STRUCT_ADDRINFO */
|
||||
|
||||
|
||||
|
||||
/* Rename private copies per comments above */
|
||||
#ifdef getaddrinfo
|
||||
#undef getaddrinfo
|
||||
#endif
|
||||
|
||||
#ifdef freeaddrinfo
|
||||
#undef freeaddrinfo
|
||||
#endif
|
||||
|
||||
#ifdef gai_strerror
|
||||
#undef gai_strerror
|
||||
#endif
|
||||
|
||||
#ifdef getnameinfo
|
||||
#undef getnameinfo
|
||||
#endif
|
||||
|
||||
extern int getaddrinfo(const char *node, const char *service,
|
||||
const struct addrinfo * hints, struct addrinfo ** res);
|
||||
extern void freeaddrinfo(struct addrinfo * res);
|
||||
extern const char *gai_strerror(int errcode);
|
||||
extern int getnameinfo(const struct sockaddr * sa, int salen,
|
||||
char *node, int nodelen,
|
||||
char *service, int servicelen, int flags);
|
||||
|
||||
#endif /* GETADDRINFO_H */
|
||||
|
||||
#endif /* HAVE_GETADDRINFO */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,533 @@
|
||||
|
||||
/*
|
||||
* libpqtypes-int.h
|
||||
* Private header file for libpqtypes. All source files include
|
||||
* this header.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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.
|
||||
*/
|
||||
|
||||
#ifndef LIBPQTYPES_INT_H
|
||||
#define LIBPQTYPES_INT_H
|
||||
|
||||
#define PLN do{ \
|
||||
printf("%s:%d\n", __FUNCTION__, __LINE__); \
|
||||
fflush(stdout); \
|
||||
}while(0)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "pqt_config.h"
|
||||
#endif
|
||||
|
||||
/* Using the windows compiler */
|
||||
#ifdef _MSC_VER
|
||||
# define PQT_MSVC _MSC_VER
|
||||
#endif
|
||||
|
||||
/* We can/should use the Windows API */
|
||||
#if defined(PQT_MSVC) || defined(__MINGW32__)
|
||||
# define PQT_WINAPI
|
||||
#endif
|
||||
|
||||
/* Compiling on a windows platform */
|
||||
#if defined(PQT_WINAPI) || defined(__CYGWIN__)
|
||||
# define PQT_WIN32
|
||||
#endif
|
||||
|
||||
/* WINAPI is available, include needed winsock headers */
|
||||
#ifdef PQT_WINAPI
|
||||
# ifdef PQT_MSVC
|
||||
# pragma warning (disable : 4706 4100 4711 4127 4702)
|
||||
# pragma warning (disable: 4706 4100 4514 4710 4201 4206)
|
||||
# endif
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# include <Wspiapi.h> /* need for getaddrinfo (2000 and below */
|
||||
# include <windows.h>
|
||||
#else
|
||||
# if defined(__CYGWIN__) || defined(HAVE_SYS_TYPES_H)
|
||||
# include <sys/types.h>
|
||||
# endif
|
||||
# if defined(__CYGWIN__) || defined(HAVE_SYS_SOCKET_H)
|
||||
# include <sys/socket.h>
|
||||
# endif
|
||||
# if defined(__CYGWIN__) || defined(HAVE_NETDB_H)
|
||||
# include <netdb.h>
|
||||
# endif
|
||||
# if defined(__CYGWIN__) || defined(HAVE_NETINET_IN_H)
|
||||
# include <netinet/in.h>
|
||||
# endif
|
||||
# if defined(__CYGWIN__) || defined(HAVE_ARPA_INET_H)
|
||||
# include <arpa/inet.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* string*.h, windows platforms just include string.h */
|
||||
#ifdef PQT_WIN32
|
||||
# include <string.h>
|
||||
#else
|
||||
# ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
# endif
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Include stddef.h if on windows or if we have it */
|
||||
#if defined(PQT_WIN32) || defined(HAVE_STDDEF_H)
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
|
||||
/* Include limits.h if on windows or if we have it */
|
||||
#if defined(PQT_WIN32) || defined(HAVE_LIMITS_H)
|
||||
# include <limits.h>
|
||||
#endif
|
||||
|
||||
/* Include linux/limits.h if available */
|
||||
#if defined(HAVE_CONFIG_H) && defined(HAVE_LINUX_LIMITS_H)
|
||||
# include <linux/limits.h>
|
||||
#endif
|
||||
|
||||
/* Include math.h if on windows or if we have it */
|
||||
#if defined(PQT_WIN32) || defined(HAVE_MATH_H)
|
||||
# include <math.h>
|
||||
#endif
|
||||
|
||||
/* Include the public API, pulls in libpq-fe.h for us. */
|
||||
#include "libpqtypes.h"
|
||||
#include "libpq-events.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef NULL_LEN
|
||||
# define NULL_LEN (-1)
|
||||
#endif
|
||||
|
||||
/* Win32 builds use _timezone and _tzname: manually define HAVE_TZNAME. */
|
||||
#ifdef PQT_WIN32
|
||||
# define HAVE_TZNAME
|
||||
# define pqt_timezone _timezone
|
||||
# define pqt_tzname _tzname
|
||||
#else
|
||||
# define pqt_timezone timezone
|
||||
# define pqt_tzname tzname
|
||||
#endif
|
||||
|
||||
#define PQT_OUTOFMEMORY "Out of memory error"
|
||||
#define PQT_MAXIDLEN 64
|
||||
|
||||
#define TEXTFMT 0
|
||||
#define BINARYFMT 1
|
||||
#define TYPFLAG_CASEFOLD 0x01
|
||||
#define TYPFLAG_ARRAY 0x02
|
||||
#define TYPFLAG_POINTER 0x04
|
||||
#define TYPFLAG_INVALID 0x08
|
||||
#define TYPFLAG_BYNAME 0x10
|
||||
|
||||
/* Avoid passing NULL to realloc, some systems don't support it */
|
||||
#define pqt_realloc(ptr, size) (ptr) ? realloc(ptr, size) : malloc(size)
|
||||
|
||||
#define countof(array) (int) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
/* MSVC 8 deprecated quite a few POSIX names */
|
||||
#if defined(PQT_MSVC) && PQT_MSVC >= 1400
|
||||
# undef strdup
|
||||
# undef sscanf
|
||||
# define strdup _strdup
|
||||
# define sscanf sscanf_s
|
||||
#endif
|
||||
|
||||
/* define va_copy */
|
||||
#ifndef va_copy
|
||||
# ifdef __va_copy
|
||||
# define va_copy(dest, src) __va_copy(dest, src)
|
||||
# else
|
||||
# define va_copy(dest, src) (dest) = (src)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define pqt_hex_to_dec(v) \
|
||||
(unsigned char) (((v) > '9') ? ((v) - 'a') + 10 : (v) - '0')
|
||||
|
||||
/* --------------------------------
|
||||
* Macros used by built-in handlers
|
||||
*/
|
||||
|
||||
/* used by get handlers */
|
||||
#define DECLLENGTH(args) \
|
||||
int valuel = PQgetlength((args)->get.result, (args)->get.tup_num, \
|
||||
(args)->get.field_num)
|
||||
|
||||
/* used by get handlers */
|
||||
#define DECLVALUE(getargs) \
|
||||
char *value = PQgetvalue((getargs)->get.result, (getargs)->get.tup_num, \
|
||||
(getargs)->get.field_num)
|
||||
|
||||
/* used by put handlers */
|
||||
#define PUTNULLCHK(args, valp) do{ \
|
||||
if (!(valp)) \
|
||||
return pqt_put_null(args); \
|
||||
}while (0)
|
||||
|
||||
/* used by get handlers, checks for NULL pointers and PQgetisnull */
|
||||
#define CHKGETVALS(args, outp) do{ \
|
||||
if (!(outp)) \
|
||||
RERR(args, "output pointer is NULL"); \
|
||||
memset(outp, 0, sizeof(*(outp))); \
|
||||
if (PQgetisnull((args)->get.result, (args)->get.tup_num, \
|
||||
(args)->get.field_num)) \
|
||||
return 0; \
|
||||
}while (0)
|
||||
|
||||
/* RERR for return error: errorf always returns -1 */
|
||||
#define RERR(_args, msg) return (_args)->errorf(_args, msg)
|
||||
#define RERR_STR2INT(args) RERR(args, "String to integer conversion failed")
|
||||
#define RERR_MEM(args) RERR(args, PQT_OUTOFMEMORY)
|
||||
|
||||
#ifdef STRICT_MEMORY_ALIGNMENT
|
||||
# define pqt_buf_putint2(_buffer, _val) do{ \
|
||||
short _v = (short) htons((short) (_val)); \
|
||||
memcpy(_buffer, &_v, sizeof(short)); \
|
||||
} while (0)
|
||||
|
||||
# define pqt_buf_putint4(_buffer, _val) do{ \
|
||||
int _v = (int) htonl((int) (_val)); \
|
||||
memcpy(_buffer, &_v, sizeof(int)); \
|
||||
} while (0)
|
||||
|
||||
short pqt_buf_getint2(char *buffer);
|
||||
int pqt_buf_getint4(char *buffer);
|
||||
#else
|
||||
# define pqt_buf_putint2(_out, _val) \
|
||||
*(short *) (_out) = (short) htons((short) (_val))
|
||||
# define pqt_buf_getint2(_buffer) (short) ntohs(*(short *) (_buffer))
|
||||
# define pqt_buf_putint4(_out, _val) \
|
||||
*(int *) (_out) = (int) htonl((int) (_val))
|
||||
# define pqt_buf_getint4(_buffer) (int) ntohl(*(int *) (_buffer))
|
||||
#endif
|
||||
|
||||
/* ----------------------------------
|
||||
* See: src/include/catalog/pg_type.h
|
||||
* Used by built-in type handlers
|
||||
*/
|
||||
|
||||
/* numerics types */
|
||||
#define INT2OID 21
|
||||
#define INT4OID 23
|
||||
#define INT8OID 20
|
||||
#define FLOAT4OID 700
|
||||
#define FLOAT8OID 701
|
||||
#define NUMERICOID 1700
|
||||
/* geo types */
|
||||
#define POINTOID 600
|
||||
#define LSEGOID 601
|
||||
#define PATHOID 602
|
||||
#define BOXOID 603
|
||||
#define POLYGONOID 604
|
||||
#define LINEOID 628 /* not supported yet */
|
||||
#define CIRCLEOID 718
|
||||
/* network types */
|
||||
#define INETOID 869
|
||||
#define CIDROID 650
|
||||
#define MACADDROID 829
|
||||
/* variable length types */
|
||||
#define BPCHAROID 1042
|
||||
#define VARCHAROID 1043
|
||||
#define NAMEOID 19
|
||||
#define TEXTOID 25
|
||||
#define ZPBITOID 1560 /* not supported yet */
|
||||
#define VARBITOID 1562 /* not supported yet */
|
||||
#define BYTEAOID 17
|
||||
/* date and time types */
|
||||
#define DATEOID 1082
|
||||
#define TIMEOID 1083
|
||||
#define TIMETZOID 1266
|
||||
#define TIMESTAMPOID 1114
|
||||
#define TIMESTAMPTZOID 1184
|
||||
#define INTERVALOID 1186
|
||||
/* misc types */
|
||||
#define CHAROID 18
|
||||
#define BOOLOID 16
|
||||
#define OIDOID 26
|
||||
#define CASHOID 790
|
||||
#define RECORDOID 2249
|
||||
#define UUIDOID 2950
|
||||
|
||||
/* --------------------------------
|
||||
* Private Structures
|
||||
*/
|
||||
|
||||
/* Represents a param value. An array of PGvalues was choosen over
|
||||
* allocating 4 separate arrays: oids, values, lengths, formats. Instead
|
||||
* of 4 allocations you only have one. The PGvalue array can easily be
|
||||
* converted to 4 arrays for execution.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int ptrl; /* Length of value's pointer */
|
||||
void *ptr; /* value pointer, data member uses this for non-NULL values */
|
||||
int datal; /* Length of current value: always <= ptrl */
|
||||
char *data; /* current value data, can be NULL ... thus the ptr member. */
|
||||
int format; /* format: 0=text, 1=binary */
|
||||
Oid oid; /* Oid of the data */
|
||||
} PGvalue;
|
||||
|
||||
/* performance driven structure. Instead of parsing and looking up
|
||||
* type specs each put or get, PQspecPrepare can be used to compile
|
||||
* a spec format string into a PGtypeSpec object. The biggest wins are
|
||||
* large results sets and arrays.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *name; /* name for the prepared spec, used for lookup. */
|
||||
char *stmt; /* parameterized version of stmt, if supplied */
|
||||
int idcnt; /* number of handler ids */
|
||||
int *idlist; /* handler ids */
|
||||
unsigned char *flags; /* handler flags */
|
||||
} PGtypeSpec;
|
||||
|
||||
/* PGparam structure */
|
||||
struct pg_param
|
||||
{
|
||||
int vcnt; /* current number of param values */
|
||||
int vmax; /* number of PGvalue structs in 'vals' array. */
|
||||
PGvalue *vals; /* array of param values, grown when needed */
|
||||
|
||||
/* assigned during PQparamCreate */
|
||||
PGtypeFormatInfo fmtinfo;
|
||||
int typhcnt;
|
||||
PGtypeHandler *typhandlers;
|
||||
|
||||
int typspeccnt;
|
||||
PGtypeSpec *typspecs;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PGtypeFormatInfo fmtinfo;
|
||||
int typhcnt;
|
||||
int typhmax;
|
||||
PGtypeHandler *typhandlers;
|
||||
|
||||
int typspeccnt;
|
||||
int typspecmax;
|
||||
PGtypeSpec *typspecs;
|
||||
} PGtypeData;
|
||||
|
||||
/* --------------------------------
|
||||
* Internal API functions (not exported)
|
||||
*/
|
||||
|
||||
/* === in events.c === */
|
||||
|
||||
/* The libpq PGEventProc */
|
||||
int pqt_eventproc(PGEventId id, void *info, void *passThrough);
|
||||
|
||||
void pqt_cleartypes(PGtypeData *typeData);
|
||||
|
||||
/* === in param.c === */
|
||||
|
||||
int pqt_putparam(PGparam *param, const void *data, int datal,
|
||||
int flags, int format, Oid typoid);
|
||||
|
||||
/* === in spec.c === */
|
||||
|
||||
PGtypeSpec *pqt_getspec(PGtypeSpec *specs, int count, const char *name);
|
||||
PGtypeSpec *pqt_dupspecs(PGtypeSpec *specs, int count);
|
||||
void pqt_clearspec(PGtypeSpec *cache);
|
||||
void pqt_freespecs(PGtypeSpec *specs, int count);
|
||||
|
||||
char *pqt_parse(const char *format, PGtypeHandler *h, int hcnt,
|
||||
char *stmtBuf, size_t stmtBufLen, PGtypeHandler **out, size_t *stmtPos,
|
||||
int *typpos, int *flags);
|
||||
|
||||
char *pqt_parsetype(const char *spec, char *schema, char *typname,
|
||||
int *flags, int typpos);
|
||||
|
||||
/* === in handler.c === */
|
||||
|
||||
int pqt_allowsptr(PGtypeHandler *h);
|
||||
void pqt_getfmtinfo(const PGconn *conn, PGtypeFormatInfo *info);
|
||||
PGtypeHandler *pqt_duphandlers(PGtypeHandler *handlers, int hcnt);
|
||||
void pqt_freehandlers(PGtypeHandler *handlers, int hcnt);
|
||||
int pqt_argssuper(PGtypeArgs *args, ...);
|
||||
int pqt_argserrorf(PGtypeArgs *args, const char *format, ...);
|
||||
void pqt_setresultfields(const PGresult *res);
|
||||
|
||||
PGtypeHandler *pqt_gethandler(PGtypeHandler *handlers, int hcnt,
|
||||
const char *schema, const char *typname);
|
||||
|
||||
PGtypeHandler *pqt_gethandlerbyid(PGtypeHandler *handlers,
|
||||
int hcnt, int id);
|
||||
|
||||
/* FQTN standards for Fully Qualified Type Name. Returns a pointer to out.
|
||||
* Only returns NULL if out is NULL or outl == 0.
|
||||
*/
|
||||
char *pqt_fqtn(char *out, size_t outl,
|
||||
const char *schema, const char *typname);
|
||||
|
||||
/* == in utils.c == */
|
||||
|
||||
PGresult *pqt_copyresult(PGtypeArgs *args, int nattrs);
|
||||
void pqt_swap8(void *outp, void *inp, int tonet);
|
||||
int pqt_text_to_int8(char *val, void *out);
|
||||
int pqt_text_to_float8(double *f8, char *text, char **endptr);
|
||||
|
||||
/* Checks buffer and truncates 'src' if 'dest' is too small. */
|
||||
char *pqt_strcpy(char *dest, size_t size, const char *src);
|
||||
|
||||
/* Taken from postgres project (named pg_xxx) */
|
||||
unsigned char pqt_tolower(unsigned char ch);
|
||||
int pqt_strcasecmp(const char *s1, const char *s2);
|
||||
int pqt_strncasecmp(const char *s1, const char *s2, size_t n);
|
||||
|
||||
/* == in port.c == */
|
||||
|
||||
/* Define a strtoll macro for windows, except for MinGW & Cygwin
|
||||
* which always have it. MSVC has _strtoi64, but MSVC 6 and under
|
||||
* require that we declare the prototype.
|
||||
*/
|
||||
#ifdef PQT_MSVC
|
||||
# if PQT_MSVC <= 1200
|
||||
__int64 __cdecl _strtoi64(const char *, char **, int);
|
||||
# endif
|
||||
# define strtoll _strtoi64
|
||||
#endif
|
||||
|
||||
/* In rare cases these could be absent (older platforms). We implement
|
||||
* drop-in replacements in port.c for those cases. strtod handled by
|
||||
* configure, which adds a replacement for us.
|
||||
*
|
||||
* NOTE: only unixes use configure so sections wrapped around
|
||||
* HAVE_CONFIG_H exclude windows.
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# ifndef HAVE_STRTOL
|
||||
long strtol(const char *nptr, char **endptr, int base);
|
||||
# endif
|
||||
# ifndef HAVE_STRTOUL
|
||||
unsigned long strtoul(const char *nptr, char **endptr, int base);
|
||||
# endif
|
||||
# ifndef HAVE_STRTOLL
|
||||
long long int strtoll(const char *nptr, char **endptr, int base);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Handles platform differences and always returns -1 for failure.
|
||||
* On windows: MSVC 8 uses _snprintf_s, otherwise _snprintf.
|
||||
*/
|
||||
int pqt_snprintf(char *buf, size_t size, const char *format, ...);
|
||||
int pqt_vsnprintf(char *buf, size_t size, const char *format, va_list ap);
|
||||
|
||||
/* --------------------------------
|
||||
* Built-in Type Handlers
|
||||
*/
|
||||
|
||||
/* === in array.c === */
|
||||
|
||||
int pqt_put_array(PGtypeArgs *args);
|
||||
int pqt_get_array(PGtypeArgs *args);
|
||||
|
||||
/* == in datetime.c == */
|
||||
|
||||
int pqt_put_date(PGtypeArgs *args);
|
||||
int pqt_get_date(PGtypeArgs *args);
|
||||
int pqt_put_time(PGtypeArgs *args);
|
||||
int pqt_get_time(PGtypeArgs *args);
|
||||
int pqt_put_timetz(PGtypeArgs *args);
|
||||
int pqt_get_timetz(PGtypeArgs *args);
|
||||
int pqt_put_timestamp(PGtypeArgs *args);
|
||||
int pqt_get_timestamp(PGtypeArgs *args);
|
||||
int pqt_put_timestamptz(PGtypeArgs *args);
|
||||
int pqt_get_timestamptz(PGtypeArgs *args);
|
||||
int pqt_put_interval(PGtypeArgs *args);
|
||||
int pqt_get_interval(PGtypeArgs *args);
|
||||
|
||||
/* == in geo.c == */
|
||||
|
||||
int pqt_put_point(PGtypeArgs *args);
|
||||
int pqt_get_point(PGtypeArgs *args);
|
||||
int pqt_put_lseg(PGtypeArgs *args);
|
||||
int pqt_get_lseg(PGtypeArgs *args);
|
||||
int pqt_put_box(PGtypeArgs *args);
|
||||
int pqt_get_box(PGtypeArgs *args);
|
||||
int pqt_put_circle(PGtypeArgs *args);
|
||||
int pqt_get_circle(PGtypeArgs *args);
|
||||
int pqt_put_path(PGtypeArgs *args);
|
||||
int pqt_get_path(PGtypeArgs *args);
|
||||
int pqt_put_polygon(PGtypeArgs *args);
|
||||
int pqt_get_polygon(PGtypeArgs *args);
|
||||
|
||||
/* == in misc.c == */
|
||||
|
||||
int pqt_put_char(PGtypeArgs *args); /* "char" type, not char(N) */
|
||||
int pqt_get_char(PGtypeArgs *args);
|
||||
int pqt_put_bool(PGtypeArgs *args);
|
||||
int pqt_get_bool(PGtypeArgs *args);
|
||||
int pqt_put_money(PGtypeArgs *args);
|
||||
int pqt_get_money(PGtypeArgs *args);
|
||||
int pqt_put_uuid(PGtypeArgs *args);
|
||||
int pqt_get_uuid(PGtypeArgs *args);
|
||||
int pqt_put_null(PGtypeArgs *args); /* no get for null */
|
||||
|
||||
/* == in network.c == */
|
||||
|
||||
int pqt_put_inet(PGtypeArgs *args); /* handles cidr */
|
||||
int pqt_get_inet(PGtypeArgs *args);
|
||||
int pqt_get_cidr(PGtypeArgs *args);
|
||||
int pqt_put_macaddr(PGtypeArgs *args);
|
||||
int pqt_get_macaddr(PGtypeArgs *args);
|
||||
|
||||
/* == in numerics.c == */
|
||||
|
||||
int pqt_put_int2(PGtypeArgs *args);
|
||||
int pqt_get_int2(PGtypeArgs *args);
|
||||
int pqt_put_int4(PGtypeArgs *args); /* handles oid */
|
||||
int pqt_get_int4(PGtypeArgs *args); /* handles oid */
|
||||
int pqt_put_int8(PGtypeArgs *args);
|
||||
int pqt_get_int8(PGtypeArgs *args);
|
||||
int pqt_put_float4(PGtypeArgs *args);
|
||||
int pqt_get_float4(PGtypeArgs *args);
|
||||
int pqt_put_float8(PGtypeArgs *args);
|
||||
int pqt_get_float8(PGtypeArgs *args);
|
||||
int pqt_put_numeric(PGtypeArgs *args);
|
||||
int pqt_get_numeric(PGtypeArgs *args);
|
||||
|
||||
/* == record.c (composites) == */
|
||||
|
||||
int pqt_put_record(PGtypeArgs *args);
|
||||
int pqt_get_record(PGtypeArgs *args);
|
||||
|
||||
/* == in varlena.c == */
|
||||
|
||||
int pqt_put_str(PGtypeArgs *args); /* no get for str - use PQgetvalue */
|
||||
int pqt_put_text(PGtypeArgs *args); /* handles varchar, bpchar and name */
|
||||
int pqt_get_text(PGtypeArgs *args); /* handles varchar, bpchar and name */
|
||||
int pqt_put_bytea(PGtypeArgs *args);
|
||||
int pqt_get_bytea(PGtypeArgs *args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,468 @@
|
||||
|
||||
/*
|
||||
* libpqtypes.h
|
||||
* Public header for libpqtypes. Contains the entire public API.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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.
|
||||
*/
|
||||
|
||||
#ifndef LIBPQTYPES_H
|
||||
#define LIBPQTYPES_H
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
# define PQT_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
# define PQT_EXPORT extern
|
||||
#endif
|
||||
|
||||
/* MSVC 6 must use `i64', everything else uses `LL'. */
|
||||
#if !defined(_MSC_VER) || _MSC_VER > 1200
|
||||
# define PQT_INT64CONST(x) ((PGint8) x##LL)
|
||||
#else
|
||||
# define PQT_INT64CONST(x) ((PGint8) x##i64)
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
PQT_SUBCLASS,
|
||||
PQT_COMPOSITE,
|
||||
PQT_USERDEFINED
|
||||
};
|
||||
|
||||
typedef struct pg_param PGparam;
|
||||
typedef struct pg_typeargs PGtypeArgs;
|
||||
typedef int (*PGtypeProc)(PGtypeArgs *args);
|
||||
|
||||
/* For use with a PQregisterXXX function */
|
||||
typedef struct
|
||||
{
|
||||
const char *typname;
|
||||
PGtypeProc typput;
|
||||
PGtypeProc typget;
|
||||
} PGregisterType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sversion;
|
||||
int pversion;
|
||||
char datestyle[48];
|
||||
int integer_datetimes;
|
||||
} PGtypeFormatInfo;
|
||||
|
||||
/* Record Attribute Description, its columns */
|
||||
typedef struct
|
||||
{
|
||||
Oid attoid;
|
||||
int attlen;
|
||||
int atttypmod;
|
||||
char attname[65];
|
||||
} PGrecordAttDesc;
|
||||
|
||||
/* Type handler for putf and getf functions. The char fixed length buffers
|
||||
* used to be allocated pointers. This was a performance problem when
|
||||
* many type handlers are registered and one uses getf on a composite or
|
||||
* an array. These types require generating a PGresult and duplicating
|
||||
* the type handlers. Saved 40% by not having to deep copy the strings.
|
||||
*/
|
||||
typedef struct pg_typhandler
|
||||
{
|
||||
int id;
|
||||
char typschema[65];
|
||||
char typname[65];
|
||||
int typlen;
|
||||
Oid typoid;
|
||||
Oid typoid_array;
|
||||
PGtypeProc typput;
|
||||
PGtypeProc typget;
|
||||
int base_id;
|
||||
|
||||
/* For composites, contains each attribute of a composite */
|
||||
int nattrs;
|
||||
int freeAttDescs;
|
||||
PGrecordAttDesc attDescsBuf[16];
|
||||
PGrecordAttDesc *attDescs;
|
||||
} PGtypeHandler;
|
||||
|
||||
/* Values required during a type handler put ot get operation. */
|
||||
struct pg_typeargs
|
||||
{
|
||||
int is_put;
|
||||
const PGtypeFormatInfo *fmtinfo;
|
||||
int is_ptr;
|
||||
int format;
|
||||
va_list ap;
|
||||
int typpos;
|
||||
PGtypeHandler *typhandler;
|
||||
int (*errorf)(PGtypeArgs *args, const char *format, ...);
|
||||
int (*super)(PGtypeArgs *args, ...);
|
||||
|
||||
struct
|
||||
{
|
||||
PGparam *param;
|
||||
char *out;
|
||||
char *__allocated_out; /* leave me alone! */
|
||||
int outl;
|
||||
int (*expandBuffer)(PGtypeArgs *args, int new_len);
|
||||
} put;
|
||||
|
||||
struct
|
||||
{
|
||||
PGresult *result;
|
||||
int tup_num;
|
||||
int field_num;
|
||||
} get;
|
||||
};
|
||||
|
||||
|
||||
/* ----------------
|
||||
* Variable Length types
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
typedef char *PGtext;
|
||||
typedef char *PGvarchar;
|
||||
typedef char *PGbpchar;
|
||||
typedef char *PGuuid;
|
||||
typedef struct
|
||||
{
|
||||
int len;
|
||||
char *data;
|
||||
} PGbytea;
|
||||
|
||||
/* ----------------
|
||||
* Numeric types
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
typedef signed char PGchar;
|
||||
typedef int PGbool;
|
||||
typedef short PGint2;
|
||||
typedef int PGint4;
|
||||
typedef float PGfloat4;
|
||||
typedef double PGfloat8;
|
||||
typedef char *PGnumeric;
|
||||
|
||||
/* Defined by an end-user if the system is missing long long. */
|
||||
#ifdef PQT_LONG_LONG
|
||||
typedef PQT_LONG_LONG PGint8;
|
||||
typedef PQT_LONG_LONG PGmoney;
|
||||
|
||||
/* MinGW and MSVC can use __int64 */
|
||||
#elif defined(__MINGW32__) || defined(_MSC_VER)
|
||||
typedef __int64 PGint8;
|
||||
typedef __int64 PGmoney;
|
||||
|
||||
/* Cygwin and Unixes. */
|
||||
#else
|
||||
typedef long long PGint8;
|
||||
typedef long long PGmoney;
|
||||
#endif
|
||||
|
||||
/* ----------------
|
||||
* Geometric type structures
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double x;
|
||||
double y;
|
||||
} PGpoint;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PGpoint pts[2];
|
||||
} PGlseg;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PGpoint high;
|
||||
PGpoint low;
|
||||
} PGbox;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PGpoint center;
|
||||
double radius;
|
||||
} PGcircle;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int npts;
|
||||
int closed;
|
||||
PGpoint *pts; /* for getf, only valid while PGresult is. */
|
||||
} PGpath;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int npts;
|
||||
PGpoint *pts; /* for getf, only valid while PGresult is. */
|
||||
} PGpolygon;
|
||||
|
||||
/* ----------------
|
||||
* Network type structures
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/* This struct works with CIDR as well. */
|
||||
typedef struct
|
||||
{
|
||||
int mask;
|
||||
int is_cidr;
|
||||
int sa_buf_len;
|
||||
|
||||
/* sockaddr buffer, can be casted to sockaddr, sockaddr_in,
|
||||
* sockaddr_in6 or sockaddr_stroage.
|
||||
*/
|
||||
char sa_buf[128];
|
||||
} PGinet;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int a;
|
||||
int b;
|
||||
int c;
|
||||
int d;
|
||||
int e;
|
||||
int f;
|
||||
} PGmacaddr;
|
||||
|
||||
/* ----------------
|
||||
* Date & Time structures
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int years;
|
||||
int mons;
|
||||
int days;
|
||||
int hours;
|
||||
int mins;
|
||||
int secs;
|
||||
int usecs;
|
||||
} PGinterval;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int isbc;
|
||||
int year;
|
||||
int mon;
|
||||
int mday;
|
||||
int jday;
|
||||
int yday;
|
||||
int wday;
|
||||
} PGdate;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int hour;
|
||||
int min;
|
||||
int sec;
|
||||
int usec;
|
||||
int withtz;
|
||||
int isdst;
|
||||
int gmtoff;
|
||||
char tzabbr[16];
|
||||
} PGtime;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PGint8 epoch;
|
||||
PGdate date;
|
||||
PGtime time;
|
||||
} PGtimestamp;
|
||||
|
||||
/* ----------------
|
||||
* Array structures
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
#ifndef MAXDIM
|
||||
# define MAXDIM 6
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ndims;
|
||||
int lbound[MAXDIM];
|
||||
int dims[MAXDIM];
|
||||
PGparam *param;
|
||||
PGresult *res;
|
||||
} PGarray;
|
||||
|
||||
/* ----------------
|
||||
* Public API funcs
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
/* === in events.c === */
|
||||
|
||||
/* Deprecated, see PQinitTypes */
|
||||
PQT_EXPORT int
|
||||
PQtypesRegister(PGconn *conn);
|
||||
|
||||
/* === in error.c === */
|
||||
|
||||
PQT_EXPORT char *
|
||||
PQgeterror(void);
|
||||
|
||||
/* PQseterror(NULL) will clear the error message */
|
||||
PQT_EXPORT void
|
||||
PQseterror(const char *format, ...);
|
||||
|
||||
/* Gets the error field for the last executed query. This only
|
||||
* pertains to PQparamExec and PQparamExecPrepared. When using a
|
||||
* standard libpq function like PQexec, PQresultErrorField should be used.
|
||||
*/
|
||||
PQT_EXPORT char *
|
||||
PQgetErrorField(int fieldcode);
|
||||
|
||||
/* === in spec.c === */
|
||||
|
||||
/* Set 'format' argument to NULL to clear a single prepared specifier. */
|
||||
PQT_EXPORT int
|
||||
PQspecPrepare(PGconn *conn, const char *name, const char *format, int is_stmt);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQclearSpecs(PGconn *conn);
|
||||
|
||||
/* === in handler.c === */
|
||||
|
||||
/* Initialize type support on the given connection */
|
||||
PQT_EXPORT int
|
||||
PQinitTypes(PGconn *conn);
|
||||
|
||||
/* Deprecated, see PQregisterTypes */
|
||||
PQT_EXPORT int
|
||||
PQregisterSubClasses(PGconn *conn, PGregisterType *types, int count);
|
||||
|
||||
/* Deprecated, see PQregisterTypes */
|
||||
PQT_EXPORT int
|
||||
PQregisterComposites(PGconn *conn, PGregisterType *types, int count);
|
||||
|
||||
/* Deprecated, see PQregisterTypes */
|
||||
PQT_EXPORT int
|
||||
PQregisterUserDefinedTypes(PGconn *conn, PGregisterType *types, int count);
|
||||
|
||||
/* Registers PQT_SUBCLASS, PQT_COMPOSITE or PQT_USERDEFINED
|
||||
* (the 'which' argument) for use with libpqtypes.
|
||||
*
|
||||
* For asynchronous type registration, set the 'async' argument to a
|
||||
* non-zero value. This value is ignored when 'which' is PQT_SUBCLASS,
|
||||
* since subclass registration does not execute any commands against the
|
||||
* server. Use the standard PQconsumeInput, PQisBusy and PQgetResult
|
||||
* to properly obtain a PGresult, which must be passed to PQregisterResult
|
||||
* to complete the registration.
|
||||
*/
|
||||
PQT_EXPORT int
|
||||
PQregisterTypes(PGconn *conn, int which, PGregisterType *types,
|
||||
int count, int async);
|
||||
|
||||
/* Registers a set of 'which' types found in the given PGresult. Caller
|
||||
* is responsible for clearing the result 'res'. Useful for performing
|
||||
* asynchronous type registration or for caching type result data to
|
||||
* avoid lookups on a new connection. If PQregisterTypes is ran in async
|
||||
* mode, the PGresult obtained via PGgetResult can be cached by an
|
||||
* application and provided to this function for new connections.
|
||||
*
|
||||
* Types and count should be identical to what was originally supplied
|
||||
* to PQregisterTypes.
|
||||
*
|
||||
* NOTE: although a PGconn is a required argument, it is never used
|
||||
* to perform any network operation (non-blocking safe).
|
||||
*
|
||||
* PQT_SUBCLASS is not supported and will result in an error if supplied.
|
||||
*/
|
||||
PQT_EXPORT int
|
||||
PQregisterResult(PGconn *conn, int which, PGregisterType *types,
|
||||
int count, PGresult *res);
|
||||
|
||||
/* Clears all type handlers registered on 'conn'. This is useful after a
|
||||
* PQreset or PQresetPoll to optionally allow one to re-register types via
|
||||
* PQregisterTypes.
|
||||
*/
|
||||
PQT_EXPORT int
|
||||
PQclearTypes(PGconn *conn);
|
||||
|
||||
/* === in param.c === */
|
||||
|
||||
PQT_EXPORT PGparam *
|
||||
PQparamCreate(const PGconn *conn);
|
||||
|
||||
PQT_EXPORT PGparam *
|
||||
PQparamDup(PGparam *param);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQparamCount(PGparam *param);
|
||||
|
||||
PQT_EXPORT void
|
||||
PQparamReset(PGparam *param);
|
||||
|
||||
PQT_EXPORT void
|
||||
PQparamClear(PGparam *param);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQputf(PGparam *param, const char *format, ...);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQputvf(PGparam *param, char *stmtBuf, size_t stmtBufLen,
|
||||
const char *format, va_list ap);
|
||||
|
||||
/* === in exec.c === */
|
||||
|
||||
PQT_EXPORT int
|
||||
PQgetf(const PGresult *res, int tup_num, const char *format, ...);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQgetvf(const PGresult *res, int tup_num, const char *format, va_list ap);
|
||||
|
||||
PQT_EXPORT PGresult *
|
||||
PQexecf(PGconn *conn, const char *cmdspec, ...);
|
||||
|
||||
PQT_EXPORT PGresult *
|
||||
PQexecvf(PGconn *conn, const char *cmdspec, va_list ap);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQsendf(PGconn *conn, const char *cmdspec, ...);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQsendvf(PGconn *conn, const char *cmdspec, va_list ap);
|
||||
|
||||
PQT_EXPORT PGresult *
|
||||
PQparamExec(PGconn *conn, PGparam *param,
|
||||
const char *command, int resultFormat);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQparamSendQuery(PGconn *conn, PGparam *param,
|
||||
const char *command, int resultFormat);
|
||||
|
||||
PQT_EXPORT PGresult *
|
||||
PQparamExecPrepared(PGconn *conn, PGparam *param,
|
||||
const char *stmtName, int resultFormat);
|
||||
|
||||
PQT_EXPORT int
|
||||
PQparamSendQueryPrepared(PGconn *conn, PGparam *param,
|
||||
const char *stmtName, int resultFormat);
|
||||
|
||||
/* === in datetime.c === */
|
||||
|
||||
PQT_EXPORT void
|
||||
PQlocalTZInfo(time_t *t, int *gmtoff, int *isdst, char **tzabbrp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !LIBPQTYPES_H */
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
|
||||
/*
|
||||
* misc.c
|
||||
* Type handler for CHAR, BOOL, MONEY, UUID data types.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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_char(PGtypeArgs *args)
|
||||
{
|
||||
PGchar c;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGchar *ptr = (PGchar *) va_arg(args->ap, PGchar *);
|
||||
PUTNULLCHK(args, ptr);
|
||||
c = *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = (PGchar) va_arg(args->ap, int);
|
||||
}
|
||||
|
||||
*args->put.out = c;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_char(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGchar *chp = va_arg(args->ap, PGchar *);
|
||||
CHKGETVALS(args, chp);
|
||||
*chp = *value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_bool(PGtypeArgs *args)
|
||||
{
|
||||
char b;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGbool *ptr = (PGbool *) va_arg(args->ap, PGbool *);
|
||||
PUTNULLCHK(args, ptr);
|
||||
b = (char) *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = (char) va_arg(args->ap, PGbool);
|
||||
}
|
||||
|
||||
*args->put.out = b != 0 ? 1 : 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_bool(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGbool *boolp = va_arg(args->ap, PGbool *);
|
||||
|
||||
CHKGETVALS(args, boolp);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
*boolp = (*value == 't') ? 1 : 0;
|
||||
else
|
||||
*boolp = (*value)!=0 ? 1 : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_money(PGtypeArgs *args)
|
||||
{
|
||||
PGmoney money;
|
||||
int len = args->fmtinfo->sversion >= 80300 ? 8 : 4;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGmoney *ptr = va_arg(args->ap, PGmoney *);
|
||||
PUTNULLCHK(args, ptr);
|
||||
money = *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
money = va_arg(args->ap, PGmoney);
|
||||
}
|
||||
|
||||
if (len == 8)
|
||||
pqt_swap8(args->put.out, &money, 1);
|
||||
else /* truncate: pre 8.3 server expecting a 4 byte money */
|
||||
pqt_buf_putint4(args->put.out, (int) money);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_money(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
DECLLENGTH(args);
|
||||
PGmoney *moneyp = va_arg(args->ap, PGmoney *);
|
||||
|
||||
CHKGETVALS(args, moneyp);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
char buf[64];
|
||||
char c, *p = buf;
|
||||
char *bufend = buf + sizeof(buf);
|
||||
|
||||
for (; (c = *value) != '\0' && p<bufend; ++value)
|
||||
{
|
||||
if (isdigit(c) || c == '-')
|
||||
{
|
||||
*p = c;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
buf[p - buf] = 0;
|
||||
|
||||
if (pqt_text_to_int8(buf, moneyp) == -1)
|
||||
RERR_STR2INT(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 8.3 uses a 64-bit money type. Need compatibility
|
||||
* for communicating with older servers.
|
||||
*/
|
||||
if (valuel == 4)
|
||||
pqt_buf_putint4(moneyp, pqt_buf_getint4(value));
|
||||
else
|
||||
pqt_swap8(moneyp, value, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_uuid(PGtypeArgs *args)
|
||||
{
|
||||
PGuuid uuid = va_arg(args->ap, PGuuid);
|
||||
PUTNULLCHK(args, uuid);
|
||||
args->put.out = uuid;
|
||||
return 16;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_uuid(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGuuid *uuid = va_arg(args->ap, PGuuid *);
|
||||
|
||||
CHKGETVALS(args, uuid);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
int i;
|
||||
char buf[128];
|
||||
char *p = buf;
|
||||
DECLLENGTH(args);
|
||||
|
||||
/* format: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11 */
|
||||
for (i=0; i < valuel; i++)
|
||||
{
|
||||
if (value[i] != '-')
|
||||
{
|
||||
*p++ = (pqt_hex_to_dec(value[i]) << 4) | pqt_hex_to_dec(value[i+1]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
*uuid = (char *) PQresultAlloc(args->get.result, p - buf);
|
||||
if (!*uuid)
|
||||
RERR_MEM(args);
|
||||
|
||||
memcpy(*uuid, buf, p - buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*uuid = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_null(PGtypeArgs *args)
|
||||
{
|
||||
args->put.out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
|
||||
/*
|
||||
* network.c
|
||||
* Type handler for the network data types.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
|
||||
!defined(HAVE_GETADDRINFO))
|
||||
# include "getaddrinfo.h"
|
||||
#endif
|
||||
|
||||
#ifndef PGSQL_AF_INET
|
||||
# define PGSQL_AF_INET (AF_INET + 0)
|
||||
#endif
|
||||
|
||||
#ifndef PGSQL_AF_INET6
|
||||
# define PGSQL_AF_INET6 (AF_INET + 1)
|
||||
#endif
|
||||
|
||||
#ifndef AF_INET6
|
||||
#warning NO AF_INET6 SUPPORT!
|
||||
#endif
|
||||
|
||||
/* Some platforms don't define this, like AIX 4.3 */
|
||||
#ifndef AI_NUMERICHOST
|
||||
# define AI_NUMERICHOST 0x04
|
||||
#endif
|
||||
|
||||
/* handles cidr as well */
|
||||
int
|
||||
pqt_put_inet(PGtypeArgs *args)
|
||||
{
|
||||
unsigned char *b = (unsigned char *)args->put.out;
|
||||
PGinet *inet = va_arg(args->ap, PGinet *);
|
||||
int family;
|
||||
|
||||
PUTNULLCHK(args, inet);
|
||||
|
||||
family = ((struct sockaddr *)inet->sa_buf)->sa_family;
|
||||
|
||||
if (family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf;
|
||||
*b++ = (unsigned char) PGSQL_AF_INET;
|
||||
*b++ = (unsigned char) inet->mask;
|
||||
*b++ = (unsigned char) (inet->is_cidr ? 1 : 0);
|
||||
*b++ = (unsigned char) 4;
|
||||
memcpy(b, &sa->sin_addr, 4);
|
||||
b += 4;
|
||||
}
|
||||
#ifdef AF_INET6
|
||||
else if (family == AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf;
|
||||
*b++ = (unsigned char) PGSQL_AF_INET6;
|
||||
*b++ = (unsigned char) inet->mask;
|
||||
*b++ = (unsigned char) (inet->is_cidr ? 1 : 0);
|
||||
*b++ = (unsigned char) 16;
|
||||
memcpy(b, &sa->sin6_addr, 16);
|
||||
b += 16;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
return args->errorf(args, "Unknown inet address family %d", family);
|
||||
}
|
||||
|
||||
return (int) ((char *) b - args->put.out);
|
||||
}
|
||||
|
||||
static int
|
||||
get_inet2(PGtypeArgs *args, int is_cidr)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
unsigned short family;
|
||||
PGinet *inet = va_arg(args->ap, PGinet *);
|
||||
|
||||
CHKGETVALS(args, inet);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
int r;
|
||||
char *p;
|
||||
char ipstr[80];
|
||||
struct addrinfo *ai = NULL;
|
||||
struct addrinfo hints;
|
||||
|
||||
pqt_strcpy(ipstr, sizeof(ipstr), value);
|
||||
if ((p = strrchr(ipstr, '/')))
|
||||
{
|
||||
*p = 0;
|
||||
inet->mask = atoi(p+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
inet->mask = 32;
|
||||
}
|
||||
|
||||
inet->is_cidr = is_cidr;
|
||||
|
||||
/* suppress hostname lookups */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
/* Without this, windows chokes with WSAHOST_NOT_FOUND */
|
||||
#ifdef PQT_WIN32
|
||||
hints.ai_family = AF_INET;
|
||||
#endif
|
||||
|
||||
if ((r = getaddrinfo(ipstr, NULL, &hints, &ai)) || !ai)
|
||||
{
|
||||
if(r == EAI_BADFLAGS)
|
||||
{
|
||||
hints.ai_flags = 0;
|
||||
r = getaddrinfo(ipstr, NULL, &hints, &ai);
|
||||
}
|
||||
/* Another WSAHOST_NOT_FOUND work around, but for IPv6 */
|
||||
#if defined(PQT_WIN32) && defined(AF_INET6)
|
||||
else if(r == WSAHOST_NOT_FOUND)
|
||||
{
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_family = AF_INET6;
|
||||
r = getaddrinfo(ipstr, NULL, &hints, &ai);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(r)
|
||||
RERR_STR2INT(args);
|
||||
}
|
||||
|
||||
inet->sa_buf_len = (int) ai->ai_addrlen;
|
||||
memcpy(inet->sa_buf, ai->ai_addr, inet->sa_buf_len);
|
||||
|
||||
/* Some platforms, lika AIX 4.3, do not zero this structure properly.
|
||||
* The family and port are dirty, so set the first 4 bytes to 0 and
|
||||
* then re-set the family. I saw "0x1002" as the first 2 bytes of
|
||||
* this structure (dumb getaddrinfo), it should be "0x0002" for AF_INET.
|
||||
*/
|
||||
memset(inet->sa_buf, 0, 4);
|
||||
((struct sockaddr *)inet->sa_buf)->sa_family = ai->ai_addr->sa_family;
|
||||
|
||||
/* Uninitialized memory, postgres inet/cidr types don't store this.
|
||||
* Make sure its set to 0. Another AIX problem (maybe other platforms).
|
||||
*/
|
||||
#ifdef AF_INET6
|
||||
if (ai->ai_addr->sa_family == AF_INET6)
|
||||
((struct sockaddr_in6 *)inet->sa_buf)->sin6_flowinfo = 0;
|
||||
#endif
|
||||
|
||||
freeaddrinfo(ai);
|
||||
return 0;
|
||||
}
|
||||
|
||||
family = (unsigned short) *value++;
|
||||
if (family == PGSQL_AF_INET)
|
||||
{
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *) inet->sa_buf;
|
||||
sa->sin_family = AF_INET;
|
||||
inet->mask = (unsigned char) *value++;
|
||||
inet->is_cidr = *value++;
|
||||
memcpy(&sa->sin_addr, value + 1, *value);
|
||||
inet->sa_buf_len = (int) sizeof(struct sockaddr_in);
|
||||
}
|
||||
#ifdef AF_INET6
|
||||
else if (family == PGSQL_AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 *sa = (struct sockaddr_in6 *) inet->sa_buf;
|
||||
sa->sin6_family = AF_INET6;
|
||||
inet->mask = (unsigned char) *value++;
|
||||
inet->is_cidr = *value++;
|
||||
memcpy(&sa->sin6_addr, value + 1, *value);
|
||||
inet->sa_buf_len = (int) sizeof(struct sockaddr_in6);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
return args->errorf(args, "Unknown inet address family %d", family);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_inet(PGtypeArgs *args)
|
||||
{
|
||||
return get_inet2(args, 0);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_cidr(PGtypeArgs *args)
|
||||
{
|
||||
return get_inet2(args, 1);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_macaddr(PGtypeArgs *args)
|
||||
{
|
||||
PGmacaddr *mac = va_arg(args->ap, PGmacaddr *);
|
||||
|
||||
PUTNULLCHK(args, mac);
|
||||
|
||||
args->put.out[0] = (unsigned char) mac->a;
|
||||
args->put.out[1] = (unsigned char) mac->b;
|
||||
args->put.out[2] = (unsigned char) mac->c;
|
||||
args->put.out[3] = (unsigned char) mac->d;
|
||||
args->put.out[4] = (unsigned char) mac->e;
|
||||
args->put.out[5] = (unsigned char) mac->f;
|
||||
return 6;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_macaddr(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
unsigned char *p;
|
||||
PGmacaddr *mac = va_arg(args->ap, PGmacaddr *);
|
||||
|
||||
CHKGETVALS(args, mac);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
int a,b,c,d,e,f;
|
||||
int count = sscanf(value, "%x:%x:%x:%x:%x:%x", &a,&b,&c,&d,&e,&f);
|
||||
|
||||
if (count != 6 || (a < 0) || (a > 255) || (b < 0) || (b > 255) ||
|
||||
(c < 0) || (c > 255) || (d < 0) || (d > 255) ||
|
||||
(e < 0) || (e > 255) || (f < 0) || (f > 255))
|
||||
RERR_STR2INT(args);
|
||||
|
||||
mac->a = a;
|
||||
mac->b = b;
|
||||
mac->c = c;
|
||||
mac->d = d;
|
||||
mac->e = e;
|
||||
mac->f = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = (unsigned char *) value;
|
||||
mac->a = *p++;
|
||||
mac->b = *p++;
|
||||
mac->c = *p++;
|
||||
mac->d = *p++;
|
||||
mac->e = *p++;
|
||||
mac->f = *p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
|
||||
/*
|
||||
* numeric.c
|
||||
* Type handler for the numeric data types.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Extremely annoying warning "conversion from 'int ' to 'short'" on
|
||||
* Visual Studio 6. Normally this warning is useful but not in this
|
||||
* case. Disable it.
|
||||
*/
|
||||
#if defined(PQT_MSVC) && PQT_MSVC <= 1200
|
||||
# pragma warning (disable : 4244)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macros and structures for receiving numeric field in binary
|
||||
*/
|
||||
#define NBASE 10000
|
||||
#define HALF_NBASE 5000
|
||||
#define DEC_DIGITS 4 /* decimal digits per NBASE digit */
|
||||
#define MUL_GUARD_DIGITS 2 /* these are measured in NBASE digits */
|
||||
#define DIV_GUARD_DIGITS 4
|
||||
|
||||
/*
|
||||
* Hardcoded precision limit - arbitrary, but must be small enough that
|
||||
* dscale values will fit in 14 bits.
|
||||
*/
|
||||
#define NUMERIC_MAX_PRECISION 1000
|
||||
|
||||
/*
|
||||
* Sign values and macros to deal with packing/unpacking n_sign_dscale
|
||||
*/
|
||||
#define NUMERIC_SIGN_MASK 0xC000
|
||||
#define NUMERIC_POS 0x0000
|
||||
#define NUMERIC_NEG 0x4000
|
||||
#define NUMERIC_NAN 0xC000
|
||||
#define NUMERIC_DSCALE_MASK 0x3FFF
|
||||
#define NUMERIC_SIGN(n) ((n)->n_sign_dscale & NUMERIC_SIGN_MASK)
|
||||
#define NUMERIC_DSCALE(n) ((n)->n_sign_dscale & NUMERIC_DSCALE_MASK)
|
||||
#define NUMERIC_IS_NAN(n) (NUMERIC_SIGN(n) != NUMERIC_POS && \
|
||||
NUMERIC_SIGN(n) != NUMERIC_NEG)
|
||||
|
||||
typedef short NumericDigit;
|
||||
static const int round_powers[4] = {0, 1000, 100, 10};
|
||||
|
||||
typedef struct NumericVar
|
||||
{
|
||||
int ndigits; /* # of digits in digits[] - can be 0! */
|
||||
int weight; /* weight of first digit */
|
||||
int sign; /* NUMERIC_POS, NUMERIC_NEG, or NUMERIC_NAN */
|
||||
int dscale; /* display scale */
|
||||
NumericDigit *buf; /* start of palloc'd space for digits[] */
|
||||
NumericDigit *digits; /* base-NBASE digits */
|
||||
} NumericVar;
|
||||
|
||||
static NumericVar const_nan =
|
||||
{0, 0, NUMERIC_NAN, 0, NULL, NULL};
|
||||
|
||||
static int str2num(PGtypeArgs *args, const char *str, NumericVar *dest);
|
||||
static int num2str(char *out, size_t outl, NumericVar *var, int dscale);
|
||||
|
||||
int
|
||||
pqt_put_int2(PGtypeArgs *args)
|
||||
{
|
||||
PGint2 n;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGint4 *nptr = va_arg(args->ap, PGint4 *);
|
||||
PUTNULLCHK(args, nptr);
|
||||
n = (PGint2) *nptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = (PGint2) va_arg(args->ap, PGint4);
|
||||
}
|
||||
|
||||
pqt_buf_putint2(args->put.out, n);
|
||||
return 2;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_int2(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGint2 *i2p = va_arg(args->ap, PGint2 *);
|
||||
|
||||
CHKGETVALS(args, i2p);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
int n;
|
||||
|
||||
errno = 0;
|
||||
if ((n = (int) strtol(value, NULL, 10)) == 0 && errno)
|
||||
RERR_STR2INT(args);
|
||||
|
||||
*i2p = (PGint2)n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*i2p = pqt_buf_getint2(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* handles oid as well */
|
||||
int
|
||||
pqt_put_int4(PGtypeArgs *args)
|
||||
{
|
||||
PGint4 n;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGint4 *nptr = va_arg(args->ap, PGint4 *);
|
||||
PUTNULLCHK(args, nptr);
|
||||
n = *nptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = (PGint4) va_arg(args->ap, PGint4);
|
||||
}
|
||||
|
||||
*(PGint4 *)args->put.out = (PGint4) htonl(n);
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* handles oid as well */
|
||||
int
|
||||
pqt_get_int4(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGint4 *i4p = va_arg(args->ap, PGint4 *);
|
||||
|
||||
CHKGETVALS(args, i4p);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
PGint4 n;
|
||||
|
||||
/* Use strtoul so this can support Oid */
|
||||
if ((n = (PGint4) strtoul(value, NULL, 10)) == 0 && errno)
|
||||
RERR_STR2INT(args);
|
||||
|
||||
*i4p = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*i4p = (PGint4) pqt_buf_getint4(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_int8(PGtypeArgs *args)
|
||||
{
|
||||
PGint8 i8;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGint8 *nptr = va_arg(args->ap, PGint8 *);
|
||||
PUTNULLCHK(args, nptr);
|
||||
i8 = *nptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
i8 = va_arg(args->ap, PGint8);
|
||||
}
|
||||
|
||||
pqt_swap8(args->put.out, &i8, 1);
|
||||
return 8;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_int8(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGint8 *i8p = va_arg(args->ap, PGint8 *);
|
||||
|
||||
CHKGETVALS(args, i8p);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
if (pqt_text_to_int8(value, i8p) == -1)
|
||||
RERR_STR2INT(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pqt_swap8(i8p, value, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_float4(PGtypeArgs *args)
|
||||
{
|
||||
void *vp;
|
||||
PGfloat4 f;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
vp = (void *) va_arg(args->ap, double *);
|
||||
PUTNULLCHK(args, vp);
|
||||
}
|
||||
else
|
||||
{
|
||||
f = (PGfloat4) va_arg(args->ap, double);
|
||||
vp = (void *)&f;
|
||||
}
|
||||
|
||||
pqt_buf_putint4(args->put.out, *(int *) vp);
|
||||
return 4;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_float4(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
int *f4p = (int *) va_arg(args->ap, PGfloat4 *);
|
||||
|
||||
CHKGETVALS(args, f4p);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
PGfloat4 f;
|
||||
|
||||
errno = 0;
|
||||
if ((f = (PGfloat4) strtod(value, NULL)) == 0 && errno)
|
||||
RERR_STR2INT(args);
|
||||
|
||||
*(PGfloat4 *) f4p = f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*f4p = pqt_buf_getint4(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_float8(PGtypeArgs *args)
|
||||
{
|
||||
PGfloat8 d;
|
||||
|
||||
if (args->is_ptr)
|
||||
{
|
||||
PGfloat8 *ptr = (PGfloat8 *) va_arg(args->ap, PGfloat8 *);
|
||||
PUTNULLCHK(args, ptr);
|
||||
d = *ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
d = va_arg(args->ap, PGfloat8);
|
||||
}
|
||||
|
||||
pqt_swap8(args->put.out, &d, 1);
|
||||
return 8;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_float8(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGfloat8 *f8p = va_arg(args->ap, PGfloat8 *);
|
||||
|
||||
CHKGETVALS(args, f8p);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
if (!pqt_text_to_float8(f8p, value, NULL))
|
||||
RERR_STR2INT(args);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pqt_swap8(f8p, value, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* exposing a NumericVar struct to a libpq user, or something similar,
|
||||
* doesn't seem useful w/o a library to operate on it. Instead, we
|
||||
* always expose a numeric in text format and let the API user decide
|
||||
* how to use it .. like strod or a 3rd party big number library. We
|
||||
* always send a numeric in binary though.
|
||||
*/
|
||||
int
|
||||
pqt_put_numeric(PGtypeArgs *args)
|
||||
{
|
||||
int numlen;
|
||||
NumericVar num = {0};
|
||||
short *out;
|
||||
PGnumeric str = va_arg(args->ap, PGnumeric);
|
||||
|
||||
PUTNULLCHK(args, str);
|
||||
|
||||
if (str2num(args, str, &num))
|
||||
{
|
||||
if (num.digits)
|
||||
free(num.digits);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* variable length data type, grow args->put.out buffer if needed */
|
||||
numlen = (int) sizeof(short) * (4 + num.ndigits);
|
||||
if (args->put.expandBuffer(args, numlen) == -1)
|
||||
return -1;
|
||||
|
||||
out = (short *) args->put.out;
|
||||
*out++ = htons((short) num.ndigits);
|
||||
*out++ = htons((short) num.weight);
|
||||
*out++ = htons((short) num.sign);
|
||||
*out++ = htons((short) num.dscale);
|
||||
|
||||
if (num.digits)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i < num.ndigits; i++)
|
||||
*out++ = htons(num.digits[i]);
|
||||
free(num.digits);
|
||||
}
|
||||
|
||||
return numlen;
|
||||
}
|
||||
|
||||
/* exposing a NumericVar struct to a libpq user, or something similar,
|
||||
* doesn't seem useful w/o a library to operate on it. Instead, we
|
||||
* always expose a numeric in text format and let the API user decide
|
||||
* how to use it .. like strod or a 3rd party big number library.
|
||||
*/
|
||||
int
|
||||
pqt_get_numeric(PGtypeArgs *args)
|
||||
{
|
||||
int i;
|
||||
short *s;
|
||||
NumericVar num;
|
||||
DECLVALUE(args);
|
||||
char buf[4096];
|
||||
size_t len;
|
||||
PGnumeric *str = va_arg(args->ap, PGnumeric *);
|
||||
|
||||
CHKGETVALS(args, str);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
*str = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s = (short *) value;
|
||||
num.ndigits = ntohs(*s); s++;
|
||||
num.weight = (short) ntohs(*s); s++;
|
||||
num.sign = ntohs(*s); s++;
|
||||
num.dscale = ntohs(*s); s++;
|
||||
num.digits = (short *) malloc(num.ndigits * sizeof(short));
|
||||
if (!num.digits)
|
||||
RERR_MEM(args);
|
||||
|
||||
for (i=0; i < num.ndigits; i++)
|
||||
{
|
||||
num.digits[i] = ntohs(*s);
|
||||
s++;
|
||||
}
|
||||
|
||||
i = num2str(buf, sizeof(buf), &num, num.dscale);
|
||||
free(num.digits);
|
||||
|
||||
/* num2str failed, only fails when 'str' is too small */
|
||||
if (i == -1)
|
||||
RERR(args, "out buffer is too small");
|
||||
|
||||
len = strlen(buf)+1;
|
||||
*str = PQresultAlloc(args->get.result, len);
|
||||
if (!*str)
|
||||
RERR_MEM(args);
|
||||
|
||||
memcpy(*str, buf, len);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* round_var
|
||||
*
|
||||
* Round the value of a variable to no more than rscale decimal digits
|
||||
* after the decimal point. NOTE: we allow rscale < 0 here, implying
|
||||
* rounding before the decimal point.
|
||||
*/
|
||||
static void
|
||||
round_var(NumericVar *var, int rscale)
|
||||
{
|
||||
NumericDigit *digits = var->digits;
|
||||
int di;
|
||||
int ndigits;
|
||||
int carry;
|
||||
|
||||
var->dscale = rscale;
|
||||
|
||||
/* decimal digits wanted */
|
||||
di = (var->weight + 1) * DEC_DIGITS + rscale;
|
||||
|
||||
/*
|
||||
* If di = 0, the value loses all digits, but could round up to 1 if its
|
||||
* first extra digit is >= 5. If di < 0 the result must be 0.
|
||||
*/
|
||||
if (di < 0)
|
||||
{
|
||||
var->ndigits = 0;
|
||||
var->weight = 0;
|
||||
var->sign = NUMERIC_POS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* NBASE digits wanted */
|
||||
ndigits = (di + DEC_DIGITS - 1) / DEC_DIGITS;
|
||||
|
||||
/* 0, or number of decimal digits to keep in last NBASE digit */
|
||||
di %= DEC_DIGITS;
|
||||
|
||||
if (ndigits < var->ndigits ||
|
||||
(ndigits == var->ndigits && di > 0))
|
||||
{
|
||||
var->ndigits = ndigits;
|
||||
|
||||
if (di == 0)
|
||||
carry = (digits[ndigits] >= HALF_NBASE) ? 1 : 0;
|
||||
else
|
||||
{
|
||||
/* Must round within last NBASE digit */
|
||||
int extra,
|
||||
pow10;
|
||||
|
||||
pow10 = round_powers[di];
|
||||
extra = digits[--ndigits] % pow10;
|
||||
digits[ndigits] = digits[ndigits] - (NumericDigit) extra;
|
||||
carry = 0;
|
||||
if (extra >= pow10 / 2)
|
||||
{
|
||||
pow10 += digits[ndigits];
|
||||
if (pow10 >= NBASE)
|
||||
{
|
||||
pow10 -= NBASE;
|
||||
carry = 1;
|
||||
}
|
||||
digits[ndigits] = (NumericDigit) pow10;
|
||||
}
|
||||
}
|
||||
|
||||
/* Propagate carry if needed */
|
||||
while (carry)
|
||||
{
|
||||
carry += digits[--ndigits];
|
||||
if (carry >= NBASE)
|
||||
{
|
||||
digits[ndigits] = (NumericDigit) (carry - NBASE);
|
||||
carry = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
digits[ndigits] = (NumericDigit) carry;
|
||||
carry = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ndigits < 0)
|
||||
{
|
||||
var->digits--;
|
||||
var->ndigits++;
|
||||
var->weight++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* strip_var
|
||||
*
|
||||
* Strip any leading and trailing zeroes from a numeric variable
|
||||
*/
|
||||
static void
|
||||
strip_var(NumericVar *var)
|
||||
{
|
||||
NumericDigit *digits = var->digits;
|
||||
int ndigits = var->ndigits;
|
||||
|
||||
/* Strip leading zeroes */
|
||||
while (ndigits > 0 && *digits == 0)
|
||||
{
|
||||
digits++;
|
||||
var->weight--;
|
||||
ndigits--;
|
||||
}
|
||||
|
||||
/* Strip trailing zeroes */
|
||||
while (ndigits > 0 && digits[ndigits - 1] == 0)
|
||||
ndigits--;
|
||||
|
||||
/* If it's zero, normalize the sign and weight */
|
||||
if (ndigits == 0)
|
||||
{
|
||||
var->sign = NUMERIC_POS;
|
||||
var->weight = 0;
|
||||
}
|
||||
|
||||
if (ndigits != var->ndigits) {
|
||||
memcpy(var->digits, digits, (ndigits) * sizeof(NumericDigit));
|
||||
var->ndigits = ndigits;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* str2num()
|
||||
*
|
||||
* Parse a string and put the number into a variable
|
||||
* returns -1 on error and 0 for success.
|
||||
*/
|
||||
static int
|
||||
str2num(PGtypeArgs *args, const char *str, NumericVar *dest)
|
||||
{
|
||||
const char *cp = str;
|
||||
int have_dp = FALSE;
|
||||
int i;
|
||||
unsigned char *decdigits;
|
||||
int sign = NUMERIC_POS;
|
||||
int dweight = -1;
|
||||
int ddigits;
|
||||
int dscale = 0;
|
||||
int weight;
|
||||
int ndigits;
|
||||
int offset;
|
||||
NumericDigit *digits;
|
||||
|
||||
/*
|
||||
* We first parse the string to extract decimal digits and determine the
|
||||
* correct decimal weight. Then convert to NBASE representation.
|
||||
*/
|
||||
|
||||
/* skip leading spaces */
|
||||
while (*cp)
|
||||
{
|
||||
if (!isspace((unsigned char) *cp))
|
||||
break;
|
||||
cp++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for NaN
|
||||
*/
|
||||
if (tolower(*cp) == 'n' && tolower(*(cp+1)) == 'a'
|
||||
&& tolower(*(cp+2)) == 'n')
|
||||
{
|
||||
cp += 3;
|
||||
/* Should be nothing left but spaces */
|
||||
while (*cp)
|
||||
{
|
||||
if (!isspace(*cp))
|
||||
{
|
||||
return args->errorf(args,
|
||||
"invalid input syntax for type numeric: '%s'", str);
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
*dest = const_nan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (*cp)
|
||||
{
|
||||
case '+':
|
||||
sign = NUMERIC_POS;
|
||||
cp++;
|
||||
break;
|
||||
|
||||
case '-':
|
||||
sign = NUMERIC_NEG;
|
||||
cp++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*cp == '.')
|
||||
{
|
||||
have_dp = TRUE;
|
||||
cp++;
|
||||
}
|
||||
|
||||
if (!isdigit((unsigned char) *cp))
|
||||
return args->errorf(args,
|
||||
"invalid input syntax for type numeric: '%s'", str);
|
||||
|
||||
decdigits = (unsigned char *) malloc(strlen(cp) + DEC_DIGITS * 2);
|
||||
|
||||
/* leading padding for digit alignment later */
|
||||
memset(decdigits, 0, DEC_DIGITS);
|
||||
i = DEC_DIGITS;
|
||||
|
||||
while (*cp)
|
||||
{
|
||||
if (isdigit((unsigned char) *cp))
|
||||
{
|
||||
decdigits[i++] = *cp++ - '0';
|
||||
if (!have_dp)
|
||||
dweight++;
|
||||
else
|
||||
dscale++;
|
||||
}
|
||||
else if (*cp == '.')
|
||||
{
|
||||
if (have_dp)
|
||||
{
|
||||
free(decdigits);
|
||||
return args->errorf(args,
|
||||
"invalid input syntax for type numeric: '%s'", str);
|
||||
}
|
||||
|
||||
have_dp = TRUE;
|
||||
cp++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
ddigits = i - DEC_DIGITS;
|
||||
/* trailing padding for digit alignment later */
|
||||
memset(decdigits + i, 0, DEC_DIGITS - 1);
|
||||
|
||||
/* Handle exponent, if any */
|
||||
if (*cp == 'e' || *cp == 'E')
|
||||
{
|
||||
long exponent;
|
||||
char *endptr;
|
||||
|
||||
cp++;
|
||||
exponent = strtol(cp, &endptr, 10);
|
||||
if (endptr == cp)
|
||||
{
|
||||
free(decdigits);
|
||||
return args->errorf(args,
|
||||
"invalid input syntax for type numeric: '%s'", str);
|
||||
}
|
||||
|
||||
cp = endptr;
|
||||
if (exponent > NUMERIC_MAX_PRECISION ||
|
||||
exponent < -NUMERIC_MAX_PRECISION)
|
||||
{
|
||||
free(decdigits);
|
||||
return args->errorf(args,
|
||||
"invalid input syntax for type numeric: '%s'", str);
|
||||
}
|
||||
|
||||
dweight += (int) exponent;
|
||||
dscale -= (int) exponent;
|
||||
if (dscale < 0)
|
||||
dscale = 0;
|
||||
}
|
||||
|
||||
/* Should be nothing left but spaces */
|
||||
while (*cp)
|
||||
{
|
||||
if (!isspace((unsigned char) *cp))
|
||||
{
|
||||
free(decdigits);
|
||||
return args->errorf(args,
|
||||
"invalid input syntax for type numeric: '%s'", str);
|
||||
}
|
||||
cp++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, convert pure-decimal representation to base NBASE. First we need
|
||||
* to determine the converted weight and ndigits. offset is the number of
|
||||
* decimal zeroes to insert before the first given digit to have a
|
||||
* correctly aligned first NBASE digit.
|
||||
*/
|
||||
if (dweight >= 0)
|
||||
weight = (dweight + 1 + DEC_DIGITS - 1) / DEC_DIGITS - 1;
|
||||
else
|
||||
weight = -((-dweight - 1) / DEC_DIGITS + 1);
|
||||
offset = (weight + 1) * DEC_DIGITS - (dweight + 1);
|
||||
ndigits = (ddigits + offset + DEC_DIGITS - 1) / DEC_DIGITS;
|
||||
|
||||
dest->digits = (NumericDigit *) malloc((ndigits) * sizeof(NumericDigit));
|
||||
dest->ndigits = ndigits;
|
||||
dest->sign = sign;
|
||||
dest->weight = weight;
|
||||
dest->dscale = dscale;
|
||||
|
||||
i = DEC_DIGITS - offset;
|
||||
digits = dest->digits;
|
||||
|
||||
while (ndigits-- > 0)
|
||||
{
|
||||
*digits++ = ((decdigits[i] * 10 + decdigits[i + 1]) * 10 +
|
||||
decdigits[i + 2]) * 10 + decdigits[i + 3];
|
||||
i += DEC_DIGITS;
|
||||
}
|
||||
|
||||
free(decdigits);
|
||||
|
||||
/* Strip any leading/trailing zeroes, and normalize weight if zero */
|
||||
strip_var(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* num2str() -
|
||||
*
|
||||
* Convert a var to text representation (guts of numeric_out).
|
||||
* CAUTION: var's contents may be modified by rounding!
|
||||
* returns -1 on error and 0 for success.
|
||||
*/
|
||||
static int
|
||||
num2str(char *out, size_t outl, NumericVar *var, int dscale)
|
||||
{
|
||||
//char *str;
|
||||
char *cp;
|
||||
char *endcp;
|
||||
int i;
|
||||
int d;
|
||||
NumericDigit dig;
|
||||
NumericDigit d1;
|
||||
|
||||
/*
|
||||
* Handle NaN
|
||||
*/
|
||||
if (var->sign == NUMERIC_NAN) {
|
||||
strcpy(out, "NaN");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dscale < 0)
|
||||
dscale = 0;
|
||||
|
||||
/*
|
||||
* Check if we must round up before printing the value and do so.
|
||||
*/
|
||||
round_var(var, dscale);
|
||||
|
||||
/*
|
||||
* Allocate space for the result.
|
||||
*
|
||||
* i is set to to # of decimal digits before decimal point. dscale is the
|
||||
* # of decimal digits we will print after decimal point. We may generate
|
||||
* as many as DEC_DIGITS-1 excess digits at the end, and in addition we
|
||||
* need room for sign, decimal point, null terminator.
|
||||
*/
|
||||
i = (var->weight + 1) * DEC_DIGITS;
|
||||
if (i <= 0)
|
||||
i = 1;
|
||||
|
||||
if (outl <= (size_t) (i + dscale + DEC_DIGITS + 2))
|
||||
return -1;
|
||||
|
||||
//str = palloc(i + dscale + DEC_DIGITS + 2);
|
||||
//cp = str
|
||||
cp = out;
|
||||
|
||||
/*
|
||||
* Output a dash for negative values
|
||||
*/
|
||||
if (var->sign == NUMERIC_NEG)
|
||||
*cp++ = '-';
|
||||
|
||||
/*
|
||||
* Output all digits before the decimal point
|
||||
*/
|
||||
if (var->weight < 0)
|
||||
{
|
||||
d = var->weight + 1;
|
||||
*cp++ = '0';
|
||||
}
|
||||
else
|
||||
{
|
||||
for (d = 0; d <= var->weight; d++)
|
||||
{
|
||||
dig = (d < var->ndigits) ? var->digits[d] : 0;
|
||||
/* In the first digit, suppress extra leading decimal zeroes */
|
||||
{
|
||||
int putit = (d > 0);
|
||||
|
||||
d1 = dig / 1000;
|
||||
dig -= d1 * 1000;
|
||||
putit |= (d1 > 0);
|
||||
if (putit)
|
||||
*cp++ = (char) (d1 + '0');
|
||||
d1 = dig / 100;
|
||||
dig -= d1 * 100;
|
||||
putit |= (d1 > 0);
|
||||
if (putit)
|
||||
*cp++ = (char) (d1 + '0');
|
||||
d1 = dig / 10;
|
||||
dig -= d1 * 10;
|
||||
putit |= (d1 > 0);
|
||||
if (putit)
|
||||
*cp++ = (char) (d1 + '0');
|
||||
*cp++ = (char) (dig + '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If requested, output a decimal point and all the digits that follow it.
|
||||
* We initially put out a multiple of DEC_DIGITS digits, then truncate if
|
||||
* needed.
|
||||
*/
|
||||
if (dscale > 0)
|
||||
{
|
||||
*cp++ = '.';
|
||||
endcp = cp + dscale;
|
||||
for (i = 0; i < dscale; d++, i += DEC_DIGITS)
|
||||
{
|
||||
dig = (d >= 0 && d < var->ndigits) ? var->digits[d] : 0;
|
||||
d1 = dig / 1000;
|
||||
dig -= d1 * 1000;
|
||||
*cp++ = (char) (d1 + '0');
|
||||
d1 = dig / 100;
|
||||
dig -= d1 * 100;
|
||||
*cp++ = (char) (d1 + '0');
|
||||
d1 = dig / 10;
|
||||
dig -= d1 * 10;
|
||||
*cp++ = (char) (d1 + '0');
|
||||
*cp++ = (char) (dig + '0');
|
||||
}
|
||||
cp = endcp;
|
||||
}
|
||||
|
||||
/*
|
||||
* terminate the string and return it
|
||||
*/
|
||||
*cp = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,493 @@
|
||||
|
||||
/*
|
||||
* param.c
|
||||
* Management functions for the PGparam object.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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)
|
||||
{
|
||||
PGparam *param;
|
||||
PGtypeData *connData;
|
||||
|
||||
PQseterror(NULL);
|
||||
|
||||
if (!conn)
|
||||
{
|
||||
PQseterror("PGconn cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
param = (PGparam *) malloc(sizeof(PGparam));
|
||||
if (!param)
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(param, 0, sizeof(PGparam));
|
||||
|
||||
connData = (PGtypeData *) PQinstanceData(conn, pqt_eventproc);
|
||||
if (!connData)
|
||||
{
|
||||
PQparamClear(param);
|
||||
PQseterror("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(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(PQT_OUTOFMEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
param->typspeccnt = connData->typspeccnt;
|
||||
}
|
||||
|
||||
pqt_getfmtinfo(conn, ¶m->fmtinfo);
|
||||
return param;
|
||||
}
|
||||
|
||||
PQT_EXPORT PGparam *
|
||||
PQparamDup(PGparam *param)
|
||||
{
|
||||
PGparam *new_param;
|
||||
|
||||
PQseterror(NULL);
|
||||
|
||||
if (!param)
|
||||
{
|
||||
PQseterror("PGparam to duplicate cannot be NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_param = (PGparam *) malloc(sizeof(PGparam));
|
||||
if (!new_param)
|
||||
{
|
||||
PQseterror(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(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(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, 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, const char *format, ...)
|
||||
{
|
||||
int r;
|
||||
va_list ap;
|
||||
|
||||
/* This function is just a wrapper to PQputvf */
|
||||
va_start(ap, format);
|
||||
r = PQputvf(param, NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
PQputvf(PGparam *param, 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;
|
||||
|
||||
PQseterror(NULL);
|
||||
|
||||
if (!param)
|
||||
{
|
||||
PQseterror("PGparam cannot be NULL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!format || !*format)
|
||||
{
|
||||
PQseterror("param 'format' cannot be NULL or an empty string");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (stmtBuf && stmtBufLen < 1)
|
||||
{
|
||||
PQseterror("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("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("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(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.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, 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, 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(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;
|
||||
}
|
||||
else if (datal == 0)
|
||||
{
|
||||
/* BUG FIX: allows putting empty strings in binary mode. Without this,
|
||||
* empty strings would be converted to an SQL NULL.
|
||||
* Reported by: Dmitry Epstein
|
||||
*/
|
||||
static char zero[1] = {0};
|
||||
v->data = zero;
|
||||
}
|
||||
/* wants to put a direct pointer */
|
||||
else if (flags & TYPFLAG_POINTER)
|
||||
{
|
||||
v->data = (char *) data;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* need more mem for param value ptr */
|
||||
if (v->ptrl < datal)
|
||||
{
|
||||
char *ptr = (char *) pqt_realloc(v->ptr, datal);
|
||||
|
||||
if (!ptr)
|
||||
{
|
||||
PQseterror(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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
|
||||
/*
|
||||
* port.c
|
||||
* Portability functions. Some of these functions are drop-ins for
|
||||
* systems missing them and others just centralize differences.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
# define HAVE_VSNPRINTF
|
||||
#endif
|
||||
|
||||
int
|
||||
pqt_snprintf(char *buf, size_t size, const char *format, ...)
|
||||
{
|
||||
int n;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
n = pqt_vsnprintf(buf, size, format, ap);
|
||||
va_end(ap);
|
||||
return n;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_vsnprintf(char *buf, size_t size, const char *format, va_list ap)
|
||||
{
|
||||
int n;
|
||||
|
||||
#ifdef PQT_MSVC
|
||||
# if PQT_MSVC >= 1400
|
||||
/* MSVC 8 */
|
||||
n = _vsnprintf_s(buf, size, size-1, format, ap);
|
||||
# else
|
||||
/* MSVC 6 or 7 */
|
||||
n = _vsnprintf(buf, size, format, ap);
|
||||
# endif
|
||||
|
||||
#elif defined(HAVE_VSNPRINTF)
|
||||
/* All other platforms, including MinGW and Cygwin. */
|
||||
n = vsnprintf(buf, size, format, ap);
|
||||
#else
|
||||
/* Some platforms don't have the buffer-safe version */
|
||||
n = vsprintf(buf, format, ap);
|
||||
#endif
|
||||
|
||||
if (n > -1 && (size_t) n < size)
|
||||
return n;
|
||||
|
||||
/* Although some implementations return "required" buf size, this
|
||||
* always return -1 to keep things consistent for caller.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(HAVE_CONFIG_H) && (!defined(HAVE_STRTOL) || \
|
||||
!defined(HAVE_STRTOUL))
|
||||
|
||||
static unsigned long string2long(const char *nptr, char **endptr,
|
||||
int base, int is_signed);
|
||||
|
||||
#ifndef HAVE_STRTOL
|
||||
long
|
||||
strtol(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
return (signed long) string2long(nptr, endptr, base, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRTOUL
|
||||
unsigned long
|
||||
strtoul(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
return (unsigned long) string2long(nptr, endptr, base, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define between(a, c, z) \
|
||||
((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a)))
|
||||
|
||||
static unsigned long
|
||||
string2long(const char *nptr, char ** const endptr,
|
||||
int base, int is_signed)
|
||||
{
|
||||
unsigned int v;
|
||||
unsigned long val = 0;
|
||||
int c;
|
||||
int ovfl = 0, sign = 1;
|
||||
const char *startnptr = nptr, *nrstart;
|
||||
|
||||
if (endptr)
|
||||
*endptr = (char *) nptr;
|
||||
|
||||
while (isspace(*nptr))
|
||||
nptr++;
|
||||
c = *nptr;
|
||||
|
||||
if (c == '-' || c == '+')
|
||||
{
|
||||
if (c == '-')
|
||||
sign = -1;
|
||||
nptr++;
|
||||
}
|
||||
nrstart = nptr; /* start of the number */
|
||||
|
||||
/* When base is 0, the syntax determines the actual base */
|
||||
if (base == 0)
|
||||
{
|
||||
if (*nptr == '0')
|
||||
{
|
||||
if (*++nptr == 'x' || *nptr == 'X')
|
||||
{
|
||||
base = 16;
|
||||
nptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
base = 8;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base = 10;
|
||||
}
|
||||
}
|
||||
else if (base==16 && *nptr=='0' && (*++nptr =='x' || *nptr =='X'))
|
||||
{
|
||||
nptr++;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
c = *nptr;
|
||||
if (between('0', c, '9'))
|
||||
v = c - '0';
|
||||
else if (between('a', c, 'z'))
|
||||
v = c - 'a' + 0xa;
|
||||
else if (between('A', c, 'Z'))
|
||||
v = c - 'A' + 0xA;
|
||||
else
|
||||
break;
|
||||
|
||||
if (v >= base)
|
||||
break;
|
||||
if (val > (ULONG_MAX - v) / base)
|
||||
ovfl++;
|
||||
|
||||
val = (val * base) + v;
|
||||
nptr++;
|
||||
}
|
||||
|
||||
if (endptr)
|
||||
{
|
||||
if (nrstart == nptr)
|
||||
*endptr = (char *) startnptr;
|
||||
else
|
||||
*endptr = (char *) nptr;
|
||||
}
|
||||
|
||||
if (!ovfl)
|
||||
{
|
||||
/* Overflow is only possible when converting a signed long. */
|
||||
if (is_signed && ((sign < 0 && val > -(unsigned long) LONG_MIN)
|
||||
|| (sign > 0 && val > LONG_MAX)))
|
||||
ovfl++;
|
||||
}
|
||||
|
||||
if (ovfl)
|
||||
{
|
||||
errno = ERANGE;
|
||||
if (is_signed)
|
||||
{
|
||||
if (sign < 0)
|
||||
return LONG_MIN;
|
||||
else
|
||||
return LONG_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ULONG_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
return (long) sign * val;
|
||||
}
|
||||
|
||||
#endif /* !strtol || !strtoul */
|
||||
|
||||
|
||||
/* Non-windows platforms that don't have strtoll */
|
||||
#if defined(HAVE_CONFIG_H) && !defined(HAVE_STRTOLL)
|
||||
|
||||
#define MIN_INT64 (-MAX_INT64 - PQT_INT64CONST(1))
|
||||
#define MAX_INT64 PQT_INT64CONST(9223372036854775807)
|
||||
|
||||
/* no locale support */
|
||||
long long int
|
||||
strtoll(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const char *s;
|
||||
/* LONGLONG */
|
||||
long long int acc, cutoff;
|
||||
int c;
|
||||
int neg, any, cutlim;
|
||||
|
||||
/* endptr may be NULL */
|
||||
|
||||
#ifdef __GNUC__
|
||||
/* This outrageous construct just to shut up a GCC warning. */
|
||||
(void) &acc; (void) &cutoff;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Skip white space and pick up leading +/- sign if any.
|
||||
* If base is 0, allow 0x for hex and 0 for octal, else
|
||||
* assume decimal; if base is already 16, allow 0x.
|
||||
*/
|
||||
s = nptr;
|
||||
do {
|
||||
c = (unsigned char) *s++;
|
||||
} while (isspace(c));
|
||||
if (c == '-') {
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
} else {
|
||||
neg = 0;
|
||||
if (c == '+')
|
||||
c = *s++;
|
||||
}
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X')) {
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
/*
|
||||
* Compute the cutoff value between legal numbers and illegal
|
||||
* numbers. That is the largest legal value, divided by the
|
||||
* base. An input number that is greater than this value, if
|
||||
* followed by a legal input character, is too big. One that
|
||||
* is equal to this value may be valid or not; the limit
|
||||
* between valid and invalid numbers is then based on the last
|
||||
* digit. For instance, if the range for long longs is
|
||||
* [-9223372036854775808..9223372036854775807] and the input base
|
||||
* is 10, cutoff will be set to 922337203685477580 and cutlim to
|
||||
* either 7 (neg==0) or 8 (neg==1), meaning that if we have
|
||||
* accumulated a value > 922337203685477580, or equal but the
|
||||
* next digit is > 7 (or 8), the number is too big, and we will
|
||||
* return a range error.
|
||||
*
|
||||
* Set any if any `digits' consumed; make it negative to indicate
|
||||
* overflow.
|
||||
*/
|
||||
cutoff = neg ? MIN_INT64 : MAX_INT64;
|
||||
cutlim = (int) (cutoff % base);
|
||||
cutoff /= base;
|
||||
if (neg) {
|
||||
if (cutlim > 0) {
|
||||
cutlim -= base;
|
||||
cutoff += 1;
|
||||
}
|
||||
cutlim = -cutlim;
|
||||
}
|
||||
for (acc = 0, any = 0;; c = (unsigned char) *s++) {
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0)
|
||||
continue;
|
||||
if (neg) {
|
||||
if (acc < cutoff || (acc == cutoff && c > cutlim)) {
|
||||
any = -1;
|
||||
acc = MIN_INT64;
|
||||
errno = ERANGE;
|
||||
} else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc -= c;
|
||||
}
|
||||
} else {
|
||||
if (acc > cutoff || (acc == cutoff && c > cutlim)) {
|
||||
any = -1;
|
||||
acc = MAX_INT64;
|
||||
errno = ERANGE;
|
||||
} else {
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (endptr != 0)
|
||||
/* LINTED interface specification */
|
||||
*endptr = (char *) (any ? s - 1 : nptr);
|
||||
return (acc);
|
||||
}
|
||||
|
||||
#endif /* !HAVE_STRTOLL */
|
||||
|
||||
|
||||
/* Non-windows machines missing getaddrinfo (postgres's port) */
|
||||
#if defined(__CYGWIN__) || (defined(HAVE_CONFIG_H) && \
|
||||
!defined(HAVE_GETADDRINFO))
|
||||
#undef FRONTEND
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "getaddrinfo.h"
|
||||
|
||||
extern int h_errno;
|
||||
|
||||
/*
|
||||
* get address info for ipv4 sockets.
|
||||
*
|
||||
* Bugs: - only one addrinfo is set even though hintp is NULL or
|
||||
* ai_socktype is 0
|
||||
* - AI_CANONNAME is not supported.
|
||||
* - servname can only be a number, not text.
|
||||
*/
|
||||
int
|
||||
getaddrinfo(const char *node, const char *service,
|
||||
const struct addrinfo * hintp,
|
||||
struct addrinfo ** res)
|
||||
{
|
||||
struct addrinfo *ai;
|
||||
struct sockaddr_in sin,
|
||||
*psin;
|
||||
struct addrinfo hints;
|
||||
|
||||
if (hintp == NULL)
|
||||
{
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
}
|
||||
else
|
||||
memcpy(&hints, hintp, sizeof(hints));
|
||||
|
||||
if (hints.ai_family != AF_INET && hints.ai_family != AF_UNSPEC)
|
||||
return EAI_FAMILY;
|
||||
|
||||
if (hints.ai_socktype == 0)
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
if (!node && !service)
|
||||
return EAI_NONAME;
|
||||
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
|
||||
if (node)
|
||||
{
|
||||
if (node[0] == '\0')
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
else if (hints.ai_flags & AI_NUMERICHOST)
|
||||
{
|
||||
if (!inet_aton(node, &sin.sin_addr))
|
||||
return EAI_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct hostent *hp;
|
||||
|
||||
#ifdef FRONTEND
|
||||
struct hostent hpstr;
|
||||
char buf[BUFSIZ];
|
||||
int herrno = 0;
|
||||
|
||||
pqGethostbyname(node, &hpstr, buf, sizeof(buf),
|
||||
&hp, &herrno);
|
||||
#else
|
||||
hp = gethostbyname(node);
|
||||
#endif
|
||||
if (hp == NULL)
|
||||
{
|
||||
switch (h_errno)
|
||||
{
|
||||
case HOST_NOT_FOUND:
|
||||
case NO_DATA:
|
||||
return EAI_NONAME;
|
||||
case TRY_AGAIN:
|
||||
return EAI_AGAIN;
|
||||
case NO_RECOVERY:
|
||||
default:
|
||||
return EAI_FAIL;
|
||||
}
|
||||
}
|
||||
if (hp->h_addrtype != AF_INET)
|
||||
return EAI_FAIL;
|
||||
|
||||
memcpy(&(sin.sin_addr), hp->h_addr, hp->h_length);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hints.ai_flags & AI_PASSIVE)
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
else
|
||||
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
}
|
||||
|
||||
if (service)
|
||||
sin.sin_port = htons((unsigned short) atoi(service));
|
||||
|
||||
#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN
|
||||
sin.sin_len = sizeof(sin);
|
||||
#endif
|
||||
|
||||
ai = malloc(sizeof(*ai));
|
||||
if (!ai)
|
||||
return EAI_MEMORY;
|
||||
|
||||
psin = malloc(sizeof(*psin));
|
||||
if (!psin)
|
||||
{
|
||||
free(ai);
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(psin, &sin, sizeof(*psin));
|
||||
|
||||
ai->ai_flags = 0;
|
||||
ai->ai_family = AF_INET;
|
||||
ai->ai_socktype = hints.ai_socktype;
|
||||
ai->ai_protocol = hints.ai_protocol;
|
||||
ai->ai_addrlen = sizeof(*psin);
|
||||
ai->ai_addr = (struct sockaddr *) psin;
|
||||
ai->ai_canonname = NULL;
|
||||
ai->ai_next = NULL;
|
||||
|
||||
*res = ai;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
freeaddrinfo(struct addrinfo * res)
|
||||
{
|
||||
if (res)
|
||||
{
|
||||
if (res->ai_addr)
|
||||
free(res->ai_addr);
|
||||
free(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
gai_strerror(int errcode)
|
||||
{
|
||||
#ifdef HAVE_HSTRERROR
|
||||
int hcode;
|
||||
|
||||
switch (errcode)
|
||||
{
|
||||
case EAI_NONAME:
|
||||
hcode = HOST_NOT_FOUND;
|
||||
break;
|
||||
case EAI_AGAIN:
|
||||
hcode = TRY_AGAIN;
|
||||
break;
|
||||
case EAI_FAIL:
|
||||
default:
|
||||
hcode = NO_RECOVERY;
|
||||
break;
|
||||
}
|
||||
|
||||
return hstrerror(hcode);
|
||||
#else /* !HAVE_HSTRERROR */
|
||||
|
||||
switch (errcode)
|
||||
{
|
||||
case EAI_NONAME:
|
||||
return "Unknown host";
|
||||
case EAI_AGAIN:
|
||||
return "Host name lookup failure";
|
||||
/* Errors below are probably WIN32 only */
|
||||
#ifdef EAI_BADFLAGS
|
||||
case EAI_BADFLAGS:
|
||||
return "Invalid argument";
|
||||
#endif
|
||||
#ifdef EAI_FAMILY
|
||||
case EAI_FAMILY:
|
||||
return "Address family not supported";
|
||||
#endif
|
||||
#ifdef EAI_MEMORY
|
||||
case EAI_MEMORY:
|
||||
return "Not enough memory";
|
||||
#endif
|
||||
#ifdef EAI_NODATA
|
||||
#ifndef WIN32_ONLY_COMPILER /* MSVC complains because another case has the
|
||||
* same value */
|
||||
case EAI_NODATA:
|
||||
return "No host data of that type was found";
|
||||
#endif
|
||||
#endif
|
||||
#ifdef EAI_SERVICE
|
||||
case EAI_SERVICE:
|
||||
return "Class type not found";
|
||||
#endif
|
||||
#ifdef EAI_SOCKTYPE
|
||||
case EAI_SOCKTYPE:
|
||||
return "Socket type not supported";
|
||||
#endif
|
||||
default:
|
||||
return "Unknown server error";
|
||||
}
|
||||
#endif /* HAVE_HSTRERROR */
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an ipv4 address to a hostname.
|
||||
*
|
||||
* Bugs: - Only supports NI_NUMERICHOST and NI_NUMERICSERV
|
||||
* It will never resolv a hostname.
|
||||
* - No IPv6 support.
|
||||
*/
|
||||
int
|
||||
getnameinfo(const struct sockaddr * sa, int salen,
|
||||
char *node, int nodelen,
|
||||
char *service, int servicelen, int flags)
|
||||
{
|
||||
/* Invalid arguments. */
|
||||
if (sa == NULL || (node == NULL && service == NULL))
|
||||
return EAI_FAIL;
|
||||
|
||||
/* We don't support those. */
|
||||
if ((node && !(flags & NI_NUMERICHOST))
|
||||
|| (service && !(flags & NI_NUMERICSERV)))
|
||||
return EAI_FAIL;
|
||||
|
||||
#if defined(HAVE_IPV6) || defined(AF_INET6)
|
||||
if (sa->sa_family == AF_INET6)
|
||||
return EAI_FAMILY;
|
||||
#endif
|
||||
|
||||
if (node)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (sa->sa_family == AF_INET)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = inet_ntoa(((struct sockaddr_in *) sa)->sin_addr);
|
||||
ret = pqt_snprintf(node, nodelen, "%s", p);
|
||||
}
|
||||
if (ret == -1)
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
|
||||
if (service)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (sa->sa_family == AF_INET)
|
||||
{
|
||||
ret = pqt_snprintf(service, servicelen, "%d",
|
||||
ntohs(((struct sockaddr_in *) sa)->sin_port));
|
||||
}
|
||||
if (ret == -1)
|
||||
return EAI_MEMORY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HAVE_GETADDRINFO */
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
/*
|
||||
* record.c
|
||||
* Type handler for the record/composite data type.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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_record(PGtypeArgs *args)
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
char *out;
|
||||
PGparam *param = va_arg(args->ap, PGparam *);
|
||||
|
||||
PUTNULLCHK(args, param);
|
||||
|
||||
/* watch for invalidation issues */
|
||||
if (param->vcnt > args->typhandler->nattrs)
|
||||
return args->errorf(args,
|
||||
"param value count is %d but server says it's %d",
|
||||
param->vcnt, args->typhandler->nattrs);
|
||||
|
||||
/* Auto-fill the remaining fields with SQL NULLs. This feature was
|
||||
* added because it was needed (by eSilo). We had a few cases where
|
||||
* we needed to append new fields to existing composites but wanted
|
||||
* to maintain backwards compatibility ... so some_func(mycomposite)
|
||||
* would continue to work even for older versions unaware of the
|
||||
* new composite fields. I guess for some this is unwanted behavior,
|
||||
* but I think the cases for it are much more common.
|
||||
*/
|
||||
if (param->vcnt < args->typhandler->nattrs)
|
||||
{
|
||||
int nattrs = args->typhandler->nattrs - param->vcnt;
|
||||
for (i=0; i < nattrs; i++)
|
||||
pqt_putparam(param, NULL, 0, 0, BINARYFMT,
|
||||
args->typhandler->attDescs[param->vcnt].attoid);
|
||||
}
|
||||
|
||||
/* column count, 4-byte integer */
|
||||
len = 4;
|
||||
|
||||
/* determine total byte count to ensure args->put.out is large enough */
|
||||
for (i=0; i < param->vcnt; i++)
|
||||
{
|
||||
len += (4 + 4); /* oid + len */
|
||||
|
||||
if (param->vals[i].datal > 0)
|
||||
len += param->vals[i].datal;
|
||||
}
|
||||
|
||||
/* ensure out buffer is large enough */
|
||||
if (args->put.expandBuffer(args, len) == -1)
|
||||
return -1;
|
||||
|
||||
out = args->put.out;
|
||||
|
||||
/* write column count */
|
||||
pqt_buf_putint4(out, param->vcnt);
|
||||
out += 4;
|
||||
|
||||
for (i=0; i < param->vcnt; i++)
|
||||
{
|
||||
if (param->vals[i].format == 0)
|
||||
return args->errorf(args,
|
||||
"Cannot put composite attributes in text format");
|
||||
|
||||
if (param->vals[i].datal == NULL_LEN)
|
||||
param->vals[i].oid = args->typhandler->attDescs[i].attoid;
|
||||
|
||||
/* watch for invalidation issues */
|
||||
if (param->vals[i].oid != args->typhandler->attDescs[i].attoid)
|
||||
return args->errorf(args,
|
||||
"param value OID is %u but server says it's %u",
|
||||
param->vals[i].oid, args->typhandler->attDescs[i].attoid);
|
||||
|
||||
/* write column oid */
|
||||
pqt_buf_putint4(out, param->vals[i].oid);
|
||||
out += 4;
|
||||
|
||||
/* write column data length */
|
||||
pqt_buf_putint4(out, param->vals[i].datal);
|
||||
out += 4;
|
||||
|
||||
/* write the column data */
|
||||
if (param->vals[i].data && param->vals[i].datal > 0)
|
||||
{
|
||||
memcpy(out, param->vals[i].data, param->vals[i].datal);
|
||||
out += param->vals[i].datal;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_record(PGtypeArgs *args)
|
||||
{
|
||||
int i;
|
||||
int nattrs;
|
||||
int vlen;
|
||||
Oid server_oid;
|
||||
DECLVALUE(args);
|
||||
PGresult *res;
|
||||
PGresult **resultp = va_arg(args->ap, PGresult **);
|
||||
|
||||
CHKGETVALS(args, resultp);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
return args->errorf(args, "record does not support text results");
|
||||
|
||||
/* get record column count, numAttributes */
|
||||
nattrs = pqt_buf_getint4(value);
|
||||
value += 4;
|
||||
|
||||
/* watch for invalidation issues */
|
||||
if (args->typhandler->nattrs != nattrs)
|
||||
return args->errorf(args,
|
||||
"type handler attribute count is %d but server says it's %d",
|
||||
args->typhandler->nattrs, nattrs);
|
||||
|
||||
if (!(res = pqt_copyresult(args, nattrs)))
|
||||
RERR_MEM(args);
|
||||
|
||||
for (i=0; i < nattrs; i++)
|
||||
{
|
||||
/* watch for invalidation issues */
|
||||
server_oid = (Oid) pqt_buf_getint4(value);
|
||||
if (server_oid != args->typhandler->attDescs[i].attoid)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* move past Oid */
|
||||
value += 4;
|
||||
|
||||
/* get value length */
|
||||
vlen = pqt_buf_getint4(value);
|
||||
value += 4;
|
||||
|
||||
if (!PQsetvalue(res, 0, i, value, vlen))
|
||||
{
|
||||
PQclear(res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (vlen > 0)
|
||||
value += vlen;
|
||||
}
|
||||
|
||||
*resultp = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,660 @@
|
||||
|
||||
/*
|
||||
* spec.c
|
||||
* Type Specifier parser and compiler.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
/* For use with pqt_parse */
|
||||
#define CHKSTMTBUF(nbytes_add) do{ \
|
||||
if ((*stmtPos + (nbytes_add)) >= stmtBufLen) \
|
||||
{ \
|
||||
PQseterror("statement buffer is too small"); \
|
||||
return FALSE; \
|
||||
} \
|
||||
}while (0)
|
||||
|
||||
/* For use with PQspecPrepare */
|
||||
#define FREESTMTBUF do{ \
|
||||
if (stmtBuf && stmtBuf != buffer) \
|
||||
free(stmtBuf); \
|
||||
}while(0)
|
||||
|
||||
static char *
|
||||
skipQuotes(char *s);
|
||||
|
||||
static char *
|
||||
parseId(char *id, char **start, int *len, int *flags, int typpos);
|
||||
|
||||
static int
|
||||
expandSpecs(PGtypeData *typeData);
|
||||
|
||||
int
|
||||
PQspecPrepare(PGconn *conn, const char *name,
|
||||
const char *format, int is_stmt)
|
||||
{
|
||||
int flags;
|
||||
int typpos = 0;
|
||||
int idmax = 0;
|
||||
size_t stmtPos = 0;
|
||||
PGtypeHandler *h;
|
||||
PGtypeData *typeData;
|
||||
PGtypeSpec *spec;
|
||||
size_t stmtBufLen = 0;
|
||||
char *stmtBuf = NULL;
|
||||
char buffer[8192];
|
||||
|
||||
if (!conn)
|
||||
{
|
||||
PQseterror("PGConn cannot be NULL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!name || !*name)
|
||||
{
|
||||
PQseterror("Prepared specifier name cannot be NULL or an empty string");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (format && !*format)
|
||||
{
|
||||
PQseterror("Specifier format string cannot be empty");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!isalnum(*name) && *name != '_')
|
||||
{
|
||||
PQseterror("Prepared specifier name must begin with an alpha, "
|
||||
"number or underscore.");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typeData = PQinstanceData(conn, pqt_eventproc);
|
||||
if (!typeData)
|
||||
{
|
||||
PQseterror("No type data exists for PGconn at %p", conn);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* This is a removal request */
|
||||
if (!format)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < typeData->typspeccnt; i++)
|
||||
{
|
||||
if (strcmp(typeData->typspecs[i].name, name) == 0)
|
||||
{
|
||||
/* clear it */
|
||||
pqt_clearspec(&typeData->typspecs[i]);
|
||||
|
||||
/* remove from list, not needed if its the last element */
|
||||
if (i != typeData->typspeccnt - 1)
|
||||
memmove(typeData->typspecs + i, typeData->typspecs + i + 1,
|
||||
(typeData->typspeccnt - i - 1) * sizeof(PGtypeSpec));
|
||||
|
||||
typeData->typspeccnt--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* always return TRUE, an error is not useful here */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Already exists case */
|
||||
spec = pqt_getspec(typeData->typspecs, typeData->typspeccnt, name);
|
||||
if (spec)
|
||||
{
|
||||
PQseterror("Prepared spec already exists '%s'", name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Make sure specs array is large enough */
|
||||
if (!expandSpecs(typeData))
|
||||
return FALSE;
|
||||
|
||||
spec = &typeData->typspecs[typeData->typspeccnt];
|
||||
|
||||
/* cache statement along with prepared type spec */
|
||||
if (is_stmt)
|
||||
{
|
||||
stmtBufLen = strlen(format) + 1;
|
||||
|
||||
/* no room in stack, use heap */
|
||||
if (stmtBufLen > sizeof(buffer))
|
||||
{
|
||||
stmtBuf = (char *) malloc(stmtBufLen);
|
||||
if (!stmtBuf)
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stmtBuf = buffer;
|
||||
stmtBufLen = sizeof(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
while (format && *format)
|
||||
{
|
||||
format = pqt_parse(format, typeData->typhandlers, typeData->typhcnt,
|
||||
stmtBuf, stmtBufLen, &h, &stmtPos, &typpos, &flags);
|
||||
|
||||
if (!format)
|
||||
{
|
||||
pqt_clearspec(spec);
|
||||
FREESTMTBUF;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* skipped harmless chars in format, like quoted sections. */
|
||||
if(!h)
|
||||
continue;
|
||||
|
||||
if (!spec->idlist || spec->idcnt == idmax)
|
||||
{
|
||||
int c = idmax ? idmax * 2 : 8;
|
||||
void *p = pqt_realloc(spec->idlist, c * sizeof(int));
|
||||
|
||||
if (!p)
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
pqt_clearspec(spec);
|
||||
FREESTMTBUF;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
spec->idlist = (int *) p;
|
||||
|
||||
p = pqt_realloc(spec->flags, c * sizeof(char));
|
||||
if (!p)
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
pqt_clearspec(spec);
|
||||
FREESTMTBUF;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
spec->flags = (unsigned char *) p;
|
||||
idmax = c;
|
||||
}
|
||||
|
||||
/* Parallel arrays, every handler needs type flags */
|
||||
spec->idlist[spec->idcnt] = h->id;
|
||||
spec->flags[spec->idcnt++] = (unsigned char) flags;
|
||||
}
|
||||
|
||||
/* terminate stmtBuf, guarenteed to have room for NUL */
|
||||
if (stmtBuf)
|
||||
stmtBuf[stmtPos] = 0;
|
||||
|
||||
/* copy name string */
|
||||
spec->name = strdup(name);
|
||||
if (!spec->name)
|
||||
{
|
||||
pqt_clearspec(spec);
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
FREESTMTBUF;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* copy the parameterized stmt string */
|
||||
if (stmtBuf)
|
||||
{
|
||||
spec->stmt = strdup(stmtBuf);
|
||||
if (!spec->stmt)
|
||||
{
|
||||
pqt_clearspec(spec);
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
FREESTMTBUF;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
FREESTMTBUF;
|
||||
|
||||
/* Success, increment type spec count */
|
||||
typeData->typspeccnt++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
PQclearSpecs(PGconn *conn)
|
||||
{
|
||||
PGtypeData *typeData;
|
||||
|
||||
if (!conn)
|
||||
{
|
||||
PQseterror("PGConn cannot be NULL");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
typeData = PQinstanceData(conn, pqt_eventproc);
|
||||
if (!typeData)
|
||||
{
|
||||
PQseterror("No type data exists for PGconn at %p", conn);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pqt_freespecs(typeData->typspecs, typeData->typspeccnt);
|
||||
typeData->typspecs = NULL;
|
||||
typeData->typspeccnt = 0;
|
||||
typeData->typspecmax = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *pqt_parse(const char *format, PGtypeHandler *h, int hcnt,
|
||||
char *stmtBuf, size_t stmtBufLen, PGtypeHandler **out, size_t *stmtPos,
|
||||
int *typpos, int *flags)
|
||||
{
|
||||
int specMark;
|
||||
char *s = skipQuotes((char *) format);
|
||||
char typname[PQT_MAXIDLEN + 1];
|
||||
char schema[PQT_MAXIDLEN + 1];
|
||||
char tmp[200];
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
|
||||
/* found quotes to skip */
|
||||
if (s != format)
|
||||
{
|
||||
if (stmtBuf)
|
||||
{
|
||||
size_t n = s - format;
|
||||
CHKSTMTBUF(n);
|
||||
memcpy(stmtBuf + *stmtPos, format, n);
|
||||
(*stmtPos) += n;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
specMark = *format;
|
||||
if (specMark != '%' && specMark != '#')
|
||||
{
|
||||
if (stmtBuf)
|
||||
{
|
||||
CHKSTMTBUF(1);
|
||||
stmtBuf[*stmtPos] = *format;
|
||||
(*stmtPos)++;
|
||||
}
|
||||
|
||||
format++;
|
||||
return (char *) format;
|
||||
}
|
||||
|
||||
/* spec skips % or # */
|
||||
if (!(s = pqt_parsetype(format + 1, schema, typname, flags, *typpos + 1)))
|
||||
return NULL;
|
||||
|
||||
if (*flags & TYPFLAG_INVALID)
|
||||
{
|
||||
if (stmtBuf)
|
||||
{
|
||||
CHKSTMTBUF(1);
|
||||
stmtBuf[*stmtPos] = *format++;
|
||||
(*stmtPos)++;
|
||||
PQseterror(NULL); /* set by pqt_parsetype */
|
||||
return (char *) format;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*typpos)++;
|
||||
|
||||
if (!(*out = pqt_gethandler(h, hcnt, schema, typname)))
|
||||
{
|
||||
PQseterror("Uknown type '%s' (position %d)",
|
||||
pqt_fqtn(tmp, sizeof(tmp), schema, typname), *typpos);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (stmtBuf)
|
||||
{
|
||||
int n = pqt_snprintf(tmp, sizeof(tmp), "$%d", *typpos);
|
||||
CHKSTMTBUF(n);
|
||||
memcpy(stmtBuf + *stmtPos, tmp, n);
|
||||
(*stmtPos) += n;
|
||||
}
|
||||
|
||||
if (!(*out)->typput)
|
||||
{
|
||||
PGtypeHandler *o = pqt_gethandlerbyid(h, hcnt, (*out)->base_id);
|
||||
if (!o || !o->typput)
|
||||
{
|
||||
PQseterror(
|
||||
"Type '%s' doesn't support put operations (position %d)",
|
||||
pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema,
|
||||
(*out)->typname), *typpos);
|
||||
|
||||
*out = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out = o;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if ((*flags & TYPFLAG_POINTER) && !pqt_allowsptr(*out))
|
||||
{
|
||||
PQseterror(
|
||||
"Type '%s' doesn't support putting pointers (position %d)",
|
||||
pqt_fqtn(tmp, sizeof(tmp), (*out)->typschema,
|
||||
(*out)->typname), *typpos);
|
||||
|
||||
*out = NULL;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (specMark == '#')
|
||||
(*flags) |= TYPFLAG_BYNAME;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void
|
||||
pqt_clearspec(PGtypeSpec *spec)
|
||||
{
|
||||
if (spec->name)
|
||||
free(spec->name);
|
||||
|
||||
if (spec->stmt)
|
||||
free(spec->stmt);
|
||||
|
||||
if (spec->idlist)
|
||||
free(spec->idlist);
|
||||
|
||||
if (spec->flags)
|
||||
free(spec->flags);
|
||||
|
||||
memset(spec, 0, sizeof(PGtypeSpec));
|
||||
}
|
||||
|
||||
PGtypeSpec *pqt_dupspecs(PGtypeSpec *specs, int count)
|
||||
{
|
||||
int i;
|
||||
PGtypeSpec *new_specs = (PGtypeSpec *) malloc(count * sizeof(PGtypeSpec));
|
||||
|
||||
if (!new_specs)
|
||||
return NULL;
|
||||
|
||||
memset(new_specs, 0, count * sizeof(PGtypeSpec));
|
||||
|
||||
for (i=0; i < count; i++)
|
||||
{
|
||||
PGtypeSpec *s = &specs[i];
|
||||
PGtypeSpec *news = &new_specs[i];
|
||||
|
||||
news->idcnt = s->idcnt;
|
||||
|
||||
news->name = strdup(s->name);
|
||||
if (!news->name)
|
||||
{
|
||||
pqt_freespecs(new_specs, i+1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(s->stmt)
|
||||
{
|
||||
news->stmt = strdup(s->stmt);
|
||||
if (!news->stmt)
|
||||
{
|
||||
pqt_freespecs(new_specs, i+1);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
news->idlist = (int *) malloc(s->idcnt * sizeof(int));
|
||||
if (!news->idlist)
|
||||
{
|
||||
pqt_freespecs(new_specs, i+1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(news->idlist, s->idlist, s->idcnt * sizeof(int));
|
||||
|
||||
news->flags = (unsigned char *) malloc(s->idcnt * sizeof(char));
|
||||
if (!news->flags)
|
||||
{
|
||||
pqt_freespecs(new_specs, i+1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(news->flags, s->flags, s->idcnt * sizeof(char));
|
||||
}
|
||||
|
||||
return new_specs;
|
||||
}
|
||||
|
||||
void pqt_freespecs(PGtypeSpec *specs, int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < count; i++)
|
||||
pqt_clearspec(&specs[i]);
|
||||
|
||||
if (specs)
|
||||
free(specs);
|
||||
}
|
||||
|
||||
PGtypeSpec *pqt_getspec(PGtypeSpec *specs, int count, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < count; i++)
|
||||
if (strcmp(specs[i].name, name) == 0)
|
||||
return &specs[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Parse a type identifer name (schema qualified or not) from spec. spec
|
||||
* must point to the first char after the % sign, which maybe a
|
||||
* double quote.
|
||||
*
|
||||
* spec - pointer to typname, just after the '%' or '#'
|
||||
* schema - buffer to receive schema (PQT_MAXIDLEN bytes)
|
||||
* typname - buffer to receive typname (PQT_MAXIDLEN bytes)
|
||||
* flags - a pointer to an int that is set one or more TYPFLAG_xxx
|
||||
* typpos - 1-based position of spec in specifier string (0 for unknown)
|
||||
*/
|
||||
char *
|
||||
pqt_parsetype(const char *spec, char *schema, char *typname,
|
||||
int *flags, int typpos)
|
||||
{
|
||||
int i;
|
||||
char *start;
|
||||
int len=0;
|
||||
char *s = (char *)spec;
|
||||
|
||||
if (!(s = parseId(s, &start, &len, flags, typpos)))
|
||||
return NULL;
|
||||
|
||||
/* not a valid specifer, false positive like "(x % y) = 0" */
|
||||
if (*flags & TYPFLAG_INVALID)
|
||||
return s;
|
||||
|
||||
*schema = 0;
|
||||
if (*s == '.')
|
||||
{
|
||||
memcpy(schema, start, len);
|
||||
schema[len] = 0;
|
||||
if (*flags & TYPFLAG_CASEFOLD)
|
||||
for (i=0; i < len; i++)
|
||||
schema[i] = pqt_tolower(schema[i]);
|
||||
|
||||
/* now get typname */
|
||||
if (!(s = parseId(++s, &start, &len, flags, typpos)))
|
||||
return NULL;
|
||||
|
||||
if (*flags & TYPFLAG_INVALID)
|
||||
return s;
|
||||
}
|
||||
|
||||
memcpy(typname, start, len);
|
||||
typname[len] = 0;
|
||||
if (*flags & TYPFLAG_CASEFOLD)
|
||||
for (i=0; i < len; i++)
|
||||
typname[i] = pqt_tolower(typname[i]);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static char *
|
||||
parseId(char *id, char **start, int *len, int *flags, int typpos)
|
||||
{
|
||||
char *p = id;
|
||||
|
||||
*flags = 0;
|
||||
*start = NULL;
|
||||
*len = 0;
|
||||
|
||||
if (*p == '"')
|
||||
p++;
|
||||
|
||||
/* check first character */
|
||||
if (!isalpha(*p) && *p != '_')
|
||||
{
|
||||
*flags |= TYPFLAG_INVALID;
|
||||
PQseterror(
|
||||
"Invalid first character for identifier '%c' (pos:%d)", *p, typpos);
|
||||
return p;
|
||||
}
|
||||
|
||||
if (*id == '"')
|
||||
{
|
||||
id++;
|
||||
if (!(p = strchr(id, '"')))
|
||||
{
|
||||
*flags |= TYPFLAG_INVALID;
|
||||
PQseterror("Unterminated double quote '%s' (pos:%d)",
|
||||
id-1, typpos);
|
||||
return p;
|
||||
}
|
||||
|
||||
*len = (int) (p - id);
|
||||
*start = id;
|
||||
p++;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (p=id+1; isalnum(*p) || *p=='_'; p++) ;
|
||||
|
||||
*len = (int) (p - id);
|
||||
*start = id;
|
||||
*flags |= TYPFLAG_CASEFOLD;
|
||||
}
|
||||
|
||||
/* range check */
|
||||
if (*len == 0 || *len > PQT_MAXIDLEN)
|
||||
{
|
||||
*flags |= TYPFLAG_INVALID;
|
||||
PQseterror("Identifier out of range %d (pos:%d), range is 1 to %d",
|
||||
*len, typpos, PQT_MAXIDLEN);
|
||||
return p;
|
||||
}
|
||||
|
||||
/* direct pointer request */
|
||||
if (*p == '*')
|
||||
{
|
||||
p++;
|
||||
*flags |= TYPFLAG_POINTER;
|
||||
}
|
||||
|
||||
/* Is this an array? Ex. %int4[] or %"a b"[] */
|
||||
if (p[0] == '[' && p[1] == ']')
|
||||
{
|
||||
if (*flags & TYPFLAG_POINTER)
|
||||
{
|
||||
PQseterror(
|
||||
"'*' specifer flag cannot be used with arrays[] '%s' (pos:%d)",
|
||||
id, typpos);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*flags |= TYPFLAG_ARRAY;
|
||||
p += 2;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
expandSpecs(PGtypeData *typeData)
|
||||
{
|
||||
int n;
|
||||
PGtypeSpec *specs;
|
||||
|
||||
if (typeData->typspeccnt < typeData->typspecmax)
|
||||
return TRUE;
|
||||
|
||||
n = typeData->typspecmax ? (typeData->typspecmax * 3) / 2 : 8;
|
||||
|
||||
specs = (PGtypeSpec *) pqt_realloc(
|
||||
typeData->typspecs, sizeof(PGtypeSpec) * n);
|
||||
|
||||
if (!specs)
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memset(specs + typeData->typspeccnt, 0,
|
||||
(n - typeData->typspeccnt) * sizeof(PGtypeSpec));
|
||||
|
||||
typeData->typspecs = specs;
|
||||
typeData->typspecmax = n;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* skip quoted strings. Doesn't need to account for E'' syntax. The
|
||||
* E is copied over prior to the quoted string.
|
||||
*
|
||||
* Returns a pointer to the next character after the closing quote or
|
||||
* NULL if there was an error.
|
||||
*/
|
||||
static char *
|
||||
skipQuotes(char *s)
|
||||
{
|
||||
char *end;
|
||||
|
||||
if (*s != '\'')
|
||||
return s;
|
||||
|
||||
end = s;
|
||||
while (*++end)
|
||||
{
|
||||
/* If we see a backslash, skip an extra char. No need to dig any
|
||||
* further since this method works with \digits and \hex.
|
||||
*/
|
||||
if (*end == '\\')
|
||||
end++;
|
||||
else if (*end == '\'')
|
||||
break;
|
||||
}
|
||||
|
||||
/* unterminated quote */
|
||||
if (!*end)
|
||||
{
|
||||
PQseterror("unterminated single quoted string");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ++end; /* skip ending quote */
|
||||
}
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
|
||||
/*
|
||||
* util.c
|
||||
* Utility functions.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
PGresult *
|
||||
pqt_copyresult(PGtypeArgs *args, int nattrs)
|
||||
{
|
||||
int i;
|
||||
PGresult *res;
|
||||
int tableid, columnid, format;
|
||||
PGresAttDesc *ad = (PGresAttDesc *) malloc(nattrs * sizeof(PGresAttDesc));
|
||||
|
||||
if (!ad)
|
||||
{
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tableid = PQftable(args->get.result, args->get.field_num);
|
||||
columnid = PQftablecol(args->get.result, args->get.field_num);
|
||||
format = PQfformat(args->get.result, args->get.field_num);
|
||||
|
||||
for (i=0; i < nattrs; i++)
|
||||
{
|
||||
ad[i].tableid = tableid;
|
||||
ad[i].columnid = columnid;
|
||||
ad[i].format = format;
|
||||
|
||||
/* simple array */
|
||||
if (args->typhandler->nattrs == 0)
|
||||
{
|
||||
ad[i].typid = args->typhandler->typoid;
|
||||
ad[i].typlen = args->typhandler->typlen;
|
||||
ad[i].name = NULL;
|
||||
ad[i].atttypmod = -1;
|
||||
}
|
||||
/* composite/record */
|
||||
else
|
||||
{
|
||||
ad[i].typid = args->typhandler->attDescs[i].attoid;
|
||||
ad[i].typlen = args->typhandler->attDescs[i].attlen;
|
||||
ad[i].name = args->typhandler->attDescs[i].attname;
|
||||
ad[i].atttypmod = args->typhandler->attDescs[i].atttypmod;
|
||||
}
|
||||
}
|
||||
|
||||
res = PQcopyResult(args->get.result,
|
||||
PG_COPYRES_EVENTS | PG_COPYRES_NOTICEHOOKS);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
free(ad);
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!PQsetResultAttrs(res, nattrs, ad))
|
||||
{
|
||||
PQclear(res);
|
||||
PQseterror(PQT_OUTOFMEMORY);
|
||||
res = NULL;
|
||||
}
|
||||
|
||||
free(ad);
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef STRICT_MEMORY_ALIGNMENT
|
||||
short
|
||||
pqt_buf_getint2(char *buffer)
|
||||
{
|
||||
short n;
|
||||
memcpy(&n, buffer, 2);
|
||||
return (short) ntohs(n);
|
||||
}
|
||||
|
||||
int
|
||||
pqt_buf_getint4(char *buffer)
|
||||
{
|
||||
int n;
|
||||
memcpy(&n, buffer, 4);
|
||||
return (int) ntohl(n);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
pqt_swap8(void *outp, void *inp, int tonet)
|
||||
{
|
||||
static int n = 1;
|
||||
|
||||
#ifdef STRICT_MEMORY_ALIGNMENT
|
||||
unsigned int in[2];
|
||||
unsigned int out[2];
|
||||
memcpy(&in, inp, 8);
|
||||
#else
|
||||
unsigned int *in = (unsigned int *) inp;
|
||||
unsigned int *out = (unsigned int *) outp;
|
||||
#endif
|
||||
|
||||
/* swap when needed */
|
||||
if (*(char *)&n == 1)
|
||||
{
|
||||
out[0] = (unsigned int) (tonet ? htonl(in[1]) : ntohl(in[1]));
|
||||
out[1] = (unsigned int) (tonet ? htonl(in[0]) : ntohl(in[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
out[0] = in[0];
|
||||
out[1] = in[1];
|
||||
}
|
||||
|
||||
#ifdef STRICT_MEMORY_ALIGNMENT
|
||||
memcpy(outp, out, 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
pqt_text_to_int8(char *val, void *out)
|
||||
{
|
||||
PGint8 n;
|
||||
|
||||
/* NOTE: port version of strtoll in port.c. */
|
||||
errno = 0;
|
||||
if ((n = (PGint8) strtoll(val, NULL, 10)) == 0 && errno)
|
||||
return -1;
|
||||
|
||||
*(PGint8 *) out = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_text_to_float8(double *f8, char *text, char **endptr)
|
||||
{
|
||||
double d;
|
||||
|
||||
errno = 0;
|
||||
if ((d = strtod(text, endptr)) == 0 && errno)
|
||||
return 0;
|
||||
|
||||
*f8 = d;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Checks buffer and truncates 'src' if 'dest' is too small. */
|
||||
char *
|
||||
pqt_strcpy(char *dest, size_t size, const char *src)
|
||||
{
|
||||
size_t src_len = strlen(src);
|
||||
|
||||
/* truncate if needed */
|
||||
if (src_len >= size)
|
||||
src_len = size - 1;
|
||||
|
||||
memcpy(dest, src, src_len);
|
||||
dest[src_len] = 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------
|
||||
* Everything below was taken from postgresql project
|
||||
* A couple of changes here and there.
|
||||
*/
|
||||
|
||||
#ifndef HIGHBIT
|
||||
# define HIGHBIT (0x80)
|
||||
#endif
|
||||
|
||||
#ifndef IS_HIGHBIT_SET
|
||||
# define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fold a character to lower case.
|
||||
*
|
||||
* Unlike some versions of tolower(), this is safe to apply to characters
|
||||
* that aren't upper case letters. Note however that the whole thing is
|
||||
* a bit bogus for multibyte character sets.
|
||||
*/
|
||||
unsigned char
|
||||
pqt_tolower(unsigned char ch)
|
||||
{
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
ch += 'a' - 'A';
|
||||
else if (IS_HIGHBIT_SET(ch) && isupper(ch))
|
||||
ch = (unsigned char) tolower(ch);
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*
|
||||
* Case-independent comparison of two null-terminated strings.
|
||||
*/
|
||||
int
|
||||
pqt_strcasecmp(const char *s1, const char *s2)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
unsigned char ch1 = (unsigned char) *s1++;
|
||||
unsigned char ch2 = (unsigned char) *s2++;
|
||||
|
||||
if (ch1 != ch2)
|
||||
{
|
||||
if (ch1 >= 'A' && ch1 <= 'Z')
|
||||
ch1 += 'a' - 'A';
|
||||
else if (IS_HIGHBIT_SET(ch1) && isupper(ch1))
|
||||
ch1 = (unsigned char)tolower(ch1);
|
||||
|
||||
if (ch2 >= 'A' && ch2 <= 'Z')
|
||||
ch2 += 'a' - 'A';
|
||||
else if (IS_HIGHBIT_SET(ch2) && isupper(ch2))
|
||||
ch2 = (unsigned char)tolower(ch2);
|
||||
|
||||
if (ch1 != ch2)
|
||||
return (int) ch1 - (int) ch2;
|
||||
}
|
||||
if (ch1 == 0)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Case-independent comparison of two not-necessarily-null-terminated
|
||||
* strings. At most n bytes will be examined from each string.
|
||||
*/
|
||||
int
|
||||
pqt_strncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
while (n-- > 0)
|
||||
{
|
||||
unsigned char ch1 = (unsigned char) *s1++;
|
||||
unsigned char ch2 = (unsigned char) *s2++;
|
||||
|
||||
if (ch1 != ch2)
|
||||
{
|
||||
if (ch1 >= 'A' && ch1 <= 'Z')
|
||||
ch1 += 'a' - 'A';
|
||||
else if (IS_HIGHBIT_SET(ch1) && isupper(ch1))
|
||||
ch1 = (unsigned char)tolower(ch1);
|
||||
|
||||
if (ch2 >= 'A' && ch2 <= 'Z')
|
||||
ch2 += 'a' - 'A';
|
||||
else if (IS_HIGHBIT_SET(ch2) && isupper(ch2))
|
||||
ch2 = (unsigned char)tolower(ch2);
|
||||
|
||||
if (ch1 != ch2)
|
||||
return (int) ch1 - (int) ch2;
|
||||
}
|
||||
if (ch1 == 0)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
/*
|
||||
* varlena.c
|
||||
* Type handler for the variable length data types.
|
||||
*
|
||||
* Copyright (c) 2008-2015 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"
|
||||
|
||||
/* put a type in its string representation, text format */
|
||||
int
|
||||
pqt_put_str(PGtypeArgs *args)
|
||||
{
|
||||
args->format = TEXTFMT;
|
||||
args->put.out = va_arg(args->ap, char *);
|
||||
return args->put.out ? (int)strlen(args->put.out)+1 : 0;
|
||||
}
|
||||
|
||||
/* varchar, bpchar and name use the text handlers */
|
||||
int
|
||||
pqt_put_text(PGtypeArgs *args)
|
||||
{
|
||||
args->put.out = va_arg(args->ap, PGtext);
|
||||
return args->put.out ? (int)strlen(args->put.out) : 0;
|
||||
}
|
||||
|
||||
/* varchar, bpchar and name use the text handlers */
|
||||
int
|
||||
pqt_get_text(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
PGtext *textp = va_arg(args->ap, PGtext *);
|
||||
CHKGETVALS(args, textp);
|
||||
*textp = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_put_bytea(PGtypeArgs *args)
|
||||
{
|
||||
PGbytea *bytea = va_arg(args->ap, PGbytea *);
|
||||
PUTNULLCHK(args, bytea);
|
||||
args->put.out = bytea->data;
|
||||
return bytea->len;
|
||||
}
|
||||
|
||||
int
|
||||
pqt_get_bytea(PGtypeArgs *args)
|
||||
{
|
||||
DECLVALUE(args);
|
||||
DECLLENGTH(args);
|
||||
PGbytea *bytea = va_arg(args->ap, PGbytea *);
|
||||
|
||||
CHKGETVALS(args, bytea);
|
||||
|
||||
if (args->format == TEXTFMT)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
value = (char *) PQunescapeBytea((const unsigned char *) value, &len);
|
||||
if (!value)
|
||||
RERR_STR2INT(args);
|
||||
|
||||
bytea->data = (char *) PQresultAlloc(args->get.result, len);
|
||||
if (!bytea->data)
|
||||
{
|
||||
PQfreemem(value);
|
||||
RERR_MEM(args);
|
||||
}
|
||||
|
||||
bytea->len = (int) len;
|
||||
memcpy(bytea->data, value, len);
|
||||
PQfreemem(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* binary format */
|
||||
bytea->len = valuel;
|
||||
bytea->data = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user