diff options
Diffstat (limited to '')
-rw-r--r-- | src/interfaces/ecpg/ecpglib/execute.c | 2302 |
1 files changed, 2302 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c new file mode 100644 index 0000000..e8e8fb2 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/execute.c @@ -0,0 +1,2302 @@ +/* src/interfaces/ecpg/ecpglib/execute.c */ + +/* + * The aim is to get a simpler interface to the database routines. + * All the tedious messing around with tuples is supposed to be hidden + * by this function. + */ +/* Author: Linus Tolke + (actually most if the code is "borrowed" from the distribution and just + slightly modified) + */ + +/* Taken over as part of PostgreSQL by Michael Meskes <meskes@postgresql.org> + on Feb. 5th, 1998 */ + +#define POSTGRES_ECPG_INTERNAL +#include "postgres_fe.h" + +#include <math.h> + +#include "catalog/pg_type_d.h" +#include "ecpgerrno.h" +#include "ecpglib.h" +#include "ecpglib_extern.h" +#include "ecpgtype.h" +#include "pgtypes_date.h" +#include "pgtypes_interval.h" +#include "pgtypes_numeric.h" +#include "pgtypes_timestamp.h" +#include "sql3types.h" +#include "sqlca.h" +#include "sqlda-compat.h" +#include "sqlda-native.h" + +/* + * This function returns a newly malloced string that has ' and \ + * escaped. + */ +static char * +quote_postgres(char *arg, bool quote, int lineno) +{ + char *res; + size_t length; + size_t escaped_len; + size_t buffer_len; + + /* + * if quote is false we just need to store things in a descriptor they + * will be quoted once they are inserted in a statement + */ + if (!quote) + return arg; + else + { + length = strlen(arg); + buffer_len = 2 * length + 1; + res = (char *) ecpg_alloc(buffer_len + 3, lineno); + if (!res) + return res; + escaped_len = PQescapeString(res + 1, arg, buffer_len); + if (length == escaped_len) + { + res[0] = res[escaped_len + 1] = '\''; + res[escaped_len + 2] = '\0'; + } + else + { + /* + * We don't know if the target database is using + * standard_conforming_strings, so we always use E'' strings. + */ + memmove(res + 2, res + 1, escaped_len); + res[0] = ESCAPE_STRING_SYNTAX; + res[1] = res[escaped_len + 2] = '\''; + res[escaped_len + 3] = '\0'; + } + ecpg_free(arg); + return res; + } +} + +static void +free_variable(struct variable *var) +{ + struct variable *var_next; + + while (var) + { + var_next = var->next; + ecpg_free(var); + var = var_next; + } +} + +static void +free_statement(struct statement *stmt) +{ + if (stmt == NULL) + return; + free_variable(stmt->inlist); + free_variable(stmt->outlist); + ecpg_free(stmt->command); + ecpg_free(stmt->name); +#ifndef HAVE_USELOCALE + ecpg_free(stmt->oldlocale); +#endif + ecpg_free(stmt); +} + +static int +next_insert(char *text, int pos, bool questionmarks, bool std_strings) +{ + bool string = false; + int p = pos; + + for (; text[p] != '\0'; p++) + { + if (string && !std_strings && text[p] == '\\') /* escape character */ + p++; + else if (text[p] == '\'') + string = string ? false : true; + else if (!string) + { + if (text[p] == '$' && isdigit((unsigned char) text[p + 1])) + { + /* this can be either a dollar quote or a variable */ + int i; + + for (i = p + 1; isdigit((unsigned char) text[i]); i++) + /* empty loop body */ ; + if (!isalpha((unsigned char) text[i]) && + isascii((unsigned char) text[i]) && text[i] != '_') + /* not dollar delimited quote */ + return p; + } + else if (questionmarks && text[p] == '?') + { + /* also allow old style placeholders */ + return p; + } + } + } + + return -1; +} + +static bool +ecpg_type_infocache_push(struct ECPGtype_information_cache **cache, int oid, enum ARRAY_TYPE isarray, int lineno) +{ + struct ECPGtype_information_cache *new_entry + = (struct ECPGtype_information_cache *) ecpg_alloc(sizeof(struct ECPGtype_information_cache), lineno); + + if (new_entry == NULL) + return false; + + new_entry->oid = oid; + new_entry->isarray = isarray; + new_entry->next = *cache; + *cache = new_entry; + return true; +} + +static enum ARRAY_TYPE +ecpg_is_type_an_array(int type, const struct statement *stmt, const struct variable *var) +{ + char *array_query; + enum ARRAY_TYPE isarray = ECPG_ARRAY_NOT_SET; + PGresult *query; + struct ECPGtype_information_cache *cache_entry; + + if ((stmt->connection->cache_head) == NULL) + { + /* + * Text like types are not an array for ecpg, but postgres counts them + * as an array. This define reminds you to not 'correct' these values. + */ +#define not_an_array_in_ecpg ECPG_ARRAY_NONE + + /* populate cache with well known types to speed things up */ + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BOOLOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BYTEAOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CHAROID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), NAMEOID, not_an_array_in_ecpg, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT8OID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT2OID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT2VECTOROID, ECPG_ARRAY_VECTOR, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INT4OID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), REGPROCOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TEXTOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), OIDOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIDOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), XIDOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIDOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), OIDVECTOROID, ECPG_ARRAY_VECTOR, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), POINTOID, ECPG_ARRAY_VECTOR, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), LSEGOID, ECPG_ARRAY_VECTOR, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), PATHOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BOXOID, ECPG_ARRAY_VECTOR, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), POLYGONOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), LINEOID, ECPG_ARRAY_VECTOR, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), FLOAT4OID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), FLOAT8OID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), UNKNOWNOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIRCLEOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), MONEYOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INETOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIDROID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BPCHAROID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), VARCHAROID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), DATEOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMEOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMESTAMPOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMESTAMPTZOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), INTERVALOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIMETZOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), BITOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), VARBITOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), NUMERICOID, ECPG_ARRAY_NONE, stmt->lineno)) + return ECPG_ARRAY_ERROR; + } + + for (cache_entry = (stmt->connection->cache_head); cache_entry != NULL; cache_entry = cache_entry->next) + { + if (cache_entry->oid == type) + return cache_entry->isarray; + } + + array_query = (char *) ecpg_alloc(strlen("select typlen from pg_type where oid= and typelem<>0") + 11, stmt->lineno); + if (array_query == NULL) + return ECPG_ARRAY_ERROR; + + sprintf(array_query, "select typlen from pg_type where oid=%d and typelem<>0", type); + query = PQexec(stmt->connection->connection, array_query); + ecpg_free(array_query); + if (!ecpg_check_PQresult(query, stmt->lineno, stmt->connection->connection, stmt->compat)) + return ECPG_ARRAY_ERROR; + else if (PQresultStatus(query) == PGRES_TUPLES_OK) + { + if (PQntuples(query) == 0) + isarray = ECPG_ARRAY_NONE; + else + { + isarray = (atol((char *) PQgetvalue(query, 0, 0)) == -1) ? ECPG_ARRAY_ARRAY : ECPG_ARRAY_VECTOR; + if (ecpg_dynamic_type(type) == SQL3_CHARACTER || + ecpg_dynamic_type(type) == SQL3_CHARACTER_VARYING) + { + /* + * arrays of character strings are not yet implemented + */ + isarray = ECPG_ARRAY_NONE; + } + } + PQclear(query); + } + else + return ECPG_ARRAY_ERROR; + + ecpg_type_infocache_push(&(stmt->connection->cache_head), type, isarray, stmt->lineno); + ecpg_log("ecpg_is_type_an_array on line %d: type (%d); C (%d); array (%s)\n", stmt->lineno, type, var->type, ECPG_IS_ARRAY(isarray) ? "yes" : "no"); + return isarray; +} + + +bool +ecpg_store_result(const PGresult *results, int act_field, + const struct statement *stmt, struct variable *var) +{ + enum ARRAY_TYPE isarray; + int act_tuple, + ntuples = PQntuples(results); + bool status = true; + + if ((isarray = ecpg_is_type_an_array(PQftype(results, act_field), stmt, var)) == ECPG_ARRAY_ERROR) + { + ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); + return false; + } + + if (isarray == ECPG_ARRAY_NONE) + { + /* + * if we don't have enough space, we cannot read all tuples + */ + if ((var->arrsize > 0 && ntuples > var->arrsize) || (var->ind_arrsize > 0 && ntuples > var->ind_arrsize)) + { + ecpg_log("ecpg_store_result on line %d: incorrect number of matches; %d don't fit into array of %ld\n", + stmt->lineno, ntuples, var->arrsize); + ecpg_raise(stmt->lineno, INFORMIX_MODE(stmt->compat) ? ECPG_INFORMIX_SUBSELECT_NOT_ONE : ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL); + return false; + } + } + else + { + /* + * since we read an array, the variable has to be an array too + */ + if (var->arrsize == 0) + { + ecpg_raise(stmt->lineno, ECPG_NO_ARRAY, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL); + return false; + } + } + + /* + * allocate memory for NULL pointers + */ + if ((var->arrsize == 0 || var->varcharsize == 0) && var->value == NULL) + { + int len = 0; + + if (!PQfformat(results, act_field)) + { + switch (var->type) + { + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_string: + if (!var->varcharsize && !var->arrsize) + { + /* special mode for handling char**foo=0 */ + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + len += strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + len *= var->offset; /* should be 1, but YMNK */ + len += (ntuples + 1) * sizeof(char *); + } + else + { + var->varcharsize = 0; + /* check strlen for each tuple */ + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + { + int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + + if (len > var->varcharsize) + var->varcharsize = len; + } + var->offset *= var->varcharsize; + len = var->offset * ntuples; + } + break; + case ECPGt_varchar: + len = ntuples * (var->varcharsize + sizeof(int)); + break; + default: + len = var->offset * ntuples; + break; + } + } + else + { + for (act_tuple = 0; act_tuple < ntuples; act_tuple++) + len += PQgetlength(results, act_tuple, act_field); + } + + ecpg_log("ecpg_store_result on line %d: allocating memory for %d tuples\n", stmt->lineno, ntuples); + var->value = (char *) ecpg_auto_alloc(len, stmt->lineno); + if (!var->value) + return false; + *((char **) var->pointer) = var->value; + } + + /* allocate indicator variable if needed */ + if ((var->ind_arrsize == 0 || var->ind_varcharsize == 0) && var->ind_value == NULL && var->ind_pointer != NULL) + { + int len = var->ind_offset * ntuples; + + var->ind_value = (char *) ecpg_auto_alloc(len, stmt->lineno); + if (!var->ind_value) + return false; + *((char **) var->ind_pointer) = var->ind_value; + } + + /* fill the variable with the tuple(s) */ + if (!var->varcharsize && !var->arrsize && + (var->type == ECPGt_char || var->type == ECPGt_unsigned_char || var->type == ECPGt_string)) + { + /* special mode for handling char**foo=0 */ + + /* filling the array of (char*)s */ + char **current_string = (char **) var->value; + + /* storing the data (after the last array element) */ + char *current_data_location = (char *) ¤t_string[ntuples + 1]; + + for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) + { + int len = strlen(PQgetvalue(results, act_tuple, act_field)) + 1; + + if (!ecpg_get_data(results, act_tuple, act_field, stmt->lineno, + var->type, var->ind_type, current_data_location, + var->ind_value, len, 0, var->ind_offset, isarray, stmt->compat, stmt->force_indicator)) + status = false; + else + { + *current_string = current_data_location; + current_data_location += len; + current_string++; + } + } + + /* terminate the list */ + *current_string = NULL; + } + else + { + for (act_tuple = 0; act_tuple < ntuples && status; act_tuple++) + { + if (!ecpg_get_data(results, act_tuple, act_field, stmt->lineno, + var->type, var->ind_type, var->value, + var->ind_value, var->varcharsize, var->offset, var->ind_offset, isarray, stmt->compat, stmt->force_indicator)) + status = false; + } + } + return status; +} + +static void +sprintf_double_value(char *ptr, double value, const char *delim) +{ + if (isnan(value)) + sprintf(ptr, "%s%s", "NaN", delim); + else if (isinf(value)) + { + if (value < 0) + sprintf(ptr, "%s%s", "-Infinity", delim); + else + sprintf(ptr, "%s%s", "Infinity", delim); + } + else + sprintf(ptr, "%.15g%s", value, delim); +} + +static void +sprintf_float_value(char *ptr, float value, const char *delim) +{ + if (isnan(value)) + sprintf(ptr, "%s%s", "NaN", delim); + else if (isinf(value)) + { + if (value < 0) + sprintf(ptr, "%s%s", "-Infinity", delim); + else + sprintf(ptr, "%s%s", "Infinity", delim); + } + else + sprintf(ptr, "%.15g%s", value, delim); +} + +static char * +convert_bytea_to_string(char *from_data, int from_len, int lineno) +{ + char *to_data; + int to_len = ecpg_hex_enc_len(from_len) + 4 + 1; /* backslash + 'x' + + * quote + quote */ + + to_data = ecpg_alloc(to_len, lineno); + if (!to_data) + return NULL; + + strcpy(to_data, "'\\x"); + ecpg_hex_encode(from_data, from_len, to_data + 3); + strcpy(to_data + 3 + ecpg_hex_enc_len(from_len), "\'"); + + return to_data; +} + +bool +ecpg_store_input(const int lineno, const bool force_indicator, const struct variable *var, + char **tobeinserted_p, bool quote) +{ + char *mallocedval = NULL; + char *newcopy = NULL; + + /* + * arrays are not possible unless the column is an array, too FIXME: we do + * not know if the column is an array here array input to singleton column + * will result in a runtime error + */ + + /* + * Some special treatment is needed for records since we want their + * contents to arrive in a comma-separated list on insert (I think). + */ + + *tobeinserted_p = ""; + + /* check for null value and set input buffer accordingly */ + switch (var->ind_type) + { + case ECPGt_short: + case ECPGt_unsigned_short: + if (*(short *) var->ind_value < 0) + *tobeinserted_p = NULL; + break; + case ECPGt_int: + case ECPGt_unsigned_int: + if (*(int *) var->ind_value < 0) + *tobeinserted_p = NULL; + break; + case ECPGt_long: + case ECPGt_unsigned_long: + if (*(long *) var->ind_value < 0L) + *tobeinserted_p = NULL; + break; + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + if (*(long long int *) var->ind_value < (long long) 0) + *tobeinserted_p = NULL; + break; + case ECPGt_NO_INDICATOR: + if (force_indicator == false) + { + if (ECPGis_noind_null(var->type, var->value)) + *tobeinserted_p = NULL; + } + break; + default: + break; + } + if (*tobeinserted_p != NULL) + { + int asize = var->arrsize ? var->arrsize : 1; + + switch (var->type) + { + int element; + + case ECPGt_short: + if (!(mallocedval = ecpg_alloc(asize * 20, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%hd,", ((short *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%hd", *((short *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_int: + if (!(mallocedval = ecpg_alloc(asize * 20, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%d,", ((int *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%d", *((int *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_unsigned_short: + if (!(mallocedval = ecpg_alloc(asize * 20, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%hu,", ((unsigned short *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%hu", *((unsigned short *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_unsigned_int: + if (!(mallocedval = ecpg_alloc(asize * 20, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%u,", ((unsigned int *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%u", *((unsigned int *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_long: + if (!(mallocedval = ecpg_alloc(asize * 20, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%ld,", ((long *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%ld", *((long *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_unsigned_long: + if (!(mallocedval = ecpg_alloc(asize * 20, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%lu,", ((unsigned long *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%lu", *((unsigned long *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_long_long: + if (!(mallocedval = ecpg_alloc(asize * 30, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%lld,", ((long long int *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%lld", *((long long int *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_unsigned_long_long: + if (!(mallocedval = ecpg_alloc(asize * 30, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%llu,", ((unsigned long long int *) var->value)[element]); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf(mallocedval, "%llu", *((unsigned long long int *) var->value)); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_float: + if (!(mallocedval = ecpg_alloc(asize * 25, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf_float_value(mallocedval + strlen(mallocedval), ((float *) var->value)[element], ","); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf_float_value(mallocedval, *((float *) var->value), ""); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_double: + if (!(mallocedval = ecpg_alloc(asize * 25, lineno))) + return false; + + if (asize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf_double_value(mallocedval + strlen(mallocedval), ((double *) var->value)[element], ","); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + sprintf_double_value(mallocedval, *((double *) var->value), ""); + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_bool: + if (!(mallocedval = ecpg_alloc(var->arrsize + sizeof("{}"), lineno))) + return false; + + if (var->arrsize > 1) + { + strcpy(mallocedval, "{"); + + for (element = 0; element < asize; element++) + sprintf(mallocedval + strlen(mallocedval), "%c,", (((bool *) var->value)[element]) ? 't' : 'f'); + + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + } + else + { + if (var->offset == sizeof(char)) + sprintf(mallocedval, "%c", (*((char *) var->value)) ? 't' : 'f'); + else if (var->offset == sizeof(int)) + sprintf(mallocedval, "%c", (*((int *) var->value)) ? 't' : 'f'); + else + ecpg_raise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL); + } + + *tobeinserted_p = mallocedval; + break; + + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_string: + { + /* set slen to string length if type is char * */ + int slen = (var->varcharsize == 0) ? strlen((char *) var->value) : (unsigned int) var->varcharsize; + + if (!(newcopy = ecpg_alloc(slen + 1, lineno))) + return false; + + strncpy(newcopy, (char *) var->value, slen); + newcopy[slen] = '\0'; + + mallocedval = quote_postgres(newcopy, quote, lineno); + if (!mallocedval) + { + ecpg_free(newcopy); + return false; + } + + *tobeinserted_p = mallocedval; + } + break; + case ECPGt_const: + case ECPGt_char_variable: + { + int slen = strlen((char *) var->value); + + if (!(mallocedval = ecpg_alloc(slen + 1, lineno))) + return false; + + strncpy(mallocedval, (char *) var->value, slen); + mallocedval[slen] = '\0'; + + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_bytea: + { + struct ECPGgeneric_bytea *variable = + (struct ECPGgeneric_bytea *) (var->value); + + if (!(mallocedval = (char *) ecpg_alloc(variable->len, lineno))) + return false; + + memcpy(mallocedval, variable->arr, variable->len); + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_varchar: + { + struct ECPGgeneric_varchar *variable = + (struct ECPGgeneric_varchar *) (var->value); + + if (!(newcopy = (char *) ecpg_alloc(variable->len + 1, lineno))) + return false; + + strncpy(newcopy, variable->arr, variable->len); + newcopy[variable->len] = '\0'; + + mallocedval = quote_postgres(newcopy, quote, lineno); + if (!mallocedval) + { + ecpg_free(newcopy); + return false; + } + + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_decimal: + case ECPGt_numeric: + { + char *str = NULL; + int slen; + numeric *nval; + + if (var->arrsize > 1) + mallocedval = ecpg_strdup("{", lineno); + else + mallocedval = ecpg_strdup("", lineno); + + if (!mallocedval) + return false; + + for (element = 0; element < asize; element++) + { + int result; + + nval = PGTYPESnumeric_new(); + if (!nval) + { + ecpg_free(mallocedval); + return false; + } + + if (var->type == ECPGt_numeric) + result = PGTYPESnumeric_copy(&(((numeric *) (var->value))[element]), nval); + else + result = PGTYPESnumeric_from_decimal(&(((decimal *) (var->value))[element]), nval); + + if (result != 0) + { + PGTYPESnumeric_free(nval); + ecpg_free(mallocedval); + return false; + } + + str = PGTYPESnumeric_to_asc(nval, nval->dscale); + slen = strlen(str); + PGTYPESnumeric_free(nval); + + if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno))) + { + ecpg_free(mallocedval); + ecpg_free(str); + return false; + } + mallocedval = newcopy; + + /* also copy trailing '\0' */ + memcpy(mallocedval + strlen(mallocedval), str, slen + 1); + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval), ","); + + ecpg_free(str); + } + + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_interval: + { + char *str = NULL; + int slen; + + if (var->arrsize > 1) + mallocedval = ecpg_strdup("{", lineno); + else + mallocedval = ecpg_strdup("", lineno); + + if (!mallocedval) + return false; + + for (element = 0; element < asize; element++) + { + str = quote_postgres(PGTYPESinterval_to_asc(&(((interval *) (var->value))[element])), quote, lineno); + if (!str) + { + ecpg_free(mallocedval); + return false; + } + + slen = strlen(str); + + if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno))) + { + ecpg_free(mallocedval); + ecpg_free(str); + return false; + } + mallocedval = newcopy; + + /* also copy trailing '\0' */ + memcpy(mallocedval + strlen(mallocedval), str, slen + 1); + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval), ","); + + ecpg_free(str); + } + + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_date: + { + char *str = NULL; + int slen; + + if (var->arrsize > 1) + mallocedval = ecpg_strdup("{", lineno); + else + mallocedval = ecpg_strdup("", lineno); + + if (!mallocedval) + return false; + + for (element = 0; element < asize; element++) + { + str = quote_postgres(PGTYPESdate_to_asc(((date *) (var->value))[element]), quote, lineno); + if (!str) + { + ecpg_free(mallocedval); + return false; + } + + slen = strlen(str); + + if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno))) + { + ecpg_free(mallocedval); + ecpg_free(str); + return false; + } + mallocedval = newcopy; + + /* also copy trailing '\0' */ + memcpy(mallocedval + strlen(mallocedval), str, slen + 1); + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval), ","); + + ecpg_free(str); + } + + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_timestamp: + { + char *str = NULL; + int slen; + + if (var->arrsize > 1) + mallocedval = ecpg_strdup("{", lineno); + else + mallocedval = ecpg_strdup("", lineno); + + if (!mallocedval) + return false; + + for (element = 0; element < asize; element++) + { + str = quote_postgres(PGTYPEStimestamp_to_asc(((timestamp *) (var->value))[element]), quote, lineno); + if (!str) + { + ecpg_free(mallocedval); + return false; + } + + slen = strlen(str); + + if (!(newcopy = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno))) + { + ecpg_free(mallocedval); + ecpg_free(str); + return false; + } + mallocedval = newcopy; + + /* also copy trailing '\0' */ + memcpy(mallocedval + strlen(mallocedval), str, slen + 1); + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval), ","); + + ecpg_free(str); + } + + if (var->arrsize > 1) + strcpy(mallocedval + strlen(mallocedval) - 1, "}"); + + *tobeinserted_p = mallocedval; + } + break; + + case ECPGt_descriptor: + case ECPGt_sqlda: + break; + + default: + /* Not implemented yet */ + ecpg_raise(lineno, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, ecpg_type_name(var->type)); + return false; + break; + } + } + return true; +} + +static void +print_param_value(char *value, int len, int is_binary, int lineno, int nth) +{ + char *value_s; + bool malloced = false; + + if (value == NULL) + value_s = "null"; + else if (!is_binary) + value_s = value; + else + { + value_s = ecpg_alloc(ecpg_hex_enc_len(len) + 1, lineno); + if (value_s != NULL) + { + ecpg_hex_encode(value, len, value_s); + value_s[ecpg_hex_enc_len(len)] = '\0'; + malloced = true; + } + else + value_s = "no memory for logging of parameter"; + } + + ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n", + lineno, nth, value_s); + + if (malloced) + ecpg_free(value_s); +} + +void +ecpg_free_params(struct statement *stmt, bool print) +{ + int n; + + for (n = 0; n < stmt->nparams; n++) + { + if (print) + print_param_value(stmt->paramvalues[n], stmt->paramlengths[n], + stmt->paramformats[n], stmt->lineno, n + 1); + ecpg_free(stmt->paramvalues[n]); + } + ecpg_free(stmt->paramvalues); + ecpg_free(stmt->paramlengths); + ecpg_free(stmt->paramformats); + stmt->paramvalues = NULL; + stmt->paramlengths = NULL; + stmt->paramformats = NULL; + stmt->nparams = 0; +} + +static bool +insert_tobeinserted(int position, int ph_len, struct statement *stmt, char *tobeinserted) +{ + char *newcopy; + + if (!(newcopy = (char *) ecpg_alloc(strlen(stmt->command) + + strlen(tobeinserted) + + 1, stmt->lineno))) + { + ecpg_free(tobeinserted); + return false; + } + + strcpy(newcopy, stmt->command); + strcpy(newcopy + position - 1, tobeinserted); + + /* + * The strange thing in the second argument is the rest of the string from + * the old string + */ + strcat(newcopy, + stmt->command + + position + + ph_len - 1); + + ecpg_free(stmt->command); + stmt->command = newcopy; + + ecpg_free(tobeinserted); + return true; +} + +static bool +store_input_from_desc(struct statement *stmt, struct descriptor_item *desc_item, + char **tobeinserted) +{ + struct variable var; + + /* + * In case of binary data, only allocate memory and memcpy because binary + * data have been already stored into desc_item->data with + * ecpg_store_input() at ECPGset_desc(). + */ + if (desc_item->is_binary) + { + if (!(*tobeinserted = ecpg_alloc(desc_item->data_len, stmt->lineno))) + return false; + memcpy(*tobeinserted, desc_item->data, desc_item->data_len); + return true; + } + + var.type = ECPGt_char; + var.varcharsize = strlen(desc_item->data); + var.value = desc_item->data; + var.pointer = &(desc_item->data); + var.arrsize = 1; + var.offset = 0; + + if (!desc_item->indicator) + { + var.ind_type = ECPGt_NO_INDICATOR; + var.ind_value = var.ind_pointer = NULL; + var.ind_varcharsize = var.ind_arrsize = var.ind_offset = 0; + } + else + { + var.ind_type = ECPGt_int; + var.ind_value = &(desc_item->indicator); + var.ind_pointer = &(var.ind_value); + var.ind_varcharsize = var.ind_arrsize = 1; + var.ind_offset = 0; + } + + if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &var, tobeinserted, false)) + return false; + + return true; +} + +/* + * ecpg_build_params + * Build statement parameters + * + * The input values are taken from user variables, and the results are stored + * in arrays which can be used by PQexecParams(). + */ +bool +ecpg_build_params(struct statement *stmt) +{ + struct variable *var; + int desc_counter = 0; + int position = 0; + const char *value; + bool std_strings = false; + + /* Get standard_conforming_strings setting. */ + value = PQparameterStatus(stmt->connection->connection, "standard_conforming_strings"); + if (value && strcmp(value, "on") == 0) + std_strings = true; + + /* + * If the type is one of the fill in types then we take the argument and + * enter it to our parameter array at the first position. Then if there + * are any more fill in types we add more parameters. + */ + var = stmt->inlist; + while (var) + { + char *tobeinserted; + int counter = 1; + bool binary_format; + int binary_length; + + + tobeinserted = NULL; + binary_length = 0; + binary_format = false; + + /* + * A descriptor is a special case since it contains many variables but + * is listed only once. + */ + if (var->type == ECPGt_descriptor) + { + /* + * We create an additional variable list here, so the same logic + * applies. + */ + struct descriptor *desc; + struct descriptor_item *desc_item; + + desc = ecpg_find_desc(stmt->lineno, var->pointer); + if (desc == NULL) + return false; + + desc_counter++; + for (desc_item = desc->items; desc_item; desc_item = desc_item->next) + { + if (desc_item->num != desc_counter) + continue; + + if (!store_input_from_desc(stmt, desc_item, &tobeinserted)) + return false; + + if (desc_item->is_binary) + { + binary_length = desc_item->data_len; + binary_format = true; + } + break; + } + if (desc->count == desc_counter) + desc_counter = 0; + } + else if (var->type == ECPGt_sqlda) + { + if (INFORMIX_MODE(stmt->compat)) + { + struct sqlda_compat *sqlda = *(struct sqlda_compat **) var->pointer; + struct variable desc_inlist; + int i; + + if (sqlda == NULL) + return false; + + desc_counter++; + for (i = 0; i < sqlda->sqld; i++) + { + if (i + 1 == desc_counter) + { + desc_inlist.type = sqlda->sqlvar[i].sqltype; + desc_inlist.value = sqlda->sqlvar[i].sqldata; + desc_inlist.pointer = &(sqlda->sqlvar[i].sqldata); + switch (desc_inlist.type) + { + case ECPGt_char: + case ECPGt_varchar: + desc_inlist.varcharsize = strlen(sqlda->sqlvar[i].sqldata); + break; + default: + desc_inlist.varcharsize = 0; + break; + } + desc_inlist.arrsize = 1; + desc_inlist.offset = 0; + if (sqlda->sqlvar[i].sqlind) + { + desc_inlist.ind_type = ECPGt_short; + /* ECPG expects indicator value < 0 */ + if (*(sqlda->sqlvar[i].sqlind)) + *(sqlda->sqlvar[i].sqlind) = -1; + desc_inlist.ind_value = sqlda->sqlvar[i].sqlind; + desc_inlist.ind_pointer = &(sqlda->sqlvar[i].sqlind); + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1; + desc_inlist.ind_offset = 0; + } + else + { + desc_inlist.ind_type = ECPGt_NO_INDICATOR; + desc_inlist.ind_value = desc_inlist.ind_pointer = NULL; + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0; + } + if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false)) + return false; + + break; + } + } + if (sqlda->sqld == desc_counter) + desc_counter = 0; + } + else + { + struct sqlda_struct *sqlda = *(struct sqlda_struct **) var->pointer; + struct variable desc_inlist; + int i; + + if (sqlda == NULL) + return false; + + desc_counter++; + for (i = 0; i < sqlda->sqln; i++) + { + if (i + 1 == desc_counter) + { + desc_inlist.type = sqlda->sqlvar[i].sqltype; + desc_inlist.value = sqlda->sqlvar[i].sqldata; + desc_inlist.pointer = &(sqlda->sqlvar[i].sqldata); + switch (desc_inlist.type) + { + case ECPGt_char: + case ECPGt_varchar: + desc_inlist.varcharsize = strlen(sqlda->sqlvar[i].sqldata); + break; + default: + desc_inlist.varcharsize = 0; + break; + } + desc_inlist.arrsize = 1; + desc_inlist.offset = 0; + if (sqlda->sqlvar[i].sqlind) + { + desc_inlist.ind_type = ECPGt_short; + /* ECPG expects indicator value < 0 */ + if (*(sqlda->sqlvar[i].sqlind)) + *(sqlda->sqlvar[i].sqlind) = -1; + desc_inlist.ind_value = sqlda->sqlvar[i].sqlind; + desc_inlist.ind_pointer = &(sqlda->sqlvar[i].sqlind); + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1; + desc_inlist.ind_offset = 0; + } + else + { + desc_inlist.ind_type = ECPGt_NO_INDICATOR; + desc_inlist.ind_value = desc_inlist.ind_pointer = NULL; + desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = desc_inlist.ind_offset = 0; + } + if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false)) + return false; + + break; + } + } + if (sqlda->sqln == desc_counter) + desc_counter = 0; + } + + } + else + { + if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, var, &tobeinserted, false)) + return false; + + if (var->type == ECPGt_bytea) + { + binary_length = ((struct ECPGgeneric_bytea *) (var->value))->len; + binary_format = true; + } + } + + /* + * now tobeinserted points to an area that contains the next + * parameter; now find the position in the string where it belongs + */ + if ((position = next_insert(stmt->command, position, stmt->questionmarks, std_strings) + 1) == 0) + { + /* + * We have an argument but we don't have the matched up + * placeholder in the string + */ + ecpg_raise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, + ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, + NULL); + ecpg_free_params(stmt, false); + ecpg_free(tobeinserted); + return false; + } + + /* + * if var->type=ECPGt_char_variable we have a dynamic cursor we have + * to simulate a dynamic cursor because there is no backend + * functionality for it + */ + if (var->type == ECPGt_char_variable) + { + int ph_len = (stmt->command[position] == '?') ? strlen("?") : strlen("$1"); + + if (!insert_tobeinserted(position, ph_len, stmt, tobeinserted)) + { + ecpg_free_params(stmt, false); + return false; + } + tobeinserted = NULL; + } + + /* + * if the placeholder is '$0' we have to replace it on the client side + * this is for places we want to support variables at that are not + * supported in the backend + */ + else if (stmt->command[position] == '0') + { + if (stmt->statement_type == ECPGst_prepare || + stmt->statement_type == ECPGst_exec_with_exprlist) + { + /* Need to double-quote the inserted statement name. */ + char *str = ecpg_alloc(strlen(tobeinserted) + 2 + 1, + stmt->lineno); + + if (!str) + { + ecpg_free(tobeinserted); + ecpg_free_params(stmt, false); + return false; + } + sprintf(str, "\"%s\"", tobeinserted); + ecpg_free(tobeinserted); + tobeinserted = str; + } + + if (!insert_tobeinserted(position, 2, stmt, tobeinserted)) + { + ecpg_free_params(stmt, false); + return false; + } + tobeinserted = NULL; + } + else if (stmt->statement_type == ECPGst_exec_with_exprlist) + { + if (binary_format) + { + char *p = convert_bytea_to_string(tobeinserted, + binary_length, + stmt->lineno); + + ecpg_free(tobeinserted); + if (!p) + { + ecpg_free_params(stmt, false); + return false; + } + tobeinserted = p; + } + + if (!insert_tobeinserted(position, 2, stmt, tobeinserted)) + { + ecpg_free_params(stmt, false); + return false; + } + tobeinserted = NULL; + } + else + { + bool realloc_failed = false; + char **newparamvalues; + int *newparamlengths; + int *newparamformats; + + /* enlarge all the param arrays */ + if ((newparamvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno))) + stmt->paramvalues = newparamvalues; + else + realloc_failed = true; + + if ((newparamlengths = (int *) ecpg_realloc(stmt->paramlengths, sizeof(int) * (stmt->nparams + 1), stmt->lineno))) + stmt->paramlengths = newparamlengths; + else + realloc_failed = true; + + if ((newparamformats = (int *) ecpg_realloc(stmt->paramformats, sizeof(int) * (stmt->nparams + 1), stmt->lineno))) + stmt->paramformats = newparamformats; + else + realloc_failed = true; + + if (realloc_failed) + { + ecpg_free_params(stmt, false); + ecpg_free(tobeinserted); + return false; + } + + /* only now can we assign ownership of "tobeinserted" to stmt */ + stmt->paramvalues[stmt->nparams] = tobeinserted; + stmt->paramlengths[stmt->nparams] = binary_length; + stmt->paramformats[stmt->nparams] = (binary_format ? 1 : 0); + stmt->nparams++; + + /* let's see if this was an old style placeholder */ + if (stmt->command[position] == '?') + { + /* yes, replace with new style */ + int buffersize = sizeof(int) * CHAR_BIT * 10 / 3; /* a rough guess of the + * size we need */ + + if (!(tobeinserted = (char *) ecpg_alloc(buffersize, stmt->lineno))) + { + ecpg_free_params(stmt, false); + return false; + } + + snprintf(tobeinserted, buffersize, "$%d", counter++); + + if (!insert_tobeinserted(position, 2, stmt, tobeinserted)) + { + ecpg_free_params(stmt, false); + return false; + } + tobeinserted = NULL; + } + } + + if (desc_counter == 0) + var = var->next; + } + + /* + * Check if there are unmatched things left. PREPARE AS has no parameter. + * Check other statement. + */ + if (stmt->statement_type != ECPGst_prepare && + next_insert(stmt->command, position, stmt->questionmarks, std_strings) >= 0) + { + ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, + ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL); + ecpg_free_params(stmt, false); + return false; + } + + return true; +} + +/* + * ecpg_autostart_transaction + * If we are in non-autocommit mode, automatically start a transaction. + */ +bool +ecpg_autostart_transaction(struct statement *stmt) +{ + if (PQtransactionStatus(stmt->connection->connection) == PQTRANS_IDLE && !stmt->connection->autocommit) + { + stmt->results = PQexec(stmt->connection->connection, "begin transaction"); + if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat)) + { + ecpg_free_params(stmt, false); + return false; + } + PQclear(stmt->results); + stmt->results = NULL; + } + return true; +} + +/* + * ecpg_execute + * Execute the SQL statement. + */ +bool +ecpg_execute(struct statement *stmt) +{ + ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, stmt->nparams, stmt->connection->name); + if (stmt->statement_type == ECPGst_execute) + { + stmt->results = PQexecPrepared(stmt->connection->connection, + stmt->name, + stmt->nparams, + (const char *const *) stmt->paramvalues, + (const int *) stmt->paramlengths, + (const int *) stmt->paramformats, + 0); + ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command); + } + else + { + if (stmt->nparams == 0) + { + stmt->results = PQexec(stmt->connection->connection, stmt->command); + ecpg_log("ecpg_execute on line %d: using PQexec\n", stmt->lineno); + } + else + { + stmt->results = PQexecParams(stmt->connection->connection, + stmt->command, stmt->nparams, NULL, + (const char *const *) stmt->paramvalues, + (const int *) stmt->paramlengths, + (const int *) stmt->paramformats, + 0); + + ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno); + } + + if (stmt->statement_type == ECPGst_prepare) + { + if (!ecpg_register_prepared_stmt(stmt)) + { + ecpg_free_params(stmt, true); + return false; + } + } + } + + ecpg_free_params(stmt, true); + + if (!ecpg_check_PQresult(stmt->results, stmt->lineno, stmt->connection->connection, stmt->compat)) + return false; + + return true; +} + +/*------- + * ecpg_process_output + * + * Process the statement result and store it into application variables. This + * function can be called repeatedly during the same statement in case cursor + * readahead is used and the application does FETCH N which overflows the + * readahead window. + * + * Parameters + * stmt statement structure holding the PGresult and + * the list of output variables + * clear_result + * PQclear() the result upon returning from this function + * + * Returns success as boolean. Also an SQL error is raised in case of failure. + *------- + */ +bool +ecpg_process_output(struct statement *stmt, bool clear_result) +{ + struct variable *var; + bool status = false; + char *cmdstat; + PGnotify *notify; + struct sqlca_t *sqlca = ECPGget_sqlca(); + int nfields, + ntuples, + act_field; + + if (sqlca == NULL) + { + ecpg_raise(stmt->lineno, ECPG_OUT_OF_MEMORY, + ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL); + return false; + } + + var = stmt->outlist; + switch (PQresultStatus(stmt->results)) + { + case PGRES_TUPLES_OK: + nfields = PQnfields(stmt->results); + sqlca->sqlerrd[2] = ntuples = PQntuples(stmt->results); + + ecpg_log("ecpg_process_output on line %d: correctly got %d tuples with %d fields\n", stmt->lineno, ntuples, nfields); + status = true; + + if (ntuples < 1) + { + if (ntuples) + ecpg_log("ecpg_process_output on line %d: incorrect number of matches (%d)\n", + stmt->lineno, ntuples); + ecpg_raise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); + status = false; + break; + } + + if (var != NULL && var->type == ECPGt_descriptor) + { + struct descriptor *desc = ecpg_find_desc(stmt->lineno, var->pointer); + + if (desc == NULL) + status = false; + else + { + if (desc->result) + PQclear(desc->result); + desc->result = stmt->results; + clear_result = false; + ecpg_log("ecpg_process_output on line %d: putting result (%d tuples) into descriptor %s\n", + stmt->lineno, PQntuples(stmt->results), (const char *) var->pointer); + } + var = var->next; + } + else if (var != NULL && var->type == ECPGt_sqlda) + { + if (INFORMIX_MODE(stmt->compat)) + { + struct sqlda_compat **_sqlda = (struct sqlda_compat **) var->pointer; + struct sqlda_compat *sqlda = *_sqlda; + struct sqlda_compat *sqlda_new; + int i; + + /* + * If we are passed in a previously existing sqlda (chain) + * then free it. + */ + while (sqlda) + { + sqlda_new = sqlda->desc_next; + free(sqlda); + sqlda = sqlda_new; + } + *_sqlda = sqlda = sqlda_new = NULL; + for (i = ntuples - 1; i >= 0; i--) + { + /* + * Build a new sqlda structure. Note that only + * fetching 1 record is supported + */ + sqlda_new = ecpg_build_compat_sqlda(stmt->lineno, stmt->results, i, stmt->compat); + + if (!sqlda_new) + { + /* cleanup all SQLDAs we created up */ + while (sqlda) + { + sqlda_new = sqlda->desc_next; + free(sqlda); + sqlda = sqlda_new; + } + *_sqlda = NULL; + + ecpg_log("ecpg_process_output on line %d: out of memory allocating a new sqlda\n", stmt->lineno); + status = false; + break; + } + else + { + ecpg_log("ecpg_process_output on line %d: new sqlda was built\n", stmt->lineno); + + *_sqlda = sqlda_new; + + ecpg_set_compat_sqlda(stmt->lineno, _sqlda, stmt->results, i, stmt->compat); + ecpg_log("ecpg_process_output on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n", + stmt->lineno, PQnfields(stmt->results)); + + sqlda_new->desc_next = sqlda; + sqlda = sqlda_new; + } + } + } + else + { + struct sqlda_struct **_sqlda = (struct sqlda_struct **) var->pointer; + struct sqlda_struct *sqlda = *_sqlda; + struct sqlda_struct *sqlda_new; + int i; + + /* + * If we are passed in a previously existing sqlda (chain) + * then free it. + */ + while (sqlda) + { + sqlda_new = sqlda->desc_next; + free(sqlda); + sqlda = sqlda_new; + } + *_sqlda = sqlda = sqlda_new = NULL; + for (i = ntuples - 1; i >= 0; i--) + { + /* + * Build a new sqlda structure. Note that only + * fetching 1 record is supported + */ + sqlda_new = ecpg_build_native_sqlda(stmt->lineno, stmt->results, i, stmt->compat); + + if (!sqlda_new) + { + /* cleanup all SQLDAs we created up */ + while (sqlda) + { + sqlda_new = sqlda->desc_next; + free(sqlda); + sqlda = sqlda_new; + } + *_sqlda = NULL; + + ecpg_log("ecpg_process_output on line %d: out of memory allocating a new sqlda\n", stmt->lineno); + status = false; + break; + } + else + { + ecpg_log("ecpg_process_output on line %d: new sqlda was built\n", stmt->lineno); + + *_sqlda = sqlda_new; + + ecpg_set_native_sqlda(stmt->lineno, _sqlda, stmt->results, i, stmt->compat); + ecpg_log("ecpg_process_output on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n", + stmt->lineno, PQnfields(stmt->results)); + + sqlda_new->desc_next = sqlda; + sqlda = sqlda_new; + } + } + } + + var = var->next; + } + else + for (act_field = 0; act_field < nfields && status; act_field++) + { + if (var != NULL) + { + status = ecpg_store_result(stmt->results, act_field, stmt, var); + var = var->next; + } + else if (!INFORMIX_MODE(stmt->compat)) + { + ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); + return false; + } + } + + if (status && var != NULL) + { + ecpg_raise(stmt->lineno, ECPG_TOO_MANY_ARGUMENTS, ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_TARGETS, NULL); + status = false; + } + + break; + case PGRES_COMMAND_OK: + status = true; + cmdstat = PQcmdStatus(stmt->results); + sqlca->sqlerrd[1] = PQoidValue(stmt->results); + sqlca->sqlerrd[2] = atol(PQcmdTuples(stmt->results)); + ecpg_log("ecpg_process_output on line %d: OK: %s\n", stmt->lineno, cmdstat); + if (stmt->compat != ECPG_COMPAT_INFORMIX_SE && + !sqlca->sqlerrd[2] && + (strncmp(cmdstat, "UPDATE", 6) == 0 + || strncmp(cmdstat, "INSERT", 6) == 0 + || strncmp(cmdstat, "DELETE", 6) == 0)) + ecpg_raise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); + break; + case PGRES_COPY_OUT: + { + char *buffer; + int res; + + ecpg_log("ecpg_process_output on line %d: COPY OUT data transfer in progress\n", stmt->lineno); + while ((res = PQgetCopyData(stmt->connection->connection, + &buffer, 0)) > 0) + { + printf("%s", buffer); + PQfreemem(buffer); + } + if (res == -1) + { + /* COPY done */ + PQclear(stmt->results); + stmt->results = PQgetResult(stmt->connection->connection); + if (PQresultStatus(stmt->results) == PGRES_COMMAND_OK) + ecpg_log("ecpg_process_output on line %d: got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", stmt->lineno); + else + ecpg_log("ecpg_process_output on line %d: got error after PGRES_COPY_OUT: %s", stmt->lineno, PQresultErrorMessage(stmt->results)); + } + break; + } + default: + + /* + * execution should never reach this code because it is already + * handled in ecpg_check_PQresult() + */ + ecpg_log("ecpg_process_output on line %d: unknown execution status type\n", + stmt->lineno); + ecpg_raise_backend(stmt->lineno, stmt->results, stmt->connection->connection, stmt->compat); + status = false; + break; + } + + if (clear_result) + { + PQclear(stmt->results); + stmt->results = NULL; + } + + /* check for asynchronous returns */ + PQconsumeInput(stmt->connection->connection); + while ((notify = PQnotifies(stmt->connection->connection)) != NULL) + { + ecpg_log("ecpg_process_output on line %d: asynchronous notification of \"%s\" from backend PID %d received\n", + stmt->lineno, notify->relname, notify->be_pid); + PQfreemem(notify); + PQconsumeInput(stmt->connection->connection); + } + + return status; +} + +/* + * ecpg_do_prologue + * + * Initialize various infrastructure elements for executing the statement: + * + * - create the statement structure + * - set the C numeric locale for communicating with the backend + * - preprocess the variable list of input/output parameters into + * linked lists + */ +bool +ecpg_do_prologue(int lineno, const int compat, const int force_indicator, + const char *connection_name, const bool questionmarks, + enum ECPG_statement_type statement_type, const char *query, + va_list args, struct statement **stmt_out) +{ + struct statement *stmt = NULL; + struct connection *con; + enum ECPGttype type; + struct variable **list; + char *prepname; + bool is_prepared_name_set; + + *stmt_out = NULL; + + if (!query) + { + ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL); + return false; + } + +#ifdef ENABLE_THREAD_SAFETY + ecpg_pthreads_init(); +#endif + + con = ecpg_get_connection(connection_name); + + if (!ecpg_init(con, connection_name, lineno)) + return false; + + stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno); + + if (stmt == NULL) + return false; + + /* + * Make sure we do NOT honor the locale for numeric input/output since the + * database wants the standard decimal point. If available, use + * uselocale() for this because it's thread-safe. Windows doesn't have + * that, but it usually does have _configthreadlocale(). In some versions + * of MinGW, _configthreadlocale() exists but always returns -1 --- so + * treat that situation as if the function doesn't exist. + */ +#ifdef HAVE_USELOCALE + + /* + * Since ecpg_init() succeeded, we have a connection. Any successful + * connection initializes ecpg_clocale. + */ + Assert(ecpg_clocale); + stmt->oldlocale = uselocale(ecpg_clocale); + if (stmt->oldlocale == (locale_t) 0) + { + ecpg_do_epilogue(stmt); + return false; + } +#else +#ifdef HAVE__CONFIGTHREADLOCALE + stmt->oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); +#endif + stmt->oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno); + if (stmt->oldlocale == NULL) + { + ecpg_do_epilogue(stmt); + return false; + } + setlocale(LC_NUMERIC, "C"); +#endif + + /* + * If statement type is ECPGst_prepnormal we are supposed to prepare the + * statement before executing them + */ + if (statement_type == ECPGst_prepnormal) + { + if (!ecpg_auto_prepare(lineno, connection_name, compat, &prepname, query)) + { + ecpg_do_epilogue(stmt); + return false; + } + + /* + * statement is now prepared, so instead of the query we have to + * execute the name + */ + stmt->command = prepname; + statement_type = ECPGst_execute; + } + else + stmt->command = ecpg_strdup(query, lineno); + + stmt->name = NULL; + + if (statement_type == ECPGst_execute) + { + /* if we have an EXECUTE command, only the name is send */ + char *command = ecpg_prepared(stmt->command, con); + + if (command) + { + stmt->name = stmt->command; + stmt->command = ecpg_strdup(command, lineno); + } + else + { + ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt->command); + ecpg_do_epilogue(stmt); + return false; + } + } + /* name of PREPARE AS will be set in loop of inlist */ + + stmt->connection = con; + stmt->lineno = lineno; + stmt->compat = compat; + stmt->force_indicator = force_indicator; + stmt->questionmarks = questionmarks; + stmt->statement_type = statement_type; + + /*------ + * create a list of variables + * + * The variables are listed with input variables preceding output + * variables. The end of each group is marked by an end marker. + * Per variable we list: + * + * type - as defined in ecpgtype.h + * value - where to store the data + * varcharsize - length of string in case we have a stringvariable, else 0 + * arraysize - 0 for pointer (we don't know the size of the array), 1 for + * simple variable, size for arrays + * offset - offset between ith and (i+1)th entry in an array, normally + * that means sizeof(type) + * ind_type - type of indicator variable + * ind_pointer - pointer to indicator variable + * ind_varcharsize - empty + * ind_arrsize - arraysize of indicator array + * ind_offset - indicator offset + *------ + */ + + is_prepared_name_set = false; + + list = &(stmt->inlist); + + type = va_arg(args, enum ECPGttype); + + while (type != ECPGt_EORT) + { + if (type == ECPGt_EOIT) + list = &(stmt->outlist); + else + { + struct variable *var, + *ptr; + + if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno))) + { + ecpg_do_epilogue(stmt); + return false; + } + + var->type = type; + var->pointer = va_arg(args, char *); + + var->varcharsize = va_arg(args, long); + var->arrsize = va_arg(args, long); + var->offset = va_arg(args, long); + + /* + * Unknown array size means pointer to an array. Unknown + * varcharsize usually also means pointer. But if the type is + * character and the array size is known, it is an array of + * pointers to char, so use var->pointer as it is. + */ + if (var->arrsize == 0 || + (var->varcharsize == 0 && ((var->type != ECPGt_char && var->type != ECPGt_unsigned_char) || (var->arrsize <= 1)))) + var->value = *((char **) (var->pointer)); + else + var->value = var->pointer; + + /* + * negative values are used to indicate an array without given + * bounds + */ + /* reset to zero for us */ + if (var->arrsize < 0) + var->arrsize = 0; + if (var->varcharsize < 0) + var->varcharsize = 0; + + var->next = NULL; + + var->ind_type = va_arg(args, enum ECPGttype); + var->ind_pointer = va_arg(args, char *); + var->ind_varcharsize = va_arg(args, long); + var->ind_arrsize = va_arg(args, long); + var->ind_offset = va_arg(args, long); + + if (var->ind_type != ECPGt_NO_INDICATOR + && (var->ind_arrsize == 0 || var->ind_varcharsize == 0)) + var->ind_value = *((char **) (var->ind_pointer)); + else + var->ind_value = var->ind_pointer; + + /* + * negative values are used to indicate an array without given + * bounds + */ + /* reset to zero for us */ + if (var->ind_arrsize < 0) + var->ind_arrsize = 0; + if (var->ind_varcharsize < 0) + var->ind_varcharsize = 0; + + /* if variable is NULL, the statement hasn't been prepared */ + if (var->pointer == NULL) + { + ecpg_raise(lineno, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, NULL); + ecpg_free(var); + ecpg_do_epilogue(stmt); + return false; + } + + for (ptr = *list; ptr && ptr->next; ptr = ptr->next) + ; + + if (ptr == NULL) + *list = var; + else + ptr->next = var; + + if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare) + { + stmt->name = ecpg_strdup(var->value, lineno); + is_prepared_name_set = true; + } + } + + type = va_arg(args, enum ECPGttype); + } + + /* are we connected? */ + if (con == NULL || con->connection == NULL) + { + ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>")); + ecpg_do_epilogue(stmt); + return false; + } + + if (!is_prepared_name_set && stmt->statement_type == ECPGst_prepare) + { + ecpg_raise(lineno, ECPG_TOO_FEW_ARGUMENTS, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>")); + ecpg_do_epilogue(stmt); + return false; + } + + /* initialize auto_mem struct */ + ecpg_clear_auto_mem(); + + *stmt_out = stmt; + + return true; +} + +/* + * ecpg_do_epilogue + * Restore the application locale and free the statement structure. + */ +void +ecpg_do_epilogue(struct statement *stmt) +{ + if (stmt == NULL) + return; + +#ifdef HAVE_USELOCALE + if (stmt->oldlocale != (locale_t) 0) + uselocale(stmt->oldlocale); +#else + if (stmt->oldlocale) + setlocale(LC_NUMERIC, stmt->oldlocale); +#ifdef HAVE__CONFIGTHREADLOCALE + + /* + * This is a bit trickier than it looks: if we failed partway through + * statement initialization, oldthreadlocale could still be 0. But that's + * okay because a call with 0 is defined to be a no-op. + */ + if (stmt->oldthreadlocale != -1) + (void) _configthreadlocale(stmt->oldthreadlocale); +#endif +#endif + + free_statement(stmt); +} + +/* + * Execute SQL statements in the backend. + * The input/output parameters (variable argument list) are passed + * in a va_list, so other functions can use this interface. + */ +bool +ecpg_do(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query, va_list args) +{ + struct statement *stmt = NULL; + + if (!ecpg_do_prologue(lineno, compat, force_indicator, connection_name, + questionmarks, (enum ECPG_statement_type) st, + query, args, &stmt)) + goto fail; + + if (!ecpg_build_params(stmt)) + goto fail; + + if (!ecpg_autostart_transaction(stmt)) + goto fail; + + if (!ecpg_execute(stmt)) + goto fail; + + if (!ecpg_process_output(stmt, true)) + goto fail; + + ecpg_do_epilogue(stmt); + return true; + +fail: + ecpg_do_epilogue(stmt); + return false; +} + +/* + * Execute SQL statements in the backend. + * The input/output parameters are passed as variable-length argument list. + */ +bool +ECPGdo(const int lineno, const int compat, const int force_indicator, const char *connection_name, const bool questionmarks, const int st, const char *query,...) +{ + va_list args; + bool ret; + + va_start(args, query); + ret = ecpg_do(lineno, compat, force_indicator, connection_name, + questionmarks, st, query, args); + va_end(args); + + return ret; +} + +/* old descriptor interface */ +bool +ECPGdo_descriptor(int line, const char *connection, + const char *descriptor, const char *query) +{ + return ECPGdo(line, ECPG_COMPAT_PGSQL, true, connection, '\0', 0, query, ECPGt_EOIT, + ECPGt_descriptor, descriptor, 0L, 0L, 0L, + ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT); +} |