summaryrefslogtreecommitdiffstats
path: root/src/interfaces/ecpg/ecpglib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
commit5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch)
tree739caf8c461053357daa9f162bef34516c7bf452 /src/interfaces/ecpg/ecpglib
parentInitial commit. (diff)
downloadpostgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz
postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/interfaces/ecpg/ecpglib')
-rw-r--r--src/interfaces/ecpg/ecpglib/.gitignore3
-rw-r--r--src/interfaces/ecpg/ecpglib/Makefile70
-rw-r--r--src/interfaces/ecpg/ecpglib/connect.c766
-rw-r--r--src/interfaces/ecpg/ecpglib/data.c976
-rw-r--r--src/interfaces/ecpg/ecpglib/descriptor.c1002
-rw-r--r--src/interfaces/ecpg/ecpglib/ecpglib_extern.h257
-rw-r--r--src/interfaces/ecpg/ecpglib/error.c346
-rw-r--r--src/interfaces/ecpg/ecpglib/execute.c2301
-rw-r--r--src/interfaces/ecpg/ecpglib/exports.txt31
-rw-r--r--src/interfaces/ecpg/ecpglib/memory.c174
-rw-r--r--src/interfaces/ecpg/ecpglib/misc.c593
-rw-r--r--src/interfaces/ecpg/ecpglib/nls.mk6
-rw-r--r--src/interfaces/ecpg/ecpglib/po/cs.po199
-rw-r--r--src/interfaces/ecpg/ecpglib/po/de.po206
-rw-r--r--src/interfaces/ecpg/ecpglib/po/el.po200
-rw-r--r--src/interfaces/ecpg/ecpglib/po/es.po200
-rw-r--r--src/interfaces/ecpg/ecpglib/po/fr.po212
-rw-r--r--src/interfaces/ecpg/ecpglib/po/it.po217
-rw-r--r--src/interfaces/ecpg/ecpglib/po/ja.po199
-rw-r--r--src/interfaces/ecpg/ecpglib/po/ka.po200
-rw-r--r--src/interfaces/ecpg/ecpglib/po/ko.po198
-rw-r--r--src/interfaces/ecpg/ecpglib/po/pl.po174
-rw-r--r--src/interfaces/ecpg/ecpglib/po/pt_BR.po201
-rw-r--r--src/interfaces/ecpg/ecpglib/po/ru.po204
-rw-r--r--src/interfaces/ecpg/ecpglib/po/sv.po199
-rw-r--r--src/interfaces/ecpg/ecpglib/po/tr.po205
-rw-r--r--src/interfaces/ecpg/ecpglib/po/uk.po200
-rw-r--r--src/interfaces/ecpg/ecpglib/po/vi.po200
-rw-r--r--src/interfaces/ecpg/ecpglib/po/zh_CN.po199
-rw-r--r--src/interfaces/ecpg/ecpglib/prepare.c602
-rw-r--r--src/interfaces/ecpg/ecpglib/sqlda.c592
-rw-r--r--src/interfaces/ecpg/ecpglib/typename.c144
32 files changed, 11276 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/ecpglib/.gitignore b/src/interfaces/ecpg/ecpglib/.gitignore
new file mode 100644
index 0000000..f2bf3e7
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/.gitignore
@@ -0,0 +1,3 @@
+/ecpglib.def
+/blibecpgdll.def
+/exports.list
diff --git a/src/interfaces/ecpg/ecpglib/Makefile b/src/interfaces/ecpg/ecpglib/Makefile
new file mode 100644
index 0000000..b00229f
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/Makefile
@@ -0,0 +1,70 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for ecpg library
+#
+# Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
+# Portions Copyright (c) 1994, Regents of the University of California
+#
+# src/interfaces/ecpg/ecpglib/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/interfaces/ecpg/ecpglib
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+PGFILEDESC = "ECPG - embedded SQL in C"
+NAME= ecpg
+SO_MAJOR_VERSION= 6
+SO_MINOR_VERSION= $(MAJORVERSION)
+
+override CPPFLAGS := -I../include -I$(top_srcdir)/src/interfaces/ecpg/include \
+ -I$(libpq_srcdir) -I$(top_builddir)/src/port -DFRONTEND $(CPPFLAGS)
+override CFLAGS += $(PTHREAD_CFLAGS)
+
+OBJS = \
+ $(WIN32RES) \
+ connect.o \
+ data.o \
+ descriptor.o \
+ error.o \
+ execute.o \
+ memory.o \
+ misc.o \
+ prepare.o \
+ sqlda.o \
+ typename.o
+
+SHLIB_LINK_INTERNAL = -L../pgtypeslib -lpgtypes $(libpq_pgport_shlib)
+SHLIB_LINK = $(filter -lintl -lm, $(LIBS)) $(PTHREAD_LIBS)
+SHLIB_PREREQS = submake-libpq submake-pgtypeslib
+
+SHLIB_EXPORTS = exports.txt
+
+PKG_CONFIG_REQUIRES_PRIVATE = libpq libpgtypes
+
+all: all-lib
+
+.PHONY: submake-pgtypeslib
+submake-pgtypeslib:
+ $(MAKE) -C $(top_builddir)/src/interfaces/ecpg/pgtypeslib all
+
+# Shared library stuff
+include $(top_srcdir)/src/Makefile.shlib
+
+# Make dependency on pg_config_paths.h visible.
+misc.o: misc.c $(top_builddir)/src/port/pg_config_paths.h
+
+$(top_builddir)/src/port/pg_config_paths.h:
+ $(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
+
+install: all installdirs install-lib
+
+installdirs: installdirs-lib
+
+uninstall: uninstall-lib
+
+clean distclean: clean-lib
+ rm -f $(OBJS)
+
+maintainer-clean: distclean
diff --git a/src/interfaces/ecpg/ecpglib/connect.c b/src/interfaces/ecpg/ecpglib/connect.c
new file mode 100644
index 0000000..db0bae1
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/connect.c
@@ -0,0 +1,766 @@
+/* src/interfaces/ecpg/ecpglib/connect.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include "ecpg-pthread-win32.h"
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "sqlca.h"
+
+#ifdef HAVE_USELOCALE
+locale_t ecpg_clocale = (locale_t) 0;
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+static pthread_mutex_t connections_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_key_t actual_connection_key;
+static pthread_once_t actual_connection_key_once = PTHREAD_ONCE_INIT;
+#endif
+static struct connection *actual_connection = NULL;
+static struct connection *all_connections = NULL;
+
+#ifdef ENABLE_THREAD_SAFETY
+static void
+ecpg_actual_connection_init(void)
+{
+ pthread_key_create(&actual_connection_key, NULL);
+}
+
+void
+ecpg_pthreads_init(void)
+{
+ pthread_once(&actual_connection_key_once, ecpg_actual_connection_init);
+}
+#endif
+
+static struct connection *
+ecpg_get_connection_nr(const char *connection_name)
+{
+ struct connection *ret = NULL;
+
+ if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
+ {
+#ifdef ENABLE_THREAD_SAFETY
+ ecpg_pthreads_init(); /* ensure actual_connection_key is valid */
+
+ ret = pthread_getspecific(actual_connection_key);
+
+ /*
+ * if no connection in TSD for this thread, get the global default
+ * connection and hope the user knows what they're doing (i.e. using
+ * their own mutex to protect that connection from concurrent accesses
+ */
+ if (ret == NULL)
+ /* no TSD connection, going for global */
+ ret = actual_connection;
+#else
+ ret = actual_connection;
+#endif
+ }
+ else
+ {
+ struct connection *con;
+
+ for (con = all_connections; con != NULL; con = con->next)
+ {
+ if (strcmp(connection_name, con->name) == 0)
+ break;
+ }
+ ret = con;
+ }
+
+ return ret;
+}
+
+struct connection *
+ecpg_get_connection(const char *connection_name)
+{
+ struct connection *ret = NULL;
+
+ if ((connection_name == NULL) || (strcmp(connection_name, "CURRENT") == 0))
+ {
+#ifdef ENABLE_THREAD_SAFETY
+ ecpg_pthreads_init(); /* ensure actual_connection_key is valid */
+
+ ret = pthread_getspecific(actual_connection_key);
+
+ /*
+ * if no connection in TSD for this thread, get the global default
+ * connection and hope the user knows what they're doing (i.e. using
+ * their own mutex to protect that connection from concurrent accesses
+ */
+ if (ret == NULL)
+ /* no TSD connection here either, using global */
+ ret = actual_connection;
+#else
+ ret = actual_connection;
+#endif
+ }
+ else
+ {
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_lock(&connections_mutex);
+#endif
+
+ ret = ecpg_get_connection_nr(connection_name);
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&connections_mutex);
+#endif
+ }
+
+ return ret;
+}
+
+static void
+ecpg_finish(struct connection *act)
+{
+ if (act != NULL)
+ {
+ struct ECPGtype_information_cache *cache,
+ *ptr;
+
+ ecpg_deallocate_all_conn(0, ECPG_COMPAT_PGSQL, act);
+ PQfinish(act->connection);
+
+ /*
+ * no need to lock connections_mutex - we're always called by
+ * ECPGdisconnect or ECPGconnect, which are holding the lock
+ */
+
+ /* 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;
+ }
+
+#ifdef ENABLE_THREAD_SAFETY
+ if (pthread_getspecific(actual_connection_key) == act)
+ pthread_setspecific(actual_connection_key, all_connections);
+#endif
+ if (actual_connection == act)
+ actual_connection = all_connections;
+
+ ecpg_log("ecpg_finish: connection %s closed\n", act->name ? act->name : "(null)");
+
+ for (cache = act->cache_head; cache; ptr = cache, cache = cache->next, ecpg_free(ptr));
+ ecpg_free(act->name);
+ ecpg_free(act);
+ /* delete cursor variables when last connection gets closed */
+ if (all_connections == NULL)
+ {
+ struct var_list *iv_ptr;
+
+ for (; ivlist; iv_ptr = ivlist, ivlist = ivlist->next, ecpg_free(iv_ptr));
+ }
+ }
+ else
+ ecpg_log("ecpg_finish: called an extra time\n");
+}
+
+bool
+ECPGsetcommit(int lineno, const char *mode, const char *connection_name)
+{
+ struct connection *con = ecpg_get_connection(connection_name);
+ PGresult *results;
+
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+ ecpg_log("ECPGsetcommit on line %d: action \"%s\"; connection \"%s\"\n", lineno, mode, con->name);
+
+ if (con->autocommit && strncmp(mode, "off", strlen("off")) == 0)
+ {
+ if (PQtransactionStatus(con->connection) == PQTRANS_IDLE)
+ {
+ results = PQexec(con->connection, "begin transaction");
+ if (!ecpg_check_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL))
+ return false;
+ PQclear(results);
+ }
+ con->autocommit = false;
+ }
+ else if (!con->autocommit && strncmp(mode, "on", strlen("on")) == 0)
+ {
+ if (PQtransactionStatus(con->connection) != PQTRANS_IDLE)
+ {
+ results = PQexec(con->connection, "commit");
+ if (!ecpg_check_PQresult(results, lineno, con->connection, ECPG_COMPAT_PGSQL))
+ return false;
+ PQclear(results);
+ }
+ con->autocommit = true;
+ }
+
+ return true;
+}
+
+bool
+ECPGsetconn(int lineno, const char *connection_name)
+{
+ struct connection *con = ecpg_get_connection(connection_name);
+
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_setspecific(actual_connection_key, con);
+#else
+ actual_connection = con;
+#endif
+ return true;
+}
+
+
+static void
+ECPGnoticeReceiver(void *arg, const PGresult *result)
+{
+ char *sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+ char *message = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ int sqlcode;
+
+ if (sqlca == NULL)
+ {
+ ecpg_log("out of memory");
+ return;
+ }
+
+ (void) arg; /* keep the compiler quiet */
+ if (sqlstate == NULL)
+ sqlstate = ECPG_SQLSTATE_ECPG_INTERNAL_ERROR;
+
+ if (message == NULL) /* Shouldn't happen, but need to be sure */
+ message = ecpg_gettext("empty message text");
+
+ /* these are not warnings */
+ if (strncmp(sqlstate, "00", 2) == 0)
+ return;
+
+ ecpg_log("ECPGnoticeReceiver: %s\n", message);
+
+ /* map to SQLCODE for backward compatibility */
+ if (strcmp(sqlstate, ECPG_SQLSTATE_INVALID_CURSOR_NAME) == 0)
+ sqlcode = ECPG_WARNING_UNKNOWN_PORTAL;
+ else if (strcmp(sqlstate, ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION) == 0)
+ sqlcode = ECPG_WARNING_IN_TRANSACTION;
+ else if (strcmp(sqlstate, ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION) == 0)
+ sqlcode = ECPG_WARNING_NO_TRANSACTION;
+ else if (strcmp(sqlstate, ECPG_SQLSTATE_DUPLICATE_CURSOR) == 0)
+ sqlcode = ECPG_WARNING_PORTAL_EXISTS;
+ else
+ sqlcode = 0;
+
+ strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
+ sqlca->sqlcode = sqlcode;
+ sqlca->sqlwarn[2] = 'W';
+ sqlca->sqlwarn[0] = 'W';
+
+ 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);
+
+ ecpg_log("raising sqlcode %d\n", sqlcode);
+}
+
+/* this contains some quick hacks, needs to be cleaned up, but it works */
+bool
+ECPGconnect(int lineno, int c, const char *name, const char *user, const char *passwd, const char *connection_name, int autocommit)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ enum COMPAT_MODE compat = c;
+ struct connection *this;
+ int i,
+ connect_params = 0;
+ char *dbname = name ? ecpg_strdup(name, lineno) : NULL,
+ *host = NULL,
+ *tmp,
+ *port = NULL,
+ *realname = NULL,
+ *options = NULL;
+ const char **conn_keywords;
+ const char **conn_values;
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ ecpg_free(dbname);
+ return false;
+ }
+
+ ecpg_init_sqlca(sqlca);
+
+ /*
+ * clear auto_mem structure because some error handling functions might
+ * access it
+ */
+ ecpg_clear_auto_mem();
+
+ if (INFORMIX_MODE(compat))
+ {
+ char *envname;
+
+ /*
+ * Informix uses an environment variable DBPATH that overrides the
+ * connection parameters given here. We do the same with PG_DBPATH as
+ * the syntax is different.
+ */
+ envname = getenv("PG_DBPATH");
+ if (envname)
+ {
+ ecpg_free(dbname);
+ dbname = ecpg_strdup(envname, lineno);
+ }
+ }
+
+ if (dbname == NULL && connection_name == NULL)
+ connection_name = "DEFAULT";
+
+#if ENABLE_THREAD_SAFETY
+ ecpg_pthreads_init();
+#endif
+
+ /* check if the identifier is unique */
+ if (ecpg_get_connection(connection_name))
+ {
+ ecpg_free(dbname);
+ ecpg_log("ECPGconnect: connection identifier %s is already in use\n",
+ connection_name);
+ return false;
+ }
+
+ if ((this = (struct connection *) ecpg_alloc(sizeof(struct connection), lineno)) == NULL)
+ {
+ ecpg_free(dbname);
+ return false;
+ }
+
+ if (dbname != NULL)
+ {
+ /* get the detail information from dbname */
+ 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][/db-name][?options]
+ *------
+ */
+ offset += strlen("postgresql://");
+
+ tmp = strrchr(dbname + offset, '?');
+ if (tmp != NULL) /* options given */
+ {
+ options = ecpg_strdup(tmp + 1, lineno);
+ *tmp = '\0';
+ }
+
+ tmp = last_dir_separator(dbname + offset);
+ if (tmp != NULL) /* database name given */
+ {
+ if (tmp[1] != '\0') /* non-empty database name */
+ {
+ realname = ecpg_strdup(tmp + 1, lineno);
+ connect_params++;
+ }
+ *tmp = '\0';
+ }
+
+ tmp = strrchr(dbname + offset, ':');
+ if (tmp != NULL) /* port number given */
+ {
+ *tmp = '\0';
+ port = ecpg_strdup(tmp + 1, lineno);
+ connect_params++;
+ }
+
+ if (strncmp(dbname, "unix:", 5) == 0)
+ {
+ /*
+ * The alternative of using "127.0.0.1" here is deprecated
+ * and undocumented; we'll keep it for backward
+ * compatibility's sake, but not extend it to allow IPv6.
+ */
+ if (strcmp(dbname + offset, "localhost") != 0 &&
+ strcmp(dbname + offset, "127.0.0.1") != 0)
+ {
+ ecpg_log("ECPGconnect: non-localhost access via sockets on line %d\n", lineno);
+ ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, realname ? realname : ecpg_gettext("<DEFAULT>"));
+ if (host)
+ ecpg_free(host);
+ if (port)
+ ecpg_free(port);
+ if (options)
+ ecpg_free(options);
+ if (realname)
+ ecpg_free(realname);
+ if (dbname)
+ ecpg_free(dbname);
+ free(this);
+ return false;
+ }
+ }
+ else
+ {
+ if (*(dbname + offset) != '\0')
+ {
+ host = ecpg_strdup(dbname + offset, lineno);
+ connect_params++;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* old style: dbname[@server][:port] */
+ tmp = strrchr(dbname, ':');
+ if (tmp != NULL) /* port number given */
+ {
+ port = ecpg_strdup(tmp + 1, lineno);
+ connect_params++;
+ *tmp = '\0';
+ }
+
+ tmp = strrchr(dbname, '@');
+ if (tmp != NULL) /* host name given */
+ {
+ host = ecpg_strdup(tmp + 1, lineno);
+ connect_params++;
+ *tmp = '\0';
+ }
+
+ if (strlen(dbname) > 0)
+ {
+ realname = ecpg_strdup(dbname, lineno);
+ connect_params++;
+ }
+ else
+ realname = NULL;
+ }
+ }
+ else
+ realname = NULL;
+
+ /*
+ * Count options for the allocation done below (this may produce an
+ * overestimate, it's ok).
+ */
+ if (options)
+ for (i = 0; options[i]; i++)
+ if (options[i] == '=')
+ connect_params++;
+
+ if (user && strlen(user) > 0)
+ connect_params++;
+ if (passwd && strlen(passwd) > 0)
+ connect_params++;
+
+ /*
+ * Allocate enough space for all connection parameters. These allocations
+ * are done before manipulating the list of connections to ease the error
+ * handling on failure.
+ */
+ conn_keywords = (const char **) ecpg_alloc((connect_params + 1) * sizeof(char *), lineno);
+ conn_values = (const char **) ecpg_alloc(connect_params * sizeof(char *), lineno);
+ if (conn_keywords == NULL || conn_values == NULL)
+ {
+ if (host)
+ ecpg_free(host);
+ if (port)
+ ecpg_free(port);
+ if (options)
+ ecpg_free(options);
+ if (realname)
+ ecpg_free(realname);
+ if (dbname)
+ ecpg_free(dbname);
+ if (conn_keywords)
+ ecpg_free(conn_keywords);
+ if (conn_values)
+ ecpg_free(conn_values);
+ free(this);
+ return false;
+ }
+
+ /* add connection to our list */
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_lock(&connections_mutex);
+#endif
+
+ /*
+ * ... but first, make certain we have created ecpg_clocale. Rely on
+ * holding connections_mutex to ensure this is done by only one thread.
+ */
+#ifdef HAVE_USELOCALE
+ if (!ecpg_clocale)
+ {
+ ecpg_clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
+ if (!ecpg_clocale)
+ {
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&connections_mutex);
+#endif
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ if (host)
+ ecpg_free(host);
+ if (port)
+ ecpg_free(port);
+ if (options)
+ ecpg_free(options);
+ if (realname)
+ ecpg_free(realname);
+ if (dbname)
+ ecpg_free(dbname);
+ if (conn_keywords)
+ ecpg_free(conn_keywords);
+ if (conn_values)
+ ecpg_free(conn_values);
+ free(this);
+ return false;
+ }
+ }
+#endif
+
+ if (connection_name != NULL)
+ this->name = ecpg_strdup(connection_name, lineno);
+ else
+ this->name = ecpg_strdup(realname, lineno);
+
+ this->cache_head = NULL;
+ this->prep_stmts = NULL;
+
+ if (all_connections == NULL)
+ this->next = NULL;
+ else
+ this->next = all_connections;
+
+ all_connections = this;
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_setspecific(actual_connection_key, all_connections);
+#endif
+ actual_connection = all_connections;
+
+ ecpg_log("ECPGconnect: opening database %s on %s port %s %s%s %s%s\n",
+ realname ? realname : "<DEFAULT>",
+ host ? host : "<DEFAULT>",
+ port ? (ecpg_internal_regression_mode ? "<REGRESSION_PORT>" : port) : "<DEFAULT>",
+ options ? "with options " : "", options ? options : "",
+ (user && strlen(user) > 0) ? "for user " : "", user ? user : "");
+
+ i = 0;
+ if (realname)
+ {
+ conn_keywords[i] = "dbname";
+ conn_values[i] = realname;
+ i++;
+ }
+ if (host)
+ {
+ conn_keywords[i] = "host";
+ conn_values[i] = host;
+ i++;
+ }
+ if (port)
+ {
+ conn_keywords[i] = "port";
+ conn_values[i] = port;
+ i++;
+ }
+ if (user && strlen(user) > 0)
+ {
+ conn_keywords[i] = "user";
+ conn_values[i] = user;
+ i++;
+ }
+ if (passwd && strlen(passwd) > 0)
+ {
+ conn_keywords[i] = "password";
+ conn_values[i] = passwd;
+ i++;
+ }
+ if (options)
+ {
+ char *str;
+
+ /*
+ * The options string contains "keyword=value" pairs separated by
+ * '&'s. We must break this up into keywords and values to pass to
+ * libpq (it's okay to scribble on the options string). We ignore
+ * spaces just before each keyword or value.
+ */
+ for (str = options; *str;)
+ {
+ int e,
+ a;
+ char *token1,
+ *token2;
+
+ /* Skip spaces before keyword */
+ for (token1 = str; *token1 == ' '; token1++)
+ /* skip */ ;
+ /* Find end of keyword */
+ for (e = 0; token1[e] && token1[e] != '='; e++)
+ /* skip */ ;
+ if (token1[e]) /* found "=" */
+ {
+ token1[e] = '\0';
+ /* Skip spaces before value */
+ for (token2 = token1 + e + 1; *token2 == ' '; token2++)
+ /* skip */ ;
+ /* Find end of value */
+ for (a = 0; token2[a] && token2[a] != '&'; a++)
+ /* skip */ ;
+ if (token2[a]) /* found "&" => another option follows */
+ {
+ token2[a] = '\0';
+ str = token2 + a + 1;
+ }
+ else
+ str = token2 + a;
+
+ conn_keywords[i] = token1;
+ conn_values[i] = token2;
+ i++;
+ }
+ else
+ {
+ /* Bogus options syntax ... ignore trailing garbage */
+ str = token1 + e;
+ }
+ }
+ }
+
+ Assert(i <= connect_params);
+ conn_keywords[i] = NULL; /* terminator */
+
+ this->connection = PQconnectdbParams(conn_keywords, conn_values, 0);
+
+ if (host)
+ ecpg_free(host);
+ if (port)
+ ecpg_free(port);
+ if (options)
+ ecpg_free(options);
+ if (dbname)
+ ecpg_free(dbname);
+ ecpg_free(conn_values);
+ ecpg_free(conn_keywords);
+
+ if (PQstatus(this->connection) == CONNECTION_BAD)
+ {
+ const char *errmsg = PQerrorMessage(this->connection);
+ const char *db = realname ? realname : ecpg_gettext("<DEFAULT>");
+
+ /* PQerrorMessage's result already has a trailing newline */
+ ecpg_log("ECPGconnect: %s", errmsg);
+
+ ecpg_finish(this);
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&connections_mutex);
+#endif
+
+ ecpg_raise(lineno, ECPG_CONNECT, ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION, db);
+ if (realname)
+ ecpg_free(realname);
+
+ return false;
+ }
+
+ if (realname)
+ ecpg_free(realname);
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&connections_mutex);
+#endif
+
+ this->autocommit = autocommit;
+
+ PQsetNoticeReceiver(this->connection, &ECPGnoticeReceiver, (void *) this);
+
+ return true;
+}
+
+bool
+ECPGdisconnect(int lineno, const char *connection_name)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ struct connection *con;
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_lock(&connections_mutex);
+#endif
+
+ if (strcmp(connection_name, "ALL") == 0)
+ {
+ ecpg_init_sqlca(sqlca);
+ for (con = all_connections; con;)
+ {
+ struct connection *f = con;
+
+ con = con->next;
+ ecpg_finish(f);
+ }
+ }
+ else
+ {
+ con = ecpg_get_connection_nr(connection_name);
+
+ if (!ecpg_init(con, connection_name, lineno))
+ {
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&connections_mutex);
+#endif
+ return false;
+ }
+ else
+ ecpg_finish(con);
+ }
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&connections_mutex);
+#endif
+
+ return true;
+}
+
+PGconn *
+ECPGget_PGconn(const char *connection_name)
+{
+ struct connection *con;
+
+ con = ecpg_get_connection(connection_name);
+ if (con == NULL)
+ return NULL;
+
+ return con->connection;
+}
diff --git a/src/interfaces/ecpg/ecpglib/data.c b/src/interfaces/ecpg/ecpglib/data.c
new file mode 100644
index 0000000..40cbfc0
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/data.c
@@ -0,0 +1,976 @@
+/* src/interfaces/ecpg/ecpglib/data.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include <math.h>
+
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "pgtypes_date.h"
+#include "pgtypes_interval.h"
+#include "pgtypes_numeric.h"
+#include "pgtypes_timestamp.h"
+#include "sqlca.h"
+
+/* returns true if character c is a delimiter for the given array type */
+static bool
+array_delimiter(enum ARRAY_TYPE isarray, char c)
+{
+ if (isarray == ECPG_ARRAY_ARRAY && c == ',')
+ return true;
+
+ if (isarray == ECPG_ARRAY_VECTOR && c == ' ')
+ return true;
+
+ return false;
+}
+
+/* returns true if character c marks the boundary for the given array type */
+static bool
+array_boundary(enum ARRAY_TYPE isarray, char c)
+{
+ if (isarray == ECPG_ARRAY_ARRAY && c == '}')
+ return true;
+
+ if (isarray == ECPG_ARRAY_VECTOR && c == '\0')
+ return true;
+
+ return false;
+}
+
+/* returns true if some garbage is found at the end of the scanned string */
+static bool
+garbage_left(enum ARRAY_TYPE isarray, char **scan_length, enum COMPAT_MODE compat)
+{
+ /*
+ * INFORMIX allows for selecting a numeric into an int, the result is
+ * truncated
+ */
+ if (isarray == ECPG_ARRAY_NONE)
+ {
+ if (INFORMIX_MODE(compat) && **scan_length == '.')
+ {
+ /* skip invalid characters */
+ do
+ {
+ (*scan_length)++;
+ } while (isdigit((unsigned char) **scan_length));
+ }
+
+ if (**scan_length != ' ' && **scan_length != '\0')
+ return true;
+ }
+ else if (ECPG_IS_ARRAY(isarray) && !array_delimiter(isarray, **scan_length) && !array_boundary(isarray, **scan_length))
+ return true;
+
+ return false;
+}
+
+/* stolen code from src/backend/utils/adt/float.c */
+#if defined(WIN32) && !defined(NAN)
+static const uint32 nan[2] = {0xffffffff, 0x7fffffff};
+
+#define NAN (*(const double *) nan)
+#endif
+
+static double
+get_float8_infinity(void)
+{
+#ifdef INFINITY
+ return (double) INFINITY;
+#else
+ return (double) (HUGE_VAL * HUGE_VAL);
+#endif
+}
+
+static double
+get_float8_nan(void)
+{
+ /* (double) NAN doesn't work on some NetBSD/MIPS releases */
+#if defined(NAN) && !(defined(__NetBSD__) && defined(__mips__))
+ return (double) NAN;
+#else
+ return (double) (0.0 / 0.0);
+#endif
+}
+
+static bool
+check_special_value(char *ptr, double *retval, char **endptr)
+{
+ if (pg_strncasecmp(ptr, "NaN", 3) == 0)
+ {
+ *retval = get_float8_nan();
+ *endptr = ptr + 3;
+ return true;
+ }
+ else if (pg_strncasecmp(ptr, "Infinity", 8) == 0)
+ {
+ *retval = get_float8_infinity();
+ *endptr = ptr + 8;
+ return true;
+ }
+ else if (pg_strncasecmp(ptr, "-Infinity", 9) == 0)
+ {
+ *retval = -get_float8_infinity();
+ *endptr = ptr + 9;
+ return true;
+ }
+
+ return false;
+}
+
+/* imported from src/backend/utils/adt/encode.c */
+
+unsigned
+ecpg_hex_enc_len(unsigned srclen)
+{
+ return srclen << 1;
+}
+
+unsigned
+ecpg_hex_dec_len(unsigned srclen)
+{
+ return srclen >> 1;
+}
+
+static inline char
+get_hex(char c)
+{
+ static const int8 hexlookup[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+ int res = -1;
+
+ if (c > 0 && c < 127)
+ res = hexlookup[(unsigned char) c];
+
+ return (char) res;
+}
+
+static unsigned
+hex_decode(const char *src, unsigned len, char *dst)
+{
+ const char *s,
+ *srcend;
+ char v1,
+ v2,
+ *p;
+
+ srcend = src + len;
+ s = src;
+ p = dst;
+ while (s < srcend)
+ {
+ if (*s == ' ' || *s == '\n' || *s == '\t' || *s == '\r')
+ {
+ s++;
+ continue;
+ }
+ v1 = get_hex(*s++) << 4;
+ if (s >= srcend)
+ return -1;
+
+ v2 = get_hex(*s++);
+ *p++ = v1 | v2;
+ }
+
+ return p - dst;
+}
+
+unsigned
+ecpg_hex_encode(const char *src, unsigned len, char *dst)
+{
+ static const char hextbl[] = "0123456789abcdef";
+ const char *end = src + len;
+
+ while (src < end)
+ {
+ *dst++ = hextbl[(*src >> 4) & 0xF];
+ *dst++ = hextbl[*src & 0xF];
+ src++;
+ }
+ return len * 2;
+}
+
+bool
+ecpg_get_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, enum ARRAY_TYPE isarray, enum COMPAT_MODE compat, bool force_indicator)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ char *pval = (char *) PQgetvalue(results, act_tuple, act_field);
+ int binary = PQfformat(results, act_field);
+ int size = PQgetlength(results, act_tuple, act_field);
+ int value_for_indicator = 0;
+ long log_offset;
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ /*
+ * If we are running in a regression test, do not log the offset variable,
+ * it depends on the machine's alignment.
+ */
+ if (ecpg_internal_regression_mode)
+ log_offset = -1;
+ else
+ log_offset = offset;
+
+ ecpg_log("ecpg_get_data on line %d: RESULT: %s offset: %ld; array: %s\n", lineno, pval ? (binary ? "BINARY" : pval) : "EMPTY", log_offset, ECPG_IS_ARRAY(isarray) ? "yes" : "no");
+
+ /* pval is a pointer to the value */
+ if (!pval)
+ {
+ /*
+ * This should never happen because we already checked that we found
+ * at least one tuple, but let's play it safe.
+ */
+ ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+ return false;
+ }
+
+ /* We will have to decode the value */
+
+ /*
+ * check for null value and set indicator accordingly, i.e. -1 if NULL and
+ * 0 if not
+ */
+ if (PQgetisnull(results, act_tuple, act_field))
+ value_for_indicator = -1;
+
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = value_for_indicator;
+ break;
+ case ECPGt_NO_INDICATOR:
+ if (value_for_indicator == -1)
+ {
+ if (force_indicator == false)
+ {
+ /*
+ * Informix has an additional way to specify NULLs note
+ * that this uses special values to denote NULL
+ */
+ ECPGset_noind_null(type, var + offset * act_tuple);
+ }
+ else
+ {
+ ecpg_raise(lineno, ECPG_MISSING_INDICATOR,
+ ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER,
+ NULL);
+ return false;
+ }
+ }
+ break;
+ default:
+ ecpg_raise(lineno, ECPG_UNSUPPORTED,
+ ECPG_SQLSTATE_ECPG_INTERNAL_ERROR,
+ ecpg_type_name(ind_type));
+ return false;
+ break;
+ }
+
+ if (value_for_indicator == -1)
+ return true;
+
+ /* let's check if it really is an array if it should be one */
+ if (isarray == ECPG_ARRAY_ARRAY)
+ {
+ if (*pval != '{')
+ {
+ ecpg_raise(lineno, ECPG_DATA_NOT_ARRAY,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
+ return false;
+ }
+
+ switch (type)
+ {
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_varchar:
+ case ECPGt_string:
+ break;
+
+ default:
+ pval++;
+ break;
+ }
+ }
+
+ do
+ {
+ if (binary)
+ {
+ if (varcharsize == 0 || varcharsize * offset >= size)
+ memcpy(var + offset * act_tuple, pval, size);
+ else
+ {
+ memcpy(var + offset * act_tuple, pval, varcharsize * offset);
+
+ if (varcharsize * offset < size)
+ {
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+ }
+ pval += size;
+ }
+ else
+ {
+ switch (type)
+ {
+ long res;
+ unsigned long ures;
+ double dres;
+ char *scan_length;
+ numeric *nres;
+ date ddres;
+ timestamp tres;
+ interval *ires;
+ char *endptr,
+ endchar;
+
+ case ECPGt_short:
+ case ECPGt_int:
+ case ECPGt_long:
+ res = strtol(pval, &scan_length, 10);
+ if (garbage_left(isarray, &scan_length, compat))
+ {
+ ecpg_raise(lineno, ECPG_INT_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ pval = scan_length;
+
+ switch (type)
+ {
+ case ECPGt_short:
+ *((short *) (var + offset * act_tuple)) = (short) res;
+ break;
+ case ECPGt_int:
+ *((int *) (var + offset * act_tuple)) = (int) res;
+ break;
+ case ECPGt_long:
+ *((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:
+ ures = strtoul(pval, &scan_length, 10);
+ if (garbage_left(isarray, &scan_length, compat))
+ {
+ ecpg_raise(lineno, ECPG_UINT_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ pval = scan_length;
+
+ switch (type)
+ {
+ case ECPGt_unsigned_short:
+ *((unsigned short *) (var + offset * act_tuple)) = (unsigned short) ures;
+ break;
+ case ECPGt_unsigned_int:
+ *((unsigned int *) (var + offset * act_tuple)) = (unsigned int) ures;
+ break;
+ case ECPGt_unsigned_long:
+ *((unsigned long *) (var + offset * act_tuple)) = (unsigned long) ures;
+ break;
+ default:
+ /* Cannot happen */
+ break;
+ }
+ break;
+
+#ifdef HAVE_STRTOLL
+ case ECPGt_long_long:
+ *((long long int *) (var + offset * act_tuple)) = strtoll(pval, &scan_length, 10);
+ if (garbage_left(isarray, &scan_length, compat))
+ {
+ ecpg_raise(lineno, ECPG_INT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ pval = scan_length;
+
+ break;
+#endif /* HAVE_STRTOLL */
+#ifdef HAVE_STRTOULL
+ case ECPGt_unsigned_long_long:
+ *((unsigned long long int *) (var + offset * act_tuple)) = strtoull(pval, &scan_length, 10);
+ if (garbage_left(isarray, &scan_length, compat))
+ {
+ ecpg_raise(lineno, ECPG_UINT_FORMAT, ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ pval = scan_length;
+
+ break;
+#endif /* HAVE_STRTOULL */
+
+ case ECPGt_float:
+ case ECPGt_double:
+ if (isarray && *pval == '"')
+ pval++;
+
+ if (!check_special_value(pval, &dres, &scan_length))
+ dres = strtod(pval, &scan_length);
+
+ if (isarray && *scan_length == '"')
+ scan_length++;
+
+ /* no special INFORMIX treatment for floats */
+ if (garbage_left(isarray, &scan_length, ECPG_COMPAT_PGSQL))
+ {
+ ecpg_raise(lineno, ECPG_FLOAT_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ pval = scan_length;
+
+ switch (type)
+ {
+ case ECPGt_float:
+ *((float *) (var + offset * act_tuple)) = dres;
+ break;
+ case ECPGt_double:
+ *((double *) (var + offset * act_tuple)) = dres;
+ break;
+ default:
+ /* Cannot happen */
+ break;
+ }
+ break;
+
+ case ECPGt_bool:
+ if (pval[0] == 'f' && pval[1] == '\0')
+ {
+ *((bool *) (var + offset * act_tuple)) = false;
+ pval++;
+ break;
+ }
+ else if (pval[0] == 't' && pval[1] == '\0')
+ {
+ *((bool *) (var + offset * act_tuple)) = true;
+ pval++;
+ break;
+ }
+ else if (pval[0] == '\0' && PQgetisnull(results, act_tuple, act_field))
+ {
+ /* NULL is valid */
+ break;
+ }
+
+ ecpg_raise(lineno, ECPG_CONVERT_BOOL,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_bytea *variable =
+ (struct ECPGgeneric_bytea *) (var + offset * act_tuple);
+ long dst_size,
+ src_size,
+ dec_size;
+
+ dst_size = ecpg_hex_enc_len(varcharsize);
+ src_size = size - 2; /* exclude backslash + 'x' */
+ dec_size = src_size < dst_size ? src_size : dst_size;
+ variable->len = hex_decode(pval + 2, dec_size, variable->arr);
+
+ if (dst_size < src_size)
+ {
+ long rcv_size = ecpg_hex_dec_len(size - 2);
+
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = rcv_size;
+ break;
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+
+ pval += size;
+ }
+ break;
+
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ {
+ char *str = (char *) (var + offset * act_tuple);
+
+ /*
+ * If varcharsize is unknown and the offset is that of
+ * char *, then this variable represents the array of
+ * character pointers. So, use extra indirection.
+ */
+ if (varcharsize == 0 && offset == sizeof(char *))
+ str = *(char **) str;
+
+ if (varcharsize > size)
+ {
+ /*
+ * compatibility mode, blank pad and null
+ * terminate char array
+ */
+ if (ORACLE_MODE(compat) && (type == ECPGt_char || type == ECPGt_unsigned_char))
+ {
+ memset(str, ' ', varcharsize);
+ memcpy(str, pval, size);
+ str[varcharsize - 1] = '\0';
+
+ /*
+ * compatibility mode empty string gets -1
+ * indicator but no warning
+ */
+ if (size == 0)
+ {
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = -1;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = -1;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = -1;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = -1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ strncpy(str, pval, size + 1);
+ }
+ /* do the rtrim() */
+ if (type == ECPGt_string)
+ {
+ char *last = str + size;
+
+ while (last > str && (*last == ' ' || *last == '\0'))
+ {
+ *last = '\0';
+ last--;
+ }
+ }
+ }
+ else
+ {
+ int charsize = varcharsize;
+
+ /*
+ * assume that the caller provided storage exactly
+ * fit when varcharsize is zero.
+ */
+ if (varcharsize == 0)
+ charsize = size + 1;
+
+ strncpy(str, pval, charsize);
+
+ /* compatibility mode, null terminate char array */
+ if (ORACLE_MODE(compat) && (charsize - 1) < size)
+ {
+ if (type == ECPGt_char || type == ECPGt_unsigned_char)
+ str[charsize - 1] = '\0';
+ }
+
+ if (charsize < size || (ORACLE_MODE(compat) && (charsize - 1) < size))
+ {
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = size;
+ break;
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+ }
+ }
+ pval += size;
+ }
+ break;
+
+ case ECPGt_varchar:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var + offset * act_tuple);
+
+ variable->len = size;
+ if (varcharsize == 0)
+ strncpy(variable->arr, pval, variable->len);
+ else
+ {
+ strncpy(variable->arr, pval, varcharsize);
+
+ if (variable->len > varcharsize)
+ {
+ /* truncation */
+ switch (ind_type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short *) (ind + ind_offset * act_tuple)) = variable->len;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) (ind + ind_offset * act_tuple)) = variable->len;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ *((long *) (ind + ind_offset * act_tuple)) = variable->len;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long int *) (ind + ind_offset * act_tuple)) = variable->len;
+ break;
+ default:
+ break;
+ }
+ sqlca->sqlwarn[0] = sqlca->sqlwarn[1] = 'W';
+
+ variable->len = varcharsize;
+ }
+ }
+ pval += size;
+ }
+ break;
+
+ case ECPGt_decimal:
+ case ECPGt_numeric:
+ for (endptr = pval; *endptr && *endptr != ',' && *endptr != '}'; endptr++);
+ endchar = *endptr;
+ *endptr = '\0';
+ nres = PGTYPESnumeric_from_asc(pval, &scan_length);
+ *endptr = endchar;
+
+ /* did we get an error? */
+ if (nres == NULL)
+ {
+ ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
+ lineno, pval, errno);
+
+ if (INFORMIX_MODE(compat))
+ {
+ /*
+ * Informix wants its own NULL value here instead
+ * of an error
+ */
+ nres = PGTYPESnumeric_new();
+ if (nres)
+ ECPGset_noind_null(ECPGt_numeric, nres);
+ else
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+ }
+ else
+ {
+ ecpg_raise(lineno, ECPG_NUMERIC_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+ else
+ {
+ if (!isarray && garbage_left(isarray, &scan_length, compat))
+ {
+ free(nres);
+ ecpg_raise(lineno, ECPG_NUMERIC_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+ pval = scan_length;
+
+ if (type == ECPGt_numeric)
+ PGTYPESnumeric_copy(nres, (numeric *) (var + offset * act_tuple));
+ else
+ PGTYPESnumeric_to_decimal(nres, (decimal *) (var + offset * act_tuple));
+
+ PGTYPESnumeric_free(nres);
+ break;
+
+ case ECPGt_interval:
+ if (*pval == '"')
+ pval++;
+
+ for (endptr = pval; *endptr && *endptr != ',' && *endptr != '"' && *endptr != '}'; endptr++);
+ endchar = *endptr;
+ *endptr = '\0';
+ ires = PGTYPESinterval_from_asc(pval, &scan_length);
+ *endptr = endchar;
+
+ /* did we get an error? */
+ if (ires == NULL)
+ {
+ ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
+ lineno, pval, errno);
+
+ if (INFORMIX_MODE(compat))
+ {
+ /*
+ * Informix wants its own NULL value here instead
+ * of an error
+ */
+ ires = (interval *) ecpg_alloc(sizeof(interval), lineno);
+ if (!ires)
+ return false;
+
+ ECPGset_noind_null(ECPGt_interval, ires);
+ }
+ else
+ {
+ ecpg_raise(lineno, ECPG_INTERVAL_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+ else
+ {
+ if (*scan_length == '"')
+ scan_length++;
+
+ if (!isarray && garbage_left(isarray, &scan_length, compat))
+ {
+ free(ires);
+ ecpg_raise(lineno, ECPG_INTERVAL_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+ pval = scan_length;
+
+ PGTYPESinterval_copy(ires, (interval *) (var + offset * act_tuple));
+ free(ires);
+ break;
+
+ case ECPGt_date:
+ if (*pval == '"')
+ pval++;
+
+ for (endptr = pval; *endptr && *endptr != ',' && *endptr != '"' && *endptr != '}'; endptr++);
+ endchar = *endptr;
+ *endptr = '\0';
+ ddres = PGTYPESdate_from_asc(pval, &scan_length);
+ *endptr = endchar;
+
+ /* did we get an error? */
+ if (errno != 0)
+ {
+ ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
+ lineno, pval, errno);
+
+ if (INFORMIX_MODE(compat))
+ {
+ /*
+ * Informix wants its own NULL value here instead
+ * of an error
+ */
+ ECPGset_noind_null(ECPGt_date, &ddres);
+ }
+ else
+ {
+ ecpg_raise(lineno, ECPG_DATE_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+ else
+ {
+ if (*scan_length == '"')
+ scan_length++;
+
+ if (!isarray && garbage_left(isarray, &scan_length, compat))
+ {
+ ecpg_raise(lineno, ECPG_DATE_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+
+ *((date *) (var + offset * act_tuple)) = ddres;
+ pval = scan_length;
+ break;
+
+ case ECPGt_timestamp:
+ if (*pval == '"')
+ pval++;
+
+ for (endptr = pval; *endptr && *endptr != ',' && *endptr != '"' && *endptr != '}'; endptr++);
+ endchar = *endptr;
+ *endptr = '\0';
+ tres = PGTYPEStimestamp_from_asc(pval, &scan_length);
+ *endptr = endchar;
+
+ /* did we get an error? */
+ if (errno != 0)
+ {
+ ecpg_log("ecpg_get_data on line %d: RESULT %s; errno %d\n",
+ lineno, pval, errno);
+
+ if (INFORMIX_MODE(compat))
+ {
+ /*
+ * Informix wants its own NULL value here instead
+ * of an error
+ */
+ ECPGset_noind_null(ECPGt_timestamp, &tres);
+ }
+ else
+ {
+ ecpg_raise(lineno, ECPG_TIMESTAMP_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+ else
+ {
+ if (*scan_length == '"')
+ scan_length++;
+
+ if (!isarray && garbage_left(isarray, &scan_length, compat))
+ {
+ ecpg_raise(lineno, ECPG_TIMESTAMP_FORMAT,
+ ECPG_SQLSTATE_DATATYPE_MISMATCH, pval);
+ return false;
+ }
+ }
+
+ *((timestamp *) (var + offset * act_tuple)) = tres;
+ pval = scan_length;
+ break;
+
+ default:
+ ecpg_raise(lineno, ECPG_UNSUPPORTED,
+ ECPG_SQLSTATE_ECPG_INTERNAL_ERROR,
+ ecpg_type_name(type));
+ return false;
+ break;
+ }
+ if (ECPG_IS_ARRAY(isarray))
+ {
+ bool string = false;
+
+ /* set array to next entry */
+ ++act_tuple;
+
+ /* set pval to the next entry */
+
+ /*
+ * *pval != '\0' should not be needed, but is used as a safety
+ * guard
+ */
+ for (; *pval != '\0' && (string || (!array_delimiter(isarray, *pval) && !array_boundary(isarray, *pval))); ++pval)
+ if (*pval == '"')
+ string = string ? false : true;
+
+ if (array_delimiter(isarray, *pval))
+ ++pval;
+ }
+ }
+ } while (*pval != '\0' && !array_boundary(isarray, *pval));
+
+ return true;
+}
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
new file mode 100644
index 0000000..f1898de
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -0,0 +1,1002 @@
+/* dynamic SQL support routines
+ *
+ * src/interfaces/ecpg/ecpglib/descriptor.c
+ */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include "catalog/pg_type_d.h"
+#include "ecpg-pthread-win32.h"
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "sql3types.h"
+#include "sqlca.h"
+#include "sqlda.h"
+
+static void descriptor_free(struct descriptor *desc);
+
+/* We manage descriptors separately for each thread. */
+#ifdef ENABLE_THREAD_SAFETY
+static pthread_key_t descriptor_key;
+static pthread_once_t descriptor_once = PTHREAD_ONCE_INIT;
+
+static void descriptor_deallocate_all(struct descriptor *list);
+
+static void
+descriptor_destructor(void *arg)
+{
+ descriptor_deallocate_all(arg);
+}
+
+static void
+descriptor_key_init(void)
+{
+ pthread_key_create(&descriptor_key, descriptor_destructor);
+}
+
+static struct descriptor *
+get_descriptors(void)
+{
+ pthread_once(&descriptor_once, descriptor_key_init);
+ return (struct descriptor *) pthread_getspecific(descriptor_key);
+}
+
+static void
+set_descriptors(struct descriptor *value)
+{
+ pthread_setspecific(descriptor_key, value);
+}
+#else
+static struct descriptor *all_descriptors = NULL;
+
+#define get_descriptors() (all_descriptors)
+#define set_descriptors(value) do { all_descriptors = (value); } while(0)
+#endif
+
+/* old internal convenience function that might go away later */
+static PGresult *
+ecpg_result_by_descriptor(int line, const char *name)
+{
+ struct descriptor *desc = ecpg_find_desc(line, name);
+
+ if (desc == NULL)
+ return NULL;
+ return desc->result;
+}
+
+static unsigned int
+ecpg_dynamic_type_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, const char *desc_name, int *count)
+{
+ PGresult *ECPGresult;
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ ecpg_init_sqlca(sqlca);
+ ECPGresult = ecpg_result_by_descriptor(lineno, desc_name);
+ if (!ECPGresult)
+ return false;
+
+ *count = PQnfields(ECPGresult);
+ sqlca->sqlerrd[2] = 1;
+ ecpg_log("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;
+ 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;
+ case ECPGt_float:
+ *(float *) var = (float) value;
+ break;
+ case ECPGt_double:
+ *(double *) var = (double) value;
+ break;
+ default:
+ ecpg_raise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+set_int_item(int lineno, int *target, const void *var, enum ECPGttype vartype)
+{
+ switch (vartype)
+ {
+ case ECPGt_short:
+ *target = *(const short *) var;
+ break;
+ case ECPGt_int:
+ *target = *(const int *) var;
+ break;
+ case ECPGt_long:
+ *target = *(const long *) var;
+ break;
+ case ECPGt_unsigned_short:
+ *target = *(const unsigned short *) var;
+ break;
+ case ECPGt_unsigned_int:
+ *target = *(const unsigned int *) var;
+ break;
+ case ECPGt_unsigned_long:
+ *target = *(const unsigned long *) var;
+ break;
+ case ECPGt_long_long:
+ *target = *(const long long int *) var;
+ break;
+ case ECPGt_unsigned_long_long:
+ *target = *(const unsigned long long int *) var;
+ break;
+ case ECPGt_float:
+ *target = *(const float *) var;
+ break;
+ case ECPGt_double:
+ *target = *(const double *) var;
+ break;
+ default:
+ ecpg_raise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, 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:
+ case ECPGt_string:
+ strncpy((char *) var, value, varcharsize);
+ break;
+ case ECPGt_varchar:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) var;
+
+ if (varcharsize == 0)
+ memcpy(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:
+ ecpg_raise(lineno, ECPG_VAR_NOT_CHAR, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
+ return false;
+ }
+
+ return true;
+}
+
+#define RETURN_IF_NO_DATA if (ntuples < 1) \
+ { \
+ va_end(args); \
+ ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); \
+ return false; \
+ }
+
+bool
+ECPGget_desc(int lineno, const char *desc_name, int index,...)
+{
+ va_list args;
+ PGresult *ECPGresult;
+ enum ECPGdtype type;
+ int ntuples,
+ act_tuple;
+ struct variable data_var;
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ va_start(args, index);
+ ecpg_init_sqlca(sqlca);
+ ECPGresult = ecpg_result_by_descriptor(lineno, desc_name);
+ if (!ECPGresult)
+ {
+ va_end(args);
+ return false;
+ }
+
+ ntuples = PQntuples(ECPGresult);
+
+ if (index < 1 || index > PQnfields(ECPGresult))
+ {
+ ecpg_raise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX, NULL);
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("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):
+ RETURN_IF_NO_DATA;
+ 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:
+ RETURN_IF_NO_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))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: NAME = %s\n", PQfname(ECPGresult, index));
+ break;
+
+ case ECPGd_nullable:
+ if (!get_int_item(lineno, var, vartype, 1))
+ {
+ va_end(args);
+ return false;
+ }
+
+ break;
+
+ case ECPGd_key_member:
+ if (!get_int_item(lineno, var, vartype, 0))
+ {
+ va_end(args);
+ return false;
+ }
+
+ break;
+
+ case ECPGd_scale:
+ if (!get_int_item(lineno, var, vartype, (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("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))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: PRECISION = %d\n", PQfmod(ECPGresult, index) >> 16);
+ break;
+
+ case ECPGd_octet:
+ if (!get_int_item(lineno, var, vartype, PQfsize(ECPGresult, index)))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: OCTET_LENGTH = %d\n", PQfsize(ECPGresult, index));
+ break;
+
+ case ECPGd_length:
+ if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) - VARHDRSZ))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: LENGTH = %d\n", PQfmod(ECPGresult, index) - VARHDRSZ);
+ break;
+
+ case ECPGd_type:
+ if (!get_int_item(lineno, var, vartype, ecpg_dynamic_type(PQftype(ECPGresult, index))))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: TYPE = %d\n", ecpg_dynamic_type(PQftype(ECPGresult, index)));
+ break;
+
+ case ECPGd_di_code:
+ if (!get_int_item(lineno, var, vartype, ecpg_dynamic_type_DDT(PQftype(ECPGresult, index))))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: TYPE = %d\n", ecpg_dynamic_type_DDT(PQftype(ECPGresult, index)));
+ break;
+
+ case ECPGd_cardinality:
+ if (!get_int_item(lineno, var, vartype, PQntuples(ECPGresult)))
+ {
+ va_end(args);
+ return false;
+ }
+
+ ecpg_log("ECPGget_desc: CARDINALITY = %d\n", PQntuples(ECPGresult));
+ break;
+
+ case ECPGd_ret_length:
+ case ECPGd_ret_octet:
+
+ RETURN_IF_NO_DATA;
+
+ /*
+ * this is like ECPGstore_result
+ */
+ if (arrsize > 0 && ntuples > arrsize)
+ {
+ ecpg_log("ECPGget_desc on line %d: incorrect number of matches; %d don't fit into array of %ld\n",
+ lineno, ntuples, arrsize);
+ ecpg_raise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
+ va_end(args);
+ return false;
+ }
+ /* allocate storage if needed */
+ if (arrsize == 0 && *(void **) var == NULL)
+ {
+ void *mem = (void *) ecpg_auto_alloc(offset * ntuples, lineno);
+
+ if (!mem)
+ {
+ va_end(args);
+ return false;
+ }
+ *(void **) var = mem;
+ var = mem;
+ }
+
+ for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+ {
+ if (!get_int_item(lineno, var, vartype, PQgetlength(ECPGresult, act_tuple, index)))
+ {
+ va_end(args);
+ return false;
+ }
+ var = (char *) var + offset;
+ ecpg_log("ECPGget_desc: RETURNED[%d] = %d\n", act_tuple, PQgetlength(ECPGresult, act_tuple, index));
+ }
+ break;
+
+ default:
+ snprintf(type_str, sizeof(type_str), "%d", type);
+ ecpg_raise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str);
+ va_end(args);
+ return false;
+ }
+
+ type = va_arg(args, enum ECPGdtype);
+ }
+
+ if (data_var.type != ECPGt_EORT)
+ {
+ struct statement stmt;
+
+ memset(&stmt, 0, sizeof stmt);
+ stmt.lineno = lineno;
+
+ /* Make sure we do NOT honor the locale for numeric input */
+ /* since the database gives the standard decimal point */
+ /* (see comments in execute.c) */
+#ifdef HAVE_USELOCALE
+
+ /*
+ * To get here, the above PQnfields() test must have found nonzero
+ * fields. One needs a connection to create such a descriptor. (EXEC
+ * SQL SET DESCRIPTOR can populate the descriptor's "items", but it
+ * can't change the descriptor's PQnfields().) Any successful
+ * connection initializes ecpg_clocale.
+ */
+ Assert(ecpg_clocale);
+ stmt.oldlocale = uselocale(ecpg_clocale);
+#else
+#ifdef HAVE__CONFIGTHREADLOCALE
+ stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+ stmt.oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
+ setlocale(LC_NUMERIC, "C");
+#endif
+
+ /* desperate try to guess something sensible */
+ stmt.connection = ecpg_get_connection(NULL);
+ ecpg_store_result(ECPGresult, index, &stmt, &data_var);
+
+#ifdef HAVE_USELOCALE
+ if (stmt.oldlocale != (locale_t) 0)
+ uselocale(stmt.oldlocale);
+#else
+ if (stmt.oldlocale)
+ {
+ setlocale(LC_NUMERIC, stmt.oldlocale);
+ ecpg_free(stmt.oldlocale);
+ }
+#ifdef HAVE__CONFIGTHREADLOCALE
+ if (stmt.oldthreadlocale != -1)
+ (void) _configthreadlocale(stmt.oldthreadlocale);
+#endif
+#endif
+ }
+ else if (data_var.ind_type != ECPGt_NO_INDICATOR && data_var.ind_pointer != NULL)
+
+ /*
+ * ind_type != NO_INDICATOR should always have ind_pointer != NULL but
+ * since this might be changed manually in the .c file let's play it
+ * safe
+ */
+ {
+ /*
+ * 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)
+ {
+ ecpg_log("ECPGget_desc on line %d: incorrect number of matches (indicator); %d don't fit into array of %ld\n",
+ lineno, ntuples, data_var.ind_arrsize);
+ ecpg_raise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
+ va_end(args);
+ return false;
+ }
+
+ /* allocate storage if needed */
+ if (data_var.ind_arrsize == 0 && data_var.ind_value == NULL)
+ {
+ void *mem = (void *) ecpg_auto_alloc(data_var.ind_offset * ntuples, lineno);
+
+ if (!mem)
+ {
+ va_end(args);
+ return false;
+ }
+ *(void **) data_var.ind_pointer = mem;
+ 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)))
+ {
+ va_end(args);
+ return false;
+ }
+ data_var.ind_value = (char *) data_var.ind_value + data_var.ind_offset;
+ ecpg_log("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index));
+ }
+ }
+ sqlca->sqlerrd[2] = ntuples;
+ va_end(args);
+ return true;
+}
+
+#undef RETURN_IF_NO_DATA
+
+bool
+ECPGset_desc_header(int lineno, const char *desc_name, int count)
+{
+ struct descriptor *desc = ecpg_find_desc(lineno, desc_name);
+
+ if (desc == NULL)
+ return false;
+ desc->count = count;
+ return true;
+}
+
+static void
+set_desc_attr(struct descriptor_item *desc_item, struct variable *var,
+ char *tobeinserted)
+{
+ if (var->type != ECPGt_bytea)
+ desc_item->is_binary = false;
+
+ else
+ {
+ struct ECPGgeneric_bytea *variable =
+ (struct ECPGgeneric_bytea *) (var->value);
+
+ desc_item->is_binary = true;
+ desc_item->data_len = variable->len;
+ }
+
+ ecpg_free(desc_item->data); /* free() takes care of a potential NULL value */
+ desc_item->data = (char *) tobeinserted;
+}
+
+
+bool
+ECPGset_desc(int lineno, const char *desc_name, int index,...)
+{
+ va_list args;
+ struct descriptor *desc;
+ struct descriptor_item *desc_item;
+ struct variable *var;
+
+ desc = ecpg_find_desc(lineno, desc_name);
+ if (desc == NULL)
+ return false;
+
+ for (desc_item = desc->items; desc_item; desc_item = desc_item->next)
+ {
+ if (desc_item->num == index)
+ break;
+ }
+
+ if (desc_item == NULL)
+ {
+ desc_item = (struct descriptor_item *) ecpg_alloc(sizeof(*desc_item), lineno);
+ if (!desc_item)
+ return false;
+ desc_item->num = index;
+ if (desc->count < index)
+ desc->count = index;
+ desc_item->next = desc->items;
+ desc->items = desc_item;
+ }
+
+ if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno)))
+ return false;
+
+ va_start(args, index);
+
+ for (;;)
+ {
+ enum ECPGdtype itemtype;
+ char *tobeinserted = NULL;
+
+ itemtype = va_arg(args, enum ECPGdtype);
+
+ if (itemtype == ECPGd_EODT)
+ break;
+
+ var->type = va_arg(args, enum ECPGttype);
+ var->pointer = va_arg(args, char *);
+
+ var->varcharsize = va_arg(args, long);
+ var->arrsize = va_arg(args, long);
+ var->offset = va_arg(args, long);
+
+ if (var->arrsize == 0 || var->varcharsize == 0)
+ var->value = *((char **) (var->pointer));
+ else
+ var->value = var->pointer;
+
+ /*
+ * negative values are used to indicate an array without given bounds
+ */
+ /* reset to zero for us */
+ if (var->arrsize < 0)
+ var->arrsize = 0;
+ if (var->varcharsize < 0)
+ var->varcharsize = 0;
+
+ var->next = NULL;
+
+ switch (itemtype)
+ {
+ case ECPGd_data:
+ {
+ if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
+ {
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+
+ set_desc_attr(desc_item, var, tobeinserted);
+ tobeinserted = NULL;
+ break;
+ }
+
+ case ECPGd_indicator:
+ set_int_item(lineno, &desc_item->indicator, var->pointer, var->type);
+ break;
+
+ case ECPGd_length:
+ set_int_item(lineno, &desc_item->length, var->pointer, var->type);
+ break;
+
+ case ECPGd_precision:
+ set_int_item(lineno, &desc_item->precision, var->pointer, var->type);
+ break;
+
+ case ECPGd_scale:
+ set_int_item(lineno, &desc_item->scale, var->pointer, var->type);
+ break;
+
+ case ECPGd_type:
+ set_int_item(lineno, &desc_item->type, var->pointer, var->type);
+ break;
+
+ default:
+ {
+ char type_str[20];
+
+ snprintf(type_str, sizeof(type_str), "%d", itemtype);
+ ecpg_raise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str);
+ ecpg_free(var);
+ va_end(args);
+ return false;
+ }
+ }
+ }
+ ecpg_free(var);
+ va_end(args);
+
+ return true;
+}
+
+/* Free the descriptor and items in it. */
+static void
+descriptor_free(struct descriptor *desc)
+{
+ struct descriptor_item *desc_item;
+
+ for (desc_item = desc->items; desc_item;)
+ {
+ struct descriptor_item *di;
+
+ ecpg_free(desc_item->data);
+ di = desc_item;
+ desc_item = desc_item->next;
+ ecpg_free(di);
+ }
+
+ ecpg_free(desc->name);
+ PQclear(desc->result);
+ ecpg_free(desc);
+}
+
+bool
+ECPGdeallocate_desc(int line, const char *name)
+{
+ struct descriptor *desc;
+ struct descriptor *prev;
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(line, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ ecpg_init_sqlca(sqlca);
+ for (desc = get_descriptors(), prev = NULL; desc; prev = desc, desc = desc->next)
+ {
+ if (strcmp(name, desc->name) == 0)
+ {
+ if (prev)
+ prev->next = desc->next;
+ else
+ set_descriptors(desc->next);
+ descriptor_free(desc);
+ return true;
+ }
+ }
+ ecpg_raise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name);
+ return false;
+}
+
+#ifdef ENABLE_THREAD_SAFETY
+
+/* Deallocate all descriptors in the list */
+static void
+descriptor_deallocate_all(struct descriptor *list)
+{
+ while (list)
+ {
+ struct descriptor *next = list->next;
+
+ descriptor_free(list);
+ list = next;
+ }
+}
+#endif /* ENABLE_THREAD_SAFETY */
+
+bool
+ECPGallocate_desc(int line, const char *name)
+{
+ struct descriptor *new;
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(line, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ ecpg_init_sqlca(sqlca);
+ new = (struct descriptor *) ecpg_alloc(sizeof(struct descriptor), line);
+ if (!new)
+ return false;
+ new->next = get_descriptors();
+ new->name = ecpg_alloc(strlen(name) + 1, line);
+ if (!new->name)
+ {
+ ecpg_free(new);
+ return false;
+ }
+ new->count = -1;
+ new->items = NULL;
+ new->result = PQmakeEmptyPGresult(NULL, 0);
+ if (!new->result)
+ {
+ ecpg_free(new->name);
+ ecpg_free(new);
+ ecpg_raise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+ strcpy(new->name, name);
+ set_descriptors(new);
+ return true;
+}
+
+/* Find descriptor with name in the connection. */
+struct descriptor *
+ecpg_find_desc(int line, const char *name)
+{
+ struct descriptor *desc;
+
+ for (desc = get_descriptors(); desc; desc = desc->next)
+ {
+ if (strcmp(name, desc->name) == 0)
+ return desc;
+ }
+
+ ecpg_raise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name);
+ return NULL; /* not found */
+}
+
+bool
+ECPGdescribe(int line, int compat, bool input, const char *connection_name, const char *stmt_name,...)
+{
+ bool ret = false;
+ struct connection *con;
+ struct prepared_statement *prep;
+ PGresult *res;
+ va_list args;
+
+ /* DESCRIBE INPUT is not yet supported */
+ if (input)
+ {
+ ecpg_raise(line, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, "DESCRIBE INPUT");
+ return ret;
+ }
+
+ con = ecpg_get_connection(connection_name);
+ if (!con)
+ {
+ ecpg_raise(line, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
+ connection_name ? connection_name : ecpg_gettext("NULL"));
+ return ret;
+ }
+ prep = ecpg_find_prepared_statement(stmt_name, con, NULL);
+ if (!prep)
+ {
+ ecpg_raise(line, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt_name);
+ return ret;
+ }
+
+ va_start(args, stmt_name);
+
+ for (;;)
+ {
+ enum ECPGttype type;
+ void *ptr;
+
+ /* variable type */
+ type = va_arg(args, enum ECPGttype);
+
+ if (type == ECPGt_EORT)
+ break;
+
+ /* rest of variable parameters */
+ ptr = va_arg(args, void *);
+ (void) va_arg(args, long); /* skip args */
+ (void) va_arg(args, long);
+ (void) va_arg(args, long);
+
+ /* variable indicator */
+ (void) va_arg(args, enum ECPGttype);
+ (void) va_arg(args, void *); /* skip args */
+ (void) va_arg(args, long);
+ (void) va_arg(args, long);
+ (void) va_arg(args, long);
+
+ switch (type)
+ {
+ case ECPGt_descriptor:
+ {
+ char *name = ptr;
+ struct descriptor *desc = ecpg_find_desc(line, name);
+
+ if (desc == NULL)
+ break;
+
+ res = PQdescribePrepared(con->connection, stmt_name);
+ if (!ecpg_check_PQresult(res, line, con->connection, compat))
+ break;
+
+ if (desc->result != NULL)
+ PQclear(desc->result);
+
+ desc->result = res;
+ ret = true;
+ break;
+ }
+ case ECPGt_sqlda:
+ {
+ if (INFORMIX_MODE(compat))
+ {
+ struct sqlda_compat **_sqlda = ptr;
+ struct sqlda_compat *sqlda;
+
+ res = PQdescribePrepared(con->connection, stmt_name);
+ if (!ecpg_check_PQresult(res, line, con->connection, compat))
+ break;
+
+ sqlda = ecpg_build_compat_sqlda(line, res, -1, compat);
+ if (sqlda)
+ {
+ struct sqlda_compat *sqlda_old = *_sqlda;
+ struct sqlda_compat *sqlda_old1;
+
+ while (sqlda_old)
+ {
+ sqlda_old1 = sqlda_old->desc_next;
+ free(sqlda_old);
+ sqlda_old = sqlda_old1;
+ }
+
+ *_sqlda = sqlda;
+ ret = true;
+ }
+
+ PQclear(res);
+ }
+ else
+ {
+ struct sqlda_struct **_sqlda = ptr;
+ struct sqlda_struct *sqlda;
+
+ res = PQdescribePrepared(con->connection, stmt_name);
+ if (!ecpg_check_PQresult(res, line, con->connection, compat))
+ break;
+
+ sqlda = ecpg_build_native_sqlda(line, res, -1, compat);
+ if (sqlda)
+ {
+ struct sqlda_struct *sqlda_old = *_sqlda;
+ struct sqlda_struct *sqlda_old1;
+
+ while (sqlda_old)
+ {
+ sqlda_old1 = sqlda_old->desc_next;
+ free(sqlda_old);
+ sqlda_old = sqlda_old1;
+ }
+
+ *_sqlda = sqlda;
+ ret = true;
+ }
+
+ PQclear(res);
+ }
+ break;
+ }
+ default:
+ /* nothing else may come */
+ ;
+ }
+ }
+
+ va_end(args);
+
+ return ret;
+}
diff --git a/src/interfaces/ecpg/ecpglib/ecpglib_extern.h b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
new file mode 100644
index 0000000..c438cfb
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/ecpglib_extern.h
@@ -0,0 +1,257 @@
+/* src/interfaces/ecpg/ecpglib/ecpglib_extern.h */
+
+#ifndef _ECPG_ECPGLIB_EXTERN_H
+#define _ECPG_ECPGLIB_EXTERN_H
+
+#include "ecpg_config.h"
+#include "ecpgtype.h"
+#include "libpq-fe.h"
+#include "sqlca.h"
+#include "sqlda-compat.h"
+#include "sqlda-native.h"
+
+#ifndef CHAR_BIT
+#include <limits.h>
+#endif
+#ifdef LOCALE_T_IN_XLOCALE
+#include <xlocale.h>
+#endif
+
+enum COMPAT_MODE
+{
+ ECPG_COMPAT_PGSQL = 0, ECPG_COMPAT_INFORMIX, ECPG_COMPAT_INFORMIX_SE, ECPG_COMPAT_ORACLE
+};
+
+extern bool ecpg_internal_regression_mode;
+
+#define INFORMIX_MODE(X) ((X) == ECPG_COMPAT_INFORMIX || (X) == ECPG_COMPAT_INFORMIX_SE)
+#define ORACLE_MODE(X) ((X) == ECPG_COMPAT_ORACLE)
+
+enum ARRAY_TYPE
+{
+ ECPG_ARRAY_ERROR, ECPG_ARRAY_NOT_SET, ECPG_ARRAY_ARRAY, ECPG_ARRAY_VECTOR, ECPG_ARRAY_NONE
+};
+
+#define ECPG_IS_ARRAY(X) ((X) == ECPG_ARRAY_ARRAY || (X) == ECPG_ARRAY_VECTOR)
+
+/* A generic varchar type. */
+struct ECPGgeneric_varchar
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/* A generic bytea type. */
+struct ECPGgeneric_bytea
+{
+ int len;
+ char arr[FLEXIBLE_ARRAY_MEMBER];
+};
+
+/*
+ * type information cache
+ */
+
+struct ECPGtype_information_cache
+{
+ struct ECPGtype_information_cache *next;
+ int oid;
+ enum ARRAY_TYPE isarray;
+};
+
+#ifdef HAVE_USELOCALE
+extern locale_t ecpg_clocale; /* LC_NUMERIC=C */
+#endif
+
+/* structure to store one statement */
+struct statement
+{
+ int lineno;
+ char *command;
+ char *name;
+ struct connection *connection;
+ enum COMPAT_MODE compat;
+ bool force_indicator;
+ enum ECPG_statement_type statement_type;
+ bool questionmarks;
+ struct variable *inlist;
+ struct variable *outlist;
+#ifdef HAVE_USELOCALE
+ locale_t oldlocale;
+#else
+ char *oldlocale;
+#ifdef HAVE__CONFIGTHREADLOCALE
+ int oldthreadlocale;
+#endif
+#endif
+ int nparams;
+ char **paramvalues;
+ int *paramlengths;
+ int *paramformats;
+ PGresult *results;
+};
+
+/* structure to store prepared statements for a connection */
+struct prepared_statement
+{
+ char *name;
+ bool prepared;
+ struct statement *stmt;
+ struct prepared_statement *next;
+};
+
+/* structure to store connections */
+struct connection
+{
+ char *name;
+ PGconn *connection;
+ bool autocommit;
+ struct ECPGtype_information_cache *cache_head;
+ struct prepared_statement *prep_stmts;
+ struct connection *next;
+};
+
+/* structure to store descriptors */
+struct descriptor
+{
+ char *name;
+ PGresult *result;
+ struct descriptor *next;
+ int count;
+ struct descriptor_item *items;
+};
+
+struct descriptor_item
+{
+ int num;
+ char *data;
+ int indicator;
+ int length;
+ int precision;
+ int scale;
+ int type;
+ bool is_binary;
+ int data_len;
+ struct descriptor_item *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;
+};
+
+struct var_list
+{
+ int number;
+ void *pointer;
+ struct var_list *next;
+};
+
+extern struct var_list *ivlist;
+
+/* Here are some methods used by the lib. */
+
+bool ecpg_add_mem(void *ptr, int lineno);
+
+bool ecpg_get_data(const PGresult *, int, int, int, enum ECPGttype type,
+ enum ECPGttype, char *, char *, long, long, long,
+ enum ARRAY_TYPE, enum COMPAT_MODE, bool);
+
+#ifdef ENABLE_THREAD_SAFETY
+void ecpg_pthreads_init(void);
+#endif
+struct connection *ecpg_get_connection(const char *);
+char *ecpg_alloc(long, int);
+char *ecpg_auto_alloc(long, int);
+char *ecpg_realloc(void *, long, int);
+void ecpg_free(void *);
+bool ecpg_init(const struct connection *, const char *, const int);
+char *ecpg_strdup(const char *, int);
+const char *ecpg_type_name(enum ECPGttype);
+int ecpg_dynamic_type(Oid);
+int sqlda_dynamic_type(Oid, enum COMPAT_MODE);
+void ecpg_clear_auto_mem(void);
+
+struct descriptor *ecpg_find_desc(int line, const char *name);
+
+struct prepared_statement *ecpg_find_prepared_statement(const char *,
+ struct connection *, struct prepared_statement **);
+
+bool ecpg_store_result(const PGresult *results, int act_field,
+ const struct statement *stmt, struct variable *var);
+bool ecpg_store_input(const int, const bool, const struct variable *, char **, bool);
+void ecpg_free_params(struct statement *stmt, bool print);
+bool ecpg_do_prologue(int, const int, const int, const char *, const bool,
+ enum ECPG_statement_type, const char *, va_list,
+ struct statement **);
+bool ecpg_build_params(struct statement *);
+bool ecpg_autostart_transaction(struct statement *stmt);
+bool ecpg_execute(struct statement *stmt);
+bool ecpg_process_output(struct statement *, bool);
+void ecpg_do_epilogue(struct statement *);
+bool ecpg_do(const int, const int, const int, const char *, const bool,
+ const int, const char *, va_list);
+
+bool ecpg_check_PQresult(PGresult *, int, PGconn *, enum COMPAT_MODE);
+void ecpg_raise(int line, int code, const char *sqlstate, const char *str);
+void ecpg_raise_backend(int line, PGresult *result, PGconn *conn, int compat);
+char *ecpg_prepared(const char *, struct connection *);
+bool ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *conn);
+void ecpg_log(const char *format,...) pg_attribute_printf(1, 2);
+bool ecpg_auto_prepare(int, const char *, const int, char **, const char *);
+bool ecpg_register_prepared_stmt(struct statement *);
+void ecpg_init_sqlca(struct sqlca_t *sqlca);
+
+struct sqlda_compat *ecpg_build_compat_sqlda(int, PGresult *, int, enum COMPAT_MODE);
+void ecpg_set_compat_sqlda(int, struct sqlda_compat **, const PGresult *, int, enum COMPAT_MODE);
+struct sqlda_struct *ecpg_build_native_sqlda(int, PGresult *, int, enum COMPAT_MODE);
+void ecpg_set_native_sqlda(int, struct sqlda_struct **, const PGresult *, int, enum COMPAT_MODE);
+unsigned ecpg_hex_dec_len(unsigned srclen);
+unsigned ecpg_hex_enc_len(unsigned srclen);
+unsigned ecpg_hex_encode(const char *src, unsigned len, char *dst);
+
+#ifdef ENABLE_NLS
+extern char *ecpg_gettext(const char *msgid) pg_attribute_format_arg(1);
+#else
+#define ecpg_gettext(x) (x)
+#endif
+
+/* SQLSTATE values generated or processed by ecpglib (intentionally
+ * not exported -- users should refer to the codes directly) */
+
+#define ECPG_SQLSTATE_NO_DATA "02000"
+#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS "07001"
+#define ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS "07002"
+#define ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION "07006"
+#define ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX "07009"
+#define ECPG_SQLSTATE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION "08001"
+#define ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST "08003"
+#define ECPG_SQLSTATE_TRANSACTION_RESOLUTION_UNKNOWN "08007"
+#define ECPG_SQLSTATE_CARDINALITY_VIOLATION "21000"
+#define ECPG_SQLSTATE_NULL_VALUE_NO_INDICATOR_PARAMETER "22002"
+#define ECPG_SQLSTATE_ACTIVE_SQL_TRANSACTION "25001"
+#define ECPG_SQLSTATE_NO_ACTIVE_SQL_TRANSACTION "25P01"
+#define ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME "26000"
+#define ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME "33000"
+#define ECPG_SQLSTATE_INVALID_CURSOR_NAME "34000"
+#define ECPG_SQLSTATE_SYNTAX_ERROR "42601"
+#define ECPG_SQLSTATE_DATATYPE_MISMATCH "42804"
+#define ECPG_SQLSTATE_DUPLICATE_CURSOR "42P03"
+
+/* implementation-defined internal errors of ecpg */
+#define ECPG_SQLSTATE_ECPG_INTERNAL_ERROR "YE000"
+#define ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY "YE001"
+
+#endif /* _ECPG_ECPGLIB_EXTERN_H */
diff --git a/src/interfaces/ecpg/ecpglib/error.c b/src/interfaces/ecpg/ecpglib/error.c
new file mode 100644
index 0000000..26fdcdb
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/error.c
@@ -0,0 +1,346 @@
+/* src/interfaces/ecpg/ecpglib/error.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "sqlca.h"
+
+void
+ecpg_raise(int line, int code, const char *sqlstate, const char *str)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_log("out of memory");
+ ECPGfree_auto_mem();
+ return;
+ }
+
+ sqlca->sqlcode = code;
+ strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
+
+ switch (code)
+ {
+ case ECPG_NOT_FOUND:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("no data found on line %d"), line);
+ break;
+
+ case ECPG_OUT_OF_MEMORY:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("out of memory on line %d"), line);
+ break;
+
+ case ECPG_UNSUPPORTED:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("unsupported type \"%s\" on line %d"), str, line);
+ break;
+
+ case ECPG_TOO_MANY_ARGUMENTS:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("too many arguments on line %d"), line);
+ break;
+
+ case ECPG_TOO_FEW_ARGUMENTS:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("too few arguments on line %d"), line);
+ break;
+
+ case ECPG_INT_FORMAT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("invalid input syntax for type int: \"%s\", on line %d"), str, line);
+ break;
+
+ case ECPG_UINT_FORMAT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("invalid input syntax for type unsigned int: \"%s\", on line %d"), str, line);
+ break;
+
+ case ECPG_FLOAT_FORMAT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("invalid input syntax for floating-point type: \"%s\", on line %d"), str, line);
+ break;
+
+ case ECPG_CONVERT_BOOL:
+ if (str)
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("invalid syntax for type boolean: \"%s\", on line %d"), str, line);
+ else
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("could not convert boolean value: size mismatch, on line %d"), line);
+ break;
+
+ case ECPG_EMPTY:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("empty query on line %d"), line);
+ break;
+
+ case ECPG_MISSING_INDICATOR:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("null value without indicator on line %d"), line);
+ break;
+
+ case ECPG_NO_ARRAY:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("variable does not have an array type on line %d"), line);
+ break;
+
+ case ECPG_DATA_NOT_ARRAY:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("data read from server is not an array on line %d"), line);
+ break;
+
+ case ECPG_ARRAY_INSERT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("inserting an array of variables is not supported on line %d"), line);
+ break;
+
+ case ECPG_NO_CONN:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("connection \"%s\" does not exist on line %d"), str, line);
+ break;
+
+ case ECPG_NOT_CONN:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("not connected to connection \"%s\" on line %d"), str, line);
+ break;
+
+ case ECPG_INVALID_STMT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("invalid statement name \"%s\" on line %d"), str, line);
+ break;
+
+ case ECPG_UNKNOWN_DESCRIPTOR:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("descriptor \"%s\" not found on line %d"), str, line);
+ break;
+
+ case ECPG_INVALID_DESCRIPTOR_INDEX:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("descriptor index out of range on line %d"), line);
+ break;
+
+ case ECPG_UNKNOWN_DESCRIPTOR_ITEM:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("unrecognized descriptor item \"%s\" on line %d"), str, line);
+ break;
+
+ case ECPG_VAR_NOT_NUMERIC:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("variable does not have a numeric type on line %d"), line);
+ break;
+
+ case ECPG_VAR_NOT_CHAR:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("variable does not have a character type on line %d"), line);
+ break;
+
+ case ECPG_TRANS:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("error in transaction processing on line %d"), line);
+ break;
+
+ case ECPG_CONNECT:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("could not connect to database \"%s\" on line %d"), str, line);
+ break;
+
+ default:
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc),
+ /*------
+ translator: this string will be truncated at 149 characters expanded. */
+ ecpg_gettext("SQL error %d on line %d"), code, line);
+ break;
+ }
+
+ sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
+ ecpg_log("raising sqlcode %d on line %d: %s\n", code, line, sqlca->sqlerrm.sqlerrmc);
+
+ /* free all memory we have allocated for the user */
+ ECPGfree_auto_mem();
+}
+
+void
+ecpg_raise_backend(int line, PGresult *result, PGconn *conn, int compat)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ char *sqlstate;
+ char *message;
+
+ if (sqlca == NULL)
+ {
+ ecpg_log("out of memory");
+ ECPGfree_auto_mem();
+ return;
+ }
+
+ /*
+ * PQresultErrorField will return NULL if "result" is NULL, or if there is
+ * no such field, which will happen for libpq-generated errors. Fall back
+ * to PQerrorMessage in such cases.
+ */
+ sqlstate = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+ if (sqlstate == NULL)
+ sqlstate = ECPG_SQLSTATE_ECPG_INTERNAL_ERROR;
+ message = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
+ if (message == NULL)
+ message = PQerrorMessage(conn);
+
+ if (strcmp(sqlstate, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR) == 0)
+ {
+ /*
+ * we might get here if the connection breaks down, so let's check for
+ * this instead of giving just the generic internal error
+ */
+ if (PQstatus(conn) == CONNECTION_BAD)
+ {
+ sqlstate = "57P02";
+ message = ecpg_gettext("the connection to the server was lost");
+ }
+ }
+
+ /* copy error message */
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "%s on line %d", message, line);
+ sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
+
+ /* copy SQLSTATE */
+ strncpy(sqlca->sqlstate, sqlstate, sizeof(sqlca->sqlstate));
+
+ /* assign SQLCODE for backward compatibility */
+ if (strncmp(sqlca->sqlstate, "23505", sizeof(sqlca->sqlstate)) == 0)
+ sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_DUPLICATE_KEY : ECPG_DUPLICATE_KEY;
+ else if (strncmp(sqlca->sqlstate, "21000", sizeof(sqlca->sqlstate)) == 0)
+ sqlca->sqlcode = INFORMIX_MODE(compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_SUBSELECT_NOT_ONE;
+ else
+ sqlca->sqlcode = ECPG_PGSQL;
+
+ ecpg_log("raising sqlstate %.*s (sqlcode %ld): %s\n",
+ (int) sizeof(sqlca->sqlstate), sqlca->sqlstate, sqlca->sqlcode, sqlca->sqlerrm.sqlerrmc);
+
+ /* free all memory we have allocated for the user */
+ ECPGfree_auto_mem();
+}
+
+/* filter out all error codes */
+bool
+ecpg_check_PQresult(PGresult *results, int lineno, PGconn *connection, enum COMPAT_MODE compat)
+{
+ if (results == NULL)
+ {
+ ecpg_log("ecpg_check_PQresult on line %d: no result - %s", lineno, PQerrorMessage(connection));
+ ecpg_raise_backend(lineno, NULL, connection, compat);
+ return false;
+ }
+
+ switch (PQresultStatus(results))
+ {
+
+ case PGRES_TUPLES_OK:
+ return true;
+ break;
+ case PGRES_EMPTY_QUERY:
+ /* do nothing */
+ ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+ PQclear(results);
+ return false;
+ break;
+ case PGRES_COMMAND_OK:
+ return true;
+ break;
+ case PGRES_NONFATAL_ERROR:
+ case PGRES_FATAL_ERROR:
+ case PGRES_BAD_RESPONSE:
+ ecpg_log("ecpg_check_PQresult on line %d: bad response - %s", lineno, PQresultErrorMessage(results));
+ ecpg_raise_backend(lineno, results, connection, compat);
+ PQclear(results);
+ return false;
+ break;
+ case PGRES_COPY_OUT:
+ return true;
+ break;
+ case PGRES_COPY_IN:
+ ecpg_log("ecpg_check_PQresult on line %d: COPY IN data transfer in progress\n", lineno);
+ PQendcopy(connection);
+ PQclear(results);
+ return false;
+ break;
+ default:
+ ecpg_log("ecpg_check_PQresult on line %d: unknown execution status type\n",
+ lineno);
+ ecpg_raise_backend(lineno, results, connection, compat);
+ PQclear(results);
+ return false;
+ break;
+ }
+}
+
+/* print out an error message */
+void
+sqlprint(void)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_log("out of memory");
+ return;
+ }
+
+ sqlca->sqlerrm.sqlerrmc[sqlca->sqlerrm.sqlerrml] = '\0';
+ fprintf(stderr, ecpg_gettext("SQL error: %s\n"), sqlca->sqlerrm.sqlerrmc);
+}
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
new file mode 100644
index 0000000..2ebe665
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -0,0 +1,2301 @@
+/* src/interfaces/ecpg/ecpglib/execute.c */
+
+/*
+ * The aim is to get a simpler interface to the database routines.
+ * All the tedious 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 */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include <math.h>
+
+#include "catalog/pg_type_d.h"
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "pgtypes_date.h"
+#include "pgtypes_interval.h"
+#include "pgtypes_numeric.h"
+#include "pgtypes_timestamp.h"
+#include "sql3types.h"
+#include "sqlca.h"
+#include "sqlda-compat.h"
+#include "sqlda-native.h"
+
+/*
+ * This function returns a newly malloced string that has ' and \
+ * escaped.
+ */
+static char *
+quote_postgres(char *arg, bool quote, int lineno)
+{
+ char *res;
+ size_t length;
+ size_t escaped_len;
+ size_t buffer_len;
+
+ /*
+ * if quote is false we just need to store things in a descriptor they
+ * will be quoted once they are inserted in a statement
+ */
+ if (!quote)
+ return arg;
+ else
+ {
+ length = strlen(arg);
+ buffer_len = 2 * length + 1;
+ res = (char *) ecpg_alloc(buffer_len + 3, lineno);
+ if (!res)
+ return res;
+ escaped_len = PQescapeString(res + 1, arg, buffer_len);
+ if (length == escaped_len)
+ {
+ res[0] = res[escaped_len + 1] = '\'';
+ res[escaped_len + 2] = '\0';
+ }
+ else
+ {
+ /*
+ * We don't know if the target database is using
+ * standard_conforming_strings, so we always use E'' strings.
+ */
+ memmove(res + 2, res + 1, escaped_len);
+ res[0] = ESCAPE_STRING_SYNTAX;
+ res[1] = res[escaped_len + 2] = '\'';
+ res[escaped_len + 3] = '\0';
+ }
+ ecpg_free(arg);
+ return res;
+ }
+}
+
+static void
+free_variable(struct variable *var)
+{
+ struct variable *var_next;
+
+ while (var)
+ {
+ var_next = var->next;
+ ecpg_free(var);
+ var = var_next;
+ }
+}
+
+static void
+free_statement(struct statement *stmt)
+{
+ if (stmt == NULL)
+ return;
+ free_variable(stmt->inlist);
+ free_variable(stmt->outlist);
+ ecpg_free(stmt->command);
+ ecpg_free(stmt->name);
+#ifndef HAVE_USELOCALE
+ ecpg_free(stmt->oldlocale);
+#endif
+ ecpg_free(stmt);
+}
+
+static int
+next_insert(char *text, int pos, bool questionmarks, bool std_strings)
+{
+ bool string = false;
+ int p = pos;
+
+ for (; text[p] != '\0'; p++)
+ {
+ if (string && !std_strings && text[p] == '\\') /* escape character */
+ p++;
+ else if (text[p] == '\'')
+ string = string ? false : true;
+ else if (!string)
+ {
+ if (text[p] == '$' && isdigit((unsigned char) text[p + 1]))
+ {
+ /* this can be either a dollar quote or a variable */
+ int i;
+
+ for (i = p + 1; isdigit((unsigned char) text[i]); i++)
+ /* empty loop body */ ;
+ if (!isalpha((unsigned char) text[i]) &&
+ isascii((unsigned char) text[i]) && text[i] != '_')
+ /* not dollar delimited quote */
+ return p;
+ }
+ else if (questionmarks && text[p] == '?')
+ {
+ /* also allow old style placeholders */
+ return p;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static bool
+ecpg_type_infocache_push(struct ECPGtype_information_cache **cache, int oid, enum ARRAY_TYPE isarray, int lineno)
+{
+ struct ECPGtype_information_cache *new_entry
+ = (struct ECPGtype_information_cache *) ecpg_alloc(sizeof(struct ECPGtype_information_cache), lineno);
+
+ if (new_entry == NULL)
+ return false;
+
+ new_entry->oid = oid;
+ new_entry->isarray = isarray;
+ new_entry->next = *cache;
+ *cache = new_entry;
+ return true;
+}
+
+static enum ARRAY_TYPE
+ecpg_is_type_an_array(int type, const struct statement *stmt, const struct variable *var)
+{
+ char *array_query;
+ enum ARRAY_TYPE isarray = ECPG_ARRAY_NOT_SET;
+ 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 ECPG_ARRAY_NONE
+
+ /* populate cache with well known types to speed things up */
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BOOLOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BYTEAOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CHAROID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), NAMEOID, not_an_array_in_ecpg, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT8OID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT2OID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT2VECTOROID, ECPG_ARRAY_VECTOR, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT4OID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), REGPROCOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TEXTOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), OIDOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIDOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), XIDOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIDOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), OIDVECTOROID, ECPG_ARRAY_VECTOR, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), POINTOID, ECPG_ARRAY_VECTOR, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), LSEGOID, ECPG_ARRAY_VECTOR, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), PATHOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BOXOID, ECPG_ARRAY_VECTOR, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), POLYGONOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), LINEOID, ECPG_ARRAY_VECTOR, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), FLOAT4OID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), FLOAT8OID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), UNKNOWNOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIRCLEOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), MONEYOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INETOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIDROID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BPCHAROID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), VARCHAROID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), DATEOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMEOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMESTAMPOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMESTAMPTZOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INTERVALOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMETZOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BITOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), VARBITOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), NUMERICOID, ECPG_ARRAY_NONE, stmt->lineno))
+ return ECPG_ARRAY_ERROR;
+ }
+
+ 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 *) ecpg_alloc(strlen("select typlen from pg_type where oid= and typelem<>0") + 11, stmt->lineno);
+ if (array_query == NULL)
+ return ECPG_ARRAY_ERROR;
+
+ sprintf(array_query, "select typlen from pg_type where oid=%d and typelem<>0", type);
+ query = PQexec(stmt->connection->connection, array_query);
+ ecpg_free(array_query);
+ if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
+ return ECPG_ARRAY_ERROR;
+ else if (PQresultStatus(query) == PGRES_TUPLES_OK)
+ {
+ if (PQntuples(query) == 0)
+ isarray = ECPG_ARRAY_NONE;
+ else
+ {
+ isarray = (atol((char *) PQgetvalue(query, 0, 0)) == -1) ? ECPG_ARRAY_ARRAY : ECPG_ARRAY_VECTOR;
+ if (ecpg_dynamic_type(type) == SQL3_CHARACTER ||
+ ecpg_dynamic_type(type) == SQL3_CHARACTER_VARYING)
+ {
+ /*
+ * arrays of character strings are not yet implemented
+ */
+ isarray = ECPG_ARRAY_NONE;
+ }
+ }
+ PQclear(query);
+ }
+ else
+ return ECPG_ARRAY_ERROR;
+
+ ecpg_type_infocache_push(&(stmt->connection->cache_head), type, isarray, stmt->lineno);
+ ecpg_log("ecpg_is_type_an_array on line %d: type (%d); C (%d); array (%s)\n", stmt->lineno, type, var->type, ECPG_IS_ARRAY(isarray) ? "yes" : "no");
+ return isarray;
+}
+
+
+bool
+ecpg_store_result(const PGresult *results, int act_field,
+ const struct statement *stmt, struct variable *var)
+{
+ enum ARRAY_TYPE isarray;
+ int act_tuple,
+ ntuples = PQntuples(results);
+ bool status = true;
+
+ if ((isarray = ecpg_is_type_an_array(PQftype(results, act_field), stmt, var)) == ECPG_ARRAY_ERROR)
+ {
+ ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ if (isarray == ECPG_ARRAY_NONE)
+ {
+ /*
+ * 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))
+ {
+ ecpg_log("ecpg_store_result on line %d: incorrect number of matches; %d don't fit into array of %ld\n",
+ stmt->lineno, ntuples, var->arrsize);
+ ecpg_raise(stmt->lineno, INFORMIX_MODE(stmt->compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
+ return false;
+ }
+ }
+ else
+ {
+ /*
+ * since we read an array, the variable has to be an array too
+ */
+ if (var->arrsize == 0)
+ {
+ ecpg_raise(stmt->lineno, ECPG_NO_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
+ return false;
+ }
+ }
+
+ /*
+ * allocate memory for NULL pointers
+ */
+ if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL)
+ {
+ int len = 0;
+
+ if (!PQfformat(results, act_field))
+ {
+ switch (var->type)
+ {
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ 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 *);
+ }
+ 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;
+ }
+ }
+ else
+ {
+ for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
+ len += PQgetlength(results, act_tuple, act_field);
+ }
+
+ ecpg_log("ecpg_store_result on line %d: allocating memory for %d tuples\n", stmt->lineno, ntuples);
+ var->value = (char *) ecpg_auto_alloc(len, stmt->lineno);
+ if (!var->value)
+ return false;
+ *((char **) var->pointer) = var->value;
+ }
+
+ /* 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 *) ecpg_auto_alloc(len, stmt->lineno);
+ if (!var->ind_value)
+ return false;
+ *((char **) var->ind_pointer) = var->ind_value;
+ }
+
+ /* fill the variable with the tuple(s) */
+ if (!var->varcharsize && !var->arrsize &&
+ (var->type == ECPGt_char || var->type == ECPGt_unsigned_char || var->type == ECPGt_string))
+ {
+ /* 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 (!ecpg_get_data(results, act_tuple, act_field, stmt->lineno,
+ var->type, var->ind_type, current_data_location,
+ var->ind_value, len, 0, var->ind_offset, isarray, stmt->compat, stmt->force_indicator))
+ 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 (!ecpg_get_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, stmt->compat, stmt->force_indicator))
+ status = false;
+ }
+ }
+ return status;
+}
+
+static void
+sprintf_double_value(char *ptr, double value, const char *delim)
+{
+ if (isnan(value))
+ sprintf(ptr, "%s%s", "NaN", delim);
+ else if (isinf(value))
+ {
+ if (value < 0)
+ sprintf(ptr, "%s%s", "-Infinity", delim);
+ else
+ sprintf(ptr, "%s%s", "Infinity", delim);
+ }
+ else
+ sprintf(ptr, "%.15g%s", value, delim);
+}
+
+static void
+sprintf_float_value(char *ptr, float value, const char *delim)
+{
+ if (isnan(value))
+ sprintf(ptr, "%s%s", "NaN", delim);
+ else if (isinf(value))
+ {
+ if (value < 0)
+ sprintf(ptr, "%s%s", "-Infinity", delim);
+ else
+ sprintf(ptr, "%s%s", "Infinity", delim);
+ }
+ else
+ sprintf(ptr, "%.15g%s", value, delim);
+}
+
+static char *
+convert_bytea_to_string(char *from_data, int from_len, int lineno)
+{
+ char *to_data;
+ int to_len = ecpg_hex_enc_len(from_len) + 4 + 1; /* backslash + 'x' +
+ * quote + quote */
+
+ to_data = ecpg_alloc(to_len, lineno);
+ if (!to_data)
+ return NULL;
+
+ strcpy(to_data, "'\\x");
+ ecpg_hex_encode(from_data, from_len, to_data + 3);
+ strcpy(to_data + 3 + ecpg_hex_enc_len(from_len), "\'");
+
+ return to_data;
+}
+
+bool
+ecpg_store_input(const int lineno, const bool force_indicator, const struct variable *var,
+ char **tobeinserted_p, bool quote)
+{
+ char *mallocedval = NULL;
+ char *newcopy = NULL;
+
+ /*
+ * arrays are not possible unless the column is an array, too FIXME: we do
+ * not know if the column is an array here array input to singleton column
+ * will result in a runtime error
+ */
+
+ /*
+ * Some special treatment is needed for records since we want their
+ * contents to arrive in a comma-separated list on insert (I think).
+ */
+
+ *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;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ if (*(long long int *) var->ind_value < (long long) 0)
+ *tobeinserted_p = NULL;
+ break;
+ case ECPGt_NO_INDICATOR:
+ if (force_indicator == false)
+ {
+ if (ECPGis_noind_null(var->type, var->value))
+ *tobeinserted_p = NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ if (*tobeinserted_p != NULL)
+ {
+ int asize = var->arrsize ? var->arrsize : 1;
+
+ switch (var->type)
+ {
+ int element;
+
+ case ECPGt_short:
+ if (!(mallocedval = ecpg_alloc(asize * 20, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; 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;
+ break;
+
+ case ECPGt_int:
+ if (!(mallocedval = ecpg_alloc(asize * 20, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; 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;
+ break;
+
+ case ECPGt_unsigned_short:
+ if (!(mallocedval = ecpg_alloc(asize * 20, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; 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;
+ break;
+
+ case ECPGt_unsigned_int:
+ if (!(mallocedval = ecpg_alloc(asize * 20, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; 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;
+ break;
+
+ case ECPGt_long:
+ if (!(mallocedval = ecpg_alloc(asize * 20, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; 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;
+ break;
+
+ case ECPGt_unsigned_long:
+ if (!(mallocedval = ecpg_alloc(asize * 20, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; 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;
+ break;
+
+ case ECPGt_long_long:
+ if (!(mallocedval = ecpg_alloc(asize * 30, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; element++)
+ sprintf(mallocedval + strlen(mallocedval), "%lld,", ((long long int *) var->value)[element]);
+
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+ }
+ else
+ sprintf(mallocedval, "%lld", *((long long int *) var->value));
+
+ *tobeinserted_p = mallocedval;
+ break;
+
+ case ECPGt_unsigned_long_long:
+ if (!(mallocedval = ecpg_alloc(asize * 30, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; element++)
+ sprintf(mallocedval + strlen(mallocedval), "%llu,", ((unsigned long long int *) var->value)[element]);
+
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+ }
+ else
+ sprintf(mallocedval, "%llu", *((unsigned long long int *) var->value));
+
+ *tobeinserted_p = mallocedval;
+ break;
+
+ case ECPGt_float:
+ if (!(mallocedval = ecpg_alloc(asize * 25, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; element++)
+ sprintf_float_value(mallocedval + strlen(mallocedval), ((float *) var->value)[element], ",");
+
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+ }
+ else
+ sprintf_float_value(mallocedval, *((float *) var->value), "");
+
+ *tobeinserted_p = mallocedval;
+ break;
+
+ case ECPGt_double:
+ if (!(mallocedval = ecpg_alloc(asize * 25, lineno)))
+ return false;
+
+ if (asize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; element++)
+ sprintf_double_value(mallocedval + strlen(mallocedval), ((double *) var->value)[element], ",");
+
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+ }
+ else
+ sprintf_double_value(mallocedval, *((double *) var->value), "");
+
+ *tobeinserted_p = mallocedval;
+ break;
+
+ case ECPGt_bool:
+ if (!(mallocedval = ecpg_alloc(var->arrsize + sizeof("{}"), lineno)))
+ return false;
+
+ if (var->arrsize > 1)
+ {
+ strcpy(mallocedval, "{");
+
+ for (element = 0; element < asize; element++)
+ sprintf(mallocedval + strlen(mallocedval), "%c,", (((bool *) var->value)[element]) ? 't' : 'f');
+
+ 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
+ ecpg_raise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
+ }
+
+ *tobeinserted_p = mallocedval;
+ break;
+
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ {
+ /* set slen to string length if type is char * */
+ int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : (unsigned int) var->varcharsize;
+
+ if (!(newcopy = ecpg_alloc(slen + 1, lineno)))
+ return false;
+
+ strncpy(newcopy, (char *) var->value, slen);
+ newcopy[slen] = '\0';
+
+ mallocedval = quote_postgres(newcopy, quote, lineno);
+ if (!mallocedval)
+ {
+ ecpg_free(newcopy);
+ return false;
+ }
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+ case ECPGt_const:
+ case ECPGt_char_variable:
+ {
+ int slen = strlen((char *) var->value);
+
+ if (!(mallocedval = ecpg_alloc(slen + 1, lineno)))
+ return false;
+
+ strncpy(mallocedval, (char *) var->value, slen);
+ mallocedval[slen] = '\0';
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_bytea:
+ {
+ struct ECPGgeneric_bytea *variable =
+ (struct ECPGgeneric_bytea *) (var->value);
+
+ if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno)))
+ return false;
+
+ memcpy(mallocedval, variable->arr, variable->len);
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_varchar:
+ {
+ struct ECPGgeneric_varchar *variable =
+ (struct ECPGgeneric_varchar *) (var->value);
+
+ if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, lineno)))
+ return false;
+
+ strncpy(newcopy, variable->arr, variable->len);
+ newcopy[variable->len] = '\0';
+
+ mallocedval = quote_postgres(newcopy, quote, lineno);
+ if (!mallocedval)
+ {
+ ecpg_free(newcopy);
+ return false;
+ }
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_decimal:
+ case ECPGt_numeric:
+ {
+ char *str = NULL;
+ int slen;
+ numeric *nval;
+
+ if (var->arrsize > 1)
+ mallocedval = ecpg_strdup("{", lineno);
+ else
+ mallocedval = ecpg_strdup("", lineno);
+
+ if (!mallocedval)
+ return false;
+
+ for (element = 0; element < asize; element++)
+ {
+ int result;
+
+ nval = PGTYPESnumeric_new();
+ if (!nval)
+ {
+ ecpg_free(mallocedval);
+ return false;
+ }
+
+ if (var->type == ECPGt_numeric)
+ result = PGTYPESnumeric_copy(&(((numeric *) (var->value))[element]), nval);
+ else
+ result = PGTYPESnumeric_from_decimal(&(((decimal *) (var->value))[element]), nval);
+
+ if (result != 0)
+ {
+ PGTYPESnumeric_free(nval);
+ ecpg_free(mallocedval);
+ return false;
+ }
+
+ str = PGTYPESnumeric_to_asc(nval, nval->dscale);
+ slen = strlen(str);
+ PGTYPESnumeric_free(nval);
+
+ if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
+ {
+ ecpg_free(mallocedval);
+ ecpg_free(str);
+ return false;
+ }
+ mallocedval = newcopy;
+
+ /* also copy trailing '\0' */
+ memcpy(mallocedval + strlen(mallocedval), str, slen + 1);
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval), ",");
+
+ ecpg_free(str);
+ }
+
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_interval:
+ {
+ char *str = NULL;
+ int slen;
+
+ if (var->arrsize > 1)
+ mallocedval = ecpg_strdup("{", lineno);
+ else
+ mallocedval = ecpg_strdup("", lineno);
+
+ if (!mallocedval)
+ return false;
+
+ for (element = 0; element < asize; element++)
+ {
+ str = quote_postgres(PGTYPESinterval_to_asc(&(((interval *) (var->value))[element])), quote, lineno);
+ if (!str)
+ {
+ ecpg_free(mallocedval);
+ return false;
+ }
+
+ slen = strlen(str);
+
+ if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
+ {
+ ecpg_free(mallocedval);
+ ecpg_free(str);
+ return false;
+ }
+ mallocedval = newcopy;
+
+ /* also copy trailing '\0' */
+ memcpy(mallocedval + strlen(mallocedval), str, slen + 1);
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval), ",");
+
+ ecpg_free(str);
+ }
+
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_date:
+ {
+ char *str = NULL;
+ int slen;
+
+ if (var->arrsize > 1)
+ mallocedval = ecpg_strdup("{", lineno);
+ else
+ mallocedval = ecpg_strdup("", lineno);
+
+ if (!mallocedval)
+ return false;
+
+ for (element = 0; element < asize; element++)
+ {
+ str = quote_postgres(PGTYPESdate_to_asc(((date *) (var->value))[element]), quote, lineno);
+ if (!str)
+ {
+ ecpg_free(mallocedval);
+ return false;
+ }
+
+ slen = strlen(str);
+
+ if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
+ {
+ ecpg_free(mallocedval);
+ ecpg_free(str);
+ return false;
+ }
+ mallocedval = newcopy;
+
+ /* also copy trailing '\0' */
+ memcpy(mallocedval + strlen(mallocedval), str, slen + 1);
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval), ",");
+
+ ecpg_free(str);
+ }
+
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_timestamp:
+ {
+ char *str = NULL;
+ int slen;
+
+ if (var->arrsize > 1)
+ mallocedval = ecpg_strdup("{", lineno);
+ else
+ mallocedval = ecpg_strdup("", lineno);
+
+ if (!mallocedval)
+ return false;
+
+ for (element = 0; element < asize; element++)
+ {
+ str = quote_postgres(PGTYPEStimestamp_to_asc(((timestamp *) (var->value))[element]), quote, lineno);
+ if (!str)
+ {
+ ecpg_free(mallocedval);
+ return false;
+ }
+
+ slen = strlen(str);
+
+ if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
+ {
+ ecpg_free(mallocedval);
+ ecpg_free(str);
+ return false;
+ }
+ mallocedval = newcopy;
+
+ /* also copy trailing '\0' */
+ memcpy(mallocedval + strlen(mallocedval), str, slen + 1);
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval), ",");
+
+ ecpg_free(str);
+ }
+
+ if (var->arrsize > 1)
+ strcpy(mallocedval + strlen(mallocedval) - 1, "}");
+
+ *tobeinserted_p = mallocedval;
+ }
+ break;
+
+ case ECPGt_descriptor:
+ case ECPGt_sqlda:
+ break;
+
+ default:
+ /* Not implemented yet */
+ ecpg_raise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ecpg_type_name(var->type));
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+
+static void
+print_param_value(char *value, int len, int is_binary, int lineno, int nth)
+{
+ char *value_s;
+ bool malloced = false;
+
+ if (value == NULL)
+ value_s = "null";
+ else if (!is_binary)
+ value_s = value;
+ else
+ {
+ value_s = ecpg_alloc(ecpg_hex_enc_len(len) + 1, lineno);
+ if (value_s != NULL)
+ {
+ ecpg_hex_encode(value, len, value_s);
+ value_s[ecpg_hex_enc_len(len)] = '\0';
+ malloced = true;
+ }
+ else
+ value_s = "no memory for logging of parameter";
+ }
+
+ ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n",
+ lineno, nth, value_s);
+
+ if (malloced)
+ ecpg_free(value_s);
+}
+
+void
+ecpg_free_params(struct statement *stmt, bool print)
+{
+ int n;
+
+ for (n = 0; n < stmt->nparams; n++)
+ {
+ if (print)
+ print_param_value(stmt->paramvalues[n], stmt->paramlengths[n],
+ stmt->paramformats[n], stmt->lineno, n + 1);
+ ecpg_free(stmt->paramvalues[n]);
+ }
+ ecpg_free(stmt->paramvalues);
+ ecpg_free(stmt->paramlengths);
+ ecpg_free(stmt->paramformats);
+ stmt->paramvalues = NULL;
+ stmt->paramlengths = NULL;
+ stmt->paramformats = NULL;
+ stmt->nparams = 0;
+}
+
+static bool
+insert_tobeinserted(int position, int ph_len, struct statement *stmt, char *tobeinserted)
+{
+ char *newcopy;
+
+ if (!(newcopy = (char *) ecpg_alloc(strlen(stmt->command)
+ + strlen(tobeinserted)
+ + 1, stmt->lineno)))
+ {
+ ecpg_free(tobeinserted);
+ return false;
+ }
+
+ strcpy(newcopy, stmt->command);
+ strcpy(newcopy + position - 1, tobeinserted);
+
+ /*
+ * The strange thing in the second argument is the rest of the string from
+ * the old string
+ */
+ strcat(newcopy,
+ stmt->command
+ + position
+ + ph_len - 1);
+
+ ecpg_free(stmt->command);
+ stmt->command = newcopy;
+
+ ecpg_free(tobeinserted);
+ return true;
+}
+
+static bool
+store_input_from_desc(struct statement *stmt, struct descriptor_item *desc_item,
+ char **tobeinserted)
+{
+ struct variable var;
+
+ /*
+ * In case of binary data, only allocate memory and memcpy because binary
+ * data have been already stored into desc_item->data with
+ * ecpg_store_input() at ECPGset_desc().
+ */
+ if (desc_item->is_binary)
+ {
+ if (!(*tobeinserted = ecpg_alloc(desc_item->data_len, stmt->lineno)))
+ return false;
+ memcpy(*tobeinserted, desc_item->data, desc_item->data_len);
+ return true;
+ }
+
+ var.type = ECPGt_char;
+ var.varcharsize = strlen(desc_item->data);
+ var.value = desc_item->data;
+ var.pointer = &(desc_item->data);
+ var.arrsize = 1;
+ var.offset = 0;
+
+ if (!desc_item->indicator)
+ {
+ var.ind_type = ECPGt_NO_INDICATOR;
+ var.ind_value = var.ind_pointer = NULL;
+ var.ind_varcharsize = var.ind_arrsize = var.ind_offset = 0;
+ }
+ else
+ {
+ var.ind_type = ECPGt_int;
+ var.ind_value = &(desc_item->indicator);
+ var.ind_pointer = &(var.ind_value);
+ var.ind_varcharsize = var.ind_arrsize = 1;
+ var.ind_offset = 0;
+ }
+
+ if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &var, tobeinserted, false))
+ return false;
+
+ return true;
+}
+
+/*
+ * ecpg_build_params
+ * Build statement parameters
+ *
+ * The input values are taken from user variables, and the results are stored
+ * in arrays which can be used by PQexecParams().
+ */
+bool
+ecpg_build_params(struct statement *stmt)
+{
+ struct variable *var;
+ int desc_counter = 0;
+ int position = 0;
+ const char *value;
+ bool std_strings = false;
+
+ /* Get standard_conforming_strings setting. */
+ value = PQparameterStatus(stmt->connection->connection, "standard_conforming_strings");
+ if (value && strcmp(value, "on") == 0)
+ std_strings = true;
+
+ /*
+ * If the type is one of the fill in types then we take the argument and
+ * enter it to our parameter array at the first position. Then if there
+ * are any more fill in types we add more parameters.
+ */
+ var = stmt->inlist;
+ while (var)
+ {
+ char *tobeinserted;
+ int counter = 1;
+ bool binary_format;
+ int binary_length;
+
+
+ tobeinserted = NULL;
+ binary_length = 0;
+ binary_format = false;
+
+ /*
+ * A descriptor is a special case since it contains many variables but
+ * is listed only once.
+ */
+ if (var->type == ECPGt_descriptor)
+ {
+ /*
+ * We create an additional variable list here, so the same logic
+ * applies.
+ */
+ struct descriptor *desc;
+ struct descriptor_item *desc_item;
+
+ desc = ecpg_find_desc(stmt->lineno, var->pointer);
+ if (desc == NULL)
+ return false;
+
+ desc_counter++;
+ for (desc_item = desc->items; desc_item; desc_item = desc_item->next)
+ {
+ if (desc_item->num != desc_counter)
+ continue;
+
+ if (!store_input_from_desc(stmt, desc_item, &tobeinserted))
+ return false;
+
+ if (desc_item->is_binary)
+ {
+ binary_length = desc_item->data_len;
+ binary_format = true;
+ }
+ break;
+ }
+ if (desc->count == desc_counter)
+ desc_counter = 0;
+ }
+ else if (var->type == ECPGt_sqlda)
+ {
+ if (INFORMIX_MODE(stmt->compat))
+ {
+ struct sqlda_compat *sqlda = *(struct sqlda_compat **) var->pointer;
+ struct variable desc_inlist;
+ int i;
+
+ if (sqlda == NULL)
+ return false;
+
+ desc_counter++;
+ for (i = 0; i < sqlda->sqld; i++)
+ {
+ if (i + 1 == desc_counter)
+ {
+ desc_inlist.type = sqlda->sqlvar[i].sqltype;
+ desc_inlist.value = sqlda->sqlvar[i].sqldata;
+ desc_inlist.pointer = &(sqlda->sqlvar[i].sqldata);
+ switch (desc_inlist.type)
+ {
+ case ECPGt_char:
+ case ECPGt_varchar:
+ desc_inlist.varcharsize = strlen(sqlda->sqlvar[i].sqldata);
+ break;
+ default:
+ desc_inlist.varcharsize = 0;
+ break;
+ }
+ desc_inlist.arrsize = 1;
+ desc_inlist.offset = 0;
+ if (sqlda->sqlvar[i].sqlind)
+ {
+ desc_inlist.ind_type = ECPGt_short;
+ /* ECPG expects indicator value < 0 */
+ if (*(sqlda->sqlvar[i].sqlind))
+ *(sqlda->sqlvar[i].sqlind) = -1;
+ desc_inlist.ind_value = sqlda->sqlvar[i].sqlind;
+ desc_inlist.ind_pointer = &(sqlda->sqlvar[i].sqlind);
+ desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1;
+ desc_inlist.ind_offset = 0;
+ }
+ else
+ {
+ desc_inlist.ind_type = ECPGt_NO_INDICATOR;
+ desc_inlist.ind_value = desc_inlist.ind_pointer = NULL;
+ desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0;
+ }
+ if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false))
+ return false;
+
+ break;
+ }
+ }
+ if (sqlda->sqld == desc_counter)
+ desc_counter = 0;
+ }
+ else
+ {
+ struct sqlda_struct *sqlda = *(struct sqlda_struct **) var->pointer;
+ struct variable desc_inlist;
+ int i;
+
+ if (sqlda == NULL)
+ return false;
+
+ desc_counter++;
+ for (i = 0; i < sqlda->sqln; i++)
+ {
+ if (i + 1 == desc_counter)
+ {
+ desc_inlist.type = sqlda->sqlvar[i].sqltype;
+ desc_inlist.value = sqlda->sqlvar[i].sqldata;
+ desc_inlist.pointer = &(sqlda->sqlvar[i].sqldata);
+ switch (desc_inlist.type)
+ {
+ case ECPGt_char:
+ case ECPGt_varchar:
+ desc_inlist.varcharsize = strlen(sqlda->sqlvar[i].sqldata);
+ break;
+ default:
+ desc_inlist.varcharsize = 0;
+ break;
+ }
+ desc_inlist.arrsize = 1;
+ desc_inlist.offset = 0;
+ if (sqlda->sqlvar[i].sqlind)
+ {
+ desc_inlist.ind_type = ECPGt_short;
+ /* ECPG expects indicator value < 0 */
+ if (*(sqlda->sqlvar[i].sqlind))
+ *(sqlda->sqlvar[i].sqlind) = -1;
+ desc_inlist.ind_value = sqlda->sqlvar[i].sqlind;
+ desc_inlist.ind_pointer = &(sqlda->sqlvar[i].sqlind);
+ desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1;
+ desc_inlist.ind_offset = 0;
+ }
+ else
+ {
+ desc_inlist.ind_type = ECPGt_NO_INDICATOR;
+ desc_inlist.ind_value = desc_inlist.ind_pointer = NULL;
+ desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0;
+ }
+ if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false))
+ return false;
+
+ break;
+ }
+ }
+ if (sqlda->sqln == desc_counter)
+ desc_counter = 0;
+ }
+ }
+ else
+ {
+ if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false))
+ return false;
+
+ if (var->type == ECPGt_bytea)
+ {
+ binary_length = ((struct ECPGgeneric_bytea *) (var->value))->len;
+ binary_format = true;
+ }
+ }
+
+ /*
+ * now tobeinserted points to an area that contains the next
+ * parameter; now find the position in the string where it belongs
+ */
+ if ((position = next_insert(stmt->command, position, stmt->questionmarks, std_strings) + 1) == 0)
+ {
+ /*
+ * We have an argument but we don't have the matched up
+ * placeholder in the string
+ */
+ ecpg_raise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS,
+ ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS,
+ NULL);
+ ecpg_free_params(stmt, false);
+ ecpg_free(tobeinserted);
+ return false;
+ }
+
+ /*
+ * if var->type=ECPGt_char_variable we have a dynamic cursor we have
+ * to simulate a dynamic cursor because there is no backend
+ * functionality for it
+ */
+ if (var->type == ECPGt_char_variable)
+ {
+ int ph_len = (stmt->command[position] == '?') ? strlen("?") : strlen("$1");
+
+ if (!insert_tobeinserted(position, ph_len, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
+
+ /*
+ * if the placeholder is '$0' we have to replace it on the client side
+ * this is for places we want to support variables at that are not
+ * supported in the backend
+ */
+ else if (stmt->command[position] == '0')
+ {
+ if (stmt->statement_type == ECPGst_prepare ||
+ stmt->statement_type == ECPGst_exec_with_exprlist)
+ {
+ /* Need to double-quote the inserted statement name. */
+ char *str = ecpg_alloc(strlen(tobeinserted) + 2 + 1,
+ stmt->lineno);
+
+ if (!str)
+ {
+ ecpg_free(tobeinserted);
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ sprintf(str, "\"%s\"", tobeinserted);
+ ecpg_free(tobeinserted);
+ tobeinserted = str;
+ }
+
+ if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
+ else if (stmt->statement_type == ECPGst_exec_with_exprlist)
+ {
+ if (binary_format)
+ {
+ char *p = convert_bytea_to_string(tobeinserted,
+ binary_length,
+ stmt->lineno);
+
+ ecpg_free(tobeinserted);
+ if (!p)
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = p;
+ }
+
+ if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
+ else
+ {
+ bool realloc_failed = false;
+ char **newparamvalues;
+ int *newparamlengths;
+ int *newparamformats;
+
+ /* enlarge all the param arrays */
+ if ((newparamvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
+ stmt->paramvalues = newparamvalues;
+ else
+ realloc_failed = true;
+
+ if ((newparamlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ stmt->paramlengths = newparamlengths;
+ else
+ realloc_failed = true;
+
+ if ((newparamformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno)))
+ stmt->paramformats = newparamformats;
+ else
+ realloc_failed = true;
+
+ if (realloc_failed)
+ {
+ ecpg_free_params(stmt, false);
+ ecpg_free(tobeinserted);
+ return false;
+ }
+
+ /* only now can we assign ownership of "tobeinserted" to stmt */
+ stmt->paramvalues[stmt->nparams] = tobeinserted;
+ stmt->paramlengths[stmt->nparams] = binary_length;
+ stmt->paramformats[stmt->nparams] = (binary_format ? 1 : 0);
+ stmt->nparams++;
+
+ /* let's see if this was an old style placeholder */
+ if (stmt->command[position] == '?')
+ {
+ /* yes, replace with new style */
+ int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the
+ * size we need */
+
+ if (!(tobeinserted = (char *) ecpg_alloc(buffersize, stmt->lineno)))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+
+ snprintf(tobeinserted, buffersize, "$%d", counter++);
+
+ if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ tobeinserted = NULL;
+ }
+ }
+
+ if (desc_counter == 0)
+ var = var->next;
+ }
+
+ /*
+ * Check if there are unmatched things left. PREPARE AS has no parameter.
+ * Check other statement.
+ */
+ if (stmt->statement_type != ECPGst_prepare &&
+ next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0)
+ {
+ ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
+ ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * ecpg_autostart_transaction
+ * If we are in non-autocommit mode, automatically start a transaction.
+ */
+bool
+ecpg_autostart_transaction(struct statement *stmt)
+{
+ if (PQtransactionStatus(stmt->connection->connection) == PQTRANS_IDLE && !stmt->connection->autocommit)
+ {
+ stmt->results = PQexec(stmt->connection->connection, "begin transaction");
+ if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat))
+ {
+ ecpg_free_params(stmt, false);
+ return false;
+ }
+ PQclear(stmt->results);
+ stmt->results = NULL;
+ }
+ return true;
+}
+
+/*
+ * ecpg_execute
+ * Execute the SQL statement.
+ */
+bool
+ecpg_execute(struct statement *stmt)
+{
+ ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name);
+ if (stmt->statement_type == ECPGst_execute)
+ {
+ stmt->results = PQexecPrepared(stmt->connection->connection,
+ stmt->name,
+ stmt->nparams,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
+ ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
+ }
+ else
+ {
+ if (stmt->nparams == 0)
+ {
+ stmt->results = PQexec(stmt->connection->connection, stmt->command);
+ ecpg_log("ecpg_execute on line %d: using PQexec\n", stmt->lineno);
+ }
+ else
+ {
+ stmt->results = PQexecParams(stmt->connection->connection,
+ stmt->command, stmt->nparams, NULL,
+ (const char *const *) stmt->paramvalues,
+ (const int *) stmt->paramlengths,
+ (const int *) stmt->paramformats,
+ 0);
+
+ ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
+ }
+
+ if (stmt->statement_type == ECPGst_prepare)
+ {
+ if (!ecpg_register_prepared_stmt(stmt))
+ {
+ ecpg_free_params(stmt, true);
+ return false;
+ }
+ }
+ }
+
+ ecpg_free_params(stmt, true);
+
+ if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat))
+ return false;
+
+ return true;
+}
+
+/*-------
+ * ecpg_process_output
+ *
+ * Process the statement result and store it into application variables. This
+ * function can be called repeatedly during the same statement in case cursor
+ * readahead is used and the application does FETCH N which overflows the
+ * readahead window.
+ *
+ * Parameters
+ * stmt statement structure holding the PGresult and
+ * the list of output variables
+ * clear_result
+ * PQclear() the result upon returning from this function
+ *
+ * Returns success as boolean. Also an SQL error is raised in case of failure.
+ *-------
+ */
+bool
+ecpg_process_output(struct statement *stmt, bool clear_result)
+{
+ struct variable *var;
+ bool status = false;
+ char *cmdstat;
+ PGnotify *notify;
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ int nfields,
+ ntuples,
+ act_field;
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return false;
+ }
+
+ var = stmt->outlist;
+ switch (PQresultStatus(stmt->results))
+ {
+ case PGRES_TUPLES_OK:
+ nfields = PQnfields(stmt->results);
+ sqlca->sqlerrd[2] = ntuples = PQntuples(stmt->results);
+
+ ecpg_log("ecpg_process_output on line %d: correctly got %d tuples with %d fields\n", stmt->lineno, ntuples, nfields);
+ status = true;
+
+ if (ntuples < 1)
+ {
+ if (ntuples)
+ ecpg_log("ecpg_process_output on line %d: incorrect number of matches (%d)\n",
+ stmt->lineno, ntuples);
+ ecpg_raise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+ status = false;
+ break;
+ }
+
+ if (var != NULL && var->type == ECPGt_descriptor)
+ {
+ struct descriptor *desc = ecpg_find_desc(stmt->lineno, var->pointer);
+
+ if (desc == NULL)
+ status = false;
+ else
+ {
+ if (desc->result)
+ PQclear(desc->result);
+ desc->result = stmt->results;
+ clear_result = false;
+ ecpg_log("ecpg_process_output on line %d: putting result (%d tuples) into descriptor %s\n",
+ stmt->lineno, PQntuples(stmt->results), (const char *) var->pointer);
+ }
+ var = var->next;
+ }
+ else if (var != NULL && var->type == ECPGt_sqlda)
+ {
+ if (INFORMIX_MODE(stmt->compat))
+ {
+ struct sqlda_compat **_sqlda = (struct sqlda_compat **) var->pointer;
+ struct sqlda_compat *sqlda = *_sqlda;
+ struct sqlda_compat *sqlda_new;
+ int i;
+
+ /*
+ * If we are passed in a previously existing sqlda (chain)
+ * then free it.
+ */
+ while (sqlda)
+ {
+ sqlda_new = sqlda->desc_next;
+ free(sqlda);
+ sqlda = sqlda_new;
+ }
+ *_sqlda = sqlda = sqlda_new = NULL;
+ for (i = ntuples - 1; i >= 0; i--)
+ {
+ /*
+ * Build a new sqlda structure. Note that only
+ * fetching 1 record is supported
+ */
+ sqlda_new = ecpg_build_compat_sqlda(stmt->lineno, stmt->results, i, stmt->compat);
+
+ if (!sqlda_new)
+ {
+ /* cleanup all SQLDAs we created up */
+ while (sqlda)
+ {
+ sqlda_new = sqlda->desc_next;
+ free(sqlda);
+ sqlda = sqlda_new;
+ }
+ *_sqlda = NULL;
+
+ ecpg_log("ecpg_process_output on line %d: out of memory allocating a new sqlda\n", stmt->lineno);
+ status = false;
+ break;
+ }
+ else
+ {
+ ecpg_log("ecpg_process_output on line %d: new sqlda was built\n", stmt->lineno);
+
+ *_sqlda = sqlda_new;
+
+ ecpg_set_compat_sqlda(stmt->lineno, _sqlda, stmt->results, i, stmt->compat);
+ ecpg_log("ecpg_process_output on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
+ stmt->lineno, PQnfields(stmt->results));
+
+ sqlda_new->desc_next = sqlda;
+ sqlda = sqlda_new;
+ }
+ }
+ }
+ else
+ {
+ struct sqlda_struct **_sqlda = (struct sqlda_struct **) var->pointer;
+ struct sqlda_struct *sqlda = *_sqlda;
+ struct sqlda_struct *sqlda_new;
+ int i;
+
+ /*
+ * If we are passed in a previously existing sqlda (chain)
+ * then free it.
+ */
+ while (sqlda)
+ {
+ sqlda_new = sqlda->desc_next;
+ free(sqlda);
+ sqlda = sqlda_new;
+ }
+ *_sqlda = sqlda = sqlda_new = NULL;
+ for (i = ntuples - 1; i >= 0; i--)
+ {
+ /*
+ * Build a new sqlda structure. Note that only
+ * fetching 1 record is supported
+ */
+ sqlda_new = ecpg_build_native_sqlda(stmt->lineno, stmt->results, i, stmt->compat);
+
+ if (!sqlda_new)
+ {
+ /* cleanup all SQLDAs we created up */
+ while (sqlda)
+ {
+ sqlda_new = sqlda->desc_next;
+ free(sqlda);
+ sqlda = sqlda_new;
+ }
+ *_sqlda = NULL;
+
+ ecpg_log("ecpg_process_output on line %d: out of memory allocating a new sqlda\n", stmt->lineno);
+ status = false;
+ break;
+ }
+ else
+ {
+ ecpg_log("ecpg_process_output on line %d: new sqlda was built\n", stmt->lineno);
+
+ *_sqlda = sqlda_new;
+
+ ecpg_set_native_sqlda(stmt->lineno, _sqlda, stmt->results, i, stmt->compat);
+ ecpg_log("ecpg_process_output on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
+ stmt->lineno, PQnfields(stmt->results));
+
+ sqlda_new->desc_next = sqlda;
+ sqlda = sqlda_new;
+ }
+ }
+ }
+
+ var = var->next;
+ }
+ else
+ for (act_field = 0; act_field < nfields && status; act_field++)
+ {
+ if (var != NULL)
+ {
+ status = ecpg_store_result(stmt->results, act_field, stmt, var);
+ var = var->next;
+ }
+ else if (!INFORMIX_MODE(stmt->compat))
+ {
+ ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
+ return false;
+ }
+ }
+
+ if (status && var != NULL)
+ {
+ ecpg_raise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL);
+ status = false;
+ }
+
+ break;
+ case PGRES_COMMAND_OK:
+ status = true;
+ cmdstat = PQcmdStatus(stmt->results);
+ sqlca->sqlerrd[1] = PQoidValue(stmt->results);
+ sqlca->sqlerrd[2] = atol(PQcmdTuples(stmt->results));
+ ecpg_log("ecpg_process_output on line %d: OK: %s\n", stmt->lineno, cmdstat);
+ if (stmt->compat != ECPG_COMPAT_INFORMIX_SE &&
+ !sqlca->sqlerrd[2] &&
+ (strncmp(cmdstat, "UPDATE", 6) == 0
+ || strncmp(cmdstat, "INSERT", 6) == 0
+ || strncmp(cmdstat, "DELETE", 6) == 0))
+ ecpg_raise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
+ break;
+ case PGRES_COPY_OUT:
+ {
+ char *buffer;
+ int res;
+
+ ecpg_log("ecpg_process_output on line %d: COPY OUT data transfer in progress\n", stmt->lineno);
+ while ((res = PQgetCopyData(stmt->connection->connection,
+ &buffer, 0)) > 0)
+ {
+ printf("%s", buffer);
+ PQfreemem(buffer);
+ }
+ if (res == -1)
+ {
+ /* COPY done */
+ PQclear(stmt->results);
+ stmt->results = PQgetResult(stmt->connection->connection);
+ if (PQresultStatus(stmt->results) == PGRES_COMMAND_OK)
+ ecpg_log("ecpg_process_output on line %d: got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", stmt->lineno);
+ else
+ ecpg_log("ecpg_process_output on line %d: got error after PGRES_COPY_OUT: %s", stmt->lineno, PQresultErrorMessage(stmt->results));
+ }
+ break;
+ }
+ default:
+
+ /*
+ * execution should never reach this code because it is already
+ * handled in ecpg_check_PQresult()
+ */
+ ecpg_log("ecpg_process_output on line %d: unknown execution status type\n",
+ stmt->lineno);
+ ecpg_raise_backend(stmt->lineno, stmt->results, stmt->connection->connection, stmt->compat);
+ status = false;
+ break;
+ }
+
+ if (clear_result)
+ {
+ PQclear(stmt->results);
+ stmt->results = NULL;
+ }
+
+ /* check for asynchronous returns */
+ PQconsumeInput(stmt->connection->connection);
+ while ((notify = PQnotifies(stmt->connection->connection)) != NULL)
+ {
+ ecpg_log("ecpg_process_output on line %d: asynchronous notification of \"%s\" from backend PID %d received\n",
+ stmt->lineno, notify->relname, notify->be_pid);
+ PQfreemem(notify);
+ PQconsumeInput(stmt->connection->connection);
+ }
+
+ return status;
+}
+
+/*
+ * ecpg_do_prologue
+ *
+ * Initialize various infrastructure elements for executing the statement:
+ *
+ * - create the statement structure
+ * - set the C numeric locale for communicating with the backend
+ * - preprocess the variable list of input/output parameters into
+ * linked lists
+ */
+bool
+ecpg_do_prologue(int lineno, const int compat, const int force_indicator,
+ const char *connection_name, const bool questionmarks,
+ enum ECPG_statement_type statement_type, const char *query,
+ va_list args, struct statement **stmt_out)
+{
+ struct statement *stmt = NULL;
+ struct connection *con;
+ enum ECPGttype type;
+ struct variable **list;
+ char *prepname;
+ bool is_prepared_name_set;
+
+ *stmt_out = NULL;
+
+ if (!query)
+ {
+ ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
+ return false;
+ }
+
+#ifdef ENABLE_THREAD_SAFETY
+ ecpg_pthreads_init();
+#endif
+
+ con = ecpg_get_connection(connection_name);
+
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+ stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
+
+ if (stmt == NULL)
+ return false;
+
+ /*
+ * Make sure we do NOT honor the locale for numeric input/output since the
+ * database wants the standard decimal point. If available, use
+ * uselocale() for this because it's thread-safe. Windows doesn't have
+ * that, but it usually does have _configthreadlocale(). In some versions
+ * of MinGW, _configthreadlocale() exists but always returns -1 --- so
+ * treat that situation as if the function doesn't exist.
+ */
+#ifdef HAVE_USELOCALE
+
+ /*
+ * Since ecpg_init() succeeded, we have a connection. Any successful
+ * connection initializes ecpg_clocale.
+ */
+ Assert(ecpg_clocale);
+ stmt->oldlocale = uselocale(ecpg_clocale);
+ if (stmt->oldlocale == (locale_t) 0)
+ {
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+#else
+#ifdef HAVE__CONFIGTHREADLOCALE
+ stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+#endif
+ stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
+ if (stmt->oldlocale == NULL)
+ {
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+ setlocale(LC_NUMERIC, "C");
+#endif
+
+ /*
+ * If statement type is ECPGst_prepnormal we are supposed to prepare the
+ * statement before executing them
+ */
+ if (statement_type == ECPGst_prepnormal)
+ {
+ if (!ecpg_auto_prepare(lineno, connection_name, compat, &prepname, query))
+ {
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
+ /*
+ * statement is now prepared, so instead of the query we have to
+ * execute the name
+ */
+ stmt->command = prepname;
+ statement_type = ECPGst_execute;
+ }
+ else
+ stmt->command = ecpg_strdup(query, lineno);
+
+ stmt->name = NULL;
+
+ if (statement_type == ECPGst_execute)
+ {
+ /* if we have an EXECUTE command, only the name is send */
+ char *command = ecpg_prepared(stmt->command, con);
+
+ if (command)
+ {
+ stmt->name = stmt->command;
+ stmt->command = ecpg_strdup(command, lineno);
+ }
+ else
+ {
+ ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt->command);
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+ }
+ /* name of PREPARE AS will be set in loop of inlist */
+
+ stmt->connection = con;
+ stmt->lineno = lineno;
+ stmt->compat = compat;
+ stmt->force_indicator = force_indicator;
+ stmt->questionmarks = questionmarks;
+ stmt->statement_type = statement_type;
+
+ /*------
+ * create a list of variables
+ *
+ * The variables are listed with input variables preceding output
+ * variables. 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_pointer - pointer to indicator variable
+ * ind_varcharsize - empty
+ * ind_arrsize - arraysize of indicator array
+ * ind_offset - indicator offset
+ *------
+ */
+
+ is_prepared_name_set = false;
+
+ list = &(stmt->inlist);
+
+ type = va_arg(args, enum ECPGttype);
+
+ while (type != ECPGt_EORT)
+ {
+ if (type == ECPGt_EOIT)
+ list = &(stmt->outlist);
+ else
+ {
+ struct variable *var,
+ *ptr;
+
+ if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno)))
+ {
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
+ var->type = type;
+ var->pointer = va_arg(args, char *);
+
+ var->varcharsize = va_arg(args, long);
+ var->arrsize = va_arg(args, long);
+ var->offset = va_arg(args, long);
+
+ /*
+ * Unknown array size means pointer to an array. Unknown
+ * varcharsize usually also means pointer. But if the type is
+ * character and the array size is known, it is an array of
+ * pointers to char, so use var->pointer as it is.
+ */
+ if (var->arrsize == 0 ||
+ (var->varcharsize == 0 && ((var->type != ECPGt_char && var->type != ECPGt_unsigned_char) || (var->arrsize <= 1))))
+ var->value = *((char **) (var->pointer));
+ else
+ var->value = var->pointer;
+
+ /*
+ * negative values are used to indicate an array without given
+ * bounds
+ */
+ /* reset to zero for us */
+ if (var->arrsize < 0)
+ var->arrsize = 0;
+ if (var->varcharsize < 0)
+ var->varcharsize = 0;
+
+ var->next = NULL;
+
+ var->ind_type = va_arg(args, enum ECPGttype);
+ var->ind_pointer = va_arg(args, char *);
+ var->ind_varcharsize = va_arg(args, long);
+ var->ind_arrsize = va_arg(args, long);
+ var->ind_offset = va_arg(args, long);
+
+ 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;
+
+ /*
+ * negative values are used to indicate an array without given
+ * bounds
+ */
+ /* reset to zero for us */
+ if (var->ind_arrsize < 0)
+ var->ind_arrsize = 0;
+ if (var->ind_varcharsize < 0)
+ var->ind_varcharsize = 0;
+
+ /* if variable is NULL, the statement hasn't been prepared */
+ if (var->pointer == NULL)
+ {
+ ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, NULL);
+ ecpg_free(var);
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
+ for (ptr = *list; ptr && ptr->next; ptr = ptr->next)
+ ;
+
+ if (ptr == NULL)
+ *list = var;
+ else
+ ptr->next = var;
+
+ if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
+ {
+ stmt->name = ecpg_strdup(var->value, lineno);
+ is_prepared_name_set = true;
+ }
+ }
+
+ type = va_arg(args, enum ECPGttype);
+ }
+
+ /* are we connected? */
+ if (con == NULL || con->connection == NULL)
+ {
+ ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>"));
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
+ if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare)
+ {
+ ecpg_raise(lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>"));
+ ecpg_do_epilogue(stmt);
+ return false;
+ }
+
+ /* initialize auto_mem struct */
+ ecpg_clear_auto_mem();
+
+ *stmt_out = stmt;
+
+ return true;
+}
+
+/*
+ * ecpg_do_epilogue
+ * Restore the application locale and free the statement structure.
+ */
+void
+ecpg_do_epilogue(struct statement *stmt)
+{
+ if (stmt == NULL)
+ return;
+
+#ifdef HAVE_USELOCALE
+ if (stmt->oldlocale != (locale_t) 0)
+ uselocale(stmt->oldlocale);
+#else
+ if (stmt->oldlocale)
+ setlocale(LC_NUMERIC, stmt->oldlocale);
+#ifdef HAVE__CONFIGTHREADLOCALE
+
+ /*
+ * This is a bit trickier than it looks: if we failed partway through
+ * statement initialization, oldthreadlocale could still be 0. But that's
+ * okay because a call with 0 is defined to be a no-op.
+ */
+ if (stmt->oldthreadlocale != -1)
+ (void) _configthreadlocale(stmt->oldthreadlocale);
+#endif
+#endif
+
+ free_statement(stmt);
+}
+
+/*
+ * Execute SQL statements in the backend.
+ * The input/output parameters (variable argument list) are passed
+ * in a va_list, so other functions can use this interface.
+ */
+bool
+ecpg_do(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query, va_list args)
+{
+ struct statement *stmt = NULL;
+
+ if (!ecpg_do_prologue(lineno, compat, force_indicator, connection_name,
+ questionmarks, (enum ECPG_statement_type) st,
+ query, args, &stmt))
+ goto fail;
+
+ if (!ecpg_build_params(stmt))
+ goto fail;
+
+ if (!ecpg_autostart_transaction(stmt))
+ goto fail;
+
+ if (!ecpg_execute(stmt))
+ goto fail;
+
+ if (!ecpg_process_output(stmt, true))
+ goto fail;
+
+ ecpg_do_epilogue(stmt);
+ return true;
+
+fail:
+ ecpg_do_epilogue(stmt);
+ return false;
+}
+
+/*
+ * Execute SQL statements in the backend.
+ * The input/output parameters are passed as variable-length argument list.
+ */
+bool
+ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query,...)
+{
+ va_list args;
+ bool ret;
+
+ va_start(args, query);
+ ret = ecpg_do(lineno, compat, force_indicator, connection_name,
+ questionmarks, st, query, args);
+ va_end(args);
+
+ return ret;
+}
+
+/* old descriptor interface */
+bool
+ECPGdo_descriptor(int line, const char *connection,
+ const char *descriptor, const char *query)
+{
+ return ECPGdo(line, ECPG_COMPAT_PGSQL, true, connection, '\0', 0, query, ECPGt_EOIT,
+ ECPGt_descriptor, descriptor, 0L, 0L, 0L,
+ ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT);
+}
diff --git a/src/interfaces/ecpg/ecpglib/exports.txt b/src/interfaces/ecpg/ecpglib/exports.txt
new file mode 100644
index 0000000..69e9617
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/exports.txt
@@ -0,0 +1,31 @@
+# src/interfaces/ecpg/ecpglib/exports.txt
+# Functions to be exported by ecpglib DLL
+ECPGallocate_desc 1
+ECPGconnect 2
+ECPGdeallocate 3
+ECPGdeallocate_all 4
+ECPGdeallocate_desc 5
+ECPGdebug 6
+ECPGdescribe 7
+ECPGdisconnect 8
+ECPGdo 9
+ECPGdo_descriptor 10
+ECPGfree_auto_mem 11
+ECPGget_desc 12
+ECPGget_desc_header 13
+ECPGget_sqlca 14
+ECPGis_noind_null 15
+ECPGprepare 16
+ECPGprepared_statement 17
+ECPGset_desc 18
+ECPGset_desc_header 19
+ECPGset_noind_null 20
+ECPGsetcommit 21
+ECPGsetconn 22
+ECPGstatus 23
+ECPGtrans 24
+sqlprint 25
+ECPGget_PGconn 26
+ECPGtransactionStatus 27
+ECPGset_var 28
+ECPGget_var 29
diff --git a/src/interfaces/ecpg/ecpglib/memory.c b/src/interfaces/ecpg/ecpglib/memory.c
new file mode 100644
index 0000000..bd81251
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/memory.c
@@ -0,0 +1,174 @@
+/* src/interfaces/ecpg/ecpglib/memory.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include "ecpg-pthread-win32.h"
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+
+void
+ecpg_free(void *ptr)
+{
+ free(ptr);
+}
+
+char *
+ecpg_alloc(long size, int lineno)
+{
+ char *new = (char *) calloc(1L, size);
+
+ if (!new)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return NULL;
+ }
+
+ return new;
+}
+
+char *
+ecpg_realloc(void *ptr, long size, int lineno)
+{
+ char *new = (char *) realloc(ptr, size);
+
+ if (!new)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return NULL;
+ }
+
+ return new;
+}
+
+char *
+ecpg_strdup(const char *string, int lineno)
+{
+ char *new;
+
+ if (string == NULL)
+ return NULL;
+
+ new = strdup(string);
+ if (!new)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return NULL;
+ }
+
+ return new;
+}
+
+/* keep a list of memory we allocated for the user */
+struct auto_mem
+{
+ void *pointer;
+ struct auto_mem *next;
+};
+
+#ifdef ENABLE_THREAD_SAFETY
+static pthread_key_t auto_mem_key;
+static pthread_once_t auto_mem_once = PTHREAD_ONCE_INIT;
+
+static void
+auto_mem_destructor(void *arg)
+{
+ (void) arg; /* keep the compiler quiet */
+ ECPGfree_auto_mem();
+}
+
+static void
+auto_mem_key_init(void)
+{
+ pthread_key_create(&auto_mem_key, auto_mem_destructor);
+}
+
+static struct auto_mem *
+get_auto_allocs(void)
+{
+ pthread_once(&auto_mem_once, auto_mem_key_init);
+ return (struct auto_mem *) pthread_getspecific(auto_mem_key);
+}
+
+static void
+set_auto_allocs(struct auto_mem *am)
+{
+ pthread_setspecific(auto_mem_key, am);
+}
+#else
+static struct auto_mem *auto_allocs = NULL;
+
+#define get_auto_allocs() (auto_allocs)
+#define set_auto_allocs(am) do { auto_allocs = (am); } while(0)
+#endif
+
+char *
+ecpg_auto_alloc(long size, int lineno)
+{
+ void *ptr = (void *) ecpg_alloc(size, lineno);
+
+ if (!ptr)
+ return NULL;
+
+ if (!ecpg_add_mem(ptr, lineno))
+ {
+ ecpg_free(ptr);
+ return NULL;
+ }
+ return ptr;
+}
+
+bool
+ecpg_add_mem(void *ptr, int lineno)
+{
+ struct auto_mem *am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), lineno);
+
+ if (!am)
+ return false;
+
+ am->pointer = ptr;
+ am->next = get_auto_allocs();
+ set_auto_allocs(am);
+ return true;
+}
+
+void
+ECPGfree_auto_mem(void)
+{
+ struct auto_mem *am = get_auto_allocs();
+
+ /* free all memory we have allocated for the user */
+ if (am)
+ {
+ do
+ {
+ struct auto_mem *act = am;
+
+ am = am->next;
+ ecpg_free(act->pointer);
+ ecpg_free(act);
+ } while (am);
+ set_auto_allocs(NULL);
+ }
+}
+
+void
+ecpg_clear_auto_mem(void)
+{
+ struct auto_mem *am = get_auto_allocs();
+
+ /* only free our own structure */
+ if (am)
+ {
+ do
+ {
+ struct auto_mem *act = am;
+
+ am = am->next;
+ ecpg_free(act);
+ } while (am);
+ set_auto_allocs(NULL);
+ }
+}
diff --git a/src/interfaces/ecpg/ecpglib/misc.c b/src/interfaces/ecpg/ecpglib/misc.c
new file mode 100644
index 0000000..1eef1ec
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/misc.c
@@ -0,0 +1,593 @@
+/* src/interfaces/ecpg/ecpglib/misc.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include <limits.h>
+#include <unistd.h>
+
+#include "ecpg-pthread-win32.h"
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "pg_config_paths.h"
+#include "pgtypes_date.h"
+#include "pgtypes_interval.h"
+#include "pgtypes_numeric.h"
+#include "pgtypes_timestamp.h"
+#include "sqlca.h"
+
+#ifndef LONG_LONG_MIN
+#ifdef LLONG_MIN
+#define LONG_LONG_MIN LLONG_MIN
+#else
+#define LONG_LONG_MIN LONGLONG_MIN
+#endif /* LLONG_MIN */
+#endif /* LONG_LONG_MIN */
+
+bool ecpg_internal_regression_mode = false;
+
+static struct sqlca_t sqlca_init =
+{
+ {
+ 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
+ },
+ sizeof(struct sqlca_t),
+ 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'
+ }
+};
+
+#ifdef ENABLE_THREAD_SAFETY
+static pthread_key_t sqlca_key;
+static pthread_once_t sqlca_key_once = PTHREAD_ONCE_INIT;
+#else
+static struct sqlca_t sqlca =
+{
+ {
+ 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '
+ },
+ sizeof(struct sqlca_t),
+ 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'
+ }
+};
+#endif
+
+#ifdef ENABLE_THREAD_SAFETY
+static pthread_mutex_t debug_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t debug_init_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+static int simple_debug = 0;
+static FILE *debugstream = NULL;
+
+void
+ecpg_init_sqlca(struct sqlca_t *sqlca)
+{
+ memcpy((char *) sqlca, (char *) &sqlca_init, sizeof(struct sqlca_t));
+}
+
+bool
+ecpg_init(const struct connection *con, const char *connection_name, const int lineno)
+{
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY,
+ NULL);
+ return false;
+ }
+
+ ecpg_init_sqlca(sqlca);
+ if (con == NULL)
+ {
+ ecpg_raise(lineno, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
+ connection_name ? connection_name : ecpg_gettext("NULL"));
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef ENABLE_THREAD_SAFETY
+static void
+ecpg_sqlca_key_destructor(void *arg)
+{
+ free(arg); /* sqlca structure allocated in ECPGget_sqlca */
+}
+
+static void
+ecpg_sqlca_key_init(void)
+{
+ pthread_key_create(&sqlca_key, ecpg_sqlca_key_destructor);
+}
+#endif
+
+struct sqlca_t *
+ECPGget_sqlca(void)
+{
+#ifdef ENABLE_THREAD_SAFETY
+ struct sqlca_t *sqlca;
+
+ pthread_once(&sqlca_key_once, ecpg_sqlca_key_init);
+
+ sqlca = pthread_getspecific(sqlca_key);
+ if (sqlca == NULL)
+ {
+ sqlca = malloc(sizeof(struct sqlca_t));
+ if (sqlca == NULL)
+ return NULL;
+ ecpg_init_sqlca(sqlca);
+ pthread_setspecific(sqlca_key, sqlca);
+ }
+ return sqlca;
+#else
+ return &sqlca;
+#endif
+}
+
+bool
+ECPGstatus(int lineno, const char *connection_name)
+{
+ struct connection *con = ecpg_get_connection(connection_name);
+
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+ /* are we connected? */
+ if (con->connection == NULL)
+ {
+ ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, con->name);
+ return false;
+ }
+
+ return true;
+}
+
+PGTransactionStatusType
+ECPGtransactionStatus(const char *connection_name)
+{
+ const struct connection *con;
+
+ con = ecpg_get_connection(connection_name);
+ if (con == NULL)
+ {
+ /* transaction status is unknown */
+ return PQTRANS_UNKNOWN;
+ }
+
+ return PQtransactionStatus(con->connection);
+}
+
+bool
+ECPGtrans(int lineno, const char *connection_name, const char *transaction)
+{
+ PGresult *res;
+ struct connection *con = ecpg_get_connection(connection_name);
+
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+ ecpg_log("ECPGtrans on line %d: action \"%s\"; connection \"%s\"\n", lineno, transaction, con ? con->name : "null");
+
+ /* if we have no connection we just simulate the command */
+ if (con && con->connection)
+ {
+ /*
+ * If we got a transaction command but have no open transaction, we
+ * have to start one, unless we are in autocommit, where the
+ * developers have to take care themselves. However, if the command is
+ * a begin statement, we just execute it once. And if the command is
+ * commit or rollback prepared, we don't execute it.
+ */
+ if (PQtransactionStatus(con->connection) == PQTRANS_IDLE &&
+ !con->autocommit &&
+ strncmp(transaction, "begin", 5) != 0 &&
+ strncmp(transaction, "start", 5) != 0 &&
+ strncmp(transaction, "commit prepared", 15) != 0 &&
+ strncmp(transaction, "rollback prepared", 17) != 0)
+ {
+ res = PQexec(con->connection, "begin transaction");
+ if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
+ return false;
+ PQclear(res);
+ }
+
+ res = PQexec(con->connection, transaction);
+ if (!ecpg_check_PQresult(res, lineno, con->connection, ECPG_COMPAT_PGSQL))
+ return false;
+ PQclear(res);
+ }
+
+ return true;
+}
+
+
+void
+ECPGdebug(int n, FILE *dbgs)
+{
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_lock(&debug_init_mutex);
+#endif
+
+ if (n > 100)
+ {
+ ecpg_internal_regression_mode = true;
+ simple_debug = n - 100;
+ }
+ else
+ simple_debug = n;
+
+ debugstream = dbgs;
+
+ ecpg_log("ECPGdebug: set to %d\n", simple_debug);
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&debug_init_mutex);
+#endif
+}
+
+void
+ecpg_log(const char *format,...)
+{
+ va_list ap;
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+ const char *intl_format;
+ int bufsize;
+ char *fmt;
+
+ if (!simple_debug)
+ return;
+
+ /* localize the error message string */
+ intl_format = ecpg_gettext(format);
+
+ /*
+ * Insert PID into the format, unless ecpg_internal_regression_mode is set
+ * (regression tests want unchanging output).
+ */
+ bufsize = strlen(intl_format) + 100;
+ fmt = (char *) malloc(bufsize);
+ if (fmt == NULL)
+ return;
+
+ if (ecpg_internal_regression_mode)
+ snprintf(fmt, bufsize, "[NO_PID]: %s", intl_format);
+ else
+ snprintf(fmt, bufsize, "[%d]: %s", (int) getpid(), intl_format);
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_lock(&debug_mutex);
+#endif
+
+ va_start(ap, format);
+ vfprintf(debugstream, fmt, ap);
+ va_end(ap);
+
+ /* dump out internal sqlca variables */
+ if (ecpg_internal_regression_mode && sqlca != NULL)
+ {
+ fprintf(debugstream, "[NO_PID]: sqlca: code: %ld, state: %s\n",
+ sqlca->sqlcode, sqlca->sqlstate);
+ }
+
+ fflush(debugstream);
+
+#ifdef ENABLE_THREAD_SAFETY
+ pthread_mutex_unlock(&debug_mutex);
+#endif
+
+ free(fmt);
+}
+
+void
+ECPGset_noind_null(enum ECPGttype type, void *ptr)
+{
+ switch (type)
+ {
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ *((char *) ptr) = '\0';
+ break;
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ *((short int *) ptr) = SHRT_MIN;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ *((int *) ptr) = INT_MIN;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ case ECPGt_date:
+ *((long *) ptr) = LONG_MIN;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ *((long long *) ptr) = LONG_LONG_MIN;
+ break;
+ case ECPGt_float:
+ memset((char *) ptr, 0xff, sizeof(float));
+ break;
+ case ECPGt_double:
+ memset((char *) ptr, 0xff, sizeof(double));
+ break;
+ case ECPGt_varchar:
+ *(((struct ECPGgeneric_varchar *) ptr)->arr) = 0x00;
+ ((struct ECPGgeneric_varchar *) ptr)->len = 0;
+ break;
+ case ECPGt_bytea:
+ ((struct ECPGgeneric_bytea *) ptr)->len = 0;
+ break;
+ case ECPGt_decimal:
+ memset((char *) ptr, 0, sizeof(decimal));
+ ((decimal *) ptr)->sign = NUMERIC_NULL;
+ break;
+ case ECPGt_numeric:
+ memset((char *) ptr, 0, sizeof(numeric));
+ ((numeric *) ptr)->sign = NUMERIC_NULL;
+ break;
+ case ECPGt_interval:
+ memset((char *) ptr, 0xff, sizeof(interval));
+ break;
+ case ECPGt_timestamp:
+ memset((char *) ptr, 0xff, sizeof(timestamp));
+ break;
+ default:
+ break;
+ }
+}
+
+static bool
+_check(const unsigned char *ptr, int length)
+{
+ for (length--; length >= 0; length--)
+ if (ptr[length] != 0xff)
+ return false;
+
+ return true;
+}
+
+bool
+ECPGis_noind_null(enum ECPGttype type, const void *ptr)
+{
+ switch (type)
+ {
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ if (*((const char *) ptr) == '\0')
+ return true;
+ break;
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ if (*((const short int *) ptr) == SHRT_MIN)
+ return true;
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ if (*((const int *) ptr) == INT_MIN)
+ return true;
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ case ECPGt_date:
+ if (*((const long *) ptr) == LONG_MIN)
+ return true;
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ if (*((const long long *) ptr) == LONG_LONG_MIN)
+ return true;
+ break;
+ case ECPGt_float:
+ return _check(ptr, sizeof(float));
+ break;
+ case ECPGt_double:
+ return _check(ptr, sizeof(double));
+ break;
+ case ECPGt_varchar:
+ if (*(((const struct ECPGgeneric_varchar *) ptr)->arr) == 0x00)
+ return true;
+ break;
+ case ECPGt_bytea:
+ if (((const struct ECPGgeneric_bytea *) ptr)->len == 0)
+ return true;
+ break;
+ case ECPGt_decimal:
+ if (((const decimal *) ptr)->sign == NUMERIC_NULL)
+ return true;
+ break;
+ case ECPGt_numeric:
+ if (((const numeric *) ptr)->sign == NUMERIC_NULL)
+ return true;
+ break;
+ case ECPGt_interval:
+ return _check(ptr, sizeof(interval));
+ break;
+ case ECPGt_timestamp:
+ return _check(ptr, sizeof(timestamp));
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+#ifdef WIN32
+#ifdef ENABLE_THREAD_SAFETY
+
+void
+win32_pthread_mutex(volatile pthread_mutex_t *mutex)
+{
+ if (mutex->handle == NULL)
+ {
+ while (InterlockedExchange((LONG *) &mutex->initlock, 1) == 1)
+ Sleep(0);
+ if (mutex->handle == NULL)
+ mutex->handle = CreateMutex(NULL, FALSE, NULL);
+ InterlockedExchange((LONG *) &mutex->initlock, 0);
+ }
+}
+
+static pthread_mutex_t win32_pthread_once_lock = PTHREAD_MUTEX_INITIALIZER;
+
+void
+win32_pthread_once(volatile pthread_once_t *once, void (*fn) (void))
+{
+ if (!*once)
+ {
+ pthread_mutex_lock(&win32_pthread_once_lock);
+ if (!*once)
+ {
+ fn();
+ *once = true;
+ }
+ pthread_mutex_unlock(&win32_pthread_once_lock);
+ }
+}
+#endif /* ENABLE_THREAD_SAFETY */
+#endif /* WIN32 */
+
+#ifdef ENABLE_NLS
+
+char *
+ecpg_gettext(const char *msgid)
+{
+ /*
+ * If multiple threads come through here at about the same time, it's okay
+ * for more than one of them to call bindtextdomain(). But it's not okay
+ * for any of them to reach dgettext() before bindtextdomain() is
+ * complete, so don't set the flag till that's done. Use "volatile" just
+ * to be sure the compiler doesn't try to get cute.
+ */
+ static volatile bool already_bound = false;
+
+ if (!already_bound)
+ {
+ /* dgettext() preserves errno, but bindtextdomain() doesn't */
+#ifdef WIN32
+ int save_errno = GetLastError();
+#else
+ int save_errno = errno;
+#endif
+ const char *ldir;
+
+ /* No relocatable lookup here because the binary could be anywhere */
+ ldir = getenv("PGLOCALEDIR");
+ if (!ldir)
+ ldir = LOCALEDIR;
+ bindtextdomain(PG_TEXTDOMAIN("ecpglib"), ldir);
+ already_bound = true;
+#ifdef WIN32
+ SetLastError(save_errno);
+#else
+ errno = save_errno;
+#endif
+ }
+
+ return dgettext(PG_TEXTDOMAIN("ecpglib"), msgid);
+}
+#endif /* ENABLE_NLS */
+
+struct var_list *ivlist = NULL;
+
+void
+ECPGset_var(int number, void *pointer, int lineno)
+{
+ struct var_list *ptr;
+
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return;
+ }
+
+ ecpg_init_sqlca(sqlca);
+
+ for (ptr = ivlist; ptr != NULL; ptr = ptr->next)
+ {
+ if (ptr->number == number)
+ {
+ /* already known => just change pointer value */
+ ptr->pointer = pointer;
+ return;
+ }
+ }
+
+ /* a new one has to be added */
+ ptr = (struct var_list *) calloc(1L, sizeof(struct var_list));
+ if (!ptr)
+ {
+ struct sqlca_t *sqlca = ECPGget_sqlca();
+
+ if (sqlca == NULL)
+ {
+ ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
+ ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
+ return;
+ }
+
+ sqlca->sqlcode = ECPG_OUT_OF_MEMORY;
+ strncpy(sqlca->sqlstate, "YE001", sizeof(sqlca->sqlstate));
+ snprintf(sqlca->sqlerrm.sqlerrmc, sizeof(sqlca->sqlerrm.sqlerrmc), "out of memory on line %d", lineno);
+ sqlca->sqlerrm.sqlerrml = strlen(sqlca->sqlerrm.sqlerrmc);
+ /* free all memory we have allocated for the user */
+ ECPGfree_auto_mem();
+ }
+ else
+ {
+ ptr->number = number;
+ ptr->pointer = pointer;
+ ptr->next = ivlist;
+ ivlist = ptr;
+ }
+}
+
+void *
+ECPGget_var(int number)
+{
+ struct var_list *ptr;
+
+ for (ptr = ivlist; ptr != NULL && ptr->number != number; ptr = ptr->next);
+ return (ptr) ? ptr->pointer : NULL;
+}
diff --git a/src/interfaces/ecpg/ecpglib/nls.mk b/src/interfaces/ecpg/ecpglib/nls.mk
new file mode 100644
index 0000000..fff2dfd
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/nls.mk
@@ -0,0 +1,6 @@
+# src/interfaces/ecpg/ecpglib/nls.mk
+CATALOG_NAME = ecpglib
+AVAIL_LANGUAGES = cs de el es fr it ja ka ko pl pt_BR ru sv tr uk vi zh_CN
+GETTEXT_FILES = connect.c descriptor.c error.c execute.c misc.c
+GETTEXT_TRIGGERS = ecpg_gettext
+GETTEXT_FLAGS = ecpg_gettext:1:pass-c-format
diff --git a/src/interfaces/ecpg/ecpglib/po/cs.po b/src/interfaces/ecpg/ecpglib/po/cs.po
new file mode 100644
index 0000000..bc09566
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/cs.po
@@ -0,0 +1,199 @@
+# Czech message translation file for ecpglib
+# Copyright (C) 2012 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+# Tomáš Vondra <tv@fuzzy.cz>, 2012, 2013.
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib-cs (PostgreSQL 9.3)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
+"POT-Creation-Date: 2018-07-13 19:38+0000\n"
+"PO-Revision-Date: 2018-07-13 23:44+0200\n"
+"Last-Translator: Tomas Vondra <tv@fuzzy.cz>\n"
+"Language-Team: Czech <info@cspug.cx>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.0.7\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "prázdný text zprávy"
+
+#: connect.c:401 connect.c:430 connect.c:638
+msgid "<DEFAULT>"
+msgstr "<VÝCHOZÍ>"
+
+#: descriptor.c:834 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "na řádce %d nenalezena žádná data"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "nedostatek paměti na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "nepodporovaný typ \"%s\" na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "příliš mnoho argumentů na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "příliš málo argumentů na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "chybná vstupní syntaxe pro typ int: \"%s\", na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "chybná vstupní syntaxe pro typ unsigned int: \"%s\", na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "chybná vstupní syntaxe pro typ floating-point: \"%s\", na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "chybná vstupní syntaxe pro typ boolean: \"%s\", na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "nelze zkonvertovat boolean hodnotu: nesprávná velikost, na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "prázdný dotaz na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "null hodnota bez indikátoru na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "proměnná nemá datový typ pole na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "data načtená ze serveru nejsou pole na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "vkládání pole proměnných není podporováno na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "spojení \"%s\" neexistuje na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "neotevřené spojení \"%s\" na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "neplatný název příkazu \"%s\" na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "deskriptor \"%s\" nenalezen na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "index deskriptoru mimo rozsah na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "nerozpoznaná položka deskriptoru \"%s\" na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "proměnná nemá číselný datový typ na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "proměnná nemá znakový datový typ na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "chyba v transakčním zpracování na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "nelze se spojit s databází \"%s\" na řádce %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "SQL chyba %d na řádce %d"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "spojení se serverem bylo ztraceno"
+
+#: error.c:347
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL chyba: %s\n"
+
+#: execute.c:1968
+msgid "<empty>"
+msgstr "<prázdný>"
diff --git a/src/interfaces/ecpg/ecpglib/po/de.po b/src/interfaces/ecpg/ecpglib/po/de.po
new file mode 100644
index 0000000..94daa83
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/de.po
@@ -0,0 +1,206 @@
+# German message translation file for ecpglib
+# Copyright (C) 2019 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Peter Eisentraut, 2009 - 2019.
+#
+# Use these quotes: »%s«
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 12\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2019-09-07 23:08+0000\n"
+"PO-Revision-Date: 2019-09-08 08:48+0200\n"
+"Last-Translator: Peter Eisentraut <peter@eisentraut.org>\n"
+"Language-Team: German <pgsql-translators@postgresql.org>\n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "leerer Nachrichtentext"
+
+#: connect.c:403 connect.c:432 connect.c:640
+msgid "<DEFAULT>"
+msgstr "<DEFAULT>"
+
+#: cursor.c:195 descriptor.c:887 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "keine Daten gefunden auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "Speicher aufgebraucht auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "nicht unterstützter Typ »%s« auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "zu viele Argumente auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "zu wenige Argumente auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "ungültige Eingabesyntax für Typ int: »%s«, auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "ungültige Eingabesyntax für Typ unsigned int: »%s«, auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "ungültige Eingabesyntax für Gleitkommatyp: »%s«, auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "ungültige Syntax für Typ boolean: »%s«, auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "konnte boolean-Wert nicht umwandeln: Größe stimmt nicht überein, auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "leere Anfrage auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "NULL-Wert ohne Indikator auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "Variable hat keinen Array-Typ auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "vom Server gelesene Daten sind kein Array auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "Einfügen in ein Array aus Variablen wird nicht unterstützt auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "Verbindung »%s« existiert nicht auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "nicht mit Verbindung »%s« verbunden auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "ungültiger Anweisungsname »%s« auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "Deskriptor »%s« nicht gefunden auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "Deskriptorindex außerhalb des gültigen Bereichs auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "unbekanntes Deskriptorelement »%s« auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "Variable hat keinen numerischen Typ auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "Variable hat keinen Zeichentyp auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "Fehler bei der Transaktionsverarbeitung auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "konnte nicht mit Datenbank »%s« verbinden auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "cursor is invalid on line %d"
+msgstr "Cursor ist ungültig auf Zeile %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:214
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "SQL-Fehler %d auf Zeile %d"
+
+#: error.c:261
+msgid "the connection to the server was lost"
+msgstr "die Verbindung zum Server wurde verloren"
+
+#: error.c:354
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL-Fehler: %s\n"
+
+#: execute.c:2187 execute.c:2194
+msgid "<empty>"
+msgstr "<leer>"
diff --git a/src/interfaces/ecpg/ecpglib/po/el.po b/src/interfaces/ecpg/ecpglib/po/el.po
new file mode 100644
index 0000000..1aa9583
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/el.po
@@ -0,0 +1,200 @@
+# Greek message translation file for ecpglib
+# Copyright (C) 2021 PostgreSQL Global Development Group
+# This file is distributed under the same license as the ecpglib (PostgreSQL) package.
+# Georgios Kokolatos <gkokolatos@pm.me>, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-20 09:09+0000\n"
+"PO-Revision-Date: 2021-08-23 10:40+0200\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.0\n"
+"Last-Translator: Georgios Kokolatos <gkokolatos@pm.me>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Language: el\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "κενό κείμενο μηνύματος"
+
+#: connect.c:405 connect.c:627
+msgid "<DEFAULT>"
+msgstr "<DEFAULT>"
+
+#: descriptor.c:871 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "δεν βρέθηκαν δεδομένα στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "έλλειψη μνήμης στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "μη υποστηριζόμενος τύπος «%s» στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "πάρα πολλές παράμετροι στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "πολύ λίγες παράμετροι στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "μη έγκυρη σύνταξη εισόδου για τύπο int: «%s», στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "μη έγκυρη σύνταξη εισόδου για τύπο unsigned int: «%s», στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "μη έγκυρη σύνταξη εισόδου για τύπο floating-point: «%s», on-line %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "μη έγκυρη σύνταξη για τύπο boolean: «%s», στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "δεν ήταν δυνατή η μετατροπή της δυαδικής τιμής: αναντιστοιχία μεγέθους, στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "άδειο ερώτημα στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "τιμή null χωρίς ένδειξη στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "η μεταβλητή δεν έχει τύπο συστάδας στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "τα δεδομένα που διαβάζονται από το διακομιστή δεν είναι μία συστάδα στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "η εισαγωγή μίας συστάδας μεταβλητών δεν υποστηρίζεται στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "η σύνδεση «%s» δεν υπάρχει στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "δεν έχει συνδεθεί στη σύνδεση «%s» στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "μη έγκυρο όνομα δήλωσης «%s» στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "περιγραφέας «%s» δεν βρέθηκε στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "ευρετήριο περιγραφέα εκτός εύρους στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "μη αναγνωρίσιμο στοιχείο περιγραφέα «%s» στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "η μεταβλητή δεν έχει αριθμητικό τύπο στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "η μεταβλητή δεν έχει τύπο χαρακτήρα στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "σφάλμα κατά την επεξεργασία συναλλαγής στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "δεν ήταν δυνατή η σύνδεση στη βάση δεδομένων «%s» στη γραμμή %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "SQL σφάλμα %d στη γραμμή %d"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "η σύνδεση στον διακομιστή χάθηκε"
+
+#: error.c:346
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL σφάλμα: %s\n"
+
+#: execute.c:2196 execute.c:2203
+msgid "<empty>"
+msgstr "<empty>"
diff --git a/src/interfaces/ecpg/ecpglib/po/es.po b/src/interfaces/ecpg/ecpglib/po/es.po
new file mode 100644
index 0000000..9af2825
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/es.po
@@ -0,0 +1,200 @@
+# Spanish message translation file for ecpglib
+#
+# Copyright (c) 2009-2021, PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+# Emanuel Calvo Franco <postgres.arg@gmail.com>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 15\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2023-05-07 16:39+0000\n"
+"PO-Revision-Date: 2022-10-20 09:05+0200\n"
+"Last-Translator: Emanuel Calvo Franco <postgres-arg@gmail.com>\n"
+"Language-Team: PgSQL-es-Ayuda <pgsql-es-ayuda@lists.postgresql.org>\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: connect.c:243
+msgid "empty message text"
+msgstr "mensaje de texto vacío"
+
+#: connect.c:410 connect.c:675
+msgid "<DEFAULT>"
+msgstr "<POR OMISIÓN>"
+
+#: descriptor.c:876 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "no se encontraron datos en la línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "memoria agotada en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "tipo no soportado «%s» en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "demasiados argumentos en la línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "muy pocos argumentos en la línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "sintaxis de entrada no válida para el tipo entero: «%s», en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "sintaxis de entrada no válida para el tipo entero sin signo: «%s», en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "sintaxis de entrada no válida para el tipo de coma flotante: «%s», en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "sintaxis no válida para el tipo booleano: «%s», en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "no se puede convertir el valor booleano: tamaño incorrecto, en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "consulta vacía en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "valor nulo sin indicador en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "la variable no tiene tipo array en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "el dato leído del servidor no es un array en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "la inserción de un array de variables no está soportado en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "conexión «%s» no existe en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "no conectada a la conexión «%s» en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "nombre sentencia no válida «%s» en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "descriptor «%s» no encontrado en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "índice de descriptor fuera de rango en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "elemento de descriptor no reconocido «%s» en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "la variable no tiene un tipo numérico en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "la variable no tiene un tipo textual en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "error en el procesamiento de transacción en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "no se pudo conectar a la base de datos «%s» en línea %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "error SQL %d en línea %d"
+
+#: error.c:253
+msgid "the connection to the server was lost"
+msgstr "se ha perdido la conexión al servidor"
+
+#: error.c:345
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "error SQL: %s\n"
+
+#: execute.c:2189 execute.c:2196
+msgid "<empty>"
+msgstr "<vacío>"
diff --git a/src/interfaces/ecpg/ecpglib/po/fr.po b/src/interfaces/ecpg/ecpglib/po/fr.po
new file mode 100644
index 0000000..e976d5c
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/fr.po
@@ -0,0 +1,212 @@
+# LANGUAGE message translation file for ecpglib
+# Copyright (C) 2009-2022 PostgreSQL Global Development Group
+# This file is distributed under the same license as the ecpglib (PostgreSQL) package.
+#
+# Use these quotes: « %s »
+#
+# Stéphane Schildknecht <stephane.schildknecht@dalibo.com>, 2009.
+# Guillaume Lelarge <guillaume@lelarge.info>, 2010-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 15\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-04-12 05:16+0000\n"
+"PO-Revision-Date: 2022-04-12 17:29+0200\n"
+"Last-Translator: Guillaume Lelarge <guillaume@lelarge.info>\n"
+"Language-Team: French <guillaume@lelarge.info>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+"X-Generator: Poedit 3.0.1\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "texte du message vide"
+
+#: connect.c:403 connect.c:432 connect.c:640
+msgid "<DEFAULT>"
+msgstr "<DÉFAUT>"
+
+#: cursor.c:195 descriptor.c:887 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "aucune donnée trouvée sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "mémoire épuisée à la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "type « %s » non supporté sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "trop d'arguments sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "trop peu d'arguments sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "syntaxe invalide en entrée pour le type int : « %s » sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "syntaxe invalide en entrée pour le type unisgned int : « %s » sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "syntaxe invalide en entrée pour le type float : « %s » sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "syntaxe invalide en entrée pour le type booléen : « %s » sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr ""
+"n'a pas pu convertir la valeur booléenne : différence de taille sur la\n"
+"ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "requête vide sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "valeur NULL sans indicateur sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "la valeur n'a pas de type tableau sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "la donnée lue du serveur n'est pas un tableau sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "l'insertion d'un tableau de variables n'est pas supportée, sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "la connexion « %s » n'existe pas en ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "non connecté à la connexion « %s » en ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "nom d'instruction « %s » invalide sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "descripteur « %s » introuvable sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "index de descripteur hors d'échelle sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "élément descripteur « %s » non reconnu sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "la variable n'est pas de type numeric sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "la variable n'est pas de type caractère sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "erreur dans le traitement de la transaction en ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "n'a pas pu se connecter à la base de données « %s » en ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "cursor is invalid on line %d"
+msgstr "le curseur est invalide sur la ligne %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:214
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "erreur SQL %d en ligne %d"
+
+#: error.c:261
+msgid "the connection to the server was lost"
+msgstr "la connexion au serveur a été perdue"
+
+#: error.c:354
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "erreur SQL : %s\n"
+
+#: execute.c:2187 execute.c:2194
+msgid "<empty>"
+msgstr "<vide>"
diff --git a/src/interfaces/ecpg/ecpglib/po/it.po b/src/interfaces/ecpg/ecpglib/po/it.po
new file mode 100644
index 0000000..24d7435
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/it.po
@@ -0,0 +1,217 @@
+#
+# ecpglib.po
+# Italian message translation file for ecpglib
+#
+# For development and bug report please use:
+# https://github.com/dvarrazzo/postgresql-it
+#
+# Copyright (C) 2012-2017 PostgreSQL Global Development Group
+# Copyright (C) 2010, Associazione Culturale ITPUG
+#
+# Daniele Varrazzo <daniele.varrazzo@gmail.com>, 2012-2017
+# Maurizio Totti <maurizio.totti@gmail.com>, 2010
+#
+# This file is distributed under the same license as the PostgreSQL package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 10\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
+"POT-Creation-Date: 2016-04-17 00:07+0000\n"
+"PO-Revision-Date: 2012-10-30 13:08+0100\n"
+"Last-Translator: Daniele Varrazzo <daniele.varrazzo@gmail.com>\n"
+"Language-Team: https://github.com/dvarrazzo/postgresql-it\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-SourceCharset: utf-8\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "messaggio di testo vuoto"
+
+#: connect.c:401 connect.c:430 connect.c:638
+msgid "<DEFAULT>"
+msgstr "<DEFAULT>"
+
+#: descriptor.c:833 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "non ci sono dati alla riga %d"
+
+# Utilizzerei 'memoria esaurita' al posto di 'errore di memoria' (GB)
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "memoria esaurita alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "tipo \"%s\" non supportato alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "troppi argomenti alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "numero di argomenti non sufficiente alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "sintassi in input non valida per il tipo int: \"%s\", alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "sintassi in input non valida per il tipo unsigned int: \"%s\", alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "sintassi in input non valida per il tipo floating-point: \"%s\", alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "sintassi in input non valida per il tipo boolean: \"%s\", alla riga %d"
+
+# Originariamente da MT: non si può convertire il valore booleano: la dimensione è sbagliata (disallineata), alla riga %d
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "conversione fallita per il valore booleano: dimensione incompatibile, alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "query vuota alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "valore nullo senza variabile 'indicatore' alla riga %d"
+
+# è difficile da tradurre diversamente (GB)
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "la variabile non è di tipo array alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "i dati letti dal server non sono di tipo array alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "inserire un array di variabili non è supportato alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "la connessione \"%s\" non esiste alla riga %d"
+
+# Inizialmente (MT): non si è connessi alla connessione \"%s\" alla riga %d
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "connessione \"%s\" non attiva alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "nome di istruzione non valido \"%s\" alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "il descrittore \"%s\" non esiste alla riga %d"
+
+# userei intervallo al posto di range (GB)
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "l'indice del descrittore è fuori intervallo alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "elemento del descrittore \"%s\" sconosciuto alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "la variabile non è di tipo numerico alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "la variabile non è di tipo carattere alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "errore nel processare la transazione alla riga %d"
+
+# Inizialmente (MT): non posso connettermi al database \"%s\" alla riga %d
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "connessione fallita al database \"%s\" alla riga %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "errore SQL %d alla riga %d"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "la connessione con il server è andata persa"
+
+#: error.c:347
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "errore SQL: %s\n"
+
+#: execute.c:1962
+msgid "<empty>"
+msgstr "<empty>"
diff --git a/src/interfaces/ecpg/ecpglib/po/ja.po b/src/interfaces/ecpg/ecpglib/po/ja.po
new file mode 100644
index 0000000..9a99b57
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/ja.po
@@ -0,0 +1,199 @@
+# Japanese message translation file for ecpglib
+# Copyright (C) 2022 PostgreSQL Global Development Group
+# This file is distributed under the same license as the pg_archivecleanup (PostgreSQL) package.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL 15)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-09 12:00+0900\n"
+"PO-Revision-Date: 2019-06-11 09:04+0900\n"
+"Last-Translator: Kyotaro Horiguchi <horikyota.ntt@gmail.com>\n"
+"Language-Team: Japan PostgreSQL Users Group <jpug-doc@ml.postgresql.jp>\n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Poedit 1.5.4\n"
+
+#: connect.c:243
+msgid "empty message text"
+msgstr "メッセージテキストが空です"
+
+#: connect.c:410 connect.c:675
+msgid "<DEFAULT>"
+msgstr "<デフォルト>"
+
+#: descriptor.c:876 misc.c:119
+msgid "NULL"
+msgstr "ヌル"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "行番号%dにおいてデータがありませんでした"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "行番号%dにおいてメモリ不足です"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "行番号%2$dにおいて非サポートのデータ型\"%1$s\"があります"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "行番号%dにおいて引数が多すぎます"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "行番号%dにおいて引数が少なすぎます"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "行番号%2$dにおいて、整数型に対して無効な入力構文があります:\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "行番号%2$dにおいて、符号無し整数型に対して無効な入力構文があります:\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "行番号%2$dにおいて、浮動小数点型に対して無効な入力構文があります:\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "行番号%2$dにおいて、論理型に対して無効な入力構文があります:\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "行番号%dにおいて、論理型に変換できませんでした。サイズが合っていません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "行番号%dにおいて問い合わせが空です"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "行番号%dにおいて、指示子が無いヌル値です"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "行番号%dにおいて、変数は配列型ではありません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "行番号%dにおいて、サーバーから読み込んだデータは配列ではありません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "行番号%dにおいて、変数の配列への挿入はサポートされません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "行番号%2$dにおいて、接続\"%1$s\"は存在しません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "行番号%2$dにおいて、接続\"%1$s\"に接続していません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "行番号%2$dにおいて、文の名前\"%1$s\"が無効です"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "行番号%2$dにおいて、記述子\"%1$s\"がありません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "行番号%dにおいて、記述子のインデックスが範囲外です"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "行番号%2$dにおいて、記述子項目\"%1$s\"が認識できません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "行番号%dにおいて、変数は数値型ではありません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "行番号%dにおいて、変数は文字型ではありません"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "行番号%dにおいて、トランザクション処理がエラーになりました"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "行番号%2$dにおいて、データベース\"%1$s\"に接続できませんでした"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "行番号%2$dにおいて、SQLエラー%1$dがあります"
+
+#: error.c:253
+msgid "the connection to the server was lost"
+msgstr "サーバーへの接続が切れました"
+
+#: error.c:345
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQLエラー: %s\n"
+
+#: execute.c:2189 execute.c:2196
+msgid "<empty>"
+msgstr "<空>"
diff --git a/src/interfaces/ecpg/ecpglib/po/ka.po b/src/interfaces/ecpg/ecpglib/po/ka.po
new file mode 100644
index 0000000..d0e6d4f
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/ka.po
@@ -0,0 +1,200 @@
+# Georgian message translation file for ecpglib
+# Copyright (C) 2022 PostgreSQL Global Development Group
+# This file is distributed under the same license as the ecpglib (PostgreSQL) package.
+# Temuri Doghonadze <temuri.doghonadze@gmail.com>, 2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 15\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-07-02 08:50+0200\n"
+"PO-Revision-Date: 2022-07-04 13:16+0200\n"
+"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
+"Language-Team: Georgian <nothing>\n"
+"Language: ka\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.1\n"
+
+#: connect.c:239
+msgid "empty message text"
+msgstr "შეტყობინების ცარიელი ტექსტი"
+
+#: connect.c:406 connect.c:635
+msgid "<DEFAULT>"
+msgstr "<ნაგულისხმები>"
+
+#: descriptor.c:871 misc.c:119
+msgid "NULL"
+msgstr "არაფერი"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "%d-ე ხაზზე მონაცემები ნაპოვნი არაა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "%d-ე ხაზზე მეხსიერება საკმარისი არაა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "მხარდაუჭერელი ტიპი \"%s\" %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "%d-ე ხაზზე მეტისმეტად ბევრი არგუმენტია"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "%d-ე ხაზზე არგუმენტები საკმარისი არაა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "მთელი რიცხვის შეყვანის არასწორი ფორმატი: \"%s\", %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "უნიშნო მთელი რიცხვის შეყვანის არასწორი ფორმატი: \"%s\", %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "წილადი რიცხვის შეყვანის არასწორი ფორმატი: \"%s\", %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "ლოგიკური ტიპის მიშვნეობის შეყვანის არასწორი ფორმატი: \"%s\", %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "%d-ე ხაზზე ლოგიკური მნიშვნელობის გარდაქმნის შეცდომა: არასწორი ზომა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "%d-ეხაზე მოთხოვნა ცარიელია"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "%d-ე ხაზზე ნულოვანი მნიშვნელობა, ინდიკატორის გარეშე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "%d-ე ხაზზე ცვლადს მასივის ტიპი არ გააჩნია"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "%d-ე ხაზზე სერვერიდან წაკითხული მონაცემი მასივი არაა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "%d-ე ხაზზე ცვლადების მასივის ჩასმა მხარდაუჭერელია"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "შეერთება სახელით \"%s\" არ არსებობს. %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "შეერთება \"%s\" დაკავშირებული არაა. %d -ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "ოპერატორის არასწორი სახელი: \"%s\" . %d -ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "დესკრიპტორი \"%s\" არ არსებობს. %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "%d-ე ხაზზე დესკრიპტორი ინდექსის დიაპაზონს გარეთაა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "დესკრიპტორის უცნობი ჩანაწერი \"%s\" %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "%d -ე ხაზზე ცვლადს რიცხვითი ტიპი არ გააჩნია"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "%d -ე ხაზზე ცვლადს სტრიქონის ტიპი არ გააჩნია"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "%d -ე ხაზზე ტრანზაქციის დამუშავების შეცდომა"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "ბაზასთან (\"%s\") მიერთების შეცდომა %d-ე ხაზზე"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "%d-ე ხაზზე SQL-ის შეცდომა: %d"
+
+#: error.c:253
+msgid "the connection to the server was lost"
+msgstr "სერვერთან კავშირი დაკარგულია"
+
+#: error.c:345
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL-ის შეცდომა: %s\n"
+
+#: execute.c:2195 execute.c:2202
+msgid "<empty>"
+msgstr "<empty>"
diff --git a/src/interfaces/ecpg/ecpglib/po/ko.po b/src/interfaces/ecpg/ecpglib/po/ko.po
new file mode 100644
index 0000000..e798653
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/ko.po
@@ -0,0 +1,198 @@
+# LANGUAGE message translation file for ecpglib
+# Copyright (C) 2015 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Ioseph Kim <ioseph@uri.sarang.net>, 2015
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 12\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-02-09 20:09+0000\n"
+"PO-Revision-Date: 2020-02-10 09:54+0900\n"
+"Last-Translator: Ioseph Kim <ioseph@uri.sarang.net>\n"
+"Language-Team: Korean <pgsql-kr@postgresql.kr>\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "빈 메시지 텍스트"
+
+#: connect.c:401 connect.c:430 connect.c:638
+msgid "<DEFAULT>"
+msgstr "<초기값>"
+
+#: descriptor.c:876 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "자료 없음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "메모리 부족: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "\"%s\" 형 지원하지 않음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "너무 많은 인자: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "너무 적은 인자: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "int 형에 대한 입력 구문 오류: \"%s\", %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "unsigned int 형에 대한 입력 구문 오류: \"%s\", %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "floating-point 형에 대한 입력 구문 오류: \"%s\", %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "boolean 형에 대한 입력 구문 오류: \"%s\", %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "boolean 값 변환 실패: 크기 다름, %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "빈 쿼리: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "지시자 없는 null 값: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "variable 형에서 배열형을 사용하고 있지 않음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "서버에서 읽은 자료가 배열형이 아님: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "변수들의 배열을 삽입하는 기능을 제공하지 않음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "\"%s\" 연결이 없음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "\"%s\" 연결이 현재 끊겼음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "\"%s\" 이름은 잘못된 쿼리구문 이름: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "\"%s\" 이름의 기술자가 없음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "기술자 색인 범위를 벗어남: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "\"%s\" 이름은 알 수 없는 기술자 항목: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "변수에 numeric 형이 없음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "변수에 character 형이 없음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "트랜잭션 처리 중 실패: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "\"%s\" 데이터베이스로 접속할 수 없음: %d 번째 줄"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "SQL 오류 %d: %d 번째 줄"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "서버 연결 끊김"
+
+#: error.c:347
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL 오류: %s\n"
+
+#: execute.c:2198 execute.c:2205
+msgid "<empty>"
+msgstr "<empty>"
diff --git a/src/interfaces/ecpg/ecpglib/po/pl.po b/src/interfaces/ecpg/ecpglib/po/pl.po
new file mode 100644
index 0000000..0d5554b
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/pl.po
@@ -0,0 +1,174 @@
+# Polish message translation file for ecpglib
+# Copyright (C) 2011 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+# Begina Felicysym <begina.felicysym@wp.eu>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL 9.1)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
+"POT-Creation-Date: 2013-01-29 13:40+0000\n"
+"PO-Revision-Date: 2011-09-30 09:51-0300\n"
+"Last-Translator: Begina Felicysym <begina.felicysym@wp.eu>\n"
+"Language-Team: Begina Felicysym\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Virtaal 0.7.1-beta1\n"
+
+#: connect.c:231
+msgid "empty message text"
+msgstr "pusty tekst komunikatu"
+
+#: connect.c:384 connect.c:413 connect.c:618
+msgid "<DEFAULT>"
+msgstr "<DOMYŚLNIE>"
+
+#: descriptor.c:807 misc.c:113
+msgid "NULL"
+msgstr "NULL"
+
+#: error.c:29
+#, c-format
+msgid "no data found on line %d"
+msgstr "nie znaleziono danych, linia %d"
+
+#: error.c:39
+#, c-format
+msgid "out of memory on line %d"
+msgstr "brak pamięci, linia %d"
+
+#: error.c:49
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "nieobsługiwany typ \"%s\", linia %d"
+
+#: error.c:59
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "zbyt wiele argumentów, linia %d"
+
+#: error.c:69
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "zbyt mało argumentów, linia %d"
+
+#: error.c:79
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "niepoprawna składnia wejścia dla typu int: \"%s\", linia %d"
+
+#: error.c:89
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "niepoprawna składnia wejścia dla typu unsigned int: \"%s\", linia %d"
+
+#: error.c:99
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "niepoprawna składnia wejścia dla typu floating-point: \"%s\", linia %d"
+
+#: error.c:110
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "niepoprawna składnia wejścia dla typu boolean: \"%s\", linia %d"
+
+#: error.c:118
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "nie można przekształcić wartości logicznej: niepoprawny rozmiar, linia %d"
+
+#: error.c:128
+#, c-format
+msgid "empty query on line %d"
+msgstr "puste zapytanie, linia %d"
+
+#: error.c:138
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "wartość null bez wskaźnika, linia %d"
+
+#: error.c:148
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "zmienna nie ma typu array, linia %d"
+
+#: error.c:158
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "dane odczytane z serwera nie są tablicą, linia %d"
+
+#: error.c:168
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "wstawienie tablicy zmiennych nie jest obsługiwane, linia %d"
+
+#: error.c:178
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "połączenie \"%s\" nie istnieje, linia %d"
+
+#: error.c:188
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "nie wykonano połączenia z \"%s\", linia %d"
+
+#: error.c:198
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "niepoprawna nazwa wyrażenia \"%s\", linia %d"
+
+#: error.c:208
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "nie odnaleziono deskryptora \"%s\", linia %d"
+
+#: error.c:218
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "indeks deskryptora poza zakresem, linia %d"
+
+#: error.c:228
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "niezrozumiały element deskryptora \"%s\", linia %d"
+
+#: error.c:238
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "zmienna nie ma typu typu numeric, linia %d"
+
+#: error.c:248
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "zmienna nie ma typu typu character, linia %d"
+
+#: error.c:258
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "błąd w przetwarzaniu transakcji, linia %d"
+
+#: error.c:268
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "nie można połączyć się z bazą danych \"%s\", linia %d"
+
+#: error.c:278
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "błąd SQL %d, linia %d"
+
+#: error.c:318
+msgid "the connection to the server was lost"
+msgstr "połączenie z serwerem zostało przerwane"
+
+#: error.c:405
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "błąd SQL: %s\n"
+
+#: execute.c:1921
+msgid "<empty>"
+msgstr "<pusty>"
diff --git a/src/interfaces/ecpg/ecpglib/po/pt_BR.po b/src/interfaces/ecpg/ecpglib/po/pt_BR.po
new file mode 100644
index 0000000..781ad17
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/pt_BR.po
@@ -0,0 +1,201 @@
+# Brazilian Portuguese message translation file for ecpglib
+#
+# Copyright (C) 2009-2022 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+#
+# Fernando Ike de Oliveira <fike@midstorm.org>, 2009.
+# Euler Taveira <euler@eulerto.com>, 2010-2022.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 15\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-09-10 18:24-0300\n"
+"PO-Revision-Date: 2009-02-09 13:00-0200\n"
+"Last-Translator: Fernando Ike de Oliveira <fike@midstorm.org>\n"
+"Language-Team: Brazilian Portuguese <pgsql-translators@postgresql.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: connect.c:243
+msgid "empty message text"
+msgstr "mensagem vazia"
+
+#: connect.c:410 connect.c:675
+msgid "<DEFAULT>"
+msgstr "<PADRÃO>"
+
+#: descriptor.c:876 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "nenhum dado encontrado na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "sem memória na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "tipo \"%s\" não é suportado na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "muitos argumentos na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "poucos argumentos na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "sintaxe de entrada é inválida para tipo inteiro: \"%s\", na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "sintaxe de entrada é inválida para tipo inteiro não-sinalizado: \"%s\", na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "sintaxe de entrada é inválida para tipo ponto flutuante: \"%s\", na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "sintaxe de entrada é inválida par tipo booleano: \"%s\", na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "não pôde converter valor booleano: tamanho não corresponde, na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "consulta vazia na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "valor nulo sem indicador na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "variável não tem um tipo matriz na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "dado lido do servidor não é uma matriz na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "inserir uma matriz de variáveis não é suportado na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "conexão \"%s\" não existe na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "não está conectado a conexão \"%s\" na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "nome de comando \"%s\" é inválido na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "descritor \"%s\" não foi encontrado na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "índice do descritor está fora do intervalo na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "item do descritor \"%s\" é desconhecido na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "variável não tem um tipo numérico na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "variável não tem um tipo caracter na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "erro ao processar transação na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "não pôde connectar ao banco de dados \"%s\" na linha %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "Erro SQL %d na linha %d"
+
+#: error.c:253
+msgid "the connection to the server was lost"
+msgstr "a conexão com servidor foi perdida"
+
+#: error.c:345
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "Erro SQL: %s\n"
+
+#: execute.c:2189 execute.c:2196
+msgid "<empty>"
+msgstr "<vazio>"
diff --git a/src/interfaces/ecpg/ecpglib/po/ru.po b/src/interfaces/ecpg/ecpglib/po/ru.po
new file mode 100644
index 0000000..d4eb7da
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/ru.po
@@ -0,0 +1,204 @@
+# Russian message translation file for ecpglib
+# Copyright (C) 2012-2016 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Alexander Lakhin <exclusion@gmail.com>, 2012-2017, 2019.
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL current)\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-27 14:52+0300\n"
+"PO-Revision-Date: 2019-09-09 13:30+0300\n"
+"Last-Translator: Alexander Lakhin <exclusion@gmail.com>\n"
+"Language-Team: Russian <pgsql-ru-general@postgresql.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+
+#: connect.c:243
+msgid "empty message text"
+msgstr "пустое сообщение"
+
+#: connect.c:410 connect.c:675
+msgid "<DEFAULT>"
+msgstr "<ПО_УМОЛЧАНИЮ>"
+
+#: descriptor.c:876 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "нет данных (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "нехватка памяти (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "неподдерживаемый тип \"%s\" в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "слишком много аргументов в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "недостаточно аргументов в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "неверный синтаксис для целого числа: \"%s\" (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "неверный синтаксис для беззнакового целого: \"%s\" (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "неверный синтаксис для числа с плавающей точкой: \"%s\" (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "неверный синтаксис для логического значения: \"%s\" (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr ""
+"не удалось преобразовать логическое значение: несовпадение размера (строка "
+"%d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "пустой запрос в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "значение NULL без индикатора в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "переменная должна иметь тип массива (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "полученные с сервера данные - не массив (%d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "добавление массива переменных не поддерживается (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "подключение \"%s\" не существует (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "подключение \"%s\" не установлено (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "неверный оператор \"%s\" в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "дескриптор \"%s\" не найден (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "индекс дескриптора вне диапазона (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "нераспознанный элемент дескриптора \"%s\" (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "переменная должна быть числовой (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "переменная должна быть символьной (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "ошибка при обработке транзакции в строке %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "ошибка подключения к базе данных \"%s\" (строка %d)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "SQL-ошибка %d в строке %d"
+
+#: error.c:253
+msgid "the connection to the server was lost"
+msgstr "подключение к серверу потеряно"
+
+#: error.c:345
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "ошибка SQL: %s\n"
+
+#: execute.c:2189 execute.c:2196
+msgid "<empty>"
+msgstr "<>"
+
+#~ msgid "cursor is invalid on line %d"
+#~ msgstr "некорректный курсор в строке %d"
diff --git a/src/interfaces/ecpg/ecpglib/po/sv.po b/src/interfaces/ecpg/ecpglib/po/sv.po
new file mode 100644
index 0000000..7ccde10
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/sv.po
@@ -0,0 +1,199 @@
+# SWEDISH message translation file for ecpglib
+# Copyright (C) 2017 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# Dennis Björklund <db@zigo.dhs.org>, 2017, 2018, 2019, 2020, 2021.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2020-04-10 02:39+0000\n"
+"PO-Revision-Date: 2021-11-07 10:36+0100\n"
+"Last-Translator: Dennis Björklund <db@zigo.dhs.org>\n"
+"Language-Team: Swedish <pgsql-translators@postgresql.org>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "tom meddelandetext"
+
+#: connect.c:401 connect.c:430 connect.c:653
+msgid "<DEFAULT>"
+msgstr "<DEFAULT>"
+
+#: descriptor.c:871 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "ingen data hittad på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "slut på minne på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "ej stöd för typ \"%s\" på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "för många argument på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "för få argument på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "ogiltig inputsyntax för typ int: \"%s\", på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "ogiltig inputsyntax för typ unsigned int: \"%s\", på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "ogiltig inputsyntaxc för flyttalstyp: \"%s\", på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "ogiltig syntax för typ boolean: \"%s\", på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "kunde inte konvertera booleanskt värde: storlekarna matchar inte, på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "tom fråga på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "null-värde utan indikator på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "variabel har inte array-typ på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "data inläst från servern är inte en array på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "sätta in en array med variabler stöds inte på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "anslutning \"%s\" funns inte på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "ej ansluten till anslutning \"%s\" på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "ogiltigt satsnamn \"%s\" på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "deskriptor \"%s\" hittades inte på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "deskriptor-index utanför sitt intervall på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "okänd deskriptor-post \"%s\" på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "variabel har ej numerisk typ på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "variabel har ej character-typ på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "fel i transaktionsprocessande på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "kunde inte ansluta till databas \"%s\" på rad %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "SQL-fel %d på rad %d"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "anslutningen till servern tappades"
+
+#: error.c:347
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL-fel: %s\n"
+
+#: execute.c:2196 execute.c:2203
+msgid "<empty>"
+msgstr "<tom>"
diff --git a/src/interfaces/ecpg/ecpglib/po/tr.po b/src/interfaces/ecpg/ecpglib/po/tr.po
new file mode 100644
index 0000000..38ac989
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/tr.po
@@ -0,0 +1,205 @@
+# LANGUAGE message translation file for ecpglib
+# Copyright (C) 2009 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PostgreSQL 8.4\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2019-04-26 13:38+0000\n"
+"PO-Revision-Date: 2019-06-14 10:40+0300\n"
+"Last-Translator: Devrim GÜNDÜZ <devrim@gunduz.org>\n"
+"Language-Team: TR <devrim@gunduz.org>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 1.8.7.1\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "boş mesaj metni"
+
+#: connect.c:403 connect.c:432 connect.c:640
+msgid "<DEFAULT>"
+msgstr "<ÖNTANIMLI>"
+
+#: descriptor.c:888 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "%d. satırda veri bulunamadı"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "%d. satırda yetersiz bellek"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "%2$d. satırda desteklenmeyen veri tipi \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "%d. satırda çok fazla argüman var"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "%d. satırda yetersiz argüman sayısı"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "%2$d. satırda int veri tipi için geçersiz girdi sözdizimi: \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "%2$d. satırda işaretsiz tamsayı tipi için geçersiz girdi sözdizimi: \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "%2$d. satırda kayan noktalı veri tipi için geçersiz girdi sözdizimi: \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "%2$d. satırda boolean veri tipi için geçersiz girdi sözdizimi: \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "boolean değer dönüştürülemedi: boyut uyuşmazlığı, %d. satırda"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "%d. satırda boş sorgu"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "%d. satırda belirteç olmadan null değer var"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "%d. satırda değişkenin veri tipi dizi değil"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "%d. satırda sunucudan okunan veri bir dizi değil"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "%d. satırda değişkenlerden oluşan dizinin eklenmesi (insert) desteklenmiyor"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "%2$d numaralı satırda \"%1$s\" bağlantısı yok"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "%2$d. satırda\"%1$s\" bağlantısına bağlanılmıyor"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "%2$d. satırda geçersiz ifade adı \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "%2$d. satırda tanımlayıcı \"%1$s\" bulunamadı"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "%d. satırdaki açıklayıcı indeks sınırların dışında"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "%2$d. satırda bilinmeyen tanımlayıcı öğe \"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "%d. satırdaki değişken sayısal tipte değil"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "%d. satırdaki değişkenin karakter tipi yok"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "%d. satırda tranaction'ı işlerken hata oluştu"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "%2$d. satırda \"%1$s\" veritabanına bağlanılamadı"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "The cursor is invalid on line %d"
+msgstr "%d. satırda geçersiz imleç (cursor)"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:214
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "%2$d. satırda SQL hatası %1$d"
+
+#: error.c:261
+msgid "the connection to the server was lost"
+msgstr "sunucuya bağlantı kesildi"
+
+#: error.c:354
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL hatası: %s\n"
+
+#: execute.c:2103
+msgid "<empty>"
+msgstr "<boş>"
diff --git a/src/interfaces/ecpg/ecpglib/po/uk.po b/src/interfaces/ecpg/ecpglib/po/uk.po
new file mode 100644
index 0000000..dcb1738
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/uk.po
@@ -0,0 +1,200 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: postgresql\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2022-08-12 10:39+0000\n"
+"PO-Revision-Date: 2022-09-13 11:52\n"
+"Last-Translator: \n"
+"Language-Team: Ukrainian\n"
+"Language: uk_UA\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=((n%10==1 && n%100!=11) ? 0 : ((n%10 >= 2 && n%10 <=4 && (n%100 < 12 || n%100 > 14)) ? 1 : ((n%10 == 0 || (n%10 >= 5 && n%10 <=9)) || (n%100 >= 11 && n%100 <= 14)) ? 2 : 3));\n"
+"X-Crowdin-Project: postgresql\n"
+"X-Crowdin-Project-ID: 324573\n"
+"X-Crowdin-Language: uk\n"
+"X-Crowdin-File: /REL_15_STABLE/ecpglib.pot\n"
+"X-Crowdin-File-ID: 890\n"
+
+#: connect.c:243
+msgid "empty message text"
+msgstr "пусте повідомлення"
+
+#: connect.c:410 connect.c:675
+msgid "<DEFAULT>"
+msgstr "<ЗА_ЗАМОВЧУВАННЯМ>"
+
+#: descriptor.c:876 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "в рядку %d не знайдено жодних даних"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "не вистачає пам'яті в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "непідтримуванний тип \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "забагато аргументів в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "недостатньо аргументів в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "неприпустимий синтаксис для цілочисельного типу: \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "неприпустимий синтаксис вводу для типу цілого числа без знаку: \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "неприпустимий синтаксис вводу для типу з плаваючою комою: \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "неприпустимий синтаксис для логічного типу: \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "не вдалося перетворити логічне значення: невідповідність розміру в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "пустий запит в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "значення NULL без індикатора в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "змінна не має типу масиву в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "дані, прочитані з сервера, не є масивом в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "вставлення масиву змінних не підтримується в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "підключення \"%s\" не існує в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "не підключено до підключення \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "неприпустимий оператор \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "дескриптор \"%s\" не знайдено в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "індекс дескриптора поза діапазоном в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "нерозпізнаний елемент дескриптора \"%s\" в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "змінна не має числового типу в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "змінна не має символьного типу в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "помилка в транзакції в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "неможливо під'єднатися до бази даних %s в рядку %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "помилка SQL %d в рядку %d"
+
+#: error.c:253
+msgid "the connection to the server was lost"
+msgstr "з'єднання із сервером втрачено"
+
+#: error.c:345
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "помилка SQL: %s\n"
+
+#: execute.c:2189 execute.c:2196
+msgid "<empty>"
+msgstr "<пусто>"
+
diff --git a/src/interfaces/ecpg/ecpglib/po/vi.po b/src/interfaces/ecpg/ecpglib/po/vi.po
new file mode 100644
index 0000000..850b54f
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/vi.po
@@ -0,0 +1,200 @@
+# LANGUAGE message translation file for ecpglib
+# Copyright (C) 2018 PostgreSQL Global Development Group
+# This file is distributed under the same license as the ecpglib (PostgreSQL) package.
+# FIRST AUTHOR <kakalot49@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 11\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n"
+"POT-Creation-Date: 2018-04-22 12:08+0000\n"
+"PO-Revision-Date: 2018-04-23 21:34+0900\n"
+"Language-Team: <pgvn_translators@postgresql.vn>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.0.6\n"
+"Last-Translator: Dang Minh Huong <kakalot49@gmail.com>\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"Language: vi_VN\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "văn bản tin nhắn trống"
+
+#: connect.c:401 connect.c:430 connect.c:638
+msgid "<DEFAULT>"
+msgstr "<MẶC ĐỊNH>"
+
+#: descriptor.c:834 misc.c:120
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "không tìm thấy dữ liệu trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "hết bộ nhớ trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "không hỗ trợ kiểu \"%s\" trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "quá nhiều đối số trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "quá ít đối số trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "cú pháp nhập không hợp lệ cho kiểu int: \"%s\", trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "cú pháp nhập không hợp lệ cho kiểu unsigned int: \"%s\", trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "cú pháp nhập không hợp lệ cho kiểu dấu phẩy động: \"%s\", trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "cú pháp không hợp lệ cho kiểu boolean: \"%s\", trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "không thể chuyển đổi giá trị boolean: kích thước không khớp, trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "truy vấn trống trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "giá trị null không có chỉ báo (indicator) trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "biến không có kiểu mảng trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "dữ liệu đọc từ server không phải là một mảng trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "chèn một mảng các biến không được hỗ trợ trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "kết nối \"%s\" không tồn tại trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "chưa kết nối tới kết nối \"%s\" trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "tên statement không hợp lệ \"%s\" trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "không tìm thấy descriptor \"%s\" trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "chỉ mục của descriptor nằm ngoài phạm vi trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "không nhận ra descriptor item \"%s\" trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "biến số không có kiểu numeric trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "biến số không có kiểu ký tự trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "lỗi trong xử lý giao dịch trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "không thể kết nối với cơ sở dữ liệu \"%s\" trên dòng %d"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "lỗi SQL %d trên dòng %d"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "kết nối với server bị mất"
+
+#: error.c:347
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "lỗi SQL: %s\n"
+
+#: execute.c:1968
+msgid "<empty>"
+msgstr "<trống>"
diff --git a/src/interfaces/ecpg/ecpglib/po/zh_CN.po b/src/interfaces/ecpg/ecpglib/po/zh_CN.po
new file mode 100644
index 0000000..d775d4f
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/po/zh_CN.po
@@ -0,0 +1,199 @@
+# LANGUAGE message translation file for ecpglib
+# Copyright (C) 2010 PostgreSQL Global Development Group
+# This file is distributed under the same license as the PostgreSQL package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ecpglib (PostgreSQL) 14\n"
+"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n"
+"POT-Creation-Date: 2021-08-14 05:39+0000\n"
+"PO-Revision-Date: 2021-08-15 18:50+0800\n"
+"Last-Translator: Jie Zhang <zhangjie2@fujitsu.com>\n"
+"Language-Team: Chinese (Simplified) <zhangjie2@fujitsu.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: connect.c:237
+msgid "empty message text"
+msgstr "消息文本为空"
+
+#: connect.c:405 connect.c:627
+msgid "<DEFAULT>"
+msgstr "<DEFAULT>"
+
+#: descriptor.c:871 misc.c:119
+msgid "NULL"
+msgstr "NULL"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:33
+#, c-format
+msgid "no data found on line %d"
+msgstr "在第%d行上没有找到数据"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:40
+#, c-format
+msgid "out of memory on line %d"
+msgstr "在第%d行上内存用尽"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:47
+#, c-format
+msgid "unsupported type \"%s\" on line %d"
+msgstr "在第%2$d上出现不支持的类型\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:54
+#, c-format
+msgid "too many arguments on line %d"
+msgstr "在第%d行上的参数多于指定的数量"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:61
+#, c-format
+msgid "too few arguments on line %d"
+msgstr "在第%d行上的参数少于指定的数量"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:68
+#, c-format
+msgid "invalid input syntax for type int: \"%s\", on line %d"
+msgstr "对于整数类型的输入语法无效: \"%s\" ,在第%d行"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:75
+#, c-format
+msgid "invalid input syntax for type unsigned int: \"%s\", on line %d"
+msgstr "对于无符号整数类型的输入语法无效: \"%s\" 在第%d行"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:82
+#, c-format
+msgid "invalid input syntax for floating-point type: \"%s\", on line %d"
+msgstr "对于浮点类型的输入语法无效: \"%s\",在第%d行"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:90
+#, c-format
+msgid "invalid syntax for type boolean: \"%s\", on line %d"
+msgstr "对于布尔类型语法无效: \"%s\",在第%d行上"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:95
+#, c-format
+msgid "could not convert boolean value: size mismatch, on line %d"
+msgstr "在第%d行上无法转换布尔类型值: 大小不匹配"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:102
+#, c-format
+msgid "empty query on line %d"
+msgstr "在第%d行上查询是空的"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:109
+#, c-format
+msgid "null value without indicator on line %d"
+msgstr "在第%d行上的空值没有标志"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:116
+#, c-format
+msgid "variable does not have an array type on line %d"
+msgstr "在第%d行上的变量没有数组类型"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:123
+#, c-format
+msgid "data read from server is not an array on line %d"
+msgstr "在第%d行上从服务器读取的数据不是数组"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:130
+#, c-format
+msgid "inserting an array of variables is not supported on line %d"
+msgstr "在第%d行上不支持正在插入一个的变量数组"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:137
+#, c-format
+msgid "connection \"%s\" does not exist on line %d"
+msgstr "在第%2$d行上连接\"%1$s\"不存在"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:144
+#, c-format
+msgid "not connected to connection \"%s\" on line %d"
+msgstr "在第%2$d行上没有连接到\"%1$s\"连接"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:151
+#, c-format
+msgid "invalid statement name \"%s\" on line %d"
+msgstr "在第%2$d行上的语句名称\"%1$s\"无效"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:158
+#, c-format
+msgid "descriptor \"%s\" not found on line %d"
+msgstr "在第%2$d行上没有找到描述符\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:165
+#, c-format
+msgid "descriptor index out of range on line %d"
+msgstr "在第%d行上的描述符索引超出范围"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:172
+#, c-format
+msgid "unrecognized descriptor item \"%s\" on line %d"
+msgstr "在第%2$d行上出现无法识别的描述符成员\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:179
+#, c-format
+msgid "variable does not have a numeric type on line %d"
+msgstr "在第%d上的变量没有数值类型"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:186
+#, c-format
+msgid "variable does not have a character type on line %d"
+msgstr "在第%d行上的变量没有字符类型"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:193
+#, c-format
+msgid "error in transaction processing on line %d"
+msgstr "在第%d行上的事务处理中发生错误"
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:200
+#, c-format
+msgid "could not connect to database \"%s\" on line %d"
+msgstr "在第%2$d行上无法连接数据库\"%1$s\""
+
+#. translator: this string will be truncated at 149 characters expanded.
+#: error.c:207
+#, c-format
+msgid "SQL error %d on line %d"
+msgstr "在第%2$d行上的SQL命令发生错误 代码%1$d"
+
+#: error.c:254
+msgid "the connection to the server was lost"
+msgstr "与服务器的连接丢失"
+
+#: error.c:346
+#, c-format
+msgid "SQL error: %s\n"
+msgstr "SQL语句错误: %s\n"
+
+#: execute.c:2196 execute.c:2203
+msgid "<empty>"
+msgstr "<空>"
+
diff --git a/src/interfaces/ecpg/ecpglib/prepare.c b/src/interfaces/ecpg/ecpglib/prepare.c
new file mode 100644
index 0000000..ea1146f
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/prepare.c
@@ -0,0 +1,602 @@
+/* src/interfaces/ecpg/ecpglib/prepare.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include <ctype.h>
+
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "sqlca.h"
+
+#define STMTID_SIZE 32
+
+/*
+ * The statement cache contains stmtCacheNBuckets hash buckets, each
+ * having stmtCacheEntPerBucket entries, which we recycle as needed,
+ * giving up the least-executed entry in the bucket.
+ * stmtCacheEntries[0] is never used, so that zero can be a "not found"
+ * indicator.
+ */
+#define stmtCacheNBuckets 2039 /* should be a prime number */
+#define stmtCacheEntPerBucket 8
+
+#define stmtCacheArraySize (stmtCacheNBuckets * stmtCacheEntPerBucket + 1)
+
+typedef struct
+{
+ int lineno;
+ char stmtID[STMTID_SIZE];
+ char *ecpgQuery;
+ long execs; /* # of executions */
+ const char *connection; /* connection for the statement */
+} stmtCacheEntry;
+
+static int nextStmtID = 1;
+static stmtCacheEntry *stmtCacheEntries = NULL;
+
+static bool deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
+ struct prepared_statement *prev, struct prepared_statement *this);
+
+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;
+}
+
+bool
+ecpg_register_prepared_stmt(struct statement *stmt)
+{
+ struct statement *prep_stmt;
+ struct prepared_statement *this;
+ struct connection *con = stmt->connection;
+ struct prepared_statement *prev = NULL;
+ int lineno = stmt->lineno;
+
+ /* check if we already have prepared this statement */
+ this = ecpg_find_prepared_statement(stmt->name, con, &prev);
+ if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
+ return false;
+
+ /* allocate new statement */
+ this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
+ if (!this)
+ return false;
+
+ prep_stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
+ if (!prep_stmt)
+ {
+ ecpg_free(this);
+ return false;
+ }
+ memset(prep_stmt, 0, sizeof(struct statement));
+
+ /* create statement */
+ prep_stmt->lineno = lineno;
+ prep_stmt->connection = con;
+ prep_stmt->command = ecpg_strdup(stmt->command, lineno);
+ prep_stmt->inlist = prep_stmt->outlist = NULL;
+ this->name = ecpg_strdup(stmt->name, lineno);
+ this->stmt = prep_stmt;
+ this->prepared = true;
+
+ if (con->prep_stmts == NULL)
+ this->next = NULL;
+ else
+ this->next = con->prep_stmts;
+
+ con->prep_stmts = this;
+ return true;
+}
+
+static bool
+replace_variables(char **text, int lineno)
+{
+ bool string = false;
+ int counter = 1,
+ ptr = 0;
+
+ for (; (*text)[ptr] != '\0'; ptr++)
+ {
+ if ((*text)[ptr] == '\'')
+ string = string ? false : true;
+
+ if (string || (((*text)[ptr] != ':') && ((*text)[ptr] != '?')))
+ continue;
+
+ if (((*text)[ptr] == ':') && ((*text)[ptr + 1] == ':'))
+ ptr += 2; /* skip '::' */
+ else
+ {
+ /* a rough guess of the size we need: */
+ int buffersize = sizeof(int) * CHAR_BIT * 10 / 3;
+ int len;
+ char *buffer,
+ *newcopy;
+
+ if (!(buffer = (char *) ecpg_alloc(buffersize, lineno)))
+ return false;
+
+ snprintf(buffer, buffersize, "$%d", counter++);
+
+ for (len = 1; (*text)[ptr + len] && isvarchar((*text)[ptr + len]); len++)
+ /* skip */ ;
+ if (!(newcopy = (char *) ecpg_alloc(strlen(*text) - len + strlen(buffer) + 1, lineno)))
+ {
+ ecpg_free(buffer);
+ return false;
+ }
+
+ memcpy(newcopy, *text, ptr);
+ strcpy(newcopy + ptr, buffer);
+ strcat(newcopy, (*text) +ptr + len);
+
+ ecpg_free(*text);
+ ecpg_free(buffer);
+
+ *text = newcopy;
+
+ if ((*text)[ptr] == '\0') /* we reached the end */
+ ptr--; /* since we will (*text)[ptr]++ in the top
+ * level for loop */
+ }
+ }
+ return true;
+}
+
+static bool
+prepare_common(int lineno, struct connection *con, const char *name, const char *variable)
+{
+ struct statement *stmt;
+ struct prepared_statement *this;
+ PGresult *query;
+
+ /* allocate new statement */
+ this = (struct prepared_statement *) ecpg_alloc(sizeof(struct prepared_statement), lineno);
+ if (!this)
+ return false;
+
+ stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno);
+ if (!stmt)
+ {
+ ecpg_free(this);
+ return false;
+ }
+
+ /* create statement */
+ stmt->lineno = lineno;
+ stmt->connection = con;
+ stmt->command = ecpg_strdup(variable, lineno);
+ stmt->inlist = stmt->outlist = NULL;
+
+ /* if we have C variables in our statement replace them with '?' */
+ replace_variables(&(stmt->command), lineno);
+
+ /* add prepared statement to our list */
+ this->name = ecpg_strdup(name, lineno);
+ this->stmt = stmt;
+
+ /* and finally really prepare the statement */
+ query = PQprepare(stmt->connection->connection, name, stmt->command, 0, NULL);
+ if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat))
+ {
+ ecpg_free(stmt->command);
+ ecpg_free(this->name);
+ ecpg_free(this);
+ ecpg_free(stmt);
+ return false;
+ }
+
+ ecpg_log("prepare_common on line %d: name %s; query: \"%s\"\n", stmt->lineno, name, stmt->command);
+ PQclear(query);
+ this->prepared = true;
+
+ if (con->prep_stmts == NULL)
+ this->next = NULL;
+ else
+ this->next = con->prep_stmts;
+
+ con->prep_stmts = this;
+ return true;
+}
+
+/* handle the EXEC SQL PREPARE statement */
+/* questionmarks is not needed but remains in there for the time being to not change the API */
+bool
+ECPGprepare(int lineno, const char *connection_name, const bool questionmarks,
+ const char *name, const char *variable)
+{
+ struct connection *con;
+ struct prepared_statement *this,
+ *prev;
+
+ (void) questionmarks; /* quiet the compiler */
+
+ con = ecpg_get_connection(connection_name);
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+ /* check if we already have prepared this statement */
+ this = ecpg_find_prepared_statement(name, con, &prev);
+ if (this && !deallocate_one(lineno, ECPG_COMPAT_PGSQL, con, prev, this))
+ return false;
+
+ return prepare_common(lineno, con, name, variable);
+}
+
+struct prepared_statement *
+ecpg_find_prepared_statement(const char *name,
+ struct connection *con, struct prepared_statement **prev_)
+{
+ struct prepared_statement *this,
+ *prev;
+
+ for (this = con->prep_stmts, prev = NULL;
+ this != NULL;
+ prev = this, this = this->next)
+ {
+ if (strcmp(this->name, name) == 0)
+ {
+ if (prev_)
+ *prev_ = prev;
+ return this;
+ }
+ }
+ return NULL;
+}
+
+static bool
+deallocate_one(int lineno, enum COMPAT_MODE c, struct connection *con,
+ struct prepared_statement *prev, struct prepared_statement *this)
+{
+ bool r = false;
+
+ ecpg_log("deallocate_one on line %d: name %s\n", lineno, this->name);
+
+ /* first deallocate the statement in the backend */
+ if (this->prepared)
+ {
+ char *text;
+ PGresult *query;
+
+ text = (char *) ecpg_alloc(strlen("deallocate \"\" ") + strlen(this->name), this->stmt->lineno);
+
+ if (text)
+ {
+ sprintf(text, "deallocate \"%s\"", this->name);
+ query = PQexec(this->stmt->connection->connection, text);
+ ecpg_free(text);
+ if (ecpg_check_PQresult(query, lineno,
+ this->stmt->connection->connection,
+ this->stmt->compat))
+ {
+ PQclear(query);
+ r = true;
+ }
+ }
+ }
+
+ /*
+ * Just ignore all errors since we do not know the list of cursors we are
+ * allowed to free. We have to trust the software.
+ */
+ if (!r && !INFORMIX_MODE(c))
+ {
+ ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, this->name);
+ return false;
+ }
+
+ /* okay, free all the resources */
+ ecpg_free(this->stmt->command);
+ ecpg_free(this->stmt);
+ ecpg_free(this->name);
+ if (prev != NULL)
+ prev->next = this->next;
+ else
+ con->prep_stmts = this->next;
+
+ ecpg_free(this);
+ return true;
+}
+
+/* handle the EXEC SQL DEALLOCATE PREPARE statement */
+bool
+ECPGdeallocate(int lineno, int c, const char *connection_name, const char *name)
+{
+ struct connection *con;
+ struct prepared_statement *this,
+ *prev;
+
+ con = ecpg_get_connection(connection_name);
+ if (!ecpg_init(con, connection_name, lineno))
+ return false;
+
+ this = ecpg_find_prepared_statement(name, con, &prev);
+ if (this)
+ return deallocate_one(lineno, c, con, prev, this);
+
+ /* prepared statement is not found */
+ if (INFORMIX_MODE(c))
+ return true;
+ ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, name);
+ return false;
+}
+
+bool
+ecpg_deallocate_all_conn(int lineno, enum COMPAT_MODE c, struct connection *con)
+{
+ /* deallocate all prepared statements */
+ while (con->prep_stmts)
+ {
+ if (!deallocate_one(lineno, c, con, NULL, con->prep_stmts))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ECPGdeallocate_all(int lineno, int compat, const char *connection_name)
+{
+ return ecpg_deallocate_all_conn(lineno, compat,
+ ecpg_get_connection(connection_name));
+}
+
+char *
+ecpg_prepared(const char *name, struct connection *con)
+{
+ struct prepared_statement *this;
+
+ this = ecpg_find_prepared_statement(name, con, NULL);
+ return this ? this->stmt->command : NULL;
+}
+
+/* return the prepared statement */
+/* lineno is not used here, but kept in to not break API */
+char *
+ECPGprepared_statement(const char *connection_name, const char *name, int lineno)
+{
+ (void) lineno; /* keep the compiler quiet */
+
+ return ecpg_prepared(name, ecpg_get_connection(connection_name));
+}
+
+/*
+ * hash a SQL statement - returns entry # of first entry in the bucket
+ */
+static int
+HashStmt(const char *ecpgQuery)
+{
+ int stmtIx,
+ bucketNo,
+ hashLeng,
+ stmtLeng;
+ uint64 hashVal,
+ rotVal;
+
+ stmtLeng = strlen(ecpgQuery);
+ hashLeng = 50; /* use 1st 50 characters of statement */
+ if (hashLeng > stmtLeng) /* if the statement isn't that long */
+ hashLeng = stmtLeng; /* use its actual length */
+
+ hashVal = 0;
+ for (stmtIx = 0; stmtIx < hashLeng; ++stmtIx)
+ {
+ hashVal = hashVal + (unsigned char) ecpgQuery[stmtIx];
+ /* rotate 32-bit hash value left 13 bits */
+ hashVal = hashVal << 13;
+ rotVal = (hashVal & UINT64CONST(0x1fff00000000)) >> 32;
+ hashVal = (hashVal & UINT64CONST(0xffffffff)) | rotVal;
+ }
+
+ bucketNo = hashVal % stmtCacheNBuckets;
+
+ /* Add 1 so that array entry 0 is never used */
+ return bucketNo * stmtCacheEntPerBucket + 1;
+}
+
+/*
+ * search the statement cache - search for entry with matching ECPG-format query
+ * Returns entry # in cache if found
+ * OR zero if not present (zero'th entry isn't used)
+ */
+static int
+SearchStmtCache(const char *ecpgQuery)
+{
+ int entNo,
+ entIx;
+
+ /* quick failure if cache not set up */
+ if (stmtCacheEntries == NULL)
+ return 0;
+
+ /* hash the statement */
+ entNo = HashStmt(ecpgQuery);
+
+ /* search the cache */
+ for (entIx = 0; entIx < stmtCacheEntPerBucket; ++entIx)
+ {
+ if (stmtCacheEntries[entNo].stmtID[0]) /* check if entry is in use */
+ {
+ if (strcmp(ecpgQuery, stmtCacheEntries[entNo].ecpgQuery) == 0)
+ break; /* found it */
+ }
+ ++entNo; /* incr entry # */
+ }
+
+ /* if entry wasn't found - set entry # to zero */
+ if (entIx >= stmtCacheEntPerBucket)
+ entNo = 0;
+
+ return entNo;
+}
+
+/*
+ * free an entry in the statement cache
+ * Returns entry # in cache used
+ * OR negative error code
+ */
+static int
+ecpg_freeStmtCacheEntry(int lineno, int compat,
+ int entNo) /* entry # to free */
+{
+ stmtCacheEntry *entry;
+ struct connection *con;
+ struct prepared_statement *this,
+ *prev;
+
+ /* fail if cache isn't set up */
+ if (stmtCacheEntries == NULL)
+ return -1;
+
+ entry = &stmtCacheEntries[entNo];
+ if (!entry->stmtID[0]) /* return if the entry isn't in use */
+ return 0;
+
+ con = ecpg_get_connection(entry->connection);
+
+ /* free the 'prepared_statement' list entry */
+ this = ecpg_find_prepared_statement(entry->stmtID, con, &prev);
+ if (this && !deallocate_one(lineno, compat, con, prev, this))
+ return -1;
+
+ entry->stmtID[0] = '\0';
+
+ /* free the memory used by the cache entry */
+ if (entry->ecpgQuery)
+ {
+ ecpg_free(entry->ecpgQuery);
+ entry->ecpgQuery = 0;
+ }
+
+ return entNo;
+}
+
+/*
+ * add an entry to the statement cache
+ * returns entry # in cache used OR negative error code
+ */
+static int
+AddStmtToCache(int lineno, /* line # of statement */
+ const char *stmtID, /* statement ID */
+ const char *connection, /* connection */
+ int compat, /* compatibility level */
+ const char *ecpgQuery) /* query */
+{
+ int ix,
+ initEntNo,
+ luEntNo,
+ entNo;
+ stmtCacheEntry *entry;
+
+ /* allocate and zero cache array if we haven't already */
+ if (stmtCacheEntries == NULL)
+ {
+ stmtCacheEntries = (stmtCacheEntry *)
+ ecpg_alloc(sizeof(stmtCacheEntry) * stmtCacheArraySize, lineno);
+ if (stmtCacheEntries == NULL)
+ return -1;
+ }
+
+ /* hash the statement */
+ initEntNo = HashStmt(ecpgQuery);
+
+ /* search for an unused entry */
+ entNo = initEntNo; /* start with the initial entry # for the
+ * bucket */
+ luEntNo = initEntNo; /* use it as the initial 'least used' entry */
+ for (ix = 0; ix < stmtCacheEntPerBucket; ++ix)
+ {
+ entry = &stmtCacheEntries[entNo];
+ if (!entry->stmtID[0]) /* unused entry - use it */
+ break;
+ if (entry->execs < stmtCacheEntries[luEntNo].execs)
+ luEntNo = entNo; /* save new 'least used' entry */
+ ++entNo; /* increment entry # */
+ }
+
+ /*
+ * if no unused entries were found, re-use the 'least used' entry found in
+ * the bucket
+ */
+ if (ix >= stmtCacheEntPerBucket)
+ entNo = luEntNo;
+
+ /* 'entNo' is the entry to use - make sure its free */
+ if (ecpg_freeStmtCacheEntry(lineno, compat, entNo) < 0)
+ return -1;
+
+ /* add the query to the entry */
+ entry = &stmtCacheEntries[entNo];
+ entry->lineno = lineno;
+ entry->ecpgQuery = ecpg_strdup(ecpgQuery, lineno);
+ entry->connection = connection;
+ entry->execs = 0;
+ memcpy(entry->stmtID, stmtID, sizeof(entry->stmtID));
+
+ return entNo;
+}
+
+/* handle cache and preparation of statements in auto-prepare mode */
+bool
+ecpg_auto_prepare(int lineno, const char *connection_name, const int compat, char **name, const char *query)
+{
+ int entNo;
+
+ /* search the statement cache for this statement */
+ entNo = SearchStmtCache(query);
+
+ /* if not found - add the statement to the cache */
+ if (entNo)
+ {
+ char *stmtID;
+ struct connection *con;
+ struct prepared_statement *prep;
+
+ ecpg_log("ecpg_auto_prepare on line %d: statement found in cache; entry %d\n", lineno, entNo);
+
+ stmtID = stmtCacheEntries[entNo].stmtID;
+
+ con = ecpg_get_connection(connection_name);
+ prep = ecpg_find_prepared_statement(stmtID, con, NULL);
+ /* This prepared name doesn't exist on this connection. */
+ if (!prep && !prepare_common(lineno, con, stmtID, query))
+ return false;
+
+ *name = ecpg_strdup(stmtID, lineno);
+ }
+ else
+ {
+ char stmtID[STMTID_SIZE];
+
+ ecpg_log("ecpg_auto_prepare on line %d: statement not in cache; inserting\n", lineno);
+
+ /* generate a statement ID */
+ sprintf(stmtID, "ecpg%d", nextStmtID++);
+
+ if (!ECPGprepare(lineno, connection_name, 0, stmtID, query))
+ return false;
+
+ entNo = AddStmtToCache(lineno, stmtID, connection_name, compat, query);
+ if (entNo < 0)
+ return false;
+
+ *name = ecpg_strdup(stmtID, lineno);
+ }
+
+ /* increase usage counter */
+ stmtCacheEntries[entNo].execs++;
+
+ return true;
+}
diff --git a/src/interfaces/ecpg/ecpglib/sqlda.c b/src/interfaces/ecpg/ecpglib/sqlda.c
new file mode 100644
index 0000000..081e326
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/sqlda.c
@@ -0,0 +1,592 @@
+/*
+ * SQLDA support routines
+ *
+ * The allocated memory area pointed by an sqlda pointer
+ * contains both the metadata and the data, so freeing up
+ * is a simple free(sqlda) as expected by the ESQL/C examples.
+ */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include "catalog/pg_type_d.h"
+#include "decimal.h"
+#include "ecpg-pthread-win32.h"
+#include "ecpgerrno.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "sqlca.h"
+#include "sqlda-compat.h"
+#include "sqlda-native.h"
+
+/*
+ * Compute the next variable's offset with
+ * the current variable's size and alignment.
+ *
+ *
+ * Returns:
+ * - the current variable's offset in *current
+ * - the next variable's offset in *next
+ */
+static void
+ecpg_sqlda_align_add_size(long offset, int alignment, int size, long *current, long *next)
+{
+ if (offset % alignment)
+ offset += alignment - (offset % alignment);
+ if (current)
+ *current = offset;
+ offset += size;
+ if (next)
+ *next = offset;
+}
+
+static long
+sqlda_compat_empty_size(const PGresult *res)
+{
+ long offset;
+ int i;
+ int sqld = PQnfields(res);
+
+ /* Initial size to store main structure and field structures */
+ offset = sizeof(struct sqlda_compat) + sqld * sizeof(struct sqlvar_compat);
+
+ /* Add space for field names */
+ for (i = 0; i < sqld; i++)
+ offset += strlen(PQfname(res, i)) + 1;
+
+ /* Add padding to the first field value */
+ ecpg_sqlda_align_add_size(offset, sizeof(int), 0, &offset, NULL);
+
+ return offset;
+}
+
+static long
+sqlda_common_total_size(const PGresult *res, int row, enum COMPAT_MODE compat, long offset)
+{
+ int sqld = PQnfields(res);
+ int i;
+ long next_offset;
+
+ /* Add space for the field values */
+ for (i = 0; i < sqld; i++)
+ {
+ enum ECPGttype type = sqlda_dynamic_type(PQftype(res, i), compat);
+
+ switch (type)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset);
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset);
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset);
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset);
+ break;
+ case ECPGt_bool:
+ ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset);
+ break;
+ case ECPGt_float:
+ ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset);
+ break;
+ case ECPGt_double:
+ ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset);
+ break;
+ case ECPGt_decimal:
+ ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset);
+ break;
+ case ECPGt_numeric:
+
+ /*
+ * We align the numeric struct to allow it to store a pointer,
+ * while the digits array is aligned to int (which seems like
+ * overkill, but let's keep compatibility here).
+ *
+ * Unfortunately we need to deconstruct the value twice to
+ * find out the digits array's size and then later fill it.
+ */
+ ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset);
+ if (!PQgetisnull(res, row, i))
+ {
+ char *val = PQgetvalue(res, row, i);
+ numeric *num;
+
+ num = PGTYPESnumeric_from_asc(val, NULL);
+ if (!num)
+ break;
+ if (num->buf)
+ ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset);
+ PGTYPESnumeric_free(num);
+ }
+ break;
+ case ECPGt_date:
+ ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset);
+ break;
+ case ECPGt_timestamp:
+ ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset);
+ break;
+ case ECPGt_interval:
+ ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset);
+ break;
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ default:
+ {
+ long datalen = strlen(PQgetvalue(res, row, i)) + 1;
+
+ ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset);
+ break;
+ }
+ }
+ offset = next_offset;
+ }
+ return offset;
+}
+
+
+static long
+sqlda_compat_total_size(const PGresult *res, int row, enum COMPAT_MODE compat)
+{
+ long offset;
+
+ offset = sqlda_compat_empty_size(res);
+
+ if (row < 0)
+ return offset;
+
+ offset = sqlda_common_total_size(res, row, compat, offset);
+ return offset;
+}
+
+static long
+sqlda_native_empty_size(const PGresult *res)
+{
+ long offset;
+ int sqld = PQnfields(res);
+
+ /* Initial size to store main structure and field structures */
+ offset = sizeof(struct sqlda_struct) + (sqld - 1) * sizeof(struct sqlvar_struct);
+
+ /* Add padding to the first field value */
+ ecpg_sqlda_align_add_size(offset, sizeof(int), 0, &offset, NULL);
+
+ return offset;
+}
+
+static long
+sqlda_native_total_size(const PGresult *res, int row, enum COMPAT_MODE compat)
+{
+ long offset;
+
+ offset = sqlda_native_empty_size(res);
+
+ if (row < 0)
+ return offset;
+
+ offset = sqlda_common_total_size(res, row, compat, offset);
+ return offset;
+}
+
+/*
+ * Build "struct sqlda_compat" (metadata only) from PGresult
+ * leaving enough space for the field values in
+ * the given row number
+ */
+struct sqlda_compat *
+ecpg_build_compat_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compat)
+{
+ struct sqlda_compat *sqlda;
+ struct sqlvar_compat *sqlvar;
+ char *fname;
+ long size;
+ int sqld;
+ int i;
+
+ size = sqlda_compat_total_size(res, row, compat);
+ sqlda = (struct sqlda_compat *) ecpg_alloc(size, line);
+ if (!sqlda)
+ return NULL;
+
+ memset(sqlda, 0, size);
+ sqlvar = (struct sqlvar_compat *) (sqlda + 1);
+ sqld = PQnfields(res);
+ fname = (char *) (sqlvar + sqld);
+
+ sqlda->sqld = sqld;
+ ecpg_log("ecpg_build_compat_sqlda on line %d sqld = %d\n", line, sqld);
+ sqlda->desc_occ = size; /* cheat here, keep the full allocated size */
+ sqlda->sqlvar = sqlvar;
+
+ for (i = 0; i < sqlda->sqld; i++)
+ {
+ sqlda->sqlvar[i].sqltype = sqlda_dynamic_type(PQftype(res, i), compat);
+ strcpy(fname, PQfname(res, i));
+ sqlda->sqlvar[i].sqlname = fname;
+ fname += strlen(sqlda->sqlvar[i].sqlname) + 1;
+
+ /*
+ * this is reserved for future use, so we leave it empty for the time
+ * being
+ */
+ /* sqlda->sqlvar[i].sqlformat = (char *) (long) PQfformat(res, i); */
+ sqlda->sqlvar[i].sqlxid = PQftype(res, i);
+ sqlda->sqlvar[i].sqltypelen = PQfsize(res, i);
+ }
+
+ return sqlda;
+}
+
+/*
+ * Sets values from PGresult.
+ */
+static int16 value_is_null = -1;
+static int16 value_is_not_null = 0;
+
+void
+ecpg_set_compat_sqlda(int lineno, struct sqlda_compat **_sqlda, const PGresult *res, int row, enum COMPAT_MODE compat)
+{
+ struct sqlda_compat *sqlda = (*_sqlda);
+ int i;
+ long offset,
+ next_offset;
+
+ if (row < 0)
+ return;
+
+ /* Offset for the first field value */
+ offset = sqlda_compat_empty_size(res);
+
+ /*
+ * Set sqlvar[i]->sqldata pointers and convert values to correct format
+ */
+ for (i = 0; i < sqlda->sqld; i++)
+ {
+ int isnull;
+ int datalen;
+ bool set_data = true;
+
+ switch (sqlda->sqlvar[i].sqltype)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(short);
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(int);
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(long);
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(long long);
+ break;
+ case ECPGt_bool:
+ ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(bool);
+ break;
+ case ECPGt_float:
+ ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(float);
+ break;
+ case ECPGt_double:
+ ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(double);
+ break;
+ case ECPGt_decimal:
+ ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(decimal);
+ break;
+ case ECPGt_numeric:
+ {
+ numeric *num;
+ char *val;
+
+ set_data = false;
+
+ ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(numeric);
+
+ if (PQgetisnull(res, row, i))
+ {
+ ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
+ break;
+ }
+
+ val = PQgetvalue(res, row, i);
+ num = PGTYPESnumeric_from_asc(val, NULL);
+ if (!num)
+ {
+ ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
+ break;
+ }
+
+ memcpy(sqlda->sqlvar[i].sqldata, num, sizeof(numeric));
+
+ if (num->buf)
+ {
+ ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset);
+ memcpy((char *) sqlda + offset, num->buf, num->digits - num->buf + num->ndigits);
+
+ ((numeric *) sqlda->sqlvar[i].sqldata)->buf = (NumericDigit *) sqlda + offset;
+ ((numeric *) sqlda->sqlvar[i].sqldata)->digits = (NumericDigit *) sqlda + offset + (num->digits - num->buf);
+ }
+
+ PGTYPESnumeric_free(num);
+
+ break;
+ }
+ case ECPGt_date:
+ ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(date);
+ break;
+ case ECPGt_timestamp:
+ ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(timestamp);
+ break;
+ case ECPGt_interval:
+ ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(interval);
+ break;
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ default:
+ datalen = strlen(PQgetvalue(res, row, i)) + 1;
+ ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = datalen;
+ if (datalen > 32768)
+ sqlda->sqlvar[i].sqlilongdata = sqlda->sqlvar[i].sqldata;
+ break;
+ }
+
+ isnull = PQgetisnull(res, row, i);
+ ecpg_log("ecpg_set_compat_sqlda on line %d row %d col %d %s\n", lineno, row, i, isnull ? "IS NULL" : "IS NOT NULL");
+ sqlda->sqlvar[i].sqlind = isnull ? &value_is_null : &value_is_not_null;
+ sqlda->sqlvar[i].sqlitype = ECPGt_short;
+ sqlda->sqlvar[i].sqlilen = sizeof(short);
+ if (!isnull)
+ {
+ if (set_data)
+ ecpg_get_data(res, row, i, lineno,
+ sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR,
+ sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
+ ECPG_ARRAY_NONE, compat, false);
+ }
+ else
+ ECPGset_noind_null(sqlda->sqlvar[i].sqltype, sqlda->sqlvar[i].sqldata);
+
+ offset = next_offset;
+ }
+}
+
+struct sqlda_struct *
+ecpg_build_native_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compat)
+{
+ struct sqlda_struct *sqlda;
+ long size;
+ int i;
+
+ size = sqlda_native_total_size(res, row, compat);
+ sqlda = (struct sqlda_struct *) ecpg_alloc(size, line);
+ if (!sqlda)
+ return NULL;
+
+ memset(sqlda, 0, size);
+
+ sprintf(sqlda->sqldaid, "SQLDA ");
+ sqlda->sqld = sqlda->sqln = PQnfields(res);
+ ecpg_log("ecpg_build_native_sqlda on line %d sqld = %d\n", line, sqlda->sqld);
+ sqlda->sqldabc = sizeof(struct sqlda_struct) + (sqlda->sqld - 1) * sizeof(struct sqlvar_struct);
+
+ for (i = 0; i < sqlda->sqld; i++)
+ {
+ char *fname;
+
+ sqlda->sqlvar[i].sqltype = sqlda_dynamic_type(PQftype(res, i), compat);
+ fname = PQfname(res, i);
+ sqlda->sqlvar[i].sqlname.length = strlen(fname);
+ strcpy(sqlda->sqlvar[i].sqlname.data, fname);
+ }
+
+ return sqlda;
+}
+
+void
+ecpg_set_native_sqlda(int lineno, struct sqlda_struct **_sqlda, const PGresult *res, int row, enum COMPAT_MODE compat)
+{
+ struct sqlda_struct *sqlda = (*_sqlda);
+ int i;
+ long offset,
+ next_offset;
+
+ if (row < 0)
+ return;
+
+ /* Offset for the first field value */
+ offset = sqlda_native_empty_size(res);
+
+ /*
+ * Set sqlvar[i]->sqldata pointers and convert values to correct format
+ */
+ for (i = 0; i < sqlda->sqld; i++)
+ {
+ int isnull;
+ int datalen;
+ bool set_data = true;
+
+ switch (sqlda->sqlvar[i].sqltype)
+ {
+ case ECPGt_short:
+ case ECPGt_unsigned_short:
+ ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(short);
+ break;
+ case ECPGt_int:
+ case ECPGt_unsigned_int:
+ ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(int);
+ break;
+ case ECPGt_long:
+ case ECPGt_unsigned_long:
+ ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(long);
+ break;
+ case ECPGt_long_long:
+ case ECPGt_unsigned_long_long:
+ ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(long long);
+ break;
+ case ECPGt_bool:
+ ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(bool);
+ break;
+ case ECPGt_float:
+ ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(float);
+ break;
+ case ECPGt_double:
+ ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(double);
+ break;
+ case ECPGt_decimal:
+ ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(decimal);
+ break;
+ case ECPGt_numeric:
+ {
+ numeric *num;
+ char *val;
+
+ set_data = false;
+
+ ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(numeric);
+
+ if (PQgetisnull(res, row, i))
+ {
+ ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
+ break;
+ }
+
+ val = PQgetvalue(res, row, i);
+ num = PGTYPESnumeric_from_asc(val, NULL);
+ if (!num)
+ {
+ ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata);
+ break;
+ }
+
+ memcpy(sqlda->sqlvar[i].sqldata, num, sizeof(numeric));
+
+ if (num->buf)
+ {
+ ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset);
+ memcpy((char *) sqlda + offset, num->buf, num->digits - num->buf + num->ndigits);
+
+ ((numeric *) sqlda->sqlvar[i].sqldata)->buf = (NumericDigit *) sqlda + offset;
+ ((numeric *) sqlda->sqlvar[i].sqldata)->digits = (NumericDigit *) sqlda + offset + (num->digits - num->buf);
+ }
+
+ PGTYPESnumeric_free(num);
+
+ break;
+ }
+ case ECPGt_date:
+ ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(date);
+ break;
+ case ECPGt_timestamp:
+ ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(timestamp);
+ break;
+ case ECPGt_interval:
+ ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = sizeof(interval);
+ break;
+ case ECPGt_char:
+ case ECPGt_unsigned_char:
+ case ECPGt_string:
+ default:
+ datalen = strlen(PQgetvalue(res, row, i)) + 1;
+ ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset);
+ sqlda->sqlvar[i].sqldata = (char *) sqlda + offset;
+ sqlda->sqlvar[i].sqllen = datalen;
+ break;
+ }
+
+ isnull = PQgetisnull(res, row, i);
+ ecpg_log("ecpg_set_native_sqlda on line %d row %d col %d %s\n", lineno, row, i, isnull ? "IS NULL" : "IS NOT NULL");
+ sqlda->sqlvar[i].sqlind = isnull ? &value_is_null : &value_is_not_null;
+ if (!isnull)
+ {
+ if (set_data)
+ ecpg_get_data(res, row, i, lineno,
+ sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR,
+ sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0,
+ ECPG_ARRAY_NONE, compat, false);
+ }
+
+ offset = next_offset;
+ }
+}
diff --git a/src/interfaces/ecpg/ecpglib/typename.c b/src/interfaces/ecpg/ecpglib/typename.c
new file mode 100644
index 0000000..1d482c4
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/typename.c
@@ -0,0 +1,144 @@
+/* src/interfaces/ecpg/ecpglib/typename.c */
+
+#define POSTGRES_ECPG_INTERNAL
+#include "postgres_fe.h"
+
+#include "catalog/pg_type_d.h"
+#include "ecpglib.h"
+#include "ecpglib_extern.h"
+#include "ecpgtype.h"
+#include "sql3types.h"
+#include "sqltypes.h"
+
+/*
+ * This function is used to generate the correct type names.
+ */
+const char *
+ecpg_type_name(enum ECPGttype typ)
+{
+ switch (typ)
+ {
+ case ECPGt_char:
+ case ECPGt_string:
+ 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_bytea:
+ return "bytea";
+ case ECPGt_char_variable:
+ return "char";
+ case ECPGt_decimal:
+ return "decimal";
+ case ECPGt_numeric:
+ return "numeric";
+ case ECPGt_date:
+ return "date";
+ case ECPGt_timestamp:
+ return "timestamp";
+ case ECPGt_interval:
+ return "interval";
+ case ECPGt_const:
+ return "Const";
+ default:
+ abort();
+ }
+ return ""; /* keep MSC compiler happy */
+}
+
+int
+ecpg_dynamic_type(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 0;
+ }
+}
+
+int
+sqlda_dynamic_type(Oid type, enum COMPAT_MODE compat)
+{
+ switch (type)
+ {
+ case CHAROID:
+ case VARCHAROID:
+ case BPCHAROID:
+ case TEXTOID:
+ return ECPGt_char;
+ case INT2OID:
+ return ECPGt_short;
+ case INT4OID:
+ return ECPGt_int;
+ case FLOAT8OID:
+ return ECPGt_double;
+ case FLOAT4OID:
+ return ECPGt_float;
+ case NUMERICOID:
+ return INFORMIX_MODE(compat) ? ECPGt_decimal : ECPGt_numeric;
+ case DATEOID:
+ return ECPGt_date;
+ case TIMESTAMPOID:
+ case TIMESTAMPTZOID:
+ return ECPGt_timestamp;
+ case INTERVALOID:
+ return ECPGt_interval;
+ case INT8OID:
+#ifdef HAVE_LONG_LONG_INT_64
+ return ECPGt_long_long;
+#endif
+#ifdef HAVE_LONG_INT_64
+ return ECPGt_long;
+#endif
+ /* Unhandled types always return a string */
+ default:
+ return ECPGt_char;
+ }
+}