libpqtypes - error.c

Home Page

  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) 2009 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)) my_init(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 #endif
112 
113   return err;
114 }
115 
116 char *
117 PQgeterror(void)
118 {
119   static char _empty[1] = {0};
120   pqterr_t *err = geterr();
121   return err ? err->msg : _empty;
122 }
123 
124 void
125 PQseterror(const char *format, ...)
126 {
127   /* clear error message by passing in NULL, PQseterror(NULL).*/
128   if (!format)
129   {
130     pqterr_t *err = geterr();
131 
132     if (err)
133       memset(err, 0, sizeof(pqterr_t));
134   }
135   else
136   {
137     va_list ap;
138     va_start(ap, format);
139     vseterror(format, ap, FALSE);
140   }
141 }
142 
143 char *
144 PQgetErrorField(int fieldcode)
145 {
146   pqterr_t *err = geterr();
147 
148   if (!err)
149     return NULL;
150 
151   switch (fieldcode)
152   {
153     case PG_DIAG_SEVERITY:
154       return err->severity;
155 
156     case PG_DIAG_SQLSTATE:
157       return err->sqlstate;
158 
159     case PG_DIAG_MESSAGE_PRIMARY:
160       return err->message_primary;
161 
162     case PG_DIAG_MESSAGE_DETAIL:
163       return err->message_detail;
164 
165     case PG_DIAG_MESSAGE_HINT:
166       return err->message_hint;
167 
168     case PG_DIAG_STATEMENT_POSITION:
169       return err->stmt_position;
170 
171     case PG_DIAG_INTERNAL_POSITION:
172       return err->internal_position;
173 
174     case PG_DIAG_INTERNAL_QUERY:
175       return err->internal_query;
176 
177     case PG_DIAG_CONTEXT:
178       return err->context;
179 
180     case PG_DIAG_SOURCE_FILE:
181       return err->source_file;
182 
183     case PG_DIAG_SOURCE_LINE:
184       return err->source_line;
185 
186     case PG_DIAG_SOURCE_FUNCTION:
187       return err->source_function;
188 
189     default:
190       return NULL;
191   }
192 }
193 
194 /* Used by pqt_setresultfields */
195 #define geterrfield(buf, name) do{ \
196   if ((value  = PQresultErrorField(res, name))) \
197     pqt_strcpy(buf, sizeof(buf), value); \
198   else \
199     *buf = 0; \
200 }while (0)
201 
202 void
203 pqt_setresultfields(const PGresult *res)
204 {
205   char *value;
206   pqterr_t *err = geterr();
207 
208   if (!err)
209     return;
210 
211   geterrfield(err->severity,          PG_DIAG_SEVERITY);
212   geterrfield(err->sqlstate,          PG_DIAG_SQLSTATE);
213   geterrfield(err->message_primary,   PG_DIAG_MESSAGE_PRIMARY);
214   geterrfield(err->message_detail,    PG_DIAG_MESSAGE_DETAIL);
215   geterrfield(err->message_hint,      PG_DIAG_MESSAGE_HINT);
216   geterrfield(err->stmt_position,     PG_DIAG_STATEMENT_POSITION);
217   geterrfield(err->internal_position, PG_DIAG_INTERNAL_POSITION);
218   geterrfield(err->internal_query,    PG_DIAG_INTERNAL_QUERY);
219   geterrfield(err->context,           PG_DIAG_CONTEXT);
220   geterrfield(err->source_file,       PG_DIAG_SOURCE_FILE);
221   geterrfield(err->source_line,       PG_DIAG_SOURCE_LINE);
222   geterrfield(err->source_function,   PG_DIAG_SOURCE_FUNCTION);
223 }
224 
225 /* errorf() callback for PGtypeArgs, see PQputf() and PQgetf().
226  * Always returns -1.
227  */
228 int
229 pqt_argserrorf(PGtypeArgs *args, const char *format, ...)
230 {
231   va_list ap;
232   char fqtn[200];
233 
234   if (!args || !format || !*format)
235     return -1;
236 
237   pqt_fqtn(fqtn, sizeof(fqtn), args->typhandler->typschema,
238     args->typhandler->typname);
239 
240   /* put the header */
241   PQseterror("%s[pos:%d] - ", fqtn, args->typpos);
242 
243   /* append message from type handler */
244   va_start(ap, format);
245   vseterror(format, ap, TRUE);
246   return -1;
247 }
248 
249 static void
250 vseterror(const char *format, va_list ap, int append)
251 {
252   int n;
253   int curlen = 0;
254   int size;
255   va_list ap2;
256   char *msg = NULL;
257   pqterr_t *err = geterr();
258 
259   if (!err)
260     return;
261 
262   if (append)
263     curlen = (int) strlen(err->msg);
264   else
265     *err->msg = 0;
266 
267   va_copy(ap2, ap);
268   n = pqt_vsnprintf(err->msg + curlen, sizeof(err->msg) - curlen, format, ap2);
269   va_end(ap2);
270 
271   if (n > -1)
272     return;
273 
274   /* pqterr_t msg buffer is too small for the complete message.  We have
275    * use a temporary buffer to get a successful sprintf so we can
276    * pqt_strcpy() the result; which truncates to fit.
277    */
278   size = (int) sizeof(err->msg) * 2;
279   if (!(msg = (char *) malloc(size)))
280     return;
281 
282   while (1)
283   {
284     char *p;
285 
286     va_copy(ap2, ap);
287     n = pqt_vsnprintf(msg + curlen, size - curlen, format, ap2);
288     va_end(ap2);
289 
290     /* success */
291     if (n > -1)
292       break;
293 
294     /* need more space */
295     n = size * 2;
296     if (!(p = pqt_realloc(msg, n)))
297     {
298       /* we're here because sprintf failed, don't trust buffer contents */
299       *msg = 0;
300       break;
301     }
302 
303     msg = p;
304     size = n;
305   }
306 
307   pqt_strcpy(err->msg, sizeof(err->msg), msg);
308   free(msg);
309 }