mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +03:00
DECLARE STATEMENT is a statement that lets users declare an identifier pointing at a connection. This identifier will be used in other embedded dynamic SQL statement such as PREPARE, EXECUTE, DECLARE CURSOR and so on. When connecting to a non-default connection, the AT clause can be used in a DECLARE STATEMENT once and is no longer needed in every dynamic SQL statement. This makes ECPG applications easier and more efficient. Moreover, writing code without designating connection explicitly improves portability. Authors: Ideriha-san ("Ideriha, Takeshi" <ideriha.takeshi@jp.fujitsu.com>) Kuroda-san ("Kuroda, Hayato" <kuroda.hayato@jp.fujitsu.com>) Discussion: https://postgr.es/m4E72940DA2BF16479384A86D54D0988A565669DF@G01JPEXMBKW04
2118 lines
58 KiB
C
2118 lines
58 KiB
C
/* 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 "ecpgtype.h"
|
|
#include "ecpglib.h"
|
|
#include "ecpgerrno.h"
|
|
#include "ecpglib_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;
|
|
|
|
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);
|
|
#ifdef HAVE_USELOCALE
|
|
if (stmt->clocale)
|
|
freelocale(stmt->clocale);
|
|
#else
|
|
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), 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), 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);
|
|
}
|
|
|
|
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;
|
|
#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, "{");
|
|
|
|
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;
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
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;
|
|
#endif /* HAVE_LONG_LONG_INT */
|
|
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_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;
|
|
}
|
|
|
|
void
|
|
ecpg_free_params(struct statement *stmt, bool print)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < stmt->nparams; n++)
|
|
{
|
|
if (print)
|
|
ecpg_log("ecpg_free_params on line %d: parameter %d = %s\n", stmt->lineno, n + 1, stmt->paramvalues[n] ? stmt->paramvalues[n] : "null");
|
|
ecpg_free(stmt->paramvalues[n]);
|
|
}
|
|
ecpg_free(stmt->paramvalues);
|
|
stmt->paramvalues = 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((char *) tobeinserted);
|
|
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;
|
|
|
|
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 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);
|
|
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 (!insert_tobeinserted(position, 2, stmt, tobeinserted))
|
|
{
|
|
ecpg_free_params(stmt, false);
|
|
return false;
|
|
}
|
|
tobeinserted = NULL;
|
|
}
|
|
else
|
|
{
|
|
char **paramvalues;
|
|
|
|
if (!(paramvalues = (char **) ecpg_realloc(stmt->paramvalues, sizeof(char *) * (stmt->nparams + 1), stmt->lineno)))
|
|
{
|
|
ecpg_free_params(stmt, false);
|
|
return false;
|
|
}
|
|
|
|
stmt->nparams++;
|
|
stmt->paramvalues = paramvalues;
|
|
stmt->paramvalues[stmt->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)))
|
|
{
|
|
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. */
|
|
if (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, NULL, NULL, 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, NULL, NULL, 0);
|
|
ecpg_log("ecpg_execute on line %d: using PQexecParams\n", stmt->lineno);
|
|
}
|
|
}
|
|
|
|
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 ECPGcheck_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;
|
|
|
|
*stmt_out = NULL;
|
|
|
|
if (!query)
|
|
{
|
|
ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
|
|
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
|
|
stmt->clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
|
|
if (stmt->clocale == (locale_t) 0)
|
|
{
|
|
ecpg_do_epilogue(stmt);
|
|
return false;
|
|
}
|
|
stmt->oldlocale = uselocale(stmt->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
|
|
|
|
#ifdef ENABLE_THREAD_SAFETY
|
|
ecpg_pthreads_init();
|
|
#endif
|
|
|
|
con = ecpg_get_connection(connection_name);
|
|
|
|
if (!ecpg_init(con, connection_name, lineno))
|
|
{
|
|
ecpg_do_epilogue(stmt);
|
|
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))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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 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
|
|
*------
|
|
*/
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
const char *real_connection_name = NULL;
|
|
|
|
real_connection_name = connection_name;
|
|
|
|
if (!query)
|
|
{
|
|
ecpg_raise(lineno, ECPG_EMPTY, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, NULL);
|
|
return false;
|
|
}
|
|
|
|
/* Handle the EXEC SQL EXECUTE... statement */
|
|
if (ECPGst_execute == st)
|
|
{
|
|
real_connection_name = ecpg_get_con_name_by_declared_name(query);
|
|
if (real_connection_name == NULL)
|
|
{
|
|
/*
|
|
* If can't get the connection name by declared name then using connection name
|
|
* coming from the parameter connection_name
|
|
*/
|
|
real_connection_name = connection_name;
|
|
}
|
|
}
|
|
|
|
va_start(args, query);
|
|
ret = ecpg_do(lineno, compat, force_indicator, real_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);
|
|
}
|