/* * 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; } }