| /* 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" |
| #include "common/mdb_locale.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; |
| |
| 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; |
| } |