]> granicus.if.org Git - postgresql/commitdiff
Started working on a seperate pgtypes library. First test work. PLEASE test compilati...
authorMichael Meskes <meskes@postgresql.org>
Sun, 16 Mar 2003 10:42:54 +0000 (10:42 +0000)
committerMichael Meskes <meskes@postgresql.org>
Sun, 16 Mar 2003 10:42:54 +0000 (10:42 +0000)
23 files changed:
src/interfaces/ecpg/ChangeLog
src/interfaces/ecpg/Makefile
src/interfaces/ecpg/ecpglib/Makefile [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/connect.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/data.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/descriptor.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/error.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/execute.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/extern.h [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/memory.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/misc.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/pg_type.h [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/prepare.c [new file with mode: 0644]
src/interfaces/ecpg/ecpglib/typename.c [new file with mode: 0644]
src/interfaces/ecpg/include/Makefile
src/interfaces/ecpg/include/ecpgtype.h
src/interfaces/ecpg/pgtypeslib/Makefile [new file with mode: 0644]
src/interfaces/ecpg/pgtypeslib/numeric.c [new file with mode: 0644]
src/interfaces/ecpg/preproc/ecpg.c
src/interfaces/ecpg/preproc/preproc.y
src/interfaces/ecpg/preproc/type.c
src/interfaces/ecpg/test/Makefile
src/interfaces/ecpg/test/num_test.pgc [new file with mode: 0644]

index d54f3c200c89b613610f2465a0789b2d7256526a..e4c3548dfc44e69ff17e4ea6cc62ff3bdce95155 100644 (file)
@@ -1353,6 +1353,14 @@ Tue Feb 25 16:46:27 CET 2003
        - Allow SET CONNECTION to be followed by connection object without
          leading "TO" or "=".
        - Allow whenever statement to list function without parameters.
+
+
+Sun Mar 16 11:28:01 CET 2003
+       
+       - Started with a pgtypes library.
+       - Renamed lib directory to ecpglib.
+       - Added numerical functions to library and preprocessor.
        - Set ecpg version to 2.12.0.
-       - Set library to 3.4.2.
+       - Set ecpg library to 3.4.2.
+       - Set pgtypes library to 1.0.0
 
index dcdabf17eb2191842fdf839b22564ffd2c146d0b..50b97bc2d59b5bfcfab73b3e4f63128fbc220940 100644 (file)
@@ -4,11 +4,13 @@ include $(top_builddir)/src/Makefile.global
 
 all install installdirs uninstall dep depend distprep:
        $(MAKE) -C include $@
-       $(MAKE) -C lib $@
+       $(MAKE) -C ecpglib $@
+       $(MAKE) -C pgtypeslib $@
        $(MAKE) -C preproc $@
 
 clean distclean maintainer-clean:
        -$(MAKE) -C include $@
-       -$(MAKE) -C lib $@
+       -$(MAKE) -C ecpglib $@
+       -$(MAKE) -C pgtypeslib $@
        -$(MAKE) -C preproc $@
        -$(MAKE) -C test clean
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
new file mode 100644 (file)
index 0000000..9d22fd3
--- /dev/null
@@ -0,0 +1,46 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for ecpg library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/Makefile,v 1.1 2003/03/16 10:42:53 meskes Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/interfaces/ecpg/ecpglib
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+NAME= ecpg
+SO_MAJOR_VERSION= 3
+SO_MINOR_VERSION= 4.2
+
+override CPPFLAGS := -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS)
+
+OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \
+       connect.o misc.o
+
+SHLIB_LINK= $(libpq)
+
+all: all-lib
+
+# Shared library stuff
+include $(top_srcdir)/src/Makefile.shlib
+
+install: all installdirs install-lib
+
+installdirs:
+       $(mkinstalldirs) $(DESTDIR)$(libdir)
+
+uninstall: uninstall-lib
+
+clean distclean maintainer-clean: clean-lib
+       rm -f $(OBJS)
+
+depend dep:
+       $(CC) -MM $(CFLAGS) *.c >depend
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
new file mode 100644 (file)
index 0000000..5c3c096
--- /dev/null
@@ -0,0 +1,495 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+#include "sqlca.h"
+
+static struct connection *all_connections = NULL,
+                  *actual_connection = NULL;
+
+struct connection *
+ECPGget_connection(const char *connection_name)
+{
+       struct connection *con = all_connections;
+
+       if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0)
+               return actual_connection;
+
+       for (; con && strcmp(connection_name, con->name) != 0; con = con->next);
+       if (con)
+               return con;
+       else
+               return NULL;
+}
+
+static void
+ecpg_finish(struct connection * act)
+{
+       if (act != NULL)
+       {
+               struct ECPGtype_information_cache *cache,
+                                  *ptr;
+
+               ECPGlog("ecpg_finish: finishing %s.\n", act->name);
+               PQfinish(act->connection);
+
+               /* remove act from the list */
+               if (act == all_connections)
+                       all_connections = act->next;
+               else
+               {
+                       struct connection *con;
+
+                       for (con = all_connections; con->next && con->next != act; con = con->next);
+                       if (con->next)
+                               con->next = act->next;
+               }
+
+               if (actual_connection == act)
+                       actual_connection = all_connections;
+
+               for (cache = act->cache_head; cache; ptr = cache, cache = cache->next, ECPGfree(ptr));
+               ECPGfree(act->name);
+               ECPGfree(act);
+       }
+       else
+               ECPGlog("ecpg_finish: called an extra time.\n");
+}
+
+bool
+ECPGsetcommit(int lineno, const char *mode, const char *connection_name)
+{
+       struct connection *con = ECPGget_connection(connection_name);
+       PGresult   *results;
+
+       if (!ECPGinit(con, connection_name, lineno))
+               return (false);
+
+       ECPGlog("ECPGsetcommit line %d action = %s connection = %s\n", lineno, mode, con->name);
+
+       if (con->autocommit == true && strncmp(mode, "off", strlen("off")) == 0)
+       {
+               if (con->committed)
+               {
+                       if ((results = PQexec(con->connection, "begin transaction")) == NULL)
+                       {
+                               ECPGraise(lineno, ECPG_TRANS, NULL);
+                               return false;
+                       }
+                       PQclear(results);
+                       con->committed = false;
+               }
+               con->autocommit = false;
+       }
+       else if (con->autocommit == false && strncmp(mode, "on", strlen("on")) == 0)
+       {
+               if (!con->committed)
+               {
+                       if ((results = PQexec(con->connection, "commit")) == NULL)
+                       {
+                               ECPGraise(lineno, ECPG_TRANS, NULL);
+                               return false;
+                       }
+                       PQclear(results);
+                       con->committed = true;
+               }
+               con->autocommit = true;
+       }
+
+       return true;
+}
+
+bool
+ECPGsetconn(int lineno, const char *connection_name)
+{
+       struct connection *con = ECPGget_connection(connection_name);
+
+       if (!ECPGinit(con, connection_name, lineno))
+               return (false);
+
+       actual_connection = con;
+       return true;
+}
+
+static void
+ECPGnoticeProcessor_raise(int code, const char *message)
+{
+       sqlca.sqlcode = code;
+       strncpy(sqlca.sqlerrm.sqlerrmc, message, sizeof(sqlca.sqlerrm.sqlerrmc));
+       sqlca.sqlerrm.sqlerrmc[sizeof(sqlca.sqlerrm.sqlerrmc) - 1] = 0;
+       sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc);
+
+       /* remove trailing newline */
+       if (sqlca.sqlerrm.sqlerrml
+               && sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] == '\n')
+       {
+               sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] = 0;
+               sqlca.sqlerrm.sqlerrml--;
+       }
+
+       ECPGlog("raising sqlcode %d\n", code);
+}
+
+/*
+ * I know this is a mess, but we can't redesign the backend
+ */
+
+static void
+ECPGnoticeProcessor(void *arg, const char *message)
+{
+       /* these notices raise an error */
+       if (strncmp(message, "WARNING: ", 9))
+       {
+               ECPGlog("ECPGnoticeProcessor: strange warning '%s'\n", message);
+               ECPGnoticeProcessor_raise(ECPG_WARNING_UNRECOGNIZED, message);
+               return;
+       }
+
+       message += 8;
+       while (*message == ' ')
+               message++;
+       ECPGlog("WARNING: %s", message);
+
+       /* WARNING: (transaction aborted): queries ignored until END */
+
+       /*
+        * WARNING: current transaction is aborted, queries ignored until end
+        * of transaction block
+        */
+       if (strstr(message, "queries ignored") && strstr(message, "transaction")
+               && strstr(message, "aborted"))
+       {
+               ECPGnoticeProcessor_raise(ECPG_WARNING_QUERY_IGNORED, message);
+               return;
+       }
+
+       /* WARNING: PerformPortalClose: portal "*" not found */
+       if ((!strncmp(message, "PerformPortalClose: portal", 26)
+                || !strncmp(message, "PerformPortalFetch: portal", 26))
+               && strstr(message + 26, "not found"))
+       {
+               ECPGnoticeProcessor_raise(ECPG_WARNING_UNKNOWN_PORTAL, message);
+               return;
+       }
+
+       /* WARNING: BEGIN: already a transaction in progress */
+       if (!strncmp(message, "BEGIN: already a transaction in progress", 40))
+       {
+               ECPGnoticeProcessor_raise(ECPG_WARNING_IN_TRANSACTION, message);
+               return;
+       }
+
+       /* WARNING: AbortTransaction and not in in-progress state */
+       /* WARNING: COMMIT: no transaction in progress */
+       /* WARNING: ROLLBACK: no transaction in progress */
+       if (!strncmp(message, "AbortTransaction and not in in-progress state", 45)
+               || !strncmp(message, "COMMIT: no transaction in progress", 34)
+               || !strncmp(message, "ROLLBACK: no transaction in progress", 36))
+       {
+               ECPGnoticeProcessor_raise(ECPG_WARNING_NO_TRANSACTION, message);
+               return;
+       }
+
+       /* WARNING: BlankPortalAssignName: portal * already exists */
+       if (!strncmp(message, "BlankPortalAssignName: portal", 29)
+               && strstr(message + 29, "already exists"))
+       {
+               ECPGnoticeProcessor_raise(ECPG_WARNING_PORTAL_EXISTS, message);
+               return;
+       }
+
+       /* these are harmless - do nothing */
+
+       /*
+        * WARNING: CREATE TABLE / PRIMARY KEY will create implicit index '*'
+        * for table '*'
+        */
+
+       /*
+        * WARNING: ALTER TABLE ... ADD CONSTRAINT will create implicit
+        * trigger(s) for FOREIGN KEY check(s)
+        */
+
+       /*
+        * WARNING: CREATE TABLE will create implicit sequence '*' for SERIAL
+        * column '*.*'
+        */
+
+       /*
+        * WARNING: CREATE TABLE will create implicit trigger(s) for FOREIGN
+        * KEY check(s)
+        */
+       if ((!strncmp(message, "CREATE TABLE", 12) || !strncmp(message, "ALTER TABLE", 11))
+               && strstr(message + 11, "will create implicit"))
+               return;
+
+       /* WARNING: QUERY PLAN: */
+       if (!strncmp(message, "QUERY PLAN:", 11))       /* do we really see these? */
+               return;
+
+       /*
+        * WARNING: DROP TABLE implicitly drops referential integrity trigger
+        * from table "*"
+        */
+       if (!strncmp(message, "DROP TABLE implicitly drops", 27))
+               return;
+
+       /*
+        * WARNING: Caution: DROP INDEX cannot be rolled back, so don't abort
+        * now
+        */
+       if (strstr(message, "cannot be rolled back"))
+               return;
+
+       /* these and other unmentioned should set sqlca.sqlwarn[2] */
+       /* WARNING: The ':' operator is deprecated.  Use exp(x) instead. */
+       /* WARNING: Rel *: Uninitialized page 0 - fixing */
+       /* WARNING: PortalHeapMemoryFree: * not in alloc set! */
+       /* WARNING: Too old parent tuple found - can't continue vc_repair_frag */
+       /* WARNING: identifier "*" will be truncated to "*" */
+       /* WARNING: InvalidateSharedInvalid: cache state reset */
+       /* WARNING: RegisterSharedInvalid: SI buffer overflow */
+       sqlca.sqlwarn[2] = 'W';
+       sqlca.sqlwarn[0] = 'W';
+}
+
+/* this contains some quick hacks, needs to be cleaned up, but it works */
+bool
+ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit)
+{
+       struct connection *this;
+       char       *dbname = strdup(name),
+                          *host = NULL,
+                          *tmp,
+                          *port = NULL,
+                          *realname = NULL,
+                          *options = NULL;
+
+       ECPGinit_sqlca();
+
+       if ((this = (struct connection *) ECPGalloc(sizeof(struct connection), lineno)) == NULL)
+               return false;
+
+       if (dbname == NULL && connection_name == NULL)
+               connection_name = "DEFAULT";
+
+       /* get the detail information out of dbname */
+       if (strchr(dbname, '@') != NULL)
+       {
+               /* old style: dbname[@server][:port] */
+               tmp = strrchr(dbname, ':');
+               if (tmp != NULL)                /* port number given */
+               {
+                       port = strdup(tmp + 1);
+                       *tmp = '\0';
+               }
+
+               tmp = strrchr(dbname, '@');
+               if (tmp != NULL)                /* host name given */
+               {
+                       host = strdup(tmp + 1);
+                       *tmp = '\0';
+               }
+               realname = strdup(dbname);
+       }
+       else if (strncmp(dbname, "tcp:", 4) == 0 || strncmp(dbname, "unix:", 5) == 0)
+       {
+               int                     offset = 0;
+
+               /*
+                * only allow protocols tcp and unix
+                */
+               if (strncmp(dbname, "tcp:", 4) == 0)
+                       offset = 4;
+               else if (strncmp(dbname, "unix:", 5) == 0)
+                       offset = 5;
+
+               if (strncmp(dbname + offset, "postgresql://", strlen("postgresql://")) == 0)
+               {
+
+                       /*------
+                        * new style:
+                        *      <tcp|unix>:postgresql://server[:port|:/unixsocket/path:]
+                        *      [/db name][?options]
+                        *------
+                        */
+                       offset += strlen("postgresql://");
+
+                       tmp = strrchr(dbname + offset, '?');
+                       if (tmp != NULL)        /* options given */
+                       {
+                               options = strdup(tmp + 1);
+                               *tmp = '\0';
+                       }
+
+                       tmp = strrchr(dbname + offset, '/');
+                       if (tmp != NULL)        /* database name given */
+                       {
+                               realname = strdup(tmp + 1);
+                               *tmp = '\0';
+                       }
+
+                       tmp = strrchr(dbname + offset, ':');
+                       if (tmp != NULL)        /* port number or Unix socket path given */
+                       {
+                               char       *tmp2;
+
+                               *tmp = '\0';
+                               if ((tmp2 = strchr(tmp + 1, ':')) != NULL)
+                               {
+                                       *tmp2 = '\0';
+                                       host = strdup(tmp + 1);
+                                       if (strncmp(dbname, "unix:", 5) != 0)
+                                       {
+                                               ECPGlog("connect: socketname %s given for TCP connection in line %d\n", host, lineno);
+                                               ECPGraise(lineno, ECPG_CONNECT, realname ? realname : "<DEFAULT>");
+                                               if (host)
+                                                       ECPGfree(host);
+                                               if (port)
+                                                       ECPGfree(port);
+                                               if (options)
+                                                       ECPGfree(options);
+                                               if (realname)
+                                                       ECPGfree(realname);
+                                               if (dbname)
+                                                       ECPGfree(dbname);
+                                               return false;
+                                       }
+                               }
+                               else
+                                       port = strdup(tmp + 1);
+                       }
+
+                       if (strncmp(dbname, "unix:", 5) == 0)
+                       {
+                               if (strcmp(dbname + offset, "localhost") != 0 && strcmp(dbname + offset, "127.0.0.1") != 0)
+                               {
+                                       ECPGlog("connect: non-localhost access via sockets in line %d\n", lineno);
+                                       ECPGraise(lineno, ECPG_CONNECT, realname ? realname : "<DEFAULT>");
+                                       if (host)
+                                               ECPGfree(host);
+                                       if (port)
+                                               ECPGfree(port);
+                                       if (options)
+                                               ECPGfree(options);
+                                       if (realname)
+                                               ECPGfree(realname);
+                                       if (dbname)
+                                               ECPGfree(dbname);
+                                       return false;
+                               }
+                       }
+                       else
+                               host = strdup(dbname + offset);
+
+               }
+               else
+                       realname = strdup(dbname);
+       }
+       else
+               realname = strdup(dbname);
+
+       /* add connection to our list */
+       if (connection_name != NULL)
+               this->name = ECPGstrdup(connection_name, lineno);
+       else
+               this->name = ECPGstrdup(realname, lineno);
+
+       this->cache_head = NULL;
+
+       if (all_connections == NULL)
+               this->next = NULL;
+       else
+               this->next = all_connections;
+
+       actual_connection = all_connections = this;
+
+       ECPGlog("ECPGconnect: opening database %s on %s port %s %s%s%s%s\n",
+                       realname ? realname : "<DEFAULT>",
+                       host ? host : "<DEFAULT>",
+                       port ? port : "<DEFAULT>",
+                       options ? "with options " : "", options ? options : "",
+                       user ? "for user " : "", user ? user : "");
+
+       this->connection = PQsetdbLogin(host, port, options, NULL, realname, user, passwd);
+
+       if (PQstatus(this->connection) == CONNECTION_BAD)
+       {
+        const char *errmsg = PQerrorMessage(this->connection);
+        char *db = realname ? realname : "<DEFAULT>";
+
+        set_backend_err(errmsg, lineno);
+               ecpg_finish(this);
+               ECPGlog("connect: could not open database %s on %s port %s %s%s%s%s in line %d\n\t%s\n",
+                db,
+                               host ? host : "<DEFAULT>",
+                               port ? port : "<DEFAULT>",
+                               options ? "with options " : "", options ? options : "",
+                               user ? "for user " : "", user ? user : "",
+                               lineno, errmsg);
+        
+               ECPGraise(lineno, ECPG_CONNECT, db);
+               if (host)
+                       ECPGfree(host);
+               if (port)
+                       ECPGfree(port);
+               if (options)
+                       ECPGfree(options);
+               if (realname)
+                       ECPGfree(realname);
+               if (dbname)
+                       ECPGfree(dbname);
+               return false;
+       }
+
+       if (host)
+               ECPGfree(host);
+       if (port)
+               ECPGfree(port);
+       if (options)
+               ECPGfree(options);
+       if (realname)
+               ECPGfree(realname);
+       if (dbname)
+               ECPGfree(dbname);
+
+       this->committed = true;
+       this->autocommit = autocommit;
+
+       PQsetNoticeProcessor(this->connection, &ECPGnoticeProcessor, (void *) this);
+
+       return true;
+}
+
+bool
+ECPGdisconnect(int lineno, const char *connection_name)
+{
+       struct connection *con;
+
+       if (strcmp(connection_name, "ALL") == 0)
+       {
+               ECPGinit_sqlca();
+               for (con = all_connections; con;)
+               {
+                       struct connection *f = con;
+
+                       con = con->next;
+                       ecpg_finish(f);
+               }
+       }
+       else
+       {
+               con = ECPGget_connection(connection_name);
+
+               if (!ECPGinit(con, connection_name, lineno))
+                       return (false);
+               else
+                       ecpg_finish(con);
+       }
+
+       return true;
+}
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
new file mode 100644 (file)
index 0000000..85d5e30
--- /dev/null
@@ -0,0 +1,424 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+#include "sqlca.h"
+#include "pgtypes_numeric.h"
+
+bool
+ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno,
+                        enum ECPGttype type, enum ECPGttype ind_type,
+                        char *var, char *ind, long varcharsize, long offset,
+                        long ind_offset, bool isarray)
+{
+       char       *pval = (char *) PQgetvalue(results, act_tuple, act_field);
+
+       ECPGlog("ECPGget_data line %d: RESULT: %s offset: %ld\n", lineno, pval ? pval : "", offset);
+
+       /* pval is a pointer to the value */
+       /* let's check is it really is an array if it should be one */
+       if (isarray)
+       {
+               if (*pval != '{')
+               {
+                       ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, NULL);
+                       return (false);
+               }
+
+               switch (type)
+               {
+                       case ECPGt_char:
+                       case ECPGt_unsigned_char:
+                       case ECPGt_varchar:
+                               break;
+
+                       default:
+                               pval++;
+                               break;
+               }
+       }
+
+       /* We will have to decode the value */
+
+       /*
+        * check for null value and set indicator accordingly
+        */
+       switch (ind_type)
+       {
+               case ECPGt_short:
+               case ECPGt_unsigned_short:
+/*                     ((short *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/
+                       *((short *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field);
+                       break;
+               case ECPGt_int:
+               case ECPGt_unsigned_int:
+/*                     ((int *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/
+                       *((int *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field);
+                       break;
+               case ECPGt_long:
+               case ECPGt_unsigned_long:
+/*                     ((long *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/
+                       *((long *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field);
+                       break;
+#ifdef HAVE_LONG_LONG_INT_64
+               case ECPGt_long_long:
+               case ECPGt_unsigned_long_long:
+/*                     ((long long int *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/
+                       *((long long int *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field);
+                       break;
+/*             case ECPGt_unsigned_long_long:
+                       ((unsigned long long int *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);
+                       break;*/
+#endif   /* HAVE_LONG_LONG_INT_64 */
+               case ECPGt_NO_INDICATOR:
+                       if (PQgetisnull(results, act_tuple, act_field))
+                       {
+                               ECPGraise(lineno, ECPG_MISSING_INDICATOR, NULL);
+                               return (false);
+                       }
+                       break;
+               default:
+                       ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(ind_type));
+                       return (false);
+                       break;
+       }
+
+       do
+       {
+               switch (type)
+               {
+                               long            res;
+                               unsigned long ures;
+                               double          dres;
+                               char       *scan_length;
+                               NumericVar *nres;
+
+                       case ECPGt_short:
+                       case ECPGt_int:
+                       case ECPGt_long:
+                               if (pval)
+                               {
+                                       res = strtol(pval, &scan_length, 10);
+                                       if ((isarray && *scan_length != ',' && *scan_length != '}')
+                                               || (!isarray && *scan_length != '\0'))  /* Garbage left */
+                                       {
+                                               ECPGraise(lineno, ECPG_INT_FORMAT, pval);
+                                               return (false);
+                                       }
+                               }
+                               else
+                                       res = 0L;
+
+                               switch (type)
+                               {
+                                       case ECPGt_short:
+/*                                             ((short *) var)[act_tuple] = (short) res;*/
+                                               *((short *) (var + offset * act_tuple)) = (short) res;
+                                               break;
+                                       case ECPGt_int:
+/*                                             ((int *) var)[act_tuple] = (int) res;*/
+                                               *((int *) (var + offset * act_tuple)) = (int) res;
+                                               break;
+                                       case ECPGt_long:
+/*                                             ((long *) var)[act_tuple] = res;*/
+                                               *((long *) (var + offset * act_tuple)) = (long) res;
+                                               break;
+                                       default:
+                                               /* Cannot happen */
+                                               break;
+                               }
+                               break;
+
+                       case ECPGt_unsigned_short:
+                       case ECPGt_unsigned_int:
+                       case ECPGt_unsigned_long:
+                               if (pval)
+                               {
+                                       ures = strtoul(pval, &scan_length, 10);
+                                       if ((isarray && *scan_length != ',' && *scan_length != '}')
+                                               || (!isarray && *scan_length != '\0'))  /* Garbage left */
+                                       {
+                                               ECPGraise(lineno, ECPG_UINT_FORMAT, pval);
+                                               return (false);
+                                       }
+                               }
+                               else
+                                       ures = 0L;
+
+                               switch (type)
+                               {
+                                       case ECPGt_unsigned_short:
+/*                                             ((unsigned short *) var)[act_tuple] = (unsigned short) ures;*/
+                                               *((unsigned short *) (var + offset * act_tuple)) = (unsigned short) ures;
+                                               break;
+                                       case ECPGt_unsigned_int:
+/*                                             ((unsigned int *) var)[act_tuple] = (unsigned int) ures;*/
+                                               *((unsigned int *) (var + offset * act_tuple)) = (unsigned int) ures;
+                                               break;
+                                       case ECPGt_unsigned_long:
+/*                                             ((unsigned long *) var)[act_tuple] = ures;*/
+                                               *((unsigned long *) (var + offset * act_tuple)) = (unsigned long) ures;
+                                               break;
+                                       default:
+                                               /* Cannot happen */
+                                               break;
+                               }
+                               break;
+
+#ifdef HAVE_LONG_LONG_INT_64
+#ifdef HAVE_STRTOLL
+                       case ECPGt_long_long:
+                               if (pval)
+                               {
+/*                                     ((long long int *) var)[act_tuple] = strtoll(pval, &scan_length, 10);*/
+                                       *((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
+                                       if ((isarray && *scan_length != ',' && *scan_length != '}')
+                                               || (!isarray && *scan_length != '\0'))  /* Garbage left */
+                                       {
+                                               ECPGraise(lineno, ECPG_INT_FORMAT, pval);
+                                               return (false);
+                                       }
+                               }
+                               else
+/*                                     ((long long int *) var)[act_tuple] = (long long) 0;*/
+                                       *((long long int *) (var + offset * act_tuple)) = (long long) 0;
+
+                               break;
+#endif   /* HAVE_STRTOLL */
+#ifdef HAVE_STRTOULL
+                       case ECPGt_unsigned_long_long:
+                               if (pval)
+                               {
+/*                                     ((unsigned long long int *) var)[act_tuple] = strtoull(pval, &scan_length, 10);*/
+                                       *((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
+                                       if ((isarray && *scan_length != ',' && *scan_length != '}')
+                                               || (!isarray && *scan_length != '\0'))  /* Garbage left */
+                                       {
+                                               ECPGraise(lineno, ECPG_UINT_FORMAT, pval);
+                                               return (false);
+                                       }
+                               }
+                               else
+/*                                     ((unsigned long long int *) var)[act_tuple] = (long long) 0;*/
+                                       *((unsigned long long int *) (var + offset * act_tuple)) = (long long) 0;
+
+                               break;
+#endif   /* HAVE_STRTOULL */
+#endif   /* HAVE_LONG_LONG_INT_64 */
+
+                       case ECPGt_float:
+                       case ECPGt_double:
+                               if (pval)
+                               {
+                                       if (isarray && *pval == '"')
+                                               dres = strtod(pval + 1, &scan_length);
+                                       else
+                                               dres = strtod(pval, &scan_length);
+
+                                       if (isarray && *scan_length == '"')
+                                               scan_length++;
+
+                                       if ((isarray && *scan_length != ',' && *scan_length != '}')
+                                               || (!isarray && *scan_length != '\0'))  /* Garbage left */
+                                       {
+                                               ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval);
+                                               return (false);
+                                       }
+                               }
+                               else
+                                       dres = 0.0;
+
+                               switch (type)
+                               {
+                                       case ECPGt_float:
+/*                                             ((float *) var)[act_tuple] = dres;*/
+                                               *((float *) (var + offset * act_tuple)) = dres;
+                                               break;
+                                       case ECPGt_double:
+/*                                             ((double *) var)[act_tuple] = dres;*/
+                                               *((double *) (var + offset * act_tuple)) = dres;
+                                               break;
+                                       default:
+                                               /* Cannot happen */
+                                               break;
+                               }
+                               break;
+
+                       case ECPGt_bool:
+                               if (pval)
+                               {
+                                       if (pval[0] == 'f' && pval[1] == '\0')
+                                       {
+                                               if (offset == sizeof(char))
+/*                                                     ((char *) var)[act_tuple] = false;*/
+                                                       *((char *) (var + offset * act_tuple)) = false;
+                                               else if (offset == sizeof(int))
+/*                                                     ((int *) var)[act_tuple] = false;*/
+                                                       *((int *) (var + offset * act_tuple)) = false;
+                                               else
+                                                       ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size");
+                                               break;
+                                       }
+                                       else if (pval[0] == 't' && pval[1] == '\0')
+                                       {
+                                               if (offset == sizeof(char))
+/*                                                     ((char *) var)[act_tuple] = true;*/
+                                                       *((char *) (var + offset * act_tuple)) = true;
+                                               else if (offset == sizeof(int))
+/*                                                     ((int *) var)[act_tuple] = true;*/
+                                                       *((int *) (var + offset * act_tuple)) = true;
+                                               else
+                                                       ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size");
+                                               break;
+                                       }
+                                       else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field))
+                                       {
+                                               /* NULL is valid */
+                                               break;
+                                       }
+                               }
+
+                               ECPGraise(lineno, ECPG_CONVERT_BOOL, pval);
+                               return (false);
+                               break;
+
+                       case ECPGt_char:
+                       case ECPGt_unsigned_char:
+                               {
+                                       strncpy((char *) ((long) var + offset * act_tuple), pval, varcharsize);
+                                       if (varcharsize && varcharsize < strlen(pval))
+                                       {
+                                               /* truncation */
+                                               switch (ind_type)
+                                               {
+                                                       case ECPGt_short:
+                                                       case ECPGt_unsigned_short:
+/*                                                             ((short *) ind)[act_tuple] = strlen(pval);*/
+                                                               *((short *) (ind + ind_offset * act_tuple)) = strlen(pval);
+                                                               break;
+                                                       case ECPGt_int:
+                                                       case ECPGt_unsigned_int:
+/*                                                             ((int *) ind)[act_tuple] = strlen(pval);*/
+                                                               *((int *) (ind + ind_offset * act_tuple)) = strlen(pval);
+                                                               break;
+                                                       case ECPGt_long:
+                                                       case ECPGt_unsigned_long:
+/*                                                             ((long *) ind)[act_tuple] = strlen(pval);*/
+                                                               *((long *) (ind + ind_offset * act_tuple)) = strlen(pval);
+                                                               break;
+#ifdef HAVE_LONG_LONG_INT_64
+                                                       case ECPGt_long_long:
+                                                       case ECPGt_unsigned_long_long:
+                                                               *((long long int *) (ind + ind_offset * act_tuple)) = strlen(pval);
+                                                               break;
+#endif   /* HAVE_LONG_LONG_INT_64 */
+                                                       default:
+                                                               break;
+                                               }
+                                               sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W';
+                                       }
+                               }
+                               break;
+
+                       case ECPGt_varchar:
+                               {
+                                       struct ECPGgeneric_varchar *variable =
+                                       (struct ECPGgeneric_varchar *) ((long) var + offset * act_tuple);
+
+                                       variable->len = strlen(pval);
+                                       if (varcharsize == 0)
+                                               strncpy(variable->arr, pval, variable->len);
+                                       else
+                                               strncpy(variable->arr, pval, varcharsize);
+
+                                       if (varcharsize > 0 && variable->len > varcharsize)
+                                       {
+                                               /* truncation */
+                                               switch (ind_type)
+                                               {
+                                                       case ECPGt_short:
+                                                       case ECPGt_unsigned_short:
+/*                                                             ((short *) ind)[act_tuple] = variable->len;*/
+                                                               *((short *) (ind + offset * act_tuple)) = variable->len;
+                                                               break;
+                                                       case ECPGt_int:
+                                                       case ECPGt_unsigned_int:
+/*                                                             ((int *) ind)[act_tuple] = variable->len;*/
+                                                               *((int *) (ind + offset * act_tuple)) = variable->len;
+                                                               break;
+                                                       case ECPGt_long:
+                                                       case ECPGt_unsigned_long:
+/*                                                             ((long *) ind)[act_tuple] = variable->len;*/
+                                                               *((long *) (ind + offset * act_tuple)) = variable->len;
+                                                               break;
+#ifdef HAVE_LONG_LONG_INT_64
+                                                       case ECPGt_long_long:
+                                                       case ECPGt_unsigned_long_long:
+                                                               *((long long int *) (ind + ind_offset * act_tuple)) = variable->len;
+                                                               break;
+#endif   /* HAVE_LONG_LONG_INT_64 */
+                                                       default:
+                                                               break;
+                                               }
+                                               sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W';
+
+                                               variable->len = varcharsize;
+                                       }
+                               }
+                               break;
+
+                       case ECPGt_numeric:
+                               if (pval)
+                               {
+                                       if (isarray && *pval == '"')
+                                               nres = PGTYPESnumeric_aton(pval + 1, &scan_length);
+                                       else
+                                               nres = PGTYPESnumeric_aton(pval, &scan_length);
+
+                                       if (isarray && *scan_length == '"')
+                                               scan_length++;
+
+                                       if ((isarray && *scan_length != ',' && *scan_length != '}')
+                                               || (!isarray && *scan_length != '\0'))  /* Garbage left */
+                                       {
+                                               ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval);
+                                               return (false);
+                                       }
+                               }
+                               else
+                                       nres = PGTYPESnumeric_aton("0.0", &scan_length);
+
+                               PGTYPESnumeric_copy(nres, (NumericVar *)(var + offset * act_tuple));
+                               break;
+
+                       default:
+                               ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(type));
+                               return (false);
+                               break;
+               }
+               if (isarray)
+               {
+                       bool            string = false;
+
+                       /* set array to next entry */
+                       ++act_tuple;
+
+                       /* set pval to the next entry */
+                       for (; string || (*pval != ',' && *pval != '}'); ++pval)
+                               if (*pval == '"')
+                                       string = string ? false : true;
+
+                       if (*pval == ',')
+                               ++pval;
+               }
+       } while (isarray && *pval != '}');
+
+       return (true);
+}
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
new file mode 100644 (file)
index 0000000..cf8ba34
--- /dev/null
@@ -0,0 +1,449 @@
+/* dynamic SQL support routines
+ *
+ * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.1 2003/03/16 10:42:53 meskes Exp $
+ */
+
+#include "postgres_fe.h"
+#include "pg_type.h"
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+#include "sqlca.h"
+#include "sql3types.h"
+
+struct descriptor *all_descriptors = NULL;
+
+/* old internal convenience function that might go away later */
+static PGresult
+                  *
+ECPGresultByDescriptor(int line, const char *name)
+{
+       PGresult  **resultpp = ECPGdescriptor_lvalue(line, name);
+
+       if (resultpp)
+               return *resultpp;
+       return NULL;
+}
+
+static unsigned int
+ECPGDynamicType_DDT(Oid type)
+{
+       switch (type)
+       {
+               case DATEOID:
+                       return SQL3_DDT_DATE;
+               case TIMEOID:
+                       return SQL3_DDT_TIME;
+               case TIMESTAMPOID:
+                       return SQL3_DDT_TIMESTAMP;
+               case TIMESTAMPTZOID:
+                       return SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE;
+               case TIMETZOID:
+                       return SQL3_DDT_TIME_WITH_TIME_ZONE;
+               default:
+                       return SQL3_DDT_ILLEGAL;
+       }
+}
+
+bool
+ECPGget_desc_header(int lineno, char *desc_name, int *count)
+{
+       PGresult   *ECPGresult;
+
+       ECPGinit_sqlca();
+       ECPGresult = ECPGresultByDescriptor(lineno, desc_name);
+       if (!ECPGresult)
+               return false;
+
+       *count = PQnfields(ECPGresult);
+       sqlca.sqlerrd[2] = 1;
+       ECPGlog("ECPGget_desc_header: found %d attributes.\n", *count);
+       return true;
+}
+
+static bool
+get_int_item(int lineno, void *var, enum ECPGttype vartype, int value)
+{
+       switch (vartype)
+       {
+               case ECPGt_short:
+                       *(short *) var = (short) value;
+                       break;
+               case ECPGt_int:
+                       *(int *) var = (int) value;
+                       break;
+               case ECPGt_long:
+                       *(long *) var = (long) value;
+                       break;
+               case ECPGt_unsigned_short:
+                       *(unsigned short *) var = (unsigned short) value;
+                       break;
+               case ECPGt_unsigned_int:
+                       *(unsigned int *) var = (unsigned int) value;
+                       break;
+               case ECPGt_unsigned_long:
+                       *(unsigned long *) var = (unsigned long) value;
+                       break;
+#ifdef HAVE_LONG_LONG_INT_64
+               case ECPGt_long_long:
+                       *(long long int *) var = (long long int) value;
+                       break;
+               case ECPGt_unsigned_long_long:
+                       *(unsigned long long int *) var = (unsigned long long int) value;
+                       break;
+#endif   /* HAVE_LONG_LONG_INT_64 */
+               case ECPGt_float:
+                       *(float *) var = (float) value;
+                       break;
+               case ECPGt_double:
+                       *(double *) var = (double) value;
+                       break;
+               default:
+                       ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, NULL);
+                       return (false);
+       }
+
+       return (true);
+}
+
+static bool
+get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int varcharsize)
+{
+       switch (vartype)
+       {
+               case ECPGt_char:
+               case ECPGt_unsigned_char:
+                       strncpy((char *) var, value, varcharsize);
+                       break;
+               case ECPGt_varchar:
+                       {
+                               struct ECPGgeneric_varchar *variable =
+                               (struct ECPGgeneric_varchar *) var;
+
+                               if (varcharsize == 0)
+                                       strncpy(variable->arr, value, strlen(value));
+                               else
+                                       strncpy(variable->arr, value, varcharsize);
+
+                               variable->len = strlen(value);
+                               if (varcharsize > 0 && variable->len > varcharsize)
+                                       variable->len = varcharsize;
+                       }
+                       break;
+               default:
+                       ECPGraise(lineno, ECPG_VAR_NOT_CHAR, NULL);
+                       return (false);
+       }
+
+       return (true);
+}
+
+bool
+ECPGget_desc(int lineno, char *desc_name, int index,...)
+{
+       va_list         args;
+       PGresult   *ECPGresult;
+       enum ECPGdtype type;
+       int                     ntuples,
+                               act_tuple;
+       struct variable data_var;
+
+       va_start(args, index);
+       ECPGinit_sqlca();
+       ECPGresult = ECPGresultByDescriptor(lineno, desc_name);
+       if (!ECPGresult)
+               return (false);
+
+       ntuples = PQntuples(ECPGresult);
+       if (ntuples < 1)
+       {
+               ECPGraise(lineno, ECPG_NOT_FOUND, NULL);
+               return (false);
+       }
+
+       if (index < 1 || index > PQnfields(ECPGresult))
+       {
+               ECPGraise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, NULL);
+               return (false);
+       }
+
+       ECPGlog("ECPGget_desc: reading items for tuple %d\n", index);
+       --index;
+
+       type = va_arg(args, enum ECPGdtype);
+
+       memset(&data_var, 0, sizeof data_var);
+       data_var.type = ECPGt_EORT;
+       data_var.ind_type = ECPGt_NO_INDICATOR;
+
+       while (type != ECPGd_EODT)
+       {
+               char            type_str[20];
+               long            varcharsize;
+               long            offset;
+               long            arrsize;
+               enum ECPGttype vartype;
+               void       *var;
+
+               vartype = va_arg(args, enum ECPGttype);
+               var = va_arg(args, void *);
+               varcharsize = va_arg(args, long);
+               arrsize = va_arg(args, long);
+               offset = va_arg(args, long);
+
+               switch (type)
+               {
+                       case (ECPGd_indicator):
+                               data_var.ind_type = vartype;
+                               data_var.ind_pointer = var;
+                               data_var.ind_varcharsize = varcharsize;
+                               data_var.ind_arrsize = arrsize;
+                               data_var.ind_offset = offset;
+                               if (data_var.ind_arrsize == 0 || data_var.ind_varcharsize == 0)
+                                       data_var.ind_value = *((void **) (data_var.ind_pointer));
+                               else
+                                       data_var.ind_value = data_var.ind_pointer;
+                               break;
+
+                       case ECPGd_data:
+                               data_var.type = vartype;
+                               data_var.pointer = var;
+                               data_var.varcharsize = varcharsize;
+                               data_var.arrsize = arrsize;
+                               data_var.offset = offset;
+                               if (data_var.arrsize == 0 || data_var.varcharsize == 0)
+                                       data_var.value = *((void **) (data_var.pointer));
+                               else
+                                       data_var.value = data_var.pointer;
+                               break;
+
+                       case ECPGd_name:
+                               if (!get_char_item(lineno, var, vartype, PQfname(ECPGresult, index), varcharsize))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: NAME = %s\n", PQfname(ECPGresult, index));
+                               break;
+
+                       case ECPGd_nullable:
+                               if (!get_int_item(lineno, var, vartype, 1))
+                                       return (false);
+
+                               break;
+
+                       case ECPGd_key_member:
+                               if (!get_int_item(lineno, var, vartype, 0))
+                                       return (false);
+
+                               break;
+
+                       case ECPGd_scale:
+                               if (!get_int_item(lineno, var, vartype, (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: SCALE = %d\n", (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff);
+                               break;
+
+                       case ECPGd_precision:
+                               if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) >> 16))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: PRECISION = %d\n", PQfmod(ECPGresult, index) >> 16);
+                               break;
+
+                       case ECPGd_octet:
+                               if (!get_int_item(lineno, var, vartype, PQfsize(ECPGresult, index)))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: OCTET_LENGTH = %d\n", PQfsize(ECPGresult, index));
+                               break;
+
+                       case ECPGd_length:
+                               if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) - VARHDRSZ))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: LENGTH = %d\n", PQfmod(ECPGresult, index) - VARHDRSZ);
+                               break;
+
+                       case ECPGd_type:
+                               if (!get_int_item(lineno, var, vartype, ECPGDynamicType(PQftype(ECPGresult, index))))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: TYPE = %d\n", ECPGDynamicType(PQftype(ECPGresult, index)));
+                               break;
+
+                       case ECPGd_di_code:
+                               if (!get_int_item(lineno, var, vartype, ECPGDynamicType_DDT(PQftype(ECPGresult, index))))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: TYPE = %d\n", ECPGDynamicType_DDT(PQftype(ECPGresult, index)));
+                               break;
+
+                       case ECPGd_cardinality:
+                               if (!get_int_item(lineno, var, vartype, PQntuples(ECPGresult)))
+                                       return (false);
+
+                               ECPGlog("ECPGget_desc: CARDINALITY = %d\n", PQntuples(ECPGresult));
+                               break;
+
+                       case ECPGd_ret_length:
+                       case ECPGd_ret_octet:
+
+                               /*
+                                * this is like ECPGstore_result
+                                */
+                               if (arrsize > 0 && ntuples > arrsize)
+                               {
+                                       ECPGlog("ECPGget_desc line %d: Incorrect number of matches: %d don't fit into array of %d\n",
+                                                       lineno, ntuples, arrsize);
+                                       ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL);
+                                       return false;
+                               }
+                               /* allocate storage if needed */
+                               if (arrsize == 0 && var != NULL && *(void **) var == NULL)
+                               {
+                                       void       *mem = (void *) ECPGalloc(offset * ntuples, lineno);
+
+                                       *(void **) var = mem;
+                                       ECPGadd_mem(mem, lineno);
+                                       var = mem;
+                               }
+
+                               for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+                               {
+                                       if (!get_int_item(lineno, var, vartype, PQgetlength(ECPGresult, act_tuple, index)))
+                                               return (false);
+                                       var = (char *) var + offset;
+                                       ECPGlog("ECPGget_desc: RETURNED[%d] = %d\n", act_tuple, PQgetlength(ECPGresult, act_tuple, index));
+                               }
+                               break;
+
+                       default:
+                               snprintf(type_str, sizeof(type_str), "%d", type);
+                               ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, type_str);
+                               return (false);
+               }
+
+               type = va_arg(args, enum ECPGdtype);
+       }
+
+       if (data_var.type != ECPGt_EORT)
+       {
+               struct statement stmt;
+               char       *oldlocale;
+
+               /* Make sure we do NOT honor the locale for numeric input */
+               /* since the database gives the standard decimal point */
+               oldlocale = strdup(setlocale(LC_NUMERIC, NULL));
+               setlocale(LC_NUMERIC, "C");
+
+               memset(&stmt, 0, sizeof stmt);
+               stmt.lineno = lineno;
+
+               /* desparate try to guess something sensible */
+               stmt.connection = ECPGget_connection(NULL);
+               ECPGstore_result(ECPGresult, index, &stmt, &data_var);
+
+               setlocale(LC_NUMERIC, oldlocale);
+               ECPGfree(oldlocale);
+       }
+       else if (data_var.ind_type != ECPGt_NO_INDICATOR)
+       {
+               /*
+                * this is like ECPGstore_result but since we don't have a data
+                * variable at hand, we can't call it
+                */
+               if (data_var.ind_arrsize > 0 && ntuples > data_var.ind_arrsize)
+               {
+                       ECPGlog("ECPGget_desc line %d: Incorrect number of matches (indicator): %d don't fit into array of %d\n",
+                                       lineno, ntuples, data_var.ind_arrsize);
+                       ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL);
+                       return false;
+               }
+               /* allocate storage if needed */
+               if (data_var.ind_arrsize == 0 && data_var.ind_pointer != NULL && data_var.ind_value == NULL)
+               {
+                       void       *mem = (void *) ECPGalloc(data_var.ind_offset * ntuples, lineno);
+
+                       *(void **) data_var.ind_pointer = mem;
+                       ECPGadd_mem(mem, lineno);
+                       data_var.ind_value = mem;
+               }
+               for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+               {
+                       if (!get_int_item(lineno, data_var.ind_value, data_var.ind_type, -PQgetisnull(ECPGresult, act_tuple, index)))
+                               return (false);
+                       data_var.ind_value = (char *) data_var.ind_value + data_var.ind_offset;
+                       ECPGlog("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index));
+               }
+       }
+       sqlca.sqlerrd[2] = ntuples;
+       return (true);
+}
+
+bool
+ECPGdeallocate_desc(int line, const char *name)
+{
+       struct descriptor *i;
+       struct descriptor **lastptr = &all_descriptors;
+
+       ECPGinit_sqlca();
+       for (i = all_descriptors; i; lastptr = &i->next, i = i->next)
+       {
+               if (!strcmp(name, i->name))
+               {
+                       *lastptr = i->next;
+                       ECPGfree(i->name);
+                       PQclear(i->result);
+                       ECPGfree(i);
+                       return true;
+               }
+       }
+       ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, name);
+       return false;
+}
+
+bool
+ECPGallocate_desc(int line, const char *name)
+{
+       struct descriptor *new;
+
+       ECPGinit_sqlca();
+       new = (struct descriptor *) ECPGalloc(sizeof(struct descriptor), line);
+       if (!new)
+               return false;
+       new->next = all_descriptors;
+       new->name = ECPGalloc(strlen(name) + 1, line);
+       if (!new->name)
+       {
+               ECPGfree(new);
+               return false;
+       }
+       new->result = PQmakeEmptyPGresult(NULL, 0);
+       if (!new->result)
+       {
+               ECPGfree(new->name);
+               ECPGfree(new);
+               ECPGraise(line, ECPG_OUT_OF_MEMORY, NULL);
+               return false;
+       }
+       strcpy(new->name, name);
+       all_descriptors = new;
+       return true;
+}
+
+PGresult  **
+ECPGdescriptor_lvalue(int line, const char *descriptor)
+{
+       struct descriptor *i;
+
+       for (i = all_descriptors; i != NULL; i = i->next)
+       {
+               if (!strcmp(descriptor, i->name))
+                       return &i->result;
+       }
+
+       ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, (char *) descriptor);
+       return NULL;
+}
diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c
new file mode 100644 (file)
index 0000000..578e7c5
--- /dev/null
@@ -0,0 +1,198 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include <stdio.h>
+
+#include "ecpgerrno.h"
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "extern.h"
+#include "sqlca.h"
+
+/* This should hold the back-end error message from 
+ * the last back-end operation. */
+static char *ECPGerr;
+
+void
+ECPGraise(int line, int code, const char *str)
+{
+       sqlca.sqlcode = code;
+
+       switch (code)
+       {
+               case ECPG_NOT_FOUND:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "No data found in line %d.", line);
+                       break;
+
+               case ECPG_OUT_OF_MEMORY:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Out of memory in line %d.", line);
+                       break;
+
+               case ECPG_UNSUPPORTED:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Unsupported type %s in line %d.", str, line);
+                       break;
+
+               case ECPG_TOO_MANY_ARGUMENTS:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Too many arguments in line %d.", line);
+                       break;
+
+               case ECPG_TOO_FEW_ARGUMENTS:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Too few arguments in line %d.", line);
+                       break;
+
+               case ECPG_INT_FORMAT:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                        "Not correctly formatted int type: %s line %d.", str, line);
+                       break;
+
+               case ECPG_UINT_FORMAT:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Not correctly formatted unsigned type: %s in line %d.", str, line);
+                       break;
+
+               case ECPG_FLOAT_FORMAT:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Not correctly formatted floating-point type: %s in line %d.", str, line);
+                       break;
+
+               case ECPG_CONVERT_BOOL:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                 "Unable to convert %s to bool on line %d.", str, line);
+                       break;
+
+               case ECPG_EMPTY:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Empty query in line %d.", line);
+                       break;
+
+               case ECPG_MISSING_INDICATOR:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "NULL value without indicator in line %d.", line);
+                       break;
+
+               case ECPG_NO_ARRAY:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Variable is not an array in line %d.", line);
+                       break;
+
+               case ECPG_DATA_NOT_ARRAY:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                        "Data read from backend is not an array in line %d.", line);
+                       break;
+
+               case ECPG_ARRAY_INSERT:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                        "Trying to insert an array of variables in line %d.", line);
+                       break;
+
+               case ECPG_NO_CONN:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "No such connection %s in line %d.", str, line);
+                       break;
+
+               case ECPG_NOT_CONN:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Not connected to '%s' in line %d.", str, line);
+                       break;
+
+               case ECPG_INVALID_STMT:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Invalid statement name %s in line %d.", str, line);
+                       break;
+
+               case ECPG_UNKNOWN_DESCRIPTOR:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Descriptor %s not found in line %d.", str, line);
+                       break;
+
+               case ECPG_INVALID_DESCRIPTOR_INDEX:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Descriptor index out of range in line %d.", line);
+                       break;
+
+               case ECPG_UNKNOWN_DESCRIPTOR_ITEM:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Unknown descriptor item %s in line %d.", str, line);
+                       break;
+
+               case ECPG_VAR_NOT_NUMERIC:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Variable is not a numeric type in line %d.", line);
+                       break;
+
+               case ECPG_VAR_NOT_CHAR:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                  "Variable is not a character type in line %d.", line);
+                       break;
+
+               case ECPG_PGSQL:
+                       {
+                               int                     slen = strlen(str);
+
+                               /* strip trailing newline */
+                               if (slen > 0 && str[slen - 1] == '\n')
+                                       slen--;
+                               snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                                "'%.*s' in line %d.", slen, str, line);
+                               break;
+                       }
+
+               case ECPG_TRANS:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "Error in transaction processing in line %d.", line);
+                       break;
+
+               case ECPG_CONNECT:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                         "Could not connect to database %s in line %d.", str, line);
+                       break;
+
+               default:
+                       snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc),
+                                        "SQL error #%d in line %d.", code, line);
+                       break;
+       }
+
+       sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc);
+       ECPGlog("raising sqlcode %d in line %d, '%s'.\n", code, line, sqlca.sqlerrm.sqlerrmc);
+
+       /* free all memory we have allocated for the user */
+       ECPGfree_auto_mem();
+}
+
+/* Set the error message string from the backend */
+void
+set_backend_err(const char *err, int lineno)
+{
+       if (ECPGerr)
+               ECPGfree(ECPGerr);
+
+       if (!err)
+       {
+               ECPGerr = NULL;
+               return;
+       }
+
+       ECPGerr = ECPGstrdup(err, lineno);
+}
+
+/* Retrieve the error message from the backend. */
+char *
+ECPGerrmsg(void)
+{
+       return ECPGerr;
+}
+       
+/* print out an error message */
+void
+sqlprint(void)
+{
+       sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';
+       fprintf(stderr, "sql error %s\n", sqlca.sqlerrm.sqlerrmc);
+}
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
new file mode 100644 (file)
index 0000000..4ed4b8f
--- /dev/null
@@ -0,0 +1,1184 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+/*
+ * The aim is to get a simpler inteface to the database routines.
+ * All the tidieous messing around with tuples is supposed to be hidden
+ * by this function.
+ */
+/* Author: Linus Tolke
+   (actually most if the code is "borrowed" from the distribution and just
+   slightly modified)
+ */
+
+/* Taken over as part of PostgreSQL by Michael Meskes <meskes@postgresql.org>
+   on Feb. 5th, 1998 */
+
+#include "postgres_fe.h"
+
+#include <stdio.h>
+#include <locale.h>
+
+#include "pg_type.h"
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+#include "sqlca.h"
+#include "sql3types.h"
+#include "pgtypes_numeric.h"
+
+/* variables visible to the programs */
+struct sqlca sqlca =
+{
+       {
+               'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
+       },
+       sizeof(struct sqlca),
+       0,
+       {
+               0,
+               {
+                       0
+               }
+       },
+       {
+               'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
+       },
+       {
+               0, 0, 0, 0, 0, 0
+       },
+       {
+               0, 0, 0, 0, 0, 0, 0, 0
+       },
+       {
+               0, 0, 0, 0, 0, 0, 0, 0
+       }
+};
+
+/* This function returns a newly malloced string that has the  \
+   in the argument quoted with \ and the ' quoted with ' as SQL92 says.
+ */
+static
+char *
+quote_postgres(char *arg, int lineno)
+{
+       char       *res = (char *) ECPGalloc(2 * strlen(arg) + 3, lineno);
+       int                     i,
+                               ri = 0;
+
+       if (!res)
+               return (res);
+
+       res[ri++] = '\'';
+
+       for (i = 0; arg[i]; i++, ri++)
+       {
+               switch (arg[i])
+               {
+                       case '\'':
+                               res[ri++] = '\'';
+                               break;
+                       case '\\':
+                               res[ri++] = '\\';
+                               break;
+                       default:
+                               ;
+               }
+               res[ri] = arg[i];
+       }
+
+       res[ri++] = '\'';
+       res[ri] = '\0';
+       
+       return res;
+}
+
+/*
+ * create a list of variables
+ * The variables are listed with input variables preceding outputvariables
+ * The end of each group is marked by an end marker.
+ * per variable we list:
+ * type - as defined in ecpgtype.h
+ * value - where to store the data
+ * varcharsize - length of string in case we have a stringvariable, else 0
+ * arraysize - 0 for pointer (we don't know the size of the array),
+ * 1 for simple variable, size for arrays
+ * offset - offset between ith and (i+1)th entry in an array,
+ * normally that means sizeof(type)
+ * ind_type - type of indicator variable
+ * ind_value - pointer to indicator variable
+ * ind_varcharsize - empty
+ * ind_arraysize -     arraysize of indicator array
+ * ind_offset - indicator offset
+ */
+static bool
+create_statement(int lineno, struct connection * connection, struct statement ** stmt, char *query, va_list ap)
+{
+       struct variable **list = &((*stmt)->inlist);
+       enum ECPGttype type;
+
+       if (!(*stmt = (struct statement *) ECPGalloc(sizeof(struct statement), lineno)))
+               return false;
+
+       (*stmt)->command = query;
+       (*stmt)->connection = connection;
+       (*stmt)->lineno = lineno;
+
+       list = &((*stmt)->inlist);
+
+       type = va_arg(ap, enum ECPGttype);
+
+       while (type != ECPGt_EORT)
+       {
+               if (type == ECPGt_EOIT)
+                       list = &((*stmt)->outlist);
+               else
+               {
+                       struct variable *var,
+                                          *ptr;
+
+                       if (!(var = (struct variable *) ECPGalloc(sizeof(struct variable), lineno)))
+                               return false;
+
+                       var->type = type;
+                       var->pointer = va_arg(ap, char *);
+
+                       /* if variable is NULL, the statement hasn't been prepared */
+                       if (var->pointer == NULL)
+                       {
+                               ECPGraise(lineno, ECPG_INVALID_STMT, NULL);
+                               ECPGfree(var);
+                               return false;
+                       }
+
+                       var->varcharsize = va_arg(ap, long);
+                       var->arrsize = va_arg(ap, long);
+                       var->offset = va_arg(ap, long);
+
+                       if (var->arrsize == 0 || var->varcharsize == 0)
+                               var->value = *((char **) (var->pointer));
+                       else
+                               var->value = var->pointer;
+
+                       var->ind_type = va_arg(ap, enum ECPGttype);
+                       var->ind_pointer = va_arg(ap, char *);
+                       var->ind_varcharsize = va_arg(ap, long);
+                       var->ind_arrsize = va_arg(ap, long);
+                       var->ind_offset = va_arg(ap, long);
+                       var->next = NULL;
+
+                       if (var->ind_type != ECPGt_NO_INDICATOR
+                               && (var->ind_arrsize == 0 || var->ind_varcharsize == 0))
+                               var->ind_value = *((char **) (var->ind_pointer));
+                       else
+                               var->ind_value = var->ind_pointer;
+
+                       for (ptr = *list; ptr && ptr->next; ptr = ptr->next);
+
+                       if (ptr == NULL)
+                               *list = var;
+                       else
+                               ptr->next = var;
+               }
+
+               type = va_arg(ap, enum ECPGttype);
+       }
+
+       return (true);
+}
+
+static void
+free_variable(struct variable * var)
+{
+       struct variable *var_next;
+
+       if (var == (struct variable *) NULL)
+               return;
+       var_next = var->next;
+       ECPGfree(var);
+
+       while (var_next)
+       {
+               var = var_next;
+               var_next = var->next;
+               ECPGfree(var);
+       }
+}
+
+static void
+free_statement(struct statement * stmt)
+{
+       if (stmt == (struct statement *) NULL)
+               return;
+       free_variable(stmt->inlist);
+       free_variable(stmt->outlist);
+       ECPGfree(stmt);
+}
+
+static char *
+next_insert(char *text)
+{
+       char       *ptr = text;
+       bool            string = false;
+
+       for (; *ptr != '\0' && (*ptr != '?' || string); ptr++)
+       {
+               if (*ptr == '\\')               /* escape character */
+                       ptr++;
+               else if (*ptr == '\'')
+                       string = string ? false : true;
+       }
+
+       return (*ptr == '\0') ? NULL : ptr;
+}
+
+/*
+ * push a value on the cache
+ */
+
+static void
+ECPGtypeinfocache_push(struct ECPGtype_information_cache ** cache, int oid, bool isarray, int lineno)
+{
+       struct ECPGtype_information_cache *new_entry
+       = (struct ECPGtype_information_cache *) ECPGalloc(sizeof(struct ECPGtype_information_cache), lineno);
+
+       new_entry->oid = oid;
+       new_entry->isarray = isarray;
+       new_entry->next = *cache;
+       *cache = new_entry;
+}
+
+static bool
+ECPGis_type_an_array(int type, const struct statement * stmt, const struct variable * var)
+{
+       char       *array_query;
+       int                     isarray = 0;
+       PGresult   *query;
+       struct ECPGtype_information_cache *cache_entry;
+
+       if ((stmt->connection->cache_head) == NULL)
+       {
+               /*
+                * Text like types are not an array for ecpg, but postgres counts
+                * them as an array. This define reminds you to not 'correct'
+                * these values.
+                */
+#define not_an_array_in_ecpg false
+
+               /* populate cache with well known types to speed things up */
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), BOOLOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), BYTEAOID, not_an_array_in_ecpg, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), CHAROID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), NAMEOID, not_an_array_in_ecpg, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT8OID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT2OID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT2VECTOROID, true, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT4OID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), REGPROCOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TEXTOID, not_an_array_in_ecpg, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), OIDOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIDOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), XIDOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), CIDOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), OIDVECTOROID, true, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), POINTOID, true, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), LSEGOID, true, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), PATHOID, not_an_array_in_ecpg, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), BOXOID, true, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), POLYGONOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), LINEOID, true, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), FLOAT4OID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), FLOAT8OID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), ABSTIMEOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), RELTIMEOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TINTERVALOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), UNKNOWNOID, not_an_array_in_ecpg, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), CIRCLEOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), CASHOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), INETOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), CIDROID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), BPCHAROID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), VARCHAROID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), DATEOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMEOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMESTAMPOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMESTAMPTZOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), INTERVALOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMETZOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), ZPBITOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), VARBITOID, false, stmt->lineno);
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), NUMERICOID, false, stmt->lineno);
+       }
+
+       for (cache_entry = (stmt->connection->cache_head); cache_entry != NULL; cache_entry = cache_entry->next)
+       {
+               if (cache_entry->oid == type)
+                       return cache_entry->isarray;
+       }
+
+       array_query = (char *) ECPGalloc(strlen("select typelem from pg_type where oid=") + 11, stmt->lineno);
+       sprintf(array_query, "select typelem from pg_type where oid=%d", type);
+       query = PQexec(stmt->connection->connection, array_query);
+       ECPGfree(array_query);
+       if (PQresultStatus(query) == PGRES_TUPLES_OK)
+       {
+               isarray = atol((char *) PQgetvalue(query, 0, 0));
+               if (ECPGDynamicType(type) == SQL3_CHARACTER ||
+                       ECPGDynamicType(type) == SQL3_CHARACTER_VARYING)
+               {
+                       /*
+                        * arrays of character strings are not yet implemented
+                        */
+                       isarray = false;
+               }
+               ECPGlog("ECPGexecute line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, type, var->type, isarray ? "yes" : "no");
+               ECPGtypeinfocache_push(&(stmt->connection->cache_head), type, isarray, stmt->lineno);
+       }
+       PQclear(query);
+       return isarray;
+}
+
+
+bool
+ECPGstore_result(const PGresult *results, int act_field,
+                                const struct statement * stmt, struct variable * var)
+{
+       int                     isarray,
+                               act_tuple,
+                               ntuples = PQntuples(results);
+       bool            status = true;
+
+       isarray = ECPGis_type_an_array(PQftype(results, act_field), stmt, var);
+
+       if (!isarray)
+       {
+               /*
+                * if we don't have enough space, we cannot read all tuples
+                */
+               if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize))
+               {
+                       ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n",
+                                       stmt->lineno, ntuples, var->arrsize);
+                       ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL);
+                       return false;
+               }
+       }
+       else
+       {
+               /*
+                * since we read an array, the variable has to be an array too
+                */
+               if (var->arrsize == 0)
+               {
+                       ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL);
+                       return false;
+               }
+       }
+
+       /*
+        * allocate memory for NULL pointers
+        */
+       if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL)
+       {
+               int                     len = 0;
+
+               switch (var->type)
+               {
+                       case ECPGt_char:
+                       case ECPGt_unsigned_char:
+                               if (!var->varcharsize && !var->arrsize)
+                               {
+                                       /* special mode for handling char**foo=0 */
+                                       for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+                                               len += strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
+                                       len *= var->offset; /* should be 1, but YMNK */
+                                       len += (ntuples + 1) * sizeof(char *);
+
+                                       ECPGlog("ECPGstore_result: line %d: allocating %d bytes for %d tuples (char**=0)",
+                                                       stmt->lineno, len, ntuples);
+                               }
+                               else
+                               {
+                                       var->varcharsize = 0;
+                                       /* check strlen for each tuple */
+                                       for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+                                       {
+                                               int                     len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
+
+                                               if (len > var->varcharsize)
+                                                       var->varcharsize = len;
+                                       }
+                                       var->offset *= var->varcharsize;
+                                       len = var->offset * ntuples;
+                               }
+                               break;
+                       case ECPGt_varchar:
+                               len = ntuples * (var->varcharsize + sizeof(int));
+                               break;
+                       default:
+                               len = var->offset * ntuples;
+                               break;
+               }
+               var->value = (char *) ECPGalloc(len, stmt->lineno);
+               *((char **) var->pointer) = var->value;
+               ECPGadd_mem(var->value, stmt->lineno);
+       }
+
+       /* allocate indicator variable if needed */
+       if ((var->ind_arrsize == 0 || var->ind_varcharsize == 0) && var->ind_value == NULL && var->ind_pointer != NULL)
+       {
+               int                     len = var->ind_offset * ntuples;
+
+               var->ind_value = (char *) ECPGalloc(len, stmt->lineno);
+               *((char **) var->ind_pointer) = var->ind_value;
+               ECPGadd_mem(var->ind_value, stmt->lineno);
+       }
+
+       /* fill the variable with the tuple(s) */
+       if (!var->varcharsize && !var->arrsize &&
+               (var->type == ECPGt_char || var->type == ECPGt_unsigned_char))
+       {
+               /* special mode for handling char**foo=0 */
+
+               /* filling the array of (char*)s */
+               char      **current_string = (char **) var->value;
+
+               /* storing the data (after the last array element) */
+               char       *current_data_location = (char *) &current_string[ntuples + 1];
+
+               for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
+               {
+                       int                     len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1;
+
+                       if (!ECPGget_data(results, act_tuple, act_field, stmt->lineno,
+                                                var->type, var->ind_type, current_data_location,
+                                                         var->ind_value, len, 0, 0, isarray))
+                               status = false;
+                       else
+                       {
+                               *current_string = current_data_location;
+                               current_data_location += len;
+                               current_string++;
+                       }
+               }
+
+               /* terminate the list */
+               *current_string = NULL;
+       }
+       else
+       {
+               for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++)
+               {
+                       if (!ECPGget_data(results, act_tuple, act_field, stmt->lineno,
+                                                         var->type, var->ind_type, var->value,
+                                                         var->ind_value, var->varcharsize, var->offset, var->ind_offset, isarray))
+                               status = false;
+               }
+       }
+       return status;
+}
+
+static bool
+ECPGstore_input(const struct statement * stmt, const struct variable * var,
+                               const char **tobeinserted_p, bool *malloced_p)
+{
+       char       *mallocedval = NULL;
+       char       *newcopy = NULL;
+
+       /*
+        * arrays are not possible unless the attribute is an array too FIXME:
+        * we do not know if the attribute is an array here
+        */
+
+/*      if (var->arrsize > 1 && ...)
+        {
+               ECPGraise(stmt->lineno, ECPG_ARRAY_INSERT, NULL);
+               return false;
+        }*/
+
+       /*
+        * Some special treatment is needed for records since we want their
+        * contents to arrive in a comma-separated list on insert (I think).
+        */
+
+       *malloced_p = false;
+       *tobeinserted_p = "";
+
+       /* check for null value and set input buffer accordingly */
+       switch (var->ind_type)
+       {
+               case ECPGt_short:
+               case ECPGt_unsigned_short:
+                       if (*(short *) var->ind_value < 0)
+                               *tobeinserted_p = "null";
+                       break;
+               case ECPGt_int:
+               case ECPGt_unsigned_int:
+                       if (*(int *) var->ind_value < 0)
+                               *tobeinserted_p = "null";
+                       break;
+               case ECPGt_long:
+               case ECPGt_unsigned_long:
+                       if (*(long *) var->ind_value < 0L)
+                               *tobeinserted_p = "null";
+                       break;
+#ifdef HAVE_LONG_LONG_INT_64
+               case ECPGt_long_long:
+               case ECPGt_unsigned_long_long:
+                       if (*(long long int *) var->ind_value < (long long) 0)
+                               *tobeinserted_p = "null";
+                       break;
+#endif   /* HAVE_LONG_LONG_INT_64 */
+               default:
+                       break;
+       }
+
+       if (**tobeinserted_p == '\0')
+       {
+               switch (var->type)
+               {
+                               int                     element;
+
+                       case ECPGt_short:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%hd,", ((short *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%hd", *((short *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_int:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%d", *((int *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_unsigned_short:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%hu,", ((unsigned short *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%hu", *((unsigned short *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_unsigned_int:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%u,", ((unsigned int *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%u", *((unsigned int *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_long:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%ld,", ((long *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%ld", *((long *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_unsigned_long:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%lu,", ((unsigned long *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%lu", *((unsigned long *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+#ifdef HAVE_LONG_LONG_INT_64
+                       case ECPGt_long_long:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 25, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%lld,", ((long long *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%lld", *((long long *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_unsigned_long_long:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 25, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%llu,", ((unsigned long long *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%llu", *((unsigned long long *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+#endif   /* HAVE_LONG_LONG_INT_64 */
+                       case ECPGt_float:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 21, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((float *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%.14g", *((float *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_double:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 21, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       for (element = 0; element < var->arrsize; element++)
+                                               sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((double *) var->value)[element]);
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                                       sprintf(mallocedval, "%.14g", *((double *) var->value));
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_bool:
+                               if (!(mallocedval = ECPGalloc(var->arrsize * 2, stmt->lineno)))
+                                       return false;
+
+                               if (var->arrsize > 1)
+                               {
+                                       strcpy(mallocedval, "'{");
+
+                                       if (var->offset == sizeof(char))
+                                               for (element = 0; element < var->arrsize; element++)
+                                                       sprintf(mallocedval + strlen(mallocedval), "%c,", (((char *) var->value)[element]) ? 't' : 'f');
+
+                                       /*
+                                        * this is necessary since sizeof(C++'s
+                                        * bool)==sizeof(int)
+                                        */
+                                       else if (var->offset == sizeof(int))
+                                               for (element = 0; element < var->arrsize; element++)
+                                                       sprintf(mallocedval + strlen(mallocedval), "%c,", (((int *) var->value)[element]) ? 't' : 'f');
+                                       else
+                                               ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size");
+
+                                       strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                               }
+                               else
+                               {
+                                       if (var->offset == sizeof(char))
+                                               sprintf(mallocedval, "'%c'", (*((char *) var->value)) ? 't' : 'f');
+                                       else if (var->offset == sizeof(int))
+                                               sprintf(mallocedval, "'%c'", (*((int *) var->value)) ? 't' : 'f');
+                                       else
+                                               ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size");
+                               }
+
+                               *tobeinserted_p = mallocedval;
+                               *malloced_p = true;
+                               break;
+
+                       case ECPGt_char:
+                       case ECPGt_unsigned_char:
+                               {
+                                       /* set slen to string length if type is char * */
+                                       int                     slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize;
+
+                                       if (!(newcopy = ECPGalloc(slen + 1, stmt->lineno)))
+                                               return false;
+
+                                       strncpy(newcopy, (char *) var->value, slen);
+                                       newcopy[slen] = '\0';
+
+                                       mallocedval = quote_postgres(newcopy, stmt->lineno);
+                                       if (!mallocedval)
+                                               return false;
+
+                                       ECPGfree(newcopy);
+
+                                       *tobeinserted_p = mallocedval;
+                                       *malloced_p = true;
+                               }
+                               break;
+                       case ECPGt_char_variable:
+                               {
+                                       int                     slen = strlen((char *) var->value);
+
+                                       if (!(mallocedval = ECPGalloc(slen + 1, stmt->lineno)))
+                                               return false;
+
+                                       strncpy(mallocedval, (char *) var->value, slen);
+                                       mallocedval[slen] = '\0';
+
+                                       *tobeinserted_p = mallocedval;
+                                       *malloced_p = true;
+                               }
+                               break;
+                       case ECPGt_varchar:
+                               {
+                                       struct ECPGgeneric_varchar *variable =
+                                       (struct ECPGgeneric_varchar *) (var->value);
+
+                                       if (!(newcopy = (char *) ECPGalloc(variable->len + 1, stmt->lineno)))
+                                               return false;
+
+                                       strncpy(newcopy, variable->arr, variable->len);
+                                       newcopy[variable->len] = '\0';
+
+                                       mallocedval = quote_postgres(newcopy, stmt->lineno);
+                                       if (!mallocedval)
+                                               return false;
+
+                                       ECPGfree(newcopy);
+
+                                       *tobeinserted_p = mallocedval;
+                                       *malloced_p = true;
+                               }
+                               break;
+
+                       case ECPGt_numeric:
+                               {
+                                       char *str;
+                                       int slen;
+                                       
+                                       if (var->arrsize > 1)
+                                       {
+                                               for (element = 0; element < var->arrsize; element++)
+                                               {
+                                                       str = PGTYPESnumeric_ntoa((NumericVar *)((var + var->offset * element)->value));
+                                                       slen = strlen (str);
+                                                       
+                                                       if (!(mallocedval = ECPGrealloc(mallocedval, strlen(mallocedval) + slen + 5, stmt->lineno)))
+                                                               return false;
+                                                       
+                                                       if (!element)
+                                                               strcpy(mallocedval, "'{");
+                                                       
+                                                       strncpy(mallocedval + strlen(mallocedval), str , slen + 1);
+                                                       strcpy(mallocedval + strlen(mallocedval), ",");
+                                               }
+                                               strcpy(mallocedval + strlen(mallocedval) - 1, "}'");
+                                       }
+                                       else
+                                       {
+                                               *str = PGTYPESnumeric_ntoa((NumericVar *)(var->value));
+                                               slen = strlen (str);
+                                       
+                                               if (!(mallocedval = ECPGalloc(slen + 1, stmt->lineno)))
+                                                       return false;
+
+                                               strncpy(mallocedval, str , slen);
+                                               mallocedval[slen] = '\0';
+                                       }
+                                       
+                                       *tobeinserted_p = mallocedval;
+                                       *malloced_p = true;
+                                       free(str);
+                               }
+                               break;
+                       default:
+                               /* Not implemented yet */
+                               ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, (char *) ECPGtype_name(var->type));
+                               return false;
+                               break;
+               }
+       }
+       return true;
+}
+
+static bool
+ECPGexecute(struct statement * stmt)
+{
+       bool            status = false;
+       char       *copiedquery;
+       char       *errmsg, *cmdstat;
+       PGresult   *results;
+       PGnotify   *notify;
+       struct variable *var;
+
+       copiedquery = ECPGstrdup(stmt->command, stmt->lineno);
+
+       /*
+        * Now, if the type is one of the fill in types then we take the
+        * argument and enter that in the string at the first %s position.
+        * Then if there are any more fill in types we fill in at the next and
+        * so on.
+        */
+       var = stmt->inlist;
+       while (var)
+       {
+               char       *newcopy = NULL;
+               const char *tobeinserted = NULL;
+               char       *p;
+               bool            malloced = FALSE;
+               int                     hostvarl = 0;
+
+               if (!ECPGstore_input(stmt, var, &tobeinserted, &malloced))
+                       return false;
+
+               /*
+                * Now tobeinserted points to an area that is to be inserted at
+                * the first %s
+                */
+               if (!(newcopy = (char *) ECPGalloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno)))
+                       return false;
+
+               strcpy(newcopy, copiedquery);
+               if ((p = next_insert(newcopy + hostvarl)) == NULL)
+               {
+                       /*
+                        * We have an argument but we dont have the matched up string
+                        * in the string
+                        */
+                       ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL);
+                       return false;
+               }
+               else
+               {
+                       strcpy(p, tobeinserted);
+                       hostvarl = strlen(newcopy);
+
+                       /*
+                        * The strange thing in the second argument is the rest of the
+                        * string from the old string
+                        */
+                       strcat(newcopy,
+                                  copiedquery
+                                  + (p - newcopy)
+                                  + sizeof("?") - 1 /* don't count the '\0' */ );
+               }
+
+               /*
+                * Now everything is safely copied to the newcopy. Lets free the
+                * oldcopy and let the copiedquery get the var->value from the
+                * newcopy.
+                */
+               if (malloced)
+               {
+                       ECPGfree((char *) tobeinserted);
+                       tobeinserted = NULL;
+               }
+
+               ECPGfree(copiedquery);
+               copiedquery = newcopy;
+
+               var = var->next;
+       }
+
+       /* Check if there are unmatched things left. */
+       if (next_insert(copiedquery) != NULL)
+       {
+               ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL);
+               return false;
+       }
+
+       /* Now the request is built. */
+
+       if (stmt->connection->committed && !stmt->connection->autocommit)
+       {
+               if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL)
+               {
+                       ECPGraise(stmt->lineno, ECPG_TRANS, NULL);
+                       return false;
+               }
+               PQclear(results);
+               stmt->connection->committed = false;
+       }
+
+       ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name);
+       results = PQexec(stmt->connection->connection, copiedquery);
+       ECPGfree(copiedquery);
+
+       if (results == NULL)
+       {
+               errmsg = PQerrorMessage(stmt->connection->connection);
+               ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, errmsg);
+               ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg);
+               set_backend_err(errmsg, stmt->lineno);
+       }
+       else
+
+               /*
+                * note: since some of the following code is duplicated in
+                * descriptor.c it should go into a separate function
+                */
+       {
+               bool            clear_result = TRUE;
+               errmsg = PQresultErrorMessage(results);
+               set_backend_err(errmsg, stmt->lineno);
+               
+               var = stmt->outlist;
+               switch (PQresultStatus(results))
+               {
+                               int                     nfields,
+                                                       ntuples,
+                                                       act_field;
+
+                       case PGRES_TUPLES_OK:
+                               nfields = PQnfields(results);
+                               sqlca.sqlerrd[2] = ntuples = PQntuples(results);
+                               status = true;
+
+                               if (ntuples < 1)
+                               {
+                                       if (ntuples)
+                                               ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n",
+                                                               stmt->lineno, ntuples);
+                                       ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL);
+                                       status = false;
+                                       break;
+                               }
+
+                               if (var != NULL && var->type == ECPGt_descriptor)
+                               {
+                                       PGresult  **resultpp = ECPGdescriptor_lvalue(stmt->lineno, (const char *) var->pointer);
+
+                                       if (resultpp == NULL)
+                                               status = false;
+                                       else
+                                       {
+                                               if (*resultpp)
+                                                       PQclear(*resultpp);
+                                               *resultpp = results;
+                                               clear_result = FALSE;
+                                               ECPGlog("ECPGexecute putting result (%d tuples) into descriptor '%s'\n", PQntuples(results), (const char *) var->pointer);
+                                       }
+                                       var = var->next;
+                               }
+                               else
+                                       for (act_field = 0; act_field < nfields && status; act_field++)
+                                       {
+                                               if (var == NULL)
+                                               {
+                                                       ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL);
+                                                       return (false);
+                                               }
+
+                                               status = ECPGstore_result(results, act_field, stmt, var);
+
+                                               var = var->next;
+                                       }
+
+                               if (status && var != NULL)
+                               {
+                                       ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL);
+                                       status = false;
+                               }
+
+                               break;
+                       case PGRES_EMPTY_QUERY:
+                               /* do nothing */
+                               ECPGraise(stmt->lineno, ECPG_EMPTY, NULL);
+                               break;
+                       case PGRES_COMMAND_OK:
+                               status = true;
+                               cmdstat = PQcmdStatus(results);
+                               sqlca.sqlerrd[1] = PQoidValue(results);
+                               sqlca.sqlerrd[2] = atol(PQcmdTuples(results));
+                               ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, cmdstat);
+                               if (!sqlca.sqlerrd[2] && (   !strncmp(cmdstat, "UPDATE", 6)
+                                                         || !strncmp(cmdstat, "INSERT", 6)
+                                                         || !strncmp(cmdstat, "DELETE", 6)))
+                                       ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL);
+                               break;
+                       case PGRES_NONFATAL_ERROR:
+                       case PGRES_FATAL_ERROR:
+                       case PGRES_BAD_RESPONSE:
+                               ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, errmsg);
+                               ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg);
+                               status = false;
+                               break;
+                       case PGRES_COPY_OUT:
+                               ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno);
+                               PQendcopy(stmt->connection->connection);
+                               break;
+                       case PGRES_COPY_IN:
+                               ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno);
+                               PQendcopy(stmt->connection->connection);
+                               break;
+                       default:
+                               ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n",
+                                               stmt->lineno);
+                               ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg);
+                               status = false;
+                               break;
+               }
+               if (clear_result)
+                       PQclear(results);
+       }
+
+       /* check for asynchronous returns */
+       notify = PQnotifies(stmt->connection->connection);
+       if (notify)
+       {
+               ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
+                               stmt->lineno, notify->relname, notify->be_pid);
+               ECPGfree(notify);
+       }
+
+       return status;
+}
+
+bool
+ECPGdo(int lineno, const char *connection_name, char *query,...)
+{
+       va_list         args;
+       struct statement *stmt;
+       struct connection *con = ECPGget_connection(connection_name);
+       bool            status;
+       char       *oldlocale;
+
+       /* Make sure we do NOT honor the locale for numeric input/output */
+       /* since the database wants the standard decimal point */
+       oldlocale = strdup(setlocale(LC_NUMERIC, NULL));
+       setlocale(LC_NUMERIC, "C");
+
+       if (!ECPGinit(con, connection_name, lineno))
+       {
+               setlocale(LC_NUMERIC, oldlocale);
+               ECPGfree(oldlocale);
+               return (false);
+       }
+
+       /* construct statement in our own structure */
+       va_start(args, query);
+       if (create_statement(lineno, con, &stmt, query, args) == false)
+       {
+               setlocale(LC_NUMERIC, oldlocale);
+               ECPGfree(oldlocale);
+               return (false);
+       }
+       va_end(args);
+
+       /* are we connected? */
+       if (con == NULL || con->connection == NULL)
+       {
+               free_statement(stmt);
+               ECPGraise(lineno, ECPG_NOT_CONN, (con) ? con->name : "<empty>");
+               setlocale(LC_NUMERIC, oldlocale);
+               ECPGfree(oldlocale);
+               return false;
+       }
+
+       /* initialize auto_mem struct */
+       ECPGclear_auto_mem();
+
+       status = ECPGexecute(stmt);
+       free_statement(stmt);
+
+       /* and reset locale value so our application is not affected */
+       setlocale(LC_NUMERIC, oldlocale);
+       ECPGfree(oldlocale);
+
+       return (status);
+}
+
+/* old descriptor interface */
+bool
+ECPGdo_descriptor(int line, const char *connection,
+                                 const char *descriptor, const char *query)
+{
+       return ECPGdo(line, connection, (char *) query, ECPGt_EOIT,
+                                 ECPGt_descriptor, descriptor, 0L, 0L, 0L,
+                                 ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT);
+}
diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h
new file mode 100644 (file)
index 0000000..a501451
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef _ECPG_LIB_EXTERN_H
+#define _ECPG_LIB_EXTERN_H
+
+#include "postgres_fe.h"
+#include "libpq-fe.h"
+
+/* Here are some methods used by the lib. */
+
+/* Stores the backend error message for client access */
+void set_backend_err(const char *err, int lineon);
+
+/* Store and retrieve the backend error message for client access */
+void set_backend_err(const char *err, int lineon);
+char *ECPGerrmsg(void);
+
+/* Returns a pointer to a string containing a simple type name. */
+void           ECPGadd_mem(void *ptr, int lineno);
+
+bool ECPGget_data(const PGresult *, int, int, int, enum ECPGttype type,
+                        enum ECPGttype, char *, char *, long, long, long, bool);
+struct connection *ECPGget_connection(const char *);
+void           ECPGinit_sqlca(void);
+char      *ECPGalloc(long, int);
+char      *ECPGrealloc(void *, long, int);
+void           ECPGfree(void *);
+bool           ECPGinit(const struct connection *, const char *, const int);
+char      *ECPGstrdup(const char *, int);
+const char *ECPGtype_name(enum ECPGttype);
+unsigned int ECPGDynamicType(Oid);
+void           ECPGfree_auto_mem(void);
+void           ECPGclear_auto_mem(void);
+
+/* A generic varchar type. */
+struct ECPGgeneric_varchar
+{
+       int                     len;
+       char            arr[1];
+};
+
+/*
+ * type information cache
+ */
+
+struct ECPGtype_information_cache
+{
+       struct ECPGtype_information_cache *next;
+       int                     oid;
+       bool            isarray;
+};
+
+/* structure to store one statement */
+struct statement
+{
+       int                     lineno;
+       char       *command;
+       struct connection *connection;
+       struct variable *inlist;
+       struct variable *outlist;
+};
+
+/* structure to store connections */
+struct connection
+{
+       char       *name;
+       PGconn     *connection;
+       bool            committed;
+       int                     autocommit;
+       struct ECPGtype_information_cache *cache_head;
+       struct connection *next;
+};
+
+/* structure to store descriptors */
+struct descriptor
+{
+       char       *name;
+       PGresult   *result;
+       struct descriptor *next;
+};
+
+struct variable
+{
+       enum ECPGttype type;
+       void       *value;
+       void       *pointer;
+       long            varcharsize;
+       long            arrsize;
+       long            offset;
+       enum ECPGttype ind_type;
+       void       *ind_value;
+       void       *ind_pointer;
+       long            ind_varcharsize;
+       long            ind_arrsize;
+       long            ind_offset;
+       struct variable *next;
+};
+
+PGresult  **
+                       ECPGdescriptor_lvalue(int line, const char *descriptor);
+
+bool ECPGstore_result(const PGresult *results, int act_field,
+                                const struct statement * stmt, struct variable * var);
+
+#endif /* _ECPG_LIB_EXTERN_H */
diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c
new file mode 100644 (file)
index 0000000..a94b293
--- /dev/null
@@ -0,0 +1,109 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+
+void
+ECPGfree(void *ptr)
+{
+       free(ptr);
+}
+
+char *
+ECPGalloc(long size, int lineno)
+{
+       char       *new = (char *) calloc(1L, size);
+
+       if (!new)
+       {
+               ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL);
+               return NULL;
+       }
+
+       memset(new, '\0', size);
+       return (new);
+}
+
+char *
+ECPGrealloc(void *ptr, long size, int lineno)
+{
+       char       *new = (char *) realloc(ptr, size);
+
+       if (!new)
+       {
+               ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL);
+               return NULL;
+       }
+
+       return (new);
+}
+
+char *
+ECPGstrdup(const char *string, int lineno)
+{
+       char       *new = strdup(string);
+
+       if (!new)
+       {
+               ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL);
+               return NULL;
+       }
+
+       return (new);
+}
+
+/* keep a list of memory we allocated for the user */
+static struct auto_mem
+{
+       void       *pointer;
+       struct auto_mem *next;
+}      *auto_allocs = NULL;
+
+void
+ECPGadd_mem(void *ptr, int lineno)
+{
+       struct auto_mem *am = (struct auto_mem *) ECPGalloc(sizeof(struct auto_mem), lineno);
+
+       am->pointer = ptr;
+       am->next = auto_allocs;
+       auto_allocs = am;
+}
+
+void
+ECPGfree_auto_mem(void)
+{
+       struct auto_mem *am;
+
+       /* free all memory we have allocated for the user */
+       for (am = auto_allocs; am;)
+       {
+               struct auto_mem *act = am;
+
+               am = am->next;
+               ECPGfree(act->pointer);
+               ECPGfree(act);
+       }
+
+       auto_allocs = NULL;
+}
+
+void
+ECPGclear_auto_mem(void)
+{
+       struct auto_mem *am;
+
+       /* free just our own structure */
+       for (am = auto_allocs; am;)
+       {
+               struct auto_mem *act = am;
+
+               am = am->next;
+               ECPGfree(act);
+       }
+
+       auto_allocs = NULL;
+}
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
new file mode 100644 (file)
index 0000000..4e2f92d
--- /dev/null
@@ -0,0 +1,149 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include <unistd.h>
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+#include "sqlca.h"
+
+static struct sqlca sqlca_init =
+{
+       {
+               'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
+       },
+       sizeof(struct sqlca),
+       0,
+       {
+               0,
+               {
+                       0
+               }
+       },
+       {
+               'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '
+       },
+       {
+               0, 0, 0, 0, 0, 0
+       },
+       {
+               0, 0, 0, 0, 0, 0, 0, 0
+       },
+       {
+               0, 0, 0, 0, 0, 0, 0, 0
+       }
+};
+
+static int     simple_debug = 0;
+static FILE *debugstream = NULL;
+
+void
+ECPGinit_sqlca(void)
+{
+       memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca));
+}
+
+bool
+ECPGinit(const struct connection * con, const char *connection_name, const int lineno)
+{
+       ECPGinit_sqlca();
+       if (con == NULL)
+       {
+               ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL");
+               return (false);
+       }
+
+       return (true);
+}
+
+bool
+ECPGstatus(int lineno, const char *connection_name)
+{
+       struct connection *con = ECPGget_connection(connection_name);
+
+       if (!ECPGinit(con, connection_name, lineno))
+               return (false);
+
+       /* are we connected? */
+       if (con->connection == NULL)
+       {
+               ECPGraise(lineno, ECPG_NOT_CONN, con->name);
+               return false;
+       }
+
+       return (true);
+}
+
+bool
+ECPGtrans(int lineno, const char *connection_name, const char *transaction)
+{
+       PGresult   *res;
+       struct connection *con = ECPGget_connection(connection_name);
+
+       if (!ECPGinit(con, connection_name, lineno))
+               return (false);
+
+       ECPGlog("ECPGtrans line %d action = %s connection = %s\n", lineno, transaction, con->name);
+
+       /* if we have no connection we just simulate the command */
+       if (con && con->connection)
+       {
+               /*
+                * if we are not in autocommit mode, already have committed the
+                * transaction and get another commit, just ignore it
+                */
+               if (!con->committed || con->autocommit)
+               {
+                       if ((res = PQexec(con->connection, transaction)) == NULL)
+                       {
+                               ECPGraise(lineno, ECPG_TRANS, NULL);
+                               return FALSE;
+                       }
+                       PQclear(res);
+               }
+       }
+
+       if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0)
+       {
+               con->committed = true;
+
+               /* deallocate all prepared statements */
+               if (!ECPGdeallocate_all(lineno))
+                       return false;
+       }
+
+       return true;
+}
+
+
+void
+ECPGdebug(int n, FILE *dbgs)
+{
+       simple_debug = n;
+       debugstream = dbgs;
+       ECPGlog("ECPGdebug: set to %d\n", simple_debug);
+}
+
+void
+ECPGlog(const char *format,...)
+{
+       va_list         ap;
+
+       if (simple_debug)
+       {
+               char       *f = (char *) malloc(strlen(format) + 100);
+
+               if (!f)
+                       return;
+
+               sprintf(f, "[%d]: %s", (int) getpid(), format);
+
+               va_start(ap, format);
+               vfprintf(debugstream, f, ap);
+               va_end(ap);
+
+               ECPGfree(f);
+       }
+}
diff --git a/src/interfaces/ecpg/ecpglib/pg_type.h b/src/interfaces/ecpg/ecpglib/pg_type.h
new file mode 100644 (file)
index 0000000..0fb3e8b
--- /dev/null
@@ -0,0 +1,73 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_type.h
+ *       definition of the system "type" relation (pg_type)
+ *       along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: pg_type.h,v 1.1 2003/03/16 10:42:53 meskes Exp $
+ *
+ * NOTES
+ *       the genbki.sh script reads this file and generates .bki
+ *       information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_TYPE_H
+#define PG_TYPE_H
+
+/* ----------------
+ *             initial contents of pg_type
+ * ----------------
+ */
+
+/* keep the following ordered by OID so that later changes can be made easier*/
+
+/* OIDS 1 - 99 */
+#define BOOLOID                        16
+#define BYTEAOID               17
+#define CHAROID                        18
+#define NAMEOID                        19
+#define INT8OID                        20
+#define INT2OID                        21
+#define INT2VECTOROID  22
+#define INT4OID                        23
+#define REGPROCOID             24
+#define TEXTOID                        25
+#define OIDOID                 26
+#define TIDOID         27
+#define XIDOID 28
+#define CIDOID 29
+#define OIDVECTOROID   30
+#define POINTOID               600
+#define LSEGOID                        601
+#define PATHOID                        602
+#define BOXOID                 603
+#define POLYGONOID             604
+#define LINEOID                        628
+#define FLOAT4OID 700
+#define FLOAT8OID 701
+#define ABSTIMEOID             702
+#define RELTIMEOID             703
+#define TINTERVALOID   704
+#define UNKNOWNOID             705
+#define CIRCLEOID              718
+#define CASHOID 790
+#define INETOID 869
+#define CIDROID 650
+#define BPCHAROID              1042
+#define VARCHAROID             1043
+#define DATEOID                        1082
+#define TIMEOID                        1083
+#define TIMESTAMPOID   1114
+#define TIMESTAMPTZOID 1184
+#define INTERVALOID            1186
+#define TIMETZOID              1266
+#define ZPBITOID        1560
+#define VARBITOID        1562
+#define NUMERICOID             1700
+
+#endif   /* PG_TYPE_H */
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
new file mode 100644 (file)
index 0000000..13dd240
--- /dev/null
@@ -0,0 +1,155 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include <ctype.h>
+
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "ecpgerrno.h"
+#include "extern.h"
+#include "sqlca.h"
+
+static struct prepared_statement
+{
+       char       *name;
+       struct statement *stmt;
+       struct prepared_statement *next;
+}      *prep_stmts = NULL;
+
+static bool
+isvarchar(unsigned char c)
+{
+       if (isalnum(c))
+               return true;
+
+       if (c == '_' || c == '>' || c == '-' || c == '.')
+               return true;
+
+       if (c >= 128)
+               return true;
+
+       return (false);
+}
+
+static void
+replace_variables(char *text)
+{
+       char       *ptr = text;
+       bool            string = false;
+
+       for (; *ptr != '\0'; ptr++)
+       {
+               if (*ptr == '\'')
+                       string = string ? false : true;
+
+               if (!string && *ptr == ':')
+               {
+                       *ptr = '?';
+                       for (++ptr; *ptr && isvarchar(*ptr); ptr++)
+                               *ptr = ' ';
+               }
+       }
+}
+
+/* handle the EXEC SQL PREPARE statement */
+bool
+ECPGprepare(int lineno, char *name, char *variable)
+{
+       struct statement *stmt;
+       struct prepared_statement *this;
+
+       /* 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, name);
+
+               if (!b)
+                       return false;
+       }
+
+       this = (struct prepared_statement *) ECPGalloc(sizeof(struct prepared_statement), lineno);
+       if (!this)
+               return false;
+
+       stmt = (struct statement *) ECPGalloc(sizeof(struct statement), lineno);
+       if (!stmt)
+       {
+               ECPGfree(this);
+               return false;
+       }
+
+       /* create statement */
+       stmt->lineno = lineno;
+       stmt->connection = NULL;
+       stmt->command = ECPGstrdup(variable, lineno);
+       stmt->inlist = stmt->outlist = NULL;
+
+       /* if we have C variables in our statment replace them with '?' */
+       replace_variables(stmt->command);
+
+       /* add prepared statement to our list */
+       this->name = ECPGstrdup(name, lineno);
+       this->stmt = stmt;
+
+       if (prep_stmts == NULL)
+               this->next = NULL;
+       else
+               this->next = prep_stmts;
+
+       prep_stmts = this;
+       return true;
+}
+
+/* handle the EXEC SQL DEALLOCATE PREPARE statement */
+bool
+ECPGdeallocate(int lineno, 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)
+       {
+               /* okay, free all the resources */
+               ECPGfree(this->name);
+               ECPGfree(this->stmt->command);
+               ECPGfree(this->stmt);
+               if (prev != NULL)
+                       prev->next = this->next;
+               else
+                       prep_stmts = this->next;
+
+               ECPGfree(this);
+               return true;
+       }
+       ECPGraise(lineno, ECPG_INVALID_STMT, name);
+       return false;
+}
+
+bool
+ECPGdeallocate_all(int lineno)
+{
+       /* deallocate all prepared statements */
+       while (prep_stmts != NULL)
+       {
+               bool            b = ECPGdeallocate(lineno, prep_stmts->name);
+
+               if (!b)
+                       return false;
+       }
+
+       return true;
+}
+
+/* return the prepared statement */
+char *
+ECPGprepared_statement(char *name)
+{
+       struct prepared_statement *this;
+
+       for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next);
+       return (this) ? this->stmt->command : NULL;
+}
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
new file mode 100644 (file)
index 0000000..c970f76
--- /dev/null
@@ -0,0 +1,90 @@
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/typename.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */
+
+#include "postgres_fe.h"
+
+#include <stdlib.h>
+#include "ecpgtype.h"
+#include "ecpglib.h"
+#include "extern.h"
+#include "sql3types.h"
+#include "pg_type.h"
+
+/*
+ * This function is used to generate the correct type names.
+ */
+const char *
+ECPGtype_name(enum ECPGttype typ)
+{
+       switch (typ)
+       {
+               case ECPGt_char:
+                       return "char";
+               case ECPGt_unsigned_char:
+                       return "unsigned char";
+               case ECPGt_short:
+                       return "short";
+               case ECPGt_unsigned_short:
+                       return "unsigned short";
+               case ECPGt_int:
+                       return "int";
+               case ECPGt_unsigned_int:
+                       return "unsigned int";
+               case ECPGt_long:
+                       return "long";
+               case ECPGt_unsigned_long:
+                       return "unsigned long";
+               case ECPGt_long_long:
+                       return "long long";
+               case ECPGt_unsigned_long_long:
+                       return "unsigned long long";
+               case ECPGt_float:
+                       return "float";
+               case ECPGt_double:
+                       return "double";
+               case ECPGt_bool:
+                       return "bool";
+               case ECPGt_varchar:
+                       return "varchar";
+               case ECPGt_char_variable:
+                       return "char";
+               case ECPGt_numeric:
+                       return "numeric";
+               default:
+                       abort();
+       }
+       return NULL;
+}
+
+unsigned int
+ECPGDynamicType(Oid type)
+{
+       switch (type)
+       {
+               case BOOLOID:
+                       return SQL3_BOOLEAN;    /* bool */
+               case INT2OID:
+                       return SQL3_SMALLINT;           /* int2 */
+               case INT4OID:
+                       return SQL3_INTEGER;    /* int4 */
+               case TEXTOID:
+                       return SQL3_CHARACTER;          /* text */
+               case FLOAT4OID:
+                       return SQL3_REAL;       /* float4 */
+               case FLOAT8OID:
+                       return SQL3_DOUBLE_PRECISION;           /* float8 */
+               case BPCHAROID:
+                       return SQL3_CHARACTER;          /* bpchar */
+               case VARCHAROID:
+                       return SQL3_CHARACTER_VARYING;          /* varchar */
+               case DATEOID:
+                       return SQL3_DATE_TIME_TIMESTAMP;        /* date */
+               case TIMEOID:
+                       return SQL3_DATE_TIME_TIMESTAMP;        /* time */
+               case TIMESTAMPOID:
+                       return SQL3_DATE_TIME_TIMESTAMP;        /* datetime */
+               case NUMERICOID:
+                       return SQL3_NUMERIC;    /* numeric */
+               default:
+                       return -type;
+       }
+}
index 6b9e39d1f3d6c4b4db53a6229c4243122adb21d7..9b40e0368c80d53d37ec674b003eb848399b71e4 100644 (file)
@@ -5,7 +5,7 @@ include $(top_builddir)/src/Makefile.global
 install: all installdirs install-headers
 
 .PHONY: install-headers
-ecpg_headers = ecpgerrno.h ecpglib.h ecpgtype.h sqlca.h sql3types.h ecpg_informix.h
+ecpg_headers = ecpgerrno.h ecpglib.h ecpgtype.h sqlca.h sql3types.h ecpg_informix.h pgtypes_error.h pgtypes_numeric.h
 install-headers: $(ecpg_headers)
        for i in $^; do $(INSTALL_DATA) $$i $(DESTDIR)$(includedir); done
 
index 5520ff882f689bd49ad49cc4965c31be8b915e64..6ed5f5d3e4f7741f5932d24baf79d57cdac731d1 100644 (file)
@@ -11,7 +11,7 @@
  * that is registered and that has nothing whatsoever to do with the storage
  * class.
  *
- * Simle types
+ * Simple types
  * integers: char, short, int, long (signed and unsigned)
  * floats: float, double
  *
@@ -51,7 +51,8 @@ enum ECPGttype
        ECPGt_EORT,                                     /* End of result types. */
        ECPGt_NO_INDICATOR,                     /* no indicator */
        ECPGt_long_long, ECPGt_unsigned_long_long,
-       ECPGt_descriptor                        /* sql descriptor, no C variable */
+       ECPGt_descriptor,                       /* sql descriptor, no C variable */
+       ECPGt_numeric
 };
 
  /* descriptor items */
@@ -76,7 +77,7 @@ enum ECPGdtype
        ECPGd_cardinality
 };
 
-#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_varchar2) || ((type)>=ECPGt_long_long && (type) <= ECPGt_unsigned_long_long))
+#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_varchar2) || ((type)>=ECPGt_long_long && (type) <= ECPGt_unsigned_long_long) || (type) >= ECPGt_numeric)
 
 #ifdef __cplusplus
 }
diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile
new file mode 100644 (file)
index 0000000..73d174e
--- /dev/null
@@ -0,0 +1,43 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for ecpg library
+#
+# Copyright (c) 1994, Regents of the University of California
+#
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/pgtypeslib/Makefile,v 1.1 2003/03/16 10:42:54 meskes Exp $
+#
+#-------------------------------------------------------------------------
+
+subdir = src/interfaces/ecpg/pgtypeslib
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+NAME= pgtypes
+SO_MAJOR_VERSION= 1
+SO_MINOR_VERSION= 0.0
+
+override CPPFLAGS := -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(top_srcdir)/src/include/utils $(CPPFLAGS)
+
+OBJS= numeric.o
+
+all: all-lib
+
+# Shared library stuff
+include $(top_srcdir)/src/Makefile.shlib
+
+install: all installdirs install-lib
+
+installdirs:
+       $(mkinstalldirs) $(DESTDIR)$(libdir)
+
+uninstall: uninstall-lib
+
+clean distclean maintainer-clean: clean-lib
+       rm -f $(OBJS)
+
+depend dep:
+       $(CC) -MM $(CFLAGS) *.c >depend
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c
new file mode 100644 (file)
index 0000000..bd6c950
--- /dev/null
@@ -0,0 +1,1715 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "c.h"
+#include "numeric.h"
+#include "pgtypes_error.h"
+
+#define Max(x, y)               ((x) > (y) ? (x) : (y))
+#define Min(x, y)               ((x) < (y) ? (x) : (y))
+
+#define init_var(v)             memset(v,0,sizeof(NumericVar))
+
+#define digitbuf_alloc(size) ((NumericDigit *) pgtypes_alloc(size))
+#define digitbuf_free(buf)      \
+       do { \
+                 if ((buf) != NULL) \
+                          free(buf); \
+          } while (0)
+
+#include "pgtypes_numeric.h"
+
+static char *
+pgtypes_alloc(long size)
+{
+       char *new = (char *) calloc(1L, size);
+
+       if (!new)
+       {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       memset(new, '\0', size);
+       return (new);
+}
+
+/* ----------
+ * apply_typmod() -
+ *
+ *     Do bounds checking and rounding according to the attributes
+ *     typmod field.
+ * ----------
+ */
+static int 
+apply_typmod(NumericVar *var, long typmod)
+{
+       int                     precision;
+       int                     scale;
+       int                     maxweight;
+       int                     i;
+
+       /* Do nothing if we have a default typmod (-1) */
+       if (typmod < (long) (VARHDRSZ))
+               return(0);
+
+       typmod -= VARHDRSZ;
+       precision = (typmod >> 16) & 0xffff;
+       scale = typmod & 0xffff;
+       maxweight = precision - scale;
+
+       /* Round to target scale */
+       i = scale + var->weight + 1;
+       if (i >= 0 && var->ndigits > i)
+       {
+               int                     carry = (var->digits[i] > 4) ? 1 : 0;
+
+               var->ndigits = i;
+
+               while (carry)
+               {
+                       carry += var->digits[--i];
+                       var->digits[i] = carry % 10;
+                       carry /= 10;
+               }
+
+               if (i < 0)
+               {
+                       var->digits--;
+                       var->ndigits++;
+                       var->weight++;
+               }
+       }
+       else
+               var->ndigits = Max(0, Min(i, var->ndigits));
+
+       /*
+        * Check for overflow - note we can't do this before rounding, because
+        * rounding could raise the weight.  Also note that the var's weight
+        * could be inflated by leading zeroes, which will be stripped before
+        * storage but perhaps might not have been yet. In any case, we must
+        * recognize a true zero, whose weight doesn't mean anything.
+        */
+       if (var->weight >= maxweight)
+       {
+               /* Determine true weight; and check for all-zero result */
+               int                     tweight = var->weight;
+
+               for (i = 0; i < var->ndigits; i++)
+               {
+                       if (var->digits[i])
+                               break;
+                       tweight--;
+               }
+
+               if (tweight >= maxweight && i < var->ndigits)
+               {
+                       errno = PGTYPES_OVERFLOW;
+                       return -1;
+               }
+       }
+
+       var->rscale = scale;
+       var->dscale = scale;
+       return (0);
+}
+
+/* ----------
+ *  alloc_var() -
+ *  
+ *   Allocate a digit buffer of ndigits digits (plus a spare digit for rounding)
+ * ----------
+ */
+static int
+alloc_var(NumericVar *var, int ndigits)
+{
+       digitbuf_free(var->buf);
+       var->buf = digitbuf_alloc(ndigits + 1);
+       if (var->buf == NULL)
+               return -1;
+       var->buf[0] = 0;
+       var->digits = var->buf + 1;
+       var->ndigits = ndigits;
+       return 0;
+}
+
+NumericVar * 
+PGTYPESnew(void)
+{
+       NumericVar *var;
+               
+       if ((var = (NumericVar *)pgtypes_alloc(sizeof(NumericVar))) == NULL)
+               return NULL;
+
+       if (alloc_var(var, 0) < 0) {
+               return NULL;
+       }
+
+       return var;
+}
+
+/* ----------
+ * set_var_from_str()
+ *
+ *     Parse a string and put the number into a variable
+ * ----------
+ */
+static int 
+set_var_from_str(char *str, char **ptr, NumericVar *dest)
+{
+       bool    have_dp = FALSE;
+       int     i = 0;
+
+       *ptr = str;
+       while (*(*ptr))
+       {
+               if (!isspace((unsigned char) *(*ptr)))
+                       break;
+               (*ptr)++;
+       }
+
+       if (alloc_var(dest, strlen((*ptr))) < 0)
+               return -1;
+       dest->weight = -1;
+       dest->dscale = 0;
+       dest->sign = NUMERIC_POS;
+
+       switch (*(*ptr))
+       {
+               case '+':
+                       dest->sign = NUMERIC_POS;
+                       (*ptr)++;
+                       break;
+
+               case '-':
+                       dest->sign = NUMERIC_NEG;
+                       (*ptr)++;
+                       break;
+       }
+
+       if (*(*ptr) == '.')
+       {
+               have_dp = TRUE;
+               (*ptr)++;
+       }
+
+       if (!isdigit((unsigned char) *(*ptr)))
+       {
+               errno=PGTYPES_BAD_NUMERIC;
+               return -1;
+       }
+
+       while (*(*ptr))
+       {
+               if (isdigit((unsigned char) *(*ptr)))
+               {
+                       dest->digits[i++] = *(*ptr)++ - '0';
+                       if (!have_dp)
+                               dest->weight++;
+                       else
+                               dest->dscale++;
+               }
+               else if (*(*ptr) == '.')
+               {
+                       if (have_dp)
+                       {
+                               errno = PGTYPES_BAD_NUMERIC;
+                               return -1;
+                       }
+                       have_dp = TRUE;
+                       (*ptr)++;
+               }
+               else
+                       break;
+       }
+       dest->ndigits = i;
+
+       /* Handle exponent, if any */
+       if (*(*ptr) == 'e' || *(*ptr) == 'E')
+       {
+               long            exponent;
+               char       *endptr;
+
+               (*ptr)++;
+               exponent = strtol((*ptr), &endptr, 10);
+               if (endptr == (*ptr))
+               {
+                       errno = PGTYPES_BAD_NUMERIC;
+                       return -1;
+               }
+               (*ptr) = endptr;
+               if (exponent > NUMERIC_MAX_PRECISION ||
+                       exponent < -NUMERIC_MAX_PRECISION)
+               {
+                       errno = PGTYPES_BAD_NUMERIC;
+                       return -1;
+               }
+               dest->weight += (int) exponent;
+               dest->dscale -= (int) exponent;
+               if (dest->dscale < 0)
+                       dest->dscale = 0;
+       }
+
+       /* Should be nothing left but spaces */
+       while (*(*ptr))
+       {
+               if (!isspace((unsigned char) *(*ptr)))
+               {
+                       errno = PGTYPES_BAD_NUMERIC;
+                       return -1;
+               }
+               (*ptr)++;
+       }
+
+       /* Strip any leading zeroes */
+       while (dest->ndigits > 0 && *(dest->digits) == 0)
+       {
+               (dest->digits)++;
+               (dest->weight)--;
+               (dest->ndigits)--;
+       }
+       if (dest->ndigits == 0)
+               dest->weight = 0;
+
+       dest->rscale = dest->dscale;
+       return(0);
+}
+
+
+/* ----------
+ * get_str_from_var() -
+ *
+ *     Convert a var to text representation (guts of numeric_out).
+ *     CAUTION: var's contents may be modified by rounding!
+ * ----------
+ */
+static char *
+get_str_from_var(NumericVar *var, int dscale)
+{
+       char       *str;
+       char       *cp;
+       int                     i;
+       int                     d;
+
+       /*
+        * Check if we must round up before printing the value and do so.
+        */
+       i = dscale + var->weight + 1;
+       if (i >= 0 && var->ndigits > i)
+       {
+               int                     carry = (var->digits[i] > 4) ? 1 : 0;
+
+               var->ndigits = i;
+
+               while (carry)
+               {
+                       carry += var->digits[--i];
+                       var->digits[i] = carry % 10;
+                       carry /= 10;
+               }
+
+               if (i < 0)
+               {
+                       var->digits--;
+                       var->ndigits++;
+                       var->weight++;
+               }
+       }
+       else
+               var->ndigits = Max(0, Min(i, var->ndigits));
+
+       /*
+        * Allocate space for the result
+        */
+       if ((str = (char *)pgtypes_alloc(Max(0, dscale) + Max(0, var->weight) + 4)) == NULL)
+               return NULL;
+       cp = str;
+
+       /*
+        * Output a dash for negative values
+        */
+       if (var->sign == NUMERIC_NEG)
+               *cp++ = '-';
+
+       /*
+        * Output all digits before the decimal point
+        */
+       i = Max(var->weight, 0);
+       d = 0;
+
+       while (i >= 0)
+       {
+               if (i <= var->weight && d < var->ndigits)
+                       *cp++ = var->digits[d++] + '0';
+               else
+                       *cp++ = '0';
+               i--;
+       }
+
+       /*
+        * If requested, output a decimal point and all the digits that follow
+        * it.
+        */
+       if (dscale > 0)
+       {
+               *cp++ = '.';
+               while (i >= -dscale)
+               {
+                       if (i <= var->weight && d < var->ndigits)
+                               *cp++ = var->digits[d++] + '0';
+                       else
+                               *cp++ = '0';
+                       i--;
+               }
+       }
+
+       /*
+        * terminate the string and return it
+        */
+       *cp = '\0';
+       return str;
+}
+
+/* ----------
+ * PGTYPESnumeric_aton() -
+ *
+ *     Input function for numeric data type
+ * ----------
+ */
+NumericVar *
+PGTYPESnumeric_aton(char *str, char **endptr)
+{
+       NumericVar *value = (NumericVar *)pgtypes_alloc(sizeof(NumericVar));
+       int ret;
+       long typmod = -1;
+       char *realptr;
+       char **ptr = (endptr != NULL) ? endptr : &realptr;
+       
+       if (!value)
+               return (NULL);
+       
+       ret = set_var_from_str(str, ptr, value);
+       if (ret)
+               return (NULL);
+
+       ret = apply_typmod(value, typmod);
+       if (ret)
+               return (NULL);
+       
+       return(value);
+}
+
+/* ----------
+ * numeric_out() -
+ *
+ *     Output function for numeric data type
+ * ----------
+ */
+char *
+PGTYPESnumeric_ntoa(NumericVar *num)
+{
+       return(get_str_from_var(num, num->dscale));
+}
+
+/* ----------
+ * zero_var() -
+ *
+ *     Set a variable to ZERO.
+ *     Note: rscale and dscale are not touched.
+ * ----------
+ */
+static void
+zero_var(NumericVar *var)
+{
+       digitbuf_free(var->buf);
+       var->buf = NULL;
+       var->digits = NULL;
+       var->ndigits = 0;
+       var->weight = 0;                        /* by convention; doesn't really matter */
+       var->sign = NUMERIC_POS;        /* anything but NAN... */
+}
+
+void  
+PGTYPESnumeric_free(NumericVar *var)
+{
+       digitbuf_free(var->buf);
+       free(var);
+}
+
+/* ----------
+ * cmp_abs() -
+ *
+ *     Compare the absolute values of var1 and var2
+ *     Returns:        -1 for ABS(var1) < ABS(var2)
+ *                             0  for ABS(var1) == ABS(var2)
+ *                             1  for ABS(var1) > ABS(var2)
+ * ----------
+ */
+static int
+cmp_abs(NumericVar *var1, NumericVar *var2)
+{
+       int                     i1 = 0;
+       int                     i2 = 0;
+       int                     w1 = var1->weight;
+       int                     w2 = var2->weight;
+       int                     stat;
+
+       while (w1 > w2 && i1 < var1->ndigits)
+       {
+               if (var1->digits[i1++] != 0)
+                       return 1;
+               w1--;
+       }
+       while (w2 > w1 && i2 < var2->ndigits)
+       {
+               if (var2->digits[i2++] != 0)
+                       return -1;
+               w2--;
+       }
+
+       if (w1 == w2)
+       {
+               while (i1 < var1->ndigits && i2 < var2->ndigits)
+               {
+                       stat = var1->digits[i1++] - var2->digits[i2++];
+                       if (stat)
+                       {
+                               if (stat > 0)
+                                       return 1;
+                               return -1;
+                       }
+               }
+       }
+
+       while (i1 < var1->ndigits)
+       {
+               if (var1->digits[i1++] != 0)
+                       return 1;
+       }
+       while (i2 < var2->ndigits)
+       {
+               if (var2->digits[i2++] != 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+/* ----------
+ * add_abs() -
+ *
+ *     Add the absolute values of two variables into result.
+ *     result might point to one of the operands without danger.
+ * ----------
+ */
+static int
+add_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
+{
+       NumericDigit *res_buf;
+       NumericDigit *res_digits;
+       int                     res_ndigits;
+       int                     res_weight;
+       int                     res_rscale;
+       int                     res_dscale;
+       int                     i,
+                               i1,
+                               i2;
+       int                     carry = 0;
+
+       /* copy these values into local vars for speed in inner loop */
+       int                     var1ndigits = var1->ndigits;
+       int                     var2ndigits = var2->ndigits;
+       NumericDigit *var1digits = var1->digits;
+       NumericDigit *var2digits = var2->digits;
+
+       res_weight = Max(var1->weight, var2->weight) + 1;
+       res_rscale = Max(var1->rscale, var2->rscale);
+       res_dscale = Max(var1->dscale, var2->dscale);
+       res_ndigits = res_rscale + res_weight + 1;
+       if (res_ndigits <= 0)
+               res_ndigits = 1;
+
+       if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)
+               return -1;
+       res_digits = res_buf;
+
+       i1 = res_rscale + var1->weight + 1;
+       i2 = res_rscale + var2->weight + 1;
+       for (i = res_ndigits - 1; i >= 0; i--)
+       {
+               i1--;
+               i2--;
+               if (i1 >= 0 && i1 < var1ndigits)
+                       carry += var1digits[i1];
+               if (i2 >= 0 && i2 < var2ndigits)
+                       carry += var2digits[i2];
+
+               if (carry >= 10)
+               {
+                       res_digits[i] = carry - 10;
+                       carry = 1;
+               }
+               else
+               {
+                       res_digits[i] = carry;
+                       carry = 0;
+               }
+       }
+
+       while (res_ndigits > 0 && *res_digits == 0)
+       {
+               res_digits++;
+               res_weight--;
+               res_ndigits--;
+       }
+       while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
+               res_ndigits--;
+
+       if (res_ndigits == 0)
+               res_weight = 0;
+
+       digitbuf_free(result->buf);
+       result->ndigits = res_ndigits;
+       result->buf = res_buf;
+       result->digits = res_digits;
+       result->weight = res_weight;
+       result->rscale = res_rscale;
+       result->dscale = res_dscale;
+
+       return 0;
+}
+
+
+/* ----------
+ * sub_abs() -
+ *
+ *     Subtract the absolute value of var2 from the absolute value of var1
+ *     and store in result. result might point to one of the operands
+ *     without danger.
+ *
+ *     ABS(var1) MUST BE GREATER OR EQUAL ABS(var2) !!!
+ * ----------
+ */
+static int
+sub_abs(NumericVar *var1, NumericVar *var2, NumericVar *result)
+{
+       NumericDigit *res_buf;
+       NumericDigit *res_digits;
+       int                     res_ndigits;
+       int                     res_weight;
+       int                     res_rscale;
+       int                     res_dscale;
+       int                     i,
+                               i1,
+                               i2;
+       int                     borrow = 0;
+
+       /* copy these values into local vars for speed in inner loop */
+       int                     var1ndigits = var1->ndigits;
+       int                     var2ndigits = var2->ndigits;
+       NumericDigit *var1digits = var1->digits;
+       NumericDigit *var2digits = var2->digits;
+
+       res_weight = var1->weight;
+       res_rscale = Max(var1->rscale, var2->rscale);
+       res_dscale = Max(var1->dscale, var2->dscale);
+       res_ndigits = res_rscale + res_weight + 1;
+       if (res_ndigits <= 0)
+               res_ndigits = 1;
+
+       if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)
+               return -1;
+       res_digits = res_buf;
+
+       i1 = res_rscale + var1->weight + 1;
+       i2 = res_rscale + var2->weight + 1;
+       for (i = res_ndigits - 1; i >= 0; i--)
+       {
+               i1--;
+               i2--;
+               if (i1 >= 0 && i1 < var1ndigits)
+                       borrow += var1digits[i1];
+               if (i2 >= 0 && i2 < var2ndigits)
+                       borrow -= var2digits[i2];
+
+               if (borrow < 0)
+               {
+                       res_digits[i] = borrow + 10;
+                       borrow = -1;
+               }
+               else
+               {
+                       res_digits[i] = borrow;
+                       borrow = 0;
+               }
+       }
+
+       while (res_ndigits > 0 && *res_digits == 0)
+       {
+               res_digits++;
+               res_weight--;
+               res_ndigits--;
+       }
+       while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
+               res_ndigits--;
+
+       if (res_ndigits == 0)
+               res_weight = 0;
+
+       digitbuf_free(result->buf);
+       result->ndigits = res_ndigits;
+       result->buf = res_buf;
+       result->digits = res_digits;
+       result->weight = res_weight;
+       result->rscale = res_rscale;
+       result->dscale = res_dscale;
+
+       return 0;
+}
+
+/* ----------
+ * add_var() -
+ *
+ *     Full version of add functionality on variable level (handling signs).
+ *     result might point to one of the operands too without danger.
+ * ----------
+ */
+int
+PGTYPESnumeric_add(NumericVar *var1, NumericVar *var2, NumericVar *result)
+{
+       /*
+        * Decide on the signs of the two variables what to do
+        */
+       if (var1->sign == NUMERIC_POS)
+       {
+               if (var2->sign == NUMERIC_POS)
+               {
+                       /*
+                        * Both are positive result = +(ABS(var1) + ABS(var2))
+                        */
+                       if (add_abs(var1, var2, result) != 0)
+                               return -1;
+                       result->sign = NUMERIC_POS;
+               }
+               else
+               {
+                       /*
+                        * var1 is positive, var2 is negative Must compare absolute
+                        * values
+                        */
+                       switch (cmp_abs(var1, var2))
+                       {
+                               case 0:
+                                       /* ----------
+                                        * ABS(var1) == ABS(var2)
+                                        * result = ZERO
+                                        * ----------
+                                        */
+                                       zero_var(result);
+                                       result->rscale = Max(var1->rscale, var2->rscale);
+                                       result->dscale = Max(var1->dscale, var2->dscale);
+                                       break;
+
+                               case 1:
+                                       /* ----------
+                                        * ABS(var1) > ABS(var2)
+                                        * result = +(ABS(var1) - ABS(var2))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var1, var2, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_POS;
+                                       break;
+
+                               case -1:
+                                       /* ----------
+                                        * ABS(var1) < ABS(var2)
+                                        * result = -(ABS(var2) - ABS(var1))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var2, var1, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_NEG;
+                                       break;
+                       }
+               }
+       }
+       else
+       {
+               if (var2->sign == NUMERIC_POS)
+               {
+                       /* ----------
+                        * var1 is negative, var2 is positive
+                        * Must compare absolute values
+                        * ----------
+                        */
+                       switch (cmp_abs(var1, var2))
+                       {
+                               case 0:
+                                       /* ----------
+                                        * ABS(var1) == ABS(var2)
+                                        * result = ZERO
+                                        * ----------
+                                        */
+                                       zero_var(result);
+                                       result->rscale = Max(var1->rscale, var2->rscale);
+                                       result->dscale = Max(var1->dscale, var2->dscale);
+                                       break;
+
+                               case 1:
+                                       /* ----------
+                                        * ABS(var1) > ABS(var2)
+                                        * result = -(ABS(var1) - ABS(var2))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var1, var2, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_NEG;
+                                       break;
+
+                               case -1:
+                                       /* ----------
+                                        * ABS(var1) < ABS(var2)
+                                        * result = +(ABS(var2) - ABS(var1))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var2, var1, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_POS;
+                                       break;
+                       }
+               }
+               else
+               {
+                       /* ----------
+                        * Both are negative
+                        * result = -(ABS(var1) + ABS(var2))
+                        * ----------
+                        */
+                       if (add_abs(var1, var2, result) != 0)
+                               return -1;
+                       result->sign = NUMERIC_NEG;
+               }
+       }
+
+       return 0;
+}
+
+
+/* ----------
+ * sub_var() -
+ *
+ *     Full version of sub functionality on variable level (handling signs).
+ *     result might point to one of the operands too without danger.
+ * ----------
+ */
+int
+PGTYPESnumeric_sub(NumericVar *var1, NumericVar *var2, NumericVar *result)
+{
+       /*
+        * Decide on the signs of the two variables what to do
+        */
+       if (var1->sign == NUMERIC_POS)
+       {
+               if (var2->sign == NUMERIC_NEG)
+               {
+                       /* ----------
+                        * var1 is positive, var2 is negative
+                        * result = +(ABS(var1) + ABS(var2))
+                        * ----------
+                        */
+                       if (add_abs(var1, var2, result) != 0)
+                               return -1;
+                       result->sign = NUMERIC_POS;
+               }
+               else
+               {
+                       /* ----------
+                        * Both are positive
+                        * Must compare absolute values
+                        * ----------
+                        */
+                       switch (cmp_abs(var1, var2))
+                       {
+                               case 0:
+                                       /* ----------
+                                        * ABS(var1) == ABS(var2)
+                                        * result = ZERO
+                                        * ----------
+                                        */
+                                       zero_var(result);
+                                       result->rscale = Max(var1->rscale, var2->rscale);
+                                       result->dscale = Max(var1->dscale, var2->dscale);
+                                       break;
+
+                               case 1:
+                                       /* ----------
+                                        * ABS(var1) > ABS(var2)
+                                        * result = +(ABS(var1) - ABS(var2))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var1, var2, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_POS;
+                                       break;
+
+                               case -1:
+                                       /* ----------
+                                        * ABS(var1) < ABS(var2)
+                                        * result = -(ABS(var2) - ABS(var1))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var2, var1, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_NEG;
+                                       break;
+                       }
+               }
+       }
+       else
+       {
+               if (var2->sign == NUMERIC_NEG)
+               {
+                       /* ----------
+                        * Both are negative
+                        * Must compare absolute values
+                        * ----------
+                        */
+                       switch (cmp_abs(var1, var2))
+                       {
+                               case 0:
+                                       /* ----------
+                                        * ABS(var1) == ABS(var2)
+                                        * result = ZERO
+                                        * ----------
+                                        */
+                                       zero_var(result);
+                                       result->rscale = Max(var1->rscale, var2->rscale);
+                                       result->dscale = Max(var1->dscale, var2->dscale);
+                                       break;
+
+                               case 1:
+                                       /* ----------
+                                        * ABS(var1) > ABS(var2)
+                                        * result = -(ABS(var1) - ABS(var2))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var1, var2, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_NEG;
+                                       break;
+
+                               case -1:
+                                       /* ----------
+                                        * ABS(var1) < ABS(var2)
+                                        * result = +(ABS(var2) - ABS(var1))
+                                        * ----------
+                                        */
+                                       if (sub_abs(var2, var1, result) != 0)
+                                               return -1;
+                                       result->sign = NUMERIC_POS;
+                                       break;
+                       }
+               }
+               else
+               {
+                       /* ----------
+                        * var1 is negative, var2 is positive
+                        * result = -(ABS(var1) + ABS(var2))
+                        * ----------
+                        */
+                       if (add_abs(var1, var2, result) != 0)
+                               return -1;
+                       result->sign = NUMERIC_NEG;
+               }
+       }
+
+       return 0;
+}
+
+/* ----------
+ * mul_var() -
+ *
+ *     Multiplication on variable level. Product of var1 * var2 is stored
+ *     in result.  Accuracy of result is determined by global_rscale.
+ * ----------
+ */
+int
+PGTYPESnumeric_mul(NumericVar *var1, NumericVar *var2, NumericVar *result)
+{
+       NumericDigit *res_buf;
+       NumericDigit *res_digits;
+       int                     res_ndigits;
+       int                     res_weight;
+       int                     res_sign;
+       int                     i,
+                               ri,
+                               i1,
+                               i2;
+       long            sum = 0;
+       int global_rscale = var1->rscale + var2->rscale;
+
+       res_weight = var1->weight + var2->weight + 2;
+       res_ndigits = var1->ndigits + var2->ndigits + 1;
+       if (var1->sign == var2->sign)
+               res_sign = NUMERIC_POS;
+       else
+               res_sign = NUMERIC_NEG;
+
+       if ((res_buf = digitbuf_alloc(res_ndigits)) == NULL)
+                       return -1;
+       res_digits = res_buf;
+       memset(res_digits, 0, res_ndigits);
+
+       ri = res_ndigits;
+       for (i1 = var1->ndigits - 1; i1 >= 0; i1--)
+       {
+               sum = 0;
+               i = --ri;
+
+               for (i2 = var2->ndigits - 1; i2 >= 0; i2--)
+               {
+                       sum += res_digits[i] + var1->digits[i1] * var2->digits[i2];
+                       res_digits[i--] = sum % 10;
+                       sum /= 10;
+               }
+               res_digits[i] = sum;
+       }
+
+       i = res_weight + global_rscale + 2;
+       if (i >= 0 && i < res_ndigits)
+       {
+               sum = (res_digits[i] > 4) ? 1 : 0;
+               res_ndigits = i;
+               i--;
+               while (sum)
+               {
+                       sum += res_digits[i];
+                       res_digits[i--] = sum % 10;
+                       sum /= 10;
+               }
+       }
+
+       while (res_ndigits > 0 && *res_digits == 0)
+       {
+               res_digits++;
+               res_weight--;
+               res_ndigits--;
+       }
+       while (res_ndigits > 0 && res_digits[res_ndigits - 1] == 0)
+               res_ndigits--;
+
+       if (res_ndigits == 0)
+       {
+               res_sign = NUMERIC_POS;
+               res_weight = 0;
+       }
+
+       digitbuf_free(result->buf);
+       result->buf = res_buf;
+       result->digits = res_digits;
+       result->ndigits = res_ndigits;
+       result->weight = res_weight;
+       result->rscale = global_rscale;
+       result->sign = res_sign;
+       result->dscale = var1->dscale + var2->dscale;
+
+       return 0;
+}
+
+/*
+ * Default scale selection for division
+ *
+ * Returns the appropriate display scale for the division result,
+ * and sets global_rscale to the result scale to use during div_var.
+ *
+ * Note that this must be called before div_var.
+ */
+static int
+select_div_scale(NumericVar *var1, NumericVar *var2, int *rscale)
+{
+       int                     weight1,
+                               weight2,
+                               qweight,
+                               i;
+       NumericDigit firstdigit1,
+                               firstdigit2;
+       int                     res_dscale;
+       int                     res_rscale;
+
+       /*
+        * The result scale of a division isn't specified in any SQL standard.
+        * For PostgreSQL we select a display scale that will give at least
+        * NUMERIC_MIN_SIG_DIGITS significant digits, so that numeric gives a
+        * result no less accurate than float8; but use a scale not less than
+        * either input's display scale.
+        *
+        * The result scale is NUMERIC_EXTRA_DIGITS more than the display scale,
+        * to provide some guard digits in the calculation.
+        */
+
+       /* Get the actual (normalized) weight and first digit of each input */
+
+       weight1 = 0;                            /* values to use if var1 is zero */
+       firstdigit1 = 0;
+       for (i = 0; i < var1->ndigits; i++)
+       {
+               firstdigit1 = var1->digits[i];
+               if (firstdigit1 != 0)
+               {
+                       weight1 = var1->weight - i;
+                       break;
+               }
+       }
+
+       weight2 = 0;                            /* values to use if var2 is zero */
+       firstdigit2 = 0;
+       for (i = 0; i < var2->ndigits; i++)
+       {
+               firstdigit2 = var2->digits[i];
+               if (firstdigit2 != 0)
+               {
+                       weight2 = var2->weight - i;
+                       break;
+               }
+       }
+
+       /*
+        * Estimate weight of quotient.  If the two first digits are equal,
+        * we can't be sure, but assume that var1 is less than var2.
+        */
+       qweight = weight1 - weight2;
+       if (firstdigit1 <= firstdigit2)
+               qweight--;
+
+       /* Select display scale */
+       res_dscale = NUMERIC_MIN_SIG_DIGITS - qweight;
+       res_dscale = Max(res_dscale, var1->dscale);
+       res_dscale = Max(res_dscale, var2->dscale);
+       res_dscale = Max(res_dscale, NUMERIC_MIN_DISPLAY_SCALE);
+       res_dscale = Min(res_dscale, NUMERIC_MAX_DISPLAY_SCALE);
+
+       /* Select result scale */
+       *rscale = res_rscale = res_dscale + NUMERIC_EXTRA_DIGITS;
+
+       return res_dscale;
+}
+
+
+/* ----------
+ * div_var() -
+ *
+ *     Division on variable level.  Accuracy of result is determined by
+ *     global_rscale.
+ * ----------
+ */
+int
+PGTYPESnumeric_div(NumericVar *var1, NumericVar *var2, NumericVar *result)
+{
+       NumericDigit *res_digits;
+       int                     res_ndigits;
+       int                     res_sign;
+       int                     res_weight;
+       NumericVar      dividend;
+       NumericVar      divisor[10];
+       int                     ndigits_tmp;
+       int                     weight_tmp;
+       int                     rscale_tmp;
+       int                     ri;
+       int                     i;
+       long            guess;
+       long            first_have;
+       long            first_div;
+       int                     first_nextdigit;
+       int                     stat = 0;
+       int rscale;
+       int res_dscale = select_div_scale(var1, var2, &rscale);
+       
+       /*
+        * First of all division by zero check
+        */
+       ndigits_tmp = var2->ndigits + 1;
+       if (ndigits_tmp == 1)
+       {
+               errno= PGTYPES_DIVIDE_ZERO;
+               return -1;
+       }
+
+       /*
+        * Determine the result sign, weight and number of digits to calculate
+        */
+       if (var1->sign == var2->sign)
+               res_sign = NUMERIC_POS;
+       else
+               res_sign = NUMERIC_NEG;
+       res_weight = var1->weight - var2->weight + 1;
+       res_ndigits = rscale + res_weight;
+       if (res_ndigits <= 0)
+               res_ndigits = 1;
+
+       /*
+        * Now result zero check
+        */
+       if (var1->ndigits == 0)
+       {
+               zero_var(result);
+               result->rscale = rscale;
+               return 0;
+       }
+
+       /*
+        * Initialize local variables
+        */
+       init_var(&dividend);
+       for (i = 1; i < 10; i++)
+               init_var(&divisor[i]);
+
+       /*
+        * Make a copy of the divisor which has one leading zero digit
+        */
+       divisor[1].ndigits = ndigits_tmp;
+       divisor[1].rscale = var2->ndigits;
+       divisor[1].sign = NUMERIC_POS;
+       divisor[1].buf = digitbuf_alloc(ndigits_tmp);
+       divisor[1].digits = divisor[1].buf;
+       divisor[1].digits[0] = 0;
+       memcpy(&(divisor[1].digits[1]), var2->digits, ndigits_tmp - 1);
+
+       /*
+        * Make a copy of the dividend
+        */
+       dividend.ndigits = var1->ndigits;
+       dividend.weight = 0;
+       dividend.rscale = var1->ndigits;
+       dividend.sign = NUMERIC_POS;
+       dividend.buf = digitbuf_alloc(var1->ndigits);
+       dividend.digits = dividend.buf;
+       memcpy(dividend.digits, var1->digits, var1->ndigits);
+
+       /*
+        * Setup the result
+        */
+       digitbuf_free(result->buf);
+       result->buf = digitbuf_alloc(res_ndigits + 2);
+       res_digits = result->buf;
+       result->digits = res_digits;
+       result->ndigits = res_ndigits;
+       result->weight = res_weight;
+       result->rscale = rscale;
+       result->sign = res_sign;
+       res_digits[0] = 0;
+
+       first_div = divisor[1].digits[1] * 10;
+       if (ndigits_tmp > 2)
+               first_div += divisor[1].digits[2];
+
+       first_have = 0;
+       first_nextdigit = 0;
+
+       weight_tmp = 1;
+       rscale_tmp = divisor[1].rscale;
+
+       for (ri = 0; ri <= res_ndigits; ri++)
+       {
+               first_have = first_have * 10;
+               if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)
+                       first_have += dividend.digits[first_nextdigit];
+               first_nextdigit++;
+
+               guess = (first_have * 10) / first_div + 1;
+               if (guess > 9)
+                       guess = 9;
+
+               while (guess > 0)
+               {
+                       if (divisor[guess].buf == NULL)
+                       {
+                               int                     i;
+                               long            sum = 0;
+
+                               memcpy(&divisor[guess], &divisor[1], sizeof(NumericVar));
+                               divisor[guess].buf = digitbuf_alloc(divisor[guess].ndigits);
+                               divisor[guess].digits = divisor[guess].buf;
+                               for (i = divisor[1].ndigits - 1; i >= 0; i--)
+                               {
+                                       sum += divisor[1].digits[i] * guess;
+                                       divisor[guess].digits[i] = sum % 10;
+                                       sum /= 10;
+                               }
+                       }
+
+                       divisor[guess].weight = weight_tmp;
+                       divisor[guess].rscale = rscale_tmp;
+
+                       stat = cmp_abs(&dividend, &divisor[guess]);
+                       if (stat >= 0)
+                               break;
+
+                       guess--;
+               }
+
+               res_digits[ri + 1] = guess;
+               if (stat == 0)
+               {
+                       ri++;
+                       break;
+               }
+
+               weight_tmp--;
+               rscale_tmp++;
+
+               if (guess == 0)
+                       continue;
+
+               sub_abs(&dividend, &divisor[guess], &dividend);
+
+               first_nextdigit = dividend.weight - weight_tmp;
+               first_have = 0;
+               if (first_nextdigit >= 0 && first_nextdigit < dividend.ndigits)
+                       first_have = dividend.digits[first_nextdigit];
+               first_nextdigit++;
+       }
+
+       result->ndigits = ri + 1;
+       if (ri == res_ndigits + 1)
+       {
+               int                     carry = (res_digits[ri] > 4) ? 1 : 0;
+
+               result->ndigits = ri;
+               res_digits[ri] = 0;
+
+               while (carry && ri > 0)
+               {
+                       carry += res_digits[--ri];
+                       res_digits[ri] = carry % 10;
+                       carry /= 10;
+               }
+       }
+
+       while (result->ndigits > 0 && *(result->digits) == 0)
+       {
+               (result->digits)++;
+               (result->weight)--;
+               (result->ndigits)--;
+       }
+       while (result->ndigits > 0 && result->digits[result->ndigits - 1] == 0)
+               (result->ndigits)--;
+       if (result->ndigits == 0)
+               result->sign = NUMERIC_POS;
+
+       /*
+        * Tidy up
+        */
+       digitbuf_free(dividend.buf);
+       for (i = 1; i < 10; i++)
+               digitbuf_free(divisor[i].buf);
+
+       result->dscale = res_dscale;
+       return 0;
+}
+
+
+int
+PGTYPESnumeric_cmp(NumericVar *var1, NumericVar *var2) {
+
+       /* use cmp_abs function to calculate the result */
+
+       /* both are positive: normal comparation with cmp_abs */
+       if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_POS) {
+               return cmp_abs(var1, var2);
+       }
+
+       /* both are negative: return the inverse of the normal comparation */
+       if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_NEG) {
+               /* instead of inverting the result, we invert the paramter
+                * ordering */
+               return cmp_abs(var2, var1);
+       }
+
+       /* one is positive, one is negative: trivial */
+       if (var1->sign == NUMERIC_POS && var2->sign == NUMERIC_NEG) {
+               return 1;
+       }
+       if (var1->sign == NUMERIC_NEG && var2->sign == NUMERIC_POS) {
+               return -1;
+       }
+
+       errno = PGTYPES_BAD_NUMERIC;
+       return INT_MAX;
+
+}
+
+int
+PGTYPESnumeric_iton(signed int int_val, NumericVar *var) {
+       /* implicit conversion */
+       signed long int long_int = int_val;
+       return PGTYPESnumeric_lton(long_int, var);
+}
+
+int
+PGTYPESnumeric_lton(signed long int long_val, NumericVar *var) {
+       /* calculate the size of the long int number */
+       /* a number n needs log_10 n digits */
+       /* however we multiply by 10 each time and compare instead of
+        * calculating the logarithm */
+
+       int size = 0;
+       int i;
+       signed long int abs_long_val = long_val;
+       signed long int extract;
+       signed long int reach_limit;
+       
+       if (abs_long_val < 0) {
+               abs_long_val *= -1;
+               var->sign = NUMERIC_NEG;
+       } else {
+               var->sign = NUMERIC_POS;
+       }
+
+       reach_limit = 1;
+       do {
+               size++;
+               reach_limit *= 10;
+       } while ((reach_limit-1) < abs_long_val);
+
+       /* always add a .0 */
+       size++;
+
+       if (alloc_var(var, size) < 0) {
+               return -1;
+       }
+
+       var->rscale = 1;
+       var->dscale = 1;
+       var->weight = size - 2;
+
+       i = 0;
+       do {
+               reach_limit /= 10;
+               extract = abs_long_val - (abs_long_val % reach_limit);
+               var->digits[i] = extract / reach_limit;
+               abs_long_val -= extract;
+               i++;
+               /* we can abandon if abs_long_val reaches 0, because the
+                * memory is initialized properly and filled with '0', so
+                * converting 10000 in only one step is no problem */
+       } while (abs_long_val > 0);
+
+       return 0;
+}
+
+int
+PGTYPESnumeric_copy(NumericVar *src, NumericVar *dst) {
+       int i;
+
+       zero_var(dst);
+
+       dst->weight = src->weight;
+       dst->rscale = src->rscale;
+       dst->dscale = src->dscale;
+       dst->sign = src->sign;
+
+       if (alloc_var(dst, src->ndigits) != 0)
+               return -1;
+
+       for (i = 0; i < src->ndigits; i++) {
+               dst->digits[i] = src->digits[i];
+       }
+
+       return 0;
+}
+
+int
+PGTYPESnumeric_dton(double d, NumericVar *dst)
+{
+       char buffer[100];
+       NumericVar *tmp;
+       
+       if (sprintf(buffer, "%f", d) == 0)
+               return -1;
+       
+       if ((tmp = PGTYPESnumeric_aton(buffer, NULL)) == NULL)
+               return -1;
+       if (PGTYPESnumeric_copy(tmp, dst) != 0)
+               return -1;
+       PGTYPESnumeric_free(tmp);
+       return 0;
+}
+
+static int
+numericvar_to_double_no_overflow(NumericVar *var, double *dp)
+{
+       char       *tmp;
+       double          val;
+       char       *endptr;
+
+       if ((tmp = get_str_from_var(var, var->dscale)) == NULL)
+               return -1;
+
+       /* unlike float8in, we ignore ERANGE from strtod */
+       val = strtod(tmp, &endptr);
+       if (*endptr != '\0')
+       {
+               /* shouldn't happen ... */
+               free(tmp);
+               errno = PGTYPES_BAD_NUMERIC;
+               return -1;
+       } 
+       *dp = val;
+       free(tmp);
+       return 0;
+}
+
+int
+PGTYPESnumeric_ntod(NumericVar* nv, double* dp) {
+       double tmp;
+       int i;
+       
+       if ((i = numericvar_to_double_no_overflow(nv, &tmp)) != 0)
+               return -1;
+       *dp = tmp;
+       return 0;
+}
+
+int
+PGTYPESnumeric_ntoi(NumericVar* nv, int* ip) {
+       long l;
+       int i;
+       
+       if ((i = PGTYPESnumeric_ntol(nv, &l)) != 0)
+               return i;
+
+       if (l < -INT_MAX || l > INT_MAX) {
+               errno = PGTYPES_OVERFLOW;
+               return -1;
+       } 
+
+       *ip = (int) l;
+       return 0;
+}
+
+int
+PGTYPESnumeric_ntol(NumericVar* nv, long* lp) {
+       int i;
+       long l = 0;
+
+       for (i = 1; i < nv->weight + 2; i++) {
+               l *= 10;
+               l += nv->buf[i];
+       }
+       if (nv->buf[i] >= 5) {
+               /* round up */
+               l++;
+       }
+       if (l > LONG_MAX || l < 0) {
+               errno = PGTYPES_OVERFLOW;
+               return -1;
+       }
+       
+       if (nv->sign == NUMERIC_NEG) {
+               l *= -1;
+       }
+       *lp = l;
+       return 0;
+}
+
+/* Finally we need some wrappers for the INFORMIX functions */
+int
+decadd(NumericVar *arg1, NumericVar *arg2, NumericVar *sum)
+{
+       int i = PGTYPESnumeric_add(arg1, arg2, sum);
+
+       if (i == 0) /* No error */
+               return 0;
+       if (errno == PGTYPES_OVERFLOW)
+               return -1200;
+
+       return -1201;   
+}
+
+int
+deccmp(NumericVar *arg1, NumericVar *arg2)
+{
+       int i = PGTYPESnumeric_cmp(arg1, arg2);
+       
+       /* TODO: Need to return DECUNKNOWN instead of PGTYPES_BAD_NUMERIC */
+       return (i);
+}
+
+void
+deccopy(NumericVar *src, NumericVar *target)
+{
+       PGTYPESnumeric_copy(src, target);
+}
+
+static char *
+strndup(char *str, int len)
+{
+       int real_len = strlen(str);
+       int use_len = (real_len > len) ? len : real_len;
+       
+       char *new = pgtypes_alloc(use_len + 1);
+
+       if (new)
+       {
+               memcpy(str, new, use_len);
+               new[use_len] = '\0';
+       }
+
+       return new;
+}
+
+int
+deccvasc(char *cp, int len, NumericVar *np)
+{
+       char *str = strndup(cp, len); /* Numeric_in always converts the complete string */
+       int ret = 0;
+       
+       if (!str)
+               ret = -1201;
+       else
+       {
+               np = PGTYPESnumeric_aton(str, NULL);
+               if (!np)
+               {
+                       switch (errno)
+                       {
+                               case PGTYPES_OVERFLOW:    ret = -1200;
+                                                         break;
+                               case PGTYPES_BAD_NUMERIC: ret = -1213;
+                                                         break;
+                               default:                  ret = -1216;
+                                                         break;
+                       }
+               }
+       }
+       
+       return ret;
+}
+
+int
+deccvdbl(double dbl, NumericVar *np)
+{
+       return(PGTYPESnumeric_dton(dbl, np));
+}
+
+int
+deccvint(int in, NumericVar *np)
+{
+       return(PGTYPESnumeric_iton(in, np));
+}
+
+int
+deccvlong(long lng, NumericVar *np)
+{
+       return(PGTYPESnumeric_lton(lng, np));   
+}
+
+int
+decdiv(NumericVar *n1, NumericVar *n2, NumericVar *n3)
+{
+       int i = PGTYPESnumeric_div(n1, n2, n3), ret = 0;
+
+       if (i != 0)
+               switch (errno)
+               {
+                       case PGTYPES_DIVIDE_ZERO: ret = -1202;
+                                                 break;
+                       case PGTYPES_OVERFLOW:    ret = -1200;
+                                                 break;
+                       default:                  ret = -1201;
+                                                 break;
+               }
+
+       return ret;
+}
+
+int 
+decmul(NumericVar *n1, NumericVar *n2, NumericVar *n3)
+{
+       int i = PGTYPESnumeric_mul(n1, n2, n3), ret = 0;
+
+       if (i != 0)
+               switch (errno)
+               {
+                       case PGTYPES_OVERFLOW:    ret = -1200;
+                                                 break;
+                       default:                  ret = -1201;
+                                                 break;
+               }
+
+       return ret;
+}
+
+int
+decsub(NumericVar *n1, NumericVar *n2, NumericVar *n3)
+{
+       int i = PGTYPESnumeric_sub(n1, n2, n3), ret = 0;
+
+       if (i != 0)
+               switch (errno)
+               {
+                       case PGTYPES_OVERFLOW:    ret = -1200;
+                                                 break;
+                       default:                  ret = -1201;
+                                                 break;
+               }
+
+       return ret;
+}
+
+int
+dectoasc(NumericVar *np, char *cp, int len, int right)
+{
+       char *str;
+       
+       if (right >= 0)
+               str = get_str_from_var(np, right);
+       else
+               str = get_str_from_var(np, np->dscale);
+
+       if (!str)
+               return -1;
+       
+       /* TODO: have to take care of len here and create exponatial notion if necessary */
+       strncpy(cp, str, len);
+       free (str);
+
+       return 0;
+}
+
+int
+dectodbl(NumericVar *np, double *dblp)
+{
+       return(PGTYPESnumeric_ntod(np, dblp));
+}
+
+int
+dectoint(NumericVar *np, int *ip)
+{
+       int ret = PGTYPESnumeric_ntoi(np, ip);
+
+       if (ret == PGTYPES_OVERFLOW)
+               ret = -1200;
+       
+       return ret;
+}
+
+int
+dectolong(NumericVar *np, long *lngp)  
+{
+       int ret = PGTYPESnumeric_ntol(np, lngp);
+
+       if (ret == PGTYPES_OVERFLOW)
+               ret = -1200;
+       
+       return ret;
+}
+
index b09b1f7c413d9d4dfa9db6bddd910c53fd9165d7..7d267b265771e273cb1e437c23026186902fc111 100644 (file)
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.61 2003/03/10 22:28:21 tgl Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.62 2003/03/16 10:42:54 meskes Exp $ */
 
 /* New main for ecpg, the PostgreSQL embedded SQL precompiler. */
 /* (C) Michael Meskes <meskes@postgresql.org> Feb 5th, 1998 */
@@ -313,7 +313,7 @@ main(int argc, char *const argv[])
                                lex_init();
 
                                /* we need several includes */
-                               fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These four include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n#include <ecpgerrno.h>\n#include <sqlca.h>\n#line 1 \"%s\"\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL, input_filename);
+                               fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These four include files are added by the preprocessor */\n#include <ecpgtype.h>\n#include <ecpglib.h>\n#include <ecpgerrno.h>\n#include <sqlca.h>\n#include <pgtypes_numeric.h>\n#line 1 \"%s\"\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL, input_filename);
 
                                /* add some compatibility headers */
                                if (compat == ECPG_COMPAT_INFORMIX)
index bf2ce183bab66739a7662195391995669da0931e..42c8799ea7795c426bccd4bf21c0ec7ab9829609 100644 (file)
@@ -1,4 +1,4 @@
-/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.211 2003/02/25 15:58:03 meskes Exp $ */
+/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.212 2003/03/16 10:42:54 meskes Exp $ */
 
 /* Copyright comment */
 %{
@@ -211,7 +211,7 @@ make_name(void)
 
         KEY
 
-       LANCOMPILER LANGUAGE LAST LEADING LEFT LEVEL LIKE LIMIT LISTEN
+       LANCOMPILER LANGUAGE LEADING LEFT LEVEL LIKE LIMIT LISTEN
         LOAD LOCAL LOCATION LOCK_P
 
        MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -303,7 +303,7 @@ make_name(void)
 %type  <str>   Typename SimpleTypename Numeric opt_float opt_numeric
 %type  <str>   opt_decimal Character character opt_varying opt_charset
 %type  <str>   opt_collate opt_timezone opt_interval table_ref
-%type  <str>   row_descriptor ConstDatetime AlterDomainStmt
+%type  <str>   row_descriptor ConstDatetime AlterDomainStmt 
 %type  <str>   SelectStmt into_clause OptTemp ConstraintAttributeSpec
 %type  <str>   opt_table opt_all sort_clause sortby_list ConstraintAttr
 %type  <str>   sortby OptUseOp qualified_name_list name_list ColId_or_Sconst
@@ -390,7 +390,7 @@ make_name(void)
 %type  <str>   ECPGAllocateDescr ECPGDeallocateDescr symbol opt_symbol
 %type  <str>   ECPGGetDescriptorHeader ECPGColLabel single_var_declaration
 %type  <str>   reserved_keyword unreserved_keyword
-%type  <str>   col_name_keyword func_name_keyword
+%type  <str>   col_name_keyword func_name_keyword precision opt_scale
 %type  <str>   ECPGTypeName variablelist ECPGColLabelCommon
 
 %type  <descriptor> ECPGGetDescriptor
@@ -399,11 +399,11 @@ make_name(void)
 
 %type  <dtype_enum> descriptor_item desc_header_item
 
-%type  <type>  type common_type single_vt_type
+%type  <type>  var_type common_type single_vt_type
 
 %type  <action> action
 
-%type  <index> opt_array_bounds opt_type_array_bounds
+%type  <index> opt_array_bounds 
 
 %type  <ival>  Iresult
 
@@ -4158,11 +4158,11 @@ single_var_declaration: storage_declaration
 
                        /* we do not need the string "varchar" for output */
                        /* so replace it with an empty string */
-                       if ($2.type_enum == ECPGt_varchar)
+                       /* if ($2.type_enum == ECPGt_varchar)
                        {
                                free($2.type_str);
                                $2.type_str=EMPTY;
-                       }
+                       }*/
                }
                variable_list ';'
                {
@@ -4170,6 +4170,12 @@ single_var_declaration: storage_declaration
                }
                ;
 
+precision:     NumConst        { $$ = $1; };
+
+opt_scale:     ',' NumConst    { $$ = $2; }
+               | /* EMPTY */   { $$ = EMPTY; }
+               ;
+
 single_vt_type: common_type
                | ECPGColLabelCommon
                {
@@ -4180,7 +4186,7 @@ single_vt_type: common_type
                        if (strcmp($1, "varchar") == 0)
                        {
                                $$.type_enum = ECPGt_varchar;
-                               $$.type_str = make_str("varchar");
+                               $$.type_str = EMPTY;
                                $$.type_dimension = -1;
                                $$.type_index = -1;
                                $$.type_sizeof = NULL;
@@ -4201,6 +4207,22 @@ single_vt_type: common_type
                                $$.type_index = -1;
                                $$.type_sizeof = NULL;
                        }
+                       else if (strcmp($1, "numeric") == 0)
+                       {
+                               $$.type_enum = ECPGt_numeric;
+                               $$.type_str = EMPTY;
+                               $$.type_dimension = -1;
+                               $$.type_index = -1;
+                               $$.type_sizeof = NULL;
+                       }
+                       else if (strcmp($1, "decimal") == 0)
+                       {
+                               $$.type_enum = ECPGt_numeric;
+                               $$.type_str = EMPTY;
+                               $$.type_dimension = -1;
+                               $$.type_index = -1;
+                               $$.type_sizeof = NULL;
+                       }
                        else
                        {
                                /* this is for typedef'ed types */
@@ -4214,6 +4236,17 @@ single_vt_type: common_type
                                struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
                        }
                }
+               | ECPGColLabelCommon '(' precision opt_scale ')'
+               {
+                       if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0)
+                               mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument");
+                       
+                       $$.type_enum = ECPGt_numeric;
+                       $$.type_str = EMPTY;
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+                       $$.type_sizeof = NULL;
+               }
                ;
 
 /*
@@ -4253,7 +4286,7 @@ type_declaration: S_TYPEDEF
                /* an initializer specified */
                initializer = 0;
        }
-       type opt_pointer ECPGColLabel opt_type_array_bounds ';'
+       var_type opt_pointer ECPGColLabel opt_array_bounds ';'
        {
                /* add entry to list */
                struct typedefs *ptr, *this;
@@ -4310,7 +4343,7 @@ type_declaration: S_TYPEDEF
        };
 
 var_declaration: storage_declaration
-               type
+               var_type
                {
                        actual_type[struct_level].type_enum = $2.type_enum;
                        actual_type[struct_level].type_dimension = $2.type_dimension;
@@ -4319,11 +4352,11 @@ var_declaration: storage_declaration
 
                        /* we do not need the string "varchar" for output */
                        /* so replace it with an empty string */
-                       if ($2.type_enum == ECPGt_varchar)
+                       /* if ($2.type_enum == ECPGt_varchar)
                        {
                                free($2.type_str);
                                $2.type_str=EMPTY;
-                       }
+                       }*/
                }
                variable_list ';'
                {
@@ -4384,7 +4417,7 @@ common_type: simple_type
                }
                ;
 
-type:          common_type
+var_type:      common_type
                | ECPGColLabel
                {
                        /*
@@ -4394,7 +4427,7 @@ type:             common_type
                        if (strcmp($1, "varchar") == 0)
                        {
                                $$.type_enum = ECPGt_varchar;
-                               $$.type_str = make_str("varchar");
+                               $$.type_str = EMPTY; /*make_str("varchar");*/
                                $$.type_dimension = -1;
                                $$.type_index = -1;
                                $$.type_sizeof = NULL;
@@ -4415,6 +4448,22 @@ type:            common_type
                                $$.type_index = -1;
                                $$.type_sizeof = NULL;
                        }
+                       else if (strcmp($1, "numeric") == 0)
+                       {
+                               $$.type_enum = ECPGt_numeric;
+                               $$.type_str = EMPTY;
+                               $$.type_dimension = -1;
+                               $$.type_index = -1;
+                               $$.type_sizeof = NULL;
+                       }
+                       else if (strcmp($1, "decimal") == 0)
+                       {
+                               $$.type_enum = ECPGt_numeric;
+                               $$.type_str = EMPTY;
+                               $$.type_dimension = -1;
+                               $$.type_index = -1;
+                               $$.type_sizeof = NULL;
+                       }
                        else
                        {
                                /* this is for typedef'ed types */
@@ -4428,6 +4477,17 @@ type:            common_type
                                struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list);
                        }
                }
+               | ECPGColLabelCommon '(' precision opt_scale ')'
+               {
+                       if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0)
+                               mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument");
+                       
+                       $$.type_enum = ECPGt_numeric;
+                       $$.type_str = EMPTY;
+                       $$.type_dimension = -1;
+                       $$.type_index = -1;
+                       $$.type_sizeof = NULL;
+               }
                ;
 
 enum_type: SQL_ENUM opt_symbol enum_definition
@@ -4600,6 +4660,18 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_initializer
                                        $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4);
                                        break;
 
+                               case ECPGt_numeric:
+                                       if (dimension < 0)
+                                                type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length);
+                                        else
+                                                type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension);
+
+                                       if (dimension < 0)
+                                               $$ = cat_str(4, mm_strdup(actual_storage[struct_level]), make_str("NumericVar"), mm_strdup($2), $4);
+                                       else
+                                               $$ = cat_str(5, mm_strdup(actual_storage[struct_level]), make_str("NumericVar"), mm_strdup($2), mm_strdup(dim), $4);
+                                       break;
+                                       
                                default:
                                        if (dimension < 0)
                                                type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1);
@@ -4870,7 +4942,7 @@ ECPGTypedef: TYPE_P
                        /* an initializer specified */
                        initializer = 0;
                }
-               ColLabel IS type opt_type_array_bounds opt_reference
+               ColLabel IS var_type opt_array_bounds opt_reference
                {
                        /* add entry to list */
                        struct typedefs *ptr, *this;
@@ -4925,44 +4997,6 @@ ECPGTypedef: TYPE_P
                }
                ;
 
-opt_type_array_bounds: '[' ']' opt_type_array_bounds
-               {
-                       $$.index1 = 0;
-                       $$.index2 = $3.index1;
-                       $$.str = cat2_str(make_str("[]"), $3.str);
-               }
-               | '(' ')' opt_type_array_bounds
-               {
-                       $$.index1 = 0;
-                       $$.index2 = $3.index1;
-                       $$.str = cat2_str(make_str("[]"), $3.str);
-               }
-               | '[' Iresult ']' opt_type_array_bounds
-               {
-                       char *txt = mm_alloc(20L);
-
-                       sprintf (txt, "%d", $2);
-                       $$.index1 = $2;
-                       $$.index2 = $4.index1;
-                       $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str);
-               }
-               | '(' Iresult ')' opt_type_array_bounds
-               {
-                       char *txt = mm_alloc(20L);
-
-                       sprintf (txt, "%d", $2);
-                       $$.index1 = $2;
-                       $$.index2 = $4.index1;
-                       $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str);
-               }
-               | /* EMPTY */
-               {
-                       $$.index1 = -1;
-                       $$.index2 = -1;
-                       $$.str= EMPTY;
-               }
-               ;
-
 opt_reference: SQL_REFERENCE           { $$ = make_str("reference"); }
                | /*EMPTY*/                                     { $$ = EMPTY; }
                ;
@@ -4976,7 +5010,7 @@ ECPGVar: SQL_VAR
                        /* an initializer specified */
                        initializer = 0;
                }
-               ColLabel IS type opt_type_array_bounds opt_reference
+               ColLabel IS var_type opt_array_bounds opt_reference
                {
                        struct variable *p = find_variable($3);
                        int dimension = $6.index1;
index 4722f2186357d8ca360dbc661b761fe93f78e321..ec916a1e623d297af751f4eb563deafb0e477fcf 100644 (file)
@@ -169,6 +169,9 @@ get_type(enum ECPGttype type)
                                                                                 * quoted */
                        return ("ECPGt_char_variable");
                        break;
+               case ECPGt_numeric:
+                       return ("ECPGt_numeric");
+                       break;
                case ECPGt_descriptor:
                        return ("ECPGt_descriptor");
                        break;
@@ -319,6 +322,14 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type,
 
                                sprintf(offset, "%ld*sizeof(char)", varcharsize == 0 ? 1 : varcharsize);
                                break;
+                       case ECPGt_numeric:
+
+                               /*
+                                *  we have to use a pointer here
+                                */
+                               sprintf(variable, "&(%s%s)", prefix ? prefix : "", name);
+                               sprintf(offset, "sizeof(struct NumericVar)");
+                               break;
                        default:
 
                                /*
index c5b50c6e0fa0ee95ebe46a33fbbd07677ef4cc9c..7c9c0d1edc5cc582a03ff76992e6218ab34bbd64 100644 (file)
@@ -1,4 +1,4 @@
-# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.33 2001/12/23 12:17:41 meskes Exp $
+# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.34 2003/03/16 10:42:54 meskes Exp $
 
 subdir = src/interfaces/ecpg/test
 top_builddir = ../../../..
@@ -8,12 +8,12 @@ override CPPFLAGS := -I$(srcdir)/../include $(CPPFLAGS) -g
 
 ECPG = ../preproc/ecpg -I$(srcdir)/../include
 
-TESTS = test1 test2 test3 test4 perftest dyntest dyntest2 test_notice test_code100 test_init testdynalloc
+TESTS = test1 test2 test3 test4 perftest dyntest dyntest2 test_notice test_code100 test_init testdynalloc num_test
 
 all: $(TESTS)
 
 %: %.o
-       $(CC) $(CFLAGS) $(LDFLAGS) -L../lib -L../../libpq $^ $(LIBS) -lecpg -lpq -o $@
+       $(CC) $(CFLAGS) $(LDFLAGS) -L../ecpglib -L ../pgtypeslib -L../../libpq $^ $(LIBS) -lpgtypes -lecpg -lpq -o $@
 
 %.c: %.pgc
        $(ECPG) $<
diff --git a/src/interfaces/ecpg/test/num_test.pgc b/src/interfaces/ecpg/test/num_test.pgc
new file mode 100644 (file)
index 0000000..741ae16
--- /dev/null
@@ -0,0 +1,56 @@
+#include <stdio.h>
+
+int
+main()
+{
+       char *text="error\n";
+               NumericVar *value1, *value2, *res;
+       exec sql begin declare section;
+               decimal(14,7) des = {0, 0, 0, 0, 0, NULL, NULL} ;
+       exec sql end declare section;
+       double d;
+       FILE *dbgs;
+       
+       if ((dbgs = fopen("log", "w")) != NULL)
+                ECPGdebug(1, dbgs);
+       exec sql whenever sqlerror do sqlprint();
+
+       exec sql connect to mm;
+       exec sql create table test (text char(5), num decimal(14,7));
+       
+       value1 = PGTYPESnew();
+       PGTYPESnumeric_iton(1407, value1);
+       text = PGTYPESnumeric_ntoa(value1);
+       printf("long = %s\n", text);
+               
+       value1 = PGTYPESnumeric_aton("2369.7", -1);
+       value2 = PGTYPESnumeric_aton("10.0", -1);
+       res = PGTYPESnew();
+       decadd(value1, value2, res);
+       text = PGTYPESnumeric_ntoa(res);
+       printf("add = %s\n", text);
+       
+       PGTYPESnumeric_sub(res, value2, res);
+       text = PGTYPESnumeric_ntoa(res);
+       printf("sub = %s\n", text);
+               
+       PGTYPESnumeric_copy(res, &des);
+       exec sql insert into test (text, num) values ('test', :des);
+       
+       value2 = PGTYPESnumeric_aton("2369.7", -1);
+       PGTYPESnumeric_mul(value1, value2, res);
+
+       exec sql select num into :des from test where text = 'test';
+       
+       PGTYPESnumeric_mul(res, &des, res);
+       text = PGTYPESnumeric_ntoa(res);
+       printf("mul = %s\n", text);
+
+       value2 = PGTYPESnumeric_aton("10000", -1);
+       PGTYPESnumeric_div(res, value2, res);
+       text = PGTYPESnumeric_ntoa(res);
+       PGTYPESnumeric_ntod(res, &d);
+       printf("div = %s %e\n", text, d);
+       return (0);
+}
+