Branch data Line data Source code
1 : : /* src/interfaces/ecpg/ecpglib/misc.c */
2 : :
3 : : #define POSTGRES_ECPG_INTERNAL
4 : : #include "postgres_fe.h"
5 : :
6 : : #include <limits.h>
7 : : #include <unistd.h>
8 : :
9 : : #include "ecpg-pthread-win32.h"
10 : : #include "ecpgerrno.h"
11 : : #include "ecpglib.h"
12 : : #include "ecpglib_extern.h"
13 : : #include "ecpgtype.h"
14 : : #include "pg_config_paths.h"
15 : : #include "pgtypes_date.h"
16 : : #include "pgtypes_interval.h"
17 : : #include "pgtypes_numeric.h"
18 : : #include "pgtypes_timestamp.h"
19 : : #include "sqlca.h"
20 : :
21 : : #ifndef LONG_LONG_MIN
22 : : #ifdef LLONG_MIN
23 : : #define LONG_LONG_MIN LLONG_MIN
24 : : #else
25 : : #define LONG_LONG_MIN LONGLONG_MIN
26 : : #endif /* LLONG_MIN */
27 : : #endif /* LONG_LONG_MIN */
28 : :
29 : : bool ecpg_internal_regression_mode = false;
30 : :
31 : : static struct sqlca_t sqlca_init =
32 : : {
33 : : {
34 : : 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
35 : : },
36 : : sizeof(struct sqlca_t),
37 : : 0,
38 : : {
39 : : 0,
40 : : {
41 : : 0
42 : : }
43 : : },
44 : : {
45 : : 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
46 : : },
47 : : {
48 : : 0, 0, 0, 0, 0, 0
49 : : },
50 : : {
51 : : 0, 0, 0, 0, 0, 0, 0, 0
52 : : },
53 : : {
54 : : '0', '0', '0', '0', '0'
55 : : }
56 : : };
57 : :
58 : : static pthread_key_t sqlca_key;
59 : : static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT;
60 : :
61 : : static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
62 : : static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER;
63 : : static volatile int simple_debug = 0;
64 : : static FILE *debugstream = NULL;
65 : :
66 : : void
67 : 3208628 : ecpg_init_sqlca(struct sqlca_t *sqlca_p)
68 : : {
69 : 3208628 : memcpy(sqlca_p, &sqlca_init, sizeof(struct sqlca_t));
70 : 3208628 : }
71 : :
72 : : bool
73 : 7812 : ecpg_init(const struct connection *con, const char *connection_name, const int lineno)
74 : : {
75 : 7812 : struct sqlca_t *sqlca = ECPGget_sqlca();
76 : :
77 [ - + ]: 7812 : if (sqlca == NULL)
78 : : {
79 : 0 : ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY,
80 : : NULL);
81 : 0 : return false;
82 : : }
83 : :
84 : 7812 : ecpg_init_sqlca(sqlca);
85 [ + + ]: 7812 : if (con == NULL)
86 : : {
87 [ + + ]: 55 : ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
88 : 14 : connection_name ? connection_name : ecpg_gettext("NULL"));
89 : 41 : return false;
90 : : }
91 : :
92 : 7771 : return true;
93 : : }
94 : :
95 : : static void
96 : 136 : ecpg_sqlca_key_destructor(void *arg)
97 : : {
98 : 136 : free(arg); /* sqlca structure allocated in ECPGget_sqlca */
99 : 136 : }
100 : :
101 : : static void
102 : 131 : ecpg_sqlca_key_init(void)
103 : : {
104 : 131 : pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
105 : 131 : }
106 : :
107 : : struct sqlca_t *
108 : 6436662 : ECPGget_sqlca(void)
109 : : {
110 : : struct sqlca_t *sqlca;
111 : :
112 : 6436662 : pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
113 : :
114 : 6436662 : sqlca = pthread_getspecific(sqlca_key);
115 [ + + ]: 6436662 : if (sqlca == NULL)
116 : : {
117 : 263 : sqlca = malloc(sizeof(struct sqlca_t));
118 [ - + ]: 263 : if (sqlca == NULL)
119 : 0 : return NULL;
120 : 263 : ecpg_init_sqlca(sqlca);
121 : 263 : pthread_setspecific(sqlca_key, sqlca);
122 : : }
123 : 6436662 : return sqlca;
124 : : }
125 : :
126 : : bool
127 : 0 : ECPGstatus(int lineno, const char *connection_name)
128 : : {
129 : 0 : struct connection *con = ecpg_get_connection(connection_name);
130 : :
131 [ # # ]: 0 : if (!ecpg_init(con, connection_name, lineno))
132 : 0 : return false;
133 : :
134 : : /* are we connected? */
135 [ # # ]: 0 : if (con->connection == NULL)
136 : : {
137 : 0 : ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, con->name);
138 : 0 : return false;
139 : : }
140 : :
141 : 0 : return true;
142 : : }
143 : :
144 : : PGTransactionStatusType
145 : 0 : ECPGtransactionStatus(const char *connection_name)
146 : : {
147 : : const struct connection *con;
148 : :
149 : 0 : con = ecpg_get_connection(connection_name);
150 [ # # ]: 0 : if (con == NULL)
151 : : {
152 : : /* transaction status is unknown */
153 : 0 : return PQTRANS_UNKNOWN;
154 : : }
155 : :
156 : 0 : return PQtransactionStatus(con->connection);
157 : : }
158 : :
159 : : bool
160 : 252 : ECPGtrans(int lineno, const char *connection_name, const char *transaction)
161 : : {
162 : : PGresult *res;
163 : 252 : struct connection *con = ecpg_get_connection(connection_name);
164 : :
165 [ - + ]: 252 : if (!ecpg_init(con, connection_name, lineno))
166 : 0 : return false;
167 : :
168 [ + - ]: 252 : ecpg_log("ECPGtrans on line %d: action \"%s\"; connection \"%s\"\n", lineno, transaction, con ? con->name : "null");
169 : :
170 : : /* if we have no connection we just simulate the command */
171 [ + - + - ]: 252 : if (con && con->connection)
172 : : {
173 : : /*
174 : : * If we got a transaction command but have no open transaction, we
175 : : * have to start one, unless we are in autocommit, where the
176 : : * developers have to take care themselves. However, if the command is
177 : : * a begin statement, we just execute it once. And if the command is
178 : : * commit or rollback prepared, we don't execute it.
179 : : */
180 [ + + ]: 252 : if (PQtransactionStatus(con->connection) == PQTRANS_IDLE &&
181 [ + + ]: 54 : !con->autocommit &&
182 [ + + ]: 48 : strncmp(transaction, "begin", 5) != 0 &&
183 [ + - ]: 2 : strncmp(transaction, "start", 5) != 0 &&
184 [ - + ]: 2 : strncmp(transaction, "commit prepared", 15) != 0 &&
185 [ # # ]: 0 : strncmp(transaction, "rollback prepared", 17) != 0)
186 : : {
187 : 0 : res = PQexec(con->connection, "begin transaction");
188 [ # # ]: 0 : if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
189 : 0 : return false;
190 : 0 : PQclear(res);
191 : : }
192 : :
193 : 252 : res = PQexec(con->connection, transaction);
194 [ - + ]: 252 : if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
195 : 0 : return false;
196 : 252 : PQclear(res);
197 : : }
198 : :
199 : 252 : return true;
200 : : }
201 : :
202 : :
203 : : void
204 : 121 : ECPGdebug(int n, FILE *dbgs)
205 : : {
206 : : /* Interlock against concurrent executions of ECPGdebug() */
207 : 121 : pthread_mutex_lock(&debug_init_mutex);
208 : :
209 : : /* Prevent ecpg_log() from printing while we change settings */
210 : 121 : pthread_mutex_lock(&debug_mutex);
211 : :
212 [ + - ]: 121 : if (n > 100)
213 : : {
214 : 121 : ecpg_internal_regression_mode = true;
215 : 121 : simple_debug = n - 100;
216 : : }
217 : : else
218 : 0 : simple_debug = n;
219 : :
220 : 121 : debugstream = dbgs;
221 : :
222 : : /* We must release debug_mutex before invoking ecpg_log() ... */
223 : 121 : pthread_mutex_unlock(&debug_mutex);
224 : :
225 : : /* ... but keep holding debug_init_mutex to avoid racy printout */
226 : 121 : ecpg_log("ECPGdebug: set to %d\n", simple_debug);
227 : :
228 : 121 : pthread_mutex_unlock(&debug_init_mutex);
229 : 121 : }
230 : :
231 : : void
232 : 29492 : ecpg_log(const char *format, ...)
233 : : {
234 : : va_list ap;
235 : : const char *intl_format;
236 : : int bufsize;
237 : : char *fmt;
238 : : struct sqlca_t *sqlca;
239 : :
240 : : /*
241 : : * For performance reasons, inspect simple_debug without taking the mutex.
242 : : * This could be problematic if fetching an int isn't atomic, but we
243 : : * assume that it is in many other places too.
244 : : */
245 [ + + ]: 29492 : if (!simple_debug)
246 : 22542 : return;
247 : :
248 : : /* localize the error message string */
249 : 6950 : intl_format = ecpg_gettext(format);
250 : :
251 : : /*
252 : : * Insert PID into the format, unless ecpg_internal_regression_mode is set
253 : : * (regression tests want unchanging output).
254 : : */
255 : 6950 : bufsize = strlen(intl_format) + 100;
256 : 6950 : fmt = (char *) malloc(bufsize);
257 [ - + ]: 6950 : if (fmt == NULL)
258 : 0 : return;
259 : :
260 [ + - ]: 6950 : if (ecpg_internal_regression_mode)
261 : 6950 : snprintf(fmt, bufsize, "[NO_PID]: %s", intl_format);
262 : : else
263 : 0 : snprintf(fmt, bufsize, "[%d]: %s", (int) getpid(), intl_format);
264 : :
265 : 6950 : sqlca = ECPGget_sqlca();
266 : :
267 : 6950 : pthread_mutex_lock(&debug_mutex);
268 : :
269 : : /* Now that we hold the mutex, recheck simple_debug */
270 [ + - ]: 6950 : if (simple_debug)
271 : : {
272 : 6950 : va_start(ap, format);
273 : 6950 : vfprintf(debugstream, fmt, ap);
274 : 6950 : va_end(ap);
275 : :
276 : : /* dump out internal sqlca variables */
277 [ + - + - ]: 6950 : if (ecpg_internal_regression_mode && sqlca != NULL)
278 : : {
279 : 6950 : fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
280 : 6950 : sqlca->sqlcode, sqlca->sqlstate);
281 : : }
282 : :
283 : 6950 : fflush(debugstream);
284 : : }
285 : :
286 : 6950 : pthread_mutex_unlock(&debug_mutex);
287 : :
288 : 6950 : free(fmt);
289 : : }
290 : :
291 : : void
292 : 5370 : ECPGset_noind_null(enum ECPGttype type, void *ptr)
293 : : {
294 [ + + + + : 5370 : switch (type)
- + + - -
+ + - +
+ ]
295 : : {
296 : 1890 : case ECPGt_char:
297 : : case ECPGt_unsigned_char:
298 : : case ECPGt_string:
299 : 1890 : *((char *) ptr) = '\0';
300 : 1890 : break;
301 : 4 : case ECPGt_short:
302 : : case ECPGt_unsigned_short:
303 : 4 : *((short int *) ptr) = SHRT_MIN;
304 : 4 : break;
305 : 4 : case ECPGt_int:
306 : : case ECPGt_unsigned_int:
307 : 4 : *((int *) ptr) = INT_MIN;
308 : 4 : break;
309 : 10 : case ECPGt_long:
310 : : case ECPGt_unsigned_long:
311 : : case ECPGt_date:
312 : 10 : *((long *) ptr) = LONG_MIN;
313 : 10 : break;
314 : 0 : case ECPGt_long_long:
315 : : case ECPGt_unsigned_long_long:
316 : 0 : *((long long *) ptr) = LONG_LONG_MIN;
317 : 0 : break;
318 : 4 : case ECPGt_float:
319 : 4 : memset(ptr, 0xff, sizeof(float));
320 : 4 : break;
321 : 8 : case ECPGt_double:
322 : 8 : memset(ptr, 0xff, sizeof(double));
323 : 8 : break;
324 : 0 : case ECPGt_varchar:
325 : 0 : *(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
326 : 0 : ((struct ECPGgeneric_varchar *) ptr)->len = 0;
327 : 0 : break;
328 : 0 : case ECPGt_bytea:
329 : 0 : ((struct ECPGgeneric_bytea *) ptr)->len = 0;
330 : 0 : break;
331 : 3438 : case ECPGt_decimal:
332 : 3438 : memset(ptr, 0, sizeof(decimal));
333 : 3438 : ((decimal *) ptr)->sign = NUMERIC_NULL;
334 : 3438 : break;
335 : 4 : case ECPGt_numeric:
336 : 4 : memset(ptr, 0, sizeof(numeric));
337 : 4 : ((numeric *) ptr)->sign = NUMERIC_NULL;
338 : 4 : break;
339 : 0 : case ECPGt_interval:
340 : 0 : memset(ptr, 0xff, sizeof(interval));
341 : 0 : break;
342 : 6 : case ECPGt_timestamp:
343 : 6 : memset(ptr, 0xff, sizeof(timestamp));
344 : 6 : break;
345 : 2 : default:
346 : 2 : break;
347 : : }
348 : 5370 : }
349 : :
350 : : static bool
351 : 22 : _check(const unsigned char *ptr, int length)
352 : : {
353 [ + + ]: 118 : for (length--; length >= 0; length--)
354 [ + + ]: 104 : if (ptr[length] != 0xff)
355 : 8 : return false;
356 : :
357 : 14 : return true;
358 : : }
359 : :
360 : : bool
361 : 7326 : ECPGis_noind_null(enum ECPGttype type, const void *ptr)
362 : : {
363 [ + + + + : 7326 : switch (type)
- + + - -
+ - - +
+ ]
364 : : {
365 : 40 : case ECPGt_char:
366 : : case ECPGt_unsigned_char:
367 : : case ECPGt_string:
368 [ + + ]: 40 : if (*((const char *) ptr) == '\0')
369 : 4 : return true;
370 : 36 : break;
371 : 8 : case ECPGt_short:
372 : : case ECPGt_unsigned_short:
373 [ + + ]: 8 : if (*((const short int *) ptr) == SHRT_MIN)
374 : 4 : return true;
375 : 4 : break;
376 : 1836 : case ECPGt_int:
377 : : case ECPGt_unsigned_int:
378 [ + + ]: 1836 : if (*((const int *) ptr) == INT_MIN)
379 : 4 : return true;
380 : 1832 : break;
381 : 36 : case ECPGt_long:
382 : : case ECPGt_unsigned_long:
383 : : case ECPGt_date:
384 [ + + ]: 36 : if (*((const long *) ptr) == LONG_MIN)
385 : 10 : return true;
386 : 26 : break;
387 : 0 : case ECPGt_long_long:
388 : : case ECPGt_unsigned_long_long:
389 [ # # ]: 0 : if (*((const long long *) ptr) == LONG_LONG_MIN)
390 : 0 : return true;
391 : 0 : break;
392 : 8 : case ECPGt_float:
393 : 8 : return _check(ptr, sizeof(float));
394 : : break;
395 : 8 : case ECPGt_double:
396 : 8 : return _check(ptr, sizeof(double));
397 : : break;
398 : 0 : case ECPGt_varchar:
399 [ # # ]: 0 : if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
400 : 0 : return true;
401 : 0 : break;
402 : 0 : case ECPGt_bytea:
403 [ # # ]: 0 : if (((const struct ECPGgeneric_bytea *) ptr)->len == 0)
404 : 0 : return true;
405 : 0 : break;
406 : 5376 : case ECPGt_decimal:
407 [ + + ]: 5376 : if (((const decimal *) ptr)->sign == NUMERIC_NULL)
408 : 438 : return true;
409 : 4938 : break;
410 : 0 : case ECPGt_numeric:
411 [ # # ]: 0 : if (((const numeric *) ptr)->sign == NUMERIC_NULL)
412 : 0 : return true;
413 : 0 : break;
414 : 0 : case ECPGt_interval:
415 : 0 : return _check(ptr, sizeof(interval));
416 : : break;
417 : 6 : case ECPGt_timestamp:
418 : 6 : return _check(ptr, sizeof(timestamp));
419 : : break;
420 : 8 : default:
421 : 8 : break;
422 : : }
423 : :
424 : 6844 : return false;
425 : : }
426 : :
427 : : #ifdef WIN32
428 : :
429 : : int
430 : : pthread_mutex_init(pthread_mutex_t *mp, void *attr)
431 : : {
432 : : mp->initstate = 0;
433 : : return 0;
434 : : }
435 : :
436 : : int
437 : : pthread_mutex_lock(pthread_mutex_t *mp)
438 : : {
439 : : /* Initialize the csection if not already done */
440 : : if (mp->initstate != 1)
441 : : {
442 : : LONG istate;
443 : :
444 : : while ((istate = InterlockedExchange(&mp->initstate, 2)) == 2)
445 : : Sleep(0); /* wait, another thread is doing this */
446 : : if (istate != 1)
447 : : InitializeCriticalSection(&mp->csection);
448 : : InterlockedExchange(&mp->initstate, 1);
449 : : }
450 : : EnterCriticalSection(&mp->csection);
451 : : return 0;
452 : : }
453 : :
454 : : int
455 : : pthread_mutex_unlock(pthread_mutex_t *mp)
456 : : {
457 : : if (mp->initstate != 1)
458 : : return EINVAL;
459 : : LeaveCriticalSection(&mp->csection);
460 : : return 0;
461 : : }
462 : :
463 : : static pthread_mutex_t win32_pthread_once_lock = PTHREAD_MUTEX_INITIALIZER;
464 : :
465 : : void
466 : : win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void))
467 : : {
468 : : if (!*once)
469 : : {
470 : : pthread_mutex_lock(&win32_pthread_once_lock);
471 : : if (!*once)
472 : : {
473 : : fn();
474 : : *once = true;
475 : : }
476 : : pthread_mutex_unlock(&win32_pthread_once_lock);
477 : : }
478 : : }
479 : : #endif /* WIN32 */
480 : :
481 : : #ifdef ENABLE_NLS
482 : :
483 : : char *
484 : 7111 : ecpg_gettext(const char *msgid)
485 : : {
486 : : /*
487 : : * At least on Windows, there are gettext implementations that fail if
488 : : * multiple threads call bindtextdomain() concurrently. Use a mutex and
489 : : * flag variable to ensure that we call it just once per process. It is
490 : : * not known that similar bugs exist on non-Windows platforms, but we
491 : : * might as well do it the same way everywhere.
492 : : */
493 : : static volatile bool already_bound = false;
494 : : static pthread_mutex_t binddomain_mutex = PTHREAD_MUTEX_INITIALIZER;
495 : :
496 [ + + ]: 7111 : if (!already_bound)
497 : : {
498 : : /* dgettext() preserves errno, but bindtextdomain() doesn't */
499 : : #ifdef WIN32
500 : : int save_errno = GetLastError();
501 : : #else
502 : 119 : int save_errno = errno;
503 : : #endif
504 : :
505 : 119 : (void) pthread_mutex_lock(&binddomain_mutex);
506 : :
507 [ + - ]: 119 : if (!already_bound)
508 : : {
509 : : const char *ldir;
510 : :
511 : : /*
512 : : * No relocatable lookup here because the calling executable could
513 : : * be anywhere
514 : : */
515 : 119 : ldir = getenv("PGLOCALEDIR");
516 [ - + ]: 119 : if (!ldir)
517 : 0 : ldir = LOCALEDIR;
518 : 119 : bindtextdomain(PG_TEXTDOMAIN("ecpglib"), ldir);
519 : 119 : already_bound = true;
520 : : }
521 : :
522 : 119 : (void) pthread_mutex_unlock(&binddomain_mutex);
523 : :
524 : : #ifdef WIN32
525 : : SetLastError(save_errno);
526 : : #else
527 : 119 : errno = save_errno;
528 : : #endif
529 : : }
530 : :
531 : 7111 : return dgettext(PG_TEXTDOMAIN("ecpglib"), msgid);
532 : : }
533 : : #endif /* ENABLE_NLS */
534 : :
535 : : struct var_list *ivlist = NULL;
536 : :
537 : : void
538 : 28 : ECPGset_var(int number, void *pointer, int lineno)
539 : : {
540 : : struct var_list *ptr;
541 : :
542 : 28 : struct sqlca_t *sqlca = ECPGget_sqlca();
543 : :
544 [ - + ]: 28 : if (sqlca == NULL)
545 : : {
546 : 0 : ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
547 : : ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
548 : 0 : return;
549 : : }
550 : :
551 : 28 : ecpg_init_sqlca(sqlca);
552 : :
553 [ + + ]: 78 : for (ptr = ivlist; ptr != NULL; ptr = ptr->next)
554 : : {
555 [ - + ]: 50 : if (ptr->number == number)
556 : : {
557 : : /* already known => just change pointer value */
558 : 0 : ptr->pointer = pointer;
559 : 0 : return;
560 : : }
561 : : }
562 : :
563 : : /* a new one has to be added */
564 : 28 : ptr = (struct var_list *) calloc(1L, sizeof(struct var_list));
565 [ - + ]: 28 : if (!ptr)
566 : : {
567 : 0 : sqlca = ECPGget_sqlca();
568 : :
569 [ # # ]: 0 : if (sqlca == NULL)
570 : : {
571 : 0 : ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
572 : : ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
573 : 0 : return;
574 : : }
575 : :
576 : 0 : sqlca->sqlcode = ECPG_OUT_OF_MEMORY;
577 : 0 : strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
578 : 0 : snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "out of memory on line %d", lineno);
579 : 0 : sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
580 : : /* free all memory we have allocated for the user */
581 : 0 : ECPGfree_auto_mem();
582 : : }
583 : : else
584 : : {
585 : 28 : ptr->number = number;
586 : 28 : ptr->pointer = pointer;
587 : 28 : ptr->next = ivlist;
588 : 28 : ivlist = ptr;
589 : : }
590 : : }
591 : :
592 : : void *
593 : 102 : ECPGget_var(int number)
594 : : {
595 : : struct var_list *ptr;
596 : :
597 [ + - + + ]: 152 : for (ptr = ivlist; ptr != NULL && ptr->number != number; ptr = ptr->next);
598 [ + - ]: 102 : return (ptr) ? ptr->pointer : NULL;
599 : : }
|