- 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
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
--- /dev/null
+#-------------------------------------------------------------------------
+#
+# 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
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* $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);
+}
--- /dev/null
+/* $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 *) ¤t_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);
+}
--- /dev/null
+#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 */
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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);
+ }
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
--- /dev/null
+/* $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;
+}
--- /dev/null
+/* $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;
+ }
+}
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
* 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
*
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 */
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
}
--- /dev/null
+#-------------------------------------------------------------------------
+#
+# 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
--- /dev/null
+#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(÷nd);
+ 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(÷nd, &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(÷nd, &divisor[guess], ÷nd);
+
+ 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;
+}
+
-/* $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 */
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)
-/* $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 */
%{
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
%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
%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
%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
/* 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 ';'
{
}
;
+precision: NumConst { $$ = $1; };
+
+opt_scale: ',' NumConst { $$ = $2; }
+ | /* EMPTY */ { $$ = EMPTY; }
+ ;
+
single_vt_type: common_type
| ECPGColLabelCommon
{
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;
$$.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 */
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;
+ }
;
/*
/* 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;
};
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;
/* 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 ';'
{
}
;
-type: common_type
+var_type: common_type
| ECPGColLabel
{
/*
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;
$$.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 */
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
$$ = 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);
/* 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;
}
;
-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; }
;
/* 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;
* quoted */
return ("ECPGt_char_variable");
break;
+ case ECPGt_numeric:
+ return ("ECPGt_numeric");
+ break;
case ECPGt_descriptor:
return ("ECPGt_descriptor");
break;
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:
/*
-# $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 = ../../../..
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) $<
--- /dev/null
+#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);
+}
+