ecpg PREPARE is not thread safe

Started by ITAGAKI Takahiroover 18 years ago7 messages
#1ITAGAKI Takahiro
itagaki.takahiro@oss.ntt.co.jp

Hi,

I encountered segfaults in a multi-threaded ecpg application. It uses
PREPARE statements concurrently in several threads. The cause seems to
be the global variable 'prep_stmts' in ecpg/ecpglib/prepare.c .
It is accessed without any locks.

I'm trying to fix it, but there are some approaches to fix it.
1. Add a giant lock to protect prep_stmts.
2. Put prep_stmts into TSD (Thread Specific Data).
3. Put prep_stmts into connection specific data.

I think the proper approach is 3, because server-side prepared statements
are independent in each connection. For that matter, are there any problems
in current codes? Prepared statements are managed with a single list in it.
Even if we have some kinds of exclusive controls, current ecpg might not
good at prepared statements when we use multiple connections in a signle
thread or do multiple PREPARE in multiple threads. If so, 1 and 2 are not
correct fixes.

Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center

#2Michael Meskes
meskes@postgresql.org
In reply to: ITAGAKI Takahiro (#1)
Re: ecpg PREPARE is not thread safe

On Fri, Sep 21, 2007 at 11:05:47AM +0900, ITAGAKI Takahiro wrote:

PREPARE statements concurrently in several threads. The cause seems to
be the global variable 'prep_stmts' in ecpg/ecpglib/prepare.c .
It is accessed without any locks.

And it is global, right. This has to be fixed, you're right.

I think the proper approach is 3, because server-side prepared statements
are independent in each connection. For that matter, are there any problems

Right now the prepared statements are not considered connection
specific. I'm not sure whether the standard says anything about this.
But moving this data shoudln't be a major problem.

Even if we have some kinds of exclusive controls, current ecpg might not
good at prepared statements when we use multiple connections in a signle
thread or do multiple PREPARE in multiple threads. If so, 1 and 2 are not
correct fixes.

Sorry, I don't get this. What exactly are you talking about here?

Michael
--
Michael Meskes
Email: Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
ICQ: 179140304, AIM/Yahoo: michaelmeskes, Jabber: meskes@jabber.org
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#3ITAGAKI Takahiro
itagaki.takahiro@oss.ntt.co.jp
In reply to: Michael Meskes (#2)
Re: ecpg PREPARE is not thread safe

Michael Meskes <meskes@postgresql.org> wrote:

Right now the prepared statements are not considered connection
specific. I'm not sure whether the standard says anything about this.
But moving this data shoudln't be a major problem.

Even if we have some kinds of exclusive controls, current ecpg might not
good at prepared statements when we use multiple connections in a signle
thread or do multiple PREPARE in multiple threads. If so, 1 and 2 are not
correct fixes.

Sorry, I don't get this. What exactly are you talking about here?

I'm worried that prepared statements are used in another connection.
ECPG does not consider in which connection the statements is prepared.
Are there any mix-up problem here? If no, the TSD approach is enough
to fix the race condition. If yes, per-connection approach is needed.

Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center

#4ITAGAKI Takahiro
itagaki.takahiro@oss.ntt.co.jp
In reply to: ITAGAKI Takahiro (#3)
1 attachment(s)
Thread-safe PREPARE in ecpg

Here is a WIP patch to make prepared statements thread-safe in ecpg.
The variable prep_stmts was global but not protected by any locks.
I divided it into per-connection field so that we can access prepared
statements separately in each thread.

I needed to change the following exported functions, but it will
introduce an incompatibility issue. It might be ok for CVS HEAD,
but I'd like to port the fix to back versions. Do you have any
thoughts on how we can accomplish this better?

From:
- bool ECPGdeallocate(int, int, const char *name);
- bool ECPGdeallocate_all(int, int);
- char *ECPGprepared_statement(const char *name, int);
To:
- bool ECPGdeallocate(int, int, const char *connection_name, const char *name);
- bool ECPGdeallocate_all(int, int, const char *connection_name);
- char *ECPGprepared_statement(const char *connection_name, const char *name, int);
(connection_name argument is added.)

Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center

Attachments:

ecpg_prepare.patchapplication/octet-stream; name=ecpg_prepare.patchDownload
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/connect.c ecpg_prepare/src/interfaces/ecpg/ecpglib/connect.c
*** HEAD/src/interfaces/ecpg/ecpglib/connect.c	Tue Aug 14 19:01:52 2007
--- ecpg_prepare/src/interfaces/ecpg/ecpglib/connect.c	Tue Sep 25 14:00:39 2007
*************** ECPGconnect(int lineno, int c, const cha
*** 460,465 ****
--- 460,466 ----
  		this->name = ECPGstrdup(realname, lineno);
  
  	this->cache_head = NULL;
+ 	this->prep_stmts = NULL;
  
  	if (all_connections == NULL)
  		this->next = NULL;
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/execute.c ecpg_prepare/src/interfaces/ecpg/ecpglib/execute.c
*** HEAD/src/interfaces/ecpg/ecpglib/execute.c	Fri Sep 21 19:59:27 2007
--- ecpg_prepare/src/interfaces/ecpg/ecpglib/execute.c	Tue Sep 25 14:00:39 2007
*************** ECPGdo(const int lineno, const int compa
*** 1515,1521 ****
  	if (statement_type == ECPGst_execute)
  	{
  		/* if we have an EXECUTE command, only the name is send */
! 		char *command = ECPGprepared(stmt->command, lineno);
  
  		if (command)
  		{
--- 1515,1521 ----
  	if (statement_type == ECPGst_execute)
  	{
  		/* if we have an EXECUTE command, only the name is send */
! 		char *command = ECPGprepared(stmt->command, con, lineno);
  
  		if (command)
  		{
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/extern.h ecpg_prepare/src/interfaces/ecpg/ecpglib/extern.h
*** HEAD/src/interfaces/ecpg/ecpglib/extern.h	Tue Aug 14 19:54:57 2007
--- ecpg_prepare/src/interfaces/ecpg/ecpglib/extern.h	Tue Sep 25 14:00:39 2007
*************** struct connection
*** 91,96 ****
--- 91,97 ----
  	bool		committed;
  	int			autocommit;
  	struct ECPGtype_information_cache *cache_head;
+ 	struct prepared_statement *prep_stmts;
  	struct connection *next;
  };
  
*************** bool		ECPGstore_input(const int, const b
*** 144,150 ****
  bool ECPGcheck_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
  void ECPGraise(int line, int code, const char *sqlstate, const char *str);
  void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat);
! char *ECPGprepared(const char *, int);
  
  /* SQLSTATE values generated or processed by ecpglib (intentionally
   * not exported -- users should refer to the codes directly) */
--- 145,151 ----
  bool ECPGcheck_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
  void ECPGraise(int line, int code, const char *sqlstate, const char *str);
  void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat);
! char *ECPGprepared(const char *, struct connection *, int);
  
  /* SQLSTATE values generated or processed by ecpglib (intentionally
   * not exported -- users should refer to the codes directly) */
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/prepare.c ecpg_prepare/src/interfaces/ecpg/ecpglib/prepare.c
*** HEAD/src/interfaces/ecpg/ecpglib/prepare.c	Tue Aug 14 19:01:52 2007
--- ecpg_prepare/src/interfaces/ecpg/ecpglib/prepare.c	Tue Sep 25 14:00:39 2007
***************
*** 11,23 ****
  #include "extern.h"
  #include "sqlca.h"
  
! static struct prepared_statement
  {
! 	char	*name;
! 	bool	prepared;
! 	struct statement *stmt;
! 	struct prepared_statement *next;
! }	*prep_stmts = NULL;
  
  #define STMTID_SIZE 32
  
--- 11,23 ----
  #include "extern.h"
  #include "sqlca.h"
  
! struct prepared_statement
  {
! 	char					   *name;
! 	bool						prepared;
! 	struct statement		   *stmt;
! 	struct prepared_statement  *next;
! };
  
  #define STMTID_SIZE 32
  
*************** static int             stmtCacheNBuckets
*** 35,40 ****
--- 35,45 ----
  static int             stmtCacheEntPerBucket    = 8;        /* # entries/bucket     */
  static stmtCacheEntry  stmtCacheEntries[16384] = {{0,{0},0,0,0}};
  
+ static struct prepared_statement *find_prepared_statement(const char *name,
+ 	struct connection *con, struct prepared_statement **prev);
+ static bool	deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
+ 	struct prepared_statement *prev, struct prepared_statement *this);
+ 
  static bool
  isvarchar(unsigned char c)
  {
*************** replace_variables(char **text, int linen
*** 105,127 ****
  bool
  ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable)
  {
! 	struct statement *stmt;
! 	struct prepared_statement *this;
  	struct sqlca_t *sqlca = ECPGget_sqlca();
  	PGresult   *query;
  
  	ECPGinit_sqlca(sqlca);
  
! 	/* check if we already have prepared this statement */
! 	for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
! 	if (this)
! 	{
! 		bool		b = ECPGdeallocate(lineno, ECPG_COMPAT_PGSQL, name);
  
! 		if (!b)
! 			return false;
! 	}
  
  	this = (struct prepared_statement *) ECPGalloc(sizeof(struct prepared_statement), lineno);
  	if (!this)
  		return false;
--- 110,132 ----
  bool
  ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable)
  {
! 	struct connection		   *con;
! 	struct statement		   *stmt;
! 	struct prepared_statement  *this,
! 							   *prev;
  	struct sqlca_t *sqlca = ECPGget_sqlca();
  	PGresult   *query;
  
  	ECPGinit_sqlca(sqlca);
  
! 	con = ECPGget_connection(connection_name);
  
! 	/* check if we already have prepared this statement */
! 	this = find_prepared_statement(name, con, &prev);
! 	if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
! 		return false;
  
+ 	/* allocate new statement */
  	this = (struct prepared_statement *) ECPGalloc(sizeof(struct prepared_statement), lineno);
  	if (!this)
  		return false;
*************** ECPGprepare(int lineno, const char *conn
*** 135,141 ****
  
  	/* create statement */
  	stmt->lineno = lineno;
! 	stmt->connection = ECPGget_connection(connection_name);
  	stmt->command = ECPGstrdup(variable, lineno);
  	stmt->inlist = stmt->outlist = NULL;
  
--- 140,146 ----
  
  	/* create statement */
  	stmt->lineno = lineno;
! 	stmt->connection = con;
  	stmt->command = ECPGstrdup(variable, lineno);
  	stmt->inlist = stmt->outlist = NULL;
  
*************** ECPGprepare(int lineno, const char *conn
*** 160,249 ****
  	PQclear(query);
  	this->prepared = true;
  
! 	if (prep_stmts == NULL)
  		this->next = NULL;
  	else
! 		this->next = prep_stmts;
  
! 	prep_stmts = this;
  	return true;
  }
  
  static bool
! deallocate_one(int lineno, const char *name)
  {
! 	struct prepared_statement *this,
! 			   *prev;
  
! 	/* check if we really have prepared this statement */
! 	for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next);
! 	if (this)
  	{
! 		/* first deallocate the statement in the backend */
! 		if (this->prepared)
  		{
! 			char *text;
! 			PGresult *query;
! 			
! 			if (!(text = (char *) ECPGalloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno)))
! 				return false;
! 			else
  			{
- 				sprintf(text, "deallocate \"%s\"", this->name);
- 				query = PQexec(this->stmt->connection->connection, text);
- 				ECPGfree(text);
- 				if (!ECPGcheck_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
- 					return false;
  				PQclear(query);
  			}
  		}
! 		
! 		/* okay, free all the resources */
! 		ECPGfree(this->stmt->command);
! 		ECPGfree(this->stmt);
! 		if (prev != NULL)
! 			prev->next = this->next;
! 		else
! 			prep_stmts = this->next;
  
! 		ECPGfree(this);
! 		return true;
  	}
! 	return false;
  }
  
  /* handle the EXEC SQL DEALLOCATE PREPARE statement */
  bool
! ECPGdeallocate(int lineno, int c, const char *name)
  {
! 	bool		ret = deallocate_one(lineno, name);
! 	enum COMPAT_MODE compat = c;
  
! 	ECPGlog("ECPGdeallocate line %d: NAME: %s\n", lineno, name);
! 	if (INFORMIX_MODE(compat))
! 	{
! 		/*
! 		 * Just ignore all errors since we do not know the list of cursors we
! 		 * are allowed to free. We have to trust the software.
! 		 */
! 		return true;
! 	}
  
! 	if (!ret)
! 		ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
  
! 	return ret;
  }
  
  bool
! ECPGdeallocate_all(int lineno, int compat)
  {
  	/* deallocate all prepared statements */
! 	while (prep_stmts != NULL)
  	{
! 		bool		b = ECPGdeallocate(lineno, compat, prep_stmts->name);
! 
! 		if (!b)
  			return false;
  	}
  
--- 165,280 ----
  	PQclear(query);
  	this->prepared = true;
  
! 	if (con->prep_stmts == NULL)
  		this->next = NULL;
  	else
! 		this->next = con->prep_stmts;
  
! 	con->prep_stmts = this;
  	return true;
  }
  
+ static struct prepared_statement *find_prepared_statement(const char *name,
+ 	struct connection *con, struct prepared_statement **prev_)
+ {
+ 	struct prepared_statement  *this,
+ 							   *prev;
+ 
+ 	for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
+ 	{
+ 		if (strcmp(this->name, name) == 0)
+ 		{
+ 			if (prev_)
+ 				*prev_ = prev;
+ 			return this;
+ 		}
+ 	}
+ 	return NULL;
+ }
+ 
  static bool
! deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct prepared_statement *prev, struct prepared_statement *this)
  {
! 	bool	r = false;
  
! 	ECPGlog("ECPGdeallocate line %d: NAME: %s\n", lineno, this->name);
! 
! 	/* first deallocate the statement in the backend */
! 	if (this->prepared)
  	{
! 		char *text;
! 		PGresult *query;
! 		
! 		text = (char *) ECPGalloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
! 		if (text)
  		{
! 			sprintf(text, "deallocate \"%s\"", this->name);
! 			query = PQexec(this->stmt->connection->connection, text);
! 			ECPGfree(text);
! 			if (ECPGcheck_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
  			{
  				PQclear(query);
+ 				r = true;
  			}
  		}
! 	}
  
! 	/*
! 	 * Just ignore all errors since we do not know the list of cursors we
! 	 * are allowed to free. We have to trust the software.
! 	 */
! 	if (!r && !INFORMIX_MODE(c))
! 	{
! 		ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
! 		return false;
  	}
! 	
! 	/* okay, free all the resources */
! 	ECPGfree(this->stmt->command);
! 	ECPGfree(this->stmt);
! 	if (prev != NULL)
! 		prev->next = this->next;
! 	else
! 		con->prep_stmts = this->next;
! 
! 	ECPGfree(this);
! 	return true;
  }
  
  /* handle the EXEC SQL DEALLOCATE PREPARE statement */
  bool
! ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
  {
! 	struct connection		   *con;
! 	struct prepared_statement  *this,
! 							   *prev;
  
! 	con = ECPGget_connection(connection_name);
  
! 	this = find_prepared_statement(name, con, &prev);
! 	if (this)
! 		return deallocate_one(lineno, c, con, prev, this);
  
! 	/* prepared statement is not found */
! 	if (INFORMIX_MODE(c))
! 		return true;
! 	ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
! 	return false;
  }
  
  bool
! ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
  {
+ 	struct connection		   *con;
+ 	struct prepared_statement  *this,
+ 							   *prev;
+ 
+ 	con = ECPGget_connection(connection_name);
+ 
  	/* deallocate all prepared statements */
! 	for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
  	{
! 		if (!deallocate_one(lineno, compat, con, prev, this))
  			return false;
  	}
  
*************** ECPGdeallocate_all(int lineno, int compa
*** 251,272 ****
  }
  
  char *
! ECPGprepared(const char *name, int lineno)
  {
! 	struct prepared_statement *this;
! 
! 	for (this = prep_stmts; this != NULL && ((strcmp(this->name, name) != 0) || this->prepared == false); this = this->next);
! 	return (this) ? this->stmt->command : NULL;
  }
  
  /* return the prepared statement */
  char *
! ECPGprepared_statement(const char *name, int lineno)
  {
! 	struct prepared_statement *this;
! 
! 	for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
! 	return (this) ? this->stmt->command : NULL;
  }
  
  /*
--- 282,299 ----
  }
  
  char *
! ECPGprepared(const char *name, struct connection *con, int lineno)
  {
! 	struct prepared_statement  *this;
! 	this = find_prepared_statement(name, con, NULL);
! 	return this ? this->stmt->command : NULL;
  }
  
  /* return the prepared statement */
  char *
! ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
  {
! 	return ECPGprepared(name, ECPGget_connection(connection_name), lineno);
  }
  
  /*
*************** ECPGauto_prepare(int lineno, const char 
*** 426,439 ****
  	entNo = SearchStmtCache(query);
  
  	/* if not found - add the statement to the cache    */
!         if(entNo)
  	{
! 	        ECPGlog("ECPGauto_prepare line %d: stmt found in cache, entry %d\n", lineno, entNo);
  		*name = ECPGstrdup(stmtCacheEntries[entNo].stmtID, lineno); 
  	}
  	else
  	{
! 	        ECPGlog("ECPGauto_prepare line %d: stmt not in cache; inserting\n", lineno);
  
  		/* generate a statement ID */
  		*name = (char *) ECPGalloc(STMTID_SIZE, lineno);
--- 453,466 ----
  	entNo = SearchStmtCache(query);
  
  	/* if not found - add the statement to the cache    */
! 	if(entNo)
  	{
! 		ECPGlog("ECPGauto_prepare line %d: stmt found in cache, entry %d\n", lineno, entNo);
  		*name = ECPGstrdup(stmtCacheEntries[entNo].stmtID, lineno); 
  	}
  	else
  	{
! 		ECPGlog("ECPGauto_prepare line %d: stmt not in cache; inserting\n", lineno);
  
  		/* generate a statement ID */
  		*name = (char *) ECPGalloc(STMTID_SIZE, lineno);
*************** ECPGauto_prepare(int lineno, const char 
*** 450,453 ****
  
  	return(true);
  }
- 
--- 477,479 ----
diff -cpr HEAD/src/interfaces/ecpg/include/ecpglib.h ecpg_prepare/src/interfaces/ecpg/include/ecpglib.h
*** HEAD/src/interfaces/ecpg/include/ecpglib.h	Tue Aug 14 19:01:52 2007
--- ecpg_prepare/src/interfaces/ecpg/include/ecpglib.h	Tue Sep 25 14:00:39 2007
*************** bool		ECPGtrans(int, const char *, const
*** 49,57 ****
  bool		ECPGdisconnect(int, const char *);
  bool		ECPGprepare(int, const char *, const int, const char *, const char *);
  bool		ECPGauto_prepare(int, const char *, const int, char **, const char *);
! bool		ECPGdeallocate(int, int, const char *);
! bool		ECPGdeallocate_all(int, int);
! char	   *ECPGprepared_statement(const char *, int);
  
  void		ECPGlog(const char *format,...);
  char	   *ECPGerrmsg(void);
--- 49,57 ----
  bool		ECPGdisconnect(int, const char *);
  bool		ECPGprepare(int, const char *, const int, const char *, const char *);
  bool		ECPGauto_prepare(int, const char *, const int, char **, const char *);
! bool		ECPGdeallocate(int, int, const char *connection_name, const char *name);
! bool		ECPGdeallocate_all(int, int, const char *connection_name);
! char	   *ECPGprepared_statement(const char *connection_name, const char *name, int lineno);
  
  void		ECPGlog(const char *format,...);
  char	   *ECPGerrmsg(void);
diff -cpr HEAD/src/interfaces/ecpg/preproc/output.c ecpg_prepare/src/interfaces/ecpg/preproc/output.c
*** HEAD/src/interfaces/ecpg/preproc/output.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare/src/interfaces/ecpg/preproc/output.c	Tue Sep 25 14:00:39 2007
*************** output_prepare_statement(char *name, cha
*** 153,166 ****
  void
  output_deallocate_prepare_statement(char *name)
  {
  	if (strcmp(name, "all"))
  	{
! 		fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, ", compat);
  		output_escaped_str(name, true);
  		fputs(");", yyout);
  	}
  	else
! 		fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d);", compat);
  
  	whenever_action(2);
  	free(name);
--- 153,167 ----
  void
  output_deallocate_prepare_statement(char *name)
  {
+ 	const char* con = connection ? connection : "NULL";
  	if (strcmp(name, "all"))
  	{
! 		fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, %s, ", compat, con);
  		output_escaped_str(name, true);
  		fputs(");", yyout);
  	}
  	else
! 		fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
  
  	whenever_action(2);
  	free(name);
diff -cpr HEAD/src/interfaces/ecpg/preproc/preproc.y ecpg_prepare/src/interfaces/ecpg/preproc/preproc.y
*** HEAD/src/interfaces/ecpg/preproc/preproc.y	Tue Sep  4 19:02:29 2007
--- ecpg_prepare/src/interfaces/ecpg/preproc/preproc.y	Tue Sep 25 14:00:39 2007
*************** stmt:  AlterDatabaseStmt		{ output_state
*** 906,915 ****
  		| ECPGExecuteImmediateStmt	{ output_statement($1, 0, ECPGst_exec_immediate); }
  		| ECPGFree
  		{
  			if (strcmp($1, "all"))
! 				fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, \"%s\");", compat, $1);
  			else
! 				fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d);", compat);
  
  			whenever_action(2);
  			free($1);
--- 906,916 ----
  		| ECPGExecuteImmediateStmt	{ output_statement($1, 0, ECPGst_exec_immediate); }
  		| ECPGFree
  		{
+ 			const char *con = connection ? connection : "NULL";
  			if (strcmp($1, "all"))
! 				fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, $1);
  			else
! 				fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
  
  			whenever_action(2);
  			free($1);
*************** prep_type_clause: '(' type_list ')'	{ $$
*** 3349,3355 ****
  
  ExecuteStmt: EXECUTE prepared_name execute_param_clause execute_rest /* execute_rest is an ecpg addon */
  			{
! 				/* $$ = cat_str(3, make_str("ECPGprepared_statement("), $2, make_str(", __LINE__)"));*/
  				$$ = $2;
  			}
  		| CREATE OptTemp TABLE create_as_target AS
--- 3350,3356 ----
  
  ExecuteStmt: EXECUTE prepared_name execute_param_clause execute_rest /* execute_rest is an ecpg addon */
  			{
! 				/* $$ = cat_str(3, make_str("ECPGprepared_statement("), connection, $2, make_str("__LINE__)"));*/
  				$$ = $2;
  			}
  		| CREATE OptTemp TABLE create_as_target AS
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 5185,5190 ****
--- 5186,5192 ----
  		{
  			struct cursor *ptr, *this;
  			struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
+ 			const char *con = connection ? connection : "NULL";
  
  			for (ptr = cur; ptr != NULL; ptr = ptr->next)
  			{
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 5205,5212 ****
  			thisquery->type = &ecpg_query;
  			thisquery->brace_level = 0;
  			thisquery->next = NULL;
! 			thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, __LINE__)") + strlen($7));
! 			sprintf(thisquery->name, "ECPGprepared_statement(%s, __LINE__)", $7);
  
  			this->argsinsert = NULL;
  			add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
--- 5207,5214 ----
  			thisquery->type = &ecpg_query;
  			thisquery->brace_level = 0;
  			thisquery->next = NULL;
! 			thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen($7));
! 			sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
  
  			this->argsinsert = NULL;
  			add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
*************** UsingConst: AllConst
*** 5914,5934 ****
   */
  ECPGDescribe: SQL_DESCRIBE INPUT_P name using_descriptor
  	{
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("1, ECPGprepared_statement(\"\", __LINE__)") + strlen($3));
! 		sprintf($$, "1, ECPGprepared_statement(\"%s\", __LINE__)", $3);
  	}
  	| SQL_DESCRIBE opt_output name using_descriptor
  	{
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(\"\", __LINE__)") + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(\"%s\", __LINE__)", $3);
  	}
  	| SQL_DESCRIBE opt_output name into_descriptor
  	{
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(\"\", __LINE__)") + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(\"%s\", __LINE__)", $3);
  	}
  	;
  
--- 5916,5939 ----
   */
  ECPGDescribe: SQL_DESCRIBE INPUT_P name using_descriptor
  	{
+ 		const char *con = connection ? connection : "NULL";
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("1, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
! 		sprintf($$, "1, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
  	}
  	| SQL_DESCRIBE opt_output name using_descriptor
  	{
+ 		const char *con = connection ? connection : "NULL";
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
  	}
  	| SQL_DESCRIBE opt_output name into_descriptor
  	{
+ 		const char *con = connection ? connection : "NULL";
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
  	}
  	;
  
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-desc.c ecpg_prepare/src/interfaces/ecpg/test/expected/sql-desc.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-desc.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare/src/interfaces/ecpg/test/expected/sql-desc.c	Tue Sep 25 14:00:39 2007
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 197,203 ****
  #line 45 "desc.pgc"
  
  
! 	{ ECPGdeallocate(__LINE__, 0, "Foo-1");
  #line 47 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
--- 197,203 ----
  #line 45 "desc.pgc"
  
  
! 	{ ECPGdeallocate(__LINE__, 0, NULL, "Foo-1");
  #line 47 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 247,253 ****
  #line 57 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c1  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("foo2", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 247,253 ----
  #line 57 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c1  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "foo2", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 297,303 ****
  #line 69 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("foo3", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 297,303 ----
  #line 69 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "foo3", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 344,350 ****
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 80 "desc.pgc"
  
! 	{ ECPGdeallocate_all(__LINE__, 0);
  #line 81 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
--- 344,350 ----
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 80 "desc.pgc"
  
! 	{ ECPGdeallocate_all(__LINE__, 0, NULL);
  #line 81 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-dyntest.c ecpg_prepare/src/interfaces/ecpg/test/expected/sql-dyntest.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-dyntest.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare/src/interfaces/ecpg/test/expected/sql-dyntest.c	Tue Sep 25 14:00:39 2007
*************** if (sqlca.sqlcode < 0) error (  );}
*** 263,269 ****
  
  
    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare MYCURS  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("myquery", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 60 "dyntest.pgc"
  
--- 263,269 ----
  
  
    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare MYCURS  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "myquery", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 60 "dyntest.pgc"
  
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-execute.c ecpg_prepare/src/interfaces/ecpg/test/expected/sql-execute.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-execute.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare/src/interfaces/ecpg/test/expected/sql-execute.c	Tue Sep 25 14:00:39 2007
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 142,148 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "execute.pgc"
  
--- 142,148 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "execute.pgc"
  
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 187,193 ****
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 66 "execute.pgc"
  
! 	{ ECPGdeallocate(__LINE__, 0, "f");
  #line 67 "execute.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
--- 187,193 ----
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 66 "execute.pgc"
  
! 	{ ECPGdeallocate(__LINE__, 0, NULL, "f");
  #line 67 "execute.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 207,213 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 207,213 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-oldexec.c ecpg_prepare/src/interfaces/ecpg/test/expected/sql-oldexec.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-oldexec.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare/src/interfaces/ecpg/test/expected/sql-oldexec.c	Tue Sep 25 14:00:39 2007
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 142,148 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "oldexec.pgc"
  
--- 142,148 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "oldexec.pgc"
  
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 201,207 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR3  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 201,207 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR3  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
#5Michael Meskes
meskes@postgresql.org
In reply to: ITAGAKI Takahiro (#4)
Re: Thread-safe PREPARE in ecpg

On Tue, Sep 25, 2007 at 03:22:13PM +0900, ITAGAKI Takahiro wrote:

Here is a WIP patch to make prepared statements thread-safe in ecpg.
The variable prep_stmts was global but not protected by any locks.
I divided it into per-connection field so that we can access prepared
statements separately in each thread.

Thanks a lot. This is exactly how I was planning to implement it, but I
haven't even found the time to start coding yet. :-)

Could you please create a small example that we could add to the
regression suite?

I needed to change the following exported functions, but it will
introduce an incompatibility issue. It might be ok for CVS HEAD,
but I'd like to port the fix to back versions. Do you have any
thoughts on how we can accomplish this better?

No idea at the moment, sorry.

Michael
--
Michael Meskes
Email: Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
ICQ: 179140304, AIM/Yahoo: michaelmeskes, Jabber: meskes@jabber.org
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!

#6ITAGAKI Takahiro
itagaki.takahiro@oss.ntt.co.jp
In reply to: Michael Meskes (#5)
2 attachment(s)
Re: Thread-safe PREPARE in ecpg

Here is a revised patch against CVS HEAD.
I fixed a bug in ECPGdeallocate_all().

Michael Meskes <meskes@postgresql.org> wrote:

Could you please create a small example that we could add to the
regression suite?

The attached prep.pgc is an example for this fix,
that repeats EXEC SQL PREPARE and EXECUTE in loops.
It works with pthread and Win32 thread.

I needed to change the following exported functions, but it will
introduce an incompatibility issue. It might be ok for CVS HEAD,
but I'd like to port the fix to back versions. Do you have any
thoughts on how we can accomplish this better?

No idea at the moment, sorry.

For porting back to 8.2, I'm planning to keep ECPGdeallocate,
ECPGdeallocate_all and ECPGprepared_statement as their original names,
and add new versions with different names. If users use only thread
default connection, the simplified version would work enough.

Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center

Attachments:

ecpg_prepare-8.3.patchapplication/octet-stream; name=ecpg_prepare-8.3.patchDownload
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/connect.c ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/connect.c
*** HEAD/src/interfaces/ecpg/ecpglib/connect.c	Tue Aug 14 19:01:52 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/connect.c	Wed Sep 26 13:14:43 2007
*************** ECPGconnect(int lineno, int c, const cha
*** 460,465 ****
--- 460,466 ----
  		this->name = ECPGstrdup(realname, lineno);
  
  	this->cache_head = NULL;
+ 	this->prep_stmts = NULL;
  
  	if (all_connections == NULL)
  		this->next = NULL;
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/execute.c ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/execute.c
*** HEAD/src/interfaces/ecpg/ecpglib/execute.c	Fri Sep 21 19:59:27 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/execute.c	Wed Sep 26 13:14:43 2007
*************** ECPGdo(const int lineno, const int compa
*** 1515,1521 ****
  	if (statement_type == ECPGst_execute)
  	{
  		/* if we have an EXECUTE command, only the name is send */
! 		char *command = ECPGprepared(stmt->command, lineno);
  
  		if (command)
  		{
--- 1515,1521 ----
  	if (statement_type == ECPGst_execute)
  	{
  		/* if we have an EXECUTE command, only the name is send */
! 		char *command = ECPGprepared(stmt->command, con, lineno);
  
  		if (command)
  		{
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/extern.h ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/extern.h
*** HEAD/src/interfaces/ecpg/ecpglib/extern.h	Tue Aug 14 19:54:57 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/extern.h	Wed Sep 26 13:14:43 2007
*************** struct connection
*** 91,96 ****
--- 91,97 ----
  	bool		committed;
  	int			autocommit;
  	struct ECPGtype_information_cache *cache_head;
+ 	struct prepared_statement *prep_stmts;
  	struct connection *next;
  };
  
*************** bool		ECPGstore_input(const int, const b
*** 144,150 ****
  bool ECPGcheck_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
  void ECPGraise(int line, int code, const char *sqlstate, const char *str);
  void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat);
! char *ECPGprepared(const char *, int);
  
  /* SQLSTATE values generated or processed by ecpglib (intentionally
   * not exported -- users should refer to the codes directly) */
--- 145,151 ----
  bool ECPGcheck_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
  void ECPGraise(int line, int code, const char *sqlstate, const char *str);
  void ECPGraise_backend(int line, PGresult *result, PGconn *conn, int compat);
! char *ECPGprepared(const char *, struct connection *, int);
  
  /* SQLSTATE values generated or processed by ecpglib (intentionally
   * not exported -- users should refer to the codes directly) */
diff -cpr HEAD/src/interfaces/ecpg/ecpglib/prepare.c ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/prepare.c
*** HEAD/src/interfaces/ecpg/ecpglib/prepare.c	Tue Aug 14 19:01:52 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/ecpglib/prepare.c	Wed Sep 26 13:14:43 2007
***************
*** 11,23 ****
  #include "extern.h"
  #include "sqlca.h"
  
! static struct prepared_statement
  {
! 	char	*name;
! 	bool	prepared;
! 	struct statement *stmt;
! 	struct prepared_statement *next;
! }	*prep_stmts = NULL;
  
  #define STMTID_SIZE 32
  
--- 11,23 ----
  #include "extern.h"
  #include "sqlca.h"
  
! struct prepared_statement
  {
! 	char					   *name;
! 	bool						prepared;
! 	struct statement		   *stmt;
! 	struct prepared_statement  *next;
! };
  
  #define STMTID_SIZE 32
  
*************** static int             stmtCacheNBuckets
*** 35,40 ****
--- 35,45 ----
  static int             stmtCacheEntPerBucket    = 8;        /* # entries/bucket     */
  static stmtCacheEntry  stmtCacheEntries[16384] = {{0,{0},0,0,0}};
  
+ static struct prepared_statement *find_prepared_statement(const char *name,
+ 	struct connection *con, struct prepared_statement **prev);
+ static bool	deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
+ 	struct prepared_statement *prev, struct prepared_statement *this);
+ 
  static bool
  isvarchar(unsigned char c)
  {
*************** replace_variables(char **text, int linen
*** 105,127 ****
  bool
  ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable)
  {
! 	struct statement *stmt;
! 	struct prepared_statement *this;
  	struct sqlca_t *sqlca = ECPGget_sqlca();
  	PGresult   *query;
  
  	ECPGinit_sqlca(sqlca);
  
! 	/* check if we already have prepared this statement */
! 	for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
! 	if (this)
! 	{
! 		bool		b = ECPGdeallocate(lineno, ECPG_COMPAT_PGSQL, name);
  
! 		if (!b)
! 			return false;
! 	}
  
  	this = (struct prepared_statement *) ECPGalloc(sizeof(struct prepared_statement), lineno);
  	if (!this)
  		return false;
--- 110,132 ----
  bool
  ECPGprepare(int lineno, const char *connection_name, const int questionmarks, const char *name, const char *variable)
  {
! 	struct connection		   *con;
! 	struct statement		   *stmt;
! 	struct prepared_statement  *this,
! 							   *prev;
  	struct sqlca_t *sqlca = ECPGget_sqlca();
  	PGresult   *query;
  
  	ECPGinit_sqlca(sqlca);
  
! 	con = ECPGget_connection(connection_name);
  
! 	/* check if we already have prepared this statement */
! 	this = find_prepared_statement(name, con, &prev);
! 	if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
! 		return false;
  
+ 	/* allocate new statement */
  	this = (struct prepared_statement *) ECPGalloc(sizeof(struct prepared_statement), lineno);
  	if (!this)
  		return false;
*************** ECPGprepare(int lineno, const char *conn
*** 135,141 ****
  
  	/* create statement */
  	stmt->lineno = lineno;
! 	stmt->connection = ECPGget_connection(connection_name);
  	stmt->command = ECPGstrdup(variable, lineno);
  	stmt->inlist = stmt->outlist = NULL;
  
--- 140,146 ----
  
  	/* create statement */
  	stmt->lineno = lineno;
! 	stmt->connection = con;
  	stmt->command = ECPGstrdup(variable, lineno);
  	stmt->inlist = stmt->outlist = NULL;
  
*************** ECPGprepare(int lineno, const char *conn
*** 160,249 ****
  	PQclear(query);
  	this->prepared = true;
  
! 	if (prep_stmts == NULL)
  		this->next = NULL;
  	else
! 		this->next = prep_stmts;
  
! 	prep_stmts = this;
  	return true;
  }
  
  static bool
! deallocate_one(int lineno, const char *name)
  {
! 	struct prepared_statement *this,
! 			   *prev;
  
! 	/* check if we really have prepared this statement */
! 	for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next);
! 	if (this)
  	{
! 		/* first deallocate the statement in the backend */
! 		if (this->prepared)
  		{
! 			char *text;
! 			PGresult *query;
! 			
! 			if (!(text = (char *) ECPGalloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno)))
! 				return false;
! 			else
  			{
- 				sprintf(text, "deallocate \"%s\"", this->name);
- 				query = PQexec(this->stmt->connection->connection, text);
- 				ECPGfree(text);
- 				if (!ECPGcheck_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
- 					return false;
  				PQclear(query);
  			}
  		}
! 		
! 		/* okay, free all the resources */
! 		ECPGfree(this->stmt->command);
! 		ECPGfree(this->stmt);
! 		if (prev != NULL)
! 			prev->next = this->next;
! 		else
! 			prep_stmts = this->next;
  
! 		ECPGfree(this);
! 		return true;
  	}
! 	return false;
  }
  
  /* handle the EXEC SQL DEALLOCATE PREPARE statement */
  bool
! ECPGdeallocate(int lineno, int c, const char *name)
  {
! 	bool		ret = deallocate_one(lineno, name);
! 	enum COMPAT_MODE compat = c;
  
! 	ECPGlog("ECPGdeallocate line %d: NAME: %s\n", lineno, name);
! 	if (INFORMIX_MODE(compat))
! 	{
! 		/*
! 		 * Just ignore all errors since we do not know the list of cursors we
! 		 * are allowed to free. We have to trust the software.
! 		 */
! 		return true;
! 	}
  
! 	if (!ret)
! 		ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
  
! 	return ret;
  }
  
  bool
! ECPGdeallocate_all(int lineno, int compat)
  {
  	/* deallocate all prepared statements */
! 	while (prep_stmts != NULL)
  	{
! 		bool		b = ECPGdeallocate(lineno, compat, prep_stmts->name);
! 
! 		if (!b)
  			return false;
  	}
  
--- 165,278 ----
  	PQclear(query);
  	this->prepared = true;
  
! 	if (con->prep_stmts == NULL)
  		this->next = NULL;
  	else
! 		this->next = con->prep_stmts;
  
! 	con->prep_stmts = this;
  	return true;
  }
  
+ static struct prepared_statement *find_prepared_statement(const char *name,
+ 	struct connection *con, struct prepared_statement **prev_)
+ {
+ 	struct prepared_statement  *this,
+ 							   *prev;
+ 
+ 	for (this = con->prep_stmts, prev = NULL; this != NULL; prev = this, this = this->next)
+ 	{
+ 		if (strcmp(this->name, name) == 0)
+ 		{
+ 			if (prev_)
+ 				*prev_ = prev;
+ 			return this;
+ 		}
+ 	}
+ 	return NULL;
+ }
+ 
  static bool
! deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con, struct prepared_statement *prev, struct prepared_statement *this)
  {
! 	bool	r = false;
  
! 	ECPGlog("ECPGdeallocate line %d: NAME: %s\n", lineno, this->name);
! 
! 	/* first deallocate the statement in the backend */
! 	if (this->prepared)
  	{
! 		char *text;
! 		PGresult *query;
! 		
! 		text = (char *) ECPGalloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
! 		if (text)
  		{
! 			sprintf(text, "deallocate \"%s\"", this->name);
! 			query = PQexec(this->stmt->connection->connection, text);
! 			ECPGfree(text);
! 			if (ECPGcheck_PQresult(query, lineno, this->stmt->connection->connection, this->stmt->compat))
  			{
  				PQclear(query);
+ 				r = true;
  			}
  		}
! 	}
  
! 	/*
! 	 * Just ignore all errors since we do not know the list of cursors we
! 	 * are allowed to free. We have to trust the software.
! 	 */
! 	if (!r && !INFORMIX_MODE(c))
! 	{
! 		ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
! 		return false;
  	}
! 	
! 	/* okay, free all the resources */
! 	ECPGfree(this->stmt->command);
! 	ECPGfree(this->stmt);
! 	if (prev != NULL)
! 		prev->next = this->next;
! 	else
! 		con->prep_stmts = this->next;
! 
! 	ECPGfree(this);
! 	return true;
  }
  
  /* handle the EXEC SQL DEALLOCATE PREPARE statement */
  bool
! ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
  {
! 	struct connection		   *con;
! 	struct prepared_statement  *this,
! 							   *prev;
  
! 	con = ECPGget_connection(connection_name);
  
! 	this = find_prepared_statement(name, con, &prev);
! 	if (this)
! 		return deallocate_one(lineno, c, con, prev, this);
  
! 	/* prepared statement is not found */
! 	if (INFORMIX_MODE(c))
! 		return true;
! 	ECPGraise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
! 	return false;
  }
  
  bool
! ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
  {
+ 	struct connection		   *con;
+ 
+ 	con = ECPGget_connection(connection_name);
+ 
  	/* deallocate all prepared statements */
! 	while (con->prep_stmts)
  	{
! 		if (!deallocate_one(lineno, compat, con, NULL, con->prep_stmts))
  			return false;
  	}
  
*************** ECPGdeallocate_all(int lineno, int compa
*** 251,272 ****
  }
  
  char *
! ECPGprepared(const char *name, int lineno)
  {
! 	struct prepared_statement *this;
! 
! 	for (this = prep_stmts; this != NULL && ((strcmp(this->name, name) != 0) || this->prepared == false); this = this->next);
! 	return (this) ? this->stmt->command : NULL;
  }
  
  /* return the prepared statement */
  char *
! ECPGprepared_statement(const char *name, int lineno)
  {
! 	struct prepared_statement *this;
! 
! 	for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
! 	return (this) ? this->stmt->command : NULL;
  }
  
  /*
--- 280,297 ----
  }
  
  char *
! ECPGprepared(const char *name, struct connection *con, int lineno)
  {
! 	struct prepared_statement  *this;
! 	this = find_prepared_statement(name, con, NULL);
! 	return this ? this->stmt->command : NULL;
  }
  
  /* return the prepared statement */
  char *
! ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
  {
! 	return ECPGprepared(name, ECPGget_connection(connection_name), lineno);
  }
  
  /*
*************** ECPGauto_prepare(int lineno, const char 
*** 426,439 ****
  	entNo = SearchStmtCache(query);
  
  	/* if not found - add the statement to the cache    */
!         if(entNo)
  	{
! 	        ECPGlog("ECPGauto_prepare line %d: stmt found in cache, entry %d\n", lineno, entNo);
  		*name = ECPGstrdup(stmtCacheEntries[entNo].stmtID, lineno); 
  	}
  	else
  	{
! 	        ECPGlog("ECPGauto_prepare line %d: stmt not in cache; inserting\n", lineno);
  
  		/* generate a statement ID */
  		*name = (char *) ECPGalloc(STMTID_SIZE, lineno);
--- 451,464 ----
  	entNo = SearchStmtCache(query);
  
  	/* if not found - add the statement to the cache    */
! 	if(entNo)
  	{
! 		ECPGlog("ECPGauto_prepare line %d: stmt found in cache, entry %d\n", lineno, entNo);
  		*name = ECPGstrdup(stmtCacheEntries[entNo].stmtID, lineno); 
  	}
  	else
  	{
! 		ECPGlog("ECPGauto_prepare line %d: stmt not in cache; inserting\n", lineno);
  
  		/* generate a statement ID */
  		*name = (char *) ECPGalloc(STMTID_SIZE, lineno);
*************** ECPGauto_prepare(int lineno, const char 
*** 450,453 ****
  
  	return(true);
  }
- 
--- 475,477 ----
diff -cpr HEAD/src/interfaces/ecpg/include/ecpglib.h ecpg_prepare-8.3/src/interfaces/ecpg/include/ecpglib.h
*** HEAD/src/interfaces/ecpg/include/ecpglib.h	Tue Aug 14 19:01:52 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/include/ecpglib.h	Wed Sep 26 13:14:43 2007
*************** bool		ECPGtrans(int, const char *, const
*** 49,57 ****
  bool		ECPGdisconnect(int, const char *);
  bool		ECPGprepare(int, const char *, const int, const char *, const char *);
  bool		ECPGauto_prepare(int, const char *, const int, char **, const char *);
! bool		ECPGdeallocate(int, int, const char *);
! bool		ECPGdeallocate_all(int, int);
! char	   *ECPGprepared_statement(const char *, int);
  
  void		ECPGlog(const char *format,...);
  char	   *ECPGerrmsg(void);
--- 49,57 ----
  bool		ECPGdisconnect(int, const char *);
  bool		ECPGprepare(int, const char *, const int, const char *, const char *);
  bool		ECPGauto_prepare(int, const char *, const int, char **, const char *);
! bool		ECPGdeallocate(int, int, const char *connection_name, const char *name);
! bool		ECPGdeallocate_all(int, int, const char *connection_name);
! char	   *ECPGprepared_statement(const char *connection_name, const char *name, int);
  
  void		ECPGlog(const char *format,...);
  char	   *ECPGerrmsg(void);
diff -cpr HEAD/src/interfaces/ecpg/preproc/output.c ecpg_prepare-8.3/src/interfaces/ecpg/preproc/output.c
*** HEAD/src/interfaces/ecpg/preproc/output.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/preproc/output.c	Wed Sep 26 13:14:43 2007
*************** output_prepare_statement(char *name, cha
*** 153,166 ****
  void
  output_deallocate_prepare_statement(char *name)
  {
  	if (strcmp(name, "all"))
  	{
! 		fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, ", compat);
  		output_escaped_str(name, true);
  		fputs(");", yyout);
  	}
  	else
! 		fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d);", compat);
  
  	whenever_action(2);
  	free(name);
--- 153,167 ----
  void
  output_deallocate_prepare_statement(char *name)
  {
+ 	const char* con = connection ? connection : "NULL";
  	if (strcmp(name, "all"))
  	{
! 		fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, %s, ", compat, con);
  		output_escaped_str(name, true);
  		fputs(");", yyout);
  	}
  	else
! 		fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
  
  	whenever_action(2);
  	free(name);
diff -cpr HEAD/src/interfaces/ecpg/preproc/preproc.y ecpg_prepare-8.3/src/interfaces/ecpg/preproc/preproc.y
*** HEAD/src/interfaces/ecpg/preproc/preproc.y	Tue Sep  4 19:02:29 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/preproc/preproc.y	Wed Sep 26 13:14:43 2007
*************** stmt:  AlterDatabaseStmt		{ output_state
*** 906,915 ****
  		| ECPGExecuteImmediateStmt	{ output_statement($1, 0, ECPGst_exec_immediate); }
  		| ECPGFree
  		{
  			if (strcmp($1, "all"))
! 				fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, \"%s\");", compat, $1);
  			else
! 				fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d);", compat);
  
  			whenever_action(2);
  			free($1);
--- 906,916 ----
  		| ECPGExecuteImmediateStmt	{ output_statement($1, 0, ECPGst_exec_immediate); }
  		| ECPGFree
  		{
+ 			const char *con = connection ? connection : "NULL";
  			if (strcmp($1, "all"))
! 				fprintf(yyout, "{ ECPGdeallocate(__LINE__, %d, %s, \"%s\");", compat, con, $1);
  			else
! 				fprintf(yyout, "{ ECPGdeallocate_all(__LINE__, %d, %s);", compat, con);
  
  			whenever_action(2);
  			free($1);
*************** prep_type_clause: '(' type_list ')'	{ $$
*** 3349,3355 ****
  
  ExecuteStmt: EXECUTE prepared_name execute_param_clause execute_rest /* execute_rest is an ecpg addon */
  			{
! 				/* $$ = cat_str(3, make_str("ECPGprepared_statement("), $2, make_str(", __LINE__)"));*/
  				$$ = $2;
  			}
  		| CREATE OptTemp TABLE create_as_target AS
--- 3350,3356 ----
  
  ExecuteStmt: EXECUTE prepared_name execute_param_clause execute_rest /* execute_rest is an ecpg addon */
  			{
! 				/* $$ = cat_str(3, make_str("ECPGprepared_statement("), connection, $2, make_str("__LINE__)"));*/
  				$$ = $2;
  			}
  		| CREATE OptTemp TABLE create_as_target AS
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 5185,5190 ****
--- 5186,5192 ----
  		{
  			struct cursor *ptr, *this;
  			struct variable *thisquery = (struct variable *)mm_alloc(sizeof(struct variable));
+ 			const char *con = connection ? connection : "NULL";
  
  			for (ptr = cur; ptr != NULL; ptr = ptr->next)
  			{
*************** ECPGCursorStmt:  DECLARE name cursor_opt
*** 5205,5212 ****
  			thisquery->type = &ecpg_query;
  			thisquery->brace_level = 0;
  			thisquery->next = NULL;
! 			thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, __LINE__)") + strlen($7));
! 			sprintf(thisquery->name, "ECPGprepared_statement(%s, __LINE__)", $7);
  
  			this->argsinsert = NULL;
  			add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
--- 5207,5214 ----
  			thisquery->type = &ecpg_query;
  			thisquery->brace_level = 0;
  			thisquery->next = NULL;
! 			thisquery->name = (char *) mm_alloc(sizeof("ECPGprepared_statement(, , __LINE__)") + strlen(con) + strlen($7));
! 			sprintf(thisquery->name, "ECPGprepared_statement(%s, %s, __LINE__)", con, $7);
  
  			this->argsinsert = NULL;
  			add_variable_to_head(&(this->argsinsert), thisquery, &no_indicator);
*************** UsingConst: AllConst
*** 5914,5934 ****
   */
  ECPGDescribe: SQL_DESCRIBE INPUT_P name using_descriptor
  	{
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("1, ECPGprepared_statement(\"\", __LINE__)") + strlen($3));
! 		sprintf($$, "1, ECPGprepared_statement(\"%s\", __LINE__)", $3);
  	}
  	| SQL_DESCRIBE opt_output name using_descriptor
  	{
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(\"\", __LINE__)") + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(\"%s\", __LINE__)", $3);
  	}
  	| SQL_DESCRIBE opt_output name into_descriptor
  	{
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(\"\", __LINE__)") + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(\"%s\", __LINE__)", $3);
  	}
  	;
  
--- 5916,5939 ----
   */
  ECPGDescribe: SQL_DESCRIBE INPUT_P name using_descriptor
  	{
+ 		const char *con = connection ? connection : "NULL";
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("1, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
! 		sprintf($$, "1, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
  	}
  	| SQL_DESCRIBE opt_output name using_descriptor
  	{
+ 		const char *con = connection ? connection : "NULL";
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
  	}
  	| SQL_DESCRIBE opt_output name into_descriptor
  	{
+ 		const char *con = connection ? connection : "NULL";
  		mmerror(PARSE_ERROR, ET_WARNING, "using unsupported describe statement.\n");
! 		$$ = (char *) mm_alloc(sizeof("0, ECPGprepared_statement(, \"\", __LINE__)") + strlen(con) + strlen($3));
! 		sprintf($$, "0, ECPGprepared_statement(%s, \"%s\", __LINE__)", con, $3);
  	}
  	;
  
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-desc.c ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-desc.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-desc.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-desc.c	Wed Sep 26 13:14:43 2007
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 197,203 ****
  #line 45 "desc.pgc"
  
  
! 	{ ECPGdeallocate(__LINE__, 0, "Foo-1");
  #line 47 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
--- 197,203 ----
  #line 45 "desc.pgc"
  
  
! 	{ ECPGdeallocate(__LINE__, 0, NULL, "Foo-1");
  #line 47 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 247,253 ****
  #line 57 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c1  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("foo2", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 247,253 ----
  #line 57 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c1  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "foo2", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 297,303 ****
  #line 69 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("foo3", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 297,303 ----
  #line 69 "desc.pgc"
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare c2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "foo3", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_descriptor, "indesc", 0L, 0L, 0L, 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 344,350 ****
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 80 "desc.pgc"
  
! 	{ ECPGdeallocate_all(__LINE__, 0);
  #line 81 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
--- 344,350 ----
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 80 "desc.pgc"
  
! 	{ ECPGdeallocate_all(__LINE__, 0, NULL);
  #line 81 "desc.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-dyntest.c ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-dyntest.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-dyntest.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-dyntest.c	Wed Sep 26 13:14:43 2007
*************** if (sqlca.sqlcode < 0) error (  );}
*** 263,269 ****
  
  
    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare MYCURS  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("myquery", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 60 "dyntest.pgc"
  
--- 263,269 ----
  
  
    { ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare MYCURS  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "myquery", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 60 "dyntest.pgc"
  
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-execute.c ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-execute.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-execute.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-execute.c	Wed Sep 26 13:14:43 2007
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 142,148 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "execute.pgc"
  
--- 142,148 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "execute.pgc"
  
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 187,193 ****
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 66 "execute.pgc"
  
! 	{ ECPGdeallocate(__LINE__, 0, "f");
  #line 67 "execute.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
--- 187,193 ----
  if (sqlca.sqlcode < 0) sqlprint();}
  #line 66 "execute.pgc"
  
! 	{ ECPGdeallocate(__LINE__, 0, NULL, "f");
  #line 67 "execute.pgc"
  
  if (sqlca.sqlcode < 0) sqlprint();}
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 207,213 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 207,213 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 0, ECPGst_normal, "declare CUR2  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
diff -cpr HEAD/src/interfaces/ecpg/test/expected/sql-oldexec.c ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-oldexec.c
*** HEAD/src/interfaces/ecpg/test/expected/sql-oldexec.c	Tue Aug 14 19:32:47 2007
--- ecpg_prepare-8.3/src/interfaces/ecpg/test/expected/sql-oldexec.c	Wed Sep 26 13:14:43 2007
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 142,148 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "oldexec.pgc"
  
--- 142,148 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
  #line 52 "oldexec.pgc"
  
*************** if (sqlca.sqlcode < 0) sqlprint();}
*** 201,207 ****
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR3  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement("f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
--- 201,207 ----
  
  
  	{ ECPGdo(__LINE__, 0, 1, NULL, 1, ECPGst_normal, "declare CUR3  cursor  for $1", 
! 	ECPGt_char_variable,(ECPGprepared_statement(NULL, "f", __LINE__)),(long)1,(long)1,(1)*sizeof(char), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, 
  	ECPGt_const,"1",(long)1,(long)1,strlen("1"), 
  	ECPGt_NO_INDICATOR, NULL , 0L, 0L, 0L, ECPGt_EOIT, ECPGt_EORT);
prep.pgcapplication/octet-stream; name=prep.pgcDownload
#7Michael Meskes
meskes@postgresql.org
In reply to: ITAGAKI Takahiro (#6)
Re: Thread-safe PREPARE in ecpg

On Wed, Sep 26, 2007 at 01:43:34PM +0900, ITAGAKI Takahiro wrote:

Here is a revised patch against CVS HEAD.
I fixed a bug in ECPGdeallocate_all().

Applied to CVS HEAD. I also added your example to the regression tests.

Michael
--
Michael Meskes
Email: Michael at Fam-Meskes dot De, Michael at Meskes dot (De|Com|Net|Org)
ICQ: 179140304, AIM/Yahoo: michaelmeskes, Jabber: meskes@jabber.org
Go SF 49ers! Go Rhein Fire! Use Debian GNU/Linux! Use PostgreSQL!