From: Michael Meskes Date: Sun, 16 Mar 2003 10:42:54 +0000 (+0000) Subject: Started working on a seperate pgtypes library. First test work. PLEASE test compilati... X-Git-Tag: REL7_4_BETA1~929 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=a4f25b6a9c2dbf5f38e498922e3761cb3bf46ba0;p=postgresql Started working on a seperate pgtypes library. First test work. PLEASE test compilation on iother systems. --- diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index d54f3c200c..e4c3548dfc 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -1353,6 +1353,14 @@ Tue Feb 25 16:46:27 CET 2003 - Allow SET CONNECTION to be followed by connection object without leading "TO" or "=". - Allow whenever statement to list function without parameters. + + +Sun Mar 16 11:28:01 CET 2003 + + - Started with a pgtypes library. + - Renamed lib directory to ecpglib. + - Added numerical functions to library and preprocessor. - Set ecpg version to 2.12.0. - - Set library to 3.4.2. + - Set ecpg library to 3.4.2. + - Set pgtypes library to 1.0.0 diff --git a/src/interfaces/ecpg/Makefile b/src/interfaces/ecpg/Makefile index dcdabf17eb..50b97bc2d5 100644 --- a/src/interfaces/ecpg/Makefile +++ b/src/interfaces/ecpg/Makefile @@ -4,11 +4,13 @@ include $(top_builddir)/src/Makefile.global all install installdirs uninstall dep depend distprep: $(MAKE) -C include $@ - $(MAKE) -C lib $@ + $(MAKE) -C ecpglib $@ + $(MAKE) -C pgtypeslib $@ $(MAKE) -C preproc $@ clean distclean maintainer-clean: -$(MAKE) -C include $@ - -$(MAKE) -C lib $@ + -$(MAKE) -C ecpglib $@ + -$(MAKE) -C pgtypeslib $@ -$(MAKE) -C preproc $@ -$(MAKE) -C test clean diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile new file mode 100644 index 0000000000..9d22fd3b6c --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/Makefile @@ -0,0 +1,46 @@ +#------------------------------------------------------------------------- +# +# Makefile for ecpg library +# +# Copyright (c) 1994, Regents of the University of California +# +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/Makefile,v 1.1 2003/03/16 10:42:53 meskes Exp $ +# +#------------------------------------------------------------------------- + +subdir = src/interfaces/ecpg/ecpglib +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +NAME= ecpg +SO_MAJOR_VERSION= 3 +SO_MINOR_VERSION= 4.2 + +override CPPFLAGS := -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(libpq_srcdir) $(CPPFLAGS) + +OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \ + connect.o misc.o + +SHLIB_LINK= $(libpq) + +all: all-lib + +# Shared library stuff +include $(top_srcdir)/src/Makefile.shlib + +install: all installdirs install-lib + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + +uninstall: uninstall-lib + +clean distclean maintainer-clean: clean-lib + rm -f $(OBJS) + +depend dep: + $(CC) -MM $(CFLAGS) *.c >depend + +ifeq (depend,$(wildcard depend)) +include depend +endif diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c new file mode 100644 index 0000000000..5c3c096dd5 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -0,0 +1,495 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/connect.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" +#include "sqlca.h" + +static struct connection *all_connections = NULL, + *actual_connection = NULL; + +struct connection * +ECPGget_connection(const char *connection_name) +{ + struct connection *con = all_connections; + + if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0) + return actual_connection; + + for (; con && strcmp(connection_name, con->name) != 0; con = con->next); + if (con) + return con; + else + return NULL; +} + +static void +ecpg_finish(struct connection * act) +{ + if (act != NULL) + { + struct ECPGtype_information_cache *cache, + *ptr; + + ECPGlog("ecpg_finish: finishing %s.\n", act->name); + PQfinish(act->connection); + + /* remove act from the list */ + if (act == all_connections) + all_connections = act->next; + else + { + struct connection *con; + + for (con = all_connections; con->next && con->next != act; con = con->next); + if (con->next) + con->next = act->next; + } + + if (actual_connection == act) + actual_connection = all_connections; + + for (cache = act->cache_head; cache; ptr = cache, cache = cache->next, ECPGfree(ptr)); + ECPGfree(act->name); + ECPGfree(act); + } + else + ECPGlog("ecpg_finish: called an extra time.\n"); +} + +bool +ECPGsetcommit(int lineno, const char *mode, const char *connection_name) +{ + struct connection *con = ECPGget_connection(connection_name); + PGresult *results; + + if (!ECPGinit(con, connection_name, lineno)) + return (false); + + ECPGlog("ECPGsetcommit line %d action = %s connection = %s\n", lineno, mode, con->name); + + if (con->autocommit == true && strncmp(mode, "off", strlen("off")) == 0) + { + if (con->committed) + { + if ((results = PQexec(con->connection, "begin transaction")) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + con->committed = false; + } + con->autocommit = false; + } + else if (con->autocommit == false && strncmp(mode, "on", strlen("on")) == 0) + { + if (!con->committed) + { + if ((results = PQexec(con->connection, "commit")) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + con->committed = true; + } + con->autocommit = true; + } + + return true; +} + +bool +ECPGsetconn(int lineno, const char *connection_name) +{ + struct connection *con = ECPGget_connection(connection_name); + + if (!ECPGinit(con, connection_name, lineno)) + return (false); + + actual_connection = con; + return true; +} + +static void +ECPGnoticeProcessor_raise(int code, const char *message) +{ + sqlca.sqlcode = code; + strncpy(sqlca.sqlerrm.sqlerrmc, message, sizeof(sqlca.sqlerrm.sqlerrmc)); + sqlca.sqlerrm.sqlerrmc[sizeof(sqlca.sqlerrm.sqlerrmc) - 1] = 0; + sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); + + /* remove trailing newline */ + if (sqlca.sqlerrm.sqlerrml + && sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] == '\n') + { + sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml - 1] = 0; + sqlca.sqlerrm.sqlerrml--; + } + + ECPGlog("raising sqlcode %d\n", code); +} + +/* + * I know this is a mess, but we can't redesign the backend + */ + +static void +ECPGnoticeProcessor(void *arg, const char *message) +{ + /* these notices raise an error */ + if (strncmp(message, "WARNING: ", 9)) + { + ECPGlog("ECPGnoticeProcessor: strange warning '%s'\n", message); + ECPGnoticeProcessor_raise(ECPG_WARNING_UNRECOGNIZED, message); + return; + } + + message += 8; + while (*message == ' ') + message++; + ECPGlog("WARNING: %s", message); + + /* WARNING: (transaction aborted): queries ignored until END */ + + /* + * WARNING: current transaction is aborted, queries ignored until end + * of transaction block + */ + if (strstr(message, "queries ignored") && strstr(message, "transaction") + && strstr(message, "aborted")) + { + ECPGnoticeProcessor_raise(ECPG_WARNING_QUERY_IGNORED, message); + return; + } + + /* WARNING: PerformPortalClose: portal "*" not found */ + if ((!strncmp(message, "PerformPortalClose: portal", 26) + || !strncmp(message, "PerformPortalFetch: portal", 26)) + && strstr(message + 26, "not found")) + { + ECPGnoticeProcessor_raise(ECPG_WARNING_UNKNOWN_PORTAL, message); + return; + } + + /* WARNING: BEGIN: already a transaction in progress */ + if (!strncmp(message, "BEGIN: already a transaction in progress", 40)) + { + ECPGnoticeProcessor_raise(ECPG_WARNING_IN_TRANSACTION, message); + return; + } + + /* WARNING: AbortTransaction and not in in-progress state */ + /* WARNING: COMMIT: no transaction in progress */ + /* WARNING: ROLLBACK: no transaction in progress */ + if (!strncmp(message, "AbortTransaction and not in in-progress state", 45) + || !strncmp(message, "COMMIT: no transaction in progress", 34) + || !strncmp(message, "ROLLBACK: no transaction in progress", 36)) + { + ECPGnoticeProcessor_raise(ECPG_WARNING_NO_TRANSACTION, message); + return; + } + + /* WARNING: BlankPortalAssignName: portal * already exists */ + if (!strncmp(message, "BlankPortalAssignName: portal", 29) + && strstr(message + 29, "already exists")) + { + ECPGnoticeProcessor_raise(ECPG_WARNING_PORTAL_EXISTS, message); + return; + } + + /* these are harmless - do nothing */ + + /* + * WARNING: CREATE TABLE / PRIMARY KEY will create implicit index '*' + * for table '*' + */ + + /* + * WARNING: ALTER TABLE ... ADD CONSTRAINT will create implicit + * trigger(s) for FOREIGN KEY check(s) + */ + + /* + * WARNING: CREATE TABLE will create implicit sequence '*' for SERIAL + * column '*.*' + */ + + /* + * WARNING: CREATE TABLE will create implicit trigger(s) for FOREIGN + * KEY check(s) + */ + if ((!strncmp(message, "CREATE TABLE", 12) || !strncmp(message, "ALTER TABLE", 11)) + && strstr(message + 11, "will create implicit")) + return; + + /* WARNING: QUERY PLAN: */ + if (!strncmp(message, "QUERY PLAN:", 11)) /* do we really see these? */ + return; + + /* + * WARNING: DROP TABLE implicitly drops referential integrity trigger + * from table "*" + */ + if (!strncmp(message, "DROP TABLE implicitly drops", 27)) + return; + + /* + * WARNING: Caution: DROP INDEX cannot be rolled back, so don't abort + * now + */ + if (strstr(message, "cannot be rolled back")) + return; + + /* these and other unmentioned should set sqlca.sqlwarn[2] */ + /* WARNING: The ':' operator is deprecated. Use exp(x) instead. */ + /* WARNING: Rel *: Uninitialized page 0 - fixing */ + /* WARNING: PortalHeapMemoryFree: * not in alloc set! */ + /* WARNING: Too old parent tuple found - can't continue vc_repair_frag */ + /* WARNING: identifier "*" will be truncated to "*" */ + /* WARNING: InvalidateSharedInvalid: cache state reset */ + /* WARNING: RegisterSharedInvalid: SI buffer overflow */ + sqlca.sqlwarn[2] = 'W'; + sqlca.sqlwarn[0] = 'W'; +} + +/* this contains some quick hacks, needs to be cleaned up, but it works */ +bool +ECPGconnect(int lineno, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit) +{ + struct connection *this; + char *dbname = strdup(name), + *host = NULL, + *tmp, + *port = NULL, + *realname = NULL, + *options = NULL; + + ECPGinit_sqlca(); + + if ((this = (struct connection *) ECPGalloc(sizeof(struct connection), lineno)) == NULL) + return false; + + if (dbname == NULL && connection_name == NULL) + connection_name = "DEFAULT"; + + /* get the detail information out of dbname */ + if (strchr(dbname, '@') != NULL) + { + /* old style: dbname[@server][:port] */ + tmp = strrchr(dbname, ':'); + if (tmp != NULL) /* port number given */ + { + port = strdup(tmp + 1); + *tmp = '\0'; + } + + tmp = strrchr(dbname, '@'); + if (tmp != NULL) /* host name given */ + { + host = strdup(tmp + 1); + *tmp = '\0'; + } + realname = strdup(dbname); + } + else if (strncmp(dbname, "tcp:", 4) == 0 || strncmp(dbname, "unix:", 5) == 0) + { + int offset = 0; + + /* + * only allow protocols tcp and unix + */ + if (strncmp(dbname, "tcp:", 4) == 0) + offset = 4; + else if (strncmp(dbname, "unix:", 5) == 0) + offset = 5; + + if (strncmp(dbname + offset, "postgresql://", strlen("postgresql://")) == 0) + { + + /*------ + * new style: + * :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 : ""); + 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 : ""); + 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 : "", + host ? host : "", + port ? port : "", + 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 : ""; + + 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 : "", + port ? port : "", + options ? "with options " : "", options ? options : "", + user ? "for user " : "", user ? user : "", + lineno, errmsg); + + ECPGraise(lineno, ECPG_CONNECT, db); + if (host) + ECPGfree(host); + if (port) + ECPGfree(port); + if (options) + ECPGfree(options); + if (realname) + ECPGfree(realname); + if (dbname) + ECPGfree(dbname); + return false; + } + + if (host) + ECPGfree(host); + if (port) + ECPGfree(port); + if (options) + ECPGfree(options); + if (realname) + ECPGfree(realname); + if (dbname) + ECPGfree(dbname); + + this->committed = true; + this->autocommit = autocommit; + + PQsetNoticeProcessor(this->connection, &ECPGnoticeProcessor, (void *) this); + + return true; +} + +bool +ECPGdisconnect(int lineno, const char *connection_name) +{ + struct connection *con; + + if (strcmp(connection_name, "ALL") == 0) + { + ECPGinit_sqlca(); + for (con = all_connections; con;) + { + struct connection *f = con; + + con = con->next; + ecpg_finish(f); + } + } + else + { + con = ECPGget_connection(connection_name); + + if (!ECPGinit(con, connection_name, lineno)) + return (false); + else + ecpg_finish(con); + } + + return true; +} diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c new file mode 100644 index 0000000000..85d5e30a1b --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/data.c @@ -0,0 +1,424 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/data.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include +#include + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" +#include "sqlca.h" +#include "pgtypes_numeric.h" + +bool +ECPGget_data(const PGresult *results, int act_tuple, int act_field, int lineno, + enum ECPGttype type, enum ECPGttype ind_type, + char *var, char *ind, long varcharsize, long offset, + long ind_offset, bool isarray) +{ + char *pval = (char *) PQgetvalue(results, act_tuple, act_field); + + ECPGlog("ECPGget_data line %d: RESULT: %s offset: %ld\n", lineno, pval ? pval : "", offset); + + /* pval is a pointer to the value */ + /* let's check is it really is an array if it should be one */ + if (isarray) + { + if (*pval != '{') + { + ECPGraise(lineno, ECPG_DATA_NOT_ARRAY, NULL); + return (false); + } + + switch (type) + { + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_varchar: + break; + + default: + pval++; + break; + } + } + + /* We will have to decode the value */ + + /* + * check for null value and set indicator accordingly + */ + switch (ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: +/* ((short *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/ + *((short *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field); + break; + case ECPGt_int: + case ECPGt_unsigned_int: +/* ((int *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/ + *((int *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field); + break; + case ECPGt_long: + case ECPGt_unsigned_long: +/* ((long *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/ + *((long *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field); + break; +#ifdef HAVE_LONG_LONG_INT_64 + case ECPGt_long_long: + case ECPGt_unsigned_long_long: +/* ((long long int *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field);*/ + *((long long int *) (ind + ind_offset * act_tuple)) = -PQgetisnull(results, act_tuple, act_field); + break; +/* case ECPGt_unsigned_long_long: + ((unsigned long long int *) ind)[act_tuple] = -PQgetisnull(results, act_tuple, act_field); + break;*/ +#endif /* HAVE_LONG_LONG_INT_64 */ + case ECPGt_NO_INDICATOR: + if (PQgetisnull(results, act_tuple, act_field)) + { + ECPGraise(lineno, ECPG_MISSING_INDICATOR, NULL); + return (false); + } + break; + default: + ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(ind_type)); + return (false); + break; + } + + do + { + switch (type) + { + long res; + unsigned long ures; + double dres; + char *scan_length; + NumericVar *nres; + + case ECPGt_short: + case ECPGt_int: + case ECPGt_long: + if (pval) + { + res = strtol(pval, &scan_length, 10); + if ((isarray && *scan_length != ',' && *scan_length != '}') + || (!isarray && *scan_length != '\0')) /* Garbage left */ + { + ECPGraise(lineno, ECPG_INT_FORMAT, pval); + return (false); + } + } + else + res = 0L; + + switch (type) + { + case ECPGt_short: +/* ((short *) var)[act_tuple] = (short) res;*/ + *((short *) (var + offset * act_tuple)) = (short) res; + break; + case ECPGt_int: +/* ((int *) var)[act_tuple] = (int) res;*/ + *((int *) (var + offset * act_tuple)) = (int) res; + break; + case ECPGt_long: +/* ((long *) var)[act_tuple] = res;*/ + *((long *) (var + offset * act_tuple)) = (long) res; + break; + default: + /* Cannot happen */ + break; + } + break; + + case ECPGt_unsigned_short: + case ECPGt_unsigned_int: + case ECPGt_unsigned_long: + if (pval) + { + ures = strtoul(pval, &scan_length, 10); + if ((isarray && *scan_length != ',' && *scan_length != '}') + || (!isarray && *scan_length != '\0')) /* Garbage left */ + { + ECPGraise(lineno, ECPG_UINT_FORMAT, pval); + return (false); + } + } + else + ures = 0L; + + switch (type) + { + case ECPGt_unsigned_short: +/* ((unsigned short *) var)[act_tuple] = (unsigned short) ures;*/ + *((unsigned short *) (var + offset * act_tuple)) = (unsigned short) ures; + break; + case ECPGt_unsigned_int: +/* ((unsigned int *) var)[act_tuple] = (unsigned int) ures;*/ + *((unsigned int *) (var + offset * act_tuple)) = (unsigned int) ures; + break; + case ECPGt_unsigned_long: +/* ((unsigned long *) var)[act_tuple] = ures;*/ + *((unsigned long *) (var + offset * act_tuple)) = (unsigned long) ures; + break; + default: + /* Cannot happen */ + break; + } + break; + +#ifdef HAVE_LONG_LONG_INT_64 +#ifdef HAVE_STRTOLL + case ECPGt_long_long: + if (pval) + { +/* ((long long int *) var)[act_tuple] = strtoll(pval, &scan_length, 10);*/ + *((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10); + if ((isarray && *scan_length != ',' && *scan_length != '}') + || (!isarray && *scan_length != '\0')) /* Garbage left */ + { + ECPGraise(lineno, ECPG_INT_FORMAT, pval); + return (false); + } + } + else +/* ((long long int *) var)[act_tuple] = (long long) 0;*/ + *((long long int *) (var + offset * act_tuple)) = (long long) 0; + + break; +#endif /* HAVE_STRTOLL */ +#ifdef HAVE_STRTOULL + case ECPGt_unsigned_long_long: + if (pval) + { +/* ((unsigned long long int *) var)[act_tuple] = strtoull(pval, &scan_length, 10);*/ + *((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10); + if ((isarray && *scan_length != ',' && *scan_length != '}') + || (!isarray && *scan_length != '\0')) /* Garbage left */ + { + ECPGraise(lineno, ECPG_UINT_FORMAT, pval); + return (false); + } + } + else +/* ((unsigned long long int *) var)[act_tuple] = (long long) 0;*/ + *((unsigned long long int *) (var + offset * act_tuple)) = (long long) 0; + + break; +#endif /* HAVE_STRTOULL */ +#endif /* HAVE_LONG_LONG_INT_64 */ + + case ECPGt_float: + case ECPGt_double: + if (pval) + { + if (isarray && *pval == '"') + dres = strtod(pval + 1, &scan_length); + else + dres = strtod(pval, &scan_length); + + if (isarray && *scan_length == '"') + scan_length++; + + if ((isarray && *scan_length != ',' && *scan_length != '}') + || (!isarray && *scan_length != '\0')) /* Garbage left */ + { + ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval); + return (false); + } + } + else + dres = 0.0; + + switch (type) + { + case ECPGt_float: +/* ((float *) var)[act_tuple] = dres;*/ + *((float *) (var + offset * act_tuple)) = dres; + break; + case ECPGt_double: +/* ((double *) var)[act_tuple] = dres;*/ + *((double *) (var + offset * act_tuple)) = dres; + break; + default: + /* Cannot happen */ + break; + } + break; + + case ECPGt_bool: + if (pval) + { + if (pval[0] == 'f' && pval[1] == '\0') + { + if (offset == sizeof(char)) +/* ((char *) var)[act_tuple] = false;*/ + *((char *) (var + offset * act_tuple)) = false; + else if (offset == sizeof(int)) +/* ((int *) var)[act_tuple] = false;*/ + *((int *) (var + offset * act_tuple)) = false; + else + ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size"); + break; + } + else if (pval[0] == 't' && pval[1] == '\0') + { + if (offset == sizeof(char)) +/* ((char *) var)[act_tuple] = true;*/ + *((char *) (var + offset * act_tuple)) = true; + else if (offset == sizeof(int)) +/* ((int *) var)[act_tuple] = true;*/ + *((int *) (var + offset * act_tuple)) = true; + else + ECPGraise(lineno, ECPG_CONVERT_BOOL, "different size"); + break; + } + else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field)) + { + /* NULL is valid */ + break; + } + } + + ECPGraise(lineno, ECPG_CONVERT_BOOL, pval); + return (false); + break; + + case ECPGt_char: + case ECPGt_unsigned_char: + { + strncpy((char *) ((long) var + offset * act_tuple), pval, varcharsize); + if (varcharsize && varcharsize < strlen(pval)) + { + /* truncation */ + switch (ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: +/* ((short *) ind)[act_tuple] = strlen(pval);*/ + *((short *) (ind + ind_offset * act_tuple)) = strlen(pval); + break; + case ECPGt_int: + case ECPGt_unsigned_int: +/* ((int *) ind)[act_tuple] = strlen(pval);*/ + *((int *) (ind + ind_offset * act_tuple)) = strlen(pval); + break; + case ECPGt_long: + case ECPGt_unsigned_long: +/* ((long *) ind)[act_tuple] = strlen(pval);*/ + *((long *) (ind + ind_offset * act_tuple)) = strlen(pval); + break; +#ifdef HAVE_LONG_LONG_INT_64 + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + *((long long int *) (ind + ind_offset * act_tuple)) = strlen(pval); + break; +#endif /* HAVE_LONG_LONG_INT_64 */ + default: + break; + } + sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; + } + } + break; + + case ECPGt_varchar: + { + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) ((long) var + offset * act_tuple); + + variable->len = strlen(pval); + if (varcharsize == 0) + strncpy(variable->arr, pval, variable->len); + else + strncpy(variable->arr, pval, varcharsize); + + if (varcharsize > 0 && variable->len > varcharsize) + { + /* truncation */ + switch (ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: +/* ((short *) ind)[act_tuple] = variable->len;*/ + *((short *) (ind + offset * act_tuple)) = variable->len; + break; + case ECPGt_int: + case ECPGt_unsigned_int: +/* ((int *) ind)[act_tuple] = variable->len;*/ + *((int *) (ind + offset * act_tuple)) = variable->len; + break; + case ECPGt_long: + case ECPGt_unsigned_long: +/* ((long *) ind)[act_tuple] = variable->len;*/ + *((long *) (ind + offset * act_tuple)) = variable->len; + break; +#ifdef HAVE_LONG_LONG_INT_64 + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + *((long long int *) (ind + ind_offset * act_tuple)) = variable->len; + break; +#endif /* HAVE_LONG_LONG_INT_64 */ + default: + break; + } + sqlca.sqlwarn[0] = sqlca.sqlwarn[1] = 'W'; + + variable->len = varcharsize; + } + } + break; + + case ECPGt_numeric: + if (pval) + { + if (isarray && *pval == '"') + nres = PGTYPESnumeric_aton(pval + 1, &scan_length); + else + nres = PGTYPESnumeric_aton(pval, &scan_length); + + if (isarray && *scan_length == '"') + scan_length++; + + if ((isarray && *scan_length != ',' && *scan_length != '}') + || (!isarray && *scan_length != '\0')) /* Garbage left */ + { + ECPGraise(lineno, ECPG_FLOAT_FORMAT, pval); + return (false); + } + } + else + nres = PGTYPESnumeric_aton("0.0", &scan_length); + + PGTYPESnumeric_copy(nres, (NumericVar *)(var + offset * act_tuple)); + break; + + default: + ECPGraise(lineno, ECPG_UNSUPPORTED, ECPGtype_name(type)); + return (false); + break; + } + if (isarray) + { + bool string = false; + + /* set array to next entry */ + ++act_tuple; + + /* set pval to the next entry */ + for (; string || (*pval != ',' && *pval != '}'); ++pval) + if (*pval == '"') + string = string ? false : true; + + if (*pval == ',') + ++pval; + } + } while (isarray && *pval != '}'); + + return (true); +} diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c new file mode 100644 index 0000000000..cf8ba3435d --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/descriptor.c @@ -0,0 +1,449 @@ +/* dynamic SQL support routines + * + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/descriptor.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ + */ + +#include "postgres_fe.h" +#include "pg_type.h" + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" +#include "sqlca.h" +#include "sql3types.h" + +struct descriptor *all_descriptors = NULL; + +/* old internal convenience function that might go away later */ +static PGresult + * +ECPGresultByDescriptor(int line, const char *name) +{ + PGresult **resultpp = ECPGdescriptor_lvalue(line, name); + + if (resultpp) + return *resultpp; + return NULL; +} + +static unsigned int +ECPGDynamicType_DDT(Oid type) +{ + switch (type) + { + case DATEOID: + return SQL3_DDT_DATE; + case TIMEOID: + return SQL3_DDT_TIME; + case TIMESTAMPOID: + return SQL3_DDT_TIMESTAMP; + case TIMESTAMPTZOID: + return SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE; + case TIMETZOID: + return SQL3_DDT_TIME_WITH_TIME_ZONE; + default: + return SQL3_DDT_ILLEGAL; + } +} + +bool +ECPGget_desc_header(int lineno, char *desc_name, int *count) +{ + PGresult *ECPGresult; + + ECPGinit_sqlca(); + ECPGresult = ECPGresultByDescriptor(lineno, desc_name); + if (!ECPGresult) + return false; + + *count = PQnfields(ECPGresult); + sqlca.sqlerrd[2] = 1; + ECPGlog("ECPGget_desc_header: found %d attributes.\n", *count); + return true; +} + +static bool +get_int_item(int lineno, void *var, enum ECPGttype vartype, int value) +{ + switch (vartype) + { + case ECPGt_short: + *(short *) var = (short) value; + break; + case ECPGt_int: + *(int *) var = (int) value; + break; + case ECPGt_long: + *(long *) var = (long) value; + break; + case ECPGt_unsigned_short: + *(unsigned short *) var = (unsigned short) value; + break; + case ECPGt_unsigned_int: + *(unsigned int *) var = (unsigned int) value; + break; + case ECPGt_unsigned_long: + *(unsigned long *) var = (unsigned long) value; + break; +#ifdef HAVE_LONG_LONG_INT_64 + case ECPGt_long_long: + *(long long int *) var = (long long int) value; + break; + case ECPGt_unsigned_long_long: + *(unsigned long long int *) var = (unsigned long long int) value; + break; +#endif /* HAVE_LONG_LONG_INT_64 */ + case ECPGt_float: + *(float *) var = (float) value; + break; + case ECPGt_double: + *(double *) var = (double) value; + break; + default: + ECPGraise(lineno, ECPG_VAR_NOT_NUMERIC, NULL); + return (false); + } + + return (true); +} + +static bool +get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int varcharsize) +{ + switch (vartype) + { + case ECPGt_char: + case ECPGt_unsigned_char: + strncpy((char *) var, value, varcharsize); + break; + case ECPGt_varchar: + { + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) var; + + if (varcharsize == 0) + strncpy(variable->arr, value, strlen(value)); + else + strncpy(variable->arr, value, varcharsize); + + variable->len = strlen(value); + if (varcharsize > 0 && variable->len > varcharsize) + variable->len = varcharsize; + } + break; + default: + ECPGraise(lineno, ECPG_VAR_NOT_CHAR, NULL); + return (false); + } + + return (true); +} + +bool +ECPGget_desc(int lineno, char *desc_name, int index,...) +{ + va_list args; + PGresult *ECPGresult; + enum ECPGdtype type; + int ntuples, + act_tuple; + struct variable data_var; + + va_start(args, index); + ECPGinit_sqlca(); + ECPGresult = ECPGresultByDescriptor(lineno, desc_name); + if (!ECPGresult) + return (false); + + ntuples = PQntuples(ECPGresult); + if (ntuples < 1) + { + ECPGraise(lineno, ECPG_NOT_FOUND, NULL); + return (false); + } + + if (index < 1 || index > PQnfields(ECPGresult)) + { + ECPGraise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, NULL); + return (false); + } + + ECPGlog("ECPGget_desc: reading items for tuple %d\n", index); + --index; + + type = va_arg(args, enum ECPGdtype); + + memset(&data_var, 0, sizeof data_var); + data_var.type = ECPGt_EORT; + data_var.ind_type = ECPGt_NO_INDICATOR; + + while (type != ECPGd_EODT) + { + char type_str[20]; + long varcharsize; + long offset; + long arrsize; + enum ECPGttype vartype; + void *var; + + vartype = va_arg(args, enum ECPGttype); + var = va_arg(args, void *); + varcharsize = va_arg(args, long); + arrsize = va_arg(args, long); + offset = va_arg(args, long); + + switch (type) + { + case (ECPGd_indicator): + data_var.ind_type = vartype; + data_var.ind_pointer = var; + data_var.ind_varcharsize = varcharsize; + data_var.ind_arrsize = arrsize; + data_var.ind_offset = offset; + if (data_var.ind_arrsize == 0 || data_var.ind_varcharsize == 0) + data_var.ind_value = *((void **) (data_var.ind_pointer)); + else + data_var.ind_value = data_var.ind_pointer; + break; + + case ECPGd_data: + data_var.type = vartype; + data_var.pointer = var; + data_var.varcharsize = varcharsize; + data_var.arrsize = arrsize; + data_var.offset = offset; + if (data_var.arrsize == 0 || data_var.varcharsize == 0) + data_var.value = *((void **) (data_var.pointer)); + else + data_var.value = data_var.pointer; + break; + + case ECPGd_name: + if (!get_char_item(lineno, var, vartype, PQfname(ECPGresult, index), varcharsize)) + return (false); + + ECPGlog("ECPGget_desc: NAME = %s\n", PQfname(ECPGresult, index)); + break; + + case ECPGd_nullable: + if (!get_int_item(lineno, var, vartype, 1)) + return (false); + + break; + + case ECPGd_key_member: + if (!get_int_item(lineno, var, vartype, 0)) + return (false); + + break; + + case ECPGd_scale: + if (!get_int_item(lineno, var, vartype, (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff)) + return (false); + + ECPGlog("ECPGget_desc: SCALE = %d\n", (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff); + break; + + case ECPGd_precision: + if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) >> 16)) + return (false); + + ECPGlog("ECPGget_desc: PRECISION = %d\n", PQfmod(ECPGresult, index) >> 16); + break; + + case ECPGd_octet: + if (!get_int_item(lineno, var, vartype, PQfsize(ECPGresult, index))) + return (false); + + ECPGlog("ECPGget_desc: OCTET_LENGTH = %d\n", PQfsize(ECPGresult, index)); + break; + + case ECPGd_length: + if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) - VARHDRSZ)) + return (false); + + ECPGlog("ECPGget_desc: LENGTH = %d\n", PQfmod(ECPGresult, index) - VARHDRSZ); + break; + + case ECPGd_type: + if (!get_int_item(lineno, var, vartype, ECPGDynamicType(PQftype(ECPGresult, index)))) + return (false); + + ECPGlog("ECPGget_desc: TYPE = %d\n", ECPGDynamicType(PQftype(ECPGresult, index))); + break; + + case ECPGd_di_code: + if (!get_int_item(lineno, var, vartype, ECPGDynamicType_DDT(PQftype(ECPGresult, index)))) + return (false); + + ECPGlog("ECPGget_desc: TYPE = %d\n", ECPGDynamicType_DDT(PQftype(ECPGresult, index))); + break; + + case ECPGd_cardinality: + if (!get_int_item(lineno, var, vartype, PQntuples(ECPGresult))) + return (false); + + ECPGlog("ECPGget_desc: CARDINALITY = %d\n", PQntuples(ECPGresult)); + break; + + case ECPGd_ret_length: + case ECPGd_ret_octet: + + /* + * this is like ECPGstore_result + */ + if (arrsize > 0 && ntuples > arrsize) + { + ECPGlog("ECPGget_desc line %d: Incorrect number of matches: %d don't fit into array of %d\n", + lineno, ntuples, arrsize); + ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL); + return false; + } + /* allocate storage if needed */ + if (arrsize == 0 && var != NULL && *(void **) var == NULL) + { + void *mem = (void *) ECPGalloc(offset * ntuples, lineno); + + *(void **) var = mem; + ECPGadd_mem(mem, lineno); + var = mem; + } + + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + { + if (!get_int_item(lineno, var, vartype, PQgetlength(ECPGresult, act_tuple, index))) + return (false); + var = (char *) var + offset; + ECPGlog("ECPGget_desc: RETURNED[%d] = %d\n", act_tuple, PQgetlength(ECPGresult, act_tuple, index)); + } + break; + + default: + snprintf(type_str, sizeof(type_str), "%d", type); + ECPGraise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, type_str); + return (false); + } + + type = va_arg(args, enum ECPGdtype); + } + + if (data_var.type != ECPGt_EORT) + { + struct statement stmt; + char *oldlocale; + + /* Make sure we do NOT honor the locale for numeric input */ + /* since the database gives the standard decimal point */ + oldlocale = strdup(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "C"); + + memset(&stmt, 0, sizeof stmt); + stmt.lineno = lineno; + + /* desparate try to guess something sensible */ + stmt.connection = ECPGget_connection(NULL); + ECPGstore_result(ECPGresult, index, &stmt, &data_var); + + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + } + else if (data_var.ind_type != ECPGt_NO_INDICATOR) + { + /* + * this is like ECPGstore_result but since we don't have a data + * variable at hand, we can't call it + */ + if (data_var.ind_arrsize > 0 && ntuples > data_var.ind_arrsize) + { + ECPGlog("ECPGget_desc line %d: Incorrect number of matches (indicator): %d don't fit into array of %d\n", + lineno, ntuples, data_var.ind_arrsize); + ECPGraise(lineno, ECPG_TOO_MANY_MATCHES, NULL); + return false; + } + /* allocate storage if needed */ + if (data_var.ind_arrsize == 0 && data_var.ind_pointer != NULL && data_var.ind_value == NULL) + { + void *mem = (void *) ECPGalloc(data_var.ind_offset * ntuples, lineno); + + *(void **) data_var.ind_pointer = mem; + ECPGadd_mem(mem, lineno); + data_var.ind_value = mem; + } + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + { + if (!get_int_item(lineno, data_var.ind_value, data_var.ind_type, -PQgetisnull(ECPGresult, act_tuple, index))) + return (false); + data_var.ind_value = (char *) data_var.ind_value + data_var.ind_offset; + ECPGlog("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index)); + } + } + sqlca.sqlerrd[2] = ntuples; + return (true); +} + +bool +ECPGdeallocate_desc(int line, const char *name) +{ + struct descriptor *i; + struct descriptor **lastptr = &all_descriptors; + + ECPGinit_sqlca(); + for (i = all_descriptors; i; lastptr = &i->next, i = i->next) + { + if (!strcmp(name, i->name)) + { + *lastptr = i->next; + ECPGfree(i->name); + PQclear(i->result); + ECPGfree(i); + return true; + } + } + ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, name); + return false; +} + +bool +ECPGallocate_desc(int line, const char *name) +{ + struct descriptor *new; + + ECPGinit_sqlca(); + new = (struct descriptor *) ECPGalloc(sizeof(struct descriptor), line); + if (!new) + return false; + new->next = all_descriptors; + new->name = ECPGalloc(strlen(name) + 1, line); + if (!new->name) + { + ECPGfree(new); + return false; + } + new->result = PQmakeEmptyPGresult(NULL, 0); + if (!new->result) + { + ECPGfree(new->name); + ECPGfree(new); + ECPGraise(line, ECPG_OUT_OF_MEMORY, NULL); + return false; + } + strcpy(new->name, name); + all_descriptors = new; + return true; +} + +PGresult ** +ECPGdescriptor_lvalue(int line, const char *descriptor) +{ + struct descriptor *i; + + for (i = all_descriptors; i != NULL; i = i->next) + { + if (!strcmp(descriptor, i->name)) + return &i->result; + } + + ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, (char *) descriptor); + return NULL; +} diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c new file mode 100644 index 0000000000..578e7c5603 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/error.c @@ -0,0 +1,198 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/error.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include + +#include "ecpgerrno.h" +#include "ecpgtype.h" +#include "ecpglib.h" +#include "extern.h" +#include "sqlca.h" + +/* This should hold the back-end error message from + * the last back-end operation. */ +static char *ECPGerr; + +void +ECPGraise(int line, int code, const char *str) +{ + sqlca.sqlcode = code; + + switch (code) + { + case ECPG_NOT_FOUND: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "No data found in line %d.", line); + break; + + case ECPG_OUT_OF_MEMORY: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Out of memory in line %d.", line); + break; + + case ECPG_UNSUPPORTED: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Unsupported type %s in line %d.", str, line); + break; + + case ECPG_TOO_MANY_ARGUMENTS: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Too many arguments in line %d.", line); + break; + + case ECPG_TOO_FEW_ARGUMENTS: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Too few arguments in line %d.", line); + break; + + case ECPG_INT_FORMAT: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Not correctly formatted int type: %s line %d.", str, line); + break; + + case ECPG_UINT_FORMAT: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Not correctly formatted unsigned type: %s in line %d.", str, line); + break; + + case ECPG_FLOAT_FORMAT: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Not correctly formatted floating-point type: %s in line %d.", str, line); + break; + + case ECPG_CONVERT_BOOL: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Unable to convert %s to bool on line %d.", str, line); + break; + + case ECPG_EMPTY: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Empty query in line %d.", line); + break; + + case ECPG_MISSING_INDICATOR: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "NULL value without indicator in line %d.", line); + break; + + case ECPG_NO_ARRAY: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Variable is not an array in line %d.", line); + break; + + case ECPG_DATA_NOT_ARRAY: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Data read from backend is not an array in line %d.", line); + break; + + case ECPG_ARRAY_INSERT: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Trying to insert an array of variables in line %d.", line); + break; + + case ECPG_NO_CONN: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "No such connection %s in line %d.", str, line); + break; + + case ECPG_NOT_CONN: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Not connected to '%s' in line %d.", str, line); + break; + + case ECPG_INVALID_STMT: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Invalid statement name %s in line %d.", str, line); + break; + + case ECPG_UNKNOWN_DESCRIPTOR: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Descriptor %s not found in line %d.", str, line); + break; + + case ECPG_INVALID_DESCRIPTOR_INDEX: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Descriptor index out of range in line %d.", line); + break; + + case ECPG_UNKNOWN_DESCRIPTOR_ITEM: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Unknown descriptor item %s in line %d.", str, line); + break; + + case ECPG_VAR_NOT_NUMERIC: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Variable is not a numeric type in line %d.", line); + break; + + case ECPG_VAR_NOT_CHAR: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Variable is not a character type in line %d.", line); + break; + + case ECPG_PGSQL: + { + int slen = strlen(str); + + /* strip trailing newline */ + if (slen > 0 && str[slen - 1] == '\n') + slen--; + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "'%.*s' in line %d.", slen, str, line); + break; + } + + case ECPG_TRANS: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Error in transaction processing in line %d.", line); + break; + + case ECPG_CONNECT: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "Could not connect to database %s in line %d.", str, line); + break; + + default: + snprintf(sqlca.sqlerrm.sqlerrmc, sizeof(sqlca.sqlerrm.sqlerrmc), + "SQL error #%d in line %d.", code, line); + break; + } + + sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); + ECPGlog("raising sqlcode %d in line %d, '%s'.\n", code, line, sqlca.sqlerrm.sqlerrmc); + + /* free all memory we have allocated for the user */ + ECPGfree_auto_mem(); +} + +/* Set the error message string from the backend */ +void +set_backend_err(const char *err, int lineno) +{ + if (ECPGerr) + ECPGfree(ECPGerr); + + if (!err) + { + ECPGerr = NULL; + return; + } + + ECPGerr = ECPGstrdup(err, lineno); +} + +/* Retrieve the error message from the backend. */ +char * +ECPGerrmsg(void) +{ + return ECPGerr; +} + +/* print out an error message */ +void +sqlprint(void) +{ + sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0'; + fprintf(stderr, "sql error %s\n", sqlca.sqlerrm.sqlerrmc); +} diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c new file mode 100644 index 0000000000..4ed4b8fde4 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -0,0 +1,1184 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +/* + * The aim is to get a simpler inteface to the database routines. + * All the tidieous messing around with tuples is supposed to be hidden + * by this function. + */ +/* Author: Linus Tolke + (actually most if the code is "borrowed" from the distribution and just + slightly modified) + */ + +/* Taken over as part of PostgreSQL by Michael Meskes + on Feb. 5th, 1998 */ + +#include "postgres_fe.h" + +#include +#include + +#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 : ""); + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + return false; + } + + /* initialize auto_mem struct */ + ECPGclear_auto_mem(); + + status = ECPGexecute(stmt); + free_statement(stmt); + + /* and reset locale value so our application is not affected */ + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + + return (status); +} + +/* old descriptor interface */ +bool +ECPGdo_descriptor(int line, const char *connection, + const char *descriptor, const char *query) +{ + return ECPGdo(line, connection, (char *) query, ECPGt_EOIT, + ECPGt_descriptor, descriptor, 0L, 0L, 0L, + ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT); +} diff --git a/src/interfaces/ecpg/ecpglib/extern.h b/src/interfaces/ecpg/ecpglib/extern.h new file mode 100644 index 0000000000..a5014512c9 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/extern.h @@ -0,0 +1,103 @@ +#ifndef _ECPG_LIB_EXTERN_H +#define _ECPG_LIB_EXTERN_H + +#include "postgres_fe.h" +#include "libpq-fe.h" + +/* Here are some methods used by the lib. */ + +/* Stores the backend error message for client access */ +void set_backend_err(const char *err, int lineon); + +/* Store and retrieve the backend error message for client access */ +void set_backend_err(const char *err, int lineon); +char *ECPGerrmsg(void); + +/* Returns a pointer to a string containing a simple type name. */ +void ECPGadd_mem(void *ptr, int lineno); + +bool ECPGget_data(const PGresult *, int, int, int, enum ECPGttype type, + enum ECPGttype, char *, char *, long, long, long, bool); +struct connection *ECPGget_connection(const char *); +void ECPGinit_sqlca(void); +char *ECPGalloc(long, int); +char *ECPGrealloc(void *, long, int); +void ECPGfree(void *); +bool ECPGinit(const struct connection *, const char *, const int); +char *ECPGstrdup(const char *, int); +const char *ECPGtype_name(enum ECPGttype); +unsigned int ECPGDynamicType(Oid); +void ECPGfree_auto_mem(void); +void ECPGclear_auto_mem(void); + +/* A generic varchar type. */ +struct ECPGgeneric_varchar +{ + int len; + char arr[1]; +}; + +/* + * type information cache + */ + +struct ECPGtype_information_cache +{ + struct ECPGtype_information_cache *next; + int oid; + bool isarray; +}; + +/* structure to store one statement */ +struct statement +{ + int lineno; + char *command; + struct connection *connection; + struct variable *inlist; + struct variable *outlist; +}; + +/* structure to store connections */ +struct connection +{ + char *name; + PGconn *connection; + bool committed; + int autocommit; + struct ECPGtype_information_cache *cache_head; + struct connection *next; +}; + +/* structure to store descriptors */ +struct descriptor +{ + char *name; + PGresult *result; + struct descriptor *next; +}; + +struct variable +{ + enum ECPGttype type; + void *value; + void *pointer; + long varcharsize; + long arrsize; + long offset; + enum ECPGttype ind_type; + void *ind_value; + void *ind_pointer; + long ind_varcharsize; + long ind_arrsize; + long ind_offset; + struct variable *next; +}; + +PGresult ** + ECPGdescriptor_lvalue(int line, const char *descriptor); + +bool ECPGstore_result(const PGresult *results, int act_field, + const struct statement * stmt, struct variable * var); + +#endif /* _ECPG_LIB_EXTERN_H */ diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c new file mode 100644 index 0000000000..a94b293de4 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/memory.c @@ -0,0 +1,109 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/memory.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" + +void +ECPGfree(void *ptr) +{ + free(ptr); +} + +char * +ECPGalloc(long size, int lineno) +{ + char *new = (char *) calloc(1L, size); + + if (!new) + { + ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL); + return NULL; + } + + memset(new, '\0', size); + return (new); +} + +char * +ECPGrealloc(void *ptr, long size, int lineno) +{ + char *new = (char *) realloc(ptr, size); + + if (!new) + { + ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL); + return NULL; + } + + return (new); +} + +char * +ECPGstrdup(const char *string, int lineno) +{ + char *new = strdup(string); + + if (!new) + { + ECPGraise(lineno, ECPG_OUT_OF_MEMORY, NULL); + return NULL; + } + + return (new); +} + +/* keep a list of memory we allocated for the user */ +static struct auto_mem +{ + void *pointer; + struct auto_mem *next; +} *auto_allocs = NULL; + +void +ECPGadd_mem(void *ptr, int lineno) +{ + struct auto_mem *am = (struct auto_mem *) ECPGalloc(sizeof(struct auto_mem), lineno); + + am->pointer = ptr; + am->next = auto_allocs; + auto_allocs = am; +} + +void +ECPGfree_auto_mem(void) +{ + struct auto_mem *am; + + /* free all memory we have allocated for the user */ + for (am = auto_allocs; am;) + { + struct auto_mem *act = am; + + am = am->next; + ECPGfree(act->pointer); + ECPGfree(act); + } + + auto_allocs = NULL; +} + +void +ECPGclear_auto_mem(void) +{ + struct auto_mem *am; + + /* free just our own structure */ + for (am = auto_allocs; am;) + { + struct auto_mem *act = am; + + am = am->next; + ECPGfree(act); + } + + auto_allocs = NULL; +} diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c new file mode 100644 index 0000000000..4e2f92d178 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/misc.c @@ -0,0 +1,149 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/misc.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" +#include "sqlca.h" + +static struct sqlca sqlca_init = +{ + { + 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' ' + }, + sizeof(struct sqlca), + 0, + { + 0, + { + 0 + } + }, + { + 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' ' + }, + { + 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + } +}; + +static int simple_debug = 0; +static FILE *debugstream = NULL; + +void +ECPGinit_sqlca(void) +{ + memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); +} + +bool +ECPGinit(const struct connection * con, const char *connection_name, const int lineno) +{ + ECPGinit_sqlca(); + if (con == NULL) + { + ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL"); + return (false); + } + + return (true); +} + +bool +ECPGstatus(int lineno, const char *connection_name) +{ + struct connection *con = ECPGget_connection(connection_name); + + if (!ECPGinit(con, connection_name, lineno)) + return (false); + + /* are we connected? */ + if (con->connection == NULL) + { + ECPGraise(lineno, ECPG_NOT_CONN, con->name); + return false; + } + + return (true); +} + +bool +ECPGtrans(int lineno, const char *connection_name, const char *transaction) +{ + PGresult *res; + struct connection *con = ECPGget_connection(connection_name); + + if (!ECPGinit(con, connection_name, lineno)) + return (false); + + ECPGlog("ECPGtrans line %d action = %s connection = %s\n", lineno, transaction, con->name); + + /* if we have no connection we just simulate the command */ + if (con && con->connection) + { + /* + * if we are not in autocommit mode, already have committed the + * transaction and get another commit, just ignore it + */ + if (!con->committed || con->autocommit) + { + if ((res = PQexec(con->connection, transaction)) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return FALSE; + } + PQclear(res); + } + } + + if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0) + { + con->committed = true; + + /* deallocate all prepared statements */ + if (!ECPGdeallocate_all(lineno)) + return false; + } + + return true; +} + + +void +ECPGdebug(int n, FILE *dbgs) +{ + simple_debug = n; + debugstream = dbgs; + ECPGlog("ECPGdebug: set to %d\n", simple_debug); +} + +void +ECPGlog(const char *format,...) +{ + va_list ap; + + if (simple_debug) + { + char *f = (char *) malloc(strlen(format) + 100); + + if (!f) + return; + + sprintf(f, "[%d]: %s", (int) getpid(), format); + + va_start(ap, format); + vfprintf(debugstream, f, ap); + va_end(ap); + + ECPGfree(f); + } +} diff --git a/src/interfaces/ecpg/ecpglib/pg_type.h b/src/interfaces/ecpg/ecpglib/pg_type.h new file mode 100644 index 0000000000..0fb3e8b6e3 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/pg_type.h @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------- + * + * pg_type.h + * definition of the system "type" relation (pg_type) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Id: pg_type.h,v 1.1 2003/03/16 10:42:53 meskes Exp $ + * + * NOTES + * the genbki.sh script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_TYPE_H +#define PG_TYPE_H + +/* ---------------- + * initial contents of pg_type + * ---------------- + */ + +/* keep the following ordered by OID so that later changes can be made easier*/ + +/* OIDS 1 - 99 */ +#define BOOLOID 16 +#define BYTEAOID 17 +#define CHAROID 18 +#define NAMEOID 19 +#define INT8OID 20 +#define INT2OID 21 +#define INT2VECTOROID 22 +#define INT4OID 23 +#define REGPROCOID 24 +#define TEXTOID 25 +#define OIDOID 26 +#define TIDOID 27 +#define XIDOID 28 +#define CIDOID 29 +#define OIDVECTOROID 30 +#define POINTOID 600 +#define LSEGOID 601 +#define PATHOID 602 +#define BOXOID 603 +#define POLYGONOID 604 +#define LINEOID 628 +#define FLOAT4OID 700 +#define FLOAT8OID 701 +#define ABSTIMEOID 702 +#define RELTIMEOID 703 +#define TINTERVALOID 704 +#define UNKNOWNOID 705 +#define CIRCLEOID 718 +#define CASHOID 790 +#define INETOID 869 +#define CIDROID 650 +#define BPCHAROID 1042 +#define VARCHAROID 1043 +#define DATEOID 1082 +#define TIMEOID 1083 +#define TIMESTAMPOID 1114 +#define TIMESTAMPTZOID 1184 +#define INTERVALOID 1186 +#define TIMETZOID 1266 +#define ZPBITOID 1560 +#define VARBITOID 1562 +#define NUMERICOID 1700 + +#endif /* PG_TYPE_H */ diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c new file mode 100644 index 0000000000..13dd2408f2 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/prepare.c @@ -0,0 +1,155 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/prepare.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" +#include "sqlca.h" + +static struct prepared_statement +{ + char *name; + struct statement *stmt; + struct prepared_statement *next; +} *prep_stmts = NULL; + +static bool +isvarchar(unsigned char c) +{ + if (isalnum(c)) + return true; + + if (c == '_' || c == '>' || c == '-' || c == '.') + return true; + + if (c >= 128) + return true; + + return (false); +} + +static void +replace_variables(char *text) +{ + char *ptr = text; + bool string = false; + + for (; *ptr != '\0'; ptr++) + { + if (*ptr == '\'') + string = string ? false : true; + + if (!string && *ptr == ':') + { + *ptr = '?'; + for (++ptr; *ptr && isvarchar(*ptr); ptr++) + *ptr = ' '; + } + } +} + +/* handle the EXEC SQL PREPARE statement */ +bool +ECPGprepare(int lineno, char *name, char *variable) +{ + struct statement *stmt; + struct prepared_statement *this; + + /* check if we already have prepared this statement */ + for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); + if (this) + { + bool b = ECPGdeallocate(lineno, name); + + if (!b) + return false; + } + + this = (struct prepared_statement *) ECPGalloc(sizeof(struct prepared_statement), lineno); + if (!this) + return false; + + stmt = (struct statement *) ECPGalloc(sizeof(struct statement), lineno); + if (!stmt) + { + ECPGfree(this); + return false; + } + + /* create statement */ + stmt->lineno = lineno; + stmt->connection = NULL; + stmt->command = ECPGstrdup(variable, lineno); + stmt->inlist = stmt->outlist = NULL; + + /* if we have C variables in our statment replace them with '?' */ + replace_variables(stmt->command); + + /* add prepared statement to our list */ + this->name = ECPGstrdup(name, lineno); + this->stmt = stmt; + + if (prep_stmts == NULL) + this->next = NULL; + else + this->next = prep_stmts; + + prep_stmts = this; + return true; +} + +/* handle the EXEC SQL DEALLOCATE PREPARE statement */ +bool +ECPGdeallocate(int lineno, char *name) +{ + struct prepared_statement *this, + *prev; + + /* check if we really have prepared this statement */ + for (this = prep_stmts, prev = NULL; this != NULL && strcmp(this->name, name) != 0; prev = this, this = this->next); + if (this) + { + /* okay, free all the resources */ + ECPGfree(this->name); + ECPGfree(this->stmt->command); + ECPGfree(this->stmt); + if (prev != NULL) + prev->next = this->next; + else + prep_stmts = this->next; + + ECPGfree(this); + return true; + } + ECPGraise(lineno, ECPG_INVALID_STMT, name); + return false; +} + +bool +ECPGdeallocate_all(int lineno) +{ + /* deallocate all prepared statements */ + while (prep_stmts != NULL) + { + bool b = ECPGdeallocate(lineno, prep_stmts->name); + + if (!b) + return false; + } + + return true; +} + +/* return the prepared statement */ +char * +ECPGprepared_statement(char *name) +{ + struct prepared_statement *this; + + for (this = prep_stmts; this != NULL && strcmp(this->name, name) != 0; this = this->next); + return (this) ? this->stmt->command : NULL; +} diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c new file mode 100644 index 0000000000..c970f76724 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/typename.c @@ -0,0 +1,90 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/typename.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +#include "postgres_fe.h" + +#include +#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; + } +} diff --git a/src/interfaces/ecpg/include/Makefile b/src/interfaces/ecpg/include/Makefile index 6b9e39d1f3..9b40e0368c 100644 --- a/src/interfaces/ecpg/include/Makefile +++ b/src/interfaces/ecpg/include/Makefile @@ -5,7 +5,7 @@ include $(top_builddir)/src/Makefile.global install: all installdirs install-headers .PHONY: install-headers -ecpg_headers = ecpgerrno.h ecpglib.h ecpgtype.h sqlca.h sql3types.h ecpg_informix.h +ecpg_headers = ecpgerrno.h ecpglib.h ecpgtype.h sqlca.h sql3types.h ecpg_informix.h pgtypes_error.h pgtypes_numeric.h install-headers: $(ecpg_headers) for i in $^; do $(INSTALL_DATA) $$i $(DESTDIR)$(includedir); done diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h index 5520ff882f..6ed5f5d3e4 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -11,7 +11,7 @@ * that is registered and that has nothing whatsoever to do with the storage * class. * - * Simle types + * Simple types * integers: char, short, int, long (signed and unsigned) * floats: float, double * @@ -51,7 +51,8 @@ enum ECPGttype ECPGt_EORT, /* End of result types. */ ECPGt_NO_INDICATOR, /* no indicator */ ECPGt_long_long, ECPGt_unsigned_long_long, - ECPGt_descriptor /* sql descriptor, no C variable */ + ECPGt_descriptor, /* sql descriptor, no C variable */ + ECPGt_numeric }; /* descriptor items */ @@ -76,7 +77,7 @@ enum ECPGdtype ECPGd_cardinality }; -#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_varchar2) || ((type)>=ECPGt_long_long && (type) <= ECPGt_unsigned_long_long)) +#define IS_SIMPLE_TYPE(type) (((type) >= ECPGt_char && (type) <= ECPGt_varchar2) || ((type)>=ECPGt_long_long && (type) <= ECPGt_unsigned_long_long) || (type) >= ECPGt_numeric) #ifdef __cplusplus } diff --git a/src/interfaces/ecpg/pgtypeslib/Makefile b/src/interfaces/ecpg/pgtypeslib/Makefile new file mode 100644 index 0000000000..73d174e3d2 --- /dev/null +++ b/src/interfaces/ecpg/pgtypeslib/Makefile @@ -0,0 +1,43 @@ +#------------------------------------------------------------------------- +# +# Makefile for ecpg library +# +# Copyright (c) 1994, Regents of the University of California +# +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/pgtypeslib/Makefile,v 1.1 2003/03/16 10:42:54 meskes Exp $ +# +#------------------------------------------------------------------------- + +subdir = src/interfaces/ecpg/pgtypeslib +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +NAME= pgtypes +SO_MAJOR_VERSION= 1 +SO_MINOR_VERSION= 0.0 + +override CPPFLAGS := -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(top_srcdir)/src/include/utils $(CPPFLAGS) + +OBJS= numeric.o + +all: all-lib + +# Shared library stuff +include $(top_srcdir)/src/Makefile.shlib + +install: all installdirs install-lib + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + +uninstall: uninstall-lib + +clean distclean maintainer-clean: clean-lib + rm -f $(OBJS) + +depend dep: + $(CC) -MM $(CFLAGS) *.c >depend + +ifeq (depend,$(wildcard depend)) +include depend +endif diff --git a/src/interfaces/ecpg/pgtypeslib/numeric.c b/src/interfaces/ecpg/pgtypeslib/numeric.c new file mode 100644 index 0000000000..bd6c950d3f --- /dev/null +++ b/src/interfaces/ecpg/pgtypeslib/numeric.c @@ -0,0 +1,1715 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index b09b1f7c41..7d267b2657 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.61 2003/03/10 22:28:21 tgl Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/ecpg.c,v 1.62 2003/03/16 10:42:54 meskes Exp $ */ /* New main for ecpg, the PostgreSQL embedded SQL precompiler. */ /* (C) Michael Meskes Feb 5th, 1998 */ @@ -313,7 +313,7 @@ main(int argc, char *const argv[]) lex_init(); /* we need several includes */ - fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These four include files are added by the preprocessor */\n#include \n#include \n#include \n#include \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 \n#include \n#include \n#include \n#include \n#line 1 \"%s\"\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL, input_filename); /* add some compatibility headers */ if (compat == ECPG_COMPAT_INFORMIX) diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index bf2ce183ba..42c8799ea7 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -1,4 +1,4 @@ -/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.211 2003/02/25 15:58:03 meskes Exp $ */ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/preproc/Attic/preproc.y,v 1.212 2003/03/16 10:42:54 meskes Exp $ */ /* Copyright comment */ %{ @@ -211,7 +211,7 @@ make_name(void) KEY - LANCOMPILER LANGUAGE LAST LEADING LEFT LEVEL LIKE LIMIT LISTEN + LANCOMPILER LANGUAGE LEADING LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCATION LOCK_P MATCH MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE @@ -303,7 +303,7 @@ make_name(void) %type Typename SimpleTypename Numeric opt_float opt_numeric %type opt_decimal Character character opt_varying opt_charset %type opt_collate opt_timezone opt_interval table_ref -%type row_descriptor ConstDatetime AlterDomainStmt +%type row_descriptor ConstDatetime AlterDomainStmt %type SelectStmt into_clause OptTemp ConstraintAttributeSpec %type opt_table opt_all sort_clause sortby_list ConstraintAttr %type sortby OptUseOp qualified_name_list name_list ColId_or_Sconst @@ -390,7 +390,7 @@ make_name(void) %type ECPGAllocateDescr ECPGDeallocateDescr symbol opt_symbol %type ECPGGetDescriptorHeader ECPGColLabel single_var_declaration %type reserved_keyword unreserved_keyword -%type col_name_keyword func_name_keyword +%type col_name_keyword func_name_keyword precision opt_scale %type ECPGTypeName variablelist ECPGColLabelCommon %type ECPGGetDescriptor @@ -399,11 +399,11 @@ make_name(void) %type descriptor_item desc_header_item -%type type common_type single_vt_type +%type var_type common_type single_vt_type %type action -%type opt_array_bounds opt_type_array_bounds +%type opt_array_bounds %type Iresult @@ -4158,11 +4158,11 @@ single_var_declaration: storage_declaration /* we do not need the string "varchar" for output */ /* so replace it with an empty string */ - if ($2.type_enum == ECPGt_varchar) + /* if ($2.type_enum == ECPGt_varchar) { free($2.type_str); $2.type_str=EMPTY; - } + }*/ } variable_list ';' { @@ -4170,6 +4170,12 @@ single_var_declaration: storage_declaration } ; +precision: NumConst { $$ = $1; }; + +opt_scale: ',' NumConst { $$ = $2; } + | /* EMPTY */ { $$ = EMPTY; } + ; + single_vt_type: common_type | ECPGColLabelCommon { @@ -4180,7 +4186,7 @@ single_vt_type: common_type if (strcmp($1, "varchar") == 0) { $$.type_enum = ECPGt_varchar; - $$.type_str = make_str("varchar"); + $$.type_str = EMPTY; $$.type_dimension = -1; $$.type_index = -1; $$.type_sizeof = NULL; @@ -4201,6 +4207,22 @@ single_vt_type: common_type $$.type_index = -1; $$.type_sizeof = NULL; } + else if (strcmp($1, "numeric") == 0) + { + $$.type_enum = ECPGt_numeric; + $$.type_str = EMPTY; + $$.type_dimension = -1; + $$.type_index = -1; + $$.type_sizeof = NULL; + } + else if (strcmp($1, "decimal") == 0) + { + $$.type_enum = ECPGt_numeric; + $$.type_str = EMPTY; + $$.type_dimension = -1; + $$.type_index = -1; + $$.type_sizeof = NULL; + } else { /* this is for typedef'ed types */ @@ -4214,6 +4236,17 @@ single_vt_type: common_type struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list); } } + | ECPGColLabelCommon '(' precision opt_scale ')' + { + if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0) + mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument"); + + $$.type_enum = ECPGt_numeric; + $$.type_str = EMPTY; + $$.type_dimension = -1; + $$.type_index = -1; + $$.type_sizeof = NULL; + } ; /* @@ -4253,7 +4286,7 @@ type_declaration: S_TYPEDEF /* an initializer specified */ initializer = 0; } - type opt_pointer ECPGColLabel opt_type_array_bounds ';' + var_type opt_pointer ECPGColLabel opt_array_bounds ';' { /* add entry to list */ struct typedefs *ptr, *this; @@ -4310,7 +4343,7 @@ type_declaration: S_TYPEDEF }; var_declaration: storage_declaration - type + var_type { actual_type[struct_level].type_enum = $2.type_enum; actual_type[struct_level].type_dimension = $2.type_dimension; @@ -4319,11 +4352,11 @@ var_declaration: storage_declaration /* we do not need the string "varchar" for output */ /* so replace it with an empty string */ - if ($2.type_enum == ECPGt_varchar) + /* if ($2.type_enum == ECPGt_varchar) { free($2.type_str); $2.type_str=EMPTY; - } + }*/ } variable_list ';' { @@ -4384,7 +4417,7 @@ common_type: simple_type } ; -type: common_type +var_type: common_type | ECPGColLabel { /* @@ -4394,7 +4427,7 @@ type: common_type if (strcmp($1, "varchar") == 0) { $$.type_enum = ECPGt_varchar; - $$.type_str = make_str("varchar"); + $$.type_str = EMPTY; /*make_str("varchar");*/ $$.type_dimension = -1; $$.type_index = -1; $$.type_sizeof = NULL; @@ -4415,6 +4448,22 @@ type: common_type $$.type_index = -1; $$.type_sizeof = NULL; } + else if (strcmp($1, "numeric") == 0) + { + $$.type_enum = ECPGt_numeric; + $$.type_str = EMPTY; + $$.type_dimension = -1; + $$.type_index = -1; + $$.type_sizeof = NULL; + } + else if (strcmp($1, "decimal") == 0) + { + $$.type_enum = ECPGt_numeric; + $$.type_str = EMPTY; + $$.type_dimension = -1; + $$.type_index = -1; + $$.type_sizeof = NULL; + } else { /* this is for typedef'ed types */ @@ -4428,6 +4477,17 @@ type: common_type struct_member_list[struct_level] = ECPGstruct_member_dup(this->struct_member_list); } } + | ECPGColLabelCommon '(' precision opt_scale ')' + { + if (strcmp($1, "numeric") != 0 && strcmp($1, "decimal") != 0) + mmerror(PARSE_ERROR, ET_ERROR, "Only numeric/decimal have precision/scale argument"); + + $$.type_enum = ECPGt_numeric; + $$.type_str = EMPTY; + $$.type_dimension = -1; + $$.type_index = -1; + $$.type_sizeof = NULL; + } ; enum_type: SQL_ENUM opt_symbol enum_definition @@ -4600,6 +4660,18 @@ variable: opt_pointer ECPGColLabel opt_array_bounds opt_initializer $$ = cat_str(4, $1, mm_strdup($2), $3.str, $4); break; + case ECPGt_numeric: + if (dimension < 0) + type = ECPGmake_simple_type(actual_type[struct_level].type_enum, length); + else + type = ECPGmake_array_type(ECPGmake_simple_type(actual_type[struct_level].type_enum, length), dimension); + + if (dimension < 0) + $$ = cat_str(4, mm_strdup(actual_storage[struct_level]), make_str("NumericVar"), mm_strdup($2), $4); + else + $$ = cat_str(5, mm_strdup(actual_storage[struct_level]), make_str("NumericVar"), mm_strdup($2), mm_strdup(dim), $4); + break; + default: if (dimension < 0) type = ECPGmake_simple_type(actual_type[struct_level].type_enum, 1); @@ -4870,7 +4942,7 @@ ECPGTypedef: TYPE_P /* an initializer specified */ initializer = 0; } - ColLabel IS type opt_type_array_bounds opt_reference + ColLabel IS var_type opt_array_bounds opt_reference { /* add entry to list */ struct typedefs *ptr, *this; @@ -4925,44 +4997,6 @@ ECPGTypedef: TYPE_P } ; -opt_type_array_bounds: '[' ']' opt_type_array_bounds - { - $$.index1 = 0; - $$.index2 = $3.index1; - $$.str = cat2_str(make_str("[]"), $3.str); - } - | '(' ')' opt_type_array_bounds - { - $$.index1 = 0; - $$.index2 = $3.index1; - $$.str = cat2_str(make_str("[]"), $3.str); - } - | '[' Iresult ']' opt_type_array_bounds - { - char *txt = mm_alloc(20L); - - sprintf (txt, "%d", $2); - $$.index1 = $2; - $$.index2 = $4.index1; - $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str); - } - | '(' Iresult ')' opt_type_array_bounds - { - char *txt = mm_alloc(20L); - - sprintf (txt, "%d", $2); - $$.index1 = $2; - $$.index2 = $4.index1; - $$.str = cat_str(4, make_str("["), txt, make_str("]"), $4.str); - } - | /* EMPTY */ - { - $$.index1 = -1; - $$.index2 = -1; - $$.str= EMPTY; - } - ; - opt_reference: SQL_REFERENCE { $$ = make_str("reference"); } | /*EMPTY*/ { $$ = EMPTY; } ; @@ -4976,7 +5010,7 @@ ECPGVar: SQL_VAR /* an initializer specified */ initializer = 0; } - ColLabel IS type opt_type_array_bounds opt_reference + ColLabel IS var_type opt_array_bounds opt_reference { struct variable *p = find_variable($3); int dimension = $6.index1; diff --git a/src/interfaces/ecpg/preproc/type.c b/src/interfaces/ecpg/preproc/type.c index 4722f21863..ec916a1e62 100644 --- a/src/interfaces/ecpg/preproc/type.c +++ b/src/interfaces/ecpg/preproc/type.c @@ -169,6 +169,9 @@ get_type(enum ECPGttype type) * quoted */ return ("ECPGt_char_variable"); break; + case ECPGt_numeric: + return ("ECPGt_numeric"); + break; case ECPGt_descriptor: return ("ECPGt_descriptor"); break; @@ -319,6 +322,14 @@ ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype type, sprintf(offset, "%ld*sizeof(char)", varcharsize == 0 ? 1 : varcharsize); break; + case ECPGt_numeric: + + /* + * we have to use a pointer here + */ + sprintf(variable, "&(%s%s)", prefix ? prefix : "", name); + sprintf(offset, "sizeof(struct NumericVar)"); + break; default: /* diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index c5b50c6e0f..7c9c0d1edc 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -1,4 +1,4 @@ -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.33 2001/12/23 12:17:41 meskes Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/test/Makefile,v 1.34 2003/03/16 10:42:54 meskes Exp $ subdir = src/interfaces/ecpg/test top_builddir = ../../../.. @@ -8,12 +8,12 @@ override CPPFLAGS := -I$(srcdir)/../include $(CPPFLAGS) -g ECPG = ../preproc/ecpg -I$(srcdir)/../include -TESTS = test1 test2 test3 test4 perftest dyntest dyntest2 test_notice test_code100 test_init testdynalloc +TESTS = test1 test2 test3 test4 perftest dyntest dyntest2 test_notice test_code100 test_init testdynalloc num_test all: $(TESTS) %: %.o - $(CC) $(CFLAGS) $(LDFLAGS) -L../lib -L../../libpq $^ $(LIBS) -lecpg -lpq -o $@ + $(CC) $(CFLAGS) $(LDFLAGS) -L../ecpglib -L ../pgtypeslib -L../../libpq $^ $(LIBS) -lpgtypes -lecpg -lpq -o $@ %.c: %.pgc $(ECPG) $< diff --git a/src/interfaces/ecpg/test/num_test.pgc b/src/interfaces/ecpg/test/num_test.pgc new file mode 100644 index 0000000000..741ae16751 --- /dev/null +++ b/src/interfaces/ecpg/test/num_test.pgc @@ -0,0 +1,56 @@ +#include + +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); +} +