diff options
Diffstat (limited to 'src/interfaces/ecpg/compatlib/informix.c')
-rw-r--r-- | src/interfaces/ecpg/compatlib/informix.c | 1027 |
1 files changed, 1027 insertions, 0 deletions
diff --git a/src/interfaces/ecpg/compatlib/informix.c b/src/interfaces/ecpg/compatlib/informix.c new file mode 100644 index 0000000..dccf395 --- /dev/null +++ b/src/interfaces/ecpg/compatlib/informix.c @@ -0,0 +1,1027 @@ +/* src/interfaces/ecpg/compatlib/informix.c */ + +#define POSTGRES_ECPG_INTERNAL +#include "postgres_fe.h" + +#include <math.h> +#include <ctype.h> +#include <limits.h> + +#include "ecpg_informix.h" +#include "ecpgerrno.h" +#include "ecpgtype.h" +#include "pgtypes_date.h" +#include "pgtypes_error.h" +#include "pgtypes_numeric.h" +#include "sqlca.h" +#include "sqltypes.h" + +/* this is also defined in ecpglib/misc.c, by defining it twice we don't have to export the symbol */ + +static struct sqlca_t sqlca_init = +{ + { + 'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' ' + }, + sizeof(struct sqlca_t), + 0, + { + 0, + { + 0 + } + }, + { + 'N', 'O', 'T', ' ', 'S', 'E', 'T', ' ' + }, + { + 0, 0, 0, 0, 0, 0 + }, + { + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + '0', '0', '0', '0', '0' + } +}; +static int +deccall2(decimal *arg1, decimal *arg2, int (*ptr) (numeric *, numeric *)) +{ + numeric *a1, + *a2; + int i; + + if ((a1 = PGTYPESnumeric_new()) == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + if ((a2 = PGTYPESnumeric_new()) == NULL) + { + PGTYPESnumeric_free(a1); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + if (PGTYPESnumeric_from_decimal(arg1, a1) != 0) + { + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + if (PGTYPESnumeric_from_decimal(arg2, a2) != 0) + { + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + i = (*ptr) (a1, a2); + + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + + return i; +} + +static int +deccall3(decimal *arg1, decimal *arg2, decimal *result, int (*ptr) (numeric *, numeric *, numeric *)) +{ + numeric *a1, + *a2, + *nres; + int i; + + /* + * we must NOT set the result to NULL here because it may be the same + * variable as one of the arguments + */ + if (risnull(CDECIMALTYPE, (char *) arg1) || risnull(CDECIMALTYPE, (char *) arg2)) + return 0; + + if ((a1 = PGTYPESnumeric_new()) == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + if ((a2 = PGTYPESnumeric_new()) == NULL) + { + PGTYPESnumeric_free(a1); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + if ((nres = PGTYPESnumeric_new()) == NULL) + { + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + if (PGTYPESnumeric_from_decimal(arg1, a1) != 0) + { + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + PGTYPESnumeric_free(nres); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + if (PGTYPESnumeric_from_decimal(arg2, a2) != 0) + { + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + PGTYPESnumeric_free(nres); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + i = (*ptr) (a1, a2, nres); + + if (i == 0) /* No error */ + { + + /* set the result to null in case it errors out later */ + rsetnull(CDECIMALTYPE, (char *) result); + PGTYPESnumeric_to_decimal(nres, result); + } + + PGTYPESnumeric_free(nres); + PGTYPESnumeric_free(a1); + PGTYPESnumeric_free(a2); + + return i; +} + +/* we start with the numeric functions */ +int +decadd(decimal *arg1, decimal *arg2, decimal *sum) +{ + errno = 0; + deccall3(arg1, arg2, sum, PGTYPESnumeric_add); + + if (errno == PGTYPES_NUM_OVERFLOW) + return ECPG_INFORMIX_NUM_OVERFLOW; + else if (errno == PGTYPES_NUM_UNDERFLOW) + return ECPG_INFORMIX_NUM_UNDERFLOW; + else if (errno != 0) + return -1; + else + return 0; +} + +int +deccmp(decimal *arg1, decimal *arg2) +{ + return deccall2(arg1, arg2, PGTYPESnumeric_cmp); +} + +void +deccopy(decimal *src, decimal *target) +{ + memcpy(target, src, sizeof(decimal)); +} + +int +deccvasc(const char *cp, int len, decimal *np) +{ + char *str; + int ret = 0; + numeric *result; + + rsetnull(CDECIMALTYPE, (char *) np); + if (risnull(CSTRINGTYPE, cp)) + return 0; + + str = pnstrdup(cp, len); /* decimal_in always converts the complete + * string */ + if (!str) + ret = ECPG_INFORMIX_NUM_UNDERFLOW; + else + { + errno = 0; + result = PGTYPESnumeric_from_asc(str, NULL); + if (!result) + { + switch (errno) + { + case PGTYPES_NUM_OVERFLOW: + ret = ECPG_INFORMIX_NUM_OVERFLOW; + break; + case PGTYPES_NUM_BAD_NUMERIC: + ret = ECPG_INFORMIX_BAD_NUMERIC; + break; + default: + ret = ECPG_INFORMIX_BAD_EXPONENT; + break; + } + } + else + { + int i = PGTYPESnumeric_to_decimal(result, np); + + PGTYPESnumeric_free(result); + if (i != 0) + ret = ECPG_INFORMIX_NUM_OVERFLOW; + } + } + + free(str); + return ret; +} + +int +deccvdbl(double dbl, decimal *np) +{ + numeric *nres; + int result = 1; + + rsetnull(CDECIMALTYPE, (char *) np); + if (risnull(CDOUBLETYPE, (char *) &dbl)) + return 0; + + nres = PGTYPESnumeric_new(); + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + result = PGTYPESnumeric_from_double(dbl, nres); + if (result == 0) + result = PGTYPESnumeric_to_decimal(nres, np); + + PGTYPESnumeric_free(nres); + return result; +} + +int +deccvint(int in, decimal *np) +{ + numeric *nres; + int result = 1; + + rsetnull(CDECIMALTYPE, (char *) np); + if (risnull(CINTTYPE, (char *) &in)) + return 0; + + nres = PGTYPESnumeric_new(); + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + result = PGTYPESnumeric_from_int(in, nres); + if (result == 0) + result = PGTYPESnumeric_to_decimal(nres, np); + + PGTYPESnumeric_free(nres); + return result; +} + +int +deccvlong(long lng, decimal *np) +{ + numeric *nres; + int result = 1; + + rsetnull(CDECIMALTYPE, (char *) np); + if (risnull(CLONGTYPE, (char *) &lng)) + return 0; + + nres = PGTYPESnumeric_new(); + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + result = PGTYPESnumeric_from_long(lng, nres); + if (result == 0) + result = PGTYPESnumeric_to_decimal(nres, np); + + PGTYPESnumeric_free(nres); + return result; +} + +int +decdiv(decimal *n1, decimal *n2, decimal *result) +{ + int i; + + errno = 0; + i = deccall3(n1, n2, result, PGTYPESnumeric_div); + + if (i != 0) + switch (errno) + { + case PGTYPES_NUM_DIVIDE_ZERO: + return ECPG_INFORMIX_DIVIDE_ZERO; + break; + case PGTYPES_NUM_OVERFLOW: + return ECPG_INFORMIX_NUM_OVERFLOW; + break; + default: + return ECPG_INFORMIX_NUM_UNDERFLOW; + break; + } + + return 0; +} + +int +decmul(decimal *n1, decimal *n2, decimal *result) +{ + int i; + + errno = 0; + i = deccall3(n1, n2, result, PGTYPESnumeric_mul); + + if (i != 0) + switch (errno) + { + case PGTYPES_NUM_OVERFLOW: + return ECPG_INFORMIX_NUM_OVERFLOW; + break; + default: + return ECPG_INFORMIX_NUM_UNDERFLOW; + break; + } + + return 0; +} + +int +decsub(decimal *n1, decimal *n2, decimal *result) +{ + int i; + + errno = 0; + i = deccall3(n1, n2, result, PGTYPESnumeric_sub); + + if (i != 0) + switch (errno) + { + case PGTYPES_NUM_OVERFLOW: + return ECPG_INFORMIX_NUM_OVERFLOW; + break; + default: + return ECPG_INFORMIX_NUM_UNDERFLOW; + break; + } + + return 0; +} + +int +dectoasc(decimal *np, char *cp, int len, int right) +{ + char *str; + numeric *nres; + + rsetnull(CSTRINGTYPE, (char *) cp); + if (risnull(CDECIMALTYPE, (char *) np)) + return 0; + + nres = PGTYPESnumeric_new(); + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + if (PGTYPESnumeric_from_decimal(np, nres) != 0) + { + PGTYPESnumeric_free(nres); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + if (right >= 0) + str = PGTYPESnumeric_to_asc(nres, right); + else + str = PGTYPESnumeric_to_asc(nres, nres->dscale); + + PGTYPESnumeric_free(nres); + if (!str) + return -1; + + /* + * TODO: have to take care of len here and create exponential notation if + * necessary + */ + if ((int) (strlen(str) + 1) > len) + { + if (len > 1) + { + cp[0] = '*'; + cp[1] = '\0'; + } + free(str); + return -1; + } + else + { + strcpy(cp, str); + free(str); + return 0; + } +} + +int +dectodbl(decimal *np, double *dblp) +{ + int i; + numeric *nres = PGTYPESnumeric_new(); + + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + if (PGTYPESnumeric_from_decimal(np, nres) != 0) + { + PGTYPESnumeric_free(nres); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + i = PGTYPESnumeric_to_double(nres, dblp); + PGTYPESnumeric_free(nres); + + return i; +} + +int +dectoint(decimal *np, int *ip) +{ + int ret; + numeric *nres = PGTYPESnumeric_new(); + + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + if (PGTYPESnumeric_from_decimal(np, nres) != 0) + { + PGTYPESnumeric_free(nres); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + ret = PGTYPESnumeric_to_int(nres, ip); + PGTYPESnumeric_free(nres); + + if (ret == PGTYPES_NUM_OVERFLOW) + ret = ECPG_INFORMIX_NUM_OVERFLOW; + + return ret; +} + +int +dectolong(decimal *np, long *lngp) +{ + int ret; + numeric *nres = PGTYPESnumeric_new(); + + if (nres == NULL) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + if (PGTYPESnumeric_from_decimal(np, nres) != 0) + { + PGTYPESnumeric_free(nres); + return ECPG_INFORMIX_OUT_OF_MEMORY; + } + + ret = PGTYPESnumeric_to_long(nres, lngp); + PGTYPESnumeric_free(nres); + + if (ret == PGTYPES_NUM_OVERFLOW) + ret = ECPG_INFORMIX_NUM_OVERFLOW; + + return ret; +} + +/* Now the date functions */ +int +rdatestr(date d, char *str) +{ + char *tmp = PGTYPESdate_to_asc(d); + + if (!tmp) + return ECPG_INFORMIX_DATE_CONVERT; + + /* move to user allocated buffer */ + strcpy(str, tmp); + free(tmp); + + return 0; +} + +/* +* +* the input for this function is mmddyyyy and any non-numeric +* character can be used as a separator +* +*/ +int +rstrdate(const char *str, date * d) +{ + return rdefmtdate(d, "mm/dd/yyyy", str); +} + +void +rtoday(date * d) +{ + PGTYPESdate_today(d); +} + +int +rjulmdy(date d, short *mdy) +{ + int mdy_int[3]; + + PGTYPESdate_julmdy(d, mdy_int); + mdy[0] = (short) mdy_int[0]; + mdy[1] = (short) mdy_int[1]; + mdy[2] = (short) mdy_int[2]; + return 0; +} + +int +rdefmtdate(date * d, const char *fmt, const char *str) +{ + /* TODO: take care of DBCENTURY environment variable */ + /* PGSQL functions allow all centuries */ + + errno = 0; + if (PGTYPESdate_defmt_asc(d, fmt, str) == 0) + return 0; + + switch (errno) + { + case PGTYPES_DATE_ERR_ENOSHORTDATE: + return ECPG_INFORMIX_ENOSHORTDATE; + case PGTYPES_DATE_ERR_EARGS: + case PGTYPES_DATE_ERR_ENOTDMY: + return ECPG_INFORMIX_ENOTDMY; + case PGTYPES_DATE_BAD_DAY: + return ECPG_INFORMIX_BAD_DAY; + case PGTYPES_DATE_BAD_MONTH: + return ECPG_INFORMIX_BAD_MONTH; + default: + return ECPG_INFORMIX_BAD_YEAR; + } +} + +int +rfmtdate(date d, const char *fmt, char *str) +{ + errno = 0; + if (PGTYPESdate_fmt_asc(d, fmt, str) == 0) + return 0; + + if (errno == ENOMEM) + return ECPG_INFORMIX_OUT_OF_MEMORY; + + return ECPG_INFORMIX_DATE_CONVERT; +} + +int +rmdyjul(short *mdy, date * d) +{ + int mdy_int[3]; + + mdy_int[0] = mdy[0]; + mdy_int[1] = mdy[1]; + mdy_int[2] = mdy[2]; + PGTYPESdate_mdyjul(mdy_int, d); + return 0; +} + +int +rdayofweek(date d) +{ + return PGTYPESdate_dayofweek(d); +} + +/* And the datetime stuff */ + +void +dtcurrent(timestamp * ts) +{ + PGTYPEStimestamp_current(ts); +} + +int +dtcvasc(char *str, timestamp * ts) +{ + timestamp ts_tmp; + int i; + char **endptr = &str; + + errno = 0; + ts_tmp = PGTYPEStimestamp_from_asc(str, endptr); + i = errno; + if (i) + /* TODO: rewrite to Informix error codes */ + return i; + if (**endptr) + { + /* extra characters exist at the end */ + return ECPG_INFORMIX_EXTRA_CHARS; + } + /* TODO: other Informix error codes missing */ + + /* everything went fine */ + *ts = ts_tmp; + + return 0; +} + +int +dtcvfmtasc(char *inbuf, char *fmtstr, timestamp * dtvalue) +{ + return PGTYPEStimestamp_defmt_asc(inbuf, fmtstr, dtvalue); +} + +int +dtsub(timestamp * ts1, timestamp * ts2, interval * iv) +{ + return PGTYPEStimestamp_sub(ts1, ts2, iv); +} + +int +dttoasc(timestamp * ts, char *output) +{ + char *asctime = PGTYPEStimestamp_to_asc(*ts); + + strcpy(output, asctime); + free(asctime); + return 0; +} + +int +dttofmtasc(timestamp * ts, char *output, int str_len, char *fmtstr) +{ + return PGTYPEStimestamp_fmt_asc(ts, output, str_len, fmtstr); +} + +int +intoasc(interval * i, char *str) +{ + char *tmp; + + errno = 0; + tmp = PGTYPESinterval_to_asc(i); + + if (!tmp) + return -errno; + + memcpy(str, tmp, strlen(tmp)); + free(tmp); + return 0; +} + +static struct +{ + long val; + int maxdigits; + int digits; + int remaining; + char sign; + char *val_string; +} value; + +/** + * initialize the struct, which holds the different forms + * of the long value + */ +static int +initValue(long lng_val) +{ + int i, + j; + long l, + dig; + + /* set some obvious things */ + value.val = lng_val >= 0 ? lng_val : lng_val * (-1); + value.sign = lng_val >= 0 ? '+' : '-'; + value.maxdigits = log10(2) * (8 * sizeof(long) - 1); + + /* determine the number of digits */ + i = 0; + l = 1; + do + { + i++; + l *= 10; + } + while ((l - 1) < value.val && l <= LONG_MAX / 10); + + if (l <= LONG_MAX / 10) + { + value.digits = i; + l /= 10; + } + else + value.digits = i + 1; + + value.remaining = value.digits; + + /* convert the long to string */ + if ((value.val_string = (char *) malloc(value.digits + 1)) == NULL) + return -1; + dig = value.val; + for (i = value.digits, j = 0; i > 0; i--, j++) + { + value.val_string[j] = dig / l + '0'; + dig = dig % l; + l /= 10; + } + value.val_string[value.digits] = '\0'; + return 0; +} + +/* return the position of the right-most dot in some string */ +static int +getRightMostDot(const char *str) +{ + size_t len = strlen(str); + int i, + j; + + j = 0; + for (i = len - 1; i >= 0; i--) + { + if (str[i] == '.') + return len - j - 1; + j++; + } + return -1; +} + +/* And finally some misc functions */ +int +rfmtlong(long lng_val, const char *fmt, char *outbuf) +{ + size_t fmt_len = strlen(fmt); + size_t temp_len; + int i, + j, /* position in temp */ + k, + dotpos; + int leftalign = 0, + blank = 0, + sign = 0, + entitydone = 0, + signdone = 0, + brackets_ok = 0; + char *temp; + char tmp[2] = " "; + char lastfmt = ' ', + fmtchar = ' '; + + temp = (char *) malloc(fmt_len + 1); + if (!temp) + { + errno = ENOMEM; + return -1; + } + + /* put all info about the long in a struct */ + if (initValue(lng_val) == -1) + { + free(temp); + errno = ENOMEM; + return -1; + } + + /* '<' is the only format, where we have to align left */ + if (strchr(fmt, (int) '<')) + leftalign = 1; + + /* '(' requires ')' */ + if (strchr(fmt, (int) '(') && strchr(fmt, (int) ')')) + brackets_ok = 1; + + /* get position of the right-most dot in the format-string */ + /* and fill the temp-string wit '0's up to there. */ + dotpos = getRightMostDot(fmt); + + /* start to parse the format-string */ + temp[0] = '\0'; + k = value.digits - 1; /* position in the value_string */ + for (i = fmt_len - 1, j = 0; i >= 0; i--, j++) + { + /* qualify, where we are in the value_string */ + if (k < 0) + { + blank = 1; + if (k == -1) + sign = 1; + if (leftalign) + { + /* can't use strncat(,,0) here, Solaris would freak out */ + if (sign) + if (signdone) + { + temp[j] = '\0'; + break; + } + } + } + /* if we're right side of the right-most dot, print '0' */ + if (dotpos >= 0 && dotpos <= i) + { + if (dotpos < i) + { + if (fmt[i] == ')') + tmp[0] = value.sign == '-' ? ')' : ' '; + else + tmp[0] = '0'; + } + else + tmp[0] = '.'; + strcat(temp, tmp); + continue; + } + /* the ',' needs special attention, if it is in the blank area */ + if (blank && fmt[i] == ',') + fmtchar = lastfmt; + else + fmtchar = fmt[i]; + /* waiting for the sign */ + if (k < 0 && leftalign && sign && !signdone && fmtchar != '+' && fmtchar != '-') + continue; + /* analyse this format-char */ + switch (fmtchar) + { + case ',': + tmp[0] = ','; + k++; + break; + case '*': + if (blank) + tmp[0] = '*'; + else + tmp[0] = value.val_string[k]; + break; + case '&': + if (blank) + tmp[0] = '0'; + else + tmp[0] = value.val_string[k]; + break; + case '#': + if (blank) + tmp[0] = ' '; + else + tmp[0] = value.val_string[k]; + break; + case '-': + if (sign && value.sign == '-' && !signdone) + { + tmp[0] = '-'; + signdone = 1; + } + else if (blank) + tmp[0] = ' '; + else + tmp[0] = value.val_string[k]; + break; + case '+': + if (sign && !signdone) + { + tmp[0] = value.sign; + signdone = 1; + } + else if (blank) + tmp[0] = ' '; + else + tmp[0] = value.val_string[k]; + break; + case '(': + if (sign && brackets_ok && value.sign == '-') + tmp[0] = '('; + else if (blank) + tmp[0] = ' '; + else + tmp[0] = value.val_string[k]; + break; + case ')': + if (brackets_ok && value.sign == '-') + tmp[0] = ')'; + else + tmp[0] = ' '; + break; + case '$': + if (blank && !entitydone) + { + tmp[0] = '$'; + entitydone = 1; + } + else if (blank) + tmp[0] = ' '; + else + tmp[0] = value.val_string[k]; + break; + case '<': + tmp[0] = value.val_string[k]; + break; + default: + tmp[0] = fmt[i]; + } + strcat(temp, tmp); + lastfmt = fmt[i]; + k--; + } + /* safety-net */ + temp[fmt_len] = '\0'; + + /* reverse the temp-string and put it into the outbuf */ + temp_len = strlen(temp); + outbuf[0] = '\0'; + for (i = temp_len - 1; i >= 0; i--) + { + tmp[0] = temp[i]; + strcat(outbuf, tmp); + } + outbuf[temp_len] = '\0'; + + /* cleaning up */ + free(temp); + free(value.val_string); + + return 0; +} + +void +rupshift(char *str) +{ + for (; *str != '\0'; str++) + if (islower((unsigned char) *str)) + *str = toupper((unsigned char) *str); +} + +int +byleng(char *str, int len) +{ + for (len--; str[len] && str[len] == ' '; len--); + return (len + 1); +} + +void +ldchar(char *src, int len, char *dest) +{ + int dlen = byleng(src, len); + + memmove(dest, src, dlen); + dest[dlen] = '\0'; +} + +int +rgetmsg(int msgnum, char *s, int maxsize) +{ + (void) msgnum; /* keep the compiler quiet */ + (void) s; /* keep the compiler quiet */ + (void) maxsize; /* keep the compiler quiet */ + return 0; +} + +int +rtypalign(int offset, int type) +{ + (void) offset; /* keep the compiler quiet */ + (void) type; /* keep the compiler quiet */ + return 0; +} + +int +rtypmsize(int type, int len) +{ + (void) type; /* keep the compiler quiet */ + (void) len; /* keep the compiler quiet */ + return 0; +} + +int +rtypwidth(int sqltype, int sqllen) +{ + (void) sqltype; /* keep the compiler quiet */ + (void) sqllen; /* keep the compiler quiet */ + return 0; +} + +void +ECPG_informix_set_var(int number, void *pointer, int lineno) +{ + ECPGset_var(number, pointer, lineno); +} + +void * +ECPG_informix_get_var(int number) +{ + return ECPGget_var(number); +} + +void +ECPG_informix_reset_sqlca(void) +{ + struct sqlca_t *sqlca = ECPGget_sqlca(); + + if (sqlca == NULL) + return; + + memcpy((char *) sqlca, (char *) &sqlca_init, sizeof(struct sqlca_t)); +} + +int +rsetnull(int t, char *ptr) +{ + ECPGset_noind_null(t, ptr); + return 0; +} + +int +risnull(int t, const char *ptr) +{ + return ECPGis_noind_null(t, ptr); +} |