From 5a197810c09da06a772c235a6bba68290745d74c Mon Sep 17 00:00:00 2001 From: Michael Meskes Date: Tue, 7 Mar 2000 15:11:03 +0000 Subject: *** empty log message *** --- src/interfaces/ecpg/ChangeLog | 5 + src/interfaces/ecpg/include/ecpglib.h | 18 - src/interfaces/ecpg/include/ecpgtype.h | 27 - src/interfaces/ecpg/lib/Makefile.in | 5 +- src/interfaces/ecpg/lib/connect.c | 180 +++++ src/interfaces/ecpg/lib/data.c | 3 +- src/interfaces/ecpg/lib/descriptor.c | 3 +- src/interfaces/ecpg/lib/ecpglib.c | 1261 -------------------------------- src/interfaces/ecpg/lib/error.c | 12 +- src/interfaces/ecpg/lib/execute.c | 984 +++++++++++++++++++++++++ src/interfaces/ecpg/lib/extern.h | 43 ++ src/interfaces/ecpg/lib/memory.c | 2 + src/interfaces/ecpg/lib/misc.c | 124 ++++ src/interfaces/ecpg/lib/prepare.c | 2 + src/interfaces/ecpg/lib/typename.c | 1 + src/interfaces/ecpg/preproc/ecpg.c | 2 +- src/interfaces/ecpg/preproc/extern.h | 2 +- src/interfaces/ecpg/preproc/output.c | 8 +- src/interfaces/ecpg/preproc/preproc.y | 104 +-- src/interfaces/ecpg/preproc/variable.c | 1 - 20 files changed, 1409 insertions(+), 1378 deletions(-) create mode 100644 src/interfaces/ecpg/lib/connect.c delete mode 100644 src/interfaces/ecpg/lib/ecpglib.c create mode 100644 src/interfaces/ecpg/lib/execute.c create mode 100644 src/interfaces/ecpg/lib/extern.h create mode 100644 src/interfaces/ecpg/lib/misc.c (limited to 'src') diff --git a/src/interfaces/ecpg/ChangeLog b/src/interfaces/ecpg/ChangeLog index a7882c20a1e..41ddeadf34e 100644 --- a/src/interfaces/ecpg/ChangeLog +++ b/src/interfaces/ecpg/ChangeLog @@ -852,5 +852,10 @@ Thu Mar 2 17:42:16 CET 2000 Fri Mar 3 10:47:06 CET 2000 - Fixed handling of double quote in C code. + +Tue Mar 7 10:58:21 CET 2000 + + - More cleanup in ecpglib. + - Fixed ecpg.c not not free variable list twice. - Set library version to 3.1.0. - Set ecpg version to 2.7.0. diff --git a/src/interfaces/ecpg/include/ecpglib.h b/src/interfaces/ecpg/include/ecpglib.h index 1e527ce369e..03b09668ba9 100644 --- a/src/interfaces/ecpg/include/ecpglib.h +++ b/src/interfaces/ecpg/include/ecpglib.h @@ -24,22 +24,6 @@ extern "C" /* print an error message */ void sqlprint(void); -#ifdef LIBPQ_FE_H - bool ECPGsetdb(PGconn *); -#endif - -/* Here are some methods used by the lib. */ -/* Returns a pointer to a string containing a simple type name. */ - bool get_data(PGresult *, int, int, int, enum ECPGttype type, - enum ECPGttype, void *, void *, long, long, bool); - char *ecpg_alloc(long, int); - char *ecpg_strdup(const char *, int); - const char *ECPGtype_name(enum ECPGttype); - unsigned int ECPGDynamicType(Oid); - -/* and some vars */ - extern struct auto_mem *auto_allocs; - /* define this for simplicity as well as compatibility */ #define SQLCODE sqlca.sqlcode @@ -59,5 +43,3 @@ extern "C" } #endif - -#include diff --git a/src/interfaces/ecpg/include/ecpgtype.h b/src/interfaces/ecpg/include/ecpgtype.h index 69c0e77a4e7..40a944856d4 100644 --- a/src/interfaces/ecpg/include/ecpgtype.h +++ b/src/interfaces/ecpg/include/ecpgtype.h @@ -73,33 +73,6 @@ extern "C" #define IS_SIMPLE_TYPE(type) ((type) >= ECPGt_char && (type) <= ECPGt_varchar2) - /* A generic varchar type. */ - struct ECPGgeneric_varchar - { - int len; - char arr[1]; - }; - -/* keep a list of memory we allocated for the user */ - struct auto_mem - { - void *pointer; - struct auto_mem *next; - }; - -/* structure to store one statement */ - struct statement - { - int lineno; - char *command; - struct connection *connection; - struct variable *inlist; - struct variable *outlist; - }; - - -/* define this for simplicity as well as compatibility */ - #ifdef __cplusplus } diff --git a/src/interfaces/ecpg/lib/Makefile.in b/src/interfaces/ecpg/lib/Makefile.in index 1640d970633..a462030ff5b 100644 --- a/src/interfaces/ecpg/lib/Makefile.in +++ b/src/interfaces/ecpg/lib/Makefile.in @@ -6,7 +6,7 @@ # Copyright (c) 1994, Regents of the University of California # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.61 2000/02/25 11:11:15 meskes Exp $ +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/Makefile.in,v 1.62 2000/03/07 15:10:52 meskes Exp $ # #------------------------------------------------------------------------- @@ -23,7 +23,8 @@ ifdef KRBVERS CFLAGS+= $(KRBFLAGS) endif -OBJS= ecpglib.o typename.o descriptor.o data.o error.o prepare.o memory.o +OBJS= execute.o typename.o descriptor.o data.o error.o prepare.o memory.o \ + connect.o misc.o SHLIB_LINK= -L../../libpq -lpq diff --git a/src/interfaces/ecpg/lib/connect.c b/src/interfaces/ecpg/lib/connect.c new file mode 100644 index 00000000000..0a4d915c3e9 --- /dev/null +++ b/src/interfaces/ecpg/lib/connect.c @@ -0,0 +1,180 @@ +#include +#include +#include +#include "extern.h" +#include + +static struct connection *all_connections = NULL, *actual_connection = NULL; + +struct connection * +get_connection(const char *connection_name) +{ + struct connection *con = all_connections; + + if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0) + return actual_connection; + + for (; con && strcmp(connection_name, con->name) != 0; con = con->next); + if (con) + return con; + else + return NULL; +} + +static void +ecpg_finish(struct connection * act) +{ + if (act != NULL) + { + ECPGlog("ecpg_finish: finishing %s.\n", act->name); + PQfinish(act->connection); + + /* remove act from the list */ + if (act == all_connections) + all_connections = act->next; + else + { + struct connection *con; + + for (con = all_connections; con->next && con->next != act; con = con->next); + if (con->next) + con->next = act->next; + } + + if (actual_connection == act) + actual_connection = all_connections; + + free(act->name); + free(act); + } + else + ECPGlog("ecpg_finish: called an extra time.\n"); +} + +bool +ECPGsetcommit(int lineno, const char *mode, const char *connection_name) +{ + struct connection *con = get_connection(connection_name); + PGresult *results; + + if (!ecpg_init(con, connection_name, lineno)) + return(false); + + ECPGlog("ECPGsetcommit line %d action = %s connection = %s\n", lineno, mode, con->name); + + if (con->autocommit == true && strncmp(mode, "off", strlen("off")) == 0) + { + if (con->committed) + { + if ((results = PQexec(con->connection, "begin transaction")) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + con->committed = false; + } + con->autocommit = false; + } + else if (con->autocommit == false && strncmp(mode, "on", strlen("on")) == 0) + { + if (!con->committed) + { + if ((results = PQexec(con->connection, "commit")) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + con->committed = true; + } + con->autocommit = true; + } + + return true; +} + +bool +ECPGsetconn(int lineno, const char *connection_name) +{ + struct connection *con = get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return(false); + + actual_connection = con; + return true; +} + +bool +ECPGconnect(int lineno, const char *dbname, const char *user, const char *passwd, const char *connection_name, int autocommit) +{ + struct connection *this; + + init_sqlca(); + + if ((this = (struct connection *) ecpg_alloc(sizeof(struct connection), lineno)) == NULL) + return false; + + if (dbname == NULL && connection_name == NULL) + connection_name = "DEFAULT"; + + /* add connection to our list */ + if (connection_name != NULL) + this->name = ecpg_strdup(connection_name, lineno); + else + this->name = ecpg_strdup(dbname, lineno); + + if (all_connections == NULL) + this->next = NULL; + else + this->next = all_connections; + + actual_connection = all_connections = this; + + ECPGlog("ECPGconnect: opening database %s %s%s\n", dbname ? dbname : "", user ? "for user " : "", user ? user : ""); + + this->connection = PQsetdbLogin(NULL, NULL, NULL, NULL, dbname, user, passwd); + + if (PQstatus(this->connection) == CONNECTION_BAD) + { + ecpg_finish(this); + ECPGlog("connect: could not open database %s %s%s in line %d\n", dbname ? dbname : "", user ? "for user " : "", user ? user : "", lineno); + ECPGraise(lineno, ECPG_CONNECT, dbname ? dbname : ""); + return false; + } + + this->committed = true; + this->autocommit = autocommit; + + return true; +} + +bool +ECPGdisconnect(int lineno, const char *connection_name) +{ + struct connection *con; + + if (strcmp(connection_name, "ALL") == 0) + { + init_sqlca(); + for (con = all_connections; con;) + { + struct connection *f = con; + + con = con->next; + ecpg_finish(f); + } + } + else + { + con = get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return(false); + else + ecpg_finish(con); + } + + return true; +} diff --git a/src/interfaces/ecpg/lib/data.c b/src/interfaces/ecpg/lib/data.c index af3c78ce591..e9b01c385e9 100644 --- a/src/interfaces/ecpg/lib/data.c +++ b/src/interfaces/ecpg/lib/data.c @@ -1,8 +1,9 @@ #include -#include #include #include +#include +#include "extern.h" #include bool diff --git a/src/interfaces/ecpg/lib/descriptor.c b/src/interfaces/ecpg/lib/descriptor.c index a27915ce5c1..b1fe2b79d04 100644 --- a/src/interfaces/ecpg/lib/descriptor.c +++ b/src/interfaces/ecpg/lib/descriptor.c @@ -1,6 +1,7 @@ #include #include - +#include +#include "extern.h" #include struct descriptor diff --git a/src/interfaces/ecpg/lib/ecpglib.c b/src/interfaces/ecpg/lib/ecpglib.c deleted file mode 100644 index fddb36c3caa..00000000000 --- a/src/interfaces/ecpg/lib/ecpglib.c +++ /dev/null @@ -1,1261 +0,0 @@ -/* Copyright comment */ -/* - * The aim is to get a simpler inteface to the database routines. - * All the tidieous messing around with tuples is supposed to be hidden - * by this function. - */ -/* Author: Linus Tolke - (actually most if the code is "borrowed" from the distribution and just - slightly modified) - */ - -/* Taken over as part of PostgreSQL by Michael Meskes - on Feb. 5th, 1998 */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -/* variables visible to the programs */ -static struct sqlca sqlca_init = -{ - {'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '}, - sizeof(struct sqlca), - 0, - {0, {0}}, - {'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '}, - {0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0} -}; - -struct sqlca sqlca = -{ - {'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '}, - sizeof(struct sqlca), - 0, - {0, {0}}, - {'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '}, - {0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0, 0} -}; - -static struct connection -{ - char *name; - PGconn *connection; - bool committed; - int autocommit; - struct connection *next; -} *all_connections = NULL, *actual_connection = NULL; - -struct variable -{ - enum ECPGttype type; - void *value; - void *pointer; - long varcharsize; - long arrsize; - long offset; - enum ECPGttype ind_type; - void *ind_value; - long ind_varcharsize; - long ind_arrsize; - long ind_offset; - struct variable *next; -}; - -struct auto_mem *auto_allocs; - -static int simple_debug = 0; -static FILE *debugstream = NULL; - -static struct connection * -get_connection(const char *connection_name) -{ - struct connection *con = all_connections; - - if (connection_name == NULL || strcmp(connection_name, "CURRENT") == 0) - return actual_connection; - - for (; con && strcmp(connection_name, con->name) != 0; con = con->next); - if (con) - return con; - else - return NULL; -} - -static bool -ecpg_init(const struct connection *con, const char * connection_name, const int lineno) -{ - memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); - if (con == NULL) - { - ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL"); - return (false); - } - - auto_allocs = NULL; - - return (true); -} - -static void -ecpg_finish(struct connection * act) -{ - if (act != NULL) - { - ECPGlog("ecpg_finish: finishing %s.\n", act->name); - PQfinish(act->connection); - /* remove act from the list */ - if (act == all_connections) - all_connections = act->next; - else - { - struct connection *con; - - for (con = all_connections; con->next && con->next != act; con = con->next); - if (con->next) - con->next = act->next; - } - - if (actual_connection == act) - actual_connection = all_connections; - - free(act->name); - free(act); - } - else - ECPGlog("ecpg_finish: called an extra time.\n"); -} - -static void -add_mem(void *ptr, int lineno) -{ - struct auto_mem *am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), lineno); - - am->next = auto_allocs; - auto_allocs = am; -} - -/* This function returns a newly malloced string that has the \ - in the argument quoted with \ and the ' quote with ' as SQL92 says. - */ -static -char * -quote_postgres(char *arg, int lineno) -{ - char *res = (char *) ecpg_alloc(2 * strlen(arg) + 3, lineno); - int i, - ri = 0; - - if (!res) - return (res); - - res[ri++] = '\''; - for (i = 0; arg[i]; i++, ri++) - { - switch (arg[i]) - { - case '\'': - res[ri++] = '\''; - break; - case '\\': - res[ri++] = '\\'; - break; - default: - ; - } - - res[ri] = arg[i]; - } - res[ri++] = '\''; - res[ri] = '\0'; - - return res; -} - -/* - * create a list of variables - * The variables are listed with input variables preceeding outputvariables - * The end of each group is marked by an end marker. - * per variable we list: - * type - as defined in ecpgtype.h - * value - where to store the data - * varcharsize - length of string in case we have a stringvariable, else 0 - * arraysize - 0 for pointer (we don't know the size of the array), - * 1 for simple variable, size for arrays - * offset - offset between ith and (i+1)th entry in an array, - * normally that means sizeof(type) - * ind_type - type of indicator variable - * ind_value - pointer to indicator variable - * ind_varcharsize - empty - * ind_arraysize - arraysize of indicator array - * ind_offset - indicator offset - */ -static bool -create_statement(int lineno, struct connection * connection, struct statement ** stmt, char *query, va_list ap) -{ - struct variable **list = &((*stmt)->inlist); - enum ECPGttype type; - - if (!(*stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno))) - return false; - - (*stmt)->command = query; - (*stmt)->connection = connection; - (*stmt)->lineno = lineno; - - list = &((*stmt)->inlist); - - type = va_arg(ap, enum ECPGttype); - - while (type != ECPGt_EORT) - { - if (type == ECPGt_EOIT) - list = &((*stmt)->outlist); - else - { - struct variable *var, - *ptr; - - if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno))) - return false; - - var->type = type; - var->pointer = va_arg(ap, void *); - - /* if variable is NULL, the statement hasn't been prepared */ - if (var->pointer == NULL) - { - ECPGlog("create_statement: invalid statement name\n"); - ECPGraise(lineno, ECPG_INVALID_STMT, NULL); - free(var); - return false; - } - - var->varcharsize = va_arg(ap, long); - var->arrsize = va_arg(ap, long); - var->offset = va_arg(ap, long); - - if (var->arrsize == 0 || var->varcharsize == 0) - var->value = *((void **) (var->pointer)); - else - var->value = var->pointer; - - var->ind_type = va_arg(ap, enum ECPGttype); - var->ind_value = va_arg(ap, void *); - var->ind_varcharsize = va_arg(ap, long); - var->ind_arrsize = va_arg(ap, long); - var->ind_offset = va_arg(ap, long); - var->next = NULL; - - for (ptr = *list; ptr && ptr->next; ptr = ptr->next); - - if (ptr == NULL) - *list = var; - else - ptr->next = var; - } - - type = va_arg(ap, enum ECPGttype); - } - - return (true); -} - -static void -free_variable(struct variable * var) -{ - struct variable *var_next; - - if (var == (struct variable *) NULL) - return; - var_next = var->next; - free(var); - - while (var_next) - { - var = var_next; - var_next = var->next; - free(var); - } -} - -static void -free_statement(struct statement * stmt) -{ - if (stmt == (struct statement *) NULL) - return; - free_variable(stmt->inlist); - free_variable(stmt->outlist); - free(stmt); -} - -static char * -next_insert(char *text) -{ - char *ptr = text; - bool string = false; - - for (; *ptr != '\0' && (*ptr != '?' || string); ptr++) - if (*ptr == '\'' && *(ptr-1) != '\\') - string = string ? false : true; - - return (*ptr == '\0') ? NULL : ptr; -} - -static bool -ECPGexecute(struct statement * stmt) -{ - bool status = false; - char *copiedquery; - PGresult *results, *query; - PGnotify *notify; - struct variable *var; - - copiedquery = ecpg_strdup(stmt->command, stmt->lineno); - - /* - * Now, if the type is one of the fill in types then we take the - * argument and enter that in the string at the first %s position. - * Then if there are any more fill in types we fill in at the next and - * so on. - */ - var = stmt->inlist; - while (var) - { - char *newcopy; - char *mallocedval = NULL; - char *tobeinserted = NULL; - char *p; - char buff[20]; - - /* - * Some special treatment is needed for records since we want - * their contents to arrive in a comma-separated list on insert (I - * think). - */ - - buff[0] = '\0'; - - /* 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) - strcpy(buff, "null"); - break; - case ECPGt_int: - case ECPGt_unsigned_int: - if (*(int *) var->ind_value < 0) - strcpy(buff, "null"); - break; - case ECPGt_long: - case ECPGt_unsigned_long: - if (*(long *) var->ind_value < 0L) - strcpy(buff, "null"); - break; - default: - break; - } - - if (*buff == '\0') - { - switch (var->type) - { - int element; - - case ECPGt_short: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%d,", ((short *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_int: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_unsigned_short: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%d,", ((unsigned short *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_unsigned_int: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%d,", ((unsigned int *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_long: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%ld,", ((long *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_unsigned_long: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%ld,", ((unsigned long *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_float: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((float *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_double: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((double *) var->value)[element]); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_bool: - if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) - return false; - - sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); - - for (element = 0; element < var->arrsize; element++) - sprintf(mallocedval + strlen(mallocedval), "%c,", (((char *) var->value)[element]) ? 't' : 'f'); - - sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); - - tobeinserted = mallocedval; - break; - - case ECPGt_char: - case ECPGt_unsigned_char: - { - /* set slen to string length if type is char * */ - int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize; - - if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno))) - return false; - - strncpy(newcopy, (char *) var->value, slen); - newcopy[slen] = '\0'; - - mallocedval = quote_postgres(newcopy, stmt->lineno); - if (!mallocedval) - return false; - - free(newcopy); - - tobeinserted = mallocedval; - } - break; - case ECPGt_char_variable: - { - int slen = strlen((char *) var->value); - - if (!(mallocedval = ecpg_alloc(slen + 1, stmt->lineno))) - return false; - - strncpy(mallocedval, (char *) var->value, slen); - mallocedval[slen] = '\0'; - - tobeinserted = mallocedval; - } - break; - case ECPGt_varchar: - { - struct ECPGgeneric_varchar *variable = - (struct ECPGgeneric_varchar *) (var->value); - - if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, stmt->lineno))) - return false; - - strncpy(newcopy, variable->arr, variable->len); - newcopy[variable->len] = '\0'; - - mallocedval = quote_postgres(newcopy, stmt->lineno); - if (!mallocedval) - return false; - - free(newcopy); - - tobeinserted = mallocedval; - } - break; - - default: - /* Not implemented yet */ - ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, ECPGtype_name(var->type)); - return false; - break; - } - } - else - tobeinserted = buff; - - /* - * Now tobeinserted points to an area that is to be inserted at - * the first %s - */ - if (!(newcopy = (char *) ecpg_alloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno))) - return false; - - strcpy(newcopy, copiedquery); - if ((p = next_insert(newcopy)) == NULL) - { - - /* - * We have an argument but we dont have the matched up string - * in the string - */ - ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL); - return false; - } - else - { - strcpy(p, tobeinserted); - - /* - * The strange thing in the second argument is the rest of the - * string from the old string - */ - strcat(newcopy, - copiedquery - + (p - newcopy) - + sizeof("?") - 1 /* don't count the '\0' */ ); - } - - /* - * Now everything is safely copied to the newcopy. Lets free the - * oldcopy and let the copiedquery get the var->value from the - * newcopy. - */ - if (mallocedval != NULL) - { - free(mallocedval); - mallocedval = NULL; - } - - free(copiedquery); - copiedquery = newcopy; - - var = var->next; - } - - /* Check if there are unmatched things left. */ - if (next_insert(copiedquery) != NULL) - { - ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL); - return false; - } - - /* Now the request is built. */ - - if (stmt->connection->committed && !stmt->connection->autocommit) - { - if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) - { - ECPGraise(stmt->lineno, ECPG_TRANS, NULL); - return false; - } - PQclear(results); - stmt->connection->committed = false; - } - - ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name); - results = PQexec(stmt->connection->connection, copiedquery); - free(copiedquery); - - if (results == NULL) - { - ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, - PQerrorMessage(stmt->connection->connection)); - ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); - } - else - { - var = stmt->outlist; - switch (PQresultStatus(results)) - { - int nfields, - ntuples, - act_tuple, - act_field, - isarray; - - case PGRES_TUPLES_OK: - nfields = PQnfields(results); - sqlca.sqlerrd[2] = ntuples = PQntuples(results); - status = true; - - if (ntuples < 1) - { - ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", - stmt->lineno, ntuples); - ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL); - status = false; - break; - } - - for (act_field = 0; act_field < nfields && status; act_field++) - { - char *array_query; - - if (var == NULL) - { - ECPGlog("ECPGexecute line %d: Too few arguments.\n", stmt->lineno); - ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL); - return (false); - } - - array_query = (char *)ecpg_alloc(strlen("select typelem from pg_type where oid=") + 11, stmt -> lineno); - sprintf(array_query, "select typelem from pg_type where oid=%d", PQftype(results, act_field)); - query = PQexec(stmt->connection->connection, array_query); - isarray = 0; - if (PQresultStatus(query) == PGRES_TUPLES_OK) { - isarray = atol((char *)PQgetvalue(query, 0, 0)); - if (ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER || - ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER_VARYING) - { - /* arrays of character strings are not yet implemented */ - isarray = false; - } - ECPGlog("ECPGexecute line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, PQftype(results, act_field), var->type, isarray ? "yes" : "no"); - } - PQclear(query); - - if (!isarray) - { - /* - * if we don't have enough space, we cannot read all - * tuples - */ - if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize)) - { - ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n", - stmt->lineno, ntuples, var->arrsize); - ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL); - status = false; - break; - } - } - else - { - /* - * since we read an array, the variable has to be - * an array too - */ - if (var->arrsize == 0) - { - ECPGlog("ECPGexecute line %d: variable is not an array\n"); - ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL); - status = false; - break; - } - } - - /* - * allocate memory for NULL pointers - */ - if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL) - { - int len = 0; - - switch (var->type) - { - case ECPGt_char: - case ECPGt_unsigned_char: - var->varcharsize = 0; - /* check strlen for each tuple */ - for (act_tuple = 0; act_tuple < ntuples; act_tuple++) - { - int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; - - if (len > var->varcharsize) - var->varcharsize = len; - } - var->offset *= var->varcharsize; - len = var->offset * ntuples; - break; - case ECPGt_varchar: - len = ntuples * (var->varcharsize + sizeof(int)); - break; - default: - len = var->offset * ntuples; - break; - } - var->value = (void *) ecpg_alloc(len, stmt->lineno); - *((void **) var->pointer) = var->value; - add_mem(var->value, stmt->lineno); - } - - for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) - { - if (!get_data(results, act_tuple, act_field, stmt->lineno, - var->type, var->ind_type, var->value, - var->ind_value, var->varcharsize, var->offset, isarray)) - status = false; - } - var = var->next; - } - - if (status && var != NULL) - { - ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL); - status = false; - } - - break; - case PGRES_EMPTY_QUERY: - /* do nothing */ - ECPGraise(stmt->lineno, ECPG_EMPTY, NULL); - break; - case PGRES_COMMAND_OK: - status = true; - sqlca.sqlerrd[1] = atol(PQoidStatus(results)); - sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); - ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, PQcmdStatus(results)); - break; - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - case PGRES_BAD_RESPONSE: - ECPGlog("ECPGexecute line %d: Error: %s", - stmt->lineno, PQerrorMessage(stmt->connection->connection)); - ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); - status = false; - break; - case PGRES_COPY_OUT: - ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); - PQendcopy(stmt->connection->connection); - break; - case PGRES_COPY_IN: - ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); - PQendcopy(stmt->connection->connection); - break; - default: - ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", - stmt->lineno); - ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); - status = false; - break; - } - PQclear(results); - } - - /* check for asynchronous returns */ - notify = PQnotifies(stmt->connection->connection); - if (notify) - { - ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", - stmt->lineno, notify->relname, notify->be_pid); - free(notify); - } - - return status; -} - -bool -ECPGdo(int lineno, const char *connection_name, char *query, ...) -{ - va_list args; - struct statement *stmt; - struct connection *con = get_connection(connection_name); - bool status=true; - char *locale = setlocale(LC_NUMERIC, NULL); - - /* Make sure we do NOT honor the locale for numeric input/output */ - /* since the database wants teh standard decimal point */ - setlocale(LC_NUMERIC, "C"); - - if (!ecpg_init(con, connection_name, lineno)) - { - setlocale(LC_NUMERIC, locale); - return(false); - } - - va_start(args, query); - if (create_statement(lineno, con, &stmt, query, args) == false) - { - setlocale(LC_NUMERIC, locale); - return (false); - } - va_end(args); - - /* are we connected? */ - if (con == NULL || con->connection == NULL) - { - free_statement(stmt); - ECPGlog("ECPGdo: not connected to %s\n", con->name); - ECPGraise(lineno, ECPG_NOT_CONN, NULL); - setlocale(LC_NUMERIC, locale); - return false; - } - - status = ECPGexecute(stmt); - free_statement(stmt); - - /* and reset locale value so our application is not affected */ - setlocale(LC_NUMERIC, locale); - return (status); -} - -bool -ECPGstatus(int lineno, const char *connection_name) -{ - struct connection *con = get_connection(connection_name); - - if (!ecpg_init(con, connection_name, lineno)) - return(false); - - /* are we connected? */ - if (con->connection == NULL) - { - ECPGlog("ECPGdo: not connected to %s\n", con->name); - ECPGraise(lineno, ECPG_NOT_CONN, NULL); - return false; - } - - return (true); -} - -bool -ECPGtrans(int lineno, const char *connection_name, const char *transaction) -{ - PGresult *res; - struct connection *con = get_connection(connection_name); - - if (!ecpg_init(con, connection_name, lineno)) - return(false); - - ECPGlog("ECPGtrans line %d action = %s connection = %s\n", lineno, transaction, con->name); - - /* if we have no connection we just simulate the command */ - if (con && con->connection) - { - if ((res = PQexec(con->connection, transaction)) == NULL) - { - ECPGraise(lineno, ECPG_TRANS, NULL); - return FALSE; - } - PQclear(res); - } - - if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0) - { - con->committed = true; - - /* deallocate all prepared statements */ - if (!ECPGdeallocate_all(lineno)) - return false; - } - - return true; -} - -bool -ECPGsetcommit(int lineno, const char *mode, const char *connection_name) -{ - struct connection *con = get_connection(connection_name); - PGresult *results; - - if (!ecpg_init(con, connection_name, lineno)) - return(false); - - ECPGlog("ECPGsetcommit line %d action = %s connection = %s\n", lineno, mode, con->name); - - if (con->autocommit == true && strncmp(mode, "off", strlen("off")) == 0) - { - if (con->committed) - { - if ((results = PQexec(con->connection, "begin transaction")) == NULL) - { - ECPGraise(lineno, ECPG_TRANS, NULL); - return false; - } - PQclear(results); - con->committed = false; - } - con->autocommit = false; - } - else if (con->autocommit == false && strncmp(mode, "on", strlen("on")) == 0) - { - if (!con->committed) - { - if ((results = PQexec(con->connection, "commit")) == NULL) - { - ECPGraise(lineno, ECPG_TRANS, NULL); - return false; - } - PQclear(results); - con->committed = true; - } - con->autocommit = true; - } - - return true; -} - -bool -ECPGsetconn(int lineno, const char *connection_name) -{ - struct connection *con = get_connection(connection_name); - - if (!ecpg_init(con, connection_name, lineno)) - return(false); - - actual_connection = con; - return true; -} - -bool -ECPGconnect(int lineno, const char *dbname, const char *user, const char *passwd, const char *connection_name, int autocommit) -{ - struct connection *this; - - - memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); - - if ((this = (struct connection *) ecpg_alloc(sizeof(struct connection), lineno)) == NULL) - return false; - - if (dbname == NULL && connection_name == NULL) - connection_name = "DEFAULT"; - - /* add connection to our list */ - if (connection_name != NULL) - this->name = ecpg_strdup(connection_name, lineno); - else - this->name = ecpg_strdup(dbname, lineno); - - if (all_connections == NULL) - this->next = NULL; - else - this->next = all_connections; - - actual_connection = all_connections = this; - - ECPGlog("ECPGconnect: opening database %s %s%s\n", dbname ? dbname : "", user ? "for user " : "", user ? user : ""); - - this->connection = PQsetdbLogin(NULL, NULL, NULL, NULL, dbname, user, passwd); - - if (PQstatus(this->connection) == CONNECTION_BAD) - { - ecpg_finish(this); - ECPGlog("connect: could not open database %s %s%s in line %d\n", dbname ? dbname : "", user ? "for user " : "", user ? user : "", lineno); - ECPGraise(lineno, ECPG_CONNECT, dbname ? dbname : ""); - return false; - } - - this->committed = true; - this->autocommit = autocommit; - - return true; -} - -bool -ECPGdisconnect(int lineno, const char *connection_name) -{ - struct connection *con; - - if (strcmp(connection_name, "ALL") == 0) - { - memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); - for (con = all_connections; con;) - { - struct connection *f = con; - - con = con->next; - ecpg_finish(f); - } - } - else - { - con = get_connection(connection_name); - - if (!ecpg_init(con, connection_name, lineno)) - return(false); - else - ecpg_finish(con); - } - - return true; -} - -void -ECPGdebug(int n, FILE *dbgs) -{ - simple_debug = n; - debugstream = dbgs; - ECPGlog("ECPGdebug: set to %d\n", simple_debug); -} - -void -ECPGlog(const char *format,...) -{ - va_list ap; - - if (simple_debug) - { - char *f = (char *) malloc(strlen(format) + 100); - - if (!f) - return; - - sprintf(f, "[%d]: %s", (int) getpid(), format); - - va_start(ap, format); - vfprintf(debugstream, f, ap); - va_end(ap); - - free(f); - } -} - -/* dynamic SQL support routines - * - * Copyright (c) 2000, Christof Petig - * - * $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/ecpglib.c,v 1.62 2000/03/03 14:39:26 meskes Exp $ - */ - -PGconn *ECPG_internal_get_connection(char *name); - -extern struct descriptor -{ - char *name; - PGresult *result; - struct descriptor *next; -} *all_descriptors; - -// like ECPGexecute -static bool execute_descriptor(int lineno,const char *query - ,struct connection *con,PGresult **resultptr) -{ - bool status = false; - PGresult *results; - PGnotify *notify; - - /* Now the request is built. */ - - if (con->committed && !con->autocommit) - { - if ((results = PQexec(con->connection, "begin transaction")) == NULL) - { - ECPGraise(lineno, ECPG_TRANS, NULL); - return false; - } - PQclear(results); - con->committed = false; - } - - ECPGlog("execute_descriptor line %d: QUERY: %s on connection %s\n", lineno, query, con->name); - results = PQexec(con->connection, query); - - if (results == NULL) - { - ECPGlog("ECPGexecute line %d: error: %s", lineno, - PQerrorMessage(con->connection)); - ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection)); - } - else - { *resultptr=results; - switch (PQresultStatus(results)) - { int ntuples; - case PGRES_TUPLES_OK: - status = true; - sqlca.sqlerrd[2] = ntuples = PQntuples(results); - if (ntuples < 1) - { - ECPGlog("execute_descriptor line %d: Incorrect number of matches: %d\n", - lineno, ntuples); - ECPGraise(lineno, ECPG_NOT_FOUND, NULL); - status = false; - break; - } - break; -#if 1 /* strictly these are not needed (yet) */ - case PGRES_EMPTY_QUERY: - /* do nothing */ - ECPGraise(lineno, ECPG_EMPTY, NULL); - break; - case PGRES_COMMAND_OK: - status = true; - sqlca.sqlerrd[1] = atol(PQoidStatus(results)); - sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); - ECPGlog("ECPGexecute line %d Ok: %s\n", lineno, PQcmdStatus(results)); - break; - case PGRES_COPY_OUT: - ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", lineno); - PQendcopy(con->connection); - break; - case PGRES_COPY_IN: - ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", lineno); - PQendcopy(con->connection); - break; -#else - case PGRES_EMPTY_QUERY: - case PGRES_COMMAND_OK: - case PGRES_COPY_OUT: - case PGRES_COPY_IN: - break; -#endif - case PGRES_NONFATAL_ERROR: - case PGRES_FATAL_ERROR: - case PGRES_BAD_RESPONSE: - ECPGlog("ECPGexecute line %d: Error: %s", - lineno, PQerrorMessage(con->connection)); - ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection)); - status = false; - break; - default: - ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", - lineno); - ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection)); - status = false; - break; - } - } - - /* check for asynchronous returns */ - notify = PQnotifies(con->connection); - if (notify) - { - ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", - lineno, notify->relname, notify->be_pid); - free(notify); - } - return status; -} - -/* like ECPGdo */ -static bool do_descriptor2(int lineno,const char *connection_name, - PGresult **resultptr, const char *query) -{ - struct connection *con = get_connection(connection_name); - bool status=true; - char *locale = setlocale(LC_NUMERIC, NULL); - - /* Make sure we do NOT honor the locale for numeric input/output */ - /* since the database wants teh standard decimal point */ - setlocale(LC_NUMERIC, "C"); - - if (!ecpg_init(con, connection_name, lineno)) - { setlocale(LC_NUMERIC, locale); - return(false); - } - - /* are we connected? */ - if (con == NULL || con->connection == NULL) - { - ECPGlog("do_descriptor2: not connected to %s\n", con->name); - ECPGraise(lineno, ECPG_NOT_CONN, NULL); - setlocale(LC_NUMERIC, locale); - return false; - } - - status = execute_descriptor(lineno,query,con,resultptr); - - /* and reset locale value so our application is not affected */ - setlocale(LC_NUMERIC, locale); - return (status); -} - -bool ECPGdo_descriptor(int line,const char *connection, - const char *descriptor,const char *query) -{ - struct descriptor *i; - for (i=all_descriptors;i!=NULL;i=i->next) - { if (!strcmp(descriptor,i->name)) - { - bool status; - - /* free previous result */ - if (i->result) PQclear(i->result); - i->result=NULL; - - status=do_descriptor2(line,connection,&i->result,query); - - if (!i->result) PQmakeEmptyPGresult(NULL, 0); - return (status); - } - } - - ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, descriptor); - return false; -} diff --git a/src/interfaces/ecpg/lib/error.c b/src/interfaces/ecpg/lib/error.c index 54e48a55271..5aa4b771566 100644 --- a/src/interfaces/ecpg/lib/error.c +++ b/src/interfaces/ecpg/lib/error.c @@ -3,13 +3,12 @@ #include #include #include +#include "extern.h" #include void ECPGraise(int line, int code, const char *str) { - struct auto_mem *am; - sqlca.sqlcode = code; switch (code) { @@ -142,14 +141,7 @@ ECPGraise(int line, int code, const char *str) sqlca.sqlerrm.sqlerrml = strlen(sqlca.sqlerrm.sqlerrmc); /* free all memory we have allocated for the user */ - for (am = auto_allocs; am;) - { - struct auto_mem *act = am; - - am = am->next; - free(act->pointer); - free(act); - } + free_auto_mem(); } /* print out an error message */ diff --git a/src/interfaces/ecpg/lib/execute.c b/src/interfaces/ecpg/lib/execute.c new file mode 100644 index 00000000000..adc1351e4e7 --- /dev/null +++ b/src/interfaces/ecpg/lib/execute.c @@ -0,0 +1,984 @@ +/* Copyright comment */ +/* + * The aim is to get a simpler inteface to the database routines. + * All the tidieous messing around with tuples is supposed to be hidden + * by this function. + */ +/* Author: Linus Tolke + (actually most if the code is "borrowed" from the distribution and just + slightly modified) + */ + +/* Taken over as part of PostgreSQL by Michael Meskes + on Feb. 5th, 1998 */ + +#include +#include + +#include +#include +#include +#include "extern.h" +#include +#include + +/* variables visible to the programs */ +struct sqlca sqlca = +{ + {'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '}, + sizeof(struct sqlca), + 0, + {0, {0}}, + {'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +struct variable +{ + enum ECPGttype type; + void *value; + void *pointer; + long varcharsize; + long arrsize; + long offset; + enum ECPGttype ind_type; + void *ind_value; + long ind_varcharsize; + long ind_arrsize; + long ind_offset; + struct variable *next; +}; + +/* keep a list of memory we allocated for the user */ +static struct auto_mem +{ + void *pointer; + struct auto_mem *next; +} *auto_allocs = NULL; + +static void +add_mem(void *ptr, int lineno) +{ + struct auto_mem *am = (struct auto_mem *) ecpg_alloc(sizeof(struct auto_mem), lineno); + + am->next = auto_allocs; + auto_allocs = am; +} + +void free_auto_mem(void) +{ + struct auto_mem *am; + + /* free all memory we have allocated for the user */ + for (am = auto_allocs; am;) + { + struct auto_mem *act = am; + + am = am->next; + free(act->pointer); + free(act); + } + + auto_allocs = NULL; +} + +/* This function returns a newly malloced string that has the \ + in the argument quoted with \ and the ' quote with ' as SQL92 says. + */ +static +char * +quote_postgres(char *arg, int lineno) +{ + char *res = (char *) ecpg_alloc(2 * strlen(arg) + 3, lineno); + int i, + ri = 0; + + if (!res) + return (res); + + res[ri++] = '\''; + for (i = 0; arg[i]; i++, ri++) + { + switch (arg[i]) + { + case '\'': + res[ri++] = '\''; + break; + case '\\': + res[ri++] = '\\'; + break; + default: + ; + } + + res[ri] = arg[i]; + } + res[ri++] = '\''; + res[ri] = '\0'; + + return res; +} + +/* + * create a list of variables + * The variables are listed with input variables preceeding outputvariables + * The end of each group is marked by an end marker. + * per variable we list: + * type - as defined in ecpgtype.h + * value - where to store the data + * varcharsize - length of string in case we have a stringvariable, else 0 + * arraysize - 0 for pointer (we don't know the size of the array), + * 1 for simple variable, size for arrays + * offset - offset between ith and (i+1)th entry in an array, + * normally that means sizeof(type) + * ind_type - type of indicator variable + * ind_value - pointer to indicator variable + * ind_varcharsize - empty + * ind_arraysize - arraysize of indicator array + * ind_offset - indicator offset + */ +static bool +create_statement(int lineno, struct connection * connection, struct statement ** stmt, char *query, va_list ap) +{ + struct variable **list = &((*stmt)->inlist); + enum ECPGttype type; + + if (!(*stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno))) + return false; + + (*stmt)->command = query; + (*stmt)->connection = connection; + (*stmt)->lineno = lineno; + + list = &((*stmt)->inlist); + + type = va_arg(ap, enum ECPGttype); + + while (type != ECPGt_EORT) + { + if (type == ECPGt_EOIT) + list = &((*stmt)->outlist); + else + { + struct variable *var, + *ptr; + + if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno))) + return false; + + var->type = type; + var->pointer = va_arg(ap, void *); + + /* if variable is NULL, the statement hasn't been prepared */ + if (var->pointer == NULL) + { + ECPGlog("create_statement: invalid statement name\n"); + ECPGraise(lineno, ECPG_INVALID_STMT, NULL); + free(var); + return false; + } + + var->varcharsize = va_arg(ap, long); + var->arrsize = va_arg(ap, long); + var->offset = va_arg(ap, long); + + if (var->arrsize == 0 || var->varcharsize == 0) + var->value = *((void **) (var->pointer)); + else + var->value = var->pointer; + + var->ind_type = va_arg(ap, enum ECPGttype); + var->ind_value = va_arg(ap, void *); + var->ind_varcharsize = va_arg(ap, long); + var->ind_arrsize = va_arg(ap, long); + var->ind_offset = va_arg(ap, long); + var->next = NULL; + + for (ptr = *list; ptr && ptr->next; ptr = ptr->next); + + if (ptr == NULL) + *list = var; + else + ptr->next = var; + } + + type = va_arg(ap, enum ECPGttype); + } + + return (true); +} + +static void +free_variable(struct variable * var) +{ + struct variable *var_next; + + if (var == (struct variable *) NULL) + return; + var_next = var->next; + free(var); + + while (var_next) + { + var = var_next; + var_next = var->next; + free(var); + } +} + +static void +free_statement(struct statement * stmt) +{ + if (stmt == (struct statement *) NULL) + return; + free_variable(stmt->inlist); + free_variable(stmt->outlist); + free(stmt); +} + +static char * +next_insert(char *text) +{ + char *ptr = text; + bool string = false; + + for (; *ptr != '\0' && (*ptr != '?' || string); ptr++) + if (*ptr == '\'' && *(ptr-1) != '\\') + string = string ? false : true; + + return (*ptr == '\0') ? NULL : ptr; +} + +static bool +ECPGexecute(struct statement * stmt) +{ + bool status = false; + char *copiedquery; + PGresult *results, *query; + PGnotify *notify; + struct variable *var; + + copiedquery = ecpg_strdup(stmt->command, stmt->lineno); + + /* + * Now, if the type is one of the fill in types then we take the + * argument and enter that in the string at the first %s position. + * Then if there are any more fill in types we fill in at the next and + * so on. + */ + var = stmt->inlist; + while (var) + { + char *newcopy; + char *mallocedval = NULL; + char *tobeinserted = NULL; + char *p; + char buff[20]; + + /* + * Some special treatment is needed for records since we want + * their contents to arrive in a comma-separated list on insert (I + * think). + */ + + buff[0] = '\0'; + + /* 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) + strcpy(buff, "null"); + break; + case ECPGt_int: + case ECPGt_unsigned_int: + if (*(int *) var->ind_value < 0) + strcpy(buff, "null"); + break; + case ECPGt_long: + case ECPGt_unsigned_long: + if (*(long *) var->ind_value < 0L) + strcpy(buff, "null"); + break; + default: + break; + } + + if (*buff == '\0') + { + switch (var->type) + { + int element; + + case ECPGt_short: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%d,", ((short *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_int: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_unsigned_short: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%d,", ((unsigned short *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_unsigned_int: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%d,", ((unsigned int *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_long: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%ld,", ((long *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_unsigned_long: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%ld,", ((unsigned long *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_float: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((float *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_double: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((double *) var->value)[element]); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_bool: + if (!(mallocedval = ecpg_alloc(var->arrsize * 20, stmt->lineno))) + return false; + + sprintf(mallocedval, "%s", (var->arrsize > 1) ? "'{" : ""); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%c,", (((char *) var->value)[element]) ? 't' : 'f'); + + sprintf(mallocedval + strlen(mallocedval) - 1, "%s", (var->arrsize > 1) ? "}'" : ""); + + tobeinserted = mallocedval; + break; + + case ECPGt_char: + case ECPGt_unsigned_char: + { + /* set slen to string length if type is char * */ + int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize; + + if (!(newcopy = ecpg_alloc(slen + 1, stmt->lineno))) + return false; + + strncpy(newcopy, (char *) var->value, slen); + newcopy[slen] = '\0'; + + mallocedval = quote_postgres(newcopy, stmt->lineno); + if (!mallocedval) + return false; + + free(newcopy); + + tobeinserted = mallocedval; + } + break; + case ECPGt_char_variable: + { + int slen = strlen((char *) var->value); + + if (!(mallocedval = ecpg_alloc(slen + 1, stmt->lineno))) + return false; + + strncpy(mallocedval, (char *) var->value, slen); + mallocedval[slen] = '\0'; + + tobeinserted = mallocedval; + } + break; + case ECPGt_varchar: + { + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) (var->value); + + if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, stmt->lineno))) + return false; + + strncpy(newcopy, variable->arr, variable->len); + newcopy[variable->len] = '\0'; + + mallocedval = quote_postgres(newcopy, stmt->lineno); + if (!mallocedval) + return false; + + free(newcopy); + + tobeinserted = mallocedval; + } + break; + + default: + /* Not implemented yet */ + ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, ECPGtype_name(var->type)); + return false; + break; + } + } + else + tobeinserted = buff; + + /* + * Now tobeinserted points to an area that is to be inserted at + * the first %s + */ + if (!(newcopy = (char *) ecpg_alloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno))) + return false; + + strcpy(newcopy, copiedquery); + if ((p = next_insert(newcopy)) == NULL) + { + + /* + * We have an argument but we dont have the matched up string + * in the string + */ + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL); + return false; + } + else + { + strcpy(p, tobeinserted); + + /* + * The strange thing in the second argument is the rest of the + * string from the old string + */ + strcat(newcopy, + copiedquery + + (p - newcopy) + + sizeof("?") - 1 /* don't count the '\0' */ ); + } + + /* + * Now everything is safely copied to the newcopy. Lets free the + * oldcopy and let the copiedquery get the var->value from the + * newcopy. + */ + if (mallocedval != NULL) + { + free(mallocedval); + mallocedval = NULL; + } + + free(copiedquery); + copiedquery = newcopy; + + var = var->next; + } + + /* Check if there are unmatched things left. */ + if (next_insert(copiedquery) != NULL) + { + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL); + return false; + } + + /* Now the request is built. */ + + if (stmt->connection->committed && !stmt->connection->autocommit) + { + if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) + { + ECPGraise(stmt->lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + stmt->connection->committed = false; + } + + ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name); + results = PQexec(stmt->connection->connection, copiedquery); + free(copiedquery); + + if (results == NULL) + { + ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, + PQerrorMessage(stmt->connection->connection)); + ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); + } + else + { + var = stmt->outlist; + switch (PQresultStatus(results)) + { + int nfields, + ntuples, + act_tuple, + act_field, + isarray; + + case PGRES_TUPLES_OK: + nfields = PQnfields(results); + sqlca.sqlerrd[2] = ntuples = PQntuples(results); + status = true; + + if (ntuples < 1) + { + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", + stmt->lineno, ntuples); + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL); + status = false; + break; + } + + for (act_field = 0; act_field < nfields && status; act_field++) + { + char *array_query; + + if (var == NULL) + { + ECPGlog("ECPGexecute line %d: Too few arguments.\n", stmt->lineno); + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL); + return (false); + } + + array_query = (char *)ecpg_alloc(strlen("select typelem from pg_type where oid=") + 11, stmt -> lineno); + sprintf(array_query, "select typelem from pg_type where oid=%d", PQftype(results, act_field)); + query = PQexec(stmt->connection->connection, array_query); + isarray = 0; + if (PQresultStatus(query) == PGRES_TUPLES_OK) { + isarray = atol((char *)PQgetvalue(query, 0, 0)); + if (ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER || + ECPGDynamicType(PQftype(results, act_field)) == SQL3_CHARACTER_VARYING) + { + /* arrays of character strings are not yet implemented */ + isarray = false; + } + ECPGlog("ECPGexecute line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, PQftype(results, act_field), var->type, isarray ? "yes" : "no"); + } + PQclear(query); + + if (!isarray) + { + /* + * if we don't have enough space, we cannot read all + * tuples + */ + if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize)) + { + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n", + stmt->lineno, ntuples, var->arrsize); + ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL); + status = false; + break; + } + } + else + { + /* + * since we read an array, the variable has to be + * an array too + */ + if (var->arrsize == 0) + { + ECPGlog("ECPGexecute line %d: variable is not an array\n"); + ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL); + status = false; + break; + } + } + + /* + * allocate memory for NULL pointers + */ + if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL) + { + int len = 0; + + switch (var->type) + { + case ECPGt_char: + case ECPGt_unsigned_char: + var->varcharsize = 0; + /* check strlen for each tuple */ + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + { + int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + + if (len > var->varcharsize) + var->varcharsize = len; + } + var->offset *= var->varcharsize; + len = var->offset * ntuples; + break; + case ECPGt_varchar: + len = ntuples * (var->varcharsize + sizeof(int)); + break; + default: + len = var->offset * ntuples; + break; + } + var->value = (void *) ecpg_alloc(len, stmt->lineno); + *((void **) var->pointer) = var->value; + add_mem(var->value, stmt->lineno); + } + + for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) + { + if (!get_data(results, act_tuple, act_field, stmt->lineno, + var->type, var->ind_type, var->value, + var->ind_value, var->varcharsize, var->offset, isarray)) + status = false; + } + var = var->next; + } + + if (status && var != NULL) + { + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL); + status = false; + } + + break; + case PGRES_EMPTY_QUERY: + /* do nothing */ + ECPGraise(stmt->lineno, ECPG_EMPTY, NULL); + break; + case PGRES_COMMAND_OK: + status = true; + sqlca.sqlerrd[1] = atol(PQoidStatus(results)); + sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); + ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, PQcmdStatus(results)); + break; + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + ECPGlog("ECPGexecute line %d: Error: %s", + stmt->lineno, PQerrorMessage(stmt->connection->connection)); + ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); + status = false; + break; + case PGRES_COPY_OUT: + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); + PQendcopy(stmt->connection->connection); + break; + case PGRES_COPY_IN: + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); + PQendcopy(stmt->connection->connection); + break; + default: + ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", + stmt->lineno); + ECPGraise(stmt->lineno, ECPG_PGSQL, PQerrorMessage(stmt->connection->connection)); + status = false; + break; + } + PQclear(results); + } + + /* check for asynchronous returns */ + notify = PQnotifies(stmt->connection->connection); + if (notify) + { + ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", + stmt->lineno, notify->relname, notify->be_pid); + free(notify); + } + + return status; +} + +bool +ECPGdo(int lineno, const char *connection_name, char *query, ...) +{ + va_list args; + struct statement *stmt; + struct connection *con = get_connection(connection_name); + bool status=true; + char *locale = setlocale(LC_NUMERIC, NULL); + + /* Make sure we do NOT honor the locale for numeric input/output */ + /* since the database wants teh standard decimal point */ + setlocale(LC_NUMERIC, "C"); + + if (!ecpg_init(con, connection_name, lineno)) + { + setlocale(LC_NUMERIC, locale); + return(false); + } + + va_start(args, query); + if (create_statement(lineno, con, &stmt, query, args) == false) + { + setlocale(LC_NUMERIC, locale); + return (false); + } + va_end(args); + + /* are we connected? */ + if (con == NULL || con->connection == NULL) + { + free_statement(stmt); + ECPGlog("ECPGdo: not connected to %s\n", con->name); + ECPGraise(lineno, ECPG_NOT_CONN, NULL); + setlocale(LC_NUMERIC, locale); + return false; + } + + status = ECPGexecute(stmt); + free_statement(stmt); + + /* and reset locale value so our application is not affected */ + setlocale(LC_NUMERIC, locale); + return (status); +} + +/* dynamic SQL support routines + * + * Copyright (c) 2000, Christof Petig + * + * $Header: /cvsroot/pgsql/src/interfaces/ecpg/lib/Attic/execute.c,v 1.1 2000/03/07 15:10:56 meskes Exp $ + */ + +PGconn *ECPG_internal_get_connection(char *name); + +extern struct descriptor +{ + char *name; + PGresult *result; + struct descriptor *next; +} *all_descriptors; + +// like ECPGexecute +static bool execute_descriptor(int lineno,const char *query + ,struct connection *con,PGresult **resultptr) +{ + bool status = false; + PGresult *results; + PGnotify *notify; + + /* Now the request is built. */ + + if (con->committed && !con->autocommit) + { + if ((results = PQexec(con->connection, "begin transaction")) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + con->committed = false; + } + + ECPGlog("execute_descriptor line %d: QUERY: %s on connection %s\n", lineno, query, con->name); + results = PQexec(con->connection, query); + + if (results == NULL) + { + ECPGlog("ECPGexecute line %d: error: %s", lineno, + PQerrorMessage(con->connection)); + ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection)); + } + else + { *resultptr=results; + switch (PQresultStatus(results)) + { int ntuples; + case PGRES_TUPLES_OK: + status = true; + sqlca.sqlerrd[2] = ntuples = PQntuples(results); + if (ntuples < 1) + { + ECPGlog("execute_descriptor line %d: Incorrect number of matches: %d\n", + lineno, ntuples); + ECPGraise(lineno, ECPG_NOT_FOUND, NULL); + status = false; + break; + } + break; +#if 1 /* strictly these are not needed (yet) */ + case PGRES_EMPTY_QUERY: + /* do nothing */ + ECPGraise(lineno, ECPG_EMPTY, NULL); + break; + case PGRES_COMMAND_OK: + status = true; + sqlca.sqlerrd[1] = atol(PQoidStatus(results)); + sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); + ECPGlog("ECPGexecute line %d Ok: %s\n", lineno, PQcmdStatus(results)); + break; + case PGRES_COPY_OUT: + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", lineno); + PQendcopy(con->connection); + break; + case PGRES_COPY_IN: + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", lineno); + PQendcopy(con->connection); + break; +#else + case PGRES_EMPTY_QUERY: + case PGRES_COMMAND_OK: + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + break; +#endif + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + ECPGlog("ECPGexecute line %d: Error: %s", + lineno, PQerrorMessage(con->connection)); + ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection)); + status = false; + break; + default: + ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", + lineno); + ECPGraise(lineno, ECPG_PGSQL, PQerrorMessage(con->connection)); + status = false; + break; + } + } + + /* check for asynchronous returns */ + notify = PQnotifies(con->connection); + if (notify) + { + ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", + lineno, notify->relname, notify->be_pid); + free(notify); + } + return status; +} + +/* like ECPGdo */ +static bool do_descriptor2(int lineno,const char *connection_name, + PGresult **resultptr, const char *query) +{ + struct connection *con = get_connection(connection_name); + bool status=true; + char *locale = setlocale(LC_NUMERIC, NULL); + + /* Make sure we do NOT honor the locale for numeric input/output */ + /* since the database wants teh standard decimal point */ + setlocale(LC_NUMERIC, "C"); + + if (!ecpg_init(con, connection_name, lineno)) + { setlocale(LC_NUMERIC, locale); + return(false); + } + + /* are we connected? */ + if (con == NULL || con->connection == NULL) + { + ECPGlog("do_descriptor2: not connected to %s\n", con->name); + ECPGraise(lineno, ECPG_NOT_CONN, NULL); + setlocale(LC_NUMERIC, locale); + return false; + } + + status = execute_descriptor(lineno,query,con,resultptr); + + /* and reset locale value so our application is not affected */ + setlocale(LC_NUMERIC, locale); + return (status); +} + +bool ECPGdo_descriptor(int line,const char *connection, + const char *descriptor,const char *query) +{ + struct descriptor *i; + for (i=all_descriptors;i!=NULL;i=i->next) + { if (!strcmp(descriptor,i->name)) + { + bool status; + + /* free previous result */ + if (i->result) PQclear(i->result); + i->result=NULL; + + status=do_descriptor2(line,connection,&i->result,query); + + if (!i->result) PQmakeEmptyPGresult(NULL, 0); + return (status); + } + } + + ECPGraise(line, ECPG_UNKNOWN_DESCRIPTOR, descriptor); + return false; +} diff --git a/src/interfaces/ecpg/lib/extern.h b/src/interfaces/ecpg/lib/extern.h new file mode 100644 index 00000000000..ef9515d7c96 --- /dev/null +++ b/src/interfaces/ecpg/lib/extern.h @@ -0,0 +1,43 @@ +#include +#include + +/* Here are some methods used by the lib. */ +/* Returns a pointer to a string containing a simple type name. */ +void free_auto_mem(void); +bool get_data(PGresult *, int, int, int, enum ECPGttype type, + enum ECPGttype, void *, void *, long, long, bool); +struct connection *get_connection(const char *); +void init_sqlca(void); +char *ecpg_alloc(long, int); +bool ecpg_init(const struct connection *, const char *, const int); +char *ecpg_strdup(const char *, int); +const char *ECPGtype_name(enum ECPGttype); +unsigned int ECPGDynamicType(Oid); + +/* A generic varchar type. */ +struct ECPGgeneric_varchar +{ + int len; + char arr[1]; +}; + +/* structure to store one statement */ +struct statement +{ + int lineno; + char *command; + struct connection *connection; + struct variable *inlist; + struct variable *outlist; +}; + +/* structure to store connections */ +struct connection +{ + char *name; + PGconn *connection; + bool committed; + int autocommit; + struct connection *next; +}; + diff --git a/src/interfaces/ecpg/lib/memory.c b/src/interfaces/ecpg/lib/memory.c index 61c5d299f37..463dad129fb 100644 --- a/src/interfaces/ecpg/lib/memory.c +++ b/src/interfaces/ecpg/lib/memory.c @@ -1,5 +1,7 @@ #include #include +#include +#include "extern.h" char * ecpg_alloc(long size, int lineno) diff --git a/src/interfaces/ecpg/lib/misc.c b/src/interfaces/ecpg/lib/misc.c new file mode 100644 index 00000000000..55e0cfc8177 --- /dev/null +++ b/src/interfaces/ecpg/lib/misc.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include "extern.h" +#include + +static struct sqlca sqlca_init = +{ + {'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '}, + sizeof(struct sqlca), + 0, + {0, {0}}, + {'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '}, + {0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0} +}; + +static int simple_debug = 0; +static FILE *debugstream = NULL; + +void +init_sqlca(void) +{ + memcpy((char *) &sqlca, (char *) &sqlca_init, sizeof(sqlca)); +} + +bool +ecpg_init(const struct connection *con, const char * connection_name, const int lineno) +{ + init_sqlca(); + if (con == NULL) + { + ECPGraise(lineno, ECPG_NO_CONN, connection_name ? connection_name : "NULL"); + return (false); + } + + return (true); +} + +bool +ECPGstatus(int lineno, const char *connection_name) +{ + struct connection *con = get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return(false); + + /* are we connected? */ + if (con->connection == NULL) + { + ECPGlog("ECPGdo: not connected to %s\n", con->name); + ECPGraise(lineno, ECPG_NOT_CONN, NULL); + return false; + } + + return (true); +} + +bool +ECPGtrans(int lineno, const char *connection_name, const char *transaction) +{ + PGresult *res; + struct connection *con = get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return(false); + + ECPGlog("ECPGtrans line %d action = %s connection = %s\n", lineno, transaction, con->name); + + /* if we have no connection we just simulate the command */ + if (con && con->connection) + { + if ((res = PQexec(con->connection, transaction)) == NULL) + { + ECPGraise(lineno, ECPG_TRANS, NULL); + return FALSE; + } + PQclear(res); + } + + if (strcmp(transaction, "commit") == 0 || strcmp(transaction, "rollback") == 0) + { + con->committed = true; + + /* deallocate all prepared statements */ + if (!ECPGdeallocate_all(lineno)) + return false; + } + + return true; +} + + +void +ECPGdebug(int n, FILE *dbgs) +{ + simple_debug = n; + debugstream = dbgs; + ECPGlog("ECPGdebug: set to %d\n", simple_debug); +} + +void +ECPGlog(const char *format,...) +{ + va_list ap; + + if (simple_debug) + { + char *f = (char *) malloc(strlen(format) + 100); + + if (!f) + return; + + sprintf(f, "[%d]: %s", (int) getpid(), format); + + va_start(ap, format); + vfprintf(debugstream, f, ap); + va_end(ap); + + free(f); + } +} diff --git a/src/interfaces/ecpg/lib/prepare.c b/src/interfaces/ecpg/lib/prepare.c index 1b78aef6039..2155177edec 100644 --- a/src/interfaces/ecpg/lib/prepare.c +++ b/src/interfaces/ecpg/lib/prepare.c @@ -2,6 +2,8 @@ #include #include +#include +#include "extern.h" #include static struct prepared_statement diff --git a/src/interfaces/ecpg/lib/typename.c b/src/interfaces/ecpg/lib/typename.c index 1999ab82d75..2b64c19a8ce 100644 --- a/src/interfaces/ecpg/lib/typename.c +++ b/src/interfaces/ecpg/lib/typename.c @@ -1,6 +1,7 @@ #include #include #include +#include "extern.h" #include /* diff --git a/src/interfaces/ecpg/preproc/ecpg.c b/src/interfaces/ecpg/preproc/ecpg.c index 1c14fbbc0c2..ceb06fb744e 100644 --- a/src/interfaces/ecpg/preproc/ecpg.c +++ b/src/interfaces/ecpg/preproc/ecpg.c @@ -249,7 +249,7 @@ main(int argc, char *const argv[]) lex_init(); /* we need two includes */ - fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These two include files are added by the preprocessor */\n#include \n#include \n#line 1 \"%s\"\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL, input_filename); + fprintf(yyout, "/* Processed by ecpg (%d.%d.%d) */\n/* These three include files are added by the preprocessor */\n#include \n#include \n#include \n#line 1 \"%s\"\n", MAJOR_VERSION, MINOR_VERSION, PATCHLEVEL, input_filename); /* and parse the source */ yyparse(); diff --git a/src/interfaces/ecpg/preproc/extern.h b/src/interfaces/ecpg/preproc/extern.h index 1134e42c7ee..f79c3b43a0c 100644 --- a/src/interfaces/ecpg/preproc/extern.h +++ b/src/interfaces/ecpg/preproc/extern.h @@ -39,7 +39,7 @@ extern const char *get_dtype(enum ECPGdtype); extern void lex_init(void); extern char *make_str(const char *); extern void output_line_number(void); -extern void output_statement(char *, int, char *, char *, struct arguments *, struct arguments *); +extern void output_statement(char *, int, char *, char *); extern void output_simple_statement(char *); extern char *hashline_number(void); extern int yyparse(void); diff --git a/src/interfaces/ecpg/preproc/output.c b/src/interfaces/ecpg/preproc/output.c index d67f9eae5af..760751affd3 100644 --- a/src/interfaces/ecpg/preproc/output.c +++ b/src/interfaces/ecpg/preproc/output.c @@ -94,8 +94,7 @@ hashline_number(void) } void -output_statement(char * stmt, int mode, char *descriptor, - char *con, struct arguments *insert, struct arguments *result) +output_statement(char * stmt, int mode, char *descriptor, char *con) { int i, j = strlen(stmt); @@ -118,10 +117,11 @@ output_statement(char * stmt, int mode, char *descriptor, fputs("\", ", yyout); /* dump variables to C file */ - dump_variables(insert, 1); + dump_variables(argsinsert, 1); fputs("ECPGt_EOIT, ", yyout); - dump_variables(result, 1); + dump_variables(argsresult, 1); fputs("ECPGt_EORT);", yyout); + reset_variables(); } else fputs("\");", yyout); diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 88591f831bc..e21a67b0800 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -392,64 +392,64 @@ statement: ecpgstart opt_at stmt ';' { connection = NULL; } opt_at: SQL_AT connection_target { connection = $2; } -stmt: AlterTableStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | AlterGroupStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | AlterUserStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ClosePortalStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CommentStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CopyStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreateStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreateAsStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreateGroupStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreateSeqStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreatePLangStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreateTrigStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreateUserStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ClusterStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DefineStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DropStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | TruncateStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DropGroupStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DropPLangStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DropTrigStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DropUserStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ExtendStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ExplainStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | FetchStmt { output_statement($1, 1, NULL, connection, argsinsert, argsresult); } - | GrantStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | IndexStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ListenStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | UnlistenStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | LockStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ProcedureStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ReindexStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | RemoveAggrStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | RemoveOperStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | RemoveFuncStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | RemoveStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | RenameStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | RevokeStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } +stmt: AlterTableStmt { output_statement($1, 0, NULL, connection); } + | AlterGroupStmt { output_statement($1, 0, NULL, connection); } + | AlterUserStmt { output_statement($1, 0, NULL, connection); } + | ClosePortalStmt { output_statement($1, 0, NULL, connection); } + | CommentStmt { output_statement($1, 0, NULL, connection); } + | CopyStmt { output_statement($1, 0, NULL, connection); } + | CreateStmt { output_statement($1, 0, NULL, connection); } + | CreateAsStmt { output_statement($1, 0, NULL, connection); } + | CreateGroupStmt { output_statement($1, 0, NULL, connection); } + | CreateSeqStmt { output_statement($1, 0, NULL, connection); } + | CreatePLangStmt { output_statement($1, 0, NULL, connection); } + | CreateTrigStmt { output_statement($1, 0, NULL, connection); } + | CreateUserStmt { output_statement($1, 0, NULL, connection); } + | ClusterStmt { output_statement($1, 0, NULL, connection); } + | DefineStmt { output_statement($1, 0, NULL, connection); } + | DropStmt { output_statement($1, 0, NULL, connection); } + | TruncateStmt { output_statement($1, 0, NULL, connection); } + | DropGroupStmt { output_statement($1, 0, NULL, connection); } + | DropPLangStmt { output_statement($1, 0, NULL, connection); } + | DropTrigStmt { output_statement($1, 0, NULL, connection); } + | DropUserStmt { output_statement($1, 0, NULL, connection); } + | ExtendStmt { output_statement($1, 0, NULL, connection); } + | ExplainStmt { output_statement($1, 0, NULL, connection); } + | FetchStmt { output_statement($1, 1, NULL, connection); } + | GrantStmt { output_statement($1, 0, NULL, connection); } + | IndexStmt { output_statement($1, 0, NULL, connection); } + | ListenStmt { output_statement($1, 0, NULL, connection); } + | UnlistenStmt { output_statement($1, 0, NULL, connection); } + | LockStmt { output_statement($1, 0, NULL, connection); } + | ProcedureStmt { output_statement($1, 0, NULL, connection); } + | ReindexStmt { output_statement($1, 0, NULL, connection); } + | RemoveAggrStmt { output_statement($1, 0, NULL, connection); } + | RemoveOperStmt { output_statement($1, 0, NULL, connection); } + | RemoveFuncStmt { output_statement($1, 0, NULL, connection); } + | RemoveStmt { output_statement($1, 0, NULL, connection); } + | RenameStmt { output_statement($1, 0, NULL, connection); } + | RevokeStmt { output_statement($1, 0, NULL, connection); } | OptimizableStmt { if (strncmp($1, "/* " , sizeof("/* ")-1) == 0) output_simple_statement($1); else - output_statement($1, 1, NULL, connection, argsinsert, argsresult); + output_statement($1, 1, NULL, connection); } - | RuleStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } + | RuleStmt { output_statement($1, 0, NULL, connection); } | TransactionStmt { fprintf(yyout, "{ ECPGtrans(__LINE__, %s, \"%s\");", connection ? connection : "NULL", $1); whenever_action(2); free($1); } - | ViewStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | LoadStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | CreatedbStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | DropdbStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | VacuumStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | VariableSetStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | VariableShowStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | VariableResetStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ConstraintsSetStmt { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } + | ViewStmt { output_statement($1, 0, NULL, connection); } + | LoadStmt { output_statement($1, 0, NULL, connection); } + | CreatedbStmt { output_statement($1, 0, NULL, connection); } + | DropdbStmt { output_statement($1, 0, NULL, connection); } + | VacuumStmt { output_statement($1, 0, NULL, connection); } + | VariableSetStmt { output_statement($1, 0, NULL, connection); } + | VariableShowStmt { output_statement($1, 0, NULL, connection); } + | VariableResetStmt { output_statement($1, 0, NULL, connection); } + | ConstraintsSetStmt { output_statement($1, 0, NULL, connection); } | ECPGAllocateDescr { fprintf(yyout,"ECPGallocate_desc(__LINE__, \"%s\");",$1); whenever_action(0); free($1); @@ -489,8 +489,8 @@ stmt: AlterTableStmt { output_statement($1, 0, NULL, connection, argsinsert, whenever_action(2); free($1); } - | ECPGExecute { output_statement($1, 0, NULL, connection, argsinsert, argsresult); } - | ECPGFetchDescStmt { output_statement($1.str, 1, $1.name, connection, argsinsert, argsresult); } + | ECPGExecute { output_statement($1, 0, NULL, connection); } + | ECPGFetchDescStmt { output_statement($1.str, 1, $1.name, connection); } | ECPGFree { fprintf(yyout, "{ ECPGdeallocate(__LINE__, \"%s\");", $1); @@ -531,7 +531,9 @@ stmt: AlterTableStmt { output_statement($1, 0, NULL, connection, argsinsert, else argsinsert = ptr->argsinsert; - output_statement(ptr->command, 0, NULL, ptr->connection, argsinsert, ptr->argsresult); + argsresult = ptr->argsresult; + + output_statement(ptr->command, 0, NULL, ptr->connection); } | ECPGPrepare { if (connection) diff --git a/src/interfaces/ecpg/preproc/variable.c b/src/interfaces/ecpg/preproc/variable.c index 10b8af6cf95..98cf32576d7 100644 --- a/src/interfaces/ecpg/preproc/variable.c +++ b/src/interfaces/ecpg/preproc/variable.c @@ -189,7 +189,6 @@ reset_variables(void) argsresult = NULL; } - /* Add a variable to a request. */ void add_variable(struct arguments ** list, struct variable * var, struct variable * ind) -- cgit v1.2.3