From ef567413d2f7a75c31b79a48c79ff628fec3e5fa Mon Sep 17 00:00:00 2001
From: "Marc G. Fournier" <scrappy@hub.org>
Date: Mon, 18 May 1998 16:05:05 +0000
Subject: [PATCH] From: Michael Meskes <meskes@topsystem.de>

+
+ Wed May  6 16:09:45 CEST 1998
+
+       - Some more cleanups in the library.
+
+ Thu May  7 12:34:28 CEST 1998
+
+       - Made CONNECT and DISCONNECT statement more SQL3 compliant.
+       - Changed the API for the ECPGconnect function to be able to handle
+         hostnames and ports
+
+ Fri May  8 13:54:45 CEST 1998
+       - More changes to the parser. The connect statement now allows
+         ORACLE style logins.
+       - db-name is accepted in two ways:
+               - <dbname>[@<server>][:<port>]
+               - esql:postgresql://<server>[:<port>][/<dbname>]
+
+ Mon May 11 10:28:37 CEST 1998
+
+       - Added '? options' to connect call.
+       - Also allow USING as keyword for the password
+
+ Thu May 14 15:09:58 CEST 1998
+
+       - Changed preproc.y and pgc.l according to the parser changes in the
+         backend.
+
+ Fri May 15 09:55:21 CEST 1998
+
+       - Added connection_name handling
+
+
+ Mon May 18 10:33:58 CEST 1998
+
+       - Fixed some more bugs
+       - Set version to 2.3.1
+       - Set library version to 2.2
---
 src/interfaces/ecpg/ChangeLog               |  39 ++
 src/interfaces/ecpg/include/ecpgerrno.h     |  36 +-
 src/interfaces/ecpg/include/ecpglib.h       |   6 +-
 src/interfaces/ecpg/lib/Makefile.in         |   2 +-
 src/interfaces/ecpg/lib/ecpglib.c           | 234 ++++++++--
 src/interfaces/ecpg/preproc/Makefile        |   2 +-
 src/interfaces/ecpg/preproc/ecpg_keywords.c |   4 +-
 src/interfaces/ecpg/preproc/extern.h        |  10 +-
 src/interfaces/ecpg/preproc/keywords.c      |   4 +-
 src/interfaces/ecpg/preproc/pgc.l           |  12 +-
 src/interfaces/ecpg/preproc/preproc.y       | 457 ++++++++++++--------
 src/interfaces/ecpg/preproc/type.c          |  43 +-
 src/interfaces/ecpg/test/perftest.pgc       |   2 +-
 src/interfaces/ecpg/test/test1.pgc          |   2 +-
 src/interfaces/ecpg/test/test2.pgc          |   8 +-
 15 files changed, 571 insertions(+), 290 deletions(-)

diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog
index 5828ef8a81b..c7acdf023df 100644
--- a/src/interfaces/ecpg/ChangeLog
+++ b/src/interfaces/ecpg/ChangeLog
@@ -189,3 +189,42 @@ Wed May  6 11:42:48 CEST 1998
           an example)
 	- Set version to 2.3.0
 	- Set library version to 2.1
+
+Wed May  6 16:09:45 CEST 1998
+
+	- Some more cleanups in the library.
+
+Thu May  7 12:34:28 CEST 1998
+
+	- Made CONNECT and DISCONNECT statement more SQL3 compliant.
+	- Changed the API for the ECPGconnect function to be able to handle
+	  hostnames and ports
+
+Fri May  8 13:54:45 CEST 1998
+	- More changes to the parser. The connect statement now allows
+	  ORACLE style logins.
+	- db-name is accepted in two ways:
+		- <dbname>[@<server>][:<port>]
+		- esql:postgresql://<server>[:<port>][/<dbname>]
+
+Mon May 11 10:28:37 CEST 1998
+
+	- Added '? options' to connect call.
+	- Also allow USING as keyword for the password
+
+Thu May 14 15:09:58 CEST 1998
+
+	- Changed preproc.y and pgc.l according to the parser changes in the
+	  backend.
+
+Fri May 15 09:55:21 CEST 1998
+
+	- Added connection_name handling
+
+
+Mon May 18 10:33:58 CEST 1998
+
+	- Fixed some more bugs
+	- Set version to 2.3.1
+	- Set library version to 2.2
+
diff --git a/src/interfaces/ecpg/include/ecpgerrno.h b/src/interfaces/ecpg/include/ecpgerrno.h
index 1be718216d7..cddc7e6a686 100644
--- a/src/interfaces/ecpg/include/ecpgerrno.h
+++ b/src/interfaces/ecpg/include/ecpgerrno.h
@@ -1,22 +1,32 @@
 #ifndef _ECPG_ERROR_H
 #define _ECPG_ERROR_H
 
+#include <errno.h>
+
 /* This is a list of all error codes the embedded SQL program can return */
 #define	ECPG_NO_ERROR		0
 #define ECPG_NOT_FOUND		100
 
-#define ECPG_PGSQL		-1
-#define ECPG_UNSUPPORTED	-2
-#define ECPG_TOO_MANY_ARGUMENTS	-3
-#define ECPG_TOO_FEW_ARGUMENTS	-4
-#define ECPG_TRANS		-5
-#define ECPG_TOO_MANY_MATCHES	-6
-#define ECPG_INT_FORMAT		-7
-#define ECPG_UINT_FORMAT	-8
-#define ECPG_FLOAT_FORMAT	-9
-#define ECPG_CONVERT_BOOL	-10
-#define ECPG_EMPTY		-11
-#define ECPG_CONNECT		-12
-#define ECPG_DISCONNECT		-13
+/* system error codes returned by ecpglib get the correct number,
+ * but are made negative
+ */
+#define ECPG_OUT_OF_MEMORY	-ENOMEM
+
+/* first we have a set of ecpg messages, they start at 200 */
+#define ECPG_UNSUPPORTED	-200
+#define ECPG_TOO_MANY_ARGUMENTS	-201
+#define ECPG_TOO_FEW_ARGUMENTS	-202
+#define ECPG_TOO_MANY_MATCHES	-203
+#define ECPG_INT_FORMAT		-204
+#define ECPG_UINT_FORMAT	-205
+#define ECPG_FLOAT_FORMAT	-206
+#define ECPG_CONVERT_BOOL	-207
+#define ECPG_EMPTY		-208
+#define ECPG_NO_CONN		-209
+
+/* finally the backend error messages, they start at 300 */
+#define ECPG_PGSQL		-300
+#define ECPG_TRANS		-301
+#define ECPG_CONNECT		-302
 
 #endif /* !_ECPG_ERROR_H */
diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h
index 9a5c2732d8f..c0603b2e7f5 100644
--- a/src/interfaces/ecpg/include/ecpglib.h
+++ b/src/interfaces/ecpg/include/ecpglib.h
@@ -5,11 +5,11 @@ extern "C" {
 #endif
 
 void		ECPGdebug(int, FILE *);
-bool		ECPGconnect(const char *);
+bool		ECPGsetconn(int, const char *);
+bool		ECPGconnect(int, const char *, const char *, const char *, const char *);
 bool		ECPGdo(int, char *,...);
 bool		ECPGtrans(int, const char *);
-bool		ECPGfinish(void);
-bool		ECPGdisconnect(const char *);
+bool		ECPGdisconnect(int, const char *);
 
 void		ECPGlog(const char *format,...);
 
diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in
index 0fb1d6f0261..c402afc8446 100644
--- a/src/interfaces/ecpg/lib/Makefile.in
+++ b/src/interfaces/ecpg/lib/Makefile.in
@@ -4,7 +4,7 @@ include $(SRCDIR)/Makefile.global
 PQ_INCLUDE=-I$(SRCDIR)/interfaces/libpq
 
 SO_MAJOR_VERSION=2
-SO_MINOR_VERSION=1
+SO_MINOR_VERSION=2
 
 PORTNAME=@PORTNAME@
 
diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c
index 4f40a2e1020..445776ec828 100644
--- a/src/interfaces/ecpg/lib/ecpglib.c
+++ b/src/interfaces/ecpg/lib/ecpglib.c
@@ -26,7 +26,13 @@
 
 extern int no_auto_trans;
 
-static PGconn *simple_connection = NULL;
+static struct connection
+{
+	char *name;
+	PGconn *connection;
+	struct connection *next;
+} *all_connections = NULL, *actual_connection = NULL;
+
 static int	simple_debug = 0;
 static FILE *debugstream = NULL;
 static int	committed = true;
@@ -54,6 +60,9 @@ quote_postgres(char *arg)
 	int			i,
 				ri;
 
+	if (!res)
+		return(res);
+		
 	for (i = 0, ri = 0; arg[i]; i++, ri++)
 	{
 		switch (arg[i])
@@ -73,6 +82,40 @@ quote_postgres(char *arg)
 }
 
 
+static void
+ECPGfinish(struct connection *act)
+{
+	if (act != NULL)
+	{
+		ECPGlog("ECPGfinish: finishing %s.\n", act->name);
+		PQfinish(act->connection);
+		/* remove act from the list */
+		if (act == all_connections)
+		{
+			all_connections = act->next;
+			free(act->name);
+			free(act);
+		}
+		else
+		{
+			struct connection *con;
+			
+			for (con = all_connections; con->next && con->next !=  act; con = con->next);
+			if (con->next)
+			{
+				con->next = act->next;
+				free(act->name);
+				free(act);
+			}
+		}
+		
+		if (actual_connection == act)
+			actual_connection = all_connections;
+	}
+	else
+		ECPGlog("ECPGfinish: called an extra time.\n");
+}
+
 bool
 ECPGdo(int lineno, char *query,...)
 {
@@ -195,14 +238,40 @@ ECPGdo(int lineno, char *query,...)
 				{
 					/* set slen to string length if type is char * */
 					int			slen = (varcharsize == 0) ? strlen((char *) value) : varcharsize;
+					char * tmp;
 
 					newcopy = (char *) malloc(slen + 1);
+					if (!newcopy)
+					{
+						ECPGfinish(actual_connection);
+						ECPGlog("out of memory\n");
+				                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+						return false;
+					}
+						
 					strncpy(newcopy, (char *) value, slen);
 					newcopy[slen] = '\0';
 
 					mallocedval = (char *) malloc(2 * strlen(newcopy) + 3);
+					if (!mallocedval)
+					{
+						ECPGfinish(actual_connection);
+						ECPGlog("out of memory\n");
+				                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+						return false;
+					}
+						
 					strcpy(mallocedval, "'");
-					strcat(mallocedval, quote_postgres(newcopy));
+					tmp = quote_postgres(newcopy);
+					if (!tmp)
+					{
+						ECPGfinish(actual_connection);
+						ECPGlog("out of memory\n");
+				                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+						return false;
+					}
+						
+					strcat(mallocedval, tmp);
 					strcat(mallocedval, "'");
 
 					free(newcopy);
@@ -215,14 +284,40 @@ ECPGdo(int lineno, char *query,...)
 				{
 					struct ECPGgeneric_varchar *var =
 					(struct ECPGgeneric_varchar *) value;
+					char *tmp;
 
 					newcopy = (char *) malloc(var->len + 1);
+					if (!newcopy)
+					{
+						ECPGfinish(actual_connection);
+						ECPGlog("out of memory\n");
+				                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+						return false;
+					}
+						
 					strncpy(newcopy, var->arr, var->len);
 					newcopy[var->len] = '\0';
 
 					mallocedval = (char *) malloc(2 * strlen(newcopy) + 3);
+					if (!mallocedval)
+					{
+						ECPGfinish(actual_connection);
+						ECPGlog("out of memory\n");
+				                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+						return false;
+					}
+					
 					strcpy(mallocedval, "'");
-					strcat(mallocedval, quote_postgres(newcopy));
+					tmp = quote_postgres(newcopy);
+					if (!tmp)
+					{
+						ECPGfinish(actual_connection);
+						ECPGlog("out of memory\n");
+				                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+						return false;
+					}
+					                                                
+					strcat(mallocedval, tmp);
 					strcat(mallocedval, "'");
 
 					free(newcopy);
@@ -249,6 +344,14 @@ ECPGdo(int lineno, char *query,...)
 		newcopy = (char *) malloc(strlen(copiedquery)
 								  + strlen(tobeinserted)
 								  + 1);
+		if (!newcopy)
+		{
+			ECPGfinish(actual_connection);
+			ECPGlog("out of memory\n");
+	                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+			return false;
+		}
+		 	
 		strcpy(newcopy, copiedquery);
 		if ((p = strstr(newcopy, ";;")) == NULL)
 		{
@@ -301,7 +404,7 @@ ECPGdo(int lineno, char *query,...)
 
 	if (committed && !no_auto_trans)
 	{
-		if ((results = PQexec(simple_connection, "begin transaction")) == NULL)
+		if ((results = PQexec(actual_connection->connection, "begin transaction")) == NULL)
 		{
 			register_error(ECPG_TRANS, "Error starting transaction line %d.", lineno);
 			return false;
@@ -311,15 +414,15 @@ ECPGdo(int lineno, char *query,...)
 	}
 
 	ECPGlog("ECPGdo line %d: QUERY: %s\n", lineno, copiedquery);
-	results = PQexec(simple_connection, copiedquery);
+	results = PQexec(actual_connection->connection, copiedquery);
 	free(copiedquery);
 
 	if (results == NULL)
 	{
 		ECPGlog("ECPGdo line %d: error: %s", lineno,
-				PQerrorMessage(simple_connection));
+				PQerrorMessage(actual_connection->connection));
 		register_error(ECPG_PGSQL, "Postgres error: %s line %d.",
-					   PQerrorMessage(simple_connection), lineno);
+					   PQerrorMessage(actual_connection->connection), lineno);
 	}
 	else
 	{
@@ -644,9 +747,9 @@ ECPGdo(int lineno, char *query,...)
 			case PGRES_FATAL_ERROR:
 			case PGRES_BAD_RESPONSE:
 				ECPGlog("ECPGdo line %d: Error: %s",
-						lineno, PQerrorMessage(simple_connection));
+						lineno, PQerrorMessage(actual_connection->connection));
 				register_error(ECPG_PGSQL, "Error: %s line %d.",
-							   PQerrorMessage(simple_connection), lineno);
+							   PQerrorMessage(actual_connection->connection), lineno);
 				status = false;
 				break;
 			case PGRES_COPY_OUT:
@@ -667,7 +770,7 @@ ECPGdo(int lineno, char *query,...)
 	}
 
 	/* check for asynchronous returns */
-	notify = PQnotifies(simple_connection);
+	notify = PQnotifies(actual_connection->connection);
 	if (notify)
 	{
 		ECPGlog("ECPGdo line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
@@ -686,7 +789,7 @@ ECPGtrans(int lineno, const char * transaction)
 	PGresult   *res;
 
 	ECPGlog("ECPGtrans line %d action = %s\n", lineno, transaction);
-	if ((res = PQexec(simple_connection, transaction)) == NULL)
+	if ((res = PQexec(actual_connection->connection, transaction)) == NULL)
 	{
 		register_error(ECPG_TRANS, "Error in transaction processing line %d.", lineno);
 		return (FALSE);
@@ -698,59 +801,101 @@ ECPGtrans(int lineno, const char * transaction)
 }
 
 bool
-ECPGsetdb(PGconn *newcon)
+ECPGsetconn(int lineno, const char *connection_name)
 {
-	ECPGfinish();
-	simple_connection = newcon;
-	return true;
+	struct connection *con = all_connections;
+	
+	for (; con && strcmp(connection_name, con->name) == 0; con=con->next);
+	if (con)
+	{
+		actual_connection = con;
+		return true;
+	}
+	else
+	{
+		register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno);
+		return false;
+	}
 }
 
 bool
-ECPGconnect(const char *dbname)
+ECPGconnect(int lineno, const char *dbname, const char *user, const char *passwd, const char * connection_name)
 {
-	char	   *name = strdup(dbname);
-
-	ECPGlog("ECPGconnect: opening database %s\n", name);
+	struct connection *this = malloc(sizeof(struct connection));
 
-	sqlca.sqlcode = 0;
+	if (!this)
+	{
+		ECPGlog("out of memory\n");
+                register_error(ECPG_OUT_OF_MEMORY, "out of memory in line %d", lineno);
+		return false;
+	}
+				
+	if (dbname == NULL && connection_name == NULL)
+		connection_name = "DEFAULT";
+	
+	/* add connection to our list */
+	if (connection_name != NULL)
+		this->name = strdup(connection_name);
+	else
+		this->name = strdup(dbname);
+	
+	if (all_connections == NULL)
+		this->next = NULL;
+	else
+		this->next = all_connections;
 
-	ECPGsetdb(PQsetdb(NULL, NULL, NULL, NULL, name));
+	actual_connection = all_connections = this;
+		
+	ECPGlog("ECPGconnect: opening database %s %s%s\n", dbname ? dbname : "NULL", user ? "for user ": "", user ? user : "");
 
-	free(name);
-	name = NULL;
+	sqlca.sqlcode = 0;
 
-	if (PQstatus(simple_connection) == CONNECTION_BAD)
+	this->connection = PQsetdbLogin(NULL, NULL, NULL, NULL, dbname, user, passwd);
+        
+	if (PQstatus(this->connection) == CONNECTION_BAD)
 	{
-		ECPGfinish();
-		ECPGlog("connect: could not open database %s\n", dbname);
-		register_error(ECPG_CONNECT, "connect: could not open database %s.", dbname);
+		ECPGfinish(this);
+                ECPGlog("connect: could not open database %s %s%s in line %d\n", dbname ? dbname : "NULL", user ? "for user ": "", user ? user : "", lineno);
+                
+		register_error(ECPG_CONNECT, "connect: could not open database %s.", dbname ? dbname : "NULL");
 		return false;
 	}
+	
 	return true;
 }
 
 bool
-ECPGdisconnect(const char *dbname)
+ECPGdisconnect(int lineno, const char *connection_name)
 {
-	if (strlen(dbname) > 0 && strcmp(PQdb(simple_connection), dbname) != 0)
+	struct connection *con;
+	
+	if (strcmp(connection_name, "CURRENT") == 0)
+		ECPGfinish(actual_connection);
+	else if (strcmp(connection_name, "ALL") == 0)
 	{
-		ECPGlog("disconnect: not connected to database %s\n", dbname);
-		register_error(ECPG_DISCONNECT, "disconnect: not connected to database %s.", dbname);
-		return false;
+		for (con = all_connections; con;)
+		{
+			struct connection *f = con;
+			
+			con = con->next;
+			ECPGfinish(f);
+		}
 	}
-	return ECPGfinish();
-}
-
-bool
-ECPGfinish(void)
-{
-	if (simple_connection != NULL)
+	else 
 	{
-		ECPGlog("ECPGfinish: finishing.\n");
-		PQfinish(simple_connection);
+		for (con = all_connections; con && strcmp(con->name, connection_name);con = con->next);
+		if (con == NULL)
+		{		
+			ECPGlog("disconnect: not connected to connection %s\n", connection_name);
+			register_error(ECPG_NO_CONN, "No such connection %s in line %d", connection_name, lineno);
+			return false;
+		}
+		else
+		{
+			ECPGfinish(con);
+		}
 	}
-	else
-		ECPGlog("ECPGfinish: called an extra time.\n");
+	
 	return true;
 }
 
@@ -771,6 +916,9 @@ ECPGlog(const char *format,...)
 	{
 		char	   *f = (char *) malloc(strlen(format) + 100);
 
+		if (!f)
+			return;
+						
 		sprintf(f, "[%d]: %s", getpid(), format);
 
 		va_start(ap, format);
diff --git a/src/interfaces/ecpg/preproc/Makefile b/src/interfaces/ecpg/preproc/Makefile
index 5cb3861c22a..1410087832a 100644
--- a/src/interfaces/ecpg/preproc/Makefile
+++ b/src/interfaces/ecpg/preproc/Makefile
@@ -3,7 +3,7 @@ include $(SRCDIR)/Makefile.global
 
 MAJOR_VERSION=2
 MINOR_VERSION=3
-PATCHLEVEL=0
+PATCHLEVEL=1
 
 CFLAGS+=-I../include -DMAJOR_VERSION=$(MAJOR_VERSION) \
 	-DMINOR_VERSION=$(MINOR_VERSION) -DPATCHLEVEL=$(PATCHLEVEL) \
diff --git a/src/interfaces/ecpg/preproc/ecpg_keywords.c b/src/interfaces/ecpg/preproc/ecpg_keywords.c
index 3badb939974..bc724c54b31 100644
--- a/src/interfaces/ecpg/preproc/ecpg_keywords.c
+++ b/src/interfaces/ecpg/preproc/ecpg_keywords.c
@@ -24,12 +24,14 @@ static ScanKeyword ScanKeywords[] = {
 	{"break", SQL_BREAK},
 	{"call", SQL_CALL},
 	{"connect", SQL_CONNECT},
+	{"connection", SQL_CONNECTION},
 	{"continue", SQL_CONTINUE},
 	{"disconnect", SQL_DISCONNECT},
 	{"found", SQL_FOUND},
 	{"go", SQL_GO},
 	{"goto", SQL_GOTO},
-	{"immediate", SQL_IMMEDIATE},
+        {"identified", SQL_IDENTIFIED},
+        {"immediate", SQL_IMMEDIATE},
 	{"indicator", SQL_INDICATOR},
 	{"open", SQL_OPEN},
 	{"release", SQL_RELEASE},
diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h
index 6faa36be883..e18bb33219c 100644
--- a/src/interfaces/ecpg/preproc/extern.h
+++ b/src/interfaces/ecpg/preproc/extern.h
@@ -1,4 +1,5 @@
 #include "parser/keywords.h"
+#include <errno.h>
 
 /* variables */
 
@@ -47,7 +48,8 @@ extern void yyerror(char *);
 /* return codes */
 
 #define OK		0
-#define NO_INCLUDE_FILE	1
-#define PARSE_ERROR	2
-#define OUT_OF_MEMORY	3
-#define ILLEGAL_OPTION	4
+#define PARSE_ERROR	-1
+#define ILLEGAL_OPTION	-2
+
+#define NO_INCLUDE_FILE	ENOENT
+#define OUT_OF_MEMORY	ENOMEM
diff --git a/src/interfaces/ecpg/preproc/keywords.c b/src/interfaces/ecpg/preproc/keywords.c
index 4d8722f69df..9f953cb98f5 100644
--- a/src/interfaces/ecpg/preproc/keywords.c
+++ b/src/interfaces/ecpg/preproc/keywords.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.1 1998/04/21 13:23:06 scrappy Exp $
+ *	  $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/keywords.c,v 1.2 1998/05/18 16:05:00 scrappy Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -189,6 +189,8 @@ static ScanKeyword ScanKeywords[] = {
 	{"substring", SUBSTRING},
 	{"table", TABLE},
 	{"time", TIME},
+	{"timezone_hour", TIMEZONE_HOUR},
+	{"timezone_minute", TIMEZONE_MINUTE},
 	{"to", TO},
 	{"trailing", TRAILING},
 	{"transaction", TRANSACTION},
diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l
index 146938bf7df..c9f0cd459b5 100644
--- a/src/interfaces/ecpg/preproc/pgc.l
+++ b/src/interfaces/ecpg/preproc/pgc.l
@@ -139,12 +139,13 @@ self			[,()\[\].$\:\+\-\*\/\<\>\=\|]
 op_and_self		[\~\!\@\#\%\^\&\|\`\?\$\:\+\-\*\/\<\>\=]
 operator		{op_and_self}+
 
-xminteger              {integer}/-
-xmreal                 {real}/{space}*-{digit}
 xmstop			-
 
-integer			-?{digit}+
-real			-?{digit}+\.{digit}+([Ee][-+]?{digit}+)?
+integer			[\-]?{digit}+
+/*
+real			[\-]?{digit}+\.{digit}+([Ee][-+]?{digit}+)?
+*/
+real			[\-]?(((({digit}*\.{digit}+)|({digit}+\.{digit}*))([Ee][-+]?{digit}+)?)|({digit}+[Ee][-+]?{digit}+))
 
 param			\${integer}
 
@@ -309,7 +310,8 @@ before_comment);
 
 <SQL>{typecast}			{ 	return TYPECAST; }
 
-<SQL>{self}/-[\.0-9]		{
+<SQL>{self}/{space}*-[\.0-9]	{
+					BEGIN(xm);
 					return (yytext[0]);
 				}
 <SQL>{self}				{ 	return (yytext[0]); }
diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y
index b4fa4f2e38c..5e99e7190d9 100644
--- a/src/interfaces/ecpg/preproc/preproc.y
+++ b/src/interfaces/ecpg/preproc/preproc.y
@@ -70,11 +70,13 @@ whenever_action(int mode)
 {
 	if (mode == 1 && when_nf.code != W_NOTHING)
 	{
+		output_line_number();
 		fprintf(yyout, "\nif (sqlca.sqlcode == ECPG_NOT_FOUND) ");
 		print_action(&when_nf);
 	}
 	if (when_error.code != W_NOTHING)
         {
+		output_line_number();
                 fprintf(yyout, "\nif (sqlca.sqlcode < 0) ");
 		print_action(&when_error);
         }
@@ -112,7 +114,7 @@ static struct variable * find_variable(char * name);
 static struct variable *
 find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
 {
-    char *next = strpbrk(++str, ".-"), c = '\0';
+    char *next = strchr(++str, '.'), c = '\0';
 
     if (next != NULL)
     {
@@ -129,6 +131,8 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
 			/* found the end */
 			switch (members->typ->typ)
 			{
+			   case ECPGt_array:
+				return(new_variable(name, ECPGmake_array_type(members->typ->u.element, members->typ->size)));
 			   case ECPGt_struct:
 				return(new_variable(name, ECPGmake_struct_type(members->typ->u.members)));
 			   default:
@@ -138,8 +142,12 @@ find_struct_member(char *name, char *str, struct ECPGstruct_member *members)
 		else
 		{
 			*next = c;
-			if (c == '-') next++;
-			return(find_struct_member(name, next, members->typ->u.members));
+			if (c == '-')
+			{
+				next++;
+				return(find_struct_member(name, next, members->typ->u.element->u.members));
+			}
+			else return(find_struct_member(name, next, members->typ->u.members));
 		}
 	}
     }
@@ -159,9 +167,12 @@ find_struct(char * name, char *next)
 
     /* restore the name, we will need it later on */
     *next = c;
-    if (*next == '-') next++;
-
-    return (find_struct_member(name, next, p->type->u.members));
+    if (c == '-')
+    {
+	next++;
+	return (find_struct_member(name, next, p->type->u.element->u.members));
+    }
+    else return (find_struct_member(name, next, p->type->u.members));
 }
 
 static struct variable *
@@ -178,12 +189,20 @@ find_simple(char * name)
     return(NULL);
 }
 
+/* Note that this function will end the program in case of an unknown */
+/* variable */
 static struct variable *
 find_variable(char * name)
 {
     char * next;
-    struct variable * p =
-    	((next = strpbrk(name, ".-")) != NULL) ? find_struct(name, next) : find_simple(name);
+    struct variable * p;
+
+    if ((next = strchr(name, '.')) != NULL)
+	p = find_struct(name, next);
+    else if ((next = strstr(name, "->")) != NULL)
+	p = find_struct(name, next);
+    else
+	p = find_simple(name);
 
     if (p == NULL)
     {
@@ -231,7 +250,6 @@ struct arguments {
     struct arguments * next;
 };
 
-
 static struct arguments * argsinsert = NULL;
 static struct arguments * argsresult = NULL;
 
@@ -495,8 +513,9 @@ output_statement(char * stmt, int mode)
 }
 
 /* special embedded SQL token */
-%token		SQL_BREAK SQL_CALL SQL_CONNECT SQL_CONTINUE SQL_DISCONNECT SQL_FOUND SQL_GO SQL_GOTO
-%token		SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN SQL_RELEASE
+%token		SQL_BREAK SQL_CALL SQL_CONNECT SQL_CONNECTION SQL_CONTINUE
+%token		SQL_DISCONNECT SQL_FOUND SQL_GO SQL_GOTO
+%token		SQL_IDENTIFIED SQL_IMMEDIATE SQL_INDICATOR SQL_OPEN SQL_RELEASE
 %token		SQL_SECTION SQL_SEMI SQL_SQLERROR SQL_SQLPRINT SQL_START
 %token		SQL_STOP SQL_WHENEVER
 
@@ -527,8 +546,9 @@ output_statement(char * stmt, int mode)
                 PARTIAL, POSITION, PRECISION, PRIMARY, PRIVILEGES, PROCEDURE, PUBLIC,
                 REFERENCES, REVOKE, RIGHT, ROLLBACK,
                 SECOND_P, SELECT, SET, SUBSTRING,
-                TABLE, TIME, TIMESTAMP, TO, TRAILING, TRANSACTION, TRIM,
-                UNION, UNIQUE, UPDATE, USING,
+                TABLE, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE,
+		TO, TRAILING, TRANSACTION, TRIM,
+                UNION, UNIQUE, UPDATE, USER, USING,
                 VALUES, VARCHAR, VARYING, VIEW,
                 WHERE, WITH, WORK, YEAR_P, ZONE
 
@@ -559,7 +579,7 @@ output_statement(char * stmt, int mode)
  *
  *                                    Todd A. Brandys
  */
-%token  USER, PASSWORD, CREATEDB, NOCREATEDB, CREATEUSER, NOCREATEUSER, VALID, UNTIL
+%token  PASSWORD, CREATEDB, NOCREATEDB, CREATEUSER, NOCREATEUSER, VALID, UNTIL
 
 /* Special keywords, not in the query language - see the "lex" file */
 %token <str>    IDENT SCONST Op CSTRING CVARIABLE
@@ -620,7 +640,7 @@ output_statement(char * stmt, int mode)
 %type  <str>	sortby OptUseOp opt_inh_star relation_name_list name_list
 %type  <str>	group_clause groupby_list groupby having_clause from_clause
 %type  <str>	from_list from_val join_expr join_outer join_spec join_list
-%type  <str> 	join_using where_clause relation_expr
+%type  <str> 	join_using where_clause relation_expr row_op sub_type
 %type  <str>	opt_column_list insert_rest InsertStmt
 %type  <str>    columnList DeleteStmt LockStmt UpdateStmt CursorStmt
 %type  <str>    NotifyStmt columnElem copy_dirn OptimizableStmt
@@ -650,12 +670,16 @@ output_statement(char * stmt, int mode)
 %type  <str>    DestroydbStmt ClusterStmt grantee RevokeStmt
 %type  <str>	GrantStmt privileges operation_commalist operation
 
-%type  <str>	ECPGWhenever ECPGConnect db_name ECPGOpen open_opts
+%type  <str>	ECPGWhenever ECPGConnect connection_target ECPGOpen open_opts
 %type  <str>	indicator ECPGExecute c_expr variable_list dotext
 %type  <str>    storage_clause opt_initializer vartext c_anything blockstart
 %type  <str>    blockend variable_list variable var_anything sql_anything
 %type  <str>	opt_pointer ecpg_ident cvariable ECPGDisconnect dis_name
-%type  <str>	stmt symbol opt_symbol ECPGRelease execstring
+%type  <str>	stmt symbol opt_symbol ECPGRelease execstring server_name
+%type  <str>	connection_object opt_server opt_port
+%type  <str>    user_name opt_user char_variable ora_user ident
+%type  <str>    db_prefix server opt_options opt_connection_name
+%type  <str>	ECPGSetConnection
 
 %type  <type_enum> simple_type type struct_type
 
@@ -721,12 +745,12 @@ stmt:  AddAttrStmt			{ output_statement($1, 0); }
 		| VariableShowStmt	{ output_statement($1, 0); }
 		| VariableResetStmt	{ output_statement($1, 0); }
 		| ECPGConnect		{
-						fprintf(yyout, "ECPGconnect(\"%s\");", $1); 
+						fprintf(yyout, "ECPGconnect(__LINE__, %s);", $1);
 						whenever_action(0);
 						free($1);
 					} 
 		| ECPGDisconnect	{
-						fprintf(yyout, "ECPGdisconnect(\"%s\");", $1); 
+						fprintf(yyout, "ECPGdisconnect(__LINE__, \"%s\");", $1); 
 						whenever_action(0);
 						free($1);
 					} 
@@ -737,6 +761,11 @@ stmt:  AddAttrStmt			{ output_statement($1, 0); }
 					}
 		| ECPGOpen		{ output_statement($1, 0); }
 		| ECPGRelease		{ /* output already done */ }
+		| ECPGSetConnection     {
+						fprintf(yyout, "ECPGsetcon(__LINE__, %s);", $1);
+						whenever_action(0);
+                                       		free($1);
+					}
 		| ECPGWhenever		{
 						fputs($1, yyout);
 						output_line_number();
@@ -828,7 +857,7 @@ user_group_clause:  IN GROUP user_group_list	{ $$ = cat2_str(make1_str("in group
 			| /*EMPTY*/		{ $$ = make1_str(""); }
 		;
 
-user_valid_clause:  VALID UNTIL SCONST			{ $$ = cat2_str(make1_str("valid until"), $3);; }
+user_valid_clause:  VALID UNTIL Sconst			{ $$ = cat2_str(make1_str("valid until"), $3);; }
 			| /*EMPTY*/			{ $$ = make1_str(""); }
 		;
 
@@ -853,6 +882,7 @@ VariableSetStmt:  SET ColId TO var_value
 				{
 					$$ = cat2_str(make1_str("set time zone"), $4);
 				}
+
 		;
 
 var_value:  Sconst			{ $$ = $1; }
@@ -1136,7 +1166,9 @@ default_expr:  AexprConst
 					$$ = "current_timestamp";
 				}
 			| CURRENT_USER
-				{	$$ = make1_str("current user"); }
+				{	$$ = make1_str("current_user"); }
+			| USER
+				{       $$ = make1_str("user"); }
 		;
 
 /* ConstraintElem specifies constraint syntax which is not embedded into
@@ -1459,7 +1491,7 @@ TriggerFuncArg:  Iconst
 					$$ = make_name();
 				}
 			| Sconst	{  $$ = $1; }
-			| ecpg_ident		{  $$ = $1; }
+			| ident		{  $$ = $1; }
 		;
 
 DropTrigStmt:  DROP TRIGGER name ON relation_name
@@ -2434,8 +2466,10 @@ groupby:  ColId
 
 having_clause:  HAVING a_expr
 				{
+#if FALSE
 					yyerror("HAVING clause not yet implemented");
-/*					$$ = cat2_str(make1_str("having"), $2); use this line instead to enable HAVING */
+#endif
+					$$ = cat2_str(make1_str("having"), $2);
 				}
 		| /*EMPTY*/		{ $$ = make1_str(""); }
 		;
@@ -2610,7 +2644,7 @@ Generic:  generic
 				}
 		;
 
-generic:  ecpg_ident					{ $$ = $1; }
+generic:  ident					{ $$ = $1; }
 		| TYPE_P			{ $$ = make1_str("type"); }
 		;
 
@@ -2724,7 +2758,7 @@ opt_decimal:  '(' Iconst ',' Iconst ')'
 Character:  character '(' Iconst ')'
 				{
 					if (strncasecmp($1, "char", strlen("char")) && strncasecmp($1, "varchar", strlen("varchar")))
-						yyerror("parse error");
+						yyerror("internal parsing error; unrecognized character type");
 					if (atol($3) < 1) {
 						sprintf(errortext, "length for '%s' type must be at least 1",$1);
 						yyerror(errortext);
@@ -2749,10 +2783,9 @@ Character:  character '(' Iconst ')'
 
 character:  CHARACTER opt_varying opt_charset opt_collate
 				{
-					if (strlen($4) > 0) {
-						sprintf(errortext, "COLLATE %s not yet implemented",$4);
-						yyerror(errortext);
-					}
+					if (strlen($4) > 0) 
+						fprintf(stderr, "COLLATE %s not yet implemented",$4);
+
 					$$ = cat4_str(make1_str("character"), $2, $3, $4);
 				}
 		| CHAR opt_varying	{ $$ = cat2_str(make1_str("char"), $2); }
@@ -2809,6 +2842,7 @@ opt_interval:  datetime					{ $$ = $1; }
 		| DAY_P TO MINUTE_P			{ $$ = make1_str("day to minute"); }
 		| DAY_P TO SECOND_P			{ $$ = make1_str("day to second"); }
 		| HOUR_P TO MINUTE_P			{ $$ = make1_str("hour to minute"); }
+		| MINUTE_P TO SECOND_P			{ $$ = make1_str("minute to second"); }
 		| HOUR_P TO SECOND_P			{ $$ = make1_str("hour to second"); }
 		| /*EMPTY*/					{ $$ = make1_str(""); }
 		;
@@ -2831,6 +2865,11 @@ a_expr_or_null:  a_expr
 /* Expressions using row descriptors
  * Define row_descriptor to allow yacc to break the reduce/reduce conflict
  *  with singleton expressions.
+ * Eliminated lots of code by defining row_op and sub_type clauses.
+ * However, can not consolidate EXPR_LINK case with others subselects
+ *  due to shift/reduce conflict with the non-subselect clause (the parser
+ *  would have to look ahead more than one token to resolve the conflict).
+ * - thomas 1998-05-09
  */
 row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
 				{
@@ -2840,134 +2879,18 @@ row_expr: '(' row_descriptor ')' IN '(' SubSelect ')'
 				{
 					$$ = make5_str(make1_str("("), $2, make1_str(") not in ("), $7, make1_str(")"));
 				}
-		| '(' row_descriptor ')' Op '(' SubSelect ')'
-				{
-					$$ = make3_str(make5_str(make1_str("("), $2, make1_str(")"), $4, make1_str("(")), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '+' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")+("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '-' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")-("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '/' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")/("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '*' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")*("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '<' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")<("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '>' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")>("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '=' '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")=("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' Op ANY '(' SubSelect ')'
-				{
-					$$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("any("), $7, make1_str(")")));
-				}
-		| '(' row_descriptor ')' '+' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")+any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '-' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")-any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '/' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")/any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '*' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")*any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '<' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")<any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '>' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")>any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '=' ANY '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")=any("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' Op ALL '(' SubSelect ')'
-				{
-					$$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("all("), $7, make1_str(")")));
-				}
-		| '(' row_descriptor ')' '+' ALL '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")+all("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '-' ALL '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")-all("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '/' ALL '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")/all("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '*' ALL '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")*all("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '<' ALL '(' SubSelect ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")<all("), $7, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '>' ALL '(' SubSelect ')'
+		| '(' row_descriptor ')' row_op sub_type  '(' SubSelect ')'
 				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")>all("), $7, make1_str(")"));
+					$$ = make4_str(make5_str(make1_str("("), $2, make1_str(")"), $4, $5), make1_str("("), $7, make1_str(")"));
 				}
-		| '(' row_descriptor ')' '=' ALL '(' SubSelect ')'
+		| '(' row_descriptor ')' row_op '(' SubSelect ')'
 				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")=all("), $7, make1_str(")"));
+					$$ = make3_str(make5_str(make1_str("("), $2, make1_str(")"), $4, make1_str("(")), $6, make1_str(")"));
 				}
-		| '(' row_descriptor ')' Op '(' row_descriptor ')'
+		| '(' row_descriptor ')' row_op '(' row_descriptor ')'
 				{
 					$$ = cat3_str(make3_str(make1_str("("), $2, make1_str(")")), $4, make3_str(make1_str("("), $6, make1_str(")")));
 				}
-		| '(' row_descriptor ')' '+' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")+("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '-' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")-("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '/' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")/("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '*' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")*("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '<' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")<("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '>' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")>("), $6, make1_str(")"));
-				}
-		| '(' row_descriptor ')' '=' '(' row_descriptor ')'
-				{
-					$$ = make5_str(make1_str("("), $2, make1_str(")=("), $6, make1_str(")"));
-				}
 		;
 
 row_descriptor:  row_list ',' a_expr
@@ -2976,6 +2899,21 @@ row_descriptor:  row_list ',' a_expr
 				}
 		;
 
+row_op:  Op			{ $$ = $1; }
+	| '<'                   { $$ = "<"; }
+        | '='                   { $$ = "="; }
+        | '>'                   { $$ = ">"; }
+        | '+'                   { $$ = "+"; }
+        | '-'                   { $$ = "-"; }
+        | '*'                   { $$ = "*"; }
+        | '/'                   { $$ = "/"; }
+              ;
+
+sub_type:  ANY                  { $$ = make1_str("ANY"); }
+         | ALL                  { $$ = make1_str("ALL"); }
+              ;
+
+
 row_list:  row_list ',' a_expr
 				{
 					$$ = cat3_str($1, make1_str(","), $3);
@@ -3089,6 +3027,11 @@ a_expr:  attr opt_indirection
 				{
 					$$ = make1_str("current_user");
 				}
+		| USER
+				{
+  		     		        $$ = make1_str("user");
+			     	}
+
 		| EXISTS '(' SubSelect ')'
 				{
 					$$ = make3_str(make1_str("exists("), $3, make1_str(")"));
@@ -3358,6 +3301,10 @@ b_expr:  attr opt_indirection
 				{
 					$$ = make1_str("current_user");
 				}
+		| USER
+				{
+					$$ = make1_str("user");
+				}
 		| POSITION '(' position_list ')'
 				{
 					$$ = make3_str(make1_str("position ("), $3, make1_str(")"));
@@ -3417,12 +3364,9 @@ extract_list:  extract_arg FROM a_expr
 			        { $$ = make1_str(";;"); }
 		;
 
-/* Add in TIMEZONE_HOUR and TIMEZONE_MINUTE for SQL92 compliance
- *  for next release. Just set up extract_arg for now...
- * - thomas 1998-04-08
- */
-extract_arg:  datetime
-				{	$$ = $1; }
+extract_arg:  datetime		{ $$ = $1; }
+	| TIMEZONE_HOUR 	{ $$ = make1_str("timezone_hour"); }	
+	| TIMEZONE_MINUTE 	{ $$ = make1_str("timezone_minute"); }	
 		;
 
 position_list:  position_expr IN position_expr
@@ -3660,9 +3604,9 @@ relation_name:	SpecialRuleRelation
 		;
 
 database_name:			ColId			{ $$ = $1; };
-access_method:			ecpg_ident			{ $$ = $1; };
+access_method:			ident			{ $$ = $1; };
 attr_name:				ColId			{ $$ = $1; };
-class:					ecpg_ident			{ $$ = $1; };
+class:					ident			{ $$ = $1; };
 index_name:				ColId			{ $$ = $1; };
 
 /* Functions
@@ -3673,7 +3617,7 @@ name:					ColId			{ $$ = $1; };
 func_name:				ColId			{ $$ = $1; };
 
 file_name:				Sconst			{ $$ = $1; };
-recipe_name:			ecpg_ident			{ $$ = $1; };
+recipe_name:			ident			{ $$ = $1; };
 
 /* Constants
  * Include TRUE/FALSE for SQL3 support. - thomas 1997-10-24
@@ -3723,8 +3667,9 @@ Sconst:  SCONST                                 {
 				     		        strcpy($$+1, $1);
 							$$[strlen($1)+2]='\0';
 							$$[strlen($1)+1]='\'';
+							free($1);
 						}
-UserId:  ecpg_ident                                  { $$ = $1;};
+UserId:  ident                                  { $$ = $1;};
 
 /* Column and type identifier
  * Does not include explicit datetime types
@@ -3746,7 +3691,7 @@ TypeId:  ColId
  *  list due to shift/reduce conflicts in yacc. If so, move
  *  down to the ColLabel entity. - thomas 1997-11-06
  */
-ColId:  ecpg_ident							{ $$ = $1; }
+ColId:  ident							{ $$ = $1; }
 		| datetime						{ $$ = $1; }
 		| ACTION						{ $$ = make1_str("action"); }
 		| CACHE							{ $$ = make1_str("cache"); }
@@ -3773,9 +3718,10 @@ ColId:  ecpg_ident							{ $$ = $1; }
 		| START							{ $$ = make1_str("start"); }
 		| STATEMENT						{ $$ = make1_str("statement"); }
 		| TIME							{ $$ = make1_str("time"); }
+		| TIMEZONE_HOUR                                 { $$ = make1_str("timezone_hour"); }
+                | TIMEZONE_MINUTE                               { $$ = make1_str("timezone_minute"); }
 		| TRIGGER						{ $$ = make1_str("trigger"); }
 		| TYPE_P						{ $$ = make1_str("type"); }
-		| USER							{ $$ = make1_str("user"); }
 		| VALID							{ $$ = make1_str("valid"); }
 		| VERSION						{ $$ = make1_str("version"); }
 		| ZONE							{ $$ = make1_str("zone"); }
@@ -4011,8 +3957,8 @@ variable: opt_pointer symbol opt_array_bounds opt_initializer
 			if (struct_level == 0)
 				new_variable($2, type);
 			else
-			        ECPGmake_struct_member($2, type, &(struct_member_list[struct_level-1]));
-			
+				ECPGmake_struct_member($2, type, &(struct_member_list[struct_level - 1]))->typ;
+
 			free($1);
 			free($2);
 			free($3.str);
@@ -4028,10 +3974,114 @@ opt_pointer: /* empty */	{ $$ = make1_str(""); }
 /*
  * the exec sql connect statement: connect to the given database 
  */
-ECPGConnect: SQL_CONNECT db_name { $$ = $2; }
+ECPGConnect: SQL_CONNECT TO connection_target opt_connection_name opt_user
+		{
+			$$ = make5_str($3, make1_str(","), $5, make1_str(","), $4);
+                }
+	| SQL_CONNECT TO DEFAULT
+        	{
+                	$$ = make1_str("NULL,NULL,NULL,\"DEFAULT\"");
+                }
+      /* also allow ORACLE syntax */
+        | SQL_CONNECT ora_user
+                {
+		       $$ = make3_str(make1_str("NULL,"), $2, make1_str(",NULL"));
+		}
+
+connection_target: database_name opt_server opt_port
+                {
+		  /* old style: dbname[@server][:port] */
+		  if (strlen($2) > 0 && *($2) != '@')
+		  {
+		    sprintf(errortext, "parse error at or near '%s'", $2);
+		    yyerror(errortext);
+		  }
 
-db_name: database_name { $$ = $1; }
-	| cvariable { /* check if we have a char variable */
+		  $$ = make5_str(make1_str("\""), $1, $2, $3, make1_str("\""));
+		}
+        |  db_prefix server opt_port '/' database_name opt_options
+                {
+		  /* new style: esql:postgresql://server[:port][/dbname] */
+                  if (strncmp($2, "://", 3) != 0)
+		  {
+		    sprintf(errortext, "parse error at or near '%s'", $2);
+		    yyerror(errortext);
+		  }
+	
+		  $$ = make4_str(make5_str(make1_str("\""), $1, $2, $3, make1_str("/")), $5, $6, make1_str("\""));
+		}
+	| char_variable
+                {
+		  $$ = $1;
+		}
+
+db_prefix: ident cvariable
+                {
+		  if (strcmp($2, "postgresql") != 0 && strcmp($2, "postgres") != 0)
+		  {
+		    sprintf(errortext, "parse error at or near '%s'", $2);
+		    yyerror(errortext);	
+		  }
+
+		  if (strcmp($1, "esql") != 0 && strcmp($1, "ecpg") != 0 && strcmp($1, "sql") != 0 && strcmp($1, "isql") != 0 && strcmp($1, "proc") != 0)
+		  {
+		    sprintf(errortext, "Illegal connection type %s", $1);
+		    yyerror(errortext);
+		  }
+
+		  $$ = make3_str($1, make1_str(":"), $2);
+		}
+        
+server: Op server_name
+                {
+		  if (strcmp($1, "@") != 0 && strcmp($1, "://") != 0)
+		  {
+		    sprintf(errortext, "parse error at or near '%s'", $1);
+		    yyerror(errortext);
+		  }
+
+		  $$ = make2_str($1, $2);
+	        }
+
+opt_server: server { $$ = $1; }
+        | /* empty */ { $$ = make1_str(""); }
+
+server_name: ColId   { $$ = $1; }
+        | ColId '.' server_name { $$ = make3_str($1, make1_str("."), $3); }
+
+opt_port: ':' Iconst { $$ = make2_str(make1_str(":"), $2); }
+        | /* empty */ { $$ = make1_str(""); }
+
+opt_connection_name: AS connection_target { $$ = $2; }
+        | /* empty */ { $$ = make1_str("NULL"); }
+
+opt_user: USER ora_user { $$ = $2; }
+          | /* empty */ { $$ = make1_str("NULL,NULL"); }
+
+ora_user: user_name
+		{
+                        $$ = make2_str($1, make1_str(",NULL"));
+	        }
+	| user_name '/' ColId
+		{
+        		$$ = make3_str($1, make1_str(","), $3);
+                }
+        | user_name SQL_IDENTIFIED BY user_name
+                {
+        		$$ = make3_str($1, make1_str(","), $4);
+                }
+        | user_name USING user_name
+                {
+        		$$ = make3_str($1, make1_str(","), $3);
+                }
+
+user_name: UserId       { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
+        | char_variable { $$ = $1; }
+        | CSTRING       { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
+        | SCONST        { $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
+
+char_variable: cvariable
+		{ /* check if we have a char variable */
 			struct variable *p = find_variable($1);
 			enum ECPGttype typ = p->type->typ;
 
@@ -4039,33 +4089,48 @@ db_name: database_name { $$ = $1; }
 			if (typ == ECPGt_array)
 				typ = p->type->u.element->typ;
 
-			if (typ != ECPGt_char && typ != ECPGt_unsigned_char)
-				yyerror("invalid datatype");
-			$$ = $1;
-	}
+                        switch (typ)
+                        {
+                            case ECPGt_char:
+                            case ECPGt_unsigned_char:
+                                $$ = $1;
+                                break;
+                            case ECPGt_varchar:
+                                $$ = make2_str($1, make1_str(".arr"));
+                                break;
+                            default:
+                                yyerror("invalid datatype");
+                                break;
+                        }
+		}
+
+opt_options: Op ColId
+		{
+			if (strlen($1) == 0)
+				yyerror("parse error");
+				
+			if (strcmp($1, "?") != 0)
+			{
+				sprintf(errortext, "parse error at or near %s", $1);
+				yyerror(errortext);
+			}
+			
+			$$ = make2_str(make1_str("?"), $2);
+		}
+	| /* empty */ { $$ = make1_str(""); }
 
 /*
  * the exec sql disconnect statement: disconnect from the given database 
  */
 ECPGDisconnect: SQL_DISCONNECT dis_name { $$ = $2; }
 
-dis_name: database_name	{ $$ = $1; }
-	| cvariable	{ /* check if we have a char variable */
-				struct variable *p = find_variable($1);
-				enum ECPGttype typ = p->type->typ;
+dis_name: connection_object	{ $$ = $1; }
+	| CURRENT	{ $$ = make1_str("CURRENT"); }
+	| ALL		{ $$ = make1_str("ALL"); }
+	| /* empty */	{ $$ = make1_str("CURRENT"); }
 
-				/* if array see what's inside */
-				if (typ == ECPGt_array)
-					typ = p->type->u.element->typ;
-
-				if (typ != ECPGt_char && typ != ECPGt_unsigned_char)
-					yyerror("invalid datatype");
-				$$ = $1;
-			}
-	| CURRENT	{ $$ = make1_str(""); }
-	| DEFAULT	{ $$ = make1_str(""); }
-	| ALL		{ $$ = make1_str(""); }
-	| /* empty */	{ $$ = make1_str(""); }
+connection_object: connection_target { $$ = $1; }
+	| DEFAULT	{ $$ = make1_str("DEFAULT"); }
 
 /*
  * execute a given string as sql command
@@ -4118,6 +4183,14 @@ ECPGRelease: TransactionStmt SQL_RELEASE
 		free($1);
 	}
 
+/* 
+ * set the actual connection, this needs a differnet handling as the other
+ * set commands
+ */
+ECPGSetConnection:  SET SQL_CONNECTION connection_object
+           		{
+				$$ = $3;
+                        }
 /*
  * whenever statement: decide what to do in case of error/no data found
  * according to SQL standards we miss: SQLSTATE, CONSTRAINT, SQLEXCEPTION
@@ -4163,7 +4236,7 @@ action : SQL_CONTINUE {
        | DO name '(' dotext ')' {
 	$<action>$.code = W_DO;
 	$<action>$.command = make4_str($2, make1_str("("), $4, make1_str(")"));
-	$<action>$.str = cat2_str(make1_str("do"), $<action>$.command);
+	$<action>$.str = cat2_str(make1_str("do"), strdup($<action>$.command));
 }
        | DO SQL_BREAK {
         $<action>$.code = W_BREAK;
@@ -4173,7 +4246,7 @@ action : SQL_CONTINUE {
        | SQL_CALL name '(' dotext ')' {
 	$<action>$.code = W_DO;
 	$<action>$.command = make4_str($2, make1_str("("), $4, make1_str(")"));
-	$<action>$.str = cat2_str(make1_str("call"), $<action>$.command);
+	$<action>$.str = cat2_str(make1_str("call"), strdup($<action>$.command));
 }
 
 /* some other stuff for ecpg */
@@ -4478,14 +4551,16 @@ civariableonly : cvariable name {
 		add_variable(&argsinsert, find_variable($1), &no_indicator); 
 }
 
-cvariable: CVARIABLE			{ $$ = $1; }
+cvariable: CVARIABLE			{ $$ = make1_str($1); }
 
 indicator: /* empty */			{ $$ = NULL; }
 	| cvariable		 	{ check_indicator((find_variable($1))->type); $$ = $1; }
 	| SQL_INDICATOR cvariable 	{ check_indicator((find_variable($2))->type); $$ = $2; }
 	| SQL_INDICATOR name		{ check_indicator((find_variable($2))->type); $$ = $2; }
 
-ecpg_ident: IDENT	{ $$ = $1; }
+ident: IDENT	{ $$ = make1_str($1); }
+
+ecpg_ident: ident	{ $$ = $1; }
 	| CSTRING	{ $$ = make3_str(make1_str("\""), $1, make1_str("\"")); }
 /*
  * C stuff
diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c
index cbc7446909e..ce0e1b0cd20 100644
--- a/src/interfaces/ecpg/preproc/type.c
+++ b/src/interfaces/ecpg/preproc/type.c
@@ -20,21 +20,6 @@ mm_alloc(size_t size)
 	return (ptr);
 }
 
-/* realloc + error check */
-void *
-mm_realloc(void *ptr, size_t size)
-{
-	ptr = realloc(ptr, size);
-
-	if (ptr == NULL)
-	{
-		fprintf(stderr, "Out of memory\n");
-		exit(OUT_OF_MEMORY);
-	}
-
-	return (ptr);
-}
-
 /* duplicate memberlist */
 static struct ECPGstruct_member *
 struct_member_dup(struct ECPGstruct_member * rm)
@@ -43,7 +28,22 @@ struct_member_dup(struct ECPGstruct_member * rm)
 
   while (rm)
     {
-      ECPGmake_struct_member(rm->name, rm->typ, &new);
+      struct ECPGtype *type;
+      
+      switch(rm->typ->typ)
+      {
+      	case ECPGt_struct:
+      		type = ECPGmake_struct_type(rm->typ->u.members);
+      		break;
+      	case ECPGt_array:
+      		type = ECPGmake_array_type(ECPGmake_simple_type(rm->typ->u.element->typ, rm->typ->u.element->size), rm->typ->size);
+		break;      	
+      	default:
+      		type = ECPGmake_simple_type(rm->typ->typ, rm->typ->size);
+      		break;
+      }
+      
+      ECPGmake_struct_member(rm->name, type, &new);
    
       rm = rm->next;
     }
@@ -165,11 +165,11 @@ static const char *get_type(enum ECPGttype typ)
    size is the maxsize in case it is a varchar. Otherwise it is the size of
    the variable (required to do array fetches of structs).
  */
-void
+static void
 ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
 				  long varcharsize,
 				  long arrsiz, const char *siz, const char *prefix);
-void
+static void
 ECPGdump_a_struct(FILE *o, const char *name, const char *ind_name, long arrsiz,
 		  struct ECPGtype * typ, struct ECPGtype * ind_typ, const char *offset, const char *prefix, const char * ind_prefix);
 
@@ -223,7 +223,7 @@ ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype * typ, const char *in
 
 /* If siz is NULL, then the offset is 0, if not use siz as a
    string, it represents the offset needed if we are in an array of structs. */
-void
+static void
 ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
 				  long varcharsize,
 				  long arrsize,
@@ -272,7 +272,7 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
 
 
 /* Penetrate a struct and dump the contents. */
-void
+static void
 ECPGdump_a_struct(FILE *o, const char *name, const char * ind_name, long arrsiz, struct ECPGtype * typ, struct ECPGtype * ind_typ, const char *offsetarg, const char *prefix, const char *ind_prefix)
 {
 
@@ -319,6 +319,7 @@ ECPGfree_struct_member(struct ECPGstruct_member * rm)
       
       rm = rm->next;
       free(p->name);
+      free(p->typ);
       free(p);
     }
 }
@@ -337,7 +338,7 @@ ECPGfree_type(struct ECPGtype * typ)
 			else if (typ->u.element->typ == ECPGt_struct)
 			{
 				/* Array of structs. */
-			        ECPGfree_struct_member(typ->u.members);
+			        ECPGfree_struct_member(typ->u.element->u.members);
 				free(typ->u.members);
 			}
 			else
diff --git a/src/interfaces/ecpg/test/perftest.pgc b/src/interfaces/ecpg/test/perftest.pgc
index f56d814dd98..6ec604d47f7 100644
--- a/src/interfaces/ecpg/test/perftest.pgc
+++ b/src/interfaces/ecpg/test/perftest.pgc
@@ -28,7 +28,7 @@ exec sql begin declare section;
 exec sql end declare section;
 	struct timeval tvs, tve;
 
-	exec sql connect mm;
+	exec sql connect to mm;
 
 	exec sql create table perftest1(number int4, ascii char(16));
 
diff --git a/src/interfaces/ecpg/test/test1.pgc b/src/interfaces/ecpg/test/test1.pgc
index 3363af07b1f..c485a487fce 100644
--- a/src/interfaces/ecpg/test/test1.pgc
+++ b/src/interfaces/ecpg/test/test1.pgc
@@ -19,7 +19,7 @@ exec sql end declare section;
                 ECPGdebug(1, dbgs);
 
 	strcpy(msg, "connect");
-	exec sql connect mm;
+	exec sql connect to mm;
 
 	strcpy(msg, "create");
 	exec sql create table test(name char(8), amount int);
diff --git a/src/interfaces/ecpg/test/test2.pgc b/src/interfaces/ecpg/test/test2.pgc
index ed0599cdeaf..3b7ff3f9d55 100644
--- a/src/interfaces/ecpg/test/test2.pgc
+++ b/src/interfaces/ecpg/test/test2.pgc
@@ -11,9 +11,9 @@ exec sql begin declare section;
 								short age;
 					} birth;
 				} personal;
-	struct personal_indicator {	short name;
-					struct birth_indicator {	short born;
-									int age;
+	struct personal_indicator {	short ind_name;
+					struct birth_indicator {	short ind_born;
+									int ind_age;
 					} ind_birth;
 				  } ind_personal;
 	long ind_married;
@@ -26,7 +26,7 @@ exec sql end declare section;
                 ECPGdebug(1, dbgs);
 
 	strcpy(msg, "connect");
-	exec sql connect mm;
+	exec sql connect to mm;
 
 	strcpy(msg, "create");
 	exec sql create table meskes(name char(8), born integer, age smallint, married char(8));
-- 
GitLab