From 4f70680177879501aa402d880b5099fe5df09847 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Sun, 15 Jun 2003 04:07:58 +0000 Subject: [PATCH] Make ecpg thread safe. Lee Kindness --- configure.in | 6 +- doc/src/sgml/ecpg.sgml | 9 +- doc/src/sgml/installation.sgml | 5 +- src/interfaces/ecpg/ecpglib/Makefile | 10 +-- src/interfaces/ecpg/ecpglib/connect.c | 107 ++++++++++++++++------ src/interfaces/ecpg/ecpglib/data.c | 8 +- src/interfaces/ecpg/ecpglib/descriptor.c | 19 ++-- src/interfaces/ecpg/ecpglib/error.c | 68 +++++++------- src/interfaces/ecpg/ecpglib/execute.c | 41 ++------- src/interfaces/ecpg/ecpglib/extern.h | 2 +- src/interfaces/ecpg/ecpglib/memory.c | 3 +- src/interfaces/ecpg/ecpglib/misc.c | 110 ++++++++++++++++++++--- src/interfaces/ecpg/ecpglib/prepare.c | 3 +- src/interfaces/ecpg/ecpglib/typename.c | 3 +- src/interfaces/ecpg/include/sqlca.h | 7 +- src/interfaces/ecpg/preproc/Makefile | 6 +- 16 files changed, 274 insertions(+), 133 deletions(-) diff --git a/configure.in b/configure.in index 9ae42b36e4..53e48a3fe0 100644 --- a/configure.in +++ b/configure.in @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -dnl $Header: /cvsroot/pgsql/configure.in,v 1.264 2003/06/14 19:21:42 momjian Exp $ +dnl $Header: /cvsroot/pgsql/configure.in,v 1.265 2003/06/15 04:07:58 momjian Exp $ dnl dnl Developers, please strive to achieve this order: dnl @@ -323,8 +323,8 @@ IFS=$ac_save_IFS # Enable libpq to be thread-safe # AC_MSG_CHECKING([allow threaded libpq]) -PGAC_ARG_BOOL(with, threads, no, [ --with-threads allow libpq to be thread-safe], - [AC_DEFINE([USE_THREADS], 1, [Define to 1 to build libpq with threads. (--with-threads)])]) +PGAC_ARG_BOOL(with, threads, no, [ --with-threads allow libpq and ecpg to be thread-safe], + [AC_DEFINE([USE_THREADS], 1, [Define to 1 to build libpq and ecpg to be thread-safe. (--with-threads)])]) AC_MSG_RESULT([$with_threads]) AC_SUBST(with_threads) diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml index 8cb4874912..d1d36db188 100644 --- a/doc/src/sgml/ecpg.sgml +++ b/doc/src/sgml/ecpg.sgml @@ -1,5 +1,5 @@ @@ -749,6 +749,13 @@ EXEC SQL INCLUDE filename; in the arguments specified for output. + + ecpg is thread-safe if it is compiled using + the --with-threads configure + command-line option. (You might need to use other threading + command-line options to compile your client code.) + + The preprocessor program is called ecpg and is included in a normal PostgreSQL installation. diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index 4c7a0a3517..e5d78e7ad8 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -1,4 +1,4 @@ - + <![%standalone-include[<productname>PostgreSQL</>]]> @@ -918,7 +918,8 @@ JAVACMD=$JAVA_HOME/bin/java <term><option>--with-threads</option></term> <listitem> <para> - Allow separate libpq threads to safely control their private connection handles. + Allow separate libpq and ecpg threads to safely control their + private connection handles. </para> </listitem> </varlistentry> diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile index 269f2d1461..68f1a2273d 100644 --- a/src/interfaces/ecpg/ecpglib/Makefile +++ b/src/interfaces/ecpg/ecpglib/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 1994, Regents of the University of California # -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/Makefile,v 1.3 2003/05/22 17:20:44 petere Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/Makefile,v 1.4 2003/06/15 04:07:58 momjian Exp $ # #------------------------------------------------------------------------- @@ -13,15 +13,15 @@ top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global NAME= ecpg -SO_MAJOR_VERSION= 3 -SO_MINOR_VERSION= 4.2 +SO_MAJOR_VERSION= 4 +SO_MINOR_VERSION= 1.1 -override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS) +override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS) $(THREAD_CFLAGS) OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \ connect.o misc.o -SHLIB_LINK = -L../pgtypeslib -lpgtypes $(libpq) +SHLIB_LINK = -L../pgtypeslib -lpgtypes $(libpq) $(THREAD_LIBS) all: all-lib diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c index 61e078da90..58692a1b1b 100644 --- a/src/interfaces/ecpg/ecpglib/connect.c +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -1,29 +1,53 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.6 2003/06/13 10:50:57 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.7 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" +#ifdef USE_THREADS +#include <pthread.h> +#endif #include "ecpgtype.h" #include "ecpglib.h" #include "ecpgerrno.h" #include "extern.h" #include "sqlca.h" -static struct connection *all_connections = NULL, - *actual_connection = NULL; +#ifdef USE_THREADS +static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +static struct connection *all_connections = NULL; +static struct connection *actual_connection = NULL; struct connection * ECPGget_connection(const char *connection_name) { - struct connection *con = all_connections; + struct connection *ret = NULL; - if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0) - return actual_connection; +#ifdef USE_THREADS + pthread_mutex_lock(&connections_mutex); +#endif - for (; con && strcmp(connection_name, con->name) != 0; con = con->next); - if (con) - return con; - else - return NULL; + if( (connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0) ) + { + ret = actual_connection; + } + else + { + struct connection *con; + + for( con = all_connections; con != NULL; con = con->next) + { + if( strcmp(connection_name, con->name) == 0 ) + break; + } + ret = con; + } + +#ifdef USE_THREADS + pthread_mutex_unlock(&connections_mutex); +#endif + + return( ret ); } static void @@ -37,6 +61,10 @@ ecpg_finish(struct connection * act) ECPGlog("ecpg_finish: finishing %s.\n", act->name); PQfinish(act->connection); + /* no need to lock connections_mutex - we're always called + by ECPGdisconnect or ECPGconnect, which are holding + the lock */ + /* remove act from the list */ if (act == all_connections) all_connections = act->next; @@ -118,17 +146,18 @@ ECPGsetconn(int lineno, const char *connection_name) static void ECPGnoticeProcessor_raise(int code, const char *message) { - sqlca.sqlcode = code; - strncpy(sqlca.sqlerrm.sqlerrmc, message, sizeof(sqlca.sqlerrm.sqlerrmc)); - sqlca.sqlerrm.sqlerrmc[sizeof(sqlca.sqlerrm.sqlerrmc) - 1] = 0; - sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); + struct sqlca_t *sqlca = ECPGget_sqlca(); + sqlca->sqlcode = code; + strncpy(sqlca->sqlerrm.sqlerrmc, message, sizeof(sqlca->sqlerrm.sqlerrmc)); + sqlca->sqlerrm.sqlerrmc[sizeof(sqlca->sqlerrm.sqlerrmc) - 1] = 0; + sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc); /* remove trailing newline */ - if (sqlca.sqlerrm.sqlerrml - && sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] == '\n') + if (sqlca->sqlerrm.sqlerrml + && sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] == '\n') { - sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] = 0; - sqlca.sqlerrm.sqlerrml--; + sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] = 0; + sqlca->sqlerrm.sqlerrml--; } ECPGlog("raising sqlcode %d\n", code); @@ -141,6 +170,8 @@ ECPGnoticeProcessor_raise(int code, const char *message) static void ECPGnoticeProcessor(void *arg, const char *message) { + struct sqlca_t *sqlca = ECPGget_sqlca(); + /* these notices raise an error */ if (strncmp(message, "WARNING: ", 9)) { @@ -245,7 +276,7 @@ ECPGnoticeProcessor(void *arg, const char *message) if (strstr(message, "cannot be rolled back")) return; - /* these and other unmentioned should set sqlca.sqlwarn[2] */ + /* these and other unmentioned should set sqlca->sqlwarn[2] */ /* WARNING: The ':' operator is deprecated. Use exp(x) instead. */ /* WARNING: Rel *: Uninitialized page 0 - fixing */ /* WARNING: PortalHeapMemoryFree: * not in alloc set! */ @@ -253,14 +284,15 @@ ECPGnoticeProcessor(void *arg, const char *message) /* WARNING: identifier "*" will be truncated to "*" */ /* WARNING: InvalidateSharedInvalid: cache state reset */ /* WARNING: RegisterSharedInvalid: SI buffer overflow */ - sqlca.sqlwarn[2] = 'W'; - sqlca.sqlwarn[0] = 'W'; + sqlca->sqlwarn[2] = 'W'; + sqlca->sqlwarn[0] = 'W'; } /* this contains some quick hacks, needs to be cleaned up, but it works */ bool ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit) { + struct sqlca_t *sqlca = ECPGget_sqlca(); struct connection *this; char *dbname = strdup(name), *host = NULL, @@ -269,7 +301,7 @@ ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, *realname = NULL, *options = NULL; - ECPGinit_sqlca(); + ECPGinit_sqlca(sqlca); if ((this = (struct connection *) ECPGalloc(sizeof(struct connection), lineno)) == NULL) return false; @@ -394,6 +426,9 @@ ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, realname = strdup(dbname); /* add connection to our list */ +#ifdef USE_THREADS + pthread_mutex_lock(&connections_mutex); +#endif if (connection_name != NULL) this->name = ECPGstrdup(connection_name, lineno); else @@ -424,6 +459,9 @@ ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, set_backend_err(errmsg, lineno); ecpg_finish(this); +#ifdef USE_THREADS + pthread_mutex_unlock(&connections_mutex); +#endif ECPGlog("connect: could not open database %s on %s port %s %s%s%s%s in line %d\n\t%s\n", db, host ? host : "<DEFAULT>", @@ -445,6 +483,9 @@ ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, ECPGfree(dbname); return false; } +#ifdef USE_THREADS + pthread_mutex_unlock(&connections_mutex); +#endif if (host) ECPGfree(host); @@ -468,11 +509,16 @@ ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, bool ECPGdisconnect(int lineno, const char *connection_name) { + struct sqlca_t *sqlca = ECPGget_sqlca(); struct connection *con; +#ifdef USE_THREADS + pthread_mutex_lock(&connections_mutex); +#endif + if (strcmp(connection_name, "ALL") == 0) { - ECPGinit_sqlca(); + ECPGinit_sqlca(sqlca); for (con = all_connections; con;) { struct connection *f = con; @@ -486,10 +532,19 @@ ECPGdisconnect(int lineno, const char *connection_name) con = ECPGget_connection(connection_name); if (!ECPGinit(con, connection_name, lineno)) - return (false); + { +#ifdef USE_THREADS + pthread_mutex_unlock(&connections_mutex); +#endif + return (false); + } else - ecpg_finish(con); + ecpg_finish(con); } +#ifdef USE_THREADS + pthread_mutex_unlock(&connections_mutex); +#endif + return true; } diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c index cae1c500d9..8105f3a6cf 100644 --- a/src/interfaces/ecpg/ecpglib/data.c +++ b/src/interfaces/ecpg/ecpglib/data.c @@ -1,5 +1,6 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.4 2003/04/01 14:37:25 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.5 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include <stdlib.h> @@ -21,6 +22,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, char *var, char *ind, long varcharsize, long offset, long ind_offset, bool isarray) { + struct sqlca_t *sqlca = ECPGget_sqlca(); char *pval = (char *) PQgetvalue(results, act_tuple, act_field); ECPGlog("ECPGget_data line %d: RESULT: %s offset: %ld\n", lineno, pval ? pval : "", offset); @@ -328,7 +330,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, default: break; } - sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; + sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W'; } } break; @@ -373,7 +375,7 @@ ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, default: break; } - sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; + sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W'; variable->len = varcharsize; } diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c index 0a5e5a7195..ade737beea 100644 --- a/src/interfaces/ecpg/ecpglib/descriptor.c +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -1,8 +1,9 @@ /* dynamic SQL support routines * - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.2 2003/05/30 13:22:02 meskes Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.3 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include "pg_type.h" @@ -51,14 +52,15 @@ bool ECPGget_desc_header(int lineno, char *desc_name, int *count) { PGresult *ECPGresult; + struct sqlca_t *sqlca = ECPGget_sqlca(); - ECPGinit_sqlca(); + ECPGinit_sqlca(sqlca); ECPGresult = ECPGresultByDescriptor(lineno, desc_name); if (!ECPGresult) return false; *count = PQnfields(ECPGresult); - sqlca.sqlerrd[2] = 1; + sqlca->sqlerrd[2] = 1; ECPGlog("ECPGget_desc_header: found %d attributes.\n", *count); return true; } @@ -149,9 +151,10 @@ ECPGget_desc(int lineno, char *desc_name, int index,...) int ntuples, act_tuple; struct variable data_var; + struct sqlca_t *sqlca = ECPGget_sqlca(); va_start(args, index); - ECPGinit_sqlca(); + ECPGinit_sqlca(sqlca); ECPGresult = ECPGresultByDescriptor(lineno, desc_name); if (!ECPGresult) return (false); @@ -378,7 +381,7 @@ ECPGget_desc(int lineno, char *desc_name, int index,...) ECPGlog("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index)); } } - sqlca.sqlerrd[2] = ntuples; + sqlca->sqlerrd[2] = ntuples; return (true); } @@ -387,8 +390,9 @@ ECPGdeallocate_desc(int line, const char *name) { struct descriptor *i; struct descriptor **lastptr = &all_descriptors; + struct sqlca_t *sqlca = ECPGget_sqlca(); - ECPGinit_sqlca(); + ECPGinit_sqlca(sqlca); for (i = all_descriptors; i; lastptr = &i->next, i = i->next) { if (!strcmp(name, i->name)) @@ -408,8 +412,9 @@ bool ECPGallocate_desc(int line, const char *name) { struct descriptor *new; + struct sqlca_t *sqlca = ECPGget_sqlca(); - ECPGinit_sqlca(); + ECPGinit_sqlca(sqlca); new = (struct descriptor *) ECPGalloc(sizeof(struct descriptor), line); if (!new) return false; diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c index 578e7c5603..2203b7214a 100644 --- a/src/interfaces/ecpg/ecpglib/error.c +++ b/src/interfaces/ecpg/ecpglib/error.c @@ -1,5 +1,6 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.2 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include <stdio.h> @@ -17,117 +18,118 @@ static char *ECPGerr; void ECPGraise(int line, int code, const char *str) { - sqlca.sqlcode = code; + struct sqlca_t *sqlca = ECPGget_sqlca(); + sqlca->sqlcode = code; switch (code) { case ECPG_NOT_FOUND: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "No data found in line %d.", line); break; case ECPG_OUT_OF_MEMORY: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Out of memory in line %d.", line); break; case ECPG_UNSUPPORTED: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Unsupported type %s in line %d.", str, line); break; case ECPG_TOO_MANY_ARGUMENTS: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Too many arguments in line %d.", line); break; case ECPG_TOO_FEW_ARGUMENTS: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Too few arguments in line %d.", line); break; case ECPG_INT_FORMAT: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Not correctly formatted int type: %s line %d.", str, line); break; case ECPG_UINT_FORMAT: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Not correctly formatted unsigned type: %s in line %d.", str, line); break; case ECPG_FLOAT_FORMAT: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Not correctly formatted floating-point type: %s in line %d.", str, line); break; case ECPG_CONVERT_BOOL: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Unable to convert %s to bool on line %d.", str, line); break; case ECPG_EMPTY: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Empty query in line %d.", line); break; case ECPG_MISSING_INDICATOR: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "NULL value without indicator in line %d.", line); break; case ECPG_NO_ARRAY: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Variable is not an array in line %d.", line); break; case ECPG_DATA_NOT_ARRAY: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Data read from backend is not an array in line %d.", line); break; case ECPG_ARRAY_INSERT: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Trying to insert an array of variables in line %d.", line); break; case ECPG_NO_CONN: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "No such connection %s in line %d.", str, line); break; case ECPG_NOT_CONN: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Not connected to '%s' in line %d.", str, line); break; case ECPG_INVALID_STMT: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Invalid statement name %s in line %d.", str, line); break; case ECPG_UNKNOWN_DESCRIPTOR: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Descriptor %s not found in line %d.", str, line); break; case ECPG_INVALID_DESCRIPTOR_INDEX: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Descriptor index out of range in line %d.", line); break; case ECPG_UNKNOWN_DESCRIPTOR_ITEM: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Unknown descriptor item %s in line %d.", str, line); break; case ECPG_VAR_NOT_NUMERIC: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Variable is not a numeric type in line %d.", line); break; case ECPG_VAR_NOT_CHAR: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Variable is not a character type in line %d.", line); break; @@ -138,29 +140,29 @@ ECPGraise(int line, int code, const char *str) /* strip trailing newline */ if (slen > 0 && str[slen - 1] == '\n') slen--; - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "'%.*s' in line %d.", slen, str, line); break; } case ECPG_TRANS: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Error in transaction processing in line %d.", line); break; case ECPG_CONNECT: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "Could not connect to database %s in line %d.", str, line); break; default: - snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "SQL error #%d in line %d.", code, line); break; } - sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); - ECPGlog("raising sqlcode %d in line %d, '%s'.\n", code, line, sqlca.sqlerrm.sqlerrmc); + sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc); + ECPGlog("raising sqlcode %d in line %d, '%s'.\n", code, line, sqlca->sqlerrm.sqlerrmc); /* free all memory we have allocated for the user */ ECPGfree_auto_mem(); @@ -193,6 +195,8 @@ ECPGerrmsg(void) void sqlprint(void) { - sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; - fprintf(stderr, "sql error %s\n", sqlca.sqlerrm.sqlerrmc); + struct sqlca_t *sqlca = ECPGget_sqlca(); + + sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml] = '\0'; + fprintf(stderr, "sql error %s\n", sqlca->sqlerrm.sqlerrmc); } diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c index 03d1fbd646..108bbe05ab 100644 --- a/src/interfaces/ecpg/ecpglib/execute.c +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.9 2003/06/13 10:50:57 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.10 2003/06/15 04:07:58 momjian Exp $ */ /* * The aim is to get a simpler inteface to the database routines. @@ -13,6 +13,7 @@ /* Taken over as part of PostgreSQL by Michael Meskes <meskes@postgresql.org> on Feb. 5th, 1998 */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include <stdio.h> @@ -31,34 +32,6 @@ #include "pgtypes_timestamp.h" #include "pgtypes_interval.h" -/* variables visible to the programs */ -struct sqlca sqlca = -{ - { - 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' ' - }, - sizeof(struct sqlca), - 0, - { - 0, - { - 0 - } - }, - { - 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' ' - }, - { - 0, 0, 0, 0, 0, 0 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - 0, 0, 0, 0, 0, 0, 0, 0 - } -}; - /* This function returns a newly malloced string that has the \ in the argument quoted with \ and the ' quoted with ' as SQL92 says. */ @@ -1130,6 +1103,8 @@ ECPGexecute(struct statement * stmt) */ { bool clear_result = TRUE; + struct sqlca_t *sqlca = ECPGget_sqlca(); + errmsg = PQresultErrorMessage(results); set_backend_err(errmsg, stmt->lineno); @@ -1142,7 +1117,7 @@ ECPGexecute(struct statement * stmt) case PGRES_TUPLES_OK: nfields = PQnfields(results); - sqlca.sqlerrd[2] = ntuples = PQntuples(results); + sqlca->sqlerrd[2] = ntuples = PQntuples(results); status = true; if (ntuples < 1) @@ -1199,10 +1174,10 @@ ECPGexecute(struct statement * stmt) case PGRES_COMMAND_OK: status = true; cmdstat = PQcmdStatus(results); - sqlca.sqlerrd[1] = PQoidValue(results); - sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); + sqlca->sqlerrd[1] = PQoidValue(results); + sqlca->sqlerrd[2] = atol(PQcmdTuples(results)); ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, cmdstat); - if (!sqlca.sqlerrd[2] && ( !strncmp(cmdstat, "UPDATE", 6) + if (!sqlca->sqlerrd[2] && ( !strncmp(cmdstat, "UPDATE", 6) || !strncmp(cmdstat, "INSERT", 6) || !strncmp(cmdstat, "DELETE", 6))) ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL); diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h index a5014512c9..a43526c760 100644 --- a/src/interfaces/ecpg/ecpglib/extern.h +++ b/src/interfaces/ecpg/ecpglib/extern.h @@ -3,6 +3,7 @@ #include "postgres_fe.h" #include "libpq-fe.h" +#include "sqlca.h" /* Here are some methods used by the lib. */ @@ -19,7 +20,6 @@ void ECPGadd_mem(void *ptr, int lineno); bool ECPGget_data(const PGresult *, int, int, int, enum ECPGttype type, enum ECPGttype, char *, char *, long, long, long, bool); struct connection *ECPGget_connection(const char *); -void ECPGinit_sqlca(void); char *ECPGalloc(long, int); char *ECPGrealloc(void *, long, int); void ECPGfree(void *); diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c index a94b293de4..1a5de833d1 100644 --- a/src/interfaces/ecpg/ecpglib/memory.c +++ b/src/interfaces/ecpg/ecpglib/memory.c @@ -1,5 +1,6 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.2 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include "ecpgtype.h" diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c index 48223d0568..5c5128dcef 100644 --- a/src/interfaces/ecpg/ecpglib/misc.c +++ b/src/interfaces/ecpg/ecpglib/misc.c @@ -1,20 +1,24 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.2 2003/03/21 15:31:04 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.3 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include <unistd.h> +#ifdef USE_THREADS +#include <pthread.h> +#endif #include "ecpgtype.h" #include "ecpglib.h" #include "ecpgerrno.h" #include "extern.h" #include "sqlca.h" -static struct sqlca sqlca_init = +static struct sqlca_t sqlca_init = { { 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' ' }, - sizeof(struct sqlca), + sizeof(struct sqlca_t), 0, { 0, @@ -36,19 +40,54 @@ static struct sqlca sqlca_init = } }; -static int simple_debug = 0; +#ifdef USE_THREADS +static pthread_key_t sqlca_key; +static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT; +#else +static struct sqlca_t sqlca = +{ + { + 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' ' + }, + sizeof(struct sqlca_t), + 0, + { + 0, + { + 0 + } + }, + { + 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' ' + }, + { + 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + } +}; +#endif + +#ifdef USE_THREADS +static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif +static int simple_debug = 0; static FILE *debugstream = NULL; -void -ECPGinit_sqlca(void) +void ECPGinit_sqlca(struct sqlca_t *sqlca) { - memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); + memcpy((char *)sqlca, (char *)&sqlca_init, sizeof(struct sqlca_t)); } bool ECPGinit(const struct connection * con, const char *connection_name, const int lineno) { - ECPGinit_sqlca(); + struct sqlca_t *sqlca = ECPGget_sqlca(); + ECPGinit_sqlca(sqlca); if (con == NULL) { ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL"); @@ -58,6 +97,33 @@ ECPGinit(const struct connection * con, const char *connection_name, const int l return (true); } +#ifdef USE_THREADS +static void ecpg_sqlca_key_init(void) +{ + pthread_key_create(&sqlca_key, NULL); +} +#endif + +struct sqlca_t *ECPGget_sqlca(void) +{ +#ifdef USE_THREADS + struct sqlca_t *sqlca; + + pthread_once(&sqlca_key_once, ecpg_sqlca_key_init); + + sqlca = pthread_getspecific(&sqlca_key); + if( sqlca == NULL ) + { + sqlca = malloc(sizeof(struct sqlca_t)); + ECPGinit_sqlca(sqlca); + pthread_setspecific(&sqlca_key, sqlca); + } + return( sqlca ); +#else + return( &sqlca ); +#endif +} + bool ECPGstatus(int lineno, const char *connection_name) { @@ -123,9 +189,17 @@ ECPGtrans(int lineno, const char *connection_name, const char *transaction) void ECPGdebug(int n, FILE *dbgs) { +#ifdef USE_THREADS + pthread_mutex_lock(&debug_mutex); +#endif + simple_debug = n; debugstream = dbgs; ECPGlog("ECPGdebug: set to %d\n", simple_debug); + +#ifdef USE_THREADS + pthread_mutex_unlock(&debug_mutex); +#endif } void @@ -133,12 +207,20 @@ ECPGlog(const char *format,...) { va_list ap; - if (simple_debug) - { - char *f = (char *) malloc(strlen(format) + 100); +#ifdef USE_THREADS + pthread_mutex_lock(&debug_mutex); +#endif - if (!f) + if( simple_debug ) + { + char *f = (char *)malloc(strlen(format) + 100); + if( f == NULL ) + { +#ifdef USE_THREADS + pthread_mutex_unlock(&debug_mutex); +#endif return; + } sprintf(f, "[%d]: %s", (int) getpid(), format); @@ -148,4 +230,8 @@ ECPGlog(const char *format,...) ECPGfree(f); } + +#ifdef USE_THREADS + pthread_mutex_unlock(&debug_mutex); +#endif } diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c index 13dd2408f2..7de8367184 100644 --- a/src/interfaces/ecpg/ecpglib/prepare.c +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -1,5 +1,6 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.2 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include <ctype.h> diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c index 684aa9cfc8..c329398471 100644 --- a/src/interfaces/ecpg/ecpglib/typename.c +++ b/src/interfaces/ecpg/ecpglib/typename.c @@ -1,5 +1,6 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/typename.c,v 1.3 2003/03/27 14:29:17 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/typename.c,v 1.4 2003/06/15 04:07:58 momjian Exp $ */ +#define POSTGRES_ECPG_INTERNAL #include "postgres_fe.h" #include <stdlib.h> diff --git a/src/interfaces/ecpg/include/sqlca.h b/src/interfaces/ecpg/include/sqlca.h index 7987eb0d3c..8af23921d7 100644 --- a/src/interfaces/ecpg/include/sqlca.h +++ b/src/interfaces/ecpg/include/sqlca.h @@ -16,7 +16,7 @@ extern "C" { #endif -struct sqlca +struct sqlca_t { char sqlcaid[8]; long sqlabc; @@ -53,8 +53,11 @@ struct sqlca char sqlext[8]; }; -extern DLLIMPORT struct sqlca sqlca; +struct sqlca_t *ECPGget_sqlca(void); +#ifndef POSTGRES_ECPG_INTERNAL +# define sqlca (*ECPGget_sqlca()) +#endif #ifdef __cplusplus } diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile index 5941c154fc..7dd4e9375f 100644 --- a/src/interfaces/ecpg/preproc/Makefile +++ b/src/interfaces/ecpg/preproc/Makefile @@ -1,11 +1,11 @@ -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Makefile,v 1.91 2003/03/18 10:46:39 meskes Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Makefile,v 1.92 2003/06/15 04:07:58 momjian Exp $ subdir = src/interfaces/ecpg/preproc top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -MAJOR_VERSION=2 -MINOR_VERSION=12 +MAJOR_VERSION=3 +MINOR_VERSION=0 PATCHLEVEL=0 override CPPFLAGS := -I$(srcdir)/../include -I$(srcdir) $(CPPFLAGS) \