postgresql/src/interfaces/ecpg/compatlib/informix.c

1049 lines
18 KiB
C

/* 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));
}
static char *
ecpg_strndup(const char *str, size_t len)
{
size_t real_len = strlen(str);
int use_len = (int) ((real_len > len) ? len : real_len);
char *new = malloc(use_len + 1);
if (new)
{
memcpy(new, str, use_len);
new[use_len] = '\0';
}
else
errno = ENOMEM;
return new;
}
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 = ecpg_strndup(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);
return;
}
int
rjulmdy(date d, short mdy[3])
{
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[3], 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 freek 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);
return;
}
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);
}