summaryrefslogtreecommitdiffstats
path: root/src/interfaces/ecpg/ecpglib/descriptor.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:15:05 +0000
commit46651ce6fe013220ed397add242004d764fc0153 (patch)
tree6e5299f990f88e60174a1d3ae6e48eedd2688b2b /src/interfaces/ecpg/ecpglib/descriptor.c
parentInitial commit. (diff)
downloadpostgresql-14-upstream.tar.xz
postgresql-14-upstream.zip
Adding upstream version 14.5.upstream/14.5upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/interfaces/ecpg/ecpglib/descriptor.c')
-rw-r--r--src/interfaces/ecpg/ecpglib/descriptor.c1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/ecpglib/descriptor.c b/src/interfaces/ecpg/ecpglib/descriptor.c
new file mode 100644
index 0000000..f1898de
--- /dev/null
+++ b/src/interfaces/ecpg/ecpglib/descriptor.c
@@ -0,0 +1,1002 @@
+/* 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 "sql3types.h"
+#include "sqlca.h"
+#include "sqlda.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;
+ 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;
+ 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;
+ case ECPGt_long_long:
+ *target = *(const long long int *) var;
+ break;
+ case ECPGt_unsigned_long_long:
+ *target = *(const unsigned long long int *) var;
+ break;
+ 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
+
+ /*
+ * To get here, the above PQnfields() test must have found nonzero
+ * fields. One needs a connection to create such a descriptor. (EXEC
+ * SQL SET DESCRIPTOR can populate the descriptor's "items", but it
+ * can't change the descriptor's PQnfields().) Any successful
+ * connection initializes ecpg_clocale.
+ */
+ Assert(ecpg_clocale);
+ stmt.oldlocale = uselocale(ecpg_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);
+#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_bytea *variable =
+ (struct ECPGgeneric_bytea *) (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;
+}