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