diff options
Diffstat (limited to 'src/interfaces/ecpg/ecpglib')
31 files changed, 11066 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..13c4beb --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/Makefile @@ -0,0 +1,70 @@ +#------------------------------------------------------------------------- +# +# Makefile for ecpg library +# +# Portions Copyright (c) 1996-2021, 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..056940c --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/connect.c @@ -0,0 +1,767 @@ +/* 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..6bc91ef --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/data.c @@ -0,0 +1,968 @@ +/* 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 == 0 || 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 + { + strncpy(str, pval, varcharsize); + + /* compatibility mode, null terminate char array */ + if (ORACLE_MODE(compat) && (varcharsize - 1) < size) + { + if (type == ECPGt_char || type == ECPGt_unsigned_char) + str[varcharsize - 1] = '\0'; + } + + if (varcharsize < size || (ORACLE_MODE(compat) && (varcharsize - 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..e8e8fb2 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -0,0 +1,2302 @@ +/* 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 *) ¤t_string[ntuples + 1]; + + for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) + { + int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + + if (!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..b8dbe5e --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/misc.c @@ -0,0 +1,594 @@ +/* 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..2f6b089 --- /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 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..7d236e2 --- /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) 14\n" +"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n" +"POT-Creation-Date: 2022-08-07 20:26+0000\n" +"PO-Revision-Date: 2019-06-06 17:20-0400\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:411 connect.c:676 +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:2190 execute.c:2197 +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..5f6c7d7 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/po/fr.po @@ -0,0 +1,209 @@ +# translation of ecpglib.po to fr_fr +# french message translation file for ecpglib +# +# Use these quotes: « %s » +# +# Guillaume Lelarge <guillaume@lelarge.info>, 2009. +# Stéphane Schildknecht <stephane.schildknecht@dalibo.com>, 2009. +msgid "" +msgstr "" +"Project-Id-Version: PostgreSQL 14\n" +"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n" +"POT-Creation-Date: 2019-09-20 12:38+0000\n" +"PO-Revision-Date: 2019-09-20 15:13+0200\n" +"Last-Translator: Guillaume Lelarge <guillaume@lelarge.info>\n" +"Language-Team: PostgreSQLfr <pgsql-fr-generale@postgresql.org>\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 2.2.3\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..895a46a --- /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 14)\n" +"Report-Msgid-Bugs-To: pgsql-bugs@lists.postgresql.org\n" +"POT-Creation-Date: 2021-08-20 15:39+0900\n" +"PO-Revision-Date: 2021-08-19 15:13+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.8.13\n" + +#: connect.c:237 +msgid "empty message text" +msgstr "空のメッセージテキスト" + +#: connect.c:405 connect.c:627 +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 "行番号%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: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/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..cc54e74 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/po/pt_BR.po @@ -0,0 +1,199 @@ +# Brazilian Portuguese message translation file for ecpglib +# Copyright (C) 2009 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 de Oliveira <euler@timbira.com>, 2010-2014. +# +msgid "" +msgstr "" +"Project-Id-Version: PostgreSQL 9.5\n" +"Report-Msgid-Bugs-To: pgsql-bugs@postgresql.org\n" +"POT-Creation-Date: 2015-09-17 22:32-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 <pgbr-dev@listas.postgresql.org.br>\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:237 +msgid "empty message text" +msgstr "mensagem vazia" + +#: connect.c:401 connect.c:430 connect.c:638 +msgid "<DEFAULT>" +msgstr "<PADRÃO>" + +#: 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 "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:254 +msgid "the connection to the server was lost" +msgstr "a conexão com servidor foi perdida" + +#: error.c:347 +#, c-format +msgid "SQL error: %s\n" +msgstr "Erro SQL: %s\n" + +#: execute.c:1972 +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..2133fff --- /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: 2021-09-13 07:55+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:237 +msgid "empty message text" +msgstr "пустое сообщение" + +#: connect.c:405 connect.c:634 +msgid "<DEFAULT>" +msgstr "<ПО_УМОЛЧАНИЮ>" + +#: 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 "неверный синтаксис для целого числа: \"%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: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 "<>" + +#~ 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..81526f9 --- /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-06-16 03:55+0000\n" +"PO-Revision-Date: 2022-06-19 10:10\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_14_STABLE/ecpglib.pot\n" +"X-Crowdin-File-ID: 746\n" + +#: connect.c:239 +msgid "empty message text" +msgstr "пусте повідомлення" + +#: connect.c:407 connect.c:636 +msgid "<DEFAULT>" +msgstr "<ЗА_ЗАМОВЧУВАННЯМ>" + +#: 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 "неприпустимий синтаксис для цілочисельного типу: \"%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:2196 execute.c:2203 +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; + } +} |