diff options
author | Michael Meskes <meskes@postgresql.org> | 2003-03-16 10:42:54 +0000 |
---|---|---|
committer | Michael Meskes <meskes@postgresql.org> | 2003-03-16 10:42:54 +0000 |
commit | a4f25b6a9c2dbf5f38e498922e3761cb3bf46ba0 (patch) | |
tree | 54ff23d698b18c898c8fd7903df29b76e95df9fb /src/interfaces/ecpg/ecpglib/execute.c | |
parent | 48dfa0d057e0d9b2244ff1706dd72e91a0b45064 (diff) | |
download | postgresql-a4f25b6a9c2dbf5f38e498922e3761cb3bf46ba0.tar.gz postgresql-a4f25b6a9c2dbf5f38e498922e3761cb3bf46ba0.zip |
Started working on a seperate pgtypes library. First test work. PLEASE test compilation on iother systems.
Diffstat (limited to 'src/interfaces/ecpg/ecpglib/execute.c')
-rw-r--r-- | src/interfaces/ecpg/ecpglib/execute.c | 1184 |
1 files changed, 1184 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c new file mode 100644 index 00000000000..4ed4b8fde46 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -0,0 +1,1184 @@ +/* $Header: /cvsroot/pgsql/src/interfaces/ecpg/ecpglib/execute.c,v 1.1 2003/03/16 10:42:53 meskes Exp $ */ + +/* + * The aim is to get a simpler inteface to the database routines. + * All the tidieous messing around with tuples is supposed to be hidden + * by this function. + */ +/* Author: Linus Tolke + (actually most if the code is "borrowed" from the distribution and just + slightly modified) + */ + +/* Taken over as part of PostgreSQL by Michael Meskes <meskes@postgresql.org> + on Feb. 5th, 1998 */ + +#include "postgres_fe.h" + +#include <stdio.h> +#include <locale.h> + +#include "pg_type.h" + +#include "ecpgtype.h" +#include "ecpglib.h" +#include "ecpgerrno.h" +#include "extern.h" +#include "sqlca.h" +#include "sql3types.h" +#include "pgtypes_numeric.h" + +/* variables visible to the programs */ +struct sqlca sqlca = +{ + { + 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' ' + }, + sizeof(struct sqlca), + 0, + { + 0, + { + 0 + } + }, + { + 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' ' + }, + { + 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + } +}; + +/* This function returns a newly malloced string that has the \ + in the argument quoted with \ and the ' quoted with ' as SQL92 says. + */ +static +char * +quote_postgres(char *arg, int lineno) +{ + char *res = (char *) ECPGalloc(2 * strlen(arg) + 3, lineno); + int i, + ri = 0; + + if (!res) + return (res); + + res[ri++] = '\''; + + for (i = 0; arg[i]; i++, ri++) + { + switch (arg[i]) + { + case '\'': + res[ri++] = '\''; + break; + case '\\': + res[ri++] = '\\'; + break; + default: + ; + } + res[ri] = arg[i]; + } + + res[ri++] = '\''; + res[ri] = '\0'; + + return res; +} + +/* + * create a list of variables + * The variables are listed with input variables preceding outputvariables + * The end of each group is marked by an end marker. + * per variable we list: + * type - as defined in ecpgtype.h + * value - where to store the data + * varcharsize - length of string in case we have a stringvariable, else 0 + * arraysize - 0 for pointer (we don't know the size of the array), + * 1 for simple variable, size for arrays + * offset - offset between ith and (i+1)th entry in an array, + * normally that means sizeof(type) + * ind_type - type of indicator variable + * ind_value - pointer to indicator variable + * ind_varcharsize - empty + * ind_arraysize - arraysize of indicator array + * ind_offset - indicator offset + */ +static bool +create_statement(int lineno, struct connection * connection, struct statement ** stmt, char *query, va_list ap) +{ + struct variable **list = &((*stmt)->inlist); + enum ECPGttype type; + + if (!(*stmt = (struct statement *) ECPGalloc(sizeof(struct statement), lineno))) + return false; + + (*stmt)->command = query; + (*stmt)->connection = connection; + (*stmt)->lineno = lineno; + + list = &((*stmt)->inlist); + + type = va_arg(ap, enum ECPGttype); + + while (type != ECPGt_EORT) + { + if (type == ECPGt_EOIT) + list = &((*stmt)->outlist); + else + { + struct variable *var, + *ptr; + + if (!(var = (struct variable *) ECPGalloc(sizeof(struct variable), lineno))) + return false; + + var->type = type; + var->pointer = va_arg(ap, char *); + + /* if variable is NULL, the statement hasn't been prepared */ + if (var->pointer == NULL) + { + ECPGraise(lineno, ECPG_INVALID_STMT, NULL); + ECPGfree(var); + return false; + } + + var->varcharsize = va_arg(ap, long); + var->arrsize = va_arg(ap, long); + var->offset = va_arg(ap, long); + + if (var->arrsize == 0 || var->varcharsize == 0) + var->value = *((char **) (var->pointer)); + else + var->value = var->pointer; + + var->ind_type = va_arg(ap, enum ECPGttype); + var->ind_pointer = va_arg(ap, char *); + var->ind_varcharsize = va_arg(ap, long); + var->ind_arrsize = va_arg(ap, long); + var->ind_offset = va_arg(ap, long); + var->next = NULL; + + if (var->ind_type != ECPGt_NO_INDICATOR + && (var->ind_arrsize == 0 || var->ind_varcharsize == 0)) + var->ind_value = *((char **) (var->ind_pointer)); + else + var->ind_value = var->ind_pointer; + + for (ptr = *list; ptr && ptr->next; ptr = ptr->next); + + if (ptr == NULL) + *list = var; + else + ptr->next = var; + } + + type = va_arg(ap, enum ECPGttype); + } + + return (true); +} + +static void +free_variable(struct variable * var) +{ + struct variable *var_next; + + if (var == (struct variable *) NULL) + return; + var_next = var->next; + ECPGfree(var); + + while (var_next) + { + var = var_next; + var_next = var->next; + ECPGfree(var); + } +} + +static void +free_statement(struct statement * stmt) +{ + if (stmt == (struct statement *) NULL) + return; + free_variable(stmt->inlist); + free_variable(stmt->outlist); + ECPGfree(stmt); +} + +static char * +next_insert(char *text) +{ + char *ptr = text; + bool string = false; + + for (; *ptr != '\0' && (*ptr != '?' || string); ptr++) + { + if (*ptr == '\\') /* escape character */ + ptr++; + else if (*ptr == '\'') + string = string ? false : true; + } + + return (*ptr == '\0') ? NULL : ptr; +} + +/* + * push a value on the cache + */ + +static void +ECPGtypeinfocache_push(struct ECPGtype_information_cache ** cache, int oid, bool isarray, int lineno) +{ + struct ECPGtype_information_cache *new_entry + = (struct ECPGtype_information_cache *) ECPGalloc(sizeof(struct ECPGtype_information_cache), lineno); + + new_entry->oid = oid; + new_entry->isarray = isarray; + new_entry->next = *cache; + *cache = new_entry; +} + +static bool +ECPGis_type_an_array(int type, const struct statement * stmt, const struct variable * var) +{ + char *array_query; + int isarray = 0; + PGresult *query; + struct ECPGtype_information_cache *cache_entry; + + if ((stmt->connection->cache_head) == NULL) + { + /* + * Text like types are not an array for ecpg, but postgres counts + * them as an array. This define reminds you to not 'correct' + * these values. + */ +#define not_an_array_in_ecpg false + + /* populate cache with well known types to speed things up */ + ECPGtypeinfocache_push(&(stmt->connection->cache_head), BOOLOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), BYTEAOID, not_an_array_in_ecpg, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), CHAROID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), NAMEOID, not_an_array_in_ecpg, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT8OID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT2OID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT2VECTOROID, true, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), INT4OID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), REGPROCOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TEXTOID, not_an_array_in_ecpg, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), OIDOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIDOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), XIDOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), CIDOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), OIDVECTOROID, true, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), POINTOID, true, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), LSEGOID, true, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), PATHOID, not_an_array_in_ecpg, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), BOXOID, true, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), POLYGONOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), LINEOID, true, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), FLOAT4OID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), FLOAT8OID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), ABSTIMEOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), RELTIMEOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TINTERVALOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), UNKNOWNOID, not_an_array_in_ecpg, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), CIRCLEOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), CASHOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), INETOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), CIDROID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), BPCHAROID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), VARCHAROID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), DATEOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMEOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMESTAMPOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMESTAMPTZOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), INTERVALOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), TIMETZOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), ZPBITOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), VARBITOID, false, stmt->lineno); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), NUMERICOID, false, stmt->lineno); + } + + for (cache_entry = (stmt->connection->cache_head); cache_entry != NULL; cache_entry = cache_entry->next) + { + if (cache_entry->oid == type) + return cache_entry->isarray; + } + + array_query = (char *) ECPGalloc(strlen("select typelem from pg_type where oid=") + 11, stmt->lineno); + sprintf(array_query, "select typelem from pg_type where oid=%d", type); + query = PQexec(stmt->connection->connection, array_query); + ECPGfree(array_query); + if (PQresultStatus(query) == PGRES_TUPLES_OK) + { + isarray = atol((char *) PQgetvalue(query, 0, 0)); + if (ECPGDynamicType(type) == SQL3_CHARACTER || + ECPGDynamicType(type) == SQL3_CHARACTER_VARYING) + { + /* + * arrays of character strings are not yet implemented + */ + isarray = false; + } + ECPGlog("ECPGexecute line %d: TYPE database: %d C: %d array: %s\n", stmt->lineno, type, var->type, isarray ? "yes" : "no"); + ECPGtypeinfocache_push(&(stmt->connection->cache_head), type, isarray, stmt->lineno); + } + PQclear(query); + return isarray; +} + + +bool +ECPGstore_result(const PGresult *results, int act_field, + const struct statement * stmt, struct variable * var) +{ + int isarray, + act_tuple, + ntuples = PQntuples(results); + bool status = true; + + isarray = ECPGis_type_an_array(PQftype(results, act_field), stmt, var); + + if (!isarray) + { + /* + * if we don't have enough space, we cannot read all tuples + */ + if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize)) + { + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d don't fit into array of %d\n", + stmt->lineno, ntuples, var->arrsize); + ECPGraise(stmt->lineno, ECPG_TOO_MANY_MATCHES, NULL); + return false; + } + } + else + { + /* + * since we read an array, the variable has to be an array too + */ + if (var->arrsize == 0) + { + ECPGraise(stmt->lineno, ECPG_NO_ARRAY, NULL); + return false; + } + } + + /* + * allocate memory for NULL pointers + */ + if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL) + { + int len = 0; + + switch (var->type) + { + case ECPGt_char: + case ECPGt_unsigned_char: + if (!var->varcharsize && !var->arrsize) + { + /* special mode for handling char**foo=0 */ + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + len += strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + len *= var->offset; /* should be 1, but YMNK */ + len += (ntuples + 1) * sizeof(char *); + + ECPGlog("ECPGstore_result: line %d: allocating %d bytes for %d tuples (char**=0)", + stmt->lineno, len, ntuples); + } + else + { + var->varcharsize = 0; + /* check strlen for each tuple */ + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + { + int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + + if (len > var->varcharsize) + var->varcharsize = len; + } + var->offset *= var->varcharsize; + len = var->offset * ntuples; + } + break; + case ECPGt_varchar: + len = ntuples * (var->varcharsize + sizeof(int)); + break; + default: + len = var->offset * ntuples; + break; + } + var->value = (char *) ECPGalloc(len, stmt->lineno); + *((char **) var->pointer) = var->value; + ECPGadd_mem(var->value, stmt->lineno); + } + + /* allocate indicator variable if needed */ + if ((var->ind_arrsize == 0 || var->ind_varcharsize == 0) && var->ind_value == NULL && var->ind_pointer != NULL) + { + int len = var->ind_offset * ntuples; + + var->ind_value = (char *) ECPGalloc(len, stmt->lineno); + *((char **) var->ind_pointer) = var->ind_value; + ECPGadd_mem(var->ind_value, stmt->lineno); + } + + /* fill the variable with the tuple(s) */ + if (!var->varcharsize && !var->arrsize && + (var->type == ECPGt_char || var->type == ECPGt_unsigned_char)) + { + /* special mode for handling char**foo=0 */ + + /* filling the array of (char*)s */ + char **current_string = (char **) var->value; + + /* storing the data (after the last array element) */ + char *current_data_location = (char *) ¤t_string[ntuples + 1]; + + for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) + { + int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + + if (!ECPGget_data(results, act_tuple, act_field, stmt->lineno, + var->type, var->ind_type, current_data_location, + var->ind_value, len, 0, 0, isarray)) + status = false; + else + { + *current_string = current_data_location; + current_data_location += len; + current_string++; + } + } + + /* terminate the list */ + *current_string = NULL; + } + else + { + for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) + { + if (!ECPGget_data(results, act_tuple, act_field, stmt->lineno, + var->type, var->ind_type, var->value, + var->ind_value, var->varcharsize, var->offset, var->ind_offset, isarray)) + status = false; + } + } + return status; +} + +static bool +ECPGstore_input(const struct statement * stmt, const struct variable * var, + const char **tobeinserted_p, bool *malloced_p) +{ + char *mallocedval = NULL; + char *newcopy = NULL; + + /* + * arrays are not possible unless the attribute is an array too FIXME: + * we do not know if the attribute is an array here + */ + +/* if (var->arrsize > 1 && ...) + { + ECPGraise(stmt->lineno, ECPG_ARRAY_INSERT, NULL); + return false; + }*/ + + /* + * Some special treatment is needed for records since we want their + * contents to arrive in a comma-separated list on insert (I think). + */ + + *malloced_p = false; + *tobeinserted_p = ""; + + /* check for null value and set input buffer accordingly */ + switch (var->ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: + if (*(short *) var->ind_value < 0) + *tobeinserted_p = "null"; + break; + case ECPGt_int: + case ECPGt_unsigned_int: + if (*(int *) var->ind_value < 0) + *tobeinserted_p = "null"; + break; + case ECPGt_long: + case ECPGt_unsigned_long: + if (*(long *) var->ind_value < 0L) + *tobeinserted_p = "null"; + break; +#ifdef HAVE_LONG_LONG_INT_64 + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + if (*(long long int *) var->ind_value < (long long) 0) + *tobeinserted_p = "null"; + break; +#endif /* HAVE_LONG_LONG_INT_64 */ + default: + break; + } + + if (**tobeinserted_p == '\0') + { + switch (var->type) + { + int element; + + case ECPGt_short: + if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%hd,", ((short *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%hd", *((short *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_int: + if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%d", *((int *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_unsigned_short: + if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%hu,", ((unsigned short *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%hu", *((unsigned short *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_unsigned_int: + if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%u,", ((unsigned int *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%u", *((unsigned int *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_long: + if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%ld,", ((long *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%ld", *((long *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_unsigned_long: + if (!(mallocedval = ECPGalloc(var->arrsize * 20, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%lu,", ((unsigned long *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%lu", *((unsigned long *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; +#ifdef HAVE_LONG_LONG_INT_64 + case ECPGt_long_long: + if (!(mallocedval = ECPGalloc(var->arrsize * 25, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%lld,", ((long long *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%lld", *((long long *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_unsigned_long_long: + if (!(mallocedval = ECPGalloc(var->arrsize * 25, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%llu,", ((unsigned long long *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%llu", *((unsigned long long *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; +#endif /* HAVE_LONG_LONG_INT_64 */ + case ECPGt_float: + if (!(mallocedval = ECPGalloc(var->arrsize * 21, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((float *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%.14g", *((float *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_double: + if (!(mallocedval = ECPGalloc(var->arrsize * 21, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%.14g,", ((double *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + sprintf(mallocedval, "%.14g", *((double *) var->value)); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_bool: + if (!(mallocedval = ECPGalloc(var->arrsize * 2, stmt->lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "'{"); + + if (var->offset == sizeof(char)) + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%c,", (((char *) var->value)[element]) ? 't' : 'f'); + + /* + * this is necessary since sizeof(C++'s + * bool)==sizeof(int) + */ + else if (var->offset == sizeof(int)) + for (element = 0; element < var->arrsize; element++) + sprintf(mallocedval + strlen(mallocedval), "%c,", (((int *) var->value)[element]) ? 't' : 'f'); + else + ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size"); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + { + if (var->offset == sizeof(char)) + sprintf(mallocedval, "'%c'", (*((char *) var->value)) ? 't' : 'f'); + else if (var->offset == sizeof(int)) + sprintf(mallocedval, "'%c'", (*((int *) var->value)) ? 't' : 'f'); + else + ECPGraise(stmt->lineno, ECPG_CONVERT_BOOL, "different size"); + } + + *tobeinserted_p = mallocedval; + *malloced_p = true; + break; + + case ECPGt_char: + case ECPGt_unsigned_char: + { + /* set slen to string length if type is char * */ + int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : var->varcharsize; + + if (!(newcopy = ECPGalloc(slen + 1, stmt->lineno))) + return false; + + strncpy(newcopy, (char *) var->value, slen); + newcopy[slen] = '\0'; + + mallocedval = quote_postgres(newcopy, stmt->lineno); + if (!mallocedval) + return false; + + ECPGfree(newcopy); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + } + break; + case ECPGt_char_variable: + { + int slen = strlen((char *) var->value); + + if (!(mallocedval = ECPGalloc(slen + 1, stmt->lineno))) + return false; + + strncpy(mallocedval, (char *) var->value, slen); + mallocedval[slen] = '\0'; + + *tobeinserted_p = mallocedval; + *malloced_p = true; + } + break; + case ECPGt_varchar: + { + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) (var->value); + + if (!(newcopy = (char *) ECPGalloc(variable->len + 1, stmt->lineno))) + return false; + + strncpy(newcopy, variable->arr, variable->len); + newcopy[variable->len] = '\0'; + + mallocedval = quote_postgres(newcopy, stmt->lineno); + if (!mallocedval) + return false; + + ECPGfree(newcopy); + + *tobeinserted_p = mallocedval; + *malloced_p = true; + } + break; + + case ECPGt_numeric: + { + char *str; + int slen; + + if (var->arrsize > 1) + { + for (element = 0; element < var->arrsize; element++) + { + str = PGTYPESnumeric_ntoa((NumericVar *)((var + var->offset * element)->value)); + slen = strlen (str); + + if (!(mallocedval = ECPGrealloc(mallocedval, strlen(mallocedval) + slen + 5, stmt->lineno))) + return false; + + if (!element) + strcpy(mallocedval, "'{"); + + strncpy(mallocedval + strlen(mallocedval), str , slen + 1); + strcpy(mallocedval + strlen(mallocedval), ","); + } + strcpy(mallocedval + strlen(mallocedval) - 1, "}'"); + } + else + { + *str = PGTYPESnumeric_ntoa((NumericVar *)(var->value)); + slen = strlen (str); + + if (!(mallocedval = ECPGalloc(slen + 1, stmt->lineno))) + return false; + + strncpy(mallocedval, str , slen); + mallocedval[slen] = '\0'; + } + + *tobeinserted_p = mallocedval; + *malloced_p = true; + free(str); + } + break; + default: + /* Not implemented yet */ + ECPGraise(stmt->lineno, ECPG_UNSUPPORTED, (char *) ECPGtype_name(var->type)); + return false; + break; + } + } + return true; +} + +static bool +ECPGexecute(struct statement * stmt) +{ + bool status = false; + char *copiedquery; + char *errmsg, *cmdstat; + PGresult *results; + PGnotify *notify; + struct variable *var; + + copiedquery = ECPGstrdup(stmt->command, stmt->lineno); + + /* + * Now, if the type is one of the fill in types then we take the + * argument and enter that in the string at the first %s position. + * Then if there are any more fill in types we fill in at the next and + * so on. + */ + var = stmt->inlist; + while (var) + { + char *newcopy = NULL; + const char *tobeinserted = NULL; + char *p; + bool malloced = FALSE; + int hostvarl = 0; + + if (!ECPGstore_input(stmt, var, &tobeinserted, &malloced)) + return false; + + /* + * Now tobeinserted points to an area that is to be inserted at + * the first %s + */ + if (!(newcopy = (char *) ECPGalloc(strlen(copiedquery) + strlen(tobeinserted) + 1, stmt->lineno))) + return false; + + strcpy(newcopy, copiedquery); + if ((p = next_insert(newcopy + hostvarl)) == NULL) + { + /* + * We have an argument but we dont have the matched up string + * in the string + */ + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL); + return false; + } + else + { + strcpy(p, tobeinserted); + hostvarl = strlen(newcopy); + + /* + * The strange thing in the second argument is the rest of the + * string from the old string + */ + strcat(newcopy, + copiedquery + + (p - newcopy) + + sizeof("?") - 1 /* don't count the '\0' */ ); + } + + /* + * Now everything is safely copied to the newcopy. Lets free the + * oldcopy and let the copiedquery get the var->value from the + * newcopy. + */ + if (malloced) + { + ECPGfree((char *) tobeinserted); + tobeinserted = NULL; + } + + ECPGfree(copiedquery); + copiedquery = newcopy; + + var = var->next; + } + + /* Check if there are unmatched things left. */ + if (next_insert(copiedquery) != NULL) + { + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL); + return false; + } + + /* Now the request is built. */ + + if (stmt->connection->committed && !stmt->connection->autocommit) + { + if ((results = PQexec(stmt->connection->connection, "begin transaction")) == NULL) + { + ECPGraise(stmt->lineno, ECPG_TRANS, NULL); + return false; + } + PQclear(results); + stmt->connection->committed = false; + } + + ECPGlog("ECPGexecute line %d: QUERY: %s on connection %s\n", stmt->lineno, copiedquery, stmt->connection->name); + results = PQexec(stmt->connection->connection, copiedquery); + ECPGfree(copiedquery); + + if (results == NULL) + { + errmsg = PQerrorMessage(stmt->connection->connection); + ECPGlog("ECPGexecute line %d: error: %s", stmt->lineno, errmsg); + ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg); + set_backend_err(errmsg, stmt->lineno); + } + else + + /* + * note: since some of the following code is duplicated in + * descriptor.c it should go into a separate function + */ + { + bool clear_result = TRUE; + errmsg = PQresultErrorMessage(results); + set_backend_err(errmsg, stmt->lineno); + + var = stmt->outlist; + switch (PQresultStatus(results)) + { + int nfields, + ntuples, + act_field; + + case PGRES_TUPLES_OK: + nfields = PQnfields(results); + sqlca.sqlerrd[2] = ntuples = PQntuples(results); + status = true; + + if (ntuples < 1) + { + if (ntuples) + ECPGlog("ECPGexecute line %d: Incorrect number of matches: %d\n", + stmt->lineno, ntuples); + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL); + status = false; + break; + } + + if (var != NULL && var->type == ECPGt_descriptor) + { + PGresult **resultpp = ECPGdescriptor_lvalue(stmt->lineno, (const char *) var->pointer); + + if (resultpp == NULL) + status = false; + else + { + if (*resultpp) + PQclear(*resultpp); + *resultpp = results; + clear_result = FALSE; + ECPGlog("ECPGexecute putting result (%d tuples) into descriptor '%s'\n", PQntuples(results), (const char *) var->pointer); + } + var = var->next; + } + else + for (act_field = 0; act_field < nfields && status; act_field++) + { + if (var == NULL) + { + ECPGraise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, NULL); + return (false); + } + + status = ECPGstore_result(results, act_field, stmt, var); + + var = var->next; + } + + if (status && var != NULL) + { + ECPGraise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, NULL); + status = false; + } + + break; + case PGRES_EMPTY_QUERY: + /* do nothing */ + ECPGraise(stmt->lineno, ECPG_EMPTY, NULL); + break; + case PGRES_COMMAND_OK: + status = true; + cmdstat = PQcmdStatus(results); + sqlca.sqlerrd[1] = PQoidValue(results); + sqlca.sqlerrd[2] = atol(PQcmdTuples(results)); + ECPGlog("ECPGexecute line %d Ok: %s\n", stmt->lineno, cmdstat); + if (!sqlca.sqlerrd[2] && ( !strncmp(cmdstat, "UPDATE", 6) + || !strncmp(cmdstat, "INSERT", 6) + || !strncmp(cmdstat, "DELETE", 6))) + ECPGraise(stmt->lineno, ECPG_NOT_FOUND, NULL); + break; + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + case PGRES_BAD_RESPONSE: + ECPGlog("ECPGexecute line %d: Error: %s", stmt->lineno, errmsg); + ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg); + status = false; + break; + case PGRES_COPY_OUT: + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_OUT ... tossing.\n", stmt->lineno); + PQendcopy(stmt->connection->connection); + break; + case PGRES_COPY_IN: + ECPGlog("ECPGexecute line %d: Got PGRES_COPY_IN ... tossing.\n", stmt->lineno); + PQendcopy(stmt->connection->connection); + break; + default: + ECPGlog("ECPGexecute line %d: Got something else, postgres error.\n", + stmt->lineno); + ECPGraise(stmt->lineno, ECPG_PGSQL, errmsg); + status = false; + break; + } + if (clear_result) + PQclear(results); + } + + /* check for asynchronous returns */ + notify = PQnotifies(stmt->connection->connection); + if (notify) + { + ECPGlog("ECPGexecute line %d: ASYNC NOTIFY of '%s' from backend pid '%d' received\n", + stmt->lineno, notify->relname, notify->be_pid); + ECPGfree(notify); + } + + return status; +} + +bool +ECPGdo(int lineno, const char *connection_name, char *query,...) +{ + va_list args; + struct statement *stmt; + struct connection *con = ECPGget_connection(connection_name); + bool status; + char *oldlocale; + + /* Make sure we do NOT honor the locale for numeric input/output */ + /* since the database wants the standard decimal point */ + oldlocale = strdup(setlocale(LC_NUMERIC, NULL)); + setlocale(LC_NUMERIC, "C"); + + if (!ECPGinit(con, connection_name, lineno)) + { + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + return (false); + } + + /* construct statement in our own structure */ + va_start(args, query); + if (create_statement(lineno, con, &stmt, query, args) == false) + { + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + return (false); + } + va_end(args); + + /* are we connected? */ + if (con == NULL || con->connection == NULL) + { + free_statement(stmt); + ECPGraise(lineno, ECPG_NOT_CONN, (con) ? con->name : "<empty>"); + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + return false; + } + + /* initialize auto_mem struct */ + ECPGclear_auto_mem(); + + status = ECPGexecute(stmt); + free_statement(stmt); + + /* and reset locale value so our application is not affected */ + setlocale(LC_NUMERIC, oldlocale); + ECPGfree(oldlocale); + + return (status); +} + +/* old descriptor interface */ +bool +ECPGdo_descriptor(int line, const char *connection, + const char *descriptor, const char *query) +{ + return ECPGdo(line, connection, (char *) query, ECPGt_EOIT, + ECPGt_descriptor, descriptor, 0L, 0L, 0L, + ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT); +} |