Fix obsolete comment regarding FSM truncation.
[PostgreSQL.git] / src / interfaces / ecpg / ecpglib / misc.c
blobd48cb9e957337daa68e3664f562c162642b035db
1 /* $PostgreSQL$ */
3 #define POSTGRES_ECPG_INTERNAL
4 #include "postgres_fe.h"
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 "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"
20 #ifdef HAVE_LONG_LONG_INT_64
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
27 #endif
28 #endif
30 bool ecpg_internal_regression_mode = false;
32 static struct sqlca_t sqlca_init =
35 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
37 sizeof(struct sqlca_t),
46 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
49 0, 0, 0, 0, 0, 0
52 0, 0, 0, 0, 0, 0, 0, 0
55 '0', '0', '0', '0', '0'
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 =
66 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
68 sizeof(struct sqlca_t),
77 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
80 0, 0, 0, 0, 0, 0
83 0, 0, 0, 0, 0, 0, 0, 0
86 '0', '0', '0', '0', '0'
89 #endif
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;
98 void
99 ecpg_init_sqlca(struct sqlca_t * sqlca)
101 memcpy((char *) sqlca, (char *) &sqlca_init, sizeof(struct sqlca_t));
104 bool
105 ecpg_init(const struct connection * con, const char *connection_name, const int lineno)
107 struct sqlca_t *sqlca = ECPGget_sqlca();
109 ecpg_init_sqlca(sqlca);
110 if (con == NULL)
112 ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
113 connection_name ? connection_name : _("NULL"));
114 return (false);
117 return (true);
120 #ifdef ENABLE_THREAD_SAFETY
121 static void
122 ecpg_sqlca_key_destructor(void *arg)
124 free(arg); /* sqlca structure allocated in ECPGget_sqlca */
127 static void
128 ecpg_sqlca_key_init(void)
130 pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
132 #endif
134 struct sqlca_t *
135 ECPGget_sqlca(void)
137 #ifdef ENABLE_THREAD_SAFETY
138 struct sqlca_t *sqlca;
140 pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
142 sqlca = pthread_getspecific(sqlca_key);
143 if (sqlca == NULL)
145 sqlca = malloc(sizeof(struct sqlca_t));
146 ecpg_init_sqlca(sqlca);
147 pthread_setspecific(sqlca_key, sqlca);
149 return (sqlca);
150 #else
151 return (&sqlca);
152 #endif
155 bool
156 ECPGstatus(int lineno, const char *connection_name)
158 struct connection *con = ecpg_get_connection(connection_name);
160 if (!ecpg_init(con, connection_name, lineno))
161 return (false);
163 /* are we connected? */
164 if (con->connection == NULL)
166 ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, con->name);
167 return false;
170 return (true);
173 bool
174 ECPGtrans(int lineno, const char *connection_name, const char *transaction)
176 PGresult *res;
177 struct connection *con = ecpg_get_connection(connection_name);
179 if (!ecpg_init(con, connection_name, lineno))
180 return (false);
182 ecpg_log("ECPGtrans on line %d: action \"%s\"; connection \"%s\"\n", lineno, transaction, con ? con->name : _("null"));
184 /* if we have no connection we just simulate the command */
185 if (con && con->connection)
188 * If we got a transaction command but have no open transaction, we
189 * have to start one, unless we are in autocommit, where the
190 * developers have to take care themselves. However, if the command is
191 * a begin statement, we just execute it once.
193 if (con->committed && !con->autocommit && strncmp(transaction, "begin", 5) != 0 && strncmp(transaction, "start", 5) != 0)
195 res = PQexec(con->connection, "begin transaction");
196 if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
197 return FALSE;
198 PQclear(res);
201 res = PQexec(con->connection, transaction);
202 if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
203 return FALSE;
204 PQclear(res);
207 if (strncmp(transaction, "commit", 6) == 0 || strncmp(transaction, "rollback", 8) == 0)
208 con->committed = true;
209 else
210 con->committed = false;
212 return true;
216 void
217 ECPGdebug(int n, FILE *dbgs)
219 #ifdef ENABLE_THREAD_SAFETY
220 pthread_mutex_lock(&debug_init_mutex);
221 #endif
223 if (n > 100)
225 ecpg_internal_regression_mode = true;
226 simple_debug = n - 100;
228 else
229 simple_debug = n;
231 debugstream = dbgs;
233 ecpg_log("ECPGdebug: set to %d\n", simple_debug);
235 #ifdef ENABLE_THREAD_SAFETY
236 pthread_mutex_unlock(&debug_init_mutex);
237 #endif
240 void
241 ecpg_log(const char *format,...)
243 va_list ap;
244 struct sqlca_t *sqlca = ECPGget_sqlca();
246 /* internationalize the error message string */
247 format = ecpg_gettext(format);
249 if (simple_debug)
251 int bufsize = strlen(format) + 100;
252 char *f = (char *) malloc(bufsize);
254 if (f == NULL)
255 return;
258 * regression tests set this environment variable to get the same
259 * output for every run.
261 if (ecpg_internal_regression_mode)
262 snprintf(f, bufsize, "[NO_PID]: %s", format);
263 else
264 snprintf(f, bufsize, "[%d]: %s", (int) getpid(), format);
266 #ifdef ENABLE_THREAD_SAFETY
267 pthread_mutex_lock(&debug_mutex);
268 #endif
270 va_start(ap, format);
271 vfprintf(debugstream, f, ap);
272 va_end(ap);
274 /* dump out internal sqlca variables */
275 if (ecpg_internal_regression_mode)
276 fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
277 sqlca->sqlcode, sqlca->sqlstate);
279 fflush(debugstream);
281 #ifdef ENABLE_THREAD_SAFETY
282 pthread_mutex_unlock(&debug_mutex);
283 #endif
285 free(f);
289 void
290 ECPGset_noind_null(enum ECPGttype type, void *ptr)
292 switch (type)
294 case ECPGt_char:
295 case ECPGt_unsigned_char:
296 *((char *) ptr) = '\0';
297 break;
298 case ECPGt_short:
299 case ECPGt_unsigned_short:
300 *((short int *) ptr) = SHRT_MIN;
301 break;
302 case ECPGt_int:
303 case ECPGt_unsigned_int:
304 *((int *) ptr) = INT_MIN;
305 break;
306 case ECPGt_long:
307 case ECPGt_unsigned_long:
308 case ECPGt_date:
309 *((long *) ptr) = LONG_MIN;
310 break;
311 #ifdef HAVE_LONG_LONG_INT_64
312 case ECPGt_long_long:
313 case ECPGt_unsigned_long_long:
314 *((long long *) ptr) = LONG_LONG_MIN;
315 break;
316 #endif /* HAVE_LONG_LONG_INT_64 */
317 case ECPGt_float:
318 memset((char *) ptr, 0xff, sizeof(float));
319 break;
320 case ECPGt_double:
321 memset((char *) ptr, 0xff, sizeof(double));
322 break;
323 case ECPGt_varchar:
324 *(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
325 ((struct ECPGgeneric_varchar *) ptr)->len = 0;
326 break;
327 case ECPGt_decimal:
328 memset((char *) ptr, 0, sizeof(decimal));
329 ((decimal *) ptr)->sign = NUMERIC_NAN;
330 break;
331 case ECPGt_numeric:
332 memset((char *) ptr, 0, sizeof(numeric));
333 ((numeric *) ptr)->sign = NUMERIC_NAN;
334 break;
335 case ECPGt_interval:
336 memset((char *) ptr, 0xff, sizeof(interval));
337 break;
338 case ECPGt_timestamp:
339 memset((char *) ptr, 0xff, sizeof(timestamp));
340 break;
341 default:
342 break;
346 static bool
347 _check(unsigned char *ptr, int length)
349 for (; length > 0 && ptr[--length] == 0xff;);
350 if (length <= 0)
351 return true;
352 return false;
355 bool
356 ECPGis_noind_null(enum ECPGttype type, void *ptr)
358 switch (type)
360 case ECPGt_char:
361 case ECPGt_unsigned_char:
362 if (*((char *) ptr) == '\0')
363 return true;
364 break;
365 case ECPGt_short:
366 case ECPGt_unsigned_short:
367 if (*((short int *) ptr) == SHRT_MIN)
368 return true;
369 break;
370 case ECPGt_int:
371 case ECPGt_unsigned_int:
372 if (*((int *) ptr) == INT_MIN)
373 return true;
374 break;
375 case ECPGt_long:
376 case ECPGt_unsigned_long:
377 case ECPGt_date:
378 if (*((long *) ptr) == LONG_MIN)
379 return true;
380 break;
381 #ifdef HAVE_LONG_LONG_INT_64
382 case ECPGt_long_long:
383 case ECPGt_unsigned_long_long:
384 if (*((long long *) ptr) == LONG_LONG_MIN)
385 return true;
386 break;
387 #endif /* HAVE_LONG_LONG_INT_64 */
388 case ECPGt_float:
389 return (_check(ptr, sizeof(float)));
390 break;
391 case ECPGt_double:
392 return (_check(ptr, sizeof(double)));
393 break;
394 case ECPGt_varchar:
395 if (*(((struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
396 return true;
397 break;
398 case ECPGt_decimal:
399 if (((decimal *) ptr)->sign == NUMERIC_NAN)
400 return true;
401 break;
402 case ECPGt_numeric:
403 if (((numeric *) ptr)->sign == NUMERIC_NAN)
404 return true;
405 break;
406 case ECPGt_interval:
407 return (_check(ptr, sizeof(interval)));
408 break;
409 case ECPGt_timestamp:
410 return (_check(ptr, sizeof(timestamp)));
411 break;
412 default:
413 break;
416 return false;
419 #ifdef WIN32
420 #ifdef ENABLE_THREAD_SAFETY
422 void
423 win32_pthread_mutex(volatile pthread_mutex_t *mutex)
425 if (mutex->handle == NULL)
427 while (InterlockedExchange((LONG *) & mutex->initlock, 1) == 1)
428 Sleep(0);
429 if (mutex->handle == NULL)
430 mutex->handle = CreateMutex(NULL, FALSE, NULL);
431 InterlockedExchange((LONG *) & mutex->initlock, 0);
435 static pthread_mutex_t win32_pthread_once_lock = PTHREAD_MUTEX_INITIALIZER;
437 void
438 win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void))
440 if (!*once)
442 pthread_mutex_lock(&win32_pthread_once_lock);
443 if (!*once)
445 *once = true;
446 fn();
448 pthread_mutex_unlock(&win32_pthread_once_lock);
451 #endif /* ENABLE_THREAD_SAFETY */
453 #endif /* WIN32 */
455 #ifdef ENABLE_NLS
457 char *
458 ecpg_gettext(const char *msgid)
460 static bool already_bound = false;
462 if (!already_bound)
464 /* dgettext() preserves errno, but bindtextdomain() doesn't */
465 #ifdef WIN32
466 int save_errno = GetLastError();
467 #else
468 int save_errno = errno;
469 #endif
470 const char *ldir;
472 already_bound = true;
473 /* No relocatable lookup here because the binary could be anywhere */
474 ldir = getenv("PGLOCALEDIR");
475 if (!ldir)
476 ldir = LOCALEDIR;
477 bindtextdomain("ecpg", ldir);
478 #ifdef WIN32
479 SetLastError(save_errno);
480 #else
481 errno = save_errno;
482 #endif
485 return dgettext("ecpg", msgid);
488 #endif /* ENABLE_NLS */