ECPG, threading and pooling

Started by Shridhar Daithankar<shridhar_daithankar@persistent.co.in>almost 23 years ago15 messages

Hi all,

I would like to use ECPG as it is relatively easy to code. However my
application is multithreaded and also uses connecion pools.

I would like to know if ECPG is thread safe and I can use an arbitrary PGconn*
object pulled from a connection pool.

Do I need to use connection name? I mean how do I store connection name in an
array or so?

Otherwise I would have to fiddle with libpq which is bit more difficult than
ECPG.

TIA

Shridhar

#2Michael Meskes
meskes@postgresql.org
In reply to: Shridhar Daithankar<shridhar_daithankar@persistent.co.in> (#1)
Re: ECPG, threading and pooling

On Thu, Jan 23, 2003 at 02:40:33PM +0530, Shridhar Daithankar<shridhar_daithankar@persistent.co.in> wrote:

I would like to use ECPG as it is relatively easy to code. However my
application is multithreaded and also uses connecion pools.

I'm afraid it needs some work to be thread-safe. sqlca is defined
statically. No big deal it seems to implement a thread safe version but
I haven't yet found the time.

I would like to know if ECPG is thread safe and I can use an arbitrary PGconn*
object pulled from a connection pool.

Not sure what you mean with this?

Do I need to use connection name? I mean how do I store connection name in an
array or so?

How else would you decide against whcih connection to run the statement?

Michael
--
Michael Meskes
Email: Michael@Fam-Meskes.De
ICQ: 179140304
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#3Shridhar Daithankar
shridhar_daithankar@persistent.co.in
In reply to: Michael Meskes (#2)
Re: ECPG, threading and pooling

On Thursday 23 January 2003 08:40 pm, you wrote:

On Thu, Jan 23, 2003 at 02:40:33PM +0530, Shridhar

Daithankar<shridhar_daithankar@persistent.co.in> wrote:

I would like to know if ECPG is thread safe and I can use an arbitrary
PGconn* object pulled from a connection pool.

Not sure what you mean with this?

Well, when using libpq, I create a pool of PGconn*. What pool I create with
ecpg? Looking at ecpglib.h, it seems that I need to create a pool of char *

Do I need to use connection name? I mean how do I store connection name
in an array or so?

How else would you decide against whcih connection to run the statement?

That is correct but I did not find any example of how to use a particular
connection in an SQL statement. It would be a fairly trivial guess that the
connection name is one of the arguments to ECPGdo. But how the SQL statement
look like, I don't have a clue. Also I don't know know how to use ECPGsetconn
to set a connection. No documentation on that.

I can guess that ECPG maintains a mapping between connection name and PGconn*
object for a user. If possible it would be very helpful to form a ecpg
connection on top a user supplied PGconn* object.

I just downloaded 7.3.1 to take a look at. I hope to make a contribution if
possible.

Shridhar

#4Michael Meskes
meskes@postgresql.org
In reply to: Shridhar Daithankar (#3)
Re: ECPG, threading and pooling

On Thu, Jan 23, 2003 at 08:58:24PM +0530, Shridhar Daithankar wrote:

Well, when using libpq, I create a pool of PGconn*. What pool I create with
ecpg? Looking at ecpglib.h, it seems that I need to create a pool of char *

Yes. You use it as EXEC SQL AT :connection_var SELECT ...

That is correct but I did not find any example of how to use a particular
connection in an SQL statement. It would be a fairly trivial guess that the
connection name is one of the arguments to ECPGdo. But how the SQL statement
look like, I don't have a clue. Also I don't know know how to use ECPGsetconn
to set a connection. No documentation on that.

You don't call either one yourself. Let ecpg do that job. Some of the
test cases in the source tree use AT. Just look under .../ecpg/test.

I can guess that ECPG maintains a mapping between connection name and PGconn*
object for a user. If possible it would be very helpful to form a ecpg
connection on top a user supplied PGconn* object.

Why? It's possible, but you have to work with structures not meant to be
used outside the lib.

Michael
--
Michael Meskes
Email: Michael@Fam-Meskes.De
ICQ: 179140304
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#5Lee Kindness
lkindness@csl.co.uk
In reply to: Michael Meskes (#2)
1 attachment(s)
Re: ECPG, threading and pooling

Michael,

Michael Meskes writes:

On Thu, Jan 23, 2003 at 02:40:33PM +0530, Shridhar Daithankar<shridhar_daithankar@persistent.co.in> wrote:

I would like to use ECPG as it is relatively easy to code. However my
application is multithreaded and also uses connecion pools.

I'm afraid it needs some work to be thread-safe. sqlca is defined
statically. No big deal it seems to implement a thread safe version but
I haven't yet found the time.

I've spent a bit of time on making ecpg thread safe over Christmas,
while it's not finished i'm sure the attached patch is at least useful
and a step in the right direction.

Lee.

Attachments:

ecpg-threadsafe.patch.gzapplication/octet-streamDownload
#6Michael Meskes
meskes@postgresql.org
In reply to: Lee Kindness (#5)
Re: ECPG, threading and pooling

On Mon, Jan 27, 2003 at 11:52:18AM +0000, Lee Kindness wrote:
Content-Description: message body text

Michael,
...
I've spent a bit of time on making ecpg thread safe over Christmas,
while it's not finished i'm sure the attached patch is at least useful
and a step in the right direction.

Thanks a lot. I have no experience in multithreaded software development
so I cannot completely check your patch but it surely looks good. Shall
I commit it, or will you? I think we should get it into CVS for all to
test.

Just two questions:
- Is it neccessary to patch c.h for ecpg?
- Not sure with the version numbering but I can fix that later.

Michael
--
Michael Meskes
Email: Michael@Fam-Meskes.De
ICQ: 179140304
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#7Lee Kindness
lkindness@csl.co.uk
In reply to: Michael Meskes (#6)
Re: ECPG, threading and pooling

Michael Meskes writes:

On Mon, Jan 27, 2003 at 11:52:18AM +0000, Lee Kindness wrote:

I've spent a bit of time on making ecpg thread safe over Christmas,
while it's not finished i'm sure the attached patch is at least useful
and a step in the right direction.

Thanks a lot. I have no experience in multithreaded software development
so I cannot completely check your patch but it surely looks good. Shall
I commit it, or will you? I think we should get it into CVS for all to
test.

Problem with it is it needs some changes to configure.in (and
associated files) for it to be worthwhile. Checks would need to be
added to determine is threads are supported on the build platform and
the needed compile and link flags. I'm trying to get this together for
libpq too.

Just two questions:
- Is it neccessary to patch c.h for ecpg?

No, but each ecpg source file would need something like:

#ifdef HAVE_PTHREAD_H
# include <pthread.h>
# include <errno.h>
#endif

It's in c.h just now for ease of testing.

- Not sure with the version numbering but I can fix that later.

As discussed on pgsql-hackers after the 7.3 release any "binary
incompatible" library change needs a major version number increase.

Lee.

#8Shridhar Daithankar
shridhar_daithankar@persistent.co.in
In reply to: Lee Kindness (#7)
Re: ECPG, threading and pooling

On 27 Jan 2003 at 14:06, Lee Kindness wrote:

Michael Meskes writes:

On Mon, Jan 27, 2003 at 11:52:18AM +0000, Lee Kindness wrote:

I've spent a bit of time on making ecpg thread safe over Christmas,
while it's not finished i'm sure the attached patch is at least useful
and a step in the right direction.

Thanks a lot. I have no experience in multithreaded software development
so I cannot completely check your patch but it surely looks good. Shall
I commit it, or will you? I think we should get it into CVS for all to
test.

Problem with it is it needs some changes to configure.in (and
associated files) for it to be worthwhile. Checks would need to be
added to determine is threads are supported on the build platform and
the needed compile and link flags. I'm trying to get this together for
libpq too.

Just out of curiosity, what happens when there are more than one competing
threading libraries? Like native threads and linuxthreads on freeBSD?

Bye
Shridhar

--
Reliable source, n.: The guy you just met.

#9Lee Kindness
lkindness@csl.co.uk
In reply to: Shridhar Daithankar (#8)
Re: ECPG, threading and pooling

Shridhar Daithankar writes:

On 27 Jan 2003 at 14:06, Lee Kindness wrote:

Michael Meskes writes:

Thanks a lot. I have no experience in multithreaded software development
so I cannot completely check your patch but it surely looks good. Shall
I commit it, or will you? I think we should get it into CVS for all to
test.

Problem with it is it needs some changes to configure.in (and
associated files) for it to be worthwhile. Checks would need to be
added to determine is threads are supported on the build platform and
the needed compile and link flags. I'm trying to get this together for
libpq too.

Just out of curiosity, what happens when there are more than one competing
threading libraries? Like native threads and linuxthreads on freeBSD?

It's all down to the checks in configure... Looking at many packages
which have threads check in configure (e.g. openldap, mysql, mozilla,
glib, ...) some let the user specify which theading library to use
(e.g --with-threads=linuxthreads (or something)) while others just
seem to pick up the first available...

To be honest I am quite daunted by the checks needed (every other
package seems to be doing the check differently!) and any/all help
would be welcome!

L.

#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Michael Meskes (#6)
Re: ECPG, threading and pooling

Michael Meskes <meskes@postgresql.org> writes:

- Is it neccessary to patch c.h for ecpg?

If you commit that part, it will be reverted forthwith (especially the
hardwired #define HAVE_PTHREAD_H ;-)).

Keep the thread hacking on the client side, please. Isn't there one of
the ecpg/include/ files that would be suitable?

regards, tom lane

#11Michael Meskes
meskes@postgresql.org
In reply to: Lee Kindness (#7)
Re: ECPG, threading and pooling

On Mon, Jan 27, 2003 at 02:06:26PM +0000, Lee Kindness wrote:

- Not sure with the version numbering but I can fix that later.

As discussed on pgsql-hackers after the 7.3 release any "binary
incompatible" library change needs a major version number increase.

Sorry, I was not precise enough. The major number has to be inceased,
it's the minor numbers that need fixing. :-)

Michael
--
Michael Meskes
Email: Michael@Fam-Meskes.De
ICQ: 179140304
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#12Michael Meskes
meskes@postgresql.org
In reply to: Tom Lane (#10)
Re: ECPG, threading and pooling

On Mon, Jan 27, 2003 at 09:30:50AM -0500, Tom Lane wrote:

Michael Meskes <meskes@postgresql.org> writes:

- Is it neccessary to patch c.h for ecpg?

If you commit that part, it will be reverted forthwith (especially the
hardwired #define HAVE_PTHREAD_H ;-)).

Keep the thread hacking on the client side, please. Isn't there one of
the ecpg/include/ files that would be suitable?

Yes, there is. Better make that there are. There is a file called
extern.h both in preproc as well as in lib and each file in the
directory does include the extern.h in its own directory. So we just
need to add it twice I guess.

Michael
--
Michael Meskes
Email: Michael@Fam-Meskes.De
ICQ: 179140304
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#13Tom Lane
tgl@sss.pgh.pa.us
In reply to: Michael Meskes (#12)
Re: ECPG, threading and pooling

Michael Meskes <meskes@postgresql.org> writes:

Keep the thread hacking on the client side, please. Isn't there one of
the ecpg/include/ files that would be suitable?

Yes, there is. Better make that there are. There is a file called
extern.h both in preproc as well as in lib and each file in the
directory does include the extern.h in its own directory. So we just
need to add it twice I guess.

The preproc code doesn't need to be thread-safe does it?

One issue we already fought with for large-file support is that that
#define _REENTRANT probably needs to appear before you start to include
any system header files. You may find that the best way to handle it
is to make it a "-D_REENTRANT" added to CPPFLAGS (only within ecpg/lib),
rather than trying to find a safe place to put it in the .h files.

regards, tom lane

#14Michael Meskes
meskes@postgresql.org
In reply to: Tom Lane (#13)
Re: ECPG, threading and pooling

On Mon, Jan 27, 2003 at 10:26:53AM -0500, Tom Lane wrote:

The preproc code doesn't need to be thread-safe does it?

You're right of cause.

Michael
--
Michael Meskes
Email: Michael@Fam-Meskes.De
ICQ: 179140304
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#15Bruce Momjian
pgman@candle.pha.pa.us
In reply to: Lee Kindness (#5)
1 attachment(s)
Re: [HACKERS] ECPG, threading and pooling

Patch applied. I made adjustments to use the new configure thread
settings, and I added documentation. I also updated the interface
version numbers as included in the patch.

---------------------------------------------------------------------------

Lee Kindness wrote:
Content-Description: message body text

Michael,

Michael Meskes writes:

On Thu, Jan 23, 2003 at 02:40:33PM +0530, Shridhar Daithankar<shridhar_daithankar@persistent.co.in> wrote:

I would like to use ECPG as it is relatively easy to code. However my
application is multithreaded and also uses connecion pools.

I'm afraid it needs some work to be thread-safe. sqlca is defined
statically. No big deal it seems to implement a thread safe version but
I haven't yet found the time.

I've spent a bit of time on making ecpg thread safe over Christmas,
while it's not finished i'm sure the attached patch is at least useful
and a step in the right direction.

Lee.

[ Attachment, skipping... ]

---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to majordomo@postgresql.org so that your
message can get through to the mailing list cleanly

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Attachments:

/bjm/difftext/plainDownload
Index: configure.in
===================================================================
RCS file: /cvsroot/pgsql-server/configure.in,v
retrieving revision 1.264
diff -c -c -r1.264 configure.in
*** configure.in	14 Jun 2003 19:21:42 -0000	1.264
--- configure.in	15 Jun 2003 04:02:55 -0000
***************
*** 323,330 ****
  # 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)])])
  
  AC_MSG_RESULT([$with_threads])
  AC_SUBST(with_threads)
--- 323,330 ----
  # Enable libpq to be thread-safe
  #
  AC_MSG_CHECKING([allow threaded libpq])
! 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)
Index: doc/src/sgml/ecpg.sgml
===================================================================
RCS file: /cvsroot/pgsql-server/doc/src/sgml/ecpg.sgml,v
retrieving revision 1.43
diff -c -c -r1.43 ecpg.sgml
*** doc/src/sgml/ecpg.sgml	25 Mar 2003 16:15:35 -0000	1.43
--- doc/src/sgml/ecpg.sgml	15 Jun 2003 04:02:56 -0000
***************
*** 750,755 ****
--- 750,762 ----
    </para>
  
    <para>
+    <application>ecpg</application> is thread-safe if it is compiled using
+    the <literal>--with-threads</> <filename>configure</filename>
+    command-line option.  (You might need to use other threading
+    command-line options to compile your client code.)
+   </para>
+ 
+   <para>
     The preprocessor program is called <filename>ecpg</filename> and is
     included in a normal <productname>PostgreSQL</> installation.
     Embedded SQL programs are typically named with an extension
Index: doc/src/sgml/installation.sgml
===================================================================
RCS file: /cvsroot/pgsql-server/doc/src/sgml/installation.sgml,v
retrieving revision 1.134
diff -c -c -r1.134 installation.sgml
*** doc/src/sgml/installation.sgml	13 Jun 2003 23:10:07 -0000	1.134
--- doc/src/sgml/installation.sgml	15 Jun 2003 04:02:59 -0000
***************
*** 918,924 ****
         <term><option>--with-threads</option></term>
         <listitem>
          <para>
! 	 Allow separate libpq threads to safely control their private connection handles.
          </para>
         </listitem>
        </varlistentry>
--- 918,925 ----
         <term><option>--with-threads</option></term>
         <listitem>
          <para>
! 	 Allow separate libpq and ecpg threads to safely control their
!          private connection handles.
          </para>
         </listitem>
        </varlistentry>
Index: src/interfaces/ecpg/ecpglib/Makefile
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/Makefile,v
retrieving revision 1.3
diff -c -c -r1.3 Makefile
*** src/interfaces/ecpg/ecpglib/Makefile	22 May 2003 17:20:44 -0000	1.3
--- src/interfaces/ecpg/ecpglib/Makefile	15 Jun 2003 04:03:01 -0000
***************
*** 13,27 ****
  include $(top_builddir)/src/Makefile.global
  
  NAME= ecpg
! SO_MAJOR_VERSION= 3
! SO_MINOR_VERSION= 4.2
  
! override CPPFLAGS := -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS)
  
  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)
  
  all: all-lib
  
--- 13,27 ----
  include $(top_builddir)/src/Makefile.global
  
  NAME= ecpg
! SO_MAJOR_VERSION= 4
! SO_MINOR_VERSION= 1.1
  
! 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) $(THREAD_LIBS)
  
  all: all-lib
  
Index: src/interfaces/ecpg/ecpglib/connect.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/connect.c,v
retrieving revision 1.6
diff -c -c -r1.6 connect.c
*** src/interfaces/ecpg/ecpglib/connect.c	13 Jun 2003 10:50:57 -0000	1.6
--- src/interfaces/ecpg/ecpglib/connect.c	15 Jun 2003 04:03:01 -0000
***************
*** 1,29 ****
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/connect.c,v 1.6 2003/06/13 10:50:57 meskes Exp $ */
  
  #include "postgres_fe.h"
  
  #include "ecpgtype.h"
  #include "ecpglib.h"
  #include "ecpgerrno.h"
  #include "extern.h"
  #include "sqlca.h"
  
! static struct connection *all_connections = NULL,
! 		   *actual_connection = NULL;
  
  struct connection *
  ECPGget_connection(const char *connection_name)
  {
! 	struct connection *con = all_connections;
  
! 	if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0)
! 		return actual_connection;
  
! 	for (; con && strcmp(connection_name, con->name) != 0; con = con->next);
! 	if (con)
! 		return con;
! 	else
! 		return NULL;
  }
  
  static void
--- 1,53 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/connect.c,v 1.6 2003/06/13 10:50:57 meskes 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"
  
! #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 *ret = NULL;
  
! #ifdef USE_THREADS
!   pthread_mutex_lock(&connections_mutex);
! #endif
! 
!   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,42 ****
--- 61,70 ----
  		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,134 ****
  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);
  
  	/* remove trailing newline */
! 	if (sqlca.sqlerrm.sqlerrml
! 		&& sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] == '\n')
  	{
! 		sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] = 0;
! 		sqlca.sqlerrm.sqlerrml--;
  	}
  
  	ECPGlog("raising sqlcode %d\n", code);
--- 146,163 ----
  static void
  ECPGnoticeProcessor_raise(int code, const char *message)
  {
! 	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')
  	{
! 		sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml - 1] = 0;
! 		sqlca->sqlerrm.sqlerrml--;
  	}
  
  	ECPGlog("raising sqlcode %d\n", code);
***************
*** 141,146 ****
--- 170,177 ----
  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,251 ****
  	if (strstr(message, "cannot be rolled back"))
  		return;
  
! 	/* 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! */
--- 276,282 ----
  	if (strstr(message, "cannot be rolled back"))
  		return;
  
! 	/* 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,266 ****
  	/* WARNING: identifier "*" will be truncated to "*" */
  	/* WARNING: InvalidateSharedInvalid: cache state reset */
  	/* WARNING: RegisterSharedInvalid: SI buffer overflow */
! 	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 connection *this;
  	char	   *dbname = strdup(name),
  			   *host = NULL,
--- 284,298 ----
  	/* WARNING: identifier "*" will be truncated to "*" */
  	/* WARNING: InvalidateSharedInvalid: cache state reset */
  	/* WARNING: RegisterSharedInvalid: SI buffer overflow */
! 	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,275 ****
  			   *realname = NULL,
  			   *options = NULL;
  
! 	ECPGinit_sqlca();
  
  	if ((this = (struct connection *) ECPGalloc(sizeof(struct connection), lineno)) == NULL)
  		return false;
--- 301,307 ----
  			   *realname = NULL,
  			   *options = NULL;
  
! 	ECPGinit_sqlca(sqlca);
  
  	if ((this = (struct connection *) ECPGalloc(sizeof(struct connection), lineno)) == NULL)
  		return false;
***************
*** 394,399 ****
--- 426,434 ----
  		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,429 ****
--- 459,467 ----
  
          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,450 ****
--- 483,491 ----
  			ECPGfree(dbname);
  		return false;
  	}
+ #ifdef USE_THREADS
+ 	pthread_mutex_unlock(&connections_mutex);
+ #endif
  
  	if (host)
  		ECPGfree(host);
***************
*** 468,478 ****
  bool
  ECPGdisconnect(int lineno, const char *connection_name)
  {
  	struct connection *con;
  
  	if (strcmp(connection_name, "ALL") == 0)
  	{
! 		ECPGinit_sqlca();
  		for (con = all_connections; con;)
  		{
  			struct connection *f = con;
--- 509,524 ----
  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(sqlca);
  		for (con = all_connections; con;)
  		{
  			struct connection *f = con;
***************
*** 486,495 ****
  		con = ECPGget_connection(connection_name);
  
  		if (!ECPGinit(con, connection_name, lineno))
! 			return (false);
  		else
! 			ecpg_finish(con);
  	}
  
  	return true;
  }
--- 532,550 ----
  		con = ECPGget_connection(connection_name);
  
  		if (!ECPGinit(con, connection_name, lineno))
! 		  {
! #ifdef USE_THREADS
! 		    pthread_mutex_unlock(&connections_mutex);
! #endif
! 		    return (false);
! 		  }
  		else
! 		  ecpg_finish(con);
  	}
+ 
+ #ifdef USE_THREADS
+ 	pthread_mutex_unlock(&connections_mutex);
+ #endif
  
  	return true;
  }
Index: src/interfaces/ecpg/ecpglib/data.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/data.c,v
retrieving revision 1.4
diff -c -c -r1.4 data.c
*** src/interfaces/ecpg/ecpglib/data.c	1 Apr 2003 14:37:25 -0000	1.4
--- src/interfaces/ecpg/ecpglib/data.c	15 Jun 2003 04:03:01 -0000
***************
*** 1,5 ****
--- 1,6 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/data.c,v 1.4 2003/04/01 14:37:25 meskes Exp $ */
  
+ #define POSTGRES_ECPG_INTERNAL
  #include "postgres_fe.h"
  
  #include <stdlib.h>
***************
*** 21,26 ****
--- 22,28 ----
  			 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,334 ****
  							default:
  								break;
  						}
! 						sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W';
  					}
  				}
  				break;
--- 330,336 ----
  							default:
  								break;
  						}
! 						sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
  					}
  				}
  				break;
***************
*** 373,379 ****
  							default:
  								break;
  						}
! 						sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W';
  
  						variable->len = varcharsize;
  					}
--- 375,381 ----
  							default:
  								break;
  						}
! 						sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
  
  						variable->len = varcharsize;
  					}
Index: src/interfaces/ecpg/ecpglib/descriptor.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/descriptor.c,v
retrieving revision 1.2
diff -c -c -r1.2 descriptor.c
*** src/interfaces/ecpg/ecpglib/descriptor.c	30 May 2003 13:22:02 -0000	1.2
--- src/interfaces/ecpg/ecpglib/descriptor.c	15 Jun 2003 04:03:02 -0000
***************
*** 3,8 ****
--- 3,9 ----
   * $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.2 2003/05/30 13:22:02 meskes Exp $
   */
  
+ #define POSTGRES_ECPG_INTERNAL
  #include "postgres_fe.h"
  #include "pg_type.h"
  
***************
*** 51,64 ****
  ECPGget_desc_header(int lineno, char *desc_name, int *count)
  {
  	PGresult   *ECPGresult;
  
! 	ECPGinit_sqlca();
  	ECPGresult = ECPGresultByDescriptor(lineno, desc_name);
  	if (!ECPGresult)
  		return false;
  
  	*count = PQnfields(ECPGresult);
! 	sqlca.sqlerrd[2] = 1;
  	ECPGlog("ECPGget_desc_header: found %d attributes.\n", *count);
  	return true;
  }
--- 52,66 ----
  ECPGget_desc_header(int lineno, char *desc_name, int *count)
  {
  	PGresult   *ECPGresult;
+ 	struct sqlca_t *sqlca = ECPGget_sqlca();
  
! 	ECPGinit_sqlca(sqlca);
  	ECPGresult = ECPGresultByDescriptor(lineno, desc_name);
  	if (!ECPGresult)
  		return false;
  
  	*count = PQnfields(ECPGresult);
! 	sqlca->sqlerrd[2] = 1;
  	ECPGlog("ECPGget_desc_header: found %d attributes.\n", *count);
  	return true;
  }
***************
*** 149,157 ****
  	int			ntuples,
  				act_tuple;
  	struct variable data_var;
  
  	va_start(args, index);
! 	ECPGinit_sqlca();
  	ECPGresult = ECPGresultByDescriptor(lineno, desc_name);
  	if (!ECPGresult)
  		return (false);
--- 151,160 ----
  	int			ntuples,
  				act_tuple;
  	struct variable data_var;
+ 	struct sqlca_t *sqlca = ECPGget_sqlca();
  
  	va_start(args, index);
! 	ECPGinit_sqlca(sqlca);
  	ECPGresult = ECPGresultByDescriptor(lineno, desc_name);
  	if (!ECPGresult)
  		return (false);
***************
*** 378,384 ****
  			ECPGlog("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index));
  		}
  	}
! 	sqlca.sqlerrd[2] = ntuples;
  	return (true);
  }
  
--- 381,387 ----
  			ECPGlog("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index));
  		}
  	}
! 	sqlca->sqlerrd[2] = ntuples;
  	return (true);
  }
  
***************
*** 387,394 ****
  {
  	struct descriptor *i;
  	struct descriptor **lastptr = &all_descriptors;
  
! 	ECPGinit_sqlca();
  	for (i = all_descriptors; i; lastptr = &i->next, i = i->next)
  	{
  		if (!strcmp(name, i->name))
--- 390,398 ----
  {
  	struct descriptor *i;
  	struct descriptor **lastptr = &all_descriptors;
+ 	struct sqlca_t *sqlca = ECPGget_sqlca();
  
! 	ECPGinit_sqlca(sqlca);
  	for (i = all_descriptors; i; lastptr = &i->next, i = i->next)
  	{
  		if (!strcmp(name, i->name))
***************
*** 408,415 ****
  ECPGallocate_desc(int line, const char *name)
  {
  	struct descriptor *new;
  
! 	ECPGinit_sqlca();
  	new = (struct descriptor *) ECPGalloc(sizeof(struct descriptor), line);
  	if (!new)
  		return false;
--- 412,420 ----
  ECPGallocate_desc(int line, const char *name)
  {
  	struct descriptor *new;
+ 	struct sqlca_t *sqlca = ECPGget_sqlca();
  
! 	ECPGinit_sqlca(sqlca);
  	new = (struct descriptor *) ECPGalloc(sizeof(struct descriptor), line);
  	if (!new)
  		return false;
Index: src/interfaces/ecpg/ecpglib/error.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/error.c,v
retrieving revision 1.1
diff -c -c -r1.1 error.c
*** src/interfaces/ecpg/ecpglib/error.c	16 Mar 2003 10:42:53 -0000	1.1
--- src/interfaces/ecpg/ecpglib/error.c	15 Jun 2003 04:03:02 -0000
***************
*** 1,5 ****
--- 1,6 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/error.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
  
+ #define POSTGRES_ECPG_INTERNAL
  #include "postgres_fe.h"
  
  #include <stdio.h>
***************
*** 17,133 ****
  void
  ECPGraise(int line, int code, const char *str)
  {
! 	sqlca.sqlcode = code;
  
  	switch (code)
  	{
  		case ECPG_NOT_FOUND:
! 			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),
  					 "Out of memory in line %d.", line);
  			break;
  
  		case ECPG_UNSUPPORTED:
! 			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),
  					 "Too many arguments in line %d.", line);
  			break;
  
  		case ECPG_TOO_FEW_ARGUMENTS:
! 			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),
  			 "Not correctly formatted int type: %s line %d.", str, line);
  			break;
  
  		case ECPG_UINT_FORMAT:
! 			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),
  					 "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),
  				  "Unable to convert %s to bool on line %d.", str, line);
  			break;
  
  		case ECPG_EMPTY:
! 			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),
  					 "NULL value without indicator in line %d.", line);
  			break;
  
  		case ECPG_NO_ARRAY:
! 			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),
  			 "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),
  			 "Trying to insert an array of variables in line %d.", line);
  			break;
  
  		case ECPG_NO_CONN:
! 			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),
  					 "Not connected to '%s' in line %d.", str, line);
  			break;
  
  		case ECPG_INVALID_STMT:
! 			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),
  					 "Descriptor %s not found in line %d.", str, line);
  			break;
  
  		case ECPG_INVALID_DESCRIPTOR_INDEX:
! 			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),
  					 "Unknown descriptor item %s in line %d.", str, line);
  			break;
  
  		case ECPG_VAR_NOT_NUMERIC:
! 			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),
  				   "Variable is not a character type in line %d.", line);
  			break;
  
--- 18,135 ----
  void
  ECPGraise(int line, int code, const char *str)
  {
! 	struct sqlca_t *sqlca = ECPGget_sqlca();
! 	sqlca->sqlcode = code;
  
  	switch (code)
  	{
  		case ECPG_NOT_FOUND:
! 			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),
  					 "Out of memory in line %d.", line);
  			break;
  
  		case ECPG_UNSUPPORTED:
! 			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),
  					 "Too many arguments in line %d.", line);
  			break;
  
  		case ECPG_TOO_FEW_ARGUMENTS:
! 			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),
  			 "Not correctly formatted int type: %s line %d.", str, line);
  			break;
  
  		case ECPG_UINT_FORMAT:
! 			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),
  					 "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),
  				  "Unable to convert %s to bool on line %d.", str, line);
  			break;
  
  		case ECPG_EMPTY:
! 			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),
  					 "NULL value without indicator in line %d.", line);
  			break;
  
  		case ECPG_NO_ARRAY:
! 			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),
  			 "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),
  			 "Trying to insert an array of variables in line %d.", line);
  			break;
  
  		case ECPG_NO_CONN:
! 			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),
  					 "Not connected to '%s' in line %d.", str, line);
  			break;
  
  		case ECPG_INVALID_STMT:
! 			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),
  					 "Descriptor %s not found in line %d.", str, line);
  			break;
  
  		case ECPG_INVALID_DESCRIPTOR_INDEX:
! 			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),
  					 "Unknown descriptor item %s in line %d.", str, line);
  			break;
  
  		case ECPG_VAR_NOT_NUMERIC:
! 			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),
  				   "Variable is not a character type in line %d.", line);
  			break;
  
***************
*** 138,166 ****
  				/* strip trailing newline */
  				if (slen > 0 && str[slen - 1] == '\n')
  					slen--;
! 				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),
  					 "Error in transaction processing in line %d.", line);
  			break;
  
  		case ECPG_CONNECT:
! 			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),
  					 "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);
  
  	/* free all memory we have allocated for the user */
  	ECPGfree_auto_mem();
--- 140,168 ----
  				/* strip trailing newline */
  				if (slen > 0 && str[slen - 1] == '\n')
  					slen--;
! 				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),
  					 "Error in transaction processing in line %d.", line);
  			break;
  
  		case ECPG_CONNECT:
! 			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),
  					 "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);
  
  	/* free all memory we have allocated for the user */
  	ECPGfree_auto_mem();
***************
*** 193,198 ****
  void
  sqlprint(void)
  {
! 	sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
! 	fprintf(stderr, "sql error %s\n", sqlca.sqlerrm.sqlerrmc);
  }
--- 195,202 ----
  void
  sqlprint(void)
  {
! 	struct sqlca_t *sqlca = ECPGget_sqlca();
! 
! 	sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml] = '\0';
! 	fprintf(stderr, "sql error %s\n", sqlca->sqlerrm.sqlerrmc);
  }
Index: src/interfaces/ecpg/ecpglib/execute.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/execute.c,v
retrieving revision 1.9
diff -c -c -r1.9 execute.c
*** src/interfaces/ecpg/ecpglib/execute.c	13 Jun 2003 10:50:57 -0000	1.9
--- src/interfaces/ecpg/ecpglib/execute.c	15 Jun 2003 04:03:02 -0000
***************
*** 13,18 ****
--- 13,19 ----
  /* 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,64 ****
  #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.
   */
--- 32,37 ----
***************
*** 1130,1135 ****
--- 1103,1110 ----
  		 */
  	{
  		bool		clear_result = TRUE;
+ 		struct sqlca_t *sqlca = ECPGget_sqlca();
+ 
  		errmsg = PQresultErrorMessage(results);
  		set_backend_err(errmsg, stmt->lineno);
  		
***************
*** 1142,1148 ****
  
  			case PGRES_TUPLES_OK:
  				nfields = PQnfields(results);
! 				sqlca.sqlerrd[2] = ntuples = PQntuples(results);
  				status = true;
  
  				if (ntuples < 1)
--- 1117,1123 ----
  
  			case PGRES_TUPLES_OK:
  				nfields = PQnfields(results);
! 				sqlca->sqlerrd[2] = ntuples = PQntuples(results);
  				status = true;
  
  				if (ntuples < 1)
***************
*** 1199,1208 ****
  			case PGRES_COMMAND_OK:
  				status = true;
  				cmdstat = PQcmdStatus(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)
  							  || !strncmp(cmdstat, "INSERT", 6)
  							  || !strncmp(cmdstat, "DELETE", 6)))
  					ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL);
--- 1174,1183 ----
  			case PGRES_COMMAND_OK:
  				status = true;
  				cmdstat = PQcmdStatus(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)
  							  || !strncmp(cmdstat, "INSERT", 6)
  							  || !strncmp(cmdstat, "DELETE", 6)))
  					ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL);
Index: src/interfaces/ecpg/ecpglib/extern.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/extern.h,v
retrieving revision 1.1
diff -c -c -r1.1 extern.h
*** src/interfaces/ecpg/ecpglib/extern.h	16 Mar 2003 10:42:53 -0000	1.1
--- src/interfaces/ecpg/ecpglib/extern.h	15 Jun 2003 04:03:02 -0000
***************
*** 3,8 ****
--- 3,9 ----
  
  #include "postgres_fe.h"
  #include "libpq-fe.h"
+ #include "sqlca.h"
  
  /* Here are some methods used by the lib. */
  
***************
*** 19,25 ****
  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 *);
--- 20,25 ----
Index: src/interfaces/ecpg/ecpglib/memory.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/memory.c,v
retrieving revision 1.1
diff -c -c -r1.1 memory.c
*** src/interfaces/ecpg/ecpglib/memory.c	16 Mar 2003 10:42:53 -0000	1.1
--- src/interfaces/ecpg/ecpglib/memory.c	15 Jun 2003 04:03:02 -0000
***************
*** 1,5 ****
--- 1,6 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/memory.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
  
+ #define POSTGRES_ECPG_INTERNAL
  #include "postgres_fe.h"
  
  #include "ecpgtype.h"
Index: src/interfaces/ecpg/ecpglib/misc.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/misc.c,v
retrieving revision 1.2
diff -c -c -r1.2 misc.c
*** src/interfaces/ecpg/ecpglib/misc.c	21 Mar 2003 15:31:04 -0000	1.2
--- src/interfaces/ecpg/ecpglib/misc.c	15 Jun 2003 04:03:03 -0000
***************
*** 1,20 ****
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/misc.c,v 1.2 2003/03/21 15:31:04 meskes Exp $ */
  
  #include "postgres_fe.h"
  
  #include <unistd.h>
  #include "ecpgtype.h"
  #include "ecpglib.h"
  #include "ecpgerrno.h"
  #include "extern.h"
  #include "sqlca.h"
  
! static struct sqlca sqlca_init =
  {
  	{
  		'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
  	},
! 	sizeof(struct sqlca),
  	0,
  	{
  		0,
--- 1,24 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/misc.c,v 1.2 2003/03/21 15:31:04 meskes 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_t sqlca_init =
  {
  	{
  		'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
  	},
! 	sizeof(struct sqlca_t),
  	0,
  	{
  		0,
***************
*** 36,54 ****
  	}
  };
  
! static int	simple_debug = 0;
  static FILE *debugstream = NULL;
  
! void
! ECPGinit_sqlca(void)
  {
! 	memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca));
  }
  
  bool
  ECPGinit(const struct connection * con, const char *connection_name, const int lineno)
  {
! 	ECPGinit_sqlca();
  	if (con == NULL)
  	{
  		ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL");
--- 40,93 ----
  	}
  };
  
! #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(struct sqlca_t *sqlca)
  {
! 	memcpy((char *)sqlca, (char *)&sqlca_init, sizeof(struct sqlca_t));
  }
  
  bool
  ECPGinit(const struct connection * con, const char *connection_name, const int lineno)
  {
! 	struct sqlca_t *sqlca = ECPGget_sqlca();
! 	ECPGinit_sqlca(sqlca);
  	if (con == NULL)
  	{
  		ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL");
***************
*** 58,63 ****
--- 97,129 ----
  	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,131 ****
--- 189,205 ----
  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,144 ****
  {
  	va_list		ap;
  
! 	if (simple_debug)
! 	{
! 		char	   *f = (char *) malloc(strlen(format) + 100);
  
! 		if (!f)
  			return;
  
  		sprintf(f, "[%d]: %s", (int) getpid(), format);
  
--- 207,226 ----
  {
  	va_list		ap;
  
! #ifdef USE_THREADS
! 	pthread_mutex_lock(&debug_mutex);
! #endif
  
! 	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,151 ****
--- 230,237 ----
  
  		ECPGfree(f);
  	}
+ 
+ #ifdef USE_THREADS
+ 	pthread_mutex_unlock(&debug_mutex);
+ #endif
  }
Index: src/interfaces/ecpg/ecpglib/prepare.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/prepare.c,v
retrieving revision 1.1
diff -c -c -r1.1 prepare.c
*** src/interfaces/ecpg/ecpglib/prepare.c	16 Mar 2003 10:42:53 -0000	1.1
--- src/interfaces/ecpg/ecpglib/prepare.c	15 Jun 2003 04:03:03 -0000
***************
*** 1,5 ****
--- 1,6 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/prepare.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
  
+ #define POSTGRES_ECPG_INTERNAL
  #include "postgres_fe.h"
  
  #include <ctype.h>
Index: src/interfaces/ecpg/ecpglib/typename.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/typename.c,v
retrieving revision 1.3
diff -c -c -r1.3 typename.c
*** src/interfaces/ecpg/ecpglib/typename.c	27 Mar 2003 14:29:17 -0000	1.3
--- src/interfaces/ecpg/ecpglib/typename.c	15 Jun 2003 04:03:03 -0000
***************
*** 1,5 ****
--- 1,6 ----
  /* $Header: /cvsroot/pgsql-server/src/interfaces/ecpg/ecpglib/typename.c,v 1.3 2003/03/27 14:29:17 meskes Exp $ */
  
+ #define POSTGRES_ECPG_INTERNAL
  #include "postgres_fe.h"
  
  #include <stdlib.h>
Index: src/interfaces/ecpg/include/sqlca.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/include/sqlca.h,v
retrieving revision 1.23
diff -c -c -r1.23 sqlca.h
*** src/interfaces/ecpg/include/sqlca.h	18 Apr 2003 01:03:42 -0000	1.23
--- src/interfaces/ecpg/include/sqlca.h	15 Jun 2003 04:03:03 -0000
***************
*** 16,22 ****
  {
  #endif
  
! struct sqlca
  {
  	char		sqlcaid[8];
  	long		sqlabc;
--- 16,22 ----
  {
  #endif
  
! struct sqlca_t
  {
  	char		sqlcaid[8];
  	long		sqlabc;
***************
*** 53,60 ****
  	char		sqlext[8];
  };
  
! extern DLLIMPORT struct sqlca sqlca;
  
  
  #ifdef __cplusplus
  }
--- 53,63 ----
  	char		sqlext[8];
  };
  
! struct sqlca_t *ECPGget_sqlca(void);
  
+ #ifndef POSTGRES_ECPG_INTERNAL
+ # define sqlca (*ECPGget_sqlca())
+ #endif
  
  #ifdef __cplusplus
  }
Index: src/interfaces/ecpg/preproc/Makefile
===================================================================
RCS file: /cvsroot/pgsql-server/src/interfaces/ecpg/preproc/Makefile,v
retrieving revision 1.91
diff -c -c -r1.91 Makefile
*** src/interfaces/ecpg/preproc/Makefile	18 Mar 2003 10:46:39 -0000	1.91
--- src/interfaces/ecpg/preproc/Makefile	15 Jun 2003 04:03:03 -0000
***************
*** 4,11 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! MAJOR_VERSION=2
! MINOR_VERSION=12
  PATCHLEVEL=0
  
  override CPPFLAGS := -I$(srcdir)/../include -I$(srcdir) $(CPPFLAGS) \
--- 4,11 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! MAJOR_VERSION=3
! MINOR_VERSION=0
  PATCHLEVEL=0
  
  override CPPFLAGS := -I$(srcdir)/../include -I$(srcdir) $(CPPFLAGS) \