1002 lines
23 KiB
C
1002 lines
23 KiB
C
/* dynamic SQL support routines
|
|
*
|
|
* src/interfaces/ecpg/ecpglib/descriptor.c
|
|
*/
|
|
|
|
#define POSTGRES_ECPG_INTERNAL
|
|
#include "postgres_fe.h"
|
|
|
|
#include "catalog/pg_type_d.h"
|
|
#include "ecpg-pthread-win32.h"
|
|
#include "ecpgerrno.h"
|
|
#include "ecpglib.h"
|
|
#include "ecpglib_extern.h"
|
|
#include "ecpgtype.h"
|
|
#include "sqlca.h"
|
|
#include "sqlda.h"
|
|
#include "sql3types.h"
|
|
|
|
static void descriptor_free(struct descriptor *desc);
|
|
|
|
/* We manage descriptors separately for each thread. */
|
|
#ifdef ENABLE_THREAD_SAFETY
|
|
static pthread_key_t descriptor_key;
|
|
static pthread_once_t descriptor_once = PTHREAD_ONCE_INIT;
|
|
|
|
static void descriptor_deallocate_all(struct descriptor *list);
|
|
|
|
static void
|
|
descriptor_destructor(void *arg)
|
|
{
|
|
descriptor_deallocate_all(arg);
|
|
}
|
|
|
|
static void
|
|
descriptor_key_init(void)
|
|
{
|
|
pthread_key_create(&descriptor_key, descriptor_destructor);
|
|
}
|
|
|
|
static struct descriptor *
|
|
get_descriptors(void)
|
|
{
|
|
pthread_once(&descriptor_once, descriptor_key_init);
|
|
return (struct descriptor *) pthread_getspecific(descriptor_key);
|
|
}
|
|
|
|
static void
|
|
set_descriptors(struct descriptor *value)
|
|
{
|
|
pthread_setspecific(descriptor_key, value);
|
|
}
|
|
#else
|
|
static struct descriptor *all_descriptors = NULL;
|
|
|
|
#define get_descriptors() (all_descriptors)
|
|
#define set_descriptors(value) do { all_descriptors = (value); } while(0)
|
|
#endif
|
|
|
|
/* old internal convenience function that might go away later */
|
|
static PGresult *
|
|
ecpg_result_by_descriptor(int line, const char *name)
|
|
{
|
|
struct descriptor *desc = ecpg_find_desc(line, name);
|
|
|
|
if (desc == NULL)
|
|
return NULL;
|
|
return desc->result;
|
|
}
|
|
|
|
static unsigned int
|
|
ecpg_dynamic_type_DDT(Oid type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case DATEOID:
|
|
return SQL3_DDT_DATE;
|
|
case TIMEOID:
|
|
return SQL3_DDT_TIME;
|
|
case TIMESTAMPOID:
|
|
return SQL3_DDT_TIMESTAMP;
|
|
case TIMESTAMPTZOID:
|
|
return SQL3_DDT_TIMESTAMP_WITH_TIME_ZONE;
|
|
case TIMETZOID:
|
|
return SQL3_DDT_TIME_WITH_TIME_ZONE;
|
|
default:
|
|
return SQL3_DDT_ILLEGAL;
|
|
}
|
|
}
|
|
|
|
bool
|
|
ECPGget_desc_header(int lineno, const char *desc_name, int *count)
|
|
{
|
|
PGresult *ECPGresult;
|
|
struct sqlca_t *sqlca = ECPGget_sqlca();
|
|
|
|
if (sqlca == NULL)
|
|
{
|
|
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
|
|
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
|
|
return false;
|
|
}
|
|
|
|
ecpg_init_sqlca(sqlca);
|
|
ECPGresult = ecpg_result_by_descriptor(lineno, desc_name);
|
|
if (!ECPGresult)
|
|
return false;
|
|
|
|
*count = PQnfields(ECPGresult);
|
|
sqlca->sqlerrd[2] = 1;
|
|
ecpg_log("ECPGget_desc_header: found %d attributes\n", *count);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
get_int_item(int lineno, void *var, enum ECPGttype vartype, int value)
|
|
{
|
|
switch (vartype)
|
|
{
|
|
case ECPGt_short:
|
|
*(short *) var = (short) value;
|
|
break;
|
|
case ECPGt_int:
|
|
*(int *) var = (int) value;
|
|
break;
|
|
case ECPGt_long:
|
|
*(long *) var = (long) value;
|
|
break;
|
|
case ECPGt_unsigned_short:
|
|
*(unsigned short *) var = (unsigned short) value;
|
|
break;
|
|
case ECPGt_unsigned_int:
|
|
*(unsigned int *) var = (unsigned int) value;
|
|
break;
|
|
case ECPGt_unsigned_long:
|
|
*(unsigned long *) var = (unsigned long) value;
|
|
break;
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
case ECPGt_long_long:
|
|
*(long long int *) var = (long long int) value;
|
|
break;
|
|
case ECPGt_unsigned_long_long:
|
|
*(unsigned long long int *) var = (unsigned long long int) value;
|
|
break;
|
|
#endif /* HAVE_LONG_LONG_INT */
|
|
case ECPGt_float:
|
|
*(float *) var = (float) value;
|
|
break;
|
|
case ECPGt_double:
|
|
*(double *) var = (double) value;
|
|
break;
|
|
default:
|
|
ecpg_raise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
set_int_item(int lineno, int *target, const void *var, enum ECPGttype vartype)
|
|
{
|
|
switch (vartype)
|
|
{
|
|
case ECPGt_short:
|
|
*target = *(const short *) var;
|
|
break;
|
|
case ECPGt_int:
|
|
*target = *(const int *) var;
|
|
break;
|
|
case ECPGt_long:
|
|
*target = *(const long *) var;
|
|
break;
|
|
case ECPGt_unsigned_short:
|
|
*target = *(const unsigned short *) var;
|
|
break;
|
|
case ECPGt_unsigned_int:
|
|
*target = *(const unsigned int *) var;
|
|
break;
|
|
case ECPGt_unsigned_long:
|
|
*target = *(const unsigned long *) var;
|
|
break;
|
|
#ifdef HAVE_LONG_LONG_INT
|
|
case ECPGt_long_long:
|
|
*target = *(const long long int *) var;
|
|
break;
|
|
case ECPGt_unsigned_long_long:
|
|
*target = *(const unsigned long long int *) var;
|
|
break;
|
|
#endif /* HAVE_LONG_LONG_INT */
|
|
case ECPGt_float:
|
|
*target = *(const float *) var;
|
|
break;
|
|
case ECPGt_double:
|
|
*target = *(const double *) var;
|
|
break;
|
|
default:
|
|
ecpg_raise(lineno, ECPG_VAR_NOT_NUMERIC, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
get_char_item(int lineno, void *var, enum ECPGttype vartype, char *value, int varcharsize)
|
|
{
|
|
switch (vartype)
|
|
{
|
|
case ECPGt_char:
|
|
case ECPGt_unsigned_char:
|
|
case ECPGt_string:
|
|
strncpy((char *) var, value, varcharsize);
|
|
break;
|
|
case ECPGt_varchar:
|
|
{
|
|
struct ECPGgeneric_varchar *variable =
|
|
(struct ECPGgeneric_varchar *) var;
|
|
|
|
if (varcharsize == 0)
|
|
memcpy(variable->arr, value, strlen(value));
|
|
else
|
|
strncpy(variable->arr, value, varcharsize);
|
|
|
|
variable->len = strlen(value);
|
|
if (varcharsize > 0 && variable->len > varcharsize)
|
|
variable->len = varcharsize;
|
|
}
|
|
break;
|
|
default:
|
|
ecpg_raise(lineno, ECPG_VAR_NOT_CHAR, ECPG_SQLSTATE_RESTRICTED_DATA_TYPE_ATTRIBUTE_VIOLATION, NULL);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define RETURN_IF_NO_DATA if (ntuples < 1) \
|
|
{ \
|
|
va_end(args); \
|
|
ecpg_raise(lineno, ECPG_NOT_FOUND, ECPG_SQLSTATE_NO_DATA, NULL); \
|
|
return false; \
|
|
}
|
|
|
|
bool
|
|
ECPGget_desc(int lineno, const char *desc_name, int index,...)
|
|
{
|
|
va_list args;
|
|
PGresult *ECPGresult;
|
|
enum ECPGdtype type;
|
|
int ntuples,
|
|
act_tuple;
|
|
struct variable data_var;
|
|
struct sqlca_t *sqlca = ECPGget_sqlca();
|
|
|
|
if (sqlca == NULL)
|
|
{
|
|
ecpg_raise(lineno, ECPG_OUT_OF_MEMORY,
|
|
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
|
|
return false;
|
|
}
|
|
|
|
va_start(args, index);
|
|
ecpg_init_sqlca(sqlca);
|
|
ECPGresult = ecpg_result_by_descriptor(lineno, desc_name);
|
|
if (!ECPGresult)
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ntuples = PQntuples(ECPGresult);
|
|
|
|
if (index < 1 || index > PQnfields(ECPGresult))
|
|
{
|
|
ecpg_raise(lineno, ECPG_INVALID_DESCRIPTOR_INDEX, ECPG_SQLSTATE_INVALID_DESCRIPTOR_INDEX, NULL);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: reading items for tuple %d\n", index);
|
|
--index;
|
|
|
|
type = va_arg(args, enum ECPGdtype);
|
|
|
|
memset(&data_var, 0, sizeof data_var);
|
|
data_var.type = ECPGt_EORT;
|
|
data_var.ind_type = ECPGt_NO_INDICATOR;
|
|
|
|
while (type != ECPGd_EODT)
|
|
{
|
|
char type_str[20];
|
|
long varcharsize;
|
|
long offset;
|
|
long arrsize;
|
|
enum ECPGttype vartype;
|
|
void *var;
|
|
|
|
vartype = va_arg(args, enum ECPGttype);
|
|
var = va_arg(args, void *);
|
|
varcharsize = va_arg(args, long);
|
|
arrsize = va_arg(args, long);
|
|
offset = va_arg(args, long);
|
|
|
|
switch (type)
|
|
{
|
|
case (ECPGd_indicator):
|
|
RETURN_IF_NO_DATA;
|
|
data_var.ind_type = vartype;
|
|
data_var.ind_pointer = var;
|
|
data_var.ind_varcharsize = varcharsize;
|
|
data_var.ind_arrsize = arrsize;
|
|
data_var.ind_offset = offset;
|
|
if (data_var.ind_arrsize == 0 || data_var.ind_varcharsize == 0)
|
|
data_var.ind_value = *((void **) (data_var.ind_pointer));
|
|
else
|
|
data_var.ind_value = data_var.ind_pointer;
|
|
break;
|
|
|
|
case ECPGd_data:
|
|
RETURN_IF_NO_DATA;
|
|
data_var.type = vartype;
|
|
data_var.pointer = var;
|
|
data_var.varcharsize = varcharsize;
|
|
data_var.arrsize = arrsize;
|
|
data_var.offset = offset;
|
|
if (data_var.arrsize == 0 || data_var.varcharsize == 0)
|
|
data_var.value = *((void **) (data_var.pointer));
|
|
else
|
|
data_var.value = data_var.pointer;
|
|
break;
|
|
|
|
case ECPGd_name:
|
|
if (!get_char_item(lineno, var, vartype, PQfname(ECPGresult, index), varcharsize))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: NAME = %s\n", PQfname(ECPGresult, index));
|
|
break;
|
|
|
|
case ECPGd_nullable:
|
|
if (!get_int_item(lineno, var, vartype, 1))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
case ECPGd_key_member:
|
|
if (!get_int_item(lineno, var, vartype, 0))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
|
|
case ECPGd_scale:
|
|
if (!get_int_item(lineno, var, vartype, (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: SCALE = %d\n", (PQfmod(ECPGresult, index) - VARHDRSZ) & 0xffff);
|
|
break;
|
|
|
|
case ECPGd_precision:
|
|
if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) >> 16))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: PRECISION = %d\n", PQfmod(ECPGresult, index) >> 16);
|
|
break;
|
|
|
|
case ECPGd_octet:
|
|
if (!get_int_item(lineno, var, vartype, PQfsize(ECPGresult, index)))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: OCTET_LENGTH = %d\n", PQfsize(ECPGresult, index));
|
|
break;
|
|
|
|
case ECPGd_length:
|
|
if (!get_int_item(lineno, var, vartype, PQfmod(ECPGresult, index) - VARHDRSZ))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: LENGTH = %d\n", PQfmod(ECPGresult, index) - VARHDRSZ);
|
|
break;
|
|
|
|
case ECPGd_type:
|
|
if (!get_int_item(lineno, var, vartype, ecpg_dynamic_type(PQftype(ECPGresult, index))))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: TYPE = %d\n", ecpg_dynamic_type(PQftype(ECPGresult, index)));
|
|
break;
|
|
|
|
case ECPGd_di_code:
|
|
if (!get_int_item(lineno, var, vartype, ecpg_dynamic_type_DDT(PQftype(ECPGresult, index))))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: TYPE = %d\n", ecpg_dynamic_type_DDT(PQftype(ECPGresult, index)));
|
|
break;
|
|
|
|
case ECPGd_cardinality:
|
|
if (!get_int_item(lineno, var, vartype, PQntuples(ECPGresult)))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
ecpg_log("ECPGget_desc: CARDINALITY = %d\n", PQntuples(ECPGresult));
|
|
break;
|
|
|
|
case ECPGd_ret_length:
|
|
case ECPGd_ret_octet:
|
|
|
|
RETURN_IF_NO_DATA;
|
|
|
|
/*
|
|
* this is like ECPGstore_result
|
|
*/
|
|
if (arrsize > 0 && ntuples > arrsize)
|
|
{
|
|
ecpg_log("ECPGget_desc on line %d: incorrect number of matches; %d don't fit into array of %ld\n",
|
|
lineno, ntuples, arrsize);
|
|
ecpg_raise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
/* allocate storage if needed */
|
|
if (arrsize == 0 && *(void **) var == NULL)
|
|
{
|
|
void *mem = (void *) ecpg_auto_alloc(offset * ntuples, lineno);
|
|
|
|
if (!mem)
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
*(void **) var = mem;
|
|
var = mem;
|
|
}
|
|
|
|
for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
|
|
{
|
|
if (!get_int_item(lineno, var, vartype, PQgetlength(ECPGresult, act_tuple, index)))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
var = (char *) var + offset;
|
|
ecpg_log("ECPGget_desc: RETURNED[%d] = %d\n", act_tuple, PQgetlength(ECPGresult, act_tuple, index));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
snprintf(type_str, sizeof(type_str), "%d", type);
|
|
ecpg_raise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
type = va_arg(args, enum ECPGdtype);
|
|
}
|
|
|
|
if (data_var.type != ECPGt_EORT)
|
|
{
|
|
struct statement stmt;
|
|
|
|
memset(&stmt, 0, sizeof stmt);
|
|
stmt.lineno = lineno;
|
|
|
|
/* Make sure we do NOT honor the locale for numeric input */
|
|
/* since the database gives the standard decimal point */
|
|
/* (see comments in execute.c) */
|
|
#ifdef HAVE_USELOCALE
|
|
stmt.clocale = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
|
|
if (stmt.clocale != (locale_t) 0)
|
|
stmt.oldlocale = uselocale(stmt.clocale);
|
|
#else
|
|
#ifdef HAVE__CONFIGTHREADLOCALE
|
|
stmt.oldthreadlocale = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
|
#endif
|
|
stmt.oldlocale = ecpg_strdup(setlocale(LC_NUMERIC, NULL), lineno);
|
|
setlocale(LC_NUMERIC, "C");
|
|
#endif
|
|
|
|
/* desperate try to guess something sensible */
|
|
stmt.connection = ecpg_get_connection(NULL);
|
|
ecpg_store_result(ECPGresult, index, &stmt, &data_var);
|
|
|
|
#ifdef HAVE_USELOCALE
|
|
if (stmt.oldlocale != (locale_t) 0)
|
|
uselocale(stmt.oldlocale);
|
|
if (stmt.clocale)
|
|
freelocale(stmt.clocale);
|
|
#else
|
|
if (stmt.oldlocale)
|
|
{
|
|
setlocale(LC_NUMERIC, stmt.oldlocale);
|
|
ecpg_free(stmt.oldlocale);
|
|
}
|
|
#ifdef HAVE__CONFIGTHREADLOCALE
|
|
if (stmt.oldthreadlocale != -1)
|
|
(void) _configthreadlocale(stmt.oldthreadlocale);
|
|
#endif
|
|
#endif
|
|
}
|
|
else if (data_var.ind_type != ECPGt_NO_INDICATOR && data_var.ind_pointer != NULL)
|
|
|
|
/*
|
|
* ind_type != NO_INDICATOR should always have ind_pointer != NULL but
|
|
* since this might be changed manually in the .c file let's play it
|
|
* safe
|
|
*/
|
|
{
|
|
/*
|
|
* this is like ECPGstore_result but since we don't have a data
|
|
* variable at hand, we can't call it
|
|
*/
|
|
if (data_var.ind_arrsize > 0 && ntuples > data_var.ind_arrsize)
|
|
{
|
|
ecpg_log("ECPGget_desc on line %d: incorrect number of matches (indicator); %d don't fit into array of %ld\n",
|
|
lineno, ntuples, data_var.ind_arrsize);
|
|
ecpg_raise(lineno, ECPG_TOO_MANY_MATCHES, ECPG_SQLSTATE_CARDINALITY_VIOLATION, NULL);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
/* allocate storage if needed */
|
|
if (data_var.ind_arrsize == 0 && data_var.ind_value == NULL)
|
|
{
|
|
void *mem = (void *) ecpg_auto_alloc(data_var.ind_offset * ntuples, lineno);
|
|
|
|
if (!mem)
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
*(void **) data_var.ind_pointer = mem;
|
|
data_var.ind_value = mem;
|
|
}
|
|
|
|
for (act_tuple = 0; act_tuple < ntuples; act_tuple++)
|
|
{
|
|
if (!get_int_item(lineno, data_var.ind_value, data_var.ind_type, -PQgetisnull(ECPGresult, act_tuple, index)))
|
|
{
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
data_var.ind_value = (char *) data_var.ind_value + data_var.ind_offset;
|
|
ecpg_log("ECPGget_desc: INDICATOR[%d] = %d\n", act_tuple, -PQgetisnull(ECPGresult, act_tuple, index));
|
|
}
|
|
}
|
|
sqlca->sqlerrd[2] = ntuples;
|
|
va_end(args);
|
|
return true;
|
|
}
|
|
|
|
#undef RETURN_IF_NO_DATA
|
|
|
|
bool
|
|
ECPGset_desc_header(int lineno, const char *desc_name, int count)
|
|
{
|
|
struct descriptor *desc = ecpg_find_desc(lineno, desc_name);
|
|
|
|
if (desc == NULL)
|
|
return false;
|
|
desc->count = count;
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
set_desc_attr(struct descriptor_item *desc_item, struct variable *var,
|
|
char *tobeinserted)
|
|
{
|
|
if (var->type != ECPGt_bytea)
|
|
desc_item->is_binary = false;
|
|
|
|
else
|
|
{
|
|
struct ECPGgeneric_varchar *variable =
|
|
(struct ECPGgeneric_varchar *) (var->value);
|
|
|
|
desc_item->is_binary = true;
|
|
desc_item->data_len = variable->len;
|
|
}
|
|
|
|
ecpg_free(desc_item->data); /* free() takes care of a potential NULL value */
|
|
desc_item->data = (char *) tobeinserted;
|
|
}
|
|
|
|
|
|
bool
|
|
ECPGset_desc(int lineno, const char *desc_name, int index,...)
|
|
{
|
|
va_list args;
|
|
struct descriptor *desc;
|
|
struct descriptor_item *desc_item;
|
|
struct variable *var;
|
|
|
|
desc = ecpg_find_desc(lineno, desc_name);
|
|
if (desc == NULL)
|
|
return false;
|
|
|
|
for (desc_item = desc->items; desc_item; desc_item = desc_item->next)
|
|
{
|
|
if (desc_item->num == index)
|
|
break;
|
|
}
|
|
|
|
if (desc_item == NULL)
|
|
{
|
|
desc_item = (struct descriptor_item *) ecpg_alloc(sizeof(*desc_item), lineno);
|
|
if (!desc_item)
|
|
return false;
|
|
desc_item->num = index;
|
|
if (desc->count < index)
|
|
desc->count = index;
|
|
desc_item->next = desc->items;
|
|
desc->items = desc_item;
|
|
}
|
|
|
|
if (!(var = (struct variable *) ecpg_alloc(sizeof(struct variable), lineno)))
|
|
return false;
|
|
|
|
va_start(args, index);
|
|
|
|
for (;;)
|
|
{
|
|
enum ECPGdtype itemtype;
|
|
char *tobeinserted = NULL;
|
|
|
|
itemtype = va_arg(args, enum ECPGdtype);
|
|
|
|
if (itemtype == ECPGd_EODT)
|
|
break;
|
|
|
|
var->type = va_arg(args, enum ECPGttype);
|
|
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;
|
|
|
|
switch (itemtype)
|
|
{
|
|
case ECPGd_data:
|
|
{
|
|
if (!ecpg_store_input(lineno, true, var, &tobeinserted, false))
|
|
{
|
|
ecpg_free(var);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
|
|
set_desc_attr(desc_item, var, tobeinserted);
|
|
tobeinserted = NULL;
|
|
break;
|
|
}
|
|
|
|
case ECPGd_indicator:
|
|
set_int_item(lineno, &desc_item->indicator, var->pointer, var->type);
|
|
break;
|
|
|
|
case ECPGd_length:
|
|
set_int_item(lineno, &desc_item->length, var->pointer, var->type);
|
|
break;
|
|
|
|
case ECPGd_precision:
|
|
set_int_item(lineno, &desc_item->precision, var->pointer, var->type);
|
|
break;
|
|
|
|
case ECPGd_scale:
|
|
set_int_item(lineno, &desc_item->scale, var->pointer, var->type);
|
|
break;
|
|
|
|
case ECPGd_type:
|
|
set_int_item(lineno, &desc_item->type, var->pointer, var->type);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char type_str[20];
|
|
|
|
snprintf(type_str, sizeof(type_str), "%d", itemtype);
|
|
ecpg_raise(lineno, ECPG_UNKNOWN_DESCRIPTOR_ITEM, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, type_str);
|
|
ecpg_free(var);
|
|
va_end(args);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
ecpg_free(var);
|
|
va_end(args);
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Free the descriptor and items in it. */
|
|
static void
|
|
descriptor_free(struct descriptor *desc)
|
|
{
|
|
struct descriptor_item *desc_item;
|
|
|
|
for (desc_item = desc->items; desc_item;)
|
|
{
|
|
struct descriptor_item *di;
|
|
|
|
ecpg_free(desc_item->data);
|
|
di = desc_item;
|
|
desc_item = desc_item->next;
|
|
ecpg_free(di);
|
|
}
|
|
|
|
ecpg_free(desc->name);
|
|
PQclear(desc->result);
|
|
ecpg_free(desc);
|
|
}
|
|
|
|
bool
|
|
ECPGdeallocate_desc(int line, const char *name)
|
|
{
|
|
struct descriptor *desc;
|
|
struct descriptor *prev;
|
|
struct sqlca_t *sqlca = ECPGget_sqlca();
|
|
|
|
if (sqlca == NULL)
|
|
{
|
|
ecpg_raise(line, ECPG_OUT_OF_MEMORY,
|
|
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
|
|
return false;
|
|
}
|
|
|
|
ecpg_init_sqlca(sqlca);
|
|
for (desc = get_descriptors(), prev = NULL; desc; prev = desc, desc = desc->next)
|
|
{
|
|
if (strcmp(name, desc->name) == 0)
|
|
{
|
|
if (prev)
|
|
prev->next = desc->next;
|
|
else
|
|
set_descriptors(desc->next);
|
|
descriptor_free(desc);
|
|
return true;
|
|
}
|
|
}
|
|
ecpg_raise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name);
|
|
return false;
|
|
}
|
|
|
|
#ifdef ENABLE_THREAD_SAFETY
|
|
|
|
/* Deallocate all descriptors in the list */
|
|
static void
|
|
descriptor_deallocate_all(struct descriptor *list)
|
|
{
|
|
while (list)
|
|
{
|
|
struct descriptor *next = list->next;
|
|
|
|
descriptor_free(list);
|
|
list = next;
|
|
}
|
|
}
|
|
#endif /* ENABLE_THREAD_SAFETY */
|
|
|
|
bool
|
|
ECPGallocate_desc(int line, const char *name)
|
|
{
|
|
struct descriptor *new;
|
|
struct sqlca_t *sqlca = ECPGget_sqlca();
|
|
|
|
if (sqlca == NULL)
|
|
{
|
|
ecpg_raise(line, ECPG_OUT_OF_MEMORY,
|
|
ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
|
|
return false;
|
|
}
|
|
|
|
ecpg_init_sqlca(sqlca);
|
|
new = (struct descriptor *) ecpg_alloc(sizeof(struct descriptor), line);
|
|
if (!new)
|
|
return false;
|
|
new->next = get_descriptors();
|
|
new->name = ecpg_alloc(strlen(name) + 1, line);
|
|
if (!new->name)
|
|
{
|
|
ecpg_free(new);
|
|
return false;
|
|
}
|
|
new->count = -1;
|
|
new->items = NULL;
|
|
new->result = PQmakeEmptyPGresult(NULL, 0);
|
|
if (!new->result)
|
|
{
|
|
ecpg_free(new->name);
|
|
ecpg_free(new);
|
|
ecpg_raise(line, ECPG_OUT_OF_MEMORY, ECPG_SQLSTATE_ECPG_OUT_OF_MEMORY, NULL);
|
|
return false;
|
|
}
|
|
strcpy(new->name, name);
|
|
set_descriptors(new);
|
|
return true;
|
|
}
|
|
|
|
/* Find descriptor with name in the connection. */
|
|
struct descriptor *
|
|
ecpg_find_desc(int line, const char *name)
|
|
{
|
|
struct descriptor *desc;
|
|
|
|
for (desc = get_descriptors(); desc; desc = desc->next)
|
|
{
|
|
if (strcmp(name, desc->name) == 0)
|
|
return desc;
|
|
}
|
|
|
|
ecpg_raise(line, ECPG_UNKNOWN_DESCRIPTOR, ECPG_SQLSTATE_INVALID_SQL_DESCRIPTOR_NAME, name);
|
|
return NULL; /* not found */
|
|
}
|
|
|
|
bool
|
|
ECPGdescribe(int line, int compat, bool input, const char *connection_name, const char *stmt_name,...)
|
|
{
|
|
bool ret = false;
|
|
struct connection *con;
|
|
struct prepared_statement *prep;
|
|
PGresult *res;
|
|
va_list args;
|
|
|
|
/* DESCRIBE INPUT is not yet supported */
|
|
if (input)
|
|
{
|
|
ecpg_raise(line, ECPG_UNSUPPORTED, ECPG_SQLSTATE_ECPG_INTERNAL_ERROR, "DESCRIBE INPUT");
|
|
return ret;
|
|
}
|
|
|
|
con = ecpg_get_connection(connection_name);
|
|
if (!con)
|
|
{
|
|
ecpg_raise(line, ECPG_NO_CONN, ECPG_SQLSTATE_CONNECTION_DOES_NOT_EXIST,
|
|
connection_name ? connection_name : ecpg_gettext("NULL"));
|
|
return ret;
|
|
}
|
|
prep = ecpg_find_prepared_statement(stmt_name, con, NULL);
|
|
if (!prep)
|
|
{
|
|
ecpg_raise(line, ECPG_INVALID_STMT, ECPG_SQLSTATE_INVALID_SQL_STATEMENT_NAME, stmt_name);
|
|
return ret;
|
|
}
|
|
|
|
va_start(args, stmt_name);
|
|
|
|
for (;;)
|
|
{
|
|
enum ECPGttype type;
|
|
void *ptr;
|
|
|
|
/* variable type */
|
|
type = va_arg(args, enum ECPGttype);
|
|
|
|
if (type == ECPGt_EORT)
|
|
break;
|
|
|
|
/* rest of variable parameters */
|
|
ptr = va_arg(args, void *);
|
|
(void) va_arg(args, long); /* skip args */
|
|
(void) va_arg(args, long);
|
|
(void) va_arg(args, long);
|
|
|
|
/* variable indicator */
|
|
(void) va_arg(args, enum ECPGttype);
|
|
(void) va_arg(args, void *); /* skip args */
|
|
(void) va_arg(args, long);
|
|
(void) va_arg(args, long);
|
|
(void) va_arg(args, long);
|
|
|
|
switch (type)
|
|
{
|
|
case ECPGt_descriptor:
|
|
{
|
|
char *name = ptr;
|
|
struct descriptor *desc = ecpg_find_desc(line, name);
|
|
|
|
if (desc == NULL)
|
|
break;
|
|
|
|
res = PQdescribePrepared(con->connection, stmt_name);
|
|
if (!ecpg_check_PQresult(res, line, con->connection, compat))
|
|
break;
|
|
|
|
if (desc->result != NULL)
|
|
PQclear(desc->result);
|
|
|
|
desc->result = res;
|
|
ret = true;
|
|
break;
|
|
}
|
|
case ECPGt_sqlda:
|
|
{
|
|
if (INFORMIX_MODE(compat))
|
|
{
|
|
struct sqlda_compat **_sqlda = ptr;
|
|
struct sqlda_compat *sqlda;
|
|
|
|
res = PQdescribePrepared(con->connection, stmt_name);
|
|
if (!ecpg_check_PQresult(res, line, con->connection, compat))
|
|
break;
|
|
|
|
sqlda = ecpg_build_compat_sqlda(line, res, -1, compat);
|
|
if (sqlda)
|
|
{
|
|
struct sqlda_compat *sqlda_old = *_sqlda;
|
|
struct sqlda_compat *sqlda_old1;
|
|
|
|
while (sqlda_old)
|
|
{
|
|
sqlda_old1 = sqlda_old->desc_next;
|
|
free(sqlda_old);
|
|
sqlda_old = sqlda_old1;
|
|
}
|
|
|
|
*_sqlda = sqlda;
|
|
ret = true;
|
|
}
|
|
|
|
PQclear(res);
|
|
}
|
|
else
|
|
{
|
|
struct sqlda_struct **_sqlda = ptr;
|
|
struct sqlda_struct *sqlda;
|
|
|
|
res = PQdescribePrepared(con->connection, stmt_name);
|
|
if (!ecpg_check_PQresult(res, line, con->connection, compat))
|
|
break;
|
|
|
|
sqlda = ecpg_build_native_sqlda(line, res, -1, compat);
|
|
if (sqlda)
|
|
{
|
|
struct sqlda_struct *sqlda_old = *_sqlda;
|
|
struct sqlda_struct *sqlda_old1;
|
|
|
|
while (sqlda_old)
|
|
{
|
|
sqlda_old1 = sqlda_old->desc_next;
|
|
free(sqlda_old);
|
|
sqlda_old = sqlda_old1;
|
|
}
|
|
|
|
*_sqlda = sqlda;
|
|
ret = true;
|
|
}
|
|
|
|
PQclear(res);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
/* nothing else may come */
|
|
;
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
return ret;
|
|
}
|