diff options
Diffstat (limited to 'src/interfaces/ecpg/ecpglib/sqlda.c')
-rw-r--r-- | src/interfaces/ecpg/ecpglib/sqlda.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/ecpglib/sqlda.c b/src/interfaces/ecpg/ecpglib/sqlda.c new file mode 100644 index 0000000..081e326 --- /dev/null +++ b/src/interfaces/ecpg/ecpglib/sqlda.c @@ -0,0 +1,592 @@ +/* + * SQLDA support routines + * + * The allocated memory area pointed by an sqlda pointer + * contains both the metadata and the data, so freeing up + * is a simple free(sqlda) as expected by the ESQL/C examples. + */ + +#define POSTGRES_ECPG_INTERNAL +#include "postgres_fe.h" + +#include "catalog/pg_type_d.h" +#include "decimal.h" +#include "ecpg-pthread-win32.h" +#include "ecpgerrno.h" +#include "ecpglib.h" +#include "ecpglib_extern.h" +#include "ecpgtype.h" +#include "sqlca.h" +#include "sqlda-compat.h" +#include "sqlda-native.h" + +/* + * Compute the next variable's offset with + * the current variable's size and alignment. + * + * + * Returns: + * - the current variable's offset in *current + * - the next variable's offset in *next + */ +static void +ecpg_sqlda_align_add_size(long offset, int alignment, int size, long *current, long *next) +{ + if (offset % alignment) + offset += alignment - (offset % alignment); + if (current) + *current = offset; + offset += size; + if (next) + *next = offset; +} + +static long +sqlda_compat_empty_size(const PGresult *res) +{ + long offset; + int i; + int sqld = PQnfields(res); + + /* Initial size to store main structure and field structures */ + offset = sizeof(struct sqlda_compat) + sqld * sizeof(struct sqlvar_compat); + + /* Add space for field names */ + for (i = 0; i < sqld; i++) + offset += strlen(PQfname(res, i)) + 1; + + /* Add padding to the first field value */ + ecpg_sqlda_align_add_size(offset, sizeof(int), 0, &offset, NULL); + + return offset; +} + +static long +sqlda_common_total_size(const PGresult *res, int row, enum COMPAT_MODE compat, long offset) +{ + int sqld = PQnfields(res); + int i; + long next_offset; + + /* Add space for the field values */ + for (i = 0; i < sqld; i++) + { + enum ECPGttype type = sqlda_dynamic_type(PQftype(res, i), compat); + + switch (type) + { + case ECPGt_short: + case ECPGt_unsigned_short: + ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset); + break; + case ECPGt_int: + case ECPGt_unsigned_int: + ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset); + break; + case ECPGt_long: + case ECPGt_unsigned_long: + ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset); + break; + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset); + break; + case ECPGt_bool: + ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset); + break; + case ECPGt_float: + ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset); + break; + case ECPGt_double: + ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset); + break; + case ECPGt_decimal: + ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset); + break; + case ECPGt_numeric: + + /* + * We align the numeric struct to allow it to store a pointer, + * while the digits array is aligned to int (which seems like + * overkill, but let's keep compatibility here). + * + * Unfortunately we need to deconstruct the value twice to + * find out the digits array's size and then later fill it. + */ + ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset); + if (!PQgetisnull(res, row, i)) + { + char *val = PQgetvalue(res, row, i); + numeric *num; + + num = PGTYPESnumeric_from_asc(val, NULL); + if (!num) + break; + if (num->buf) + ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset); + PGTYPESnumeric_free(num); + } + break; + case ECPGt_date: + ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset); + break; + case ECPGt_timestamp: + ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset); + break; + case ECPGt_interval: + ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset); + break; + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_string: + default: + { + long datalen = strlen(PQgetvalue(res, row, i)) + 1; + + ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset); + break; + } + } + offset = next_offset; + } + return offset; +} + + +static long +sqlda_compat_total_size(const PGresult *res, int row, enum COMPAT_MODE compat) +{ + long offset; + + offset = sqlda_compat_empty_size(res); + + if (row < 0) + return offset; + + offset = sqlda_common_total_size(res, row, compat, offset); + return offset; +} + +static long +sqlda_native_empty_size(const PGresult *res) +{ + long offset; + int sqld = PQnfields(res); + + /* Initial size to store main structure and field structures */ + offset = sizeof(struct sqlda_struct) + (sqld - 1) * sizeof(struct sqlvar_struct); + + /* Add padding to the first field value */ + ecpg_sqlda_align_add_size(offset, sizeof(int), 0, &offset, NULL); + + return offset; +} + +static long +sqlda_native_total_size(const PGresult *res, int row, enum COMPAT_MODE compat) +{ + long offset; + + offset = sqlda_native_empty_size(res); + + if (row < 0) + return offset; + + offset = sqlda_common_total_size(res, row, compat, offset); + return offset; +} + +/* + * Build "struct sqlda_compat" (metadata only) from PGresult + * leaving enough space for the field values in + * the given row number + */ +struct sqlda_compat * +ecpg_build_compat_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compat) +{ + struct sqlda_compat *sqlda; + struct sqlvar_compat *sqlvar; + char *fname; + long size; + int sqld; + int i; + + size = sqlda_compat_total_size(res, row, compat); + sqlda = (struct sqlda_compat *) ecpg_alloc(size, line); + if (!sqlda) + return NULL; + + memset(sqlda, 0, size); + sqlvar = (struct sqlvar_compat *) (sqlda + 1); + sqld = PQnfields(res); + fname = (char *) (sqlvar + sqld); + + sqlda->sqld = sqld; + ecpg_log("ecpg_build_compat_sqlda on line %d sqld = %d\n", line, sqld); + sqlda->desc_occ = size; /* cheat here, keep the full allocated size */ + sqlda->sqlvar = sqlvar; + + for (i = 0; i < sqlda->sqld; i++) + { + sqlda->sqlvar[i].sqltype = sqlda_dynamic_type(PQftype(res, i), compat); + strcpy(fname, PQfname(res, i)); + sqlda->sqlvar[i].sqlname = fname; + fname += strlen(sqlda->sqlvar[i].sqlname) + 1; + + /* + * this is reserved for future use, so we leave it empty for the time + * being + */ + /* sqlda->sqlvar[i].sqlformat = (char *) (long) PQfformat(res, i); */ + sqlda->sqlvar[i].sqlxid = PQftype(res, i); + sqlda->sqlvar[i].sqltypelen = PQfsize(res, i); + } + + return sqlda; +} + +/* + * Sets values from PGresult. + */ +static int16 value_is_null = -1; +static int16 value_is_not_null = 0; + +void +ecpg_set_compat_sqlda(int lineno, struct sqlda_compat **_sqlda, const PGresult *res, int row, enum COMPAT_MODE compat) +{ + struct sqlda_compat *sqlda = (*_sqlda); + int i; + long offset, + next_offset; + + if (row < 0) + return; + + /* Offset for the first field value */ + offset = sqlda_compat_empty_size(res); + + /* + * Set sqlvar[i]->sqldata pointers and convert values to correct format + */ + for (i = 0; i < sqlda->sqld; i++) + { + int isnull; + int datalen; + bool set_data = true; + + switch (sqlda->sqlvar[i].sqltype) + { + case ECPGt_short: + case ECPGt_unsigned_short: + ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(short); + break; + case ECPGt_int: + case ECPGt_unsigned_int: + ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(int); + break; + case ECPGt_long: + case ECPGt_unsigned_long: + ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(long); + break; + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(long long); + break; + case ECPGt_bool: + ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(bool); + break; + case ECPGt_float: + ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(float); + break; + case ECPGt_double: + ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(double); + break; + case ECPGt_decimal: + ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(decimal); + break; + case ECPGt_numeric: + { + numeric *num; + char *val; + + set_data = false; + + ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(numeric); + + if (PQgetisnull(res, row, i)) + { + ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata); + break; + } + + val = PQgetvalue(res, row, i); + num = PGTYPESnumeric_from_asc(val, NULL); + if (!num) + { + ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata); + break; + } + + memcpy(sqlda->sqlvar[i].sqldata, num, sizeof(numeric)); + + if (num->buf) + { + ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset); + memcpy((char *) sqlda + offset, num->buf, num->digits - num->buf + num->ndigits); + + ((numeric *) sqlda->sqlvar[i].sqldata)->buf = (NumericDigit *) sqlda + offset; + ((numeric *) sqlda->sqlvar[i].sqldata)->digits = (NumericDigit *) sqlda + offset + (num->digits - num->buf); + } + + PGTYPESnumeric_free(num); + + break; + } + case ECPGt_date: + ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(date); + break; + case ECPGt_timestamp: + ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(timestamp); + break; + case ECPGt_interval: + ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(interval); + break; + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_string: + default: + datalen = strlen(PQgetvalue(res, row, i)) + 1; + ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = datalen; + if (datalen > 32768) + sqlda->sqlvar[i].sqlilongdata = sqlda->sqlvar[i].sqldata; + break; + } + + isnull = PQgetisnull(res, row, i); + ecpg_log("ecpg_set_compat_sqlda on line %d row %d col %d %s\n", lineno, row, i, isnull ? "IS NULL" : "IS NOT NULL"); + sqlda->sqlvar[i].sqlind = isnull ? &value_is_null : &value_is_not_null; + sqlda->sqlvar[i].sqlitype = ECPGt_short; + sqlda->sqlvar[i].sqlilen = sizeof(short); + if (!isnull) + { + if (set_data) + ecpg_get_data(res, row, i, lineno, + sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR, + sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0, + ECPG_ARRAY_NONE, compat, false); + } + else + ECPGset_noind_null(sqlda->sqlvar[i].sqltype, sqlda->sqlvar[i].sqldata); + + offset = next_offset; + } +} + +struct sqlda_struct * +ecpg_build_native_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compat) +{ + struct sqlda_struct *sqlda; + long size; + int i; + + size = sqlda_native_total_size(res, row, compat); + sqlda = (struct sqlda_struct *) ecpg_alloc(size, line); + if (!sqlda) + return NULL; + + memset(sqlda, 0, size); + + sprintf(sqlda->sqldaid, "SQLDA "); + sqlda->sqld = sqlda->sqln = PQnfields(res); + ecpg_log("ecpg_build_native_sqlda on line %d sqld = %d\n", line, sqlda->sqld); + sqlda->sqldabc = sizeof(struct sqlda_struct) + (sqlda->sqld - 1) * sizeof(struct sqlvar_struct); + + for (i = 0; i < sqlda->sqld; i++) + { + char *fname; + + sqlda->sqlvar[i].sqltype = sqlda_dynamic_type(PQftype(res, i), compat); + fname = PQfname(res, i); + sqlda->sqlvar[i].sqlname.length = strlen(fname); + strcpy(sqlda->sqlvar[i].sqlname.data, fname); + } + + return sqlda; +} + +void +ecpg_set_native_sqlda(int lineno, struct sqlda_struct **_sqlda, const PGresult *res, int row, enum COMPAT_MODE compat) +{ + struct sqlda_struct *sqlda = (*_sqlda); + int i; + long offset, + next_offset; + + if (row < 0) + return; + + /* Offset for the first field value */ + offset = sqlda_native_empty_size(res); + + /* + * Set sqlvar[i]->sqldata pointers and convert values to correct format + */ + for (i = 0; i < sqlda->sqld; i++) + { + int isnull; + int datalen; + bool set_data = true; + + switch (sqlda->sqlvar[i].sqltype) + { + case ECPGt_short: + case ECPGt_unsigned_short: + ecpg_sqlda_align_add_size(offset, sizeof(short), sizeof(short), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(short); + break; + case ECPGt_int: + case ECPGt_unsigned_int: + ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(int), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(int); + break; + case ECPGt_long: + case ECPGt_unsigned_long: + ecpg_sqlda_align_add_size(offset, sizeof(long), sizeof(long), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(long); + break; + case ECPGt_long_long: + case ECPGt_unsigned_long_long: + ecpg_sqlda_align_add_size(offset, sizeof(long long), sizeof(long long), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(long long); + break; + case ECPGt_bool: + ecpg_sqlda_align_add_size(offset, sizeof(bool), sizeof(bool), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(bool); + break; + case ECPGt_float: + ecpg_sqlda_align_add_size(offset, sizeof(float), sizeof(float), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(float); + break; + case ECPGt_double: + ecpg_sqlda_align_add_size(offset, sizeof(double), sizeof(double), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(double); + break; + case ECPGt_decimal: + ecpg_sqlda_align_add_size(offset, sizeof(int), sizeof(decimal), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(decimal); + break; + case ECPGt_numeric: + { + numeric *num; + char *val; + + set_data = false; + + ecpg_sqlda_align_add_size(offset, sizeof(NumericDigit *), sizeof(numeric), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(numeric); + + if (PQgetisnull(res, row, i)) + { + ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata); + break; + } + + val = PQgetvalue(res, row, i); + num = PGTYPESnumeric_from_asc(val, NULL); + if (!num) + { + ECPGset_noind_null(ECPGt_numeric, sqlda->sqlvar[i].sqldata); + break; + } + + memcpy(sqlda->sqlvar[i].sqldata, num, sizeof(numeric)); + + if (num->buf) + { + ecpg_sqlda_align_add_size(next_offset, sizeof(int), num->digits - num->buf + num->ndigits, &offset, &next_offset); + memcpy((char *) sqlda + offset, num->buf, num->digits - num->buf + num->ndigits); + + ((numeric *) sqlda->sqlvar[i].sqldata)->buf = (NumericDigit *) sqlda + offset; + ((numeric *) sqlda->sqlvar[i].sqldata)->digits = (NumericDigit *) sqlda + offset + (num->digits - num->buf); + } + + PGTYPESnumeric_free(num); + + break; + } + case ECPGt_date: + ecpg_sqlda_align_add_size(offset, sizeof(date), sizeof(date), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(date); + break; + case ECPGt_timestamp: + ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(timestamp), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(timestamp); + break; + case ECPGt_interval: + ecpg_sqlda_align_add_size(offset, sizeof(int64), sizeof(interval), &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = sizeof(interval); + break; + case ECPGt_char: + case ECPGt_unsigned_char: + case ECPGt_string: + default: + datalen = strlen(PQgetvalue(res, row, i)) + 1; + ecpg_sqlda_align_add_size(offset, sizeof(int), datalen, &offset, &next_offset); + sqlda->sqlvar[i].sqldata = (char *) sqlda + offset; + sqlda->sqlvar[i].sqllen = datalen; + break; + } + + isnull = PQgetisnull(res, row, i); + ecpg_log("ecpg_set_native_sqlda on line %d row %d col %d %s\n", lineno, row, i, isnull ? "IS NULL" : "IS NOT NULL"); + sqlda->sqlvar[i].sqlind = isnull ? &value_is_null : &value_is_not_null; + if (!isnull) + { + if (set_data) + ecpg_get_data(res, row, i, lineno, + sqlda->sqlvar[i].sqltype, ECPGt_NO_INDICATOR, + sqlda->sqlvar[i].sqldata, NULL, 0, 0, 0, + ECPG_ARRAY_NONE, compat, false); + } + + offset = next_offset; + } +} |