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 } |