libpqtypes - error.c

Home Page

array.c
datetime.c
error.c
events.c
exec.c
geo.c
getaddrinfo.h
handler.c
libpqtypes-int.h
libpqtypes.h
misc.c
network.c
numerics.c
param.c
port.c
record.c
regression-test.c
spec.c
utils.c
varlena.c
  1 
  2 /*
  3  * error.c
  4  *   The functions in this file represent the libpqtypes error
  5  *   system.  It offers the API user the ability to set/get errors.
  6  *   The error system uses a per-thread global error, implemented
  7  *   using TLS (via declspec(thread) or pthread TLS).  For
  8  *   non-PQT_THREAD_SAFE builds, a static buffer is used.
  9  *
 10  * Copyright (c) 2011 eSilo, LLC. All rights reserved.
 11  * This is free software; see the source for copying conditions.  There is
 12  * NO warranty; not even for MERCHANTABILITY or  FITNESS FOR A  PARTICULAR
 13  * PURPOSE.
 14  */
 15 
 16 #include "libpqtypes-int.h"
 17 
 18 /* Cannot use allocated memory for the pqterr_t members.  This is
 19  * because windows has no way of freeing per thread allocated
 20  * memory automatically, other than DllMain which won't work for
 21  * a third-party library such as libpqtypes.  All error messages
 22  * and fields are truncated to fit their buffers via pqt_strcpy().
 23  */
 24 typedef struct
 25 {
 26   /* Room for user message and a full libpq message */
 27   char msg[1024 + 8192];
 28 
 29   /* Error Fields (a little less than 8K for all) */
 30   char severity[16];
 31   char sqlstate[16];
 32   char message_primary[2048];
 33   char message_detail[1024];
 34   char message_hint[512];
 35   char stmt_position[16];
 36   char internal_position[16];
 37   char internal_query[2048];
 38   char context[2048];
 39   char source_file[256];
 40   char source_line[16];
 41   char source_function[80];
 42 } pqterr_t;
 43 
 44 /* non thread-safe, use a static */
 45 #ifndef PQT_THREAD_SAFE
 46 static pqterr_t lasterr = {{0}};
 47 
 48 /* MSVC always uses __declspec(thread) */
 49 #elif defined(PQT_MSVC)
 50 static __declspec(thread) pqterr_t lasterr = {0};
 51 
 52 /* Unix-variant, MinGW or Cygwin. */
 53 #else
 54 #include <pthread.h>
 55 static pthread_key_t tlskey_lasterr;
 56 
 57 /* When the thread dies, this will get called. */
 58 static void tls_free_lasterr(void *value)
 59 {
 60   if (value)
 61   {
 62     free(value);
 63     pthread_setspecific(tlskey_lasterr, NULL);
 64 
 65   }
 66 }
 67 
 68 /* called before main.  This attribute is available since gcc 2.7.0.
 69  * It replaced the obsolete _init ... destructor replaced _fini.
 70  * We used to use pthread_once, but solaris doesn't support this
 71  * in older versions (its simply a stub returning 0, ouch!).
 72  */
 73 void __attribute__((constructor)) __InItErRoRkEy__(void)
 74 {
 75   pthread_key_create(&tlskey_lasterr, tls_free_lasterr);
 76 }
 77 #endif
 78 
 79 static void
 80 vseterror(const char *format, va_list ap, int append);
 81 
 82 static pqterr_t *
 83 geterr(void)
 84 {
 85   pqterr_t *err = NULL;
 86 
 87   /* Non thread-safe mode or windows msvc. */
 88 #if !defined(PQT_THREAD_SAFE) || defined(PQT_MSVC)
 89   err = &lasterr;
 90 
 91   /* systems requiring pthread TLS keys */
 92 #else
 93 
 94   /* System is using FSU Threads, like SCO OpenServer 5, which
 95    * has minor prototype differences.
 96    */
 97 # ifdef PTHREAD_FSU
 98     pthread_getspecific(tlskey_lasterr, (void **) &err);
 99 # else
100     err = (pqterr_t *) pthread_getspecific(tlskey_lasterr);
101 # endif
102 
103   if (!err)
104   {
105     err = (pqterr_t *) malloc(sizeof(pqterr_t));
106     if (!err)
107       return NULL;
108     memset(err, 0, sizeof(pqterr_t));
109     pthread_setspecific(tlskey_lasterr, err);
110   }
111 
112 #endif
113 
114   return err;
115 }
116 
117 char *
118 PQgeterror(void)
119 {
120   static char _empty[1] = {0};
121   pqterr_t *err = geterr();
122   return err ? err->msg : _empty;
123 }
124 
125 void
126 PQseterror(const char *format, ...)
127 {
128   /* clear error message by passing in NULL, PQseterror(NULL).*/
129   if (!format)
130   {
131     pqterr_t *err = geterr();
132 
133     if (err)
134       memset(err, 0, sizeof(pqterr_t));
135   }
136   else
137   {
138     va_list ap;
139     va_start(ap, format);
140     vseterror(format, ap, FALSE);
141   }
142 }
143 
144 char *
145 PQgetErrorField(int fieldcode)
146 {
147   pqterr_t *err = geterr();
148 
149   if (!err)
150     return NULL;
151 
152   switch (fieldcode)
153   {
154     case PG_DIAG_SEVERITY:
155       return err->severity;
156 
157     case PG_DIAG_SQLSTATE:
158       return err->sqlstate;
159 
160     case PG_DIAG_MESSAGE_PRIMARY:
161       return err->message_primary;
162 
163     case PG_DIAG_MESSAGE_DETAIL:
164       return err->message_detail;
165 
166     case PG_DIAG_MESSAGE_HINT:
167       return err->message_hint;
168 
169     case PG_DIAG_STATEMENT_POSITION:
170       return err->stmt_position;
171 
172     case PG_DIAG_INTERNAL_POSITION:
173       return err->internal_position;
174 
175     case PG_DIAG_INTERNAL_QUERY:
176       return err->internal_query;
177 
178     case PG_DIAG_CONTEXT:
179       return err->context;
180 
181     case PG_DIAG_SOURCE_FILE:
182       return err->source_file;
183 
184     case PG_DIAG_SOURCE_LINE:
185       return err->source_line;
186 
187     case PG_DIAG_SOURCE_FUNCTION:
188       return err->source_function;
189 
190     default:
191       return NULL;
192   }
193 }
194 
195 /* Used by pqt_setresultfields */
196 #define geterrfield(buf, name) do{ \
197   if ((value  = PQresultErrorField(res, name))) \
198     pqt_strcpy(buf, sizeof(buf), value); \
199   else \
200     *buf = 0; \
201 }while (0)
202 
203 void
204 pqt_setresultfields(const PGresult *res)
205 {
206   char *value;
207   pqterr_t *err = geterr();
208 
209   if (!err)
210     return;
211 
212   geterrfield(err->severity,          PG_DIAG_SEVERITY);
213   geterrfield(err->sqlstate,          PG_DIAG_SQLSTATE);
214   geterrfield(err->message_primary,   PG_DIAG_MESSAGE_PRIMARY);
215   geterrfield(err->message_detail,    PG_DIAG_MESSAGE_DETAIL);
216   geterrfield(err->message_hint,      PG_DIAG_MESSAGE_HINT);
217   geterrfield(err->stmt_position,     PG_DIAG_STATEMENT_POSITION);
218   geterrfield(err->internal_position, PG_DIAG_INTERNAL_POSITION);
219   geterrfield(err->internal_query,    PG_DIAG_INTERNAL_QUERY);
220   geterrfield(err->context,           PG_DIAG_CONTEXT);
221   geterrfield(err->source_file,       PG_DIAG_SOURCE_FILE);
222   geterrfield(err->source_line,       PG_DIAG_SOURCE_LINE);
223   geterrfield(err->source_function,   PG_DIAG_SOURCE_FUNCTION);
224 }
225 
226 /* errorf() callback for PGtypeArgs, see PQputf() and PQgetf().
227  * Always returns -1.
228  */
229 int
230 pqt_argserrorf(PGtypeArgs *args, const char *format, ...)
231 {
232   va_list ap;
233   char fqtn[200];
234 
235   if (!args || !format || !*format)
236     return -1;
237 
238   pqt_fqtn(fqtn, sizeof(fqtn), args->typhandler->typschema,
239     args->typhandler->typname);
240 
241   /* put the header */
242   PQseterror("%s[pos:%d] - ", fqtn, args->typpos);
243 
244   /* append message from type handler */
245   va_start(ap, format);
246   vseterror(format, ap, TRUE);
247   return -1;
248 }
249 
250 static void
251 vseterror(const char *format, va_list ap, int append)
252 {
253   int n;
254   int curlen = 0;
255   int size;
256   va_list ap2;
257   char *msg = NULL;
258   pqterr_t *err = geterr();
259 
260   if (!err)
261     return;
262 
263   if (append)
264     curlen = (int) strlen(err->msg);
265   else
266     *err->msg = 0;
267 
268   va_copy(ap2, ap);
269   n = pqt_vsnprintf(err->msg + curlen, sizeof(err->msg) - curlen, format, ap2);
270   va_end(ap2);
271 
272   if (n > -1)
273     return;
274 
275   /* pqterr_t msg buffer is too small for the complete message.  We have
276    * use a temporary buffer to get a successful sprintf so we can
277    * pqt_strcpy() the result; which truncates to fit.
278    */
279   size = (int) sizeof(err->msg) * 2;
280   if (!(msg = (char *) malloc(size)))
281     return;
282 
283   while (1)
284   {
285     char *p;
286 
287     va_copy(ap2, ap);
288     n = pqt_vsnprintf(msg + curlen, size - curlen, format, ap2);
289     va_end(ap2);
290 
291     /* success */
292     if (n > -1)
293       break;
294 
295     /* need more space */
296     n = size * 2;
297     if (!(p = pqt_realloc(msg, n)))
298     {
299       /* we're here because sprintf failed, don't trust buffer contents */
300       *msg = 0;
301       break;
302     }
303 
304     msg = p;
305     size = n;
306   }
307 
308   pqt_strcpy(err->msg, sizeof(err->msg), msg);
309   free(msg);
310 }