From 5e37f16be0ec70489ce12e3cbc4f5c097f61ce17 Mon Sep 17 00:00:00 2001 From: Michael Meskes Date: Sun, 30 Mar 2003 13:26:09 +0000 Subject: [PATCH] Forgot two new files and one that was moved. --- src/interfaces/ecpg/compatlib/Makefile | 43 ++ src/interfaces/ecpg/compatlib/informix.c | 361 ++++++++++ src/interfaces/ecpg/pgtypeslib/informix.c | 44 -- src/interfaces/ecpg/pgtypeslib/interval.c | 834 ++++++++++++++++++++++ 4 files changed, 1238 insertions(+), 44 deletions(-) create mode 100644 src/interfaces/ecpg/compatlib/Makefile create mode 100644 src/interfaces/ecpg/compatlib/informix.c delete mode 100644 src/interfaces/ecpg/pgtypeslib/informix.c create mode 100644 src/interfaces/ecpg/pgtypeslib/interval.c diff --git a/src/interfaces/ecpg/compatlib/Makefile b/src/interfaces/ecpg/compatlib/Makefile new file mode 100644 index 0000000000..f48ac474bf --- /dev/null +++ b/src/interfaces/ecpg/compatlib/Makefile @@ -0,0 +1,43 @@ +#------------------------------------------------------------------------- +# +# Makefile for ecpg library +# +# Copyright (c) 1994, Regents of the University of California +# +# $Header: /cvsroot/pgsql/src/interfaces/ecpg/compatlib/Makefile,v 1.1 2003/03/30 13:26:09 meskes Exp $ +# +#------------------------------------------------------------------------- + +subdir = src/interfaces/ecpg/pgtypeslib +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global + +NAME= ecpg_compat +SO_MAJOR_VERSION= 1 +SO_MINOR_VERSION= 0.0 + +override CPPFLAGS := -O1 -g -I$(top_srcdir)/src/interfaces/ecpg/include -I$(top_srcdir)/src/include/utils $(CPPFLAGS) + +OBJS= informix.o + +all: all-lib + +# Shared library stuff +include $(top_srcdir)/src/Makefile.shlib + +install: all installdirs install-lib + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + +uninstall: uninstall-lib + +clean distclean maintainer-clean: clean-lib + rm -f $(OBJS) + +depend dep: + $(CC) -MM $(CFLAGS) *.c >depend + +ifeq (depend,$(wildcard depend)) +include depend +endif diff --git a/src/interfaces/ecpg/compatlib/informix.c b/src/interfaces/ecpg/compatlib/informix.c new file mode 100644 index 0000000000..b8fd79acde --- /dev/null +++ b/src/interfaces/ecpg/compatlib/informix.c @@ -0,0 +1,361 @@ +#include +#include +#include + +#include +#include +#include + +/* we start with the numeric functions */ +int +decadd(Numeric *arg1, Numeric *arg2, Numeric *sum) +{ + int i = PGTYPESnumeric_add(arg1, arg2, sum); + + if (i == 0) /* No error */ + return 0; + if (errno == PGTYPES_NUM_OVERFLOW) + return -1200; + + return -1201; +} + +int +deccmp(Numeric *arg1, Numeric *arg2) +{ + int i = PGTYPESnumeric_cmp(arg1, arg2); + + /* TODO: Need to return DECUNKNOWN instead of PGTYPES_NUM_BAD_NUMERIC */ + return (i); +} + +void +deccopy(Numeric *src, Numeric *target) +{ + PGTYPESnumeric_copy(src, target); +} + +static char * +strndup(char *str, int len) +{ + int real_len = strlen(str); + int use_len = (real_len > len) ? len : real_len; + + char *new = malloc(use_len + 1); + + if (new) + { + memcpy(str, new, use_len); + new[use_len] = '\0'; + } + else + errno = ENOMEM; + + return new; +} + +int +deccvasc(char *cp, int len, Numeric *np) +{ + char *str = strndup(cp, len); /* Numeric_in always converts the complete string */ + int ret = 0; + + if (!str) + ret = -1201; + else + { + np = PGTYPESnumeric_aton(str, NULL); + if (!np) + { + switch (errno) + { + case PGTYPES_NUM_OVERFLOW: ret = -1200; + break; + case PGTYPES_NUM_BAD_NUMERIC: ret = -1213; + break; + default: ret = -1216; + break; + } + } + } + + return ret; +} + +int +deccvdbl(double dbl, Numeric *np) +{ + return(PGTYPESnumeric_dton(dbl, np)); +} + +int +deccvint(int in, Numeric *np) +{ + return(PGTYPESnumeric_iton(in, np)); +} + +int +deccvlong(long lng, Numeric *np) +{ + return(PGTYPESnumeric_lton(lng, np)); +} + +int +decdiv(Numeric *n1, Numeric *n2, Numeric *n3) +{ + int i = PGTYPESnumeric_div(n1, n2, n3), ret = 0; + + if (i != 0) + switch (errno) + { + case PGTYPES_NUM_DIVIDE_ZERO: ret = -1202; + break; + case PGTYPES_NUM_OVERFLOW: ret = -1200; + break; + default: ret = -1201; + break; + } + + return ret; +} + +int +decmul(Numeric *n1, Numeric *n2, Numeric *n3) +{ + int i = PGTYPESnumeric_mul(n1, n2, n3), ret = 0; + + if (i != 0) + switch (errno) + { + case PGTYPES_NUM_OVERFLOW: ret = -1200; + break; + default: ret = -1201; + break; + } + + return ret; +} + +int +decsub(Numeric *n1, Numeric *n2, Numeric *n3) +{ + int i = PGTYPESnumeric_sub(n1, n2, n3), ret = 0; + + if (i != 0) + switch (errno) + { + case PGTYPES_NUM_OVERFLOW: ret = -1200; + break; + default: ret = -1201; + break; + } + + return ret; +} + +int +dectoasc(Numeric *np, char *cp, int len, int right) +{ + char *str; + + if (right >= 0) + str = PGTYPESnumeric_ntoa(np, right); + else + str = PGTYPESnumeric_ntoa(np, 0); + + if (!str) + return -1; + + /* TODO: have to take care of len here and create exponatial notion if necessary */ + strncpy(cp, str, len); + free (str); + + return 0; +} + +int +dectodbl(Numeric *np, double *dblp) +{ + return(PGTYPESnumeric_ntod(np, dblp)); +} + +int +dectoint(Numeric *np, int *ip) +{ + int ret = PGTYPESnumeric_ntoi(np, ip); + + if (ret == PGTYPES_NUM_OVERFLOW) + ret = -1200; + + return ret; +} + +int +dectolong(Numeric *np, long *lngp) +{ + int ret = PGTYPESnumeric_ntol(np, lngp); + + if (ret == PGTYPES_NUM_OVERFLOW) + ret = -1200; + + return ret; +} + +/* Now the date functions */ +int +rdatestr (Date d, char *str) +{ + char *tmp = PGTYPESdate_dtoa(d); + + if (!tmp) + return -1210; + + /* move to user allocated buffer */ + strcpy(tmp, str); + free(str); + + return 0; +} + +void +rtoday (Date *d) +{ + PGTYPESdate_today(d); + return; +} + +int +rjulmdy (Date d, short mdy[3]) +{ + PGTYPESdate_julmdy(d, (int *)mdy); + return 0; +} + +int +rdefmtdate (Date *d, char *fmt, char *str) +{ + /* TODO: take care of DBCENTURY environment variable */ + /* PGSQL functions allow all centuries */ + + if (PGTYPESdate_defmtdate(d, fmt, str) == 0) + return 0; + + switch (errno) + { + case PGTYPES_DATE_ERR_ENOSHORTDATE: return -1209; + case PGTYPES_DATE_ERR_EARGS: + case PGTYPES_DATE_ERR_ENOTDMY: return -1212; + case PGTYPES_DATE_BAD_DAY: return -1204; + case PGTYPES_DATE_BAD_MONTH: return -1205; + default: return -1206; + } +} + +int +rfmtdate (Date d, char *fmt, char *str) +{ + if (PGTYPESdate_fmtdate(d, fmt, str) == 0) + return 0; + + if (errno == ENOMEM) + return -1211; + + return -1210; +} + +int +rmdyjul (short mdy[3], Date *d) +{ + PGTYPESdate_mdyjul((int *)mdy, d); + return 0; +} + +/* And the datetime stuff */ + +void +dtcurrent (Timestamp *ts) +{ + return; +} + +int +dtcvasc (char *str, Timestamp *ts) +{ + return 0; +} + +int +dtsub (Timestamp *ts1, Timestamp *ts2, Interval *iv) +{ + return 0; +} + +int +dttoasc (Timestamp *ts, char *output) +{ + return 0; +} + +int +dttofmtasc (Timestamp *ts, char *output, int str_len, char *fmtstr) +{ + return 0; +} + +int +intoasc(Interval *i, char *str) +{ + return 0; +} + +/* And finally some misc functions */ +int +rstrdate (char *str, Date *d) +{ + return 0; +} + +int +rfmtlong(long lvalue, char *format, char *outbuf) +{ + return 0; +} + +int +rgetmsg(int msgnum, char *s, int maxsize) +{ + return 0; +} + +int +risnull(int vtype, char *pcvar) +{ + return 0; +} + +int +rsetnull(int vtype, char *pcvar) +{ + return 0; +} + +int +rtypalign(int offset, int type) +{ + return 0; +} + +int +rtypmsize(int type, int len) +{ + return 0; +} + +void +rupshift(char *s) +{ + return; +} + + + diff --git a/src/interfaces/ecpg/pgtypeslib/informix.c b/src/interfaces/ecpg/pgtypeslib/informix.c deleted file mode 100644 index d5cebe9aa6..0000000000 --- a/src/interfaces/ecpg/pgtypeslib/informix.c +++ /dev/null @@ -1,44 +0,0 @@ -int -rfmtlong(long lvalue, char *format, char *outbuf) -{ - return 0; -} - -int -rgetmsg(int msgnum, char *s, int maxsize) -{ - return 0; -} - -int -risnull(int vtype, char *pcvar) -{ - return 0; -} - -int -rsetnull(int vtype, char *pcvar) -{ - return 0; -} - -int -rtypalign(int offset, int type) -{ - return 0; -} - -int -rtypmsize(int type, mint len) -{ - return 0; -} - -void -rupshift(char *s) -{ - return; -} - - - diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c new file mode 100644 index 0000000000..3d20bd3db3 --- /dev/null +++ b/src/interfaces/ecpg/pgtypeslib/interval.c @@ -0,0 +1,834 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FAST_MATH__ +#error -ffast-math is known to break this code +#endif + +#include "dt.h" +#include "extern.h" +#include "pgtypes_error.h" +#include "pgtypes_interval.h" +#include "datetime.h" + +/* TrimTrailingZeros() + * ... resulting from printing numbers with full precision. + */ +static void +TrimTrailingZeros(char *str) +{ + int len = strlen(str); + + /* chop off trailing zeros... but leave at least 2 fractional digits */ + while ((*(str + len - 1) == '0') + && (*(str + len - 3) != '.')) + { + len--; + *(str + len) = '\0'; + } +} + +/* DecodeTime() + * Decode time string which includes delimiters. + * Only check the lower limit on hours, since this same code + * can be used to represent time spans. + */ +static int +DecodeTime(char *str, int fmask, int *tmask, struct tm * tm, fsec_t *fsec) +{ + char *cp; + + *tmask = DTK_TIME_M; + + tm->tm_hour = strtol(str, &cp, 10); + if (*cp != ':') + return -1; + str = cp + 1; + tm->tm_min = strtol(str, &cp, 10); + if (*cp == '\0') + { + tm->tm_sec = 0; + *fsec = 0; + } + else if (*cp != ':') + return -1; + else + { + str = cp + 1; + tm->tm_sec = strtol(str, &cp, 10); + if (*cp == '\0') + *fsec = 0; + else if (*cp == '.') + { +#ifdef HAVE_INT64_TIMESTAMP + char fstr[MAXDATELEN + 1]; + + /* + * OK, we have at most six digits to work with. Let's + * construct a string and then do the conversion to an + * integer. + */ + strncpy(fstr, (cp + 1), 7); + strcpy((fstr + strlen(fstr)), "000000"); + *(fstr + 6) = '\0'; + *fsec = strtol(fstr, &cp, 10); +#else + str = cp; + *fsec = strtod(str, &cp); +#endif + if (*cp != '\0') + return -1; + } + else + return -1; + } + + /* do a sanity check */ +#ifdef HAVE_INT64_TIMESTAMP + if ((tm->tm_hour < 0) + || (tm->tm_min < 0) || (tm->tm_min > 59) + || (tm->tm_sec < 0) || (tm->tm_sec > 59) + || (*fsec >= INT64CONST(1000000))) + return -1; +#else + if ((tm->tm_hour < 0) + || (tm->tm_min < 0) || (tm->tm_min > 59) + || (tm->tm_sec < 0) || (tm->tm_sec > 59) + || (*fsec >= 1)) + return -1; +#endif + + return 0; +} /* DecodeTime() */ + +/* DecodeInterval() + * Interpret previously parsed fields for general time interval. + * Return 0 if decoded and -1 if problems. + * + * Allow "date" field DTK_DATE since this could be just + * an unsigned floating point number. - thomas 1997-11-16 + * + * Allow ISO-style time span, with implicit units on number of days + * preceding an hh:mm:ss field. - thomas 1998-04-30 + */ +int +DecodeInterval(char **field, int *ftype, int nf, int *dtype, struct tm * tm, fsec_t *fsec) +{ + int is_before = FALSE; + + char *cp; + int fmask = 0, + tmask, + type; + int i; + int val; + double fval; + + *dtype = DTK_DELTA; + + type = IGNORE_DTF; + tm->tm_year = 0; + tm->tm_mon = 0; + tm->tm_mday = 0; + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; + *fsec = 0; + + /* read through list backwards to pick up units before values */ + for (i = nf - 1; i >= 0; i--) + { + switch (ftype[i]) + { + case DTK_TIME: + if (DecodeTime(field[i], fmask, &tmask, tm, fsec) != 0) + return -1; + type = DTK_DAY; + break; + + case DTK_TZ: + + /* + * Timezone is a token with a leading sign character and + * otherwise the same as a non-signed time field + */ + + /* + * A single signed number ends up here, but will be + * rejected by DecodeTime(). So, work this out to drop + * through to DTK_NUMBER, which *can* tolerate this. + */ + cp = field[i] + 1; + while ((*cp != '\0') && (*cp != ':') && (*cp != '.')) + cp++; + if ((*cp == ':') + && (DecodeTime((field[i] + 1), fmask, &tmask, tm, fsec) == 0)) + { + if (*field[i] == '-') + { + /* flip the sign on all fields */ + tm->tm_hour = -tm->tm_hour; + tm->tm_min = -tm->tm_min; + tm->tm_sec = -tm->tm_sec; + *fsec = -(*fsec); + } + + /* + * Set the next type to be a day, if units are not + * specified. This handles the case of '1 +02:03' + * since we are reading right to left. + */ + type = DTK_DAY; + tmask = DTK_M(TZ); + break; + } + else if (type == IGNORE_DTF) + { + if (*cp == '.') + { + /* + * Got a decimal point? Then assume some sort of + * seconds specification + */ + type = DTK_SECOND; + } + else if (*cp == '\0') + { + /* + * Only a signed integer? Then must assume a + * timezone-like usage + */ + type = DTK_HOUR; + } + } + /* DROP THROUGH */ + + case DTK_DATE: + case DTK_NUMBER: + val = strtol(field[i], &cp, 10); + + if (type == IGNORE_DTF) + type = DTK_SECOND; + + if (*cp == '.') + { + fval = strtod(cp, &cp); + if (*cp != '\0') + return -1; + + if (val < 0) + fval = -(fval); + } + else if (*cp == '\0') + fval = 0; + else + return -1; + + tmask = 0; /* DTK_M(type); */ + + switch (type) + { + case DTK_MICROSEC: +#ifdef HAVE_INT64_TIMESTAMP + *fsec += (val + fval); +#else + *fsec += ((val + fval) * 1e-6); +#endif + break; + + case DTK_MILLISEC: +#ifdef HAVE_INT64_TIMESTAMP + *fsec += ((val + fval) * 1000); +#else + *fsec += ((val + fval) * 1e-3); +#endif + break; + + case DTK_SECOND: + tm->tm_sec += val; +#ifdef HAVE_INT64_TIMESTAMP + *fsec += (fval * 1000000); +#else + *fsec += fval; +#endif + tmask = DTK_M(SECOND); + break; + + case DTK_MINUTE: + tm->tm_min += val; + if (fval != 0) + { + int sec; + + fval *= 60; + sec = fval; + tm->tm_sec += sec; +#ifdef HAVE_INT64_TIMESTAMP + *fsec += ((fval - sec) * 1000000); +#else + *fsec += (fval - sec); +#endif + } + tmask = DTK_M(MINUTE); + break; + + case DTK_HOUR: + tm->tm_hour += val; + if (fval != 0) + { + int sec; + + fval *= 3600; + sec = fval; + tm->tm_sec += sec; +#ifdef HAVE_INT64_TIMESTAMP + *fsec += ((fval - sec) * 1000000); +#else + *fsec += (fval - sec); +#endif + } + tmask = DTK_M(HOUR); + break; + + case DTK_DAY: + tm->tm_mday += val; + if (fval != 0) + { + int sec; + + fval *= 86400; + sec = fval; + tm->tm_sec += sec; +#ifdef HAVE_INT64_TIMESTAMP + *fsec += ((fval - sec) * 1000000); +#else + *fsec += (fval - sec); +#endif + } + tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY)); + break; + + case DTK_WEEK: + tm->tm_mday += val * 7; + if (fval != 0) + { + int sec; + + fval *= (7 * 86400); + sec = fval; + tm->tm_sec += sec; +#ifdef HAVE_INT64_TIMESTAMP + *fsec += ((fval - sec) * 1000000); +#else + *fsec += (fval - sec); +#endif + } + tmask = ((fmask & DTK_M(DAY)) ? 0 : DTK_M(DAY)); + break; + + case DTK_MONTH: + tm->tm_mon += val; + if (fval != 0) + { + int sec; + + fval *= (30 * 86400); + sec = fval; + tm->tm_sec += sec; +#ifdef HAVE_INT64_TIMESTAMP + *fsec += ((fval - sec) * 1000000); +#else + *fsec += (fval - sec); +#endif + } + tmask = DTK_M(MONTH); + break; + + case DTK_YEAR: + tm->tm_year += val; + if (fval != 0) + tm->tm_mon += (fval * 12); + tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); + break; + + case DTK_DECADE: + tm->tm_year += val * 10; + if (fval != 0) + tm->tm_mon += (fval * 120); + tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); + break; + + case DTK_CENTURY: + tm->tm_year += val * 100; + if (fval != 0) + tm->tm_mon += (fval * 1200); + tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); + break; + + case DTK_MILLENNIUM: + tm->tm_year += val * 1000; + if (fval != 0) + tm->tm_mon += (fval * 12000); + tmask = ((fmask & DTK_M(YEAR)) ? 0 : DTK_M(YEAR)); + break; + + default: + return -1; + } + break; + + case DTK_STRING: + case DTK_SPECIAL: + type = DecodeUnits(i, field[i], &val); + if (type == IGNORE_DTF) + continue; + + tmask = 0; /* DTK_M(type); */ + switch (type) + { + case UNITS: + type = val; + break; + + case AGO: + is_before = TRUE; + type = val; + break; + + case RESERV: + tmask = (DTK_DATE_M || DTK_TIME_M); + *dtype = val; + break; + + default: + return -1; + } + break; + + default: + return -1; + } + + if (tmask & fmask) + return -1; + fmask |= tmask; + } + + if (*fsec != 0) + { + int sec; + +#ifdef HAVE_INT64_TIMESTAMP + sec = (*fsec / INT64CONST(1000000)); + *fsec -= (sec * INT64CONST(1000000)); +#else + TMODULO(*fsec, sec, 1e0); +#endif + tm->tm_sec += sec; + } + + if (is_before) + { + *fsec = -(*fsec); + tm->tm_sec = -(tm->tm_sec); + tm->tm_min = -(tm->tm_min); + tm->tm_hour = -(tm->tm_hour); + tm->tm_mday = -(tm->tm_mday); + tm->tm_mon = -(tm->tm_mon); + tm->tm_year = -(tm->tm_year); + } + + /* ensure that at least one time field has been found */ + return (fmask != 0) ? 0 : -1; +} /* DecodeInterval() */ + +/* EncodeInterval() + * Interpret time structure as a delta time and convert to string. + * + * Support "traditional Postgres" and ISO-8601 styles. + * Actually, afaik ISO does not address time interval formatting, + * but this looks similar to the spec for absolute date/time. + * - thomas 1998-04-30 + */ +int +EncodeInterval(struct tm * tm, fsec_t fsec, int style, char *str) +{ + int is_before = FALSE; + int is_nonzero = FALSE; + char *cp = str; + + /* + * The sign of year and month are guaranteed to match, since they are + * stored internally as "month". But we'll need to check for is_before + * and is_nonzero when determining the signs of hour/minute/seconds + * fields. + */ + switch (style) + { + /* compatible with ISO date formats */ + case USE_ISO_DATES: + if (tm->tm_year != 0) + { + sprintf(cp, "%d year%s", + tm->tm_year, ((tm->tm_year != 1) ? "s" : "")); + cp += strlen(cp); + is_before = (tm->tm_year < 0); + is_nonzero = TRUE; + } + + if (tm->tm_mon != 0) + { + sprintf(cp, "%s%s%d mon%s", (is_nonzero ? " " : ""), + ((is_before && (tm->tm_mon > 0)) ? "+" : ""), + tm->tm_mon, ((tm->tm_mon != 1) ? "s" : "")); + cp += strlen(cp); + is_before = (tm->tm_mon < 0); + is_nonzero = TRUE; + } + + if (tm->tm_mday != 0) + { + sprintf(cp, "%s%s%d day%s", (is_nonzero ? " " : ""), + ((is_before && (tm->tm_mday > 0)) ? "+" : ""), + tm->tm_mday, ((tm->tm_mday != 1) ? "s" : "")); + cp += strlen(cp); + is_before = (tm->tm_mday < 0); + is_nonzero = TRUE; + } + if ((!is_nonzero) || (tm->tm_hour != 0) || (tm->tm_min != 0) + || (tm->tm_sec != 0) || (fsec != 0)) + { + int minus = ((tm->tm_hour < 0) || (tm->tm_min < 0) + || (tm->tm_sec < 0) || (fsec < 0)); + + sprintf(cp, "%s%s%02d:%02d", (is_nonzero ? " " : ""), + (minus ? "-" : (is_before ? "+" : "")), + abs(tm->tm_hour), abs(tm->tm_min)); + cp += strlen(cp); + /* Mark as "non-zero" since the fields are now filled in */ + is_nonzero = TRUE; + + /* fractional seconds? */ + if (fsec != 0) + { +#ifdef HAVE_INT64_TIMESTAMP + sprintf(cp, ":%02d", abs(tm->tm_sec)); + cp += strlen(cp); + sprintf(cp, ".%06d", ((fsec >= 0) ? fsec : -(fsec))); +#else + fsec += tm->tm_sec; + sprintf(cp, ":%013.10f", fabs(fsec)); +#endif + TrimTrailingZeros(cp); + cp += strlen(cp); + is_nonzero = TRUE; + } + /* otherwise, integer seconds only? */ + else if (tm->tm_sec != 0) + { + sprintf(cp, ":%02d", abs(tm->tm_sec)); + cp += strlen(cp); + is_nonzero = TRUE; + } + } + break; + + case USE_POSTGRES_DATES: + default: + strcpy(cp, "@ "); + cp += strlen(cp); + + if (tm->tm_year != 0) + { + int year = tm->tm_year; + + if (tm->tm_year < 0) + year = -year; + + sprintf(cp, "%d year%s", year, + ((year != 1) ? "s" : "")); + cp += strlen(cp); + is_before = (tm->tm_year < 0); + is_nonzero = TRUE; + } + + if (tm->tm_mon != 0) + { + int mon = tm->tm_mon; + + if (is_before || ((!is_nonzero) && (tm->tm_mon < 0))) + mon = -mon; + + sprintf(cp, "%s%d mon%s", (is_nonzero ? " " : ""), mon, + ((mon != 1) ? "s" : "")); + cp += strlen(cp); + if (!is_nonzero) + is_before = (tm->tm_mon < 0); + is_nonzero = TRUE; + } + + if (tm->tm_mday != 0) + { + int day = tm->tm_mday; + + if (is_before || ((!is_nonzero) && (tm->tm_mday < 0))) + day = -day; + + sprintf(cp, "%s%d day%s", (is_nonzero ? " " : ""), day, + ((day != 1) ? "s" : "")); + cp += strlen(cp); + if (!is_nonzero) + is_before = (tm->tm_mday < 0); + is_nonzero = TRUE; + } + if (tm->tm_hour != 0) + { + int hour = tm->tm_hour; + + if (is_before || ((!is_nonzero) && (tm->tm_hour < 0))) + hour = -hour; + + sprintf(cp, "%s%d hour%s", (is_nonzero ? " " : ""), hour, + ((hour != 1) ? "s" : "")); + cp += strlen(cp); + if (!is_nonzero) + is_before = (tm->tm_hour < 0); + is_nonzero = TRUE; + } + + if (tm->tm_min != 0) + { + int min = tm->tm_min; + + if (is_before || ((!is_nonzero) && (tm->tm_min < 0))) + min = -min; + + sprintf(cp, "%s%d min%s", (is_nonzero ? " " : ""), min, + ((min != 1) ? "s" : "")); + cp += strlen(cp); + if (!is_nonzero) + is_before = (tm->tm_min < 0); + is_nonzero = TRUE; + } + + /* fractional seconds? */ + if (fsec != 0) + { +#ifdef HAVE_INT64_TIMESTAMP + if (is_before || ((!is_nonzero) && (tm->tm_sec < 0))) + tm->tm_sec = -tm->tm_sec; + sprintf(cp, "%s%d.%02d secs", (is_nonzero ? " " : ""), + tm->tm_sec, (((int) fsec) / 10000)); + cp += strlen(cp); + if (!is_nonzero) + is_before = (fsec < 0); +#else + fsec_t sec; + + fsec += tm->tm_sec; + sec = fsec; + if (is_before || ((!is_nonzero) && (fsec < 0))) + sec = -sec; + + sprintf(cp, "%s%.2f secs", (is_nonzero ? " " : ""), sec); + cp += strlen(cp); + if (!is_nonzero) + is_before = (fsec < 0); +#endif + is_nonzero = TRUE; + + /* otherwise, integer seconds only? */ + } + else if (tm->tm_sec != 0) + { + int sec = tm->tm_sec; + + if (is_before || ((!is_nonzero) && (tm->tm_sec < 0))) + sec = -sec; + + sprintf(cp, "%s%d sec%s", (is_nonzero ? " " : ""), sec, + ((sec != 1) ? "s" : "")); + cp += strlen(cp); + if (!is_nonzero) + is_before = (tm->tm_sec < 0); + is_nonzero = TRUE; + } + break; + } + + /* identically zero? then put in a unitless zero... */ + if (!is_nonzero) + { + strcat(cp, "0"); + cp += strlen(cp); + } + + if (is_before && (style == USE_POSTGRES_DATES)) + { + strcat(cp, " ago"); + cp += strlen(cp); + } + + return 0; +} /* EncodeInterval() */ + +/* interval2tm() + * Convert a interval data type to a tm structure. + */ +static int +interval2tm(Interval span, struct tm * tm, fsec_t *fsec) +{ +#ifdef HAVE_INT64_TIMESTAMP + int64 time; + +#else + double time; +#endif + + if (span.month != 0) + { + tm->tm_year = span.month / 12; + tm->tm_mon = span.month % 12; + + } + else + { + tm->tm_year = 0; + tm->tm_mon = 0; + } + + time = span.time; + +#ifdef HAVE_INT64_TIMESTAMP + tm->tm_mday = (time / INT64CONST(86400000000)); + time -= (tm->tm_mday * INT64CONST(86400000000)); + tm->tm_hour = (time / INT64CONST(3600000000)); + time -= (tm->tm_hour * INT64CONST(3600000000)); + tm->tm_min = (time / INT64CONST(60000000)); + time -= (tm->tm_min * INT64CONST(60000000)); + tm->tm_sec = (time / INT64CONST(1000000)); + *fsec = (time - (tm->tm_sec * INT64CONST(1000000))); +#else + TMODULO(time, tm->tm_mday, 86400e0); + TMODULO(time, tm->tm_hour, 3600e0); + TMODULO(time, tm->tm_min, 60e0); + TMODULO(time, tm->tm_sec, 1e0); + *fsec = time; +#endif + + return 0; +} /* interval2tm() */ + +static int +tm2interval(struct tm * tm, fsec_t fsec, Interval *span) +{ + span->month = ((tm->tm_year * 12) + tm->tm_mon); +#ifdef HAVE_INT64_TIMESTAMP + span->time = ((((((((tm->tm_mday * INT64CONST(24)) + + tm->tm_hour) * INT64CONST(60)) + + tm->tm_min) * INT64CONST(60)) + + tm->tm_sec) * INT64CONST(1000000)) + fsec); +#else + span->time = ((((((tm->tm_mday * 24.0) + + tm->tm_hour) * 60.0) + + tm->tm_min) * 60.0) + + tm->tm_sec); + span->time = JROUND(span->time + fsec); +#endif + + return 0; +} /* tm2interval() */ + +Interval * +PGTYPESinterval_atoi(char *str, char **endptr) +{ + Interval *result = NULL; + fsec_t fsec; + struct tm tt, + *tm = &tt; + int dtype; + int nf; + char *field[MAXDATEFIELDS]; + int ftype[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + MAXDATEFIELDS]; + char *realptr; + char **ptr = (endptr != NULL) ? endptr : &realptr; + + tm->tm_year = 0; + tm->tm_mon = 0; + tm->tm_mday = 0; + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; + fsec = 0; + + if (strlen(str) >= sizeof(lowstr)) + { + errno = PGTYPES_INTVL_BAD_INTERVAL; + return NULL; + } + + if ((ParseDateTime(str, lowstr, field, ftype, MAXDATEFIELDS, &nf, ptr) != 0) + || (DecodeInterval(field, ftype, nf, &dtype, tm, &fsec) != 0)) + { + errno = PGTYPES_INTVL_BAD_INTERVAL; + return NULL; + } + + result = (Interval *) pgtypes_alloc(sizeof(Interval)); + if (!result) + return NULL; + + if (dtype != DTK_DELTA) + { + errno = PGTYPES_INTVL_BAD_INTERVAL; + return NULL; + } + + if (tm2interval(tm, fsec, result) != 0) + { + errno = PGTYPES_INTVL_BAD_INTERVAL; + return NULL; + } + + return result; +} + +char * +PGTYPESinterval_itoa(Interval *span) +{ + struct tm tt, + *tm = &tt; + fsec_t fsec; + char buf[MAXDATELEN + 1]; + int DateStyle=0; + + if (interval2tm(*span, tm, &fsec) != 0) + { + errno = PGTYPES_INTVL_BAD_INTERVAL; + return NULL; + } + + if (EncodeInterval(tm, fsec, DateStyle, buf) != 0) + { + errno = PGTYPES_INTVL_BAD_INTERVAL; + return NULL; + } + + return pgtypes_strdup(buf); +} + +int +PGTYPESinterval_copy(Interval *intvlsrc, Interval *intrcldest) +{ + intrcldest->time = intvlsrc->time; + intrcldest->month = intvlsrc->month; + + return 0; +} + -- 2.40.0