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:
pgagarinov
2016-01-22 14:27:33 +03:00
commit 08ba30e8f9
77 changed files with 17222 additions and 0 deletions
+278
View File
@@ -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
+330
View File
@@ -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);
}
+173
View File
@@ -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);
}
}
+562
View File
@@ -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;
}
+391
View File
@@ -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;
}
+146
View File
@@ -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 */
+1077
View File
File diff suppressed because it is too large Load Diff
+533
View File
@@ -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
+468
View File
@@ -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 */
+199
View File
@@ -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;
}
+257
View File
@@ -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;
}
+838
View File
@@ -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;
}
+493
View File
@@ -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, &param->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, &param->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 = &param->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 = &param->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 = &param->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;
}
+587
View File
@@ -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 */
+164
View File
@@ -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
+660
View File
@@ -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 */
}
+265
View File
@@ -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;
}
+88
View File
@@ -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;
}