OSDN Git Service

8d4146cfae3de8077b3aa222e8651e23348cc152
[pg-rex/syncrep.git] / src / interfaces / ecpg / ecpglib / connect.c
1 /* $PostgreSQL: pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.44 2007/09/30 11:38:48 meskes Exp $ */
2
3 #define POSTGRES_ECPG_INTERNAL
4 #include "postgres_fe.h"
5
6 #include "ecpg-pthread-win32.h"
7 #include "ecpgtype.h"
8 #include "ecpglib.h"
9 #include "ecpgerrno.h"
10 #include "extern.h"
11 #include "sqlca.h"
12
13 #ifdef ENABLE_THREAD_SAFETY
14 NON_EXEC_STATIC pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
15 static pthread_key_t    actual_connection_key;
16 #ifndef WIN32
17 static pthread_once_t   actual_connection_key_once = PTHREAD_ONCE_INIT;
18 #endif
19 #endif
20 static struct connection *actual_connection = NULL;
21 static struct connection *all_connections = NULL;
22
23 #ifdef ENABLE_THREAD_SAFETY
24 NON_EXEC_STATIC void
25 ecpg_actual_connection_init(void)
26 {
27         pthread_key_create(&actual_connection_key, NULL);
28 }
29
30 void
31 ecpg_pthreads_init(void)
32 {
33         pthread_once(&actual_connection_key_once, ecpg_actual_connection_init);
34 }
35 #endif
36
37 static struct connection *
38 ecpg_get_connection_nr(const char *connection_name)
39 {
40         struct connection *ret = NULL;
41
42         if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
43         {
44 #ifdef ENABLE_THREAD_SAFETY
45                 ret = pthread_getspecific(actual_connection_key);
46
47                 /*
48                  * if no connection in TSD for this thread, get the global default
49                  * connection and hope the user knows what they're doing (i.e. using
50                  * their own mutex to protect that connection from concurrent accesses
51                  */
52                 /* if !ret then  we  got the connection from TSD */
53                 if (NULL == ret)
54                         /* no TSD connection, going for global */
55                         ret = actual_connection;
56 #else
57                 ret = actual_connection;
58 #endif
59         }
60         else
61         {
62                 struct connection *con;
63
64                 for (con = all_connections; con != NULL; con = con->next)
65                 {
66                         if (strcmp(connection_name, con->name) == 0)
67                                 break;
68                 }
69                 ret = con;
70         }
71
72         return (ret);
73 }
74
75 struct connection *
76 ECPGget_connection(const char *connection_name)
77 {
78         struct connection *ret = NULL;
79
80         if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
81         {
82 #ifdef ENABLE_THREAD_SAFETY
83                 ret = pthread_getspecific(actual_connection_key);
84
85                 /*
86                  * if no connection in TSD for this thread, get the global default
87                  * connection and hope the user knows what they're doing (i.e. using
88                  * their own mutex to protect that connection from concurrent accesses
89                  */
90                 /* if !ret then  we  got the connection from TSD */
91                 if (NULL == ret)
92                         /* no TSD connection here either, using global */
93                         ret = actual_connection;
94 #else
95                 ret = actual_connection;
96 #endif
97         }
98         else
99         {
100 #ifdef ENABLE_THREAD_SAFETY
101                 pthread_mutex_lock(&connections_mutex);
102 #endif
103
104                 ret = ecpg_get_connection_nr(connection_name);
105
106 #ifdef ENABLE_THREAD_SAFETY
107                 pthread_mutex_unlock(&connections_mutex);
108 #endif
109         }
110
111         return (ret);
112 }
113
114 static void
115 ecpg_finish(struct connection * act)
116 {
117         if (act != NULL)
118         {
119                 struct ECPGtype_information_cache *cache,
120                                    *ptr;
121
122                 ECPGdeallocate_all_conn(0, ECPG_COMPAT_PGSQL, act);
123                 PQfinish(act->connection);
124
125                 /*
126                  * no need to lock connections_mutex - we're always called by
127                  * ECPGdisconnect or ECPGconnect, which are holding the lock
128                  */
129
130                 /* remove act from the list */
131                 if (act == all_connections)
132                         all_connections = act->next;
133                 else
134                 {
135                         struct connection *con;
136
137                         for (con = all_connections; con->next && con->next != act; con = con->next);
138                         if (con->next)
139                                 con->next = act->next;
140                 }
141
142 #ifdef ENABLE_THREAD_SAFETY
143                 if (pthread_getspecific(actual_connection_key) == act)
144                         pthread_setspecific(actual_connection_key, all_connections);
145 #endif
146                 if (actual_connection == act)
147                         actual_connection = all_connections;
148
149                 ECPGlog("ecpg_finish: Connection %s closed.\n", act->name);
150
151                 for (cache = act->cache_head; cache; ptr = cache, cache = cache->next, ECPGfree(ptr));
152                 ECPGfree(act->name);
153                 ECPGfree(act);
154         }
155         else
156                 ECPGlog("ecpg_finish: called an extra time.\n");
157 }
158
159 bool
160 ECPGsetcommit(int lineno, const char *mode, const char *connection_name)
161 {
162         struct connection *con = ECPGget_connection(connection_name);
163         PGresult   *results;
164
165         if (!ECPGinit(con, connection_name, lineno))
166                 return (false);
167
168         ECPGlog("ECPGsetcommit line %d action = %s connection = %s\n", lineno, mode, con->name);
169
170         if (con->autocommit == true && strncmp(mode, "off", strlen("off")) == 0)
171         {
172                 if (con->committed)
173                 {
174                         results = PQexec(con->connection, "begin transaction");
175                         if (!ECPGcheck_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL))
176                                 return false;
177                         PQclear(results);
178                         con->committed = false;
179                 }
180                 con->autocommit = false;
181         }
182         else if (con->autocommit == false && strncmp(mode, "on", strlen("on")) == 0)
183         {
184                 if (!con->committed)
185                 {
186                         results = PQexec(con->connection, "commit");
187                         if (!ECPGcheck_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL))
188                                 return false;
189                         PQclear(results);
190                         con->committed = true;
191                 }
192                 con->autocommit = true;
193         }
194
195         return true;
196 }
197
198 bool
199 ECPGsetconn(int lineno, const char *connection_name)
200 {
201         struct connection *con = ECPGget_connection(connection_name);
202
203         if (!ECPGinit(con, connection_name, lineno))
204                 return (false);
205
206 #ifdef ENABLE_THREAD_SAFETY
207         pthread_setspecific(actual_connection_key, con);
208 #else
209         actual_connection = con;
210 #endif
211         return true;
212 }
213
214
215 static void
216 ECPGnoticeReceiver(void *arg, const PGresult *result)
217 {
218         char       *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
219         char       *message = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
220         struct sqlca_t *sqlca = ECPGget_sqlca();
221
222         int                     sqlcode;
223
224         if (sqlstate == NULL)
225                 sqlstate = ECPG_SQLSTATE_ECPG_INTERNAL_ERROR;
226
227         if (message == NULL)            /* Shouldn't happen, but need to be sure */
228                 message = "No message received";
229
230         /* these are not warnings */
231         if (strncmp(sqlstate, "00", 2) == 0)
232                 return;
233
234         ECPGlog("ECPGnoticeReceiver %s\n", message);
235
236         /* map to SQLCODE for backward compatibility */
237         if (strcmp(sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME) == 0)
238                 sqlcode = ECPG_WARNING_UNKNOWN_PORTAL;
239         else if (strcmp(sqlstate, ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION) == 0)
240                 sqlcode = ECPG_WARNING_IN_TRANSACTION;
241         else if (strcmp(sqlstate, ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION) == 0)
242                 sqlcode = ECPG_WARNING_NO_TRANSACTION;
243         else if (strcmp(sqlstate, ECPG_SQLSTATE_DUPLICATE_CURSOR) == 0)
244                 sqlcode = ECPG_WARNING_PORTAL_EXISTS;
245         else
246                 sqlcode = 0;
247
248         strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
249         sqlca->sqlcode = sqlcode;
250         sqlca->sqlwarn[2] = 'W';
251         sqlca->sqlwarn[0] = 'W';
252
253         strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof(sqlca->sqlerrm.sqlerrmc));
254         sqlca->sqlerrm.sqlerrmc[sizeof(sqlca->sqlerrm.sqlerrmc) - 1] = 0;
255         sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
256
257         ECPGlog("raising sqlcode %d\n", sqlcode);
258 }
259
260
261 /* this contains some quick hacks, needs to be cleaned up, but it works */
262 bool
263 ECPGconnect(int lineno, int c, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit)
264 {
265         struct sqlca_t *sqlca = ECPGget_sqlca();
266         enum COMPAT_MODE compat = c;
267         struct connection *this;
268         char       *dbname = name ? ECPGstrdup(name, lineno) : NULL,
269                            *host = NULL,
270                            *tmp,
271                            *port = NULL,
272                            *realname = NULL,
273                            *options = NULL;
274
275         ECPGinit_sqlca(sqlca);
276
277         /* clear auto_mem structure because some error handling functions might access it */
278         ECPGclear_auto_mem();
279
280         if (INFORMIX_MODE(compat))
281         {
282                 char       *envname;
283
284                 /*
285                  * Informix uses an environment variable DBPATH that overrides the
286                  * connection parameters given here. We do the same with PG_DBPATH as
287                  * the syntax is different.
288                  */
289                 envname = getenv("PG_DBPATH");
290                 if (envname)
291                 {
292                         ECPGfree(dbname);
293                         dbname = ECPGstrdup(envname, lineno);
294                 }
295
296         }
297
298         if (dbname == NULL && connection_name == NULL)
299                 connection_name = "DEFAULT";
300
301 #if ENABLE_THREAD_SAFETY
302         ecpg_pthreads_init();
303 #endif
304
305         /* check if the identifier is unique */
306         if (ECPGget_connection(connection_name))
307         {
308                 ECPGfree(dbname);
309                 ECPGlog("connect: connection identifier %s is already in use\n",
310                                 connection_name);
311                 return false;
312         }
313
314         if ((this = (struct connection *) ECPGalloc(sizeof(struct connection), lineno)) == NULL)
315                 return false;
316
317         if (dbname != NULL)
318         {
319                 /* get the detail information out of dbname */
320                 if (strncmp(dbname, "tcp:", 4) == 0 || strncmp(dbname, "unix:", 5) == 0)
321                 {
322                         int                     offset = 0;
323
324                         /*
325                          * only allow protocols tcp and unix
326                          */
327                         if (strncmp(dbname, "tcp:", 4) == 0)
328                                 offset = 4;
329                         else if (strncmp(dbname, "unix:", 5) == 0)
330                                 offset = 5;
331
332                         if (strncmp(dbname + offset, "postgresql://", strlen("postgresql://")) == 0)
333                         {
334
335                                 /*------
336                                  * new style:
337                                  *      <tcp|unix>:postgresql://server[:port|:/unixsocket/path:]
338                                  *      [/db name][?options]
339                                  *------
340                                  */
341                                 offset += strlen("postgresql://");
342
343                                 tmp = strrchr(dbname + offset, '?');
344                                 if (tmp != NULL)        /* options given */
345                                 {
346                                         options = ECPGstrdup(tmp + 1, lineno);
347                                         *tmp = '\0';
348                                 }
349
350                                 tmp = last_dir_separator(dbname + offset);
351                                 if (tmp != NULL)        /* database name given */
352                                 {
353                                         realname = ECPGstrdup(tmp + 1, lineno);
354                                         *tmp = '\0';
355                                 }
356
357                                 tmp = strrchr(dbname + offset, ':');
358                                 if (tmp != NULL)        /* port number or Unix socket path given */
359                                 {
360                                         char       *tmp2;
361
362                                         *tmp = '\0';
363                                         if ((tmp2 = strchr(tmp + 1, ':')) != NULL)
364                                         {
365                                                 *tmp2 = '\0';
366                                                 host = ECPGstrdup(tmp + 1, lineno);
367                                                 if (strncmp(dbname, "unix:", 5) != 0)
368                                                 {
369                                                         ECPGlog("connect: socketname %s given for TCP connection in line %d\n", host, lineno);
370                                                         ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : "<DEFAULT>");
371                                                         if (host)
372                                                                 ECPGfree(host);
373
374                                                         /*
375                                                          * port not set yet if (port) ECPGfree(port);
376                                                          */
377                                                         if (options)
378                                                                 ECPGfree(options);
379                                                         if (realname)
380                                                                 ECPGfree(realname);
381                                                         if (dbname)
382                                                                 ECPGfree(dbname);
383                                                         free(this);
384                                                         return false;
385                                                 }
386                                         }
387                                         else
388                                                 port = ECPGstrdup(tmp + 1, lineno);
389                                 }
390
391                                 if (strncmp(dbname, "unix:", 5) == 0)
392                                 {
393                                         if (strcmp(dbname + offset, "localhost") != 0 && strcmp(dbname + offset, "127.0.0.1") != 0)
394                                         {
395                                                 ECPGlog("connect: non-localhost access via sockets in line %d\n", lineno);
396                                                 ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : "<DEFAULT>");
397                                                 if (host)
398                                                         ECPGfree(host);
399                                                 if (port)
400                                                         ECPGfree(port);
401                                                 if (options)
402                                                         ECPGfree(options);
403                                                 if (realname)
404                                                         ECPGfree(realname);
405                                                 if (dbname)
406                                                         ECPGfree(dbname);
407                                                 free(this);
408                                                 return false;
409                                         }
410                                 }
411                                 else
412                                         host = ECPGstrdup(dbname + offset, lineno);
413
414                         }
415                 }
416                 else
417                 {
418                         /* old style: dbname[@server][:port] */
419                         tmp = strrchr(dbname, ':');
420                         if (tmp != NULL)        /* port number given */
421                         {
422                                 port = ECPGstrdup(tmp + 1, lineno);
423                                 *tmp = '\0';
424                         }
425
426                         tmp = strrchr(dbname, '@');
427                         if (tmp != NULL)        /* host name given */
428                         {
429                                 host = ECPGstrdup(tmp + 1, lineno);
430                                 *tmp = '\0';
431                         }
432
433                         realname = (strlen(dbname) > 0) ? ECPGstrdup(dbname, lineno) : NULL;
434                 }
435         }
436         else
437                 realname = NULL;
438
439         /* add connection to our list */
440 #ifdef ENABLE_THREAD_SAFETY
441         pthread_mutex_lock(&connections_mutex);
442 #endif
443         if (connection_name != NULL)
444                 this->name = ECPGstrdup(connection_name, lineno);
445         else
446                 this->name = ECPGstrdup(realname, lineno);
447
448         this->cache_head = NULL;
449         this->prep_stmts = NULL;
450
451         if (all_connections == NULL)
452                 this->next = NULL;
453         else
454                 this->next = all_connections;
455
456         all_connections = this;
457 #ifdef ENABLE_THREAD_SAFETY
458         pthread_setspecific(actual_connection_key, all_connections);
459 #endif
460         actual_connection = all_connections;
461
462         ECPGlog("ECPGconnect: opening database %s on %s port %s %s%s%s%s\n",
463                         realname ? realname : "<DEFAULT>",
464                         host ? host : "<DEFAULT>",
465                         port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
466                         options ? "with options " : "", options ? options : "",
467                         user ? "for user " : "", user ? user : "");
468
469         this->connection = PQsetdbLogin(host, port, options, NULL, realname, user, passwd);
470
471         if (PQstatus(this->connection) == CONNECTION_BAD)
472         {
473                 const char *errmsg = PQerrorMessage(this->connection);
474                 const char *db = realname ? realname : "<DEFAULT>";
475
476                 ECPGlog("connect: could not open database %s on %s port %s %s%s%s%s in line %d\n\t%s\n",
477                                 db,
478                                 host ? host : "<DEFAULT>",
479                                 port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
480                                 options ? "with options " : "", options ? options : "",
481                                 user ? "for user " : "", user ? user : "",
482                                 lineno, errmsg);
483
484                 ecpg_finish(this);
485 #ifdef ENABLE_THREAD_SAFETY
486                 pthread_mutex_unlock(&connections_mutex);
487 #endif
488
489                 ECPGraise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
490                 if (host)
491                         ECPGfree(host);
492                 if (port)
493                         ECPGfree(port);
494                 if (options)
495                         ECPGfree(options);
496                 if (realname)
497                         ECPGfree(realname);
498                 if (dbname)
499                         ECPGfree(dbname);
500                 return false;
501         }
502 #ifdef ENABLE_THREAD_SAFETY
503         pthread_mutex_unlock(&connections_mutex);
504 #endif
505
506         if (host)
507                 ECPGfree(host);
508         if (port)
509                 ECPGfree(port);
510         if (options)
511                 ECPGfree(options);
512         if (realname)
513                 ECPGfree(realname);
514         if (dbname)
515                 ECPGfree(dbname);
516
517         this->committed = true;
518         this->autocommit = autocommit;
519
520         PQsetNoticeReceiver(this->connection, &ECPGnoticeReceiver, (void *) this);
521
522         return true;
523 }
524
525 bool
526 ECPGdisconnect(int lineno, const char *connection_name)
527 {
528         struct sqlca_t *sqlca = ECPGget_sqlca();
529         struct connection *con;
530
531 #ifdef ENABLE_THREAD_SAFETY
532         pthread_mutex_lock(&connections_mutex);
533 #endif
534
535         if (strcmp(connection_name, "ALL") == 0)
536         {
537                 ECPGinit_sqlca(sqlca);
538                 for (con = all_connections; con;)
539                 {
540                         struct connection *f = con;
541
542                         con = con->next;
543                         ecpg_finish(f);
544                 }
545         }
546         else
547         {
548                 con = ecpg_get_connection_nr(connection_name);
549
550                 if (!ECPGinit(con, connection_name, lineno))
551                 {
552 #ifdef ENABLE_THREAD_SAFETY
553                         pthread_mutex_unlock(&connections_mutex);
554 #endif
555                         return (false);
556                 }
557                 else
558                         ecpg_finish(con);
559         }
560
561 #ifdef ENABLE_THREAD_SAFETY
562         pthread_mutex_unlock(&connections_mutex);
563 #endif
564
565         return true;
566 }