Commit a4f25b6a authored by Michael Meskes's avatar Michael Meskes

Started working on a seperate pgtypes library. First test work. PLEASE test...

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