
Add __attribute__ decorations for printf format checking to the places that were missing them. Fix the resulting warnings. Add -Wmissing-format-attribute to the standard set of warnings for GCC, so these don't happen again. The warning fixes here are relatively harmless. The one serious problem discovered by this was already committed earlier in cf15fb5cabfbc71e07be23cfbc813daee6c5014f.
1947 lines
54 KiB
C
1947 lines
54 KiB
C
/* src/interfaces/ecpg/ecpglib/execute.c */
|
|
|
|
/*
|
|
* 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 */
|
|
|
|
#define POSTGRES_ECPG_INTERNAL
|
|
#include "postgres_fe.h"
|
|
|
|
#include <locale.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
|
|
#include "pg_type.h"
|
|
|
|
#include "ecpgtype.h"
|
|
#include "ecpglib.h"
|
|
#include "ecpgerrno.h"
|
|
#include "extern.h"
|
|
#include "sqlca.h"
|
|
#include "sqlda-native.h"
|
|
#include "sqlda-compat.h"
|
|
#include "sql3types.h"
|
|
#include "pgtypes_numeric.h"
|
|
#include "pgtypes_date.h"
|
|
#include "pgtypes_timestamp.h"
|
|
#include "pgtypes_interval.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;
|
|
|
|
if (var == NULL)
|
|
return;
|
|
var_next = var->next;
|
|
ecpg_free(var);
|
|
|
|
while (var_next)
|
|
{
|
|
var = var_next;
|
|
var_next = var->next;
|
|
ecpg_free(var);
|
|
}
|
|
}
|
|
|
|
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);
|
|
ecpg_free(stmt);
|
|
}
|
|
|
|
static int
|
|
next_insert(char *text, int pos, bool questionmarks)
|
|
{
|
|
bool string = false;
|
|
int p = pos;
|
|
|
|
for (; text[p] != '\0'; p++)
|
|
{
|
|
if (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, bool 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), ABSTIMEOID, ECPG_ARRAY_NONE, stmt->lineno))
|
|
return (ECPG_ARRAY_ERROR);
|
|
if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), RELTIMEOID, ECPG_ARRAY_NONE, stmt->lineno))
|
|
return (ECPG_ARRAY_ERROR);
|
|
if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TINTERVALOID, 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), CASHOID, 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), ZPBITOID, 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_alloc(len, stmt->lineno);
|
|
if (!var->value)
|
|
return false;
|
|
*((char **) var->pointer) = var->value;
|
|
ecpg_add_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 *) ecpg_alloc(len, stmt->lineno);
|
|
if (!var->ind_value)
|
|
return false;
|
|
*((char **) var->ind_pointer) = var->ind_value;
|
|
ecpg_add_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 || 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);
|
|
}
|
|
|
|
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 attribute is an array too FIXME: we
|
|
* do not know if the attribute is an array here
|
|
*/
|
|
#if 0
|
|
if (var->arrsize > 1 &&...)
|
|
{
|
|
ecpg_raise(lineno, ECPG_ARRAY_INSERT, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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;
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
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 */
|
|
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, "array [");
|
|
|
|
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, "array [");
|
|
|
|
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, "array [");
|
|
|
|
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, "array [");
|
|
|
|
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, "array [");
|
|
|
|
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;
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
case ECPGt_long_long:
|
|
if (!(mallocedval = ecpg_alloc(asize * 30, lineno)))
|
|
return false;
|
|
|
|
if (asize > 1)
|
|
{
|
|
strcpy(mallocedval, "array [");
|
|
|
|
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, "array [");
|
|
|
|
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;
|
|
#endif /* HAVE_LONG_LONG_INT */
|
|
case ECPGt_float:
|
|
if (!(mallocedval = ecpg_alloc(asize * 25, lineno)))
|
|
return false;
|
|
|
|
if (asize > 1)
|
|
{
|
|
strcpy(mallocedval, "array [");
|
|
|
|
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, "array [");
|
|
|
|
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("array []"), lineno)))
|
|
return false;
|
|
|
|
if (var->arrsize > 1)
|
|
{
|
|
strcpy(mallocedval, "array [");
|
|
|
|
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
|
|
ecpg_raise(lineno, ECPG_CONVERT_BOOL, ECPG_SQLSTATE_DATATYPE_MISMATCH, NULL);
|
|
|
|
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)
|
|
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_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)
|
|
return false;
|
|
|
|
*tobeinserted_p = mallocedval;
|
|
}
|
|
break;
|
|
|
|
case ECPGt_decimal:
|
|
case ECPGt_numeric:
|
|
{
|
|
char *str = NULL;
|
|
int slen;
|
|
numeric *nval;
|
|
|
|
if (var->arrsize > 1)
|
|
{
|
|
if (!(mallocedval = ecpg_strdup("array [", lineno)))
|
|
return false;
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
{
|
|
nval = PGTYPESnumeric_new();
|
|
if (!nval)
|
|
return false;
|
|
|
|
if (var->type == ECPGt_numeric)
|
|
PGTYPESnumeric_copy((numeric *) ((var + var->offset * element)->value), nval);
|
|
else
|
|
PGTYPESnumeric_from_decimal((decimal *) ((var + var->offset * element)->value), nval);
|
|
|
|
str = PGTYPESnumeric_to_asc(nval, nval->dscale);
|
|
slen = strlen(str);
|
|
PGTYPESnumeric_free(nval);
|
|
|
|
if (!(mallocedval = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
strcpy(mallocedval + strlen(mallocedval), ",");
|
|
ecpg_free(str);
|
|
}
|
|
strcpy(mallocedval + strlen(mallocedval) - 1, "]");
|
|
}
|
|
else
|
|
{
|
|
nval = PGTYPESnumeric_new();
|
|
if (!nval)
|
|
return false;
|
|
|
|
if (var->type == ECPGt_numeric)
|
|
PGTYPESnumeric_copy((numeric *) (var->value), nval);
|
|
else
|
|
PGTYPESnumeric_from_decimal((decimal *) (var->value), nval);
|
|
|
|
str = PGTYPESnumeric_to_asc(nval, nval->dscale);
|
|
slen = strlen(str);
|
|
PGTYPESnumeric_free(nval);
|
|
|
|
if (!(mallocedval = ecpg_alloc(slen + 1, lineno)))
|
|
{
|
|
free(str);
|
|
return false;
|
|
}
|
|
|
|
strncpy(mallocedval, str, slen);
|
|
mallocedval[slen] = '\0';
|
|
ecpg_free(str);
|
|
}
|
|
|
|
*tobeinserted_p = mallocedval;
|
|
}
|
|
break;
|
|
|
|
case ECPGt_interval:
|
|
{
|
|
char *str = NULL;
|
|
int slen;
|
|
|
|
if (var->arrsize > 1)
|
|
{
|
|
if (!(mallocedval = ecpg_strdup("array [", lineno)))
|
|
return false;
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
{
|
|
str = quote_postgres(PGTYPESinterval_to_asc((interval *) ((var + var->offset * element)->value)), quote, lineno);
|
|
if (!str)
|
|
return false;
|
|
slen = strlen(str);
|
|
|
|
if (!(mallocedval = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
strcpy(mallocedval + strlen(mallocedval), ",");
|
|
ecpg_free(str);
|
|
}
|
|
strcpy(mallocedval + strlen(mallocedval) - 1, "]");
|
|
}
|
|
else
|
|
{
|
|
str = quote_postgres(PGTYPESinterval_to_asc((interval *) (var->value)), quote, lineno);
|
|
if (!str)
|
|
return false;
|
|
slen = strlen(str);
|
|
|
|
if (!(mallocedval = ecpg_alloc(slen + sizeof("interval ") + 1, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
/* also copy trailing '\0' */
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
ecpg_free(str);
|
|
}
|
|
|
|
*tobeinserted_p = mallocedval;
|
|
}
|
|
break;
|
|
|
|
case ECPGt_date:
|
|
{
|
|
char *str = NULL;
|
|
int slen;
|
|
|
|
if (var->arrsize > 1)
|
|
{
|
|
if (!(mallocedval = ecpg_strdup("array [", lineno)))
|
|
return false;
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
{
|
|
str = quote_postgres(PGTYPESdate_to_asc(*(date *) ((var + var->offset * element)->value)), quote, lineno);
|
|
if (!str)
|
|
return false;
|
|
slen = strlen(str);
|
|
|
|
if (!(mallocedval = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
strcpy(mallocedval + strlen(mallocedval), ",");
|
|
ecpg_free(str);
|
|
}
|
|
strcpy(mallocedval + strlen(mallocedval) - 1, "]");
|
|
}
|
|
else
|
|
{
|
|
str = quote_postgres(PGTYPESdate_to_asc(*(date *) (var->value)), quote, lineno);
|
|
if (!str)
|
|
return false;
|
|
slen = strlen(str);
|
|
|
|
if (!(mallocedval = ecpg_alloc(slen + sizeof("date ") + 1, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
/* also copy trailing '\0' */
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
ecpg_free(str);
|
|
}
|
|
|
|
*tobeinserted_p = mallocedval;
|
|
}
|
|
break;
|
|
|
|
case ECPGt_timestamp:
|
|
{
|
|
char *str = NULL;
|
|
int slen;
|
|
|
|
if (var->arrsize > 1)
|
|
{
|
|
if (!(mallocedval = ecpg_strdup("array [", lineno)))
|
|
return false;
|
|
|
|
for (element = 0; element < var->arrsize; element++)
|
|
{
|
|
str = quote_postgres(PGTYPEStimestamp_to_asc(*(timestamp *) ((var + var->offset * element)->value)), quote, lineno);
|
|
if (!str)
|
|
return false;
|
|
|
|
slen = strlen(str);
|
|
|
|
if (!(mallocedval = ecpg_realloc(mallocedval, strlen(mallocedval) + slen + 2, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
strcpy(mallocedval + strlen(mallocedval), ",");
|
|
ecpg_free(str);
|
|
}
|
|
strcpy(mallocedval + strlen(mallocedval) - 1, "]");
|
|
}
|
|
else
|
|
{
|
|
str = quote_postgres(PGTYPEStimestamp_to_asc(*(timestamp *) (var->value)), quote, lineno);
|
|
if (!str)
|
|
return false;
|
|
slen = strlen(str);
|
|
|
|
if (!(mallocedval = ecpg_alloc(slen + sizeof("timestamp") + 1, lineno)))
|
|
{
|
|
ecpg_free(str);
|
|
return false;
|
|
}
|
|
|
|
/* also copy trailing '\0' */
|
|
strncpy(mallocedval + strlen(mallocedval), str, slen + 1);
|
|
ecpg_free(str);
|
|
}
|
|
|
|
*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, (char *) ecpg_type_name(var->type));
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
free_params(const char **paramValues, int nParams, bool print, int lineno)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < nParams; n++)
|
|
{
|
|
if (print)
|
|
ecpg_log("free_params on line %d: parameter %d = %s\n", lineno, n + 1, paramValues[n] ? paramValues[n] : "null");
|
|
ecpg_free((void *) (paramValues[n]));
|
|
}
|
|
ecpg_free(paramValues);
|
|
}
|
|
|
|
|
|
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((char *) tobeinserted);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
ecpg_execute(struct statement * stmt)
|
|
{
|
|
bool status = false;
|
|
char *cmdstat;
|
|
PGresult *results;
|
|
PGnotify *notify;
|
|
struct variable *var;
|
|
int desc_counter = 0;
|
|
const char **paramValues = NULL;
|
|
int nParams = 0;
|
|
int position = 0;
|
|
struct sqlca_t *sqlca = ECPGget_sqlca();
|
|
bool clear_result = 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;
|
|
|
|
tobeinserted = NULL;
|
|
|
|
/*
|
|
* 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 variable desc_inlist;
|
|
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)
|
|
{
|
|
desc_inlist.type = ECPGt_char;
|
|
desc_inlist.value = desc_item->data;
|
|
desc_inlist.pointer = &(desc_item->data);
|
|
desc_inlist.varcharsize = strlen(desc_item->data);
|
|
desc_inlist.arrsize = 1;
|
|
desc_inlist.offset = 0;
|
|
if (!desc_item->indicator)
|
|
{
|
|
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;
|
|
}
|
|
else
|
|
{
|
|
desc_inlist.ind_type = ECPGt_int;
|
|
desc_inlist.ind_value = &(desc_item->indicator);
|
|
desc_inlist.ind_pointer = &(desc_inlist.ind_value);
|
|
desc_inlist.ind_varcharsize = desc_inlist.ind_arrsize = 1;
|
|
desc_inlist.ind_offset = 0;
|
|
}
|
|
if (!ecpg_store_input(stmt->lineno, stmt->force_indicator, &desc_inlist, &tobeinserted, false))
|
|
return false;
|
|
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* now tobeinserted points to an area that contains the next parameter
|
|
* now find the positin in the string where it belongs
|
|
*/
|
|
if ((position = next_insert(stmt->command, position, stmt->questionmarks) + 1) == 0)
|
|
{
|
|
/*
|
|
* We have an argument but we dont 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);
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
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))
|
|
{
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
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 (!insert_tobeinserted(position, 2, stmt, tobeinserted))
|
|
{
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
return false;
|
|
}
|
|
tobeinserted = NULL;
|
|
}
|
|
else
|
|
{
|
|
nParams++;
|
|
if (!(paramValues = (const char **) ecpg_realloc(paramValues, sizeof(const char *) * nParams, stmt->lineno)))
|
|
{
|
|
ecpg_free(paramValues);
|
|
return false;
|
|
}
|
|
|
|
paramValues[nParams - 1] = tobeinserted;
|
|
|
|
/* 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)))
|
|
{
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
return false;
|
|
}
|
|
|
|
snprintf(tobeinserted, buffersize, "$%d", counter++);
|
|
|
|
if (!insert_tobeinserted(position, 2, stmt, tobeinserted))
|
|
{
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
return false;
|
|
}
|
|
tobeinserted = NULL;
|
|
}
|
|
}
|
|
|
|
if (desc_counter == 0)
|
|
var = var->next;
|
|
}
|
|
|
|
/* Check if there are unmatched things left. */
|
|
if (next_insert(stmt->command, position, stmt->questionmarks) >= 0)
|
|
{
|
|
ecpg_raise(stmt->lineno, ECPG_TOO_FEW_ARGUMENTS,
|
|
ECPG_SQLSTATE_USING_CLAUSE_DOES_NOT_MATCH_PARAMETERS, NULL);
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
return false;
|
|
}
|
|
|
|
/* The request has been build. */
|
|
|
|
if (PQtransactionStatus(stmt->connection->connection) == PQTRANS_IDLE && !stmt->connection->autocommit)
|
|
{
|
|
results = PQexec(stmt->connection->connection, "begin transaction");
|
|
if (!ecpg_check_PQresult(results, stmt->lineno, stmt->connection->connection, stmt->compat))
|
|
{
|
|
free_params(paramValues, nParams, false, stmt->lineno);
|
|
return false;
|
|
}
|
|
PQclear(results);
|
|
}
|
|
|
|
ecpg_log("ecpg_execute on line %d: query: %s; with %d parameter(s) on connection %s\n", stmt->lineno, stmt->command, nParams, stmt->connection->name);
|
|
if (stmt->statement_type == ECPGst_execute)
|
|
{
|
|
results = PQexecPrepared(stmt->connection->connection, stmt->name, nParams, paramValues, NULL, NULL, 0);
|
|
ecpg_log("ecpg_execute on line %d: using PQexecPrepared for \"%s\"\n", stmt->lineno, stmt->command);
|
|
}
|
|
else
|
|
{
|
|
if (nParams == 0)
|
|
{
|
|
results = PQexec(stmt->connection->connection, stmt->command);
|
|
ecpg_log("ecpg_execute on line %d: using PQexec\n", stmt->lineno);
|
|
}
|
|
else
|
|
{
|
|
results = PQexecParams(stmt->connection->connection, stmt->command, nParams, NULL, paramValues, NULL, NULL, 0);
|
|
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
|
|
}
|
|
}
|
|
|
|
free_params(paramValues, nParams, true, stmt->lineno);
|
|
|
|
if (!ecpg_check_PQresult(results, stmt->lineno, stmt->connection->connection, stmt->compat))
|
|
return (false);
|
|
|
|
var = stmt->outlist;
|
|
switch (PQresultStatus(results))
|
|
{
|
|
int nfields,
|
|
ntuples,
|
|
act_field;
|
|
|
|
case PGRES_TUPLES_OK:
|
|
nfields = PQnfields(results);
|
|
sqlca->sqlerrd[2] = ntuples = PQntuples(results);
|
|
ecpg_log("ecpg_execute 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_execute 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 = results;
|
|
clear_result = false;
|
|
ecpg_log("ecpg_execute on line %d: putting result (%d tuples) into descriptor %s\n",
|
|
stmt->lineno, PQntuples(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, 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_execute on line %d: out of memory allocating a new sqlda\n", stmt->lineno);
|
|
status = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ecpg_log("ecpg_execute on line %d: new sqlda was built\n", stmt->lineno);
|
|
|
|
*_sqlda = sqlda_new;
|
|
|
|
ecpg_set_compat_sqlda(stmt->lineno, _sqlda, results, i, stmt->compat);
|
|
ecpg_log("ecpg_execute on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
|
|
stmt->lineno, PQnfields(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, 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_execute on line %d: out of memory allocating a new sqlda\n", stmt->lineno);
|
|
status = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ecpg_log("ecpg_execute on line %d: new sqlda was built\n", stmt->lineno);
|
|
|
|
*_sqlda = sqlda_new;
|
|
|
|
ecpg_set_native_sqlda(stmt->lineno, _sqlda, results, i, stmt->compat);
|
|
ecpg_log("ecpg_execute on line %d: putting result (1 tuple %d fields) into sqlda descriptor\n",
|
|
stmt->lineno, PQnfields(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(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(results);
|
|
sqlca->sqlerrd[1] = PQoidValue(results);
|
|
sqlca->sqlerrd[2] = atol(PQcmdTuples(results));
|
|
ecpg_log("ecpg_execute on line %d: OK: %s\n", stmt->lineno, cmdstat);
|
|
if (stmt->compat != ECPG_COMPAT_INFORMIX_SE &&
|
|
!sqlca->sqlerrd[2] &&
|
|
(!strncmp(cmdstat, "UPDATE", 6)
|
|
|| !strncmp(cmdstat, "INSERT", 6)
|
|
|| !strncmp(cmdstat, "DELETE", 6)))
|
|
ecpg_raise(stmt->lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL);
|
|
break;
|
|
case PGRES_COPY_OUT:
|
|
{
|
|
char *buffer;
|
|
int res;
|
|
|
|
ecpg_log("ecpg_execute 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(results);
|
|
results = PQgetResult(stmt->connection->connection);
|
|
if (PQresultStatus(results) == PGRES_COMMAND_OK)
|
|
ecpg_log("ecpg_execute on line %d: got PGRES_COMMAND_OK after PGRES_COPY_OUT\n", stmt->lineno);
|
|
else
|
|
ecpg_log("ecpg_execute on line %d: got error after PGRES_COPY_OUT: %s", stmt->lineno, PQresultErrorMessage(results));
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
|
|
/*
|
|
* execution should never reach this code because it is already
|
|
* handled in ECPGcheck_PQresult()
|
|
*/
|
|
ecpg_log("ecpg_execute on line %d: unknown execution status type\n",
|
|
stmt->lineno);
|
|
ecpg_raise_backend(stmt->lineno, results, stmt->connection->connection, stmt->compat);
|
|
status = false;
|
|
break;
|
|
}
|
|
if (clear_result)
|
|
PQclear(results);
|
|
|
|
/* check for asynchronous returns */
|
|
notify = PQnotifies(stmt->connection->connection);
|
|
if (notify)
|
|
{
|
|
ecpg_log("ecpg_execute on line %d: asynchronous notification of \"%s\" from backend PID %d received\n",
|
|
stmt->lineno, notify->relname, notify->be_pid);
|
|
PQfreemem(notify);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
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;
|
|
struct statement *stmt;
|
|
struct connection *con;
|
|
bool status;
|
|
char *oldlocale;
|
|
enum ECPGttype type;
|
|
struct variable **list;
|
|
enum ECPG_statement_type statement_type = (enum ECPG_statement_type) st;
|
|
char *prepname;
|
|
|
|
if (!query)
|
|
{
|
|
ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
|
|
return (false);
|
|
}
|
|
|
|
/* Make sure we do NOT honor the locale for numeric input/output */
|
|
/* since the database wants the standard decimal point */
|
|
oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
|
|
setlocale(LC_NUMERIC, "C");
|
|
|
|
#ifdef ENABLE_THREAD_SAFETY
|
|
ecpg_pthreads_init();
|
|
#endif
|
|
|
|
con = ecpg_get_connection(connection_name);
|
|
|
|
if (!ecpg_init(con, connection_name, lineno))
|
|
{
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
return (false);
|
|
}
|
|
|
|
/* construct statement in our own structure */
|
|
va_start(args, query);
|
|
|
|
/*
|
|
* 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
|
|
*/
|
|
if (!(stmt = (struct statement *) ecpg_alloc(sizeof(struct statement), lineno)))
|
|
{
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* 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))
|
|
{
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
va_end(args);
|
|
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);
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
va_end(args);
|
|
return (false);
|
|
}
|
|
}
|
|
|
|
stmt->connection = con;
|
|
stmt->lineno = lineno;
|
|
stmt->compat = compat;
|
|
stmt->force_indicator = force_indicator;
|
|
stmt->questionmarks = questionmarks;
|
|
stmt->statement_type = statement_type;
|
|
|
|
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)))
|
|
{
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
free_statement(stmt);
|
|
va_end(args);
|
|
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);
|
|
|
|
if (var->arrsize == 0 || var->varcharsize == 0)
|
|
var->value = *((char **) (var->pointer));
|
|
else
|
|
var->value = var->pointer;
|
|
|
|
/*
|
|
* negative values are used to indicate an array without given
|
|
* bounds
|
|
*/
|
|
/* reset to zero for us */
|
|
if (var->arrsize < 0)
|
|
var->arrsize = 0;
|
|
if (var->varcharsize < 0)
|
|
var->varcharsize = 0;
|
|
|
|
var->next = NULL;
|
|
|
|
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);
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
free_statement(stmt);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
for (ptr = *list; ptr && ptr->next; ptr = ptr->next);
|
|
|
|
if (ptr == NULL)
|
|
*list = var;
|
|
else
|
|
ptr->next = var;
|
|
}
|
|
|
|
type = va_arg(args, enum ECPGttype);
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
/* are we connected? */
|
|
if (con == NULL || con->connection == NULL)
|
|
{
|
|
free_statement(stmt);
|
|
ecpg_raise(lineno, ECPG_NOT_CONN, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, (con) ? con->name : ecpg_gettext("<empty>"));
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
return false;
|
|
}
|
|
|
|
/* initialize auto_mem struct */
|
|
ecpg_clear_auto_mem();
|
|
|
|
status = ecpg_execute(stmt);
|
|
free_statement(stmt);
|
|
|
|
/* and reset locale value so our application is not affected */
|
|
setlocale(LC_NUMERIC, oldlocale);
|
|
ecpg_free(oldlocale);
|
|
|
|
return (status);
|
|
}
|
|
|
|
/* 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, (char *) query, ECPGt_EOIT,
|
|
ECPGt_descriptor, descriptor, 0L, 0L, 0L,
|
|
ECPGt_NO_INDICATOR, NULL, 0L, 0L, 0L, ECPGt_EORT);
|
|
}
|