diff options
Diffstat (limited to 'src/backend/utils/adt')
33 files changed, 13240 insertions, 0 deletions
diff --git a/src/backend/utils/adt/Makefile.inc b/src/backend/utils/adt/Makefile.inc new file mode 100644 index 00000000000..9a7d353f175 --- /dev/null +++ b/src/backend/utils/adt/Makefile.inc @@ -0,0 +1,20 @@ + +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/adt +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= acl.c arrayfuncs.c arrayutils.c bool.c char.c chunk.c date.c \ + datum.c dt.c filename.c float.c geo-ops.c geo-selfuncs.c int.c \ + misc.c nabstime.c name.c not_in.c numutils.c oid.c \ + oidname.c oidint2.c oidint4.c regexp.c regproc.c selfuncs.c \ + tid.c varchar.c varlena.c sets.c datetimes.c like.c + diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c new file mode 100644 index 00000000000..289b4575c7b --- /dev/null +++ b/src/backend/utils/adt/acl.c @@ -0,0 +1,618 @@ +/*------------------------------------------------------------------------- + * + * acl.c-- + * Basic access control list data structures manipulation routines. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <string.h> +#include "postgres.h" +#include "c.h" +#include "utils/acl.h" +#include "access/htup.h" +#include "catalog/pg_user.h" +#include "utils/syscache.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +static char *getid(char *s, char *n); +static int32 aclitemeq(AclItem *a1, AclItem *a2); +static int32 aclitemgt(AclItem *a1, AclItem *a2); + +#define ACL_IDTYPE_GID_KEYWORD "group" +#define ACL_IDTYPE_UID_KEYWORD "user" + + +/* + * getid + * Consumes the first alphanumeric string (identifier) found in string + * 's', ignoring any leading white space. + * + * RETURNS: + * the string position in 's' that points to the next non-space character + * in 's'. Also: + * - loads the identifier into 'name'. (If no identifier is found, 'name' + * contains an empty string). + */ +static char * +getid(char *s, char *n) +{ + unsigned len; + char *id; + + Assert(s && n); + + while (isspace(*s)) + ++s; + for (id = s, len = 0; isalnum(*s); ++len, ++s) + ; + if (len > sizeof(NameData)) + elog(WARN, "getid: identifier cannot be >%d characters", + sizeof(NameData)); + if (len > 0) + memmove(n, id, len); + n[len] = '\0'; + while (isspace(*s)) + ++s; + return(s); +} + +/* + * aclparse + * Consumes and parses an ACL specification of the form: + * [group|user] [A-Za-z0-9]*[+-=][rwaR]* + * from string 's', ignoring any leading white space or white space + * between the optional id type keyword (group|user) and the actual + * ACL specification. + * + * This routine is called by the parser as well as aclitemin(), hence + * the added generality. + * + * RETURNS: + * the string position in 's' immediately following the ACL + * specification. Also: + * - loads the structure pointed to by 'aip' with the appropriate + * UID/GID, id type identifier and mode type values. + * - loads 'modechg' with the mode change flag. + */ +char * +aclparse(char *s, AclItem *aip, unsigned *modechg) +{ + HeapTuple htp; + char name[NAMEDATALEN+1]; + extern AclId get_grosysid(); + + Assert(s && aip && modechg); + + aip->ai_idtype = ACL_IDTYPE_UID; + s = getid(s, name); + if (*s != ACL_MODECHG_ADD_CHR && + *s != ACL_MODECHG_DEL_CHR && + *s != ACL_MODECHG_EQL_CHR) + { /* we just read a keyword, not a name */ + if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD)) { + aip->ai_idtype = ACL_IDTYPE_GID; + } else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD)) { + elog(WARN, "aclparse: bad keyword, must be [group|user]"); + } + s = getid(s, name); /* move s to the name beyond the keyword */ + if (name[0] == '\0') + elog(WARN, "aclparse: a name must follow the [group|user] keyword"); + } + if (name[0] == '\0') + aip->ai_idtype = ACL_IDTYPE_WORLD; + + switch (*s) { + case ACL_MODECHG_ADD_CHR: *modechg = ACL_MODECHG_ADD; break; + case ACL_MODECHG_DEL_CHR: *modechg = ACL_MODECHG_DEL; break; + case ACL_MODECHG_EQL_CHR: *modechg = ACL_MODECHG_EQL; break; + default: elog(WARN, "aclparse: mode change flag must use \"%s\"", + ACL_MODECHG_STR); + } + + aip->ai_mode = ACL_NO; + while (isalpha(*++s)) { + switch (*s) { + case ACL_MODE_AP_CHR: aip->ai_mode |= ACL_AP; break; + case ACL_MODE_RD_CHR: aip->ai_mode |= ACL_RD; break; + case ACL_MODE_WR_CHR: aip->ai_mode |= ACL_WR; break; + case ACL_MODE_RU_CHR: aip->ai_mode |= ACL_RU; break; + default: elog(WARN, "aclparse: mode flags must use \"%s\"", + ACL_MODE_STR); + } + } + + switch (aip->ai_idtype) { + case ACL_IDTYPE_UID: + htp = SearchSysCacheTuple(USENAME, PointerGetDatum(name), + 0,0,0); + if (!HeapTupleIsValid(htp)) + elog(WARN, "aclparse: non-existent user \"%s\"", name); + aip->ai_id = ((Form_pg_user) GETSTRUCT(htp))->usesysid; + break; + case ACL_IDTYPE_GID: + aip->ai_id = get_grosysid(name); + break; + case ACL_IDTYPE_WORLD: + aip->ai_id = ACL_ID_WORLD; + break; + } + +#ifdef ACLDEBUG_TRACE + elog(DEBUG, "aclparse: correctly read [%x %d %x], modechg=%x", + aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg); +#endif + return(s); +} + +/* + * makeacl + * Allocates storage for a new Acl with 'n' entries. + * + * RETURNS: + * the new Acl + */ +Acl * +makeacl(int n) +{ + Acl *new_acl; + Size size; + + if (n < 0) + elog(WARN, "makeacl: invalid size: %d\n", n); + size = ACL_N_SIZE(n); + if (!(new_acl = (Acl *) palloc(size))) + elog(WARN, "makeacl: palloc failed on %d\n", size); + memset((char *) new_acl, 0, size); + new_acl->size = size; + new_acl->ndim = 1; + new_acl->flags = 0; + ARR_LBOUND(new_acl)[0] = 0; + ARR_DIMS(new_acl)[0] = n; + return(new_acl); +} + +/* + * aclitemin + * Allocates storage for, and fills in, a new AclItem given a string + * 's' that contains an ACL specification. See aclparse for details. + * + * RETURNS: + * the new AclItem + */ +AclItem * +aclitemin(char *s) +{ + unsigned modechg; + AclItem *aip; + + if (!s) + elog(WARN, "aclitemin: null string"); + + aip = (AclItem *) palloc(sizeof(AclItem)); + if (!aip) + elog(WARN, "aclitemin: palloc failed"); + s = aclparse(s, aip, &modechg); + if (modechg != ACL_MODECHG_EQL) + elog(WARN, "aclitemin: cannot accept anything but = ACLs"); + while (isspace(*s)) + ++s; + if (*s) + elog(WARN, "aclitemin: extra garbage at end of specification"); + return(aip); +} + +/* + * aclitemout + * Allocates storage for, and fills in, a new null-delimited string + * containing a formatted ACL specification. See aclparse for details. + * + * RETURNS: + * the new string + */ +char * +aclitemout(AclItem *aip) +{ + register char *p; + char *out; + HeapTuple htp; + unsigned i; + static AclItem default_aclitem = { ACL_ID_WORLD, + ACL_IDTYPE_WORLD, + ACL_WORLD_DEFAULT }; + extern char *int2out(); + char *tmpname; + + if (!aip) + aip = &default_aclitem; + + p = out = palloc(strlen("group =arwR ") + 1 + NAMEDATALEN); + if (!out) + elog(WARN, "aclitemout: palloc failed"); + *p = '\0'; + + switch (aip->ai_idtype) { + case ACL_IDTYPE_UID: + htp = SearchSysCacheTuple(USESYSID, ObjectIdGetDatum(aip->ai_id), + 0,0,0); + if (!HeapTupleIsValid(htp)) { + char *tmp = int2out(aip->ai_id); + + elog(NOTICE, "aclitemout: usesysid %d not found", + aip->ai_id); + (void) strcat(p, tmp); + pfree(tmp); + } else + (void) strncat(p, (char *) &((Form_pg_user) + GETSTRUCT(htp))->usename, + sizeof(NameData)); + break; + case ACL_IDTYPE_GID: + (void) strcat(p, "group "); + tmpname = get_groname(aip->ai_id); + (void) strncat(p, tmpname, NAMEDATALEN); + pfree(tmpname); + break; + case ACL_IDTYPE_WORLD: + break; + default: + elog(WARN, "aclitemout: bad ai_idtype: %d", aip->ai_idtype); + break; + } + while (*p) + ++p; + *p++ = '='; + for (i = 0; i < N_ACL_MODES; ++i) + if ((aip->ai_mode >> i) & 01) + *p++ = ACL_MODE_STR[i]; + *p = '\0'; + + return(out); +} + +/* + * aclitemeq + * aclitemgt + * AclItem equality and greater-than comparison routines. + * Two AclItems are equal iff they are both NULL or they have the + * same identifier (and identifier type). + * + * RETURNS: + * a boolean value indicating = or > + */ +static int32 +aclitemeq(AclItem *a1, AclItem *a2) +{ + if (!a1 && !a2) + return(1); + if (!a1 || !a2) + return(0); + return(a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id); +} + +static int32 +aclitemgt(AclItem *a1, AclItem *a2) +{ + if (a1 && !a2) + return(1); + if (!a1 || !a2) + return(0); + return((a1->ai_idtype > a2->ai_idtype) || + (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id)); +} + +Acl * +aclownerdefault(AclId ownerid) +{ + Acl *acl; + AclItem *aip; + + acl = makeacl(2); + aip = ACL_DAT(acl); + aip[0].ai_idtype = ACL_IDTYPE_WORLD; + aip[0].ai_id = ACL_ID_WORLD; + aip[0].ai_mode = ACL_WORLD_DEFAULT; + aip[1].ai_idtype = ACL_IDTYPE_UID; + aip[1].ai_id = ownerid; + aip[1].ai_mode = ACL_OWNER_DEFAULT; + return(acl); +} + +Acl * +acldefault() +{ + Acl *acl; + AclItem *aip; + + acl = makeacl(1); + aip = ACL_DAT(acl); + aip[0].ai_idtype = ACL_IDTYPE_WORLD; + aip[0].ai_id = ACL_ID_WORLD; + aip[0].ai_mode = ACL_WORLD_DEFAULT; + return(acl); +} + +Acl * +aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg) +{ + Acl *new_acl; + AclItem *old_aip, *new_aip; + unsigned src, dst, num; + + if (!old_acl || ACL_NUM(old_acl) < 1) { + new_acl = makeacl(0); + return(new_acl); + } + if (!mod_aip) { + new_acl = makeacl(ACL_NUM(old_acl)); + memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl)); + return(new_acl); + } + + num = ACL_NUM(old_acl); + old_aip = ACL_DAT(old_acl); + + /* + * Search the ACL for an existing entry for 'id'. If one exists, + * just modify the entry in-place (well, in the same position, since + * we actually return a copy); otherwise, insert the new entry in + * sort-order. + */ + /* find the first element not less than the element to be inserted */ + for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip+dst); ++dst) + ; + if (dst < num && aclitemeq(mod_aip, old_aip+dst)) { + /* modify in-place */ + new_acl = makeacl(ACL_NUM(old_acl)); + memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl)); + new_aip = ACL_DAT(new_acl); + src = dst; + } else { + new_acl = makeacl(num + 1); + new_aip = ACL_DAT(new_acl); + if (dst == 0) { /* start */ + elog(WARN, "aclinsert3: insertion before world ACL??"); + } else if (dst >= num) { /* end */ + memmove((char *) new_aip, + (char *) old_aip, + num * sizeof(AclItem)); + } else { /* middle */ + memmove((char *) new_aip, + (char *) old_aip, + dst * sizeof(AclItem)); + memmove((char *) (new_aip+dst+1), + (char *) (old_aip+dst), + (num - dst) * sizeof(AclItem)); + } + new_aip[dst].ai_id = mod_aip->ai_id; + new_aip[dst].ai_idtype = mod_aip->ai_idtype; + num++; /* set num to the size of new_acl */ + src = 0; /* world entry */ + } + switch (modechg) { + case ACL_MODECHG_ADD: new_aip[dst].ai_mode = + old_aip[src].ai_mode | mod_aip->ai_mode; + break; + case ACL_MODECHG_DEL: new_aip[dst].ai_mode = + old_aip[src].ai_mode & ~mod_aip->ai_mode; + break; + case ACL_MODECHG_EQL: new_aip[dst].ai_mode = + mod_aip->ai_mode; + break; + } + /* if the newly added entry has no permissions, delete it from + the list. For example, this helps in removing entries for users who + no longer exists...*/ + for (dst = 1; dst < num; dst++) { + if (new_aip[dst].ai_mode == 0) { + int i; + for (i=dst+1; i<num; i++) { + new_aip[i-1].ai_id = new_aip[i].ai_id; + new_aip[i-1].ai_idtype = new_aip[i].ai_idtype; + new_aip[i-1].ai_mode = new_aip[i].ai_mode; + } + ARR_DIMS(new_acl)[0] = num -1 ; + break; + } + } + + return(new_acl); +} + +/* + * aclinsert + * + */ +Acl * +aclinsert(Acl *old_acl, AclItem *mod_aip) +{ + return(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL)); +} + +Acl * +aclremove(Acl *old_acl, AclItem *mod_aip) +{ + Acl *new_acl; + AclItem *old_aip, *new_aip; + unsigned dst, old_num, new_num; + + if (!old_acl || ACL_NUM(old_acl) < 1) { + new_acl = makeacl(0); + return(new_acl); + } + if (!mod_aip) { + new_acl = makeacl(ACL_NUM(old_acl)); + memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl)); + return(new_acl); + } + + old_num = ACL_NUM(old_acl); + old_aip = ACL_DAT(old_acl); + + for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip+dst); ++dst) + ; + if (dst >= old_num) { /* not found or empty */ + new_acl = makeacl(ACL_NUM(old_acl)); + memmove((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl)); + } else { + new_num = old_num - 1; + new_acl = makeacl(ACL_NUM(old_acl) - 1); + new_aip = ACL_DAT(new_acl); + if (dst == 0) { /* start */ + elog(WARN, "aclremove: removal of the world ACL??"); + } else if (dst == old_num - 1) {/* end */ + memmove((char *) new_aip, + (char *) old_aip, + new_num * sizeof(AclItem)); + } else { /* middle */ + memmove((char *) new_aip, + (char *) old_aip, + dst * sizeof(AclItem)); + memmove((char *) (new_aip+dst), + (char *) (old_aip+dst+1), + (new_num - dst) * sizeof(AclItem)); + } + } + return(new_acl); +} + +int32 +aclcontains(Acl *acl, AclItem *aip) +{ + unsigned i, num; + AclItem *aidat; + + if (!acl || !aip || ((num = ACL_NUM(acl)) < 1)) + return(0); + aidat = ACL_DAT(acl); + for (i = 0; i < num; ++i) + if (aclitemeq(aip, aidat+i)) + return(1); + return(0); +} + +/* parser support routines */ + +/* + * aclmakepriv + * make a acl privilege string out of an existing privilege string + * and a new privilege + * + * does not add duplicate privileges + * + * the CALLER is reponsible for free'ing the string returned + */ + +char* +aclmakepriv(char* old_privlist, char new_priv) +{ + char* priv; + int i; + int l; + + Assert(strlen(old_privlist)<5); + priv = malloc(5); /* at most "rwaR" */; + + if (old_privlist == NULL || old_privlist[0] == '\0') { + priv[0] = new_priv; + priv[1] = '\0'; + return priv; + } + + strcpy(priv,old_privlist); + + l = strlen(old_privlist); + + if (l == 4) { /* can't add any more privileges */ + return priv; + } + + /* check to see if the new privilege is already in the old string */ + for (i=0;i<l;i++) { + if (priv[i] == new_priv) + break; + } + if (i == l) { /* we really have a new privilege*/ + priv[l] = new_priv; + priv[l+1] = '\0'; + } + + return priv; +} + +/* + * aclmakeuser + * user_type must be "A" - all users + * "G" - group + * "U" - user + * + * concatentates the two strings together with a space in between + * + * this routine is used in the parser + * + * the CALLER is responsible for freeing the memory allocated + */ + +char* +aclmakeuser(char* user_type, char* user) +{ + char* user_list; + + user_list = malloc(strlen(user) + 3); + sprintf(user_list, "%s %s", user_type, user); + return user_list; +} + + +/* + * makeAclStmt: + * this is a helper routine called by the parser + * create a ChangeAclStmt + * we take in the privilegs, relation_name_list, and grantee + * as well as a single character '+' or '-' to indicate grant or revoke + * + * returns a new ChangeACLStmt* + * + * this routines works by creating a old-style changle acl string and + * then calling aclparse; + */ + +ChangeACLStmt* +makeAclStmt(char* privileges, List* rel_list, char* grantee, + char grant_or_revoke) +{ + ChangeACLStmt *n = makeNode(ChangeACLStmt); + char str[MAX_PARSE_BUFFER]; + + n->aclitem = (AclItem*)palloc(sizeof(AclItem)); + /* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */ + if (grantee[0] == 'G') /* group permissions */ + { + sprintf(str,"%s %s%c%s", + ACL_IDTYPE_GID_KEYWORD, + grantee+2, grant_or_revoke,privileges); + } + else if (grantee[0] == 'U') /* user permission */ + { + sprintf(str,"%s %s%c%s", + ACL_IDTYPE_UID_KEYWORD, + grantee+2, grant_or_revoke,privileges); + } + else /* all permission */ + { + sprintf(str,"%c%s", + grant_or_revoke,privileges); + } + n->relNames = rel_list; + aclparse(str, n->aclitem, (unsigned*)&n->modechg); + return n; +} + + diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c new file mode 100644 index 00000000000..2e780fb03b6 --- /dev/null +++ b/src/backend/utils/adt/arrayfuncs.c @@ -0,0 +1,1375 @@ +/*------------------------------------------------------------------------- + * + * arrayfuncs.c-- + * Special functions for arrays. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <stdio.h> + +#include "postgres.h" + +#include "catalog/catalog.h" +#include "catalog/pg_type.h" + +#include "utils/syscache.h" +#include "utils/palloc.h" +#include "utils/memutils.h" +#include "storage/fd.h" /* for SEEK_ */ +#include "fmgr.h" +#include "utils/elog.h" +#include "utils/array.h" + +#include "libpq/libpq-fs.h" +#include "libpq/be-fsstubs.h" + +#define ASSGN "=" + +/* An array has the following internal structure: + * <nbytes> - total number of bytes + * <ndim> - number of dimensions of the array + * <flags> - bit mask of flags + * <dim> - size of each array axis + * <dim_lower> - lower boundary of each dimension + * <actual data> - whatever is the stored data +*/ + +/*-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-*/ +static int _ArrayCount(char *str, int dim[], int typdelim); +static char *_ReadArrayStr(char *arrayStr, int nitems, int ndim, int dim[], + func_ptr inputproc, Oid typelem, char typdelim, + int typlen, bool typbyval, char typalign, + int *nbytes); +static char *_ReadLOArray(char *str, int *nbytes, int *fd, bool *chunkFlag, + int ndim, int dim[], int baseSize); +static void _CopyArrayEls(char **values, char *p, int nitems, int typlen, + char typalign, bool typbyval); +static void system_cache_lookup(Oid element_type, bool input, int *typlen, + bool *typbyval, char *typdelim, Oid *typelem, Oid *proc, + char *typalign); +static Datum _ArrayCast(char *value, bool byval, int len); +static char *_AdvanceBy1word(char *str, char **word); +static void _ArrayRange(int st[], int endp[], int bsize, char *destPtr, + ArrayType *array, int from); +static int _ArrayClipCount(int stI[], int endpI[], ArrayType *array); +static void _LOArrayRange(int st[], int endp[], int bsize, int srcfd, + int destfd, ArrayType *array, int isSrcLO, bool *isNull); +static void _ReadArray (int st[], int endp[], int bsize, int srcfd, int destfd, + ArrayType *array, int isDestLO, bool *isNull); +static char *_array_set(ArrayType *array, struct varlena *indx_str, + struct varlena *dataPtr); +static ArrayCastAndSet(char *src, bool typbyval, int typlen, char *dest); + + +/*--------------------------------------------------------------------- + * array_in : + * converts an array from the external format in "string" to + * it internal format. + * return value : + * the internal representation of the input array + *-------------------------------------------------------------------- + */ +char * +array_in(char *string, /* input array in external form */ + Oid element_type) /* type OID of an array element */ +{ + int typlen; + bool typbyval, done; + char typdelim; + Oid typinput; + Oid typelem; + char *string_save, *p, *q, *r; + func_ptr inputproc; + int i, nitems, dummy; + int32 nbytes; + char *dataPtr; + ArrayType *retval; + int ndim, dim[MAXDIM], lBound[MAXDIM]; + char typalign; + + system_cache_lookup(element_type, true, &typlen, &typbyval, &typdelim, + &typelem, &typinput, &typalign); + + fmgr_info(typinput, &inputproc, &dummy); + + string_save = (char *) palloc(strlen(string) + 3); + strcpy(string_save, string); + + /* --- read array dimensions ---------- */ + p = q = string_save; done = false; + for ( ndim = 0; !done; ) { + while (isspace(*p)) p++; + if (*p == '[' ) { + p++; + if ((r = (char *)strchr(p, ':')) == (char *)NULL) + lBound[ndim] = 1; + else { + *r = '\0'; + lBound[ndim] = atoi(p); + p = r + 1; + } + for (q = p; isdigit(*q); q++); + if (*q != ']') + elog(WARN, "array_in: missing ']' in array declaration"); + *q = '\0'; + dim[ndim] = atoi(p); + if ((dim[ndim] < 0) || (lBound[ndim] < 0)) + elog(WARN,"array_in: array dimensions need to be positive"); + dim[ndim] = dim[ndim] - lBound[ndim] + 1; + if (dim[ndim] < 0) + elog(WARN, "array_in: upper_bound cannot be < lower_bound"); + p = q + 1; ndim++; + } else { + done = true; + } + } + + if (ndim == 0) { + if (*p == '{') { + ndim = _ArrayCount(p, dim, typdelim); + for (i = 0; i < ndim; lBound[i++] = 1); + } else { + elog(WARN,"array_in: Need to specify dimension"); + } + } else { + while (isspace(*p)) p++; + if (strncmp(p, ASSGN, strlen(ASSGN))) + elog(WARN, "array_in: missing assignment operator"); + p+= strlen(ASSGN); + while (isspace(*p)) p++; + } + + nitems = getNitems( ndim, dim); + if (nitems == 0) { + char *emptyArray = palloc(sizeof(ArrayType)); + memset(emptyArray, 0, sizeof(ArrayType)); + * (int32 *) emptyArray = sizeof(ArrayType); + return emptyArray; + } + + if (*p == '{') { + /* array not a large object */ + dataPtr = + (char *) _ReadArrayStr(p, nitems, ndim, dim, inputproc, typelem, + typdelim, typlen, typbyval, typalign, + &nbytes ); + nbytes += ARR_OVERHEAD(ndim); + retval = (ArrayType *) palloc(nbytes); + memset(retval,0, nbytes); + memmove(retval, (char *)&nbytes, sizeof(int)); + memmove((char*)ARR_NDIM_PTR(retval), (char *)&ndim, sizeof(int)); + SET_LO_FLAG (false, retval); + memmove((char *)ARR_DIMS(retval), (char *)dim, ndim*sizeof(int)); + memmove((char *)ARR_LBOUND(retval), (char *)lBound, + ndim*sizeof(int)); + /* dataPtr is an array of arbitraystuff even though its type is char* + cast to char** to pass to _CopyArrayEls for now - jolly */ + _CopyArrayEls((char**)dataPtr, + ARR_DATA_PTR(retval), nitems, + typlen, typalign, typbyval); + } else { +#ifdef LOARRAY + int dummy, bytes; + bool chunked = false; + + dataPtr = _ReadLOArray(p, &bytes, &dummy, &chunked, ndim, + dim, typlen ); + nbytes = bytes + ARR_OVERHEAD(ndim); + retval = (ArrayType *) palloc(nbytes); + memset(retval, 0,nbytes); + memmove(retval, (char *)&nbytes, sizeof(int)); + memmove((char *)ARR_NDIM_PTR(retval), (char *)&ndim, sizeof(int)); + SET_LO_FLAG (true, retval); + SET_CHUNK_FLAG (chunked, retval); + memmove((char *)ARR_DIMS(retval), (char *)dim, ndim*sizeof(int)); + memmove((char *)ARR_LBOUND(retval),(char *)lBound, ndim*sizeof(int)); + memmove(ARR_DATA_PTR(retval), dataPtr, bytes); +#endif + elog(WARN, "large object arrays not supported"); + } + pfree(string_save); + return((char *)retval); +} + +/*----------------------------------------------------------------------------- + * _ArrayCount -- + * Counts the number of dimensions and the dim[] array for an array string. + * The syntax for array input is C-like nested curly braces + *----------------------------------------------------------------------------- + */ +static int +_ArrayCount(char *str, int dim[], int typdelim) +{ + int nest_level = 0, i; + int ndim = 0, temp[MAXDIM]; + bool scanning_string = false; + bool eoArray = false; + char *q; + + for (i = 0; i < MAXDIM; ++i) { + temp[i] = dim[i] = 0; + } + + if (strncmp (str, "{}", 2) == 0) return(0); + + q = str; + while (eoArray != true) { + bool done = false; + while (!done) { + switch (*q) { + case '\"': + scanning_string = ! scanning_string; + break; + case '{': + if (!scanning_string) { + temp[nest_level] = 0; + nest_level++; + } + break; + case '}': + if (!scanning_string) { + if (!ndim) ndim = nest_level; + nest_level--; + if (nest_level) temp[nest_level-1]++; + if (nest_level == 0) eoArray = done = true; + } + break; + default: + if (!ndim) ndim = nest_level; + if (*q == typdelim && !scanning_string ) + done = true; + break; + } + if (!done) q++; + } + temp[ndim-1]++; + q++; + if (!eoArray) + while (isspace(*q)) q++; + } + for (i = 0; i < ndim; ++i) { + dim[i] = temp[i]; + } + + return(ndim); +} + +/*--------------------------------------------------------------------------- + * _ReadArrayStr : + * parses the array string pointed by "arrayStr" and converts it in the + * internal format. The external format expected is like C array + * declaration. Unspecified elements are initialized to zero for fixed length + * base types and to empty varlena structures for variable length base + * types. + * result : + * returns the internal representation of the array elements + * nbytes is set to the size of the array in its internal representation. + *--------------------------------------------------------------------------- + */ +static char * +_ReadArrayStr(char *arrayStr, + int nitems, + int ndim, + int dim[], + func_ptr inputproc, /* function used for the conversion */ + Oid typelem, + char typdelim, + int typlen, + bool typbyval, + char typalign, + int *nbytes) +{ + int i, nest_level = 0; + char *p, *q, *r, **values; + bool scanning_string = false; + int indx[MAXDIM], prod[MAXDIM]; + bool eoArray = false; + + mda_get_prod(ndim, dim, prod); + for (i = 0; i < ndim; indx[i++] = 0); + /* read array enclosed within {} */ + values = (char **) palloc(nitems * sizeof(char *)); + memset(values, 0, nitems * sizeof(char *)); + q = p = arrayStr; + + while ( ! eoArray ) { + bool done = false; + int i = -1; + + while (!done) { + switch (*q) { + case '\\': + /* Crunch the string on top of the backslash. */ + for (r = q; *r != '\0'; r++) *r = *(r+1); + break; + case '\"': + if (!scanning_string ) { + while (p != q) p++; + p++; /* get p past first doublequote */ + } else + *q = '\0'; + scanning_string = ! scanning_string; + break; + case '{': + if (!scanning_string) { + p++; + nest_level++; + if (nest_level > ndim) + elog(WARN, "array_in: illformed array constant"); + indx[nest_level - 1] = 0; + indx[ndim - 1] = 0; + } + break; + case '}': + if (!scanning_string) { + if (i == -1) + i = tuple2linear(ndim, indx, prod); + nest_level--; + if (nest_level == 0) + eoArray = done = true; + else { + *q = '\0'; + indx[nest_level - 1]++; + } + } + break; + default: + if (*q == typdelim && !scanning_string ) { + if (i == -1) + i = tuple2linear(ndim, indx, prod); + done = true; + indx[ndim - 1]++; + } + break; + } + if (!done) + q++; + } + *q = '\0'; + if (i >= nitems) + elog(WARN, "array_in: illformed array constant"); + values[i] = (*inputproc) (p, typelem); + p = ++q; + if (!eoArray) + /* + * if not at the end of the array skip white space + */ + while (isspace(*q)) { + p++; + q++; + } + } + if (typlen > 0) { + *nbytes = nitems * typlen; + if (!typbyval) + for (i = 0; i < nitems; i++) + if (!values[i]) { + values[i] = palloc(typlen); + memset(values[i], 0, typlen); + } + } else { + for (i = 0, *nbytes = 0; i < nitems; i++) { + if (values[i]) { + if (typalign=='d') { + *nbytes += DOUBLEALIGN(* (int32 *) values[i]); + } else { + *nbytes += INTALIGN(* (int32 *) values[i]); + } + } else { + *nbytes += sizeof(int32); + values[i] = palloc(sizeof(int32)); + *(int32 *)values[i] = sizeof(int32); + } + } + } + return((char *)values); +} + + +/*---------------------------------------------------------------------------- + * Read data about an array to be stored as a large object + *---------------------------------------------------------------------------- + */ +static char * +_ReadLOArray(char *str, + int *nbytes, + int *fd, + bool *chunkFlag, + int ndim, + int dim[], + int baseSize) +{ + char *inputfile, *accessfile = NULL, *chunkfile = NULL; + char *retStr, *_AdvanceBy1word(); + Oid lobjId; + + str = _AdvanceBy1word(str, &inputfile); + + while (str != NULL) { + char *word; + + str = _AdvanceBy1word(str, &word); + + if (!strcmp (word, "-chunk")) { + if (str == NULL) + elog(WARN, "array_in: access pattern file required"); + str = _AdvanceBy1word(str, &accessfile); + } + else if (!strcmp (word, "-noreorg")) { + if (str == NULL) + elog(WARN, "array_in: chunk file required"); + str = _AdvanceBy1word(str, &chunkfile); + } else { + elog(WARN, "usage: <input file> -chunk DEFAULT/<access pattern file> -invert/-native [-noreorg <chunk file>]"); + } + } + + if (inputfile == NULL) + elog(WARN, "array_in: missing file name"); + lobjId = lo_creat(0); + *fd = lo_open(lobjId, INV_READ); + if ( *fd < 0 ) + elog(WARN, "Large object create failed"); + retStr = inputfile; + *nbytes = strlen(retStr) + 2; + + if ( accessfile ) { + FILE *afd; + if ((afd = fopen (accessfile, "r")) == NULL) + elog(WARN, "unable to open access pattern file"); + *chunkFlag = true; + retStr = _ChunkArray(*fd, afd, ndim, dim, baseSize, nbytes, + chunkfile); + } + return(retStr); +} + +static void +_CopyArrayEls(char **values, + char *p, + int nitems, + int typlen, + char typalign, + bool typbyval) +{ + int i; + + for (i = 0; i < nitems; i++) { + int inc; + inc = ArrayCastAndSet(values[i], typbyval, typlen, p); + p += inc; + if (!typbyval) + pfree(values[i]); + } + pfree(values); +} + +/*------------------------------------------------------------------------- + * array_out : + * takes the internal representation of an array and returns a string + * containing the array in its external format. + *------------------------------------------------------------------------- + */ +char * +array_out(ArrayType *v, Oid element_type) +{ + int typlen; + bool typbyval; + char typdelim; + Oid typoutput, typelem; + func_ptr outputproc; + char typalign; + + char *p, *retval, **values, delim[2]; + int nitems, overall_length, i, j, k, indx[MAXDIM]; + bool dummy_bool; + int dummy_int; + int ndim, *dim; + + if (v == (ArrayType *) NULL) + return ((char *) NULL); + + if (ARR_IS_LO(v) == true) { + char *p, *save_p; + int nbytes; + + /* get a wide string to print to */ + p = array_dims(v, &dummy_bool); + nbytes = strlen(ARR_DATA_PTR(v)) + 4 + *(int *)p; + + save_p = (char *) palloc(nbytes); + + strcpy(save_p, p + sizeof(int)); + strcat(save_p, ASSGN); + strcat(save_p, ARR_DATA_PTR(v)); + pfree(p); + return (save_p); + } + + system_cache_lookup(element_type, false, &typlen, &typbyval, + &typdelim, &typelem, &typoutput, &typalign); + fmgr_info(typoutput, & outputproc, &dummy_int); + sprintf(delim, "%c", typdelim); + ndim = ARR_NDIM(v); + dim = ARR_DIMS(v); + nitems = getNitems(ndim, dim); + + if (nitems == 0) { + char *emptyArray = palloc(3); + emptyArray[0] = '{'; + emptyArray[1] = '}'; + emptyArray[2] = '\0'; + return emptyArray; + } + + p = ARR_DATA_PTR(v); + overall_length = 1; /* [TRH] don't forget to count \0 at end. */ + values = (char **) palloc(nitems * sizeof (char *)); + for (i = 0; i < nitems; i++) { + if (typbyval) { + switch(typlen) { + case 1: + values[i] = (*outputproc) (*p, typelem); + break; + case 2: + values[i] = (*outputproc) (* (int16 *) p, typelem); + break; + case 3: + case 4: + values[i] = (*outputproc) (* (int32 *) p, typelem); + break; + } + p += typlen; + } else { + values[i] = (*outputproc) (p, typelem); + if (typlen > 0) + p += typlen; + else + p += INTALIGN(* (int32 *) p); + /* + * For the pair of double quotes + */ + overall_length += 2; + } + overall_length += (strlen(values[i]) + 1); + } + + /* + * count total number of curly braces in output string + */ + for (i = j = 0, k = 1; i < ndim; k *= dim[i++], j += k); + + p = (char *) palloc(overall_length + 2*j); + retval = p; + + strcpy(p, "{"); + for (i = 0; i < ndim; indx[i++] = 0); + j = 0; k = 0; + do { + for (i = j; i < ndim - 1; i++) + strcat(p, "{"); + /* + * Surround anything that is not passed by value in double quotes. + * See above for more details. + */ + if (!typbyval) { + strcat(p, "\""); + strcat(p, values[k]); + strcat(p, "\""); + } else + strcat(p, values[k]); + pfree(values[k++]); + + for (i = ndim - 1; i >= 0; i--) { + indx[i] = (indx[i] + 1)%dim[i]; + if (indx[i]) { + strcat (p, delim); + break; + } else + strcat (p, "}"); + } + j = i; + } while (j != -1); + + pfree(values); + return(retval); +} + +/*----------------------------------------------------------------------------- + * array_dims : + * returns the dimension of the array pointed to by "v" + *---------------------------------------------------------------------------- + */ +char * +array_dims(ArrayType *v, bool *isNull) +{ + char *p, *save_p; + int nbytes, i; + int *dimv, *lb; + + if (v == (ArrayType *) NULL) RETURN_NULL; + nbytes = ARR_NDIM(v)*33; + /* + * 33 since we assume 15 digits per number + ':' +'[]' + */ + save_p = p = (char *) palloc(nbytes + 4); + memset(save_p, 0, nbytes + 4); + dimv = ARR_DIMS(v); lb = ARR_LBOUND(v); + p += 4; + for (i = 0; i < ARR_NDIM(v); i++) { + sprintf(p, "[%d:%d]", lb[i], dimv[i]+lb[i]-1); + p += strlen(p); + } + nbytes = strlen(save_p + 4) + 4; + memmove(save_p, &nbytes,4); + return (save_p); +} + +/*--------------------------------------------------------------------------- + * array_ref : + * This routing takes an array pointer and an index array and returns + * a pointer to the referred element if element is passed by + * reference otherwise returns the value of the referred element. + *--------------------------------------------------------------------------- + */ +Datum +array_ref(ArrayType *array, + int n, + int indx[], + int reftype, + int elmlen, + int arraylen, + bool *isNull) +{ + int i, ndim, *dim, *lb, offset, nbytes; + struct varlena *v; + char *retval; + + if (array == (ArrayType *) NULL) RETURN_NULL; + if (arraylen > 0) { + /* + * fixed length arrays -- these are assumed to be 1-d + */ + if (indx[0]*elmlen > arraylen) + elog(WARN, "array_ref: array bound exceeded"); + retval = (char *)array + indx[0]*elmlen; + return _ArrayCast(retval, reftype, elmlen); + } + dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + ndim = ARR_NDIM(array); + nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim); + + if (!SanityCheckInput(ndim, n, dim, lb, indx)) + RETURN_NULL; + + offset = GetOffset(n, dim, lb, indx); + + if (ARR_IS_LO(array)) { + char * lo_name; + int fd; + + /* We are assuming fixed element lengths here */ + offset *= elmlen; + lo_name = (char *)ARR_DATA_PTR(array); +#ifdef LOARRAY + if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_READ:O_RDONLY)) < 0) + RETURN_NULL; +#endif + if (ARR_IS_CHUNKED(array)) + v = _ReadChunkArray1El(indx, elmlen, fd, array, isNull); + else { + if (lo_lseek(fd, offset, SEEK_SET) < 0) + RETURN_NULL; +#ifdef LOARRAY + v = (struct varlena *) LOread(fd, elmlen); +#endif + } + if (*isNull) RETURN_NULL; + if (VARSIZE(v) - 4 < elmlen) + RETURN_NULL; + (void) lo_close(fd); + retval = (char *)_ArrayCast((char *)VARDATA(v), reftype, elmlen); + if ( reftype == 0) { /* not by value */ + char * tempdata = palloc (elmlen); + memmove(tempdata, retval, elmlen); + retval = tempdata; + } + pfree(v); + return (Datum) retval; + } + + if (elmlen > 0) { + offset = offset * elmlen; + /* off the end of the array */ + if (nbytes - offset < 1) RETURN_NULL; + retval = ARR_DATA_PTR (array) + offset; + return _ArrayCast(retval, reftype, elmlen); + } else { + bool done = false; + char *temp; + int bytes = nbytes; + temp = ARR_DATA_PTR (array); + i = 0; + while (bytes > 0 && !done) { + if (i == offset) { + retval = temp; + done = true; + } + bytes -= INTALIGN(* (int32 *) temp); + temp += INTALIGN(* (int32 *) temp); + i++; + } + if (! done) + RETURN_NULL; + return (Datum) retval; + } +} + +/*----------------------------------------------------------------------------- + * array_clip : + * This routine takes an array and a range of indices (upperIndex and + * lowerIndx), creates a new array structure for the referred elements + * and returns a pointer to it. + *----------------------------------------------------------------------------- + */ +Datum +array_clip(ArrayType *array, + int n, + int upperIndx[], + int lowerIndx[], + int reftype, + int len, + bool *isNull) +{ + int i, ndim, *dim, *lb, nbytes; + ArrayType *newArr; + int bytes, span[MAXDIM]; + + /* timer_start(); */ + if (array == (ArrayType *) NULL) + RETURN_NULL; + dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + ndim = ARR_NDIM(array); + nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim); + + if (!SanityCheckInput(ndim, n, dim, lb, upperIndx)) + RETURN_NULL; + + if (!SanityCheckInput(ndim, n, dim, lb, lowerIndx)) + RETURN_NULL; + + for (i = 0; i < n; i++) + if (lowerIndx[i] > upperIndx[i]) + elog(WARN, "lowerIndex cannot be larger than upperIndx"); + mda_get_range(n, span, lowerIndx, upperIndx); + + if (ARR_IS_LO(array)) { + char * lo_name, * newname; + int fd, newfd, isDestLO = true, rsize; + + if (len < 0) + elog(WARN, "array_clip: array of variable length objects not supported"); +#ifdef LOARRAY + lo_name = (char *)ARR_DATA_PTR(array); + if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_READ:O_RDONLY)) < 0) + RETURN_NULL; + newname = _array_newLO( &newfd, Unix ); +#endif + bytes = strlen(newname) + 1 + ARR_OVERHEAD(n); + newArr = (ArrayType *) palloc(bytes); + memmove(newArr, array, sizeof(ArrayType)); + memmove(newArr, &bytes, sizeof(int)); + memmove(ARR_DIMS(newArr), span, n*sizeof(int)); + memmove(ARR_LBOUND(newArr), lowerIndx, n*sizeof(int)); + strcpy(ARR_DATA_PTR(newArr), newname); + + rsize = compute_size (lowerIndx, upperIndx, n, len); + if (rsize < MAX_BUFF_SIZE) { + char *buff; + rsize += 4; + buff = palloc(rsize); + if ( buff ) + isDestLO = false; + if (ARR_IS_CHUNKED(array)) { + _ReadChunkArray(lowerIndx, upperIndx, len, fd, &(buff[4]), + array,0,isNull); + } else { + _ReadArray(lowerIndx, upperIndx, len, fd, (int)&(buff[4]), + array, + 0,isNull); + } + memmove(buff, &rsize, 4); +#ifdef LOARRAY + if (! *isNull) + bytes = LOwrite(newfd, (struct varlena *)buff); +#endif + pfree (buff); + } + if (isDestLO) + if (ARR_IS_CHUNKED(array)) { + _ReadChunkArray(lowerIndx, upperIndx, len, fd, (char*)newfd, array, + 1,isNull); + } else { + _ReadArray(lowerIndx, upperIndx, len, fd, newfd, array, 1,isNull); + } +#ifdef LOARRAY + (void) LOclose(fd); + (void) LOclose(newfd); +#endif + if (*isNull) { + pfree(newArr); + newArr = NULL; + } + /* timer_end(); */ + return ((Datum) newArr); + } + + if (len > 0) { + bytes = getNitems(n, span); + bytes = bytes*len + ARR_OVERHEAD(n); + } else { + bytes = _ArrayClipCount(lowerIndx, upperIndx, array); + bytes += ARR_OVERHEAD(n); + } + newArr = (ArrayType *) palloc(bytes); + memmove(newArr, array, sizeof(ArrayType)); + memmove(newArr, &bytes, sizeof(int)); + memmove(ARR_DIMS(newArr), span, n*sizeof(int)); + memmove(ARR_LBOUND(newArr), lowerIndx, n*sizeof(int)); + _ArrayRange(lowerIndx, upperIndx, len, ARR_DATA_PTR(newArr), array, 1); + return (Datum) newArr; +} + +/*----------------------------------------------------------------------------- + * array_set : + * This routine sets the value of an array location (specified by an index array) + * to a new value specified by "dataPtr". + * result : + * returns a pointer to the modified array. + *----------------------------------------------------------------------------- + */ +char * +array_set(ArrayType *array, + int n, + int indx[], + char *dataPtr, + int reftype, + int elmlen, + int arraylen, + bool *isNull) +{ + int ndim, *dim, *lb, offset, nbytes; + char *pos; + + if (array == (ArrayType *) NULL) + RETURN_NULL; + if (arraylen > 0) { + /* + * fixed length arrays -- these are assumed to be 1-d + */ + if (indx[0]*elmlen > arraylen) + elog(WARN, "array_ref: array bound exceeded"); + pos = (char *)array + indx[0]*elmlen; + ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, pos); + return((char *)array); + } + dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + ndim = ARR_NDIM(array); + nbytes = (* (int32 *) array) - ARR_OVERHEAD(ndim); + + if (!SanityCheckInput(ndim, n, dim, lb, indx)) + return((char *)array); + offset = GetOffset( n, dim, lb, indx); + + if (ARR_IS_LO(array)) { + int fd; + char * lo_name; + struct varlena *v; + + /* We are assuming fixed element lengths here */ + offset *= elmlen; +#ifdef LOARRAY + lo_name = ARR_DATA_PTR(array); + if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_WRITE:O_WRONLY)) < 0) + return((char *)array); +#endif + if (lo_lseek(fd, offset, SEEK_SET) < 0) + return((char *)array); + v = (struct varlena *) palloc(elmlen + 4); + VARSIZE (v) = elmlen + 4; + ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, VARDATA(v)); +#ifdef LOARRAY + n = LOwrite(fd, v); +#endif + /* if (n < VARSIZE(v) - 4) + RETURN_NULL; + */ + pfree(v); + (void) lo_close(fd); + return((char *)array); + } + if (elmlen > 0) { + offset = offset * elmlen; + /* off the end of the array */ + if (nbytes - offset < 1) return((char *)array); + pos = ARR_DATA_PTR (array) + offset; + } else { + elog(WARN, "array_set: update of variable length fields not supported"); + } + ArrayCastAndSet(dataPtr, (bool) reftype, elmlen, pos); + return((char *)array); +} + +/*---------------------------------------------------------------------------- + * array_assgn : + * This routine sets the value of a range of array locations (specified + * by upper and lower index values ) to new values passed as + * another array + * result : + * returns a pointer to the modified array. + *---------------------------------------------------------------------------- + */ +char * +array_assgn(ArrayType *array, + int n, + int upperIndx[], + int lowerIndx[], + ArrayType *newArr, + int reftype, + int len, + bool *isNull) +{ + int i, ndim, *dim, *lb; + + if (array == (ArrayType *) NULL) + RETURN_NULL; + if (len < 0) + elog(WARN,"array_assgn:updates on arrays of variable length elements not allowed"); + + dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + ndim = ARR_NDIM(array); + + if (!SanityCheckInput(ndim, n, dim, lb, upperIndx) || + !SanityCheckInput(ndim, n, dim, lb, lowerIndx)) { + return((char *)array); + } + + for (i = 0; i < n; i++) + if (lowerIndx[i] > upperIndx[i]) + elog(WARN, "lowerIndex larger than upperIndx"); + + if (ARR_IS_LO(array)) { + char * lo_name; + int fd, newfd; + +#ifdef LOARRAY + lo_name = (char *)ARR_DATA_PTR(array); + if ((fd = LOopen(lo_name, ARR_IS_INV(array)?INV_WRITE:O_WRONLY)) < 0) + return((char *)array); +#endif + if (ARR_IS_LO(newArr)) { +#ifdef LOARRAY + lo_name = (char *)ARR_DATA_PTR(newArr); + if ((newfd = LOopen(lo_name, ARR_IS_INV(newArr)?INV_READ:O_RDONLY)) < 0) + return((char *)array); +#endif + _LOArrayRange(lowerIndx, upperIndx, len, fd, newfd, array, 1, isNull); + (void) lo_close(newfd); + } else { + _LOArrayRange(lowerIndx, upperIndx, len, fd, (int)ARR_DATA_PTR(newArr), + array, 0, isNull); + } + (void) lo_close(fd); + return ((char *) array); + } + _ArrayRange(lowerIndx, upperIndx, len, ARR_DATA_PTR(newArr), array, 0); + return (char *) array; +} + +/*----------------------------------------------------------------------------- + * array_eq : + * compares two arrays for equality + * result : + * returns 1 if the arrays are equal, 0 otherwise. + *----------------------------------------------------------------------------- + */ +int +array_eq (ArrayType *array1, ArrayType *array2) +{ + if ((array1 == NULL) || (array2 == NULL)) + return(0); + if (*(int *)array1 != *(int *)array2) + return (0); + if (memcmp(array1, array2, *(int *)array1)) + return(0); + return(1); +} + +/***************************************************************************/ +/******************| Support Routines |*****************/ +/***************************************************************************/ +static void +system_cache_lookup(Oid element_type, + bool input, + int *typlen, + bool *typbyval, + char *typdelim, + Oid *typelem, + Oid *proc, + char *typalign) +{ + HeapTuple typeTuple; + TypeTupleForm typeStruct; + + typeTuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(element_type), + 0,0,0); + + if (!HeapTupleIsValid(typeTuple)) { + elog(WARN, "array_out: Cache lookup failed for type %d\n", + element_type); + return; + } + typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple); + *typlen = typeStruct->typlen; + *typbyval = typeStruct->typbyval; + *typdelim = typeStruct->typdelim; + *typelem = typeStruct->typelem; + *typalign = typeStruct->typalign; + if (input) { + *proc = typeStruct->typinput; + } else { + *proc = typeStruct->typoutput; + } +} + +static Datum +_ArrayCast(char *value, bool byval, int len) +{ + if (byval) { + switch (len) { + case 1: + return((Datum) * value); + case 2: + return((Datum) * (int16 *) value); + case 3: + case 4: + return((Datum) * (int32 *) value); + default: + elog(WARN, "array_ref: byval and elt len > 4!"); + break; + } + } else { + return (Datum) value; + } + return 0; +} + + +static int +ArrayCastAndSet(char *src, + bool typbyval, + int typlen, + char *dest) +{ + int inc; + + if (typlen > 0) { + if (typbyval) { + switch(typlen) { + case 1: + *dest = DatumGetChar(src); + break; + case 2: + * (int16 *) dest = DatumGetInt16(src); + break; + case 4: + * (int32 *) dest = (int32)src; + break; + } + } else { + memmove(dest, src, typlen); + } + inc = typlen; + } else { + memmove(dest, src, *(int32 *)src); + inc = (INTALIGN(* (int32 *) src)); + } + return(inc); +} + +static char * +_AdvanceBy1word(char *str, char **word) +{ + char *retstr, *space; + + *word = NULL; + if (str == NULL) return str; + while (isspace(*str)) str++; + *word = str; + if ((space = (char *)strchr(str, ' ')) != (char *) NULL) { + retstr = space + 1; + *space = '\0'; + } + else + retstr = NULL; + return retstr; +} + +int +SanityCheckInput(int ndim, int n, int dim[], int lb[], int indx[]) +{ + int i; + /* Do Sanity check on input */ + if (n != ndim) return 0; + for (i = 0; i < ndim; i++) + if ((lb[i] > indx[i]) || (indx[i] >= (dim[i] + lb[i]))) + return 0; + return 1; +} + +static void +_ArrayRange(int st[], + int endp[], + int bsize, + char *destPtr, + ArrayType *array, + int from) +{ + int n, *dim, *lb, st_pos, prod[MAXDIM]; + int span[MAXDIM], dist[MAXDIM], indx[MAXDIM]; + int i, j, inc; + char *srcPtr, *array_seek(); + + n = ARR_NDIM(array); dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); srcPtr = ARR_DATA_PTR(array); + for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++); + mda_get_prod(n, dim, prod); + st_pos = tuple2linear(n, st, prod); + srcPtr = array_seek(srcPtr, bsize, st_pos); + mda_get_range(n, span, st, endp); + mda_get_offset_values(n, dist, prod, span); + for (i=0; i < n; indx[i++]=0); + i = j = n-1; inc = bsize; + do { + srcPtr = array_seek(srcPtr, bsize, dist[j]); + if (from) + inc = array_read(destPtr, bsize, 1, srcPtr); + else + inc = array_read(srcPtr, bsize, 1, destPtr); + destPtr += inc; srcPtr += inc; + } while ((j = next_tuple(i+1, indx, span)) != -1); +} + +static int +_ArrayClipCount(int stI[], int endpI[], ArrayType *array) +{ + int n, *dim, *lb, st_pos, prod[MAXDIM]; + int span[MAXDIM], dist[MAXDIM], indx[MAXDIM]; + int i, j, inc, st[MAXDIM], endp[MAXDIM]; + int count = 0; + char *ptr, *array_seek(); + + n = ARR_NDIM(array); dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); ptr = ARR_DATA_PTR(array); + for (i = 0; i < n; st[i] = stI[i]-lb[i], endp[i]=endpI[i]-lb[i], i++); + mda_get_prod(n, dim, prod); + st_pos = tuple2linear(n, st, prod); + ptr = array_seek(ptr, -1, st_pos); + mda_get_range(n, span, st, endp); + mda_get_offset_values(n, dist, prod, span); + for (i=0; i < n; indx[i++]=0); + i = j = n-1; + do { + ptr = array_seek(ptr, -1, dist[j]); + inc = INTALIGN(* (int32 *) ptr); + ptr += inc; count += inc; + } while ((j = next_tuple(i+1, indx, span)) != -1); + return count; +} + +char * +array_seek(char *ptr, int eltsize, int nitems) +{ + int i; + + if (eltsize > 0) + return(ptr + eltsize*nitems); + for (i = 0; i < nitems; i++) + ptr += INTALIGN(* (int32 *) ptr); + return(ptr); +} + +int +array_read(char *destptr, int eltsize, int nitems, char *srcptr) +{ + int i, inc, tmp; + + if (eltsize > 0) { + memmove(destptr, srcptr, eltsize*nitems); + return(eltsize*nitems); + } + for (i = inc = 0; i < nitems; i++) { + tmp = (INTALIGN(* (int32 *) srcptr)); + memmove(destptr, srcptr, tmp); + srcptr += tmp; + destptr += tmp; + inc += tmp; + } + return(inc); +} + +static void +_LOArrayRange(int st[], + int endp[], + int bsize, + int srcfd, + int destfd, + ArrayType *array, + int isSrcLO, + bool *isNull) +{ + int n, *dim, st_pos, prod[MAXDIM]; + int span[MAXDIM], dist[MAXDIM], indx[MAXDIM]; + int i, j, inc, tmp, *lb, offset; + + n = ARR_NDIM(array); dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++); + + mda_get_prod(n, dim, prod); + st_pos = tuple2linear(n, st, prod); + offset = st_pos*bsize; + if (lo_lseek(srcfd, offset, SEEK_SET) < 0) + return; + mda_get_range(n, span, st, endp); + mda_get_offset_values(n, dist, prod, span); + for (i=0; i < n; indx[i++]=0); + for (i = n-1, inc = bsize; i >= 0; inc *= span[i--]) + if (dist[i]) + break; + j = n-1; + do { + offset += (dist[j]*bsize); + if (lo_lseek(srcfd, offset, SEEK_SET) < 0) + return; + tmp = _LOtransfer((char**)&srcfd, inc, 1, (char**)&destfd, isSrcLO, 1); + if ( tmp < inc ) + return; + offset += inc; + } while ((j = next_tuple(i+1, indx, span)) != -1); +} + + +static void +_ReadArray (int st[], + int endp[], + int bsize, + int srcfd, + int destfd, + ArrayType *array, + int isDestLO, + bool *isNull) +{ + int n, *dim, st_pos, prod[MAXDIM]; + int span[MAXDIM], dist[MAXDIM], indx[MAXDIM]; + int i, j, inc, tmp, *lb, offset; + + n = ARR_NDIM(array); dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++); + + mda_get_prod(n, dim, prod); + st_pos = tuple2linear(n, st, prod); + offset = st_pos*bsize; + if (lo_lseek(srcfd, offset, SEEK_SET) < 0) + return; + mda_get_range(n, span, st, endp); + mda_get_offset_values(n, dist, prod, span); + for (i=0; i < n; indx[i++]=0); + for (i = n-1, inc = bsize; i >= 0; inc *= span[i--]) + if (dist[i]) + break; + j = n-1; + do { + offset += (dist[j]*bsize); + if (lo_lseek(srcfd, offset, SEEK_SET) < 0) + return; + tmp = _LOtransfer((char**)&destfd, inc, 1, (char**)&srcfd, 1, isDestLO); + if ( tmp < inc ) + return; + offset += inc; + } while ((j = next_tuple(i+1, indx, span)) != -1); +} + + +int +_LOtransfer(char **destfd, + int size, + int nitems, + char **srcfd, + int isSrcLO, + int isDestLO) +{ +#define MAX_READ (512 * 1024) +#define min(a, b) (a < b ? a : b) + struct varlena *v; + int tmp, inc, resid; + + inc = nitems*size; + if (isSrcLO && isDestLO && inc > 0) + for (tmp = 0, resid = inc; + resid > 0 && (inc = min(resid, MAX_READ)) > 0; resid -= inc) { +#ifdef LOARRAY + v = (struct varlena *) LOread((int) *srcfd, inc); + if (VARSIZE(v) - 4 < inc) + {pfree(v); return(-1);} + tmp += LOwrite((int) *destfd, v); +#endif + pfree(v); + + } + else if (!isSrcLO && isDestLO) { + tmp = lo_write((int) *destfd, *srcfd, inc); + *srcfd = *srcfd + tmp; + } + else if (isSrcLO && !isDestLO) { + tmp = lo_read((int) *srcfd, *destfd, inc); + *destfd = *destfd + tmp; + } + else { + memmove(*destfd, *srcfd, inc); + tmp = inc; + *srcfd += inc; + *destfd += inc; + } + return(tmp); +#undef MAX_READ +} + +char * +_array_newLO(int *fd, int flag) +{ + char *p; + char saveName[NAME_LEN]; + + p = (char *) palloc(NAME_LEN); + sprintf(p, "/Arry.%d", newoid()); + strcpy (saveName, p); +#ifdef LOARRAY + if ( (*fd = LOcreat (saveName, 0600, flag)) < 0) + elog(WARN, "Large object create failed"); +#endif + return (p); +} + diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c new file mode 100644 index 00000000000..9a16b950f2b --- /dev/null +++ b/src/backend/utils/adt/arrayutils.c @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------- + * + * arrayutils.c-- + * This file contains some support routines required for array functions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayutils.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +#define WEAK_C_OPTIMIZER + +#include "c.h" + +int +GetOffset(int n, int dim[], int lb[], int indx[]) +{ + int i, scale, offset; + for (i = n-1, scale = 1, offset = 0; i >= 0; scale*=dim[i--]) + offset += (indx[i] - lb[i])*scale; + return offset ; +} + +int +getNitems(int n, int a[]) +{ + int i, ret; + for (i = 0, ret = 1; i < n; ret *= a[i++]); + if (n == 0) ret = 0; + return ret; +} + +int +compute_size(int st[], int endp[], int n, int base) +{ + int i, ret; + for (i = 0, ret = base; i < n; i++) + ret *= (endp[i] - st[i] + 1); + return ret; +} + +void +mda_get_offset_values(int n, int dist[], int PC[], int span[]) +{ + int i, j; + for (j = n-2, dist[n-1]=0; j >= 0; j--) + for (i = j+1, dist[j] = PC[j]-1; i < n; + dist[j] -= (span[i] - 1)*PC[i], i++); +} + +void +mda_get_range(int n, int span[], int st[], int endp[]) +{ + int i; + for (i= 0; i < n; i++) + span[i] = endp[i] - st[i] + 1; +} + +void +mda_get_prod(int n, int range[], int P[]) +{ + int i; + for (i= n-2, P[n-1] = 1; i >= 0; i--) + P[i] = P[i+1] * range[i + 1]; +} + +int +tuple2linear(int n, int tup[], int scale[]) +{ + int i, lin; + for (i= lin = 0; i < n; i++) + lin += tup[i]*scale[i]; + return lin; +} + +void +array2chunk_coord(int n, int C[], int a_coord[], int c_coord[]) +{ + int i; + for (i= 0; i < n; i++) + c_coord[i] = a_coord[i]/C[i]; +} + +/*----------------------------------------------------------------------------- + generates the tuple that is lexicographically one greater than the current + n-tuple in "curr", with the restriction that the i-th element of "curr" is + less than the i-th element of "span". + RETURNS 0 if no next tuple exists + 1 otherwise + -----------------------------------------------------------------------------*/ +int +next_tuple(int n, int curr[], int span[]) +{ + int i; + + if (!n) return(-1); + curr[n-1] = (curr[n-1]+1)%span[n-1]; + for (i = n-1; i*(!curr[i]); i--) + curr[i-1] = (curr[i-1]+1)%span[i-1]; + + if (i) + return(i); + if (curr[0]) + return(0); + return(-1); +} + diff --git a/src/backend/utils/adt/bool.c b/src/backend/utils/adt/bool.c new file mode 100644 index 00000000000..d0f3d34b52a --- /dev/null +++ b/src/backend/utils/adt/bool.c @@ -0,0 +1,65 @@ +/*------------------------------------------------------------------------- + * + * bool.c-- + * Functions for the built-in type "bool". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/bool.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "utils/builtins.h" /* where the declarations go */ +#include "utils/elog.h" +#include "utils/palloc.h" + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * boolin - converts "t" or "f" to 1 or 0 + */ +int32 +boolin(char *b) +{ + if (b == NULL) + elog(WARN, "Bad input string for type bool"); + return((int32) (*b == 't') || (*b == 'T')); +} + +/* + * boolout - converts 1 or 0 to "t" or "f" + */ +char * +boolout(long b) +{ + char *result = (char *) palloc(2); + + *result = (b) ? 't' : 'f'; + result[1] = '\0'; + return(result); +} + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +int32 +booleq(int8 arg1, int8 arg2) +{ + return(arg1 == arg2); +} + +int32 +boolne(int8 arg1, int8 arg2) +{ + return(arg1 != arg2); +} + + + + + diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c new file mode 100644 index 00000000000..0d9c8f4fb58 --- /dev/null +++ b/src/backend/utils/adt/char.c @@ -0,0 +1,392 @@ +/*------------------------------------------------------------------------- + * + * char.c-- + * Functions for the built-in type "char". + * Functions for the built-in type "cid". + * Functions for the built-in type "char2". + * Functions for the built-in type "char4". + * Functions for the built-in type "char8". + * Functions for the built-in type "char16". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/char.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/builtins.h" /* where the declarations go */ + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * charin - converts "x" to 'x' + */ +int32 charin(char *ch) +{ + if (ch == NULL) + return((int32) NULL); + return((int32) *ch); +} + +/* + * charout - converts 'x' to "x" + */ +char *charout(int32 ch) +{ + char *result = (char *) palloc(2); + + result[0] = (char) ch; + result[1] = '\0'; + return(result); +} + +/* + * cidin - converts "..." to internal representation. + * + * NOTE: we must not use 'charin' because cid might be a non + * printable character... + */ +int32 cidin(char *s) +{ + CommandId c; + + if (s==NULL) + c = 0; + else + c = atoi(s); + + return((int32)c); +} + +/* + * cidout - converts a cid to "..." + * + * NOTE: we must no use 'charout' because cid might be a non + * printable character... + */ +char *cidout(int32 c) +{ + char *result; + CommandId c2; + + /* + * cid is a number between 0 .. 2^16-1, therefore we need at most + * 6 chars for the string (5 digits + '\0') + * NOTE: print it as an UNSIGNED int! + */ + result = palloc(6); + c2 = (CommandId)c; + sprintf(result, "%u", (unsigned)(c2)); + return(result); +} + +/* + * char16in - converts "..." to internal reprsentation + * + * Note: + * Currently if strlen(s) < 14, the extra chars are nulls + */ +char *char16in(char *s) +{ + char *result; + + if (s == NULL) + return(NULL); + result = (char *) palloc(16); + memset(result, 0, 16); + (void) strncpy(result, s, 16); + return(result); +} + +/* + * char16out - converts internal reprsentation to "..." + */ +char *char16out(char *s) +{ + char *result = (char *) palloc(17); + + memset(result, 0, 17); + if (s == NULL) { + result[0] = '-'; + } else { + strncpy(result, s, 16); + } + return(result); +} + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +int32 chareq(int8 arg1, int8 arg2) { return(arg1 == arg2); } +int32 charne(int8 arg1, int8 arg2) { return(arg1 != arg2); } +int32 charlt(int8 arg1, int8 arg2) { return(arg1 < arg2); } +int32 charle(int8 arg1, int8 arg2) { return(arg1 <= arg2); } +int32 chargt(int8 arg1, int8 arg2) { return(arg1 > arg2); } +int32 charge(int8 arg1, int8 arg2) { return(arg1 >= arg2); } +int8 charpl(int8 arg1, int8 arg2) { return(arg1 + arg2); } +int8 charmi(int8 arg1, int8 arg2) { return(arg1 - arg2); } +int8 charmul(int8 arg1, int8 arg2) { return(arg1 * arg2); } +int8 chardiv(int8 arg1, int8 arg2) { return(arg1 / arg2); } + +int32 cideq(int8 arg1, int8 arg2) { return(arg1 == arg2); } + +/* + * char16eq - returns 1 iff arguments are equal + * char16ne - returns 1 iff arguments are not equal + * + * BUGS: + * Assumes that "xy\0\0a" should be equal to "xy\0b". + * If not, can do the comparison backwards for efficiency. + * + * char16lt - returns 1 iff a < b + * char16le - returns 1 iff a <= b + * char16gt - returns 1 iff a < b + * char16ge - returns 1 iff a <= b + * + */ +int32 char16eq(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 16) == 0); +} + +int32 char16ne(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 16) != 0); +} + +int32 char16lt(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return((int32) (strncmp(arg1, arg2, 16) < 0)); +} + +int32 char16le(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return((int32) (strncmp(arg1, arg2, 16) <= 0)); +} + +int32 char16gt(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + + return((int32) (strncmp(arg1, arg2, 16) > 0)); +} + +int32 char16ge(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + + return((int32) (strncmp(arg1, arg2, 16) >= 0)); +} + + +/* ============================== char2 ============================== */ +uint16 char2in(char *s) +{ + uint16 res; + + if (s == NULL) + return(0); + + memset((char *) &res, 0, sizeof(res)); + (void) strncpy((char *) &res, s, 2); + return(res); +} + +char *char2out(uint16 s) +{ + char *result = (char *) palloc(3); + + memset(result, 0, 3); + (void) strncpy(result, (char *) &s, 2); + + return(result); +} + +int32 char2eq(uint16 a, uint16 b) +{ + return(strncmp((char *) &a, (char *) &b, 2) == 0); +} + +int32 char2ne(uint16 a, uint16 b) +{ + return(strncmp((char *) &a, (char *) &b, 2) != 0); +} + +int32 char2lt(uint16 a, uint16 b) +{ + return(strncmp((char *) &a, (char *) &b, 2) < 0); +} + +int32 char2le(uint16 a, uint16 b) +{ + return(strncmp((char *) &a, (char *) &b, 2) <= 0); +} + +int32 char2gt(uint16 a, uint16 b) +{ + return(strncmp((char *) &a, (char *) &b, 2) > 0); +} + +int32 char2ge(uint16 a, uint16 b) +{ + return(strncmp((char *) &a, (char *) &b, 2) >= 0); +} + +int32 char2cmp(uint16 a, uint16 b) +{ + return (strncmp((char *) &a, (char *) &b, 2)); +} + +/* ============================== char4 ============================== */ +uint32 char4in(char *s) +{ + uint32 res; + + if (s == NULL) + return(0); + + memset((char *) &res, 0, sizeof(res)); + (void) strncpy((char *) &res, s, 4); + + return(res); +} + +char *char4out(s) + uint32 s; +{ + char *result = (char *) palloc(5); + + memset(result, 0, 5); + (void) strncpy(result, (char *) &s, 4); + + return(result); +} + +int32 char4eq(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4) == 0); +} + +int32 char4ne(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4) != 0); +} + +int32 char4lt(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4) < 0); +} + +int32 char4le(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4) <= 0); +} + +int32 char4gt(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4) > 0); +} + +int32 char4ge(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4) >= 0); +} + +int32 char4cmp(uint32 a, uint32 b) +{ + return(strncmp((char *) &a, (char *) &b, 4)); +} + +/* ============================== char8 ============================== */ +char *char8in(char *s) +{ + char *result; + + if (s == NULL) + return((char *) NULL); + + result = (char *) palloc(8); + memset(result, 0, 8); + (void) strncpy(result, s, 8); + return(result); +} + +char *char8out(char *s) +{ + char *result = (char *) palloc(9); + + memset(result, 0, 9); + if (s == NULL) { + result[0] = '-'; + } else { + strncpy(result, s, 8); + } + return(result); +} + +int32 char8eq(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 8) == 0); +} + +int32 char8ne(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 8) != 0); +} + +int32 char8lt(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 8) < 0); +} + +int32 char8le(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 8) <= 0); +} + +int32 char8gt(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 8) > 0); +} + +int32 char8ge(char *arg1, char *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1, arg2, 8) >= 0); +} + +int32 char8cmp(char *arg1, char *arg2) +{ + return(strncmp(arg1, arg2, 8)); +} diff --git a/src/backend/utils/adt/chunk.c b/src/backend/utils/adt/chunk.c new file mode 100644 index 00000000000..ca0cb2647ea --- /dev/null +++ b/src/backend/utils/adt/chunk.c @@ -0,0 +1,587 @@ +/*------------------------------------------------------------------------- + * + * chunk.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/chunk.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include "postgres.h" +#include "utils/memutils.h" +#include "libpq/libpq-fs.h" + +#include "storage/fd.h" /* for SEEK_ */ + +#include "catalog/pg_type.h" + +#include "utils/palloc.h" +#include "fmgr.h" +#include "utils/elog.h" +#include "utils/array.h" + +#include "optimizer/internal.h" + +#define INFTY 500000000 +#define MANY 10000 +#define MAXPAT 20 +#define quot_ceil(x,y) (((x)+(y)-1)/(y)) +#define min(x,y) (((x) < (y))? (x) : (y)) +#define max(x,y) (((x) > (y))? (x) : (y)) + +static CHUNK_INFO cInfo; + +/* non-export function prototypes */ +static int _FindBestChunk(int size, int dmax[], int dbest[], int dim, + int A[MAXPAT][MAXDIM+1], int N); +static int get_next(int d[], int k, int C, int dmax[]); +static void initialize_info(CHUNK_INFO *A, int ndim, int dim[], int chunk[]); +static void _ConvertToChunkFile(int n, int baseSize, int dim[], int C[], + int srcfd, int destfd); +static void read_chunk(int chunk_no[], int C[], char a_chunk[], int srcfd, + int n, int baseSize, int PX[], int dist[]); +static int write_chunk(struct varlena * a_chunk, int ofile); +static int seek_and_read(int pos, int size, char buff[], int fp, int from); + +/*------------------------------------------------------------------------ + * _ChunkArray --- + * converts an input array to chunked format using the information + * provided by the access pattern. + * Results: + * creates a new file that stores the chunked array and returns + * information about the chunked file + *----------------------------------------------------------------------- + */ +char * +_ChunkArray(int fd, + FILE *afd, + int ndim, + int dim[], + int baseSize, + int *nbytes, + char *chunkfile) +{ + int cfd; + int chunk[MAXDIM], csize; + bool reorgFlag; + + if (chunkfile == NULL) + reorgFlag = true; + else + reorgFlag = false; + +#ifdef LOARRAY + if (reorgFlag) + /* create new LO for chunked file */ + chunkfile = _array_newLO( &cfd, fileFlag ); + else + cfd = LOopen(chunkfile, O_RDONLY); +#endif + if (cfd < 0) + elog(WARN, "Enable to open chunk file"); + strcpy (cInfo.lo_name, chunkfile); + + /* find chunk size */ + csize = GetChunkSize(afd, ndim, dim, baseSize, chunk); + + if (reorgFlag) + /* copy data from input file to chunked file */ + _ConvertToChunkFile(ndim, baseSize, dim, chunk, fd, cfd); + + initialize_info(&cInfo, ndim, dim, chunk); + *nbytes = sizeof(CHUNK_INFO); + return (char *) &cInfo ; +} + +/*-------------------------------------------------------------------------- + * GetChunkSize -- + * given an access pattern and array dimensionality etc, this program + * returns the dimensions of the chunk in "d" + *----------------------------------------------------------------------- + */ +int +GetChunkSize(FILE *fd, + int ndim, + int dim[MAXDIM], + int baseSize, + int d[MAXDIM]) +{ + int N, i, j, csize; + int A[MAXPAT][MAXDIM+1], dmax[MAXDIM]; + + /* + * ----------- read input ------------ + */ + fscanf(fd, "%d", &N); + if ( N > MAXPAT ) + elog(WARN, "array_in: too many access pattern elements"); + for (i = 0; i < N; i++) + for (j = 0; j < ndim+1; j++) + if (fscanf(fd, "%d ", &(A[i][j])) == EOF) + elog (WARN, "array_in: bad access pattern input"); + + /* + * estimate chunk size + */ + for (i = 0; i < ndim; i++) + for (j = 0, dmax[i] = 1; j < N; j++) + if (dmax[i] < A[j][i]) + dmax[i] = A[j][i]; + csize = _PAGE_SIZE_/baseSize; + + _FindBestChunk (csize, dmax, d, ndim, A, N); + + return csize; +} + +/*------------------------------------------------------------------------- + * _FindBestChunk -- + * This routine does most of the number crunching to compute the + * optimal chunk shape. + * Called by GetChunkSize + *------------------------------------------------------------------------ + */ +static int +_FindBestChunk(int size, + int dmax[], + int dbest[], + int dim, + int A[MAXPAT][MAXDIM+1], + int N) +{ + int d[MAXDIM]; + int tc, mintc = INFTY; + + d[0] = 0; + mintc = INFTY; + while (get_next(d,dim,size, dmax)) { + /* + * compute the number of page fetches for a given + * chunk size (d[]) and access pattern (A[][]) + */ + register int i,j, nc; + for (i = 0, tc = 0; i < N; i++){ + for (j = 0, nc = 1; j < dim; j++) + nc *= quot_ceil(A[i][j], d[j]); + nc *= A[i][dim]; + tc += nc; + } + /* + * tc holds the total number of page fetches + */ + if (mintc >= tc) { + mintc = tc; + for (j = 0; j < dim; dbest[j] = d[j], j++) + ; + } + } + return(mintc); +} + +/*---------------------------------------------------------------------- + * get_next -- + * Called by _GetBestChunk to get the next tuple in the lexicographic order + *--------------------------------------------------------------------- + */ +static int +get_next(int d[], int k, int C, int dmax[]) +{ + register int i,j, temp; + + if (!d[0]) { + temp = C; + for (j = k-1; j >= 0; j--){ + d[j] = min(temp, dmax[j]); + temp = max(1, temp/d[j]); + } + return(1); + } + + for (j = 0, temp = 1; j < k; j++) + temp *= d[j]; + + for (i=k-1; i >= 0; i--){ + temp = temp/d[i]; + if (((temp*(d[i]+1)) < C) && (d[i]+1 <= dmax[i])) + break; + } + if (i < 0) + return(0); + + d[i]++; + j = C/temp; + d[i] = min(dmax[i], j/(j/d[i])); + temp = temp*d[i]; + temp = C/temp; + + for (j = k-1; j > i; j--){ + d[j] = min(temp, dmax[j]); + temp = max(1, temp/d[j]); + } + return(1); +} + +static char a_chunk[_PAGE_SIZE_ + 4]; /* 4 since a_chunk is in + varlena format */ + +static void +initialize_info(CHUNK_INFO *A, int ndim, int dim[], int chunk[]) +{ + int i; + + for ( i = 0; i < ndim; i++) + A->C[i] = chunk[i]; +} + +/*-------------------------------------------------------------------------- + * Procedure reorganize_data(): + * This procedure reads the input multidimensional array that is organised + * in the order specified by array "X" and breaks it up into chunks of + * dimensions specified in "C". + * + * This is a very slow process, since reading and writing of LARGE files + * may be involved. + * + *------------------------------------------------------------------------- + */ +static void +_ConvertToChunkFile(int n, + int baseSize, + int dim[], + int C[], + int srcfd, + int destfd) +{ + int max_chunks[MAXDIM], chunk_no[MAXDIM]; + int PX[MAXDIM], dist[MAXDIM]; + int csize = 1, i, temp; + + for (i = 0; i < n; chunk_no[i++] = 0) { + max_chunks[i] = dim[i]/C[i]; + csize *= C[i]; + } + csize *= baseSize; + temp = csize + 4; + memmove(a_chunk, &temp, 4); + + mda_get_prod(n, dim, PX); + mda_get_offset_values(n, dist, PX, C); + for (i = 0; i < n; dist[i] *= baseSize, i++) + ; + do { + read_chunk(chunk_no, C, &(a_chunk[4]), srcfd, n, baseSize, PX, dist); + write_chunk((struct varlena*)a_chunk, destfd); + } while (next_tuple(n, chunk_no, max_chunks) != -1); +} + +/*-------------------------------------------------------------------------- + * read_chunk + * reads a chunk from the input files into a_chunk, the position of the + * chunk is specified by chunk_no + *-------------------------------------------------------------------------- + */ +static void +read_chunk(int chunk_no[], + int C[], + char a_chunk[], + int srcfd, + int n, + int baseSize, + int PX[], + int dist[]) +{ + int i, j, cp, unit_transfer; + int start_pos, pos[MAXDIM]; + int indx[MAXDIM]; + int fpOff; + + for ( i = start_pos = 0; i < n; i++) { + pos[i] = chunk_no[i] * C[i]; + start_pos += pos[i]*PX[i]; + } + start_pos *= baseSize; + + /* Read a block of dimesion C starting at co-ordinates pos */ + unit_transfer = C[n-1] * baseSize; + + for (i = 0; i < n; indx[i++] = 0) + ; + fpOff = start_pos; + seek_and_read(fpOff, unit_transfer, a_chunk, srcfd, SEEK_SET); + fpOff += unit_transfer; + cp = unit_transfer; + + while ((j = next_tuple(n-1, indx, C)) != -1) { + fpOff += dist[j]; + seek_and_read(fpOff, unit_transfer, &(a_chunk[cp]), srcfd, SEEK_SET); + cp += unit_transfer; + fpOff += unit_transfer; + } +} + +/*-------------------------------------------------------------------------- + * write_chunk() + * writes a chunk of size csize into the output file + *-------------------------------------------------------------------------- + */ +static int +write_chunk(struct varlena * a_chunk, int ofile) +{ + int got_n; +#ifdef LOARRAY + got_n = LOwrite (ofile, a_chunk); +#endif + return(got_n); +} + +/*-------------------------------------------------------------------------- + * seek_and_read() + * seeks to the asked location in the input file and reads the + * appropriate number of blocks + * Called By: read_chunk() + *-------------------------------------------------------------------------- + */ +static int +seek_and_read(int pos, int size, char buff[], int fp, int from) +{ + struct varlena *v; + + /* Assuming only one file */ + if ( lo_lseek(fp, pos, from ) < 0) + elog(WARN, "File seek error"); +#ifdef LOARRAY + v = (struct varlena *) LOread(fp, size); +#endif + if (VARSIZE(v) - 4 < size) + elog(WARN, "File read error"); + memmove(buff, VARDATA(v), size); + pfree(v); + return(1); + +} + +/*---------------------------------------------------------------------------- + * _ReadChunkArray -- + * returns the subarray specified bu the range indices "st" and "endp" + * from the chunked array stored in file "fp" + *--------------------------------------------------------------------------- + */ +int +_ReadChunkArray(int st[], + int endp[], + int bsize, + int fp, + char *destfp, + ArrayType *array, + int isDestLO, + bool *isNull) +{ + int i,j,jj; + int n, temp, words_read; + int chunk_span[MAXDIM], chunk_off[MAXDIM]; + int chunk_st[MAXDIM], chunk_end[MAXDIM]; + int block_seek; + + int bptr, *C, csize, *dim, *lb; + int range_st[MAXDIM], range_end[MAXDIM], + range[MAXDIM], array_span[MAXDIM]; + int PA[MAXDIM], PCHUNK[MAXDIM], PC[MAXDIM]; + int to_read; + int cdist[MAXDIM], adist[MAXDIM]; + int dist[MAXDIM], temp_seek; + + int srcOff; /* Needed since LO don't understand SEEK_CUR*/ + char *baseDestFp = (char *)destfp; + + CHUNK_INFO *A = (CHUNK_INFO *) ARR_DATA_PTR(array); + n = ARR_NDIM(array); + dim = ARR_DIMS(array); + lb = ARR_LBOUND(array); + C = A->C; + + csize = C[n-1]; + PC[n-1] = 1; + temp = dim[n - 1]/C[n-1]; + for (i = n-2; i >= 0; i--){ + PC[i] = PC[i+1] * temp; + temp = dim[i] / C[i]; + csize *= C[i]; + } + + for (i = 0; i < n; st[i] -= lb[i], endp[i] -= lb[i], i++) + ; + mda_get_prod(n, C, PCHUNK); + mda_get_range(n, array_span, st, endp); + mda_get_prod(n, array_span, PA); + + array2chunk_coord(n, C, st, chunk_st); + array2chunk_coord(n, C, endp, chunk_end); + mda_get_range(n, chunk_span, chunk_st, chunk_end); + mda_get_offset_values(n, dist, PC, chunk_span); + + for (i = 0; i < n; i++) { + range_st[i] = st[i]; + range_end[i] = min(chunk_st[i]*C[i]+C[i]-1, endp[i]); + } + + for (i = j = 0; i < n; i++) + j+= chunk_st[i]*PC[i]; + temp_seek = srcOff = j * csize * bsize; + if (lo_lseek(fp, srcOff, SEEK_SET) < 0) RETURN_NULL; + + jj = n-1; + for (i = 0; i < n; chunk_off[i++] = 0) + ; + words_read = 0; temp_seek = 0; + do { + /* Write chunk (chunk_st) to output buffer */ + mda_get_range(n, array_span, range_st, range_end); + mda_get_offset_values(n, adist, PA, array_span); + mda_get_offset_values(n, cdist, PCHUNK, array_span); + for (i=0; i < n; range[i] = range_st[i]-st[i], i++); + bptr = tuple2linear(n, range, PA); + for (i = 0; i < n; range[i++] = 0); + j = n-1; bptr *= bsize; + if (isDestLO) { + if (lo_lseek(destfp, bptr, SEEK_SET) < 0) + RETURN_NULL; + } + else + destfp = baseDestFp + bptr; + for(i = 0, block_seek = 0; i < n; i++) + block_seek += (range_st[i]-(chunk_st[i] + chunk_off[i]) + *C[i])*PCHUNK[i]; + if (dist[jj] + block_seek + temp_seek) { + temp = (dist[jj]*csize+block_seek+temp_seek)*bsize; + srcOff += temp; + if (lo_lseek(fp, srcOff, SEEK_SET) < 0) + RETURN_NULL; + } + for (i = n-1, to_read = bsize; i >= 0; + to_read *= min(C[i], array_span[i]), i--) + if (cdist[i] || adist[i]) + break; + do { + if (cdist[j]) { + srcOff += (cdist[j]*bsize); + if (lo_lseek(fp, srcOff, SEEK_SET) < 0) + RETURN_NULL; + } + block_seek += cdist[j]; + bptr += adist[j]*bsize; + if (isDestLO) { + if (lo_lseek(destfp, bptr, SEEK_SET) < 0) + RETURN_NULL; + } + else + destfp = baseDestFp + bptr; + temp = _LOtransfer ((char**)&destfp, to_read, 1, (char**)&fp, 1, isDestLO); + if (temp < to_read) + RETURN_NULL; + srcOff += to_read; + words_read+=to_read; + bptr += to_read; + block_seek += (to_read/bsize); + /* + * compute next tuple in range[] + */ + { + int x; + if (!(i+1)) + j = -1; + else { + range[i] = (range[i]+1)%array_span[i]; + for (x = i; x*(!range[x]); x--) + range[x-1] = (range[x-1]+1)%array_span[x-1]; + if (x) + j = x; + else { + if (range[0]) + j = 0; + else + j = -1; + } + } + } + /* + * end of compute next tuple -- + * j is set to -1 if tuple generation is over + */ + } while (j != -1); + + block_seek = csize - block_seek; + temp_seek = block_seek; + jj = next_tuple(n, chunk_off, chunk_span); + if (jj == -1) + break; + range_st[jj] = (chunk_st[jj]+chunk_off[jj])*C[jj]; + range_end[jj] = min(range_st[jj] + C[jj]-1, endp[jj]); + + for (i = jj+1; i < n; i++) { + range_st[i] = st[i]; + range_end[i] = min((chunk_st[i]+chunk_off[i])*C[i]+C[i]-1, endp[i]); + } + } while (jj != -1); + return(words_read); +} + +/*------------------------------------------------------------------------ + * _ReadChunkArray1El -- + * returns one element of the chunked array as specified by the index "st" + * the chunked file descriptor is "fp" + *------------------------------------------------------------------------- + */ +struct varlena * +_ReadChunkArray1El(int st[], + int bsize, + int fp, + ArrayType *array, + bool *isNull) +{ + int i, j, n, temp, srcOff; + int chunk_st[MAXDIM]; + + int *C, csize, *dim, *lb; + int PCHUNK[MAXDIM], PC[MAXDIM]; + + CHUNK_INFO *A = (CHUNK_INFO *) ARR_DATA_PTR(array); + + n = ARR_NDIM(array); + lb = ARR_LBOUND(array); + C = A->C; + dim = ARR_DIMS(array); + + csize = C[n-1]; + PC[n-1] = 1; + temp = dim[n - 1]/C[n-1]; + for (i = n-2; i >= 0; i--){ + PC[i] = PC[i+1] * temp; + temp = dim[i] / C[i]; + csize *= C[i]; + } + + for (i = 0; i < n; st[i] -= lb[i], i++); + mda_get_prod(n, C, PCHUNK); + + array2chunk_coord(n, C, st, chunk_st); + + for (i = j = 0; i < n; i++) + j+= chunk_st[i]*PC[i]; + srcOff = j * csize; + + for(i = 0; i < n; i++) + srcOff += (st[i]-chunk_st[i]*C[i])*PCHUNK[i]; + + srcOff *= bsize; + if (lo_lseek(fp, srcOff, SEEK_SET) < 0) + RETURN_NULL; +#ifdef LOARRAY + return (struct varlena *) LOread(fp, bsize); +#endif + return (struct varlena *) 0; +} + diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c new file mode 100644 index 00000000000..d9eaf1227e8 --- /dev/null +++ b/src/backend/utils/adt/date.c @@ -0,0 +1,891 @@ +/*------------------------------------------------------------------------- + * + * date.c-- + * Functions for the built-in type "AbsoluteTime". + * Functions for the built-in type "RelativeTime". + * Functions for the built-in type "TimeInterval". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + * NOTES + * This code is actually (almost) unused. + * It needs to be integrated with Time and struct trange. + * + * XXX This code needs to be rewritten to work with the "new" definitions + * XXX in h/tim.h. Look for int32's, int, long, etc. in the code. The + * XXX definitions in h/tim.h may need to be rethought also. + * + * XXX This code has been cleaned up some - avi 07/07/93 + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <stdio.h> +#include <time.h> +#include <sys/time.h> +#include <sys/types.h> +#include <string.h> + +#include "postgres.h" +#include "miscadmin.h" +#include "access/xact.h" +#include "utils/builtins.h" /* where function declarations go */ +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/nabstime.h" + +#define TM_YEAR_BASE 1900 /* compatible to UNIX time */ +#define EPOCH_YEAR 1970 /* compatible to UNIX time */ +#define YEAR_MAX 2038 /* otherwise overflow */ +#define YEAR_MIN 1902 /* otherwise overflow */ +#define DAYS_PER_LYEAR 366 +#define DAYS_PER_NYEAR 365 +#define HOURS_PER_DAY 24 +#define MINS_PER_HOUR 60 +#define SECS_PER_MIN 60 +#define MAX_LONG 2147483647 /* 2^31 */ + +/* absolute time definitions */ +#define TIME_NOW_STR "now" /* represents time now */ +#define TIME_EPOCH_STR "epoch" /* Jan 1 00:00:00 1970 GMT */ +#define TIME_EPOCH_STR_LEN (sizeof(TIME_EPOCH_STR)-1) + +#define INVALID_ABSTIME_STR "Undefined AbsTime" +#define INVALID_ABSTIME_STR_LEN (sizeof(INVALID_ABSTIME_STR)-1) + +#define INVALID_RELTIME_STR "Undefined RelTime" +#define INVALID_RELTIME_STR_LEN (sizeof(INVALID_RELTIME_STR)-1) +#define RELTIME_LABEL '@' +#define RELTIME_PAST "ago" +#define DIRMAXLEN (sizeof(RELTIME_PAST)-1) + +/* + * Unix epoch is Jan 1 00:00:00 1970. Postgres knows about times + * sixty-eight years on either side of that. + */ + +#define IsCharDigit(C) isdigit(C) +#define IsCharA_Z(C) isalpha(C) +#define IsSpace(C) ((C) == ' ') +#define IsNull(C) ((C) == NULL) + +#define T_INTERVAL_INVAL 0 /* data represents no valid interval */ +#define T_INTERVAL_VALID 1 /* data represents a valid interval */ +/* + * ['Mon May 10 23:59:12 1943 PST' 'Sun Jan 14 03:14:21 1973 PST'] + * 0 1 2 3 4 5 6 + * 1234567890123456789012345678901234567890123456789012345678901234 + * + * we allocate some extra -- timezones are usually 3 characters but + * this is not in the POSIX standard... + */ +#define T_INTERVAL_LEN 80 +#define INVALID_INTERVAL_STR "Undefined Range" +#define INVALID_INTERVAL_STR_LEN (sizeof(INVALID_INTERVAL_STR)-1) + +#define ABSTIMEMIN(t1, t2) abstimele((t1),(t2)) ? (t1) : (t2) +#define ABSTIMEMAX(t1, t2) abstimelt((t1),(t2)) ? (t2) : (t1) + +static char *month_name[] = { + "Jan","Feb","Mar","Apr","May","Jun","Jul", + "Aug","Sep","Oct","Nov","Dec" }; + +static char *unit_tab[] = { + "second", "seconds", "minute", "minutes", + "hour", "hours", "day", "days", "week", "weeks", + "month", "months", "year", "years"}; +#define UNITMAXLEN 7 /* max length of a unit name */ +#define NUNITS 14 /* number of different units */ + +/* table of seconds per unit (month = 30 days, year = 365 days) */ +static int sec_tab[] = { + 1,1, 60, 60, + 3600, 3600, 86400, 86400, 604800, 604800, + 2592000, 2592000, 31536000, 31536000 }; + +/* maximal values (in seconds) per unit which can be represented */ +static int unit_max_quantity[] = { + 2144448000, 2144448000, 35740800, 35740800, + 595680, 595680, 24820, 24820, 3545, 3545, + 827, 827, 68, 68 }; + + +struct timeb *TimeDifferenceFromGMT = NULL; +static bool TimeDiffIsInited = false; +static char *timezonename = NULL; + +/* + * Function prototypes -- internal to this file only + */ +static int correct_unit(char unit[], int *unptr); +static int correct_dir(char direction[], int *signptr); +static int istinterval(char *i_string, + AbsoluteTime *i_start, + AbsoluteTime *i_end); + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * reltimein - converts a reltime string in an internal format + */ +int32 /* RelativeTime */ +reltimein(char *timestring) +{ + int error; + int32 /* RelativeTime */ timeinsec; + int sign, unitnr; + long quantity; + + error = isreltime(timestring, &sign, &quantity, &unitnr); + +#ifdef DATEDEBUG + elog(DEBUG, "reltimein: isreltime(%s) returns error=%d, %d, %d, %d", + timestring, error, sign, quantity, unitnr); +#endif /* !DATEDEBUG */ + + if (error != 1) { + timeinsec = INVALID_RELTIME; /*invalid time representation */ + } else { + /* this check is necessary, while no control on overflow */ + if (quantity > unit_max_quantity[unitnr] || quantity < 0) { +#ifdef DATEDEBUG + elog(DEBUG, "reltimein: illegal quantity %d (< %d)", + quantity, unit_max_quantity[unitnr]); +#endif /* DATEDEBUG */ + timeinsec = INVALID_RELTIME; /* illegal quantity */ + } else { + timeinsec = sign * quantity * sec_tab[unitnr]; +#ifdef DATEDEBUG + elog(DEBUG, "reltimein: computed timeinsec %d", + timeinsec); +#endif /* DATEDEBUG */ + } + } + return(timeinsec); +} + + +/* + * reltimeout - converts the internal format to a reltime string + */ +char *reltimeout(int32 timevalue) +{ + char *timestring; + long quantity; + register int i; + int unitnr; + + timestring = (char *) palloc(Max(strlen(INVALID_RELTIME_STR), + UNITMAXLEN) + 1); + if (timevalue == INVALID_RELTIME) { + (void) strcpy(timestring,INVALID_RELTIME_STR); + return(timestring); + } + if (timevalue == 0) + i = 1; /* unit = 'seconds' */ + else + for (i = 12; i >= 0; i = i-2) + if ((timevalue % sec_tab[i]) == 0) + break; /* appropriate unit found */ + unitnr = i; + quantity = (timevalue / sec_tab[unitnr]); + if (quantity > 1 || quantity < -1) + unitnr++; /* adjust index for PLURAL of unit */ + if (quantity >= 0) + (void) sprintf( timestring, "%c %lu %s", RELTIME_LABEL, + quantity, unit_tab[unitnr]); + else + (void) sprintf( timestring, "%c %lu %s %s", RELTIME_LABEL, + (quantity * -1), unit_tab[unitnr], RELTIME_PAST); + return(timestring); +} + + +/* + * tintervalin - converts an interval string to an internal format + */ +TimeInterval tintervalin(char *intervalstr) +{ + int error; + AbsoluteTime i_start, i_end, t1, t2; + TimeInterval interval; + + interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); + error = istinterval(intervalstr, &t1, &t2); + if (error == 0) + interval->status = T_INTERVAL_INVAL; + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + interval->status = T_INTERVAL_INVAL; /* undefined */ + else { + i_start = ABSTIMEMIN(t1, t2); + i_end = ABSTIMEMAX(t1, t2); + interval->data[0] = i_start; + interval->data[1] = i_end; + interval->status = T_INTERVAL_VALID; + } + return(interval); +} + + +/* + * tintervalout - converts an internal interval format to a string + * + */ +char *tintervalout(TimeInterval interval) +{ + char *i_str, *p; + + i_str = (char *) palloc( T_INTERVAL_LEN ); /* ['...' '...'] */ + (void) strcpy(i_str,"['"); + if (interval->status == T_INTERVAL_INVAL) + (void) strcat(i_str,INVALID_INTERVAL_STR); + else { + p = nabstimeout(interval->data[0]); + (void) strcat(i_str,p); + pfree(p); + (void) strcat(i_str,"' '"); + p = nabstimeout(interval->data[1]); + (void) strcat(i_str,p); + pfree(p); + } + (void) strcat(i_str,"']\0"); + return(i_str); +} + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +/* + * mktinterval - creates a time interval with endpoints t1 and t2 + */ +TimeInterval mktinterval(AbsoluteTime t1, AbsoluteTime t2) +{ + AbsoluteTime tstart = ABSTIMEMIN(t1, t2), tend = ABSTIMEMAX(t1, t2); + TimeInterval interval; + + interval = (TimeInterval) palloc(sizeof(TimeIntervalData)); + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + interval->status = T_INTERVAL_INVAL; + else { + interval->status = T_INTERVAL_VALID; + interval->data[0] = tstart; + interval->data[1] = tend; + } + + return interval; +} + +/* + * timepl, timemi and abstimemi use the formula + * abstime + reltime = abstime + * so abstime - reltime = abstime + * and abstime - abstime = reltime + */ + +/* + * timepl - returns the value of (abstime t1 + relime t2) + */ +AbsoluteTime timepl(AbsoluteTime t1, RelativeTime t2) +{ + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsReal(t1) && + RelativeTimeIsValid(t2) && + ((t2 > 0) ? (t1 < NOEND_ABSTIME - t2) + : (t1 > NOSTART_ABSTIME - t2))) /* prevent overflow */ + return (t1 + t2); + + return(INVALID_ABSTIME); +} + + +/* + * timemi - returns the value of (abstime t1 - reltime t2) + */ +AbsoluteTime timemi(AbsoluteTime t1, RelativeTime t2) +{ + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsReal(t1) && + RelativeTimeIsValid(t2) && + ((t2 > 0) ? (t1 > NOSTART_ABSTIME + t2) + : (t1 < NOEND_ABSTIME + t2))) /* prevent overflow */ + return (t1 - t2); + + return(INVALID_ABSTIME); +} + + +/* + * abstimemi - returns the value of (abstime t1 - abstime t2) + */ +static RelativeTime abstimemi(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsReal(t1) && + AbsoluteTimeIsReal(t2)) + return (t1 - t2); + + return(INVALID_RELTIME); +} + + +/* + * ininterval - returns 1, iff absolute date is in the interval + */ +int ininterval(AbsoluteTime t, TimeInterval interval) +{ + if (interval->status == T_INTERVAL_VALID && t != INVALID_ABSTIME) + return (abstimege(t, interval->data[0]) && + abstimele(t, interval->data[1])); + return(0); +} + +/* + * intervalrel - returns relative time corresponding to interval + */ +RelativeTime intervalrel(TimeInterval interval) +{ + if (interval->status == T_INTERVAL_VALID) + return(abstimemi(interval->data[1], interval->data[0])); + else + return(INVALID_RELTIME); +} + +/* + * timenow - returns time "now", internal format + * + * Now AbsoluteTime is time since Jan 1 1970 -mer 7 Feb 1992 + */ +AbsoluteTime timenow() +{ + time_t sec; + if (time(&sec) < 0) + return(INVALID_ABSTIME); + return((AbsoluteTime) sec); +} + +/* + * reltimeeq - returns 1, iff arguments are equal + * reltimene - returns 1, iff arguments are not equal + * reltimelt - returns 1, iff t1 less than t2 + * reltimegt - returns 1, iff t1 greater than t2 + * reltimele - returns 1, iff t1 less than or equal to t2 + * reltimege - returns 1, iff t1 greater than or equal to t2 + */ +int32 reltimeeq(RelativeTime t1, RelativeTime t2) +{ + if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) + return 0; + return(t1 == t2); +} + +int32 reltimene(RelativeTime t1, RelativeTime t2) +{ + if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) + return 0; + return(t1 != t2); +} + +int32 reltimelt(RelativeTime t1, RelativeTime t2) +{ + if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) + return 0; + return(t1 < t2); +} + +int32 reltimegt(RelativeTime t1, RelativeTime t2) +{ + if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) + return 0; + return(t1 > t2); +} + +int32 reltimele(RelativeTime t1, RelativeTime t2) +{ + if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) + return 0; + return(t1 <= t2); +} + +int32 reltimege(RelativeTime t1, RelativeTime t2) +{ + if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME) + return 0; + return(t1 >= t2); +} + + +/* + * intervaleq - returns 1, iff interval i1 is equal to interval i2 + */ +int32 intervaleq(TimeInterval i1, TimeInterval i2) +{ + if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) + return(0); /* invalid interval */ + return(abstimeeq(i1->data[0], i2->data[0]) && + abstimeeq(i1->data[1], i2->data[1])); +} + +/* + * intervalleneq - returns 1, iff length of interval i is equal to + * reltime t + */ +int32 intervalleneq(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return(0); + rt = intervalrel(i); + return (rt != INVALID_RELTIME && rt == t); +} + +/* + * intervallenne - returns 1, iff length of interval i is not equal + * to reltime t + */ +int32 intervallenne(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return(0); + rt = intervalrel(i); + return (rt != INVALID_RELTIME && rt != t); +} + +/* + * intervallenlt - returns 1, iff length of interval i is less than + * reltime t + */ +int32 intervallenlt(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return(0); + rt = intervalrel(i); + return (rt != INVALID_RELTIME && rt < t); +} + +/* + * intervallengt - returns 1, iff length of interval i is greater than + * reltime t + */ +int32 intervallengt(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return(0); + rt = intervalrel(i); + return (rt != INVALID_RELTIME && rt > t); +} + +/* + * intervallenle - returns 1, iff length of interval i is less or equal + * than reltime t + */ +int32 intervallenle(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return(0); + rt = intervalrel(i); + return (rt != INVALID_RELTIME && rt <= t); +} + +/* + * intervallenge - returns 1, iff length of interval i is greater or + * equal than reltime t + */ +int32 intervallenge(TimeInterval i, RelativeTime t) +{ + RelativeTime rt; + + if ((i->status == T_INTERVAL_INVAL) || (t == INVALID_RELTIME)) + return(0); + rt = intervalrel(i); + return (rt != INVALID_RELTIME && rt >= t); +} + +/* + * intervalct - returns 1, iff interval i1 contains interval i2 + */ +int32 intervalct(TimeInterval i1, TimeInterval i2) +{ + if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) + return(0); + return(abstimele(i1->data[0], i2->data[0]) && + abstimege(i1->data[1], i2->data[1])); +} + +/* + * intervalov - returns 1, iff interval i1 (partially) overlaps i2 + */ +int32 intervalov(TimeInterval i1, TimeInterval i2) +{ + if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL) + return(0); + return(! (abstimelt(i1->data[1], i2->data[0]) || + abstimegt(i1->data[0], i2->data[1]))); +} + +/* + * intervalstart - returns the start of interval i + */ +AbsoluteTime intervalstart(TimeInterval i) +{ + if (i->status == T_INTERVAL_INVAL) + return INVALID_ABSTIME; + return(i->data[0]); +} + +/* + * intervalend - returns the end of interval i + */ +AbsoluteTime intervalend(TimeInterval i) +{ + if (i->status == T_INTERVAL_INVAL) + return INVALID_ABSTIME; + return(i->data[1]); +} + + +/***************************************************************************** + * PRIVATE ROUTINES * + *****************************************************************************/ + +/* + * isreltime - returns 1, iff datestring is of type reltime + * 2, iff datestring is 'invalid time' identifier + * 0, iff datestring contains a syntax error + * + * output parameter: + * sign = -1, iff direction is 'ago' + * else sign = 1. + * quantity : quantity of unit + * unitnr : 0 or 1 ... sec + * 2 or 3 ... min + * 4 or 5 ... hour + * 6 or 7 ... day + * 8 or 9 ... week + * 10 or 11... month + * 12 or 13... year + * + * + * Relative time: + * + * `@' ` ' Quantity ` ' Unit [ ` ' Direction] + * + * OR `Undefined RelTime' (see also INVALID_RELTIME_STR) + * + * where + * Quantity is `1', `2', ... + * Unit is `second', `minute', `hour', `day', `week', + * `month' (30-days), or `year' (365-days), + * or PLURAL of these units. + * Direction is `ago' + * + * VALID time less or equal `@ 68 years' + * + */ +int isreltime(char *timestring, int *sign, long *quantity, int *unitnr) +{ + register char *p; + register char c; + int i; + char unit[UNITMAXLEN] ; + char direction[DIRMAXLEN]; + int localSign; + int localUnitNumber; + long localQuantity; + + if (!PointerIsValid(sign)) { + sign = &localSign; + } + if (!PointerIsValid(unitnr)) { + unitnr = &localUnitNumber; + } + if (!PointerIsValid(quantity)) { + quantity = &localQuantity; + } + unit[0] = '\0'; + direction[0] = '\0'; + p = timestring; + /* skip leading blanks */ + while ((c = *p) != '\0') { + if (c != ' ') + break; + p++; + } + /* Test whether 'invalid time' identifier or not */ + if (!strncmp(INVALID_RELTIME_STR,p,strlen(INVALID_RELTIME_STR) + 1)) + return(2); /* correct 'invalid time' identifier found */ + + /* handle label of relative time */ + if (c != RELTIME_LABEL) + return(0); /*syntax error*/ + c = *++p; + if (c != ' ') return(0); /*syntax error*/ + p++; + /* handle the quantity */ + *quantity = 0; + for (;;) { + c = *p; + if (isdigit(c)) { + *quantity = *quantity * 10 + (c -'0'); + p++; + } else { + if (c == ' ' ) + break; /* correct quantity found */ + else + return(0); /* syntax error */ + } + } + /* handle unit */ + p++; + i = 0; + for (;;) { + c = *p; + if (c >= 'a' && c <= 'z' && i <= (UNITMAXLEN - 1)) { + unit[i] = c; + p++; + i++; + } else { + if ((c == ' ' || c == '\0') + && correct_unit(unit, unitnr)) + break; /* correct unit found */ + else + return(0); /* syntax error */ + } + } + /* handle optional direction */ + if (c == ' ') + p++; + i = 0; + *sign = 1; + for (;;) { + c = *p; + if (c >= 'a' && c <= 'z' && i <= (DIRMAXLEN - 1)) { + direction[i] = c; + p++; + i++; + } else { + if ((c == ' ' || c == '\0') && i == 0) { + *sign = 1; + break; /* no direction specified */ + } + if ((c == ' ' || c == '\0') && i != 0) + { + direction[i] = '\0'; + correct_dir(direction, sign); + break; /* correct direction found */ + } + else + return(0); /* syntax error*/ + } + } + return(1); +} + +/* + * correct_unit - returns 1, iff unit is a correct unit description + * + * output parameter: + * unptr: points to an integer which is the appropriate unit number + * (see function isreltime()) + */ +static int correct_unit(char unit[], int *unptr) +{ + int j = 0; + + while (j < NUNITS) { + if (strncmp(unit, unit_tab[j], strlen(unit_tab[j])) == 0) { + *unptr = j; + return(1); + } + j++; + } + return (0); /* invalid unit descriptor */ +} + +/* + * correct_dir - returns 1, iff direction is a correct identifier + * + * output parameter: + * signptr: points to -1 if dir corresponds to past tense + * else to 1 + */ +static int correct_dir(char direction[], int *signptr) +{ + *signptr = 1; + if (strncmp(RELTIME_PAST, direction, strlen(RELTIME_PAST)+1) == 0) + { + *signptr = -1; + return(1); + } else + return (0); /* invalid direction descriptor */ +} + + +/* + * istinterval - returns 1, iff i_string is a valid interval descr. + * 0, iff i_string is NOT a valid interval desc. + * 2, iff any time is INVALID_ABSTIME + * + * output parameter: + * i_start, i_end: interval margins + * + * Time interval: + * `[' {` '} `'' <AbsTime> `'' {` '} `'' <AbsTime> `'' {` '} `]' + * + * OR `Undefined Range' (see also INVALID_INTERVAL_STR) + * + * where <AbsTime> satisfies the syntax of absolute time. + * + * e.g. [ ' Jan 18 1902' 'Jan 1 00:00:00 1970'] + */ +static int istinterval(char *i_string, + AbsoluteTime *i_start, + AbsoluteTime *i_end) +{ + register char *p,*p1; + register char c; + + p = i_string; + /* skip leading blanks up to '[' */ + while ((c = *p) != '\0') { + if ( IsSpace(c)) + p++; + else if (c != '[') + return(0); /* syntax error */ + else + break; + } + p++; + /* skip leading blanks up to "'" */ + while ((c = *p) != '\0') { + if (IsSpace(c)) + p++; + else if (c != '"') + return (0); /* syntax error */ + else + break; + } + p++; + if (strncmp(INVALID_INTERVAL_STR,p,strlen(INVALID_INTERVAL_STR)) == 0) + return(0); /* undefined range, handled like a syntax err.*/ + /* search for the end of the first date and change it to a NULL*/ + p1 = p; + while ((c = *p1) != '\0') { + if ( c == '"') { + *p1 = '\0'; + break; + } + p1++; + } + /* get the first date */ + *i_start = nabstimein(p); /* first absolute date */ + /* rechange NULL at the end of the first date to a "'" */ + *p1 = '"'; + p = ++p1; + /* skip blanks up to "'", beginning of second date*/ + while ((c = *p) != '\0') { + if (IsSpace(c)) + p++; + else if (c != '"') + return (0); /* syntax error */ + else + break; + } + p++; + /* search for the end of the second date and change it to a NULL*/ + p1 = p; + while ((c = *p1) != '\0') { + if ( c == '"') { + *p1 = '\0'; + break; + } + p1++; + } + /* get the second date */ + *i_end = nabstimein(p); /* second absolute date */ + /* rechange NULL at the end of the first date to a ''' */ + *p1 = '"'; + p = ++p1; + /* skip blanks up to ']'*/ + while ((c = *p) != '\0') { + if ( IsSpace(c)) + p++; + else if (c != ']') + return(0); /*syntax error */ + else + break; + } + p++; + c = *p; + if ( c != '\0' ) + return (0); /* syntax error */ + /* it seems to be a valid interval */ + return(1); +} + + +/***************************************************************************** + * + *****************************************************************************/ + +/* + * timeofday - + * returns the current time as a text. similar to timenow() but returns + * seconds with more precision (up to microsecs). (I need this to compare + * the Wisconsin benchmark with Illustra whose TimeNow() shows current + * time with precision up to microsecs.) - ay 3/95 + */ +text * +timeofday() +{ + +#ifndef WIN32 + struct timeval tp; + struct timezone tpz; +#endif /* WIN32 */ + char templ[500]; + char buf[500]; + text *tm; + int len = 0; + +#ifndef WIN32 + gettimeofday(&tp, &tpz); + (void) strftime(templ, sizeof(templ), "%a %b %d %H:%M:%S.%%d %Y %Z", + localtime((time_t *) &tp.tv_sec)); + sprintf(buf, templ, tp.tv_usec); + + len = VARHDRSZ + strlen(buf); + tm = (text *)palloc(len); + VARSIZE(tm) = len; + strncpy(VARDATA(tm), buf, strlen(buf)); + return tm; +#else + len = len / len; + return tm; +#endif /* WIN32 */ + +} diff --git a/src/backend/utils/adt/datetimes.c b/src/backend/utils/adt/datetimes.c new file mode 100644 index 00000000000..b6207be263c --- /dev/null +++ b/src/backend/utils/adt/datetimes.c @@ -0,0 +1,350 @@ +/*------------------------------------------------------------------------- + * + * datetimes.c-- + * implements DATE and TIME data types specified in SQL-92 standard + * + * Copyright (c) 1994-5, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/datetimes.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/elog.h" + +/* these things look like structs, but we pass them by value so be careful + For example, passing an int -> DateADT is not portable! */ +typedef struct DateADT { + char day; + char month; + short year; +} DateADT; + +typedef struct TimeADT { + short hr; + short min; + float sec; +} TimeADT; + +#ifndef EUROPEAN_STYLE +#define AMERICAN_STYLE +#endif + +static int day_tab[2][12] = { + {31,28,31,30,31,30,31,31,30,31,30,31}, + {31,29,31,30,31,30,31,31,30,31,30,31} }; + +static int +isleap(int year) +{ + return + (((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0); +} + +/***************************************************************************** + * Date ADT + *****************************************************************************/ + +int4 +date_in(char *datestr) +{ + int d, m, y; + int4 result; + DateADT *date = (DateADT*)&result; + +#ifdef USE_SHORT_YEAR +#define CHECK_DATE_LEN(datestr) (strlen(datestr) >= 8) +#else +#define CHECK_DATE_LEN(datestr) (strlen(datestr) == 10) +#endif /* USE_SHORT_YEAR */ + +#ifdef AMERICAN_STYLE + if (!CHECK_DATE_LEN(datestr) || + sscanf(datestr, "%d%*c%d%*c%d", &m, &d, &y) != 3) { + elog(WARN, "date_in: date \"%s\" not of the form mm-dd-yyyy", + datestr); + } +#else + if (!CHECK_DATE_LEN(datestr) || + sscanf(datestr, "%d%*c%d%*c%d", &d, &m, &y) != 3) { + elog(WARN, "date_in: date \"%s\" not of the form dd-mm-yyyy", + datestr); + } +#endif + if (m < 1 || m > 12) + elog(WARN, "date_in: month must be limited to values 1 through 12 in \"%s\"", datestr); + if (d < 1 || d > day_tab[isleap(y)][m-1]) + elog(WARN, "date_in: day must be limited to values 1 through %d in \"%s\"", + day_tab[isleap(y)][m-1], datestr); + +#ifdef USE_SHORT_YEAR + if (y < 100) + y += 1900; /* hack! */ +#endif /* USE_SHORT_YEAR */ + + date->day = d; + date->month = m; + date->year = y; + return result; +} + +char * +date_out(int4 dateVal) +{ + char *datestr = palloc(11); + int4 dateStore; + DateADT *date; + + /* DateADT is a structure that happens to be four bytes long, + trust me on this.... */ + date = (DateADT*)&dateStore; + dateStore = dateVal; + +#ifdef AMERICAN_STYLE + sprintf(datestr, "%02d-%02d-%04d", + (int)date->month, (int)date->day, (int)date->year); +#else + sprintf(datestr, "%02d-%02d-%04d", + (int)date->day, (int)date->month, (int)date->year); +#endif + + return datestr; +} + + +int +date_eq(int4 dateVal1, int4 dateVal2) +{ + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + return (date1->day==date2->day && + date1->month==date2->month && + date1->year==date2->year); +} + +int +date_ne(int4 dateVal1, int4 dateVal2) +{ + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + return (date1->day!=date2->day || date1->month!=date2->month || + date1->year!=date2->year); +} + +int +date_lt(int4 dateVal1, int4 dateVal2) +{ + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + if (date1->year!=date2->year) + return (date1->year<date2->year); + if (date1->month!=date2->month) + return (date1->month<date2->month); + return (date1->day<date2->day); +} + +int +date_le(int4 dateVal1, int4 dateVal2) +{ + + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + if (date1->year!=date2->year) + return (date1->year<=date2->year); + if (date1->month!=date2->month) + return (date1->month<=date2->month); + return (date1->day<=date2->day); +} + +int +date_gt(int4 dateVal1, int4 dateVal2) +{ + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + + if (date1->year!=date2->year) + return (date1->year>date2->year); + if (date1->month!=date2->month) + return (date1->month>date2->month); + return (date1->day>date2->day); +} + +int +date_ge(int4 dateVal1, int4 dateVal2) +{ + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + if (date1->year!=date2->year) + return (date1->year>=date2->year); + if (date1->month!=date2->month) + return (date1->month>=date2->month); + return (date1->day>=date2->day); +} + +int +date_cmp(int4 dateVal1, int4 dateVal2) +{ + int4 dateStore1 = dateVal1; + int4 dateStore2 = dateVal2; + DateADT *date1, *date2; + + date1 = (DateADT*)&dateStore1; + date2 = (DateADT*)&dateStore2; + + if (date1->year!=date2->year) + return ((date1->year<date2->year) ? -1 : 1); + if (date1->month!=date2->month) + return ((date1->month<date2->month) ? -1 : 1); + if (date1->day!=date2->day) + return ((date1->day<date2->day) ? -1 : 1); + return 0; +} + +/***************************************************************************** + * Time ADT + *****************************************************************************/ + +char * +time_in(char *timestr) +{ + int h, m; + float sec; + TimeADT *time; + + if (sscanf(timestr, "%d%*c%d%*c%f", &h, &m, &sec) != 3) { + elog(WARN, "time_in: time \"%s\" not of the form hh:mm:ss", + timestr); + } + + if (h < 0 || h > 23) + elog(WARN, "time_in: hour must be limited to values 0 through 23 in \"%s\"", timestr); + if (m < 0 || m > 59) + elog(WARN, "time_in: minute must be limited to values 0 through 59 in \"%s\"", timestr); + if (sec < 0 || sec >= 62.0) + elog(WARN, "time_in: second must be limited to values 0 through 61.99 in \"%s\"", timestr); + + time = (TimeADT*)palloc(sizeof(TimeADT)); + time->hr = h; + time->min = m; + time->sec = sec; + return (char*)time; +} + +char * +time_out(TimeADT *time) +{ + char *timestr = palloc(16); + + sprintf(timestr, "%02d:%02d:%09.6f", + (int)time->hr, (int)time->min, time->sec); + + return timestr; +} + + +int +time_eq(TimeADT *time1, TimeADT *time2) +{ + return (time1->sec==time2->sec && time1->min==time2->min && + time1->hr==time2->hr); +} + +int +time_ne(TimeADT *time1, TimeADT *time2) +{ + return (time1->sec!=time2->sec || time1->min!=time2->min || + time1->hr!=time2->hr); +} + +int +time_lt(TimeADT *time1, TimeADT *time2) +{ + if (time1->hr!=time2->hr) + return (time1->hr<time2->hr); + if (time1->min!=time2->min) + return (time1->min<time2->min); + return (time1->sec<time2->sec); +} + +int +time_le(TimeADT *time1, TimeADT *time2) +{ + if (time1->hr!=time2->hr) + return (time1->hr<=time2->hr); + if (time1->min!=time2->min) + return (time1->min<=time2->min); + return (time1->sec<=time2->sec); +} + +int +time_gt(TimeADT *time1, TimeADT *time2) +{ + if (time1->hr!=time2->hr) + return (time1->hr>time2->hr); + if (time1->min!=time2->min) + return (time1->min>time2->min); + return (time1->sec>time2->sec); +} + +int +time_ge(TimeADT *time1, TimeADT *time2) +{ + if (time1->hr!=time2->hr) + return (time1->hr>=time2->hr); + if (time1->min!=time2->min) + return (time1->min>=time2->min); + return (time1->sec>=time2->sec); +} + +int +time_cmp(TimeADT *time1, TimeADT *time2) +{ + if (time1->hr!=time2->hr) + return ((time1->hr<time2->hr) ? -1 : 1); + if (time1->min!=time2->min) + return ((time1->min<time2->min) ? -1 : 1); + if (time1->sec!=time2->sec) + return ((time1->sec<time2->sec) ? -1 : 1); + return 0; +} + +int32 /* RelativeTime */ +int42reltime(int32 timevalue) +{ + return(timevalue); +} diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c new file mode 100644 index 00000000000..e982fc7607f --- /dev/null +++ b/src/backend/utils/adt/datum.c @@ -0,0 +1,201 @@ +/*------------------------------------------------------------------------- + * + * datum.c-- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/datum.c,v 1.1.1.1 1996/07/09 06:22:03 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * In the implementation of the next routines we assume the following: + * + * A) if a type is "byVal" then all the information is stored in the + * Datum itself (i.e. no pointers involved!). In this case the + * length of the type is always greater than zero and less than + * "sizeof(Datum)" + * B) if a type is not "byVal" and it has a fixed length, then + * the "Datum" always contain a pointer to a stream of bytes. + * The number of significant bytes are always equal to the length of the + * type. + * C) if a type is not "byVal" and is of variable length (i.e. it has + * length == -1) then "Datum" always points to a "struct varlena". + * This varlena structure has information about the actual length of this + * particular instance of the type and about its value. + * + */ +#include <string.h> +#include "postgres.h" +#include "utils/datum.h" +#include "catalog/pg_type.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +/*------------------------------------------------------------------------- + * datumGetSize + * + * Find the "real" size of a datum, given the datum value, + * its type, whether it is a "by value", and its length. + * + * To cut a long story short, usually the real size is equal to the + * type length, with the exception of variable length types which have + * a length equal to -1. In this case, we have to look at the value of + * the datum itself (which is a pointer to a 'varlena' struct) to find + * its size. + *------------------------------------------------------------------------- + */ +Size +datumGetSize(Datum value, Oid type, bool byVal, Size len) +{ + + struct varlena *s; + Size size; + + if (byVal) { + if (len >= 0 && len <= sizeof(Datum)) { + size = len; + } else { + elog(WARN, + "datumGetSize: Error: type=%ld, byVaL with len=%d", + (long) type, len); + } + } else { /* not byValue */ + if (len == -1) { + /* + * variable length type + * Look at the varlena struct for its real length... + */ + s = (struct varlena *) DatumGetPointer(value); + if (!PointerIsValid(s)) { + elog(WARN, + "datumGetSize: Invalid Datum Pointer"); + } + size = (Size) VARSIZE(s); + } else { + /* + * fixed length type + */ + size = len; + } + } + + return(size); +} + +/*------------------------------------------------------------------------- + * datumCopy + * + * make a copy of a datum + * + * If the type of the datum is not passed by value (i.e. "byVal=false") + * then we assume that the datum contains a pointer and we copy all the + * bytes pointed by this pointer + *------------------------------------------------------------------------- + */ +Datum +datumCopy(Datum value, Oid type, bool byVal, Size len) +{ + + Size realSize; + Datum res; + char *s; + + + if (byVal) { + res = value; + } else { + if (value == 0) return((Datum)NULL); + realSize = datumGetSize(value, type, byVal, len); + /* + * the value is a pointer. Allocate enough space + * and copy the pointed data. + */ + s = (char *) palloc(realSize); + if (s == NULL) { + elog(WARN,"datumCopy: out of memory\n"); + } + memmove(s, DatumGetPointer(value), realSize); + res = (Datum)s; + } + return(res); +} + +/*------------------------------------------------------------------------- + * datumFree + * + * Free the space occupied by a datum CREATED BY "datumCopy" + * + * NOTE: DO NOT USE THIS ROUTINE with datums returned by amgetattr() etc. + * ONLY datums created by "datumCopy" can be freed! + *------------------------------------------------------------------------- + */ +void +datumFree(Datum value, Oid type, bool byVal, Size len) +{ + + Size realSize; + Pointer s; + + realSize = datumGetSize(value, type, byVal, len); + + if (!byVal) { + /* + * free the space palloced by "datumCopy()" + */ + s = DatumGetPointer(value); + pfree(s); + } +} + +/*------------------------------------------------------------------------- + * datumIsEqual + * + * Return true if two datums are equal, false otherwise + * + * NOTE: XXX! + * We just compare the bytes of the two values, one by one. + * This routine will return false if there are 2 different + * representations of the same value (something along the lines + * of say the representation of zero in one's complement arithmetic). + * + *------------------------------------------------------------------------- + */ +bool +datumIsEqual(Datum value1, Datum value2, Oid type, bool byVal, Size len) +{ + Size size1, size2; + char *s1, *s2; + + if (byVal) { + /* + * just compare the two datums. + * NOTE: just comparing "len" bytes will not do the + * work, because we do not know how these bytes + * are aligned inside the "Datum". + */ + if (value1 == value2) + return(true); + else + return(false); + } else { + /* + * byVal = false + * Compare the bytes pointed by the pointers stored in the + * datums. + */ + size1 = datumGetSize(value1, type, byVal, len); + size2 = datumGetSize(value2, type, byVal, len); + if (size1 != size2) + return(false); + s1 = (char *) DatumGetPointer(value1); + s2 = (char *) DatumGetPointer(value2); + if (!memcmp(s1, s2, size1)) + return(true); + else + return(false); + } +} + diff --git a/src/backend/utils/adt/dt.c b/src/backend/utils/adt/dt.c new file mode 100644 index 00000000000..bc162427d28 --- /dev/null +++ b/src/backend/utils/adt/dt.c @@ -0,0 +1,58 @@ +/*------------------------------------------------------------------------- + * + * dt.c-- + * Functions for the built-in type "dt". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/dt.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "c.h" +#include "utils/palloc.h" +#include "utils/builtins.h" /* where function declarations go */ + + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * dtin - converts "nseconds" to internal representation + * + * XXX Currently, just creates an integer. + */ +int32 dtin(char *datetime) +{ + if (datetime == NULL) + return((int32) 0); + return((int32) atol(datetime)); +} + +/* + * dtout - converts internal form to "..." + * + * XXX assumes sign, 10 digits max, '\0' + */ +char *dtout(int32 datetime) +{ + char *result; + + result = (char *) palloc(12); + Assert(result); + ltoa(datetime, result); + return(result); +} + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ +/* (see int.c for comparison/operation routines) */ + +/***************************************************************************** + * PRIVATE ROUTINES * + *****************************************************************************/ +/* (none) */ diff --git a/src/backend/utils/adt/filename.c b/src/backend/utils/adt/filename.c new file mode 100644 index 00000000000..21389597eb6 --- /dev/null +++ b/src/backend/utils/adt/filename.c @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------- + * + * filename.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/filename.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include <stdio.h> +#ifndef WIN32 +#include <pwd.h> +#endif /* WIN32 */ + +#include <sys/param.h> +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/builtins.h" /* where function declarations go */ + +char * +filename_in(char *file) +{ + char *str, *getenv(); + int ind; + + /* + * XXX - HACK CITY --- REDO + * should let the shell do expansions (shexpand) + */ + +#ifndef WIN32 + str = (char *) palloc(MAXPATHLEN * sizeof(*str)); + str[0] = '\0'; + if (file[0] == '~') { + if (file[1] == '\0' || file[1] == '/') { + /* Home directory */ + + char *userName; + struct passwd *pw; + + userName = GetPgUserName(); + + if ((pw = getpwnam(userName)) == NULL) { + elog(WARN, "User %s is not a Unix user on the db server.", + userName); + } + + strcpy(str, pw->pw_dir); + + ind = 1; + } else { + /* Someone else's directory */ + char name[16], *p; + struct passwd *pw; + int len; + + if ((p = (char *) strchr(file, '/')) == NULL) { + strcpy(name, file+1); + len = strlen(name); + } else { + len = (p - file) - 1; + strncpy(name, file+1, len); + name[len] = '\0'; + } + /*printf("name: %s\n");*/ + if ((pw = getpwnam(name)) == NULL) { + elog(WARN, "No such user: %s\n", name); + ind = 0; + } else { + strcpy(str, pw->pw_dir); + ind = len + 1; + } + } + } else if (file[0] == '$') { /* $POSTGRESHOME, etc. expand it. */ + char environment[80], *envirp, *p; + int len; + + if ((p = (char *) strchr(file, '/')) == NULL) { + strcpy(environment, file+1); + len = strlen(environment); + } else { + len = (p - file) - 1; + strncpy(environment, file+1, len); + environment[len] = '\0'; + } + envirp = getenv(environment); + if (envirp) { + strcpy(str, envirp); + ind = len + 1; + } + else { + elog(WARN,"Couldn't find %s in your environment", environment); + } + } else { + ind = 0; + } + strcat(str, file+ind); + return(str); +#else + return(NULL); +#endif /* WIN32 */ +} + +char * +filename_out(char *s) +{ + char *ret; + + if (!s) + return((char *) NULL); + ret = (char *) palloc(strlen(s) + 1); + if (!ret) + elog(WARN, "filename_out: palloc failed"); + return(strcpy(ret, s)); +} diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c new file mode 100644 index 00000000000..ef962e71368 --- /dev/null +++ b/src/backend/utils/adt/float.c @@ -0,0 +1,1320 @@ +/*------------------------------------------------------------------------- + * + * float.c-- + * Functions for the built-in floating-point types. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/float.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * OLD COMMENTS + * Basic float4 ops: + * float4in, float4out, float4abs, float4um + * Basic float8 ops: + * float8in, float8inAd, float8out, float8outAd, float8abs, float8um + * Arithmetic operators: + * float4pl, float4mi, float4mul, float4div + * float8pl, float8mi, float8mul, float8div + * Comparison operators: + * float4eq, float4ne, float4lt, float4le, float4gt, float4ge + * float8eq, float8ne, float8lt, float8le, float8gt, float8ge + * Conversion routines: + * ftod, dtof + * + * Random float8 ops: + * dround, dtrunc, dsqrt, dcbrt, dpow, dexp, dlog1 + * Arithmetic operators: + * float48pl, float48mi, float48mul, float48div + * float84pl, float84mi, float84mul, float84div + * Comparison operators: + * float48eq, float48ne, float48lt, float48le, float48gt, float48ge + * float84eq, float84ne, float84lt, float84le, float84gt, float84ge + * + * (You can do the arithmetic and comparison stuff using conversion + * routines, but then you pay the overhead of converting...) + * + * XXX GLUESOME STUFF. FIX IT! -AY '94 + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> + +#include <float.h> /* faked on sunos4 */ + +#include <math.h> + +#include "postgres.h" +#include "fmgr.h" +#include "utils/builtins.h" /* for ftod() prototype */ +#include "utils/elog.h" +#include "utils/palloc.h" + + +#define FORMAT 'g' /* use "g" output format as standard format */ +/* not sure what the following should be, but better to make it over-sufficient */ +#define MAXFLOATWIDTH 64 +#define MAXDOUBLEWIDTH 128 + +#if !(NeXT && NX_CURRENT_COMPILER_RELEASE > NX_COMPILER_RELEASE_3_2) + /* NS3.3 has conflicting declarations of these in <math.h> */ + +#ifndef atof +extern double atof(const char *p); +#endif + +#ifdef NEED_CBRT +#define cbrt my_cbrt +static double cbrt(double x); +#else /* NEED_CBRT */ +extern double cbrt(double x); +#endif /* NEED_CBRT */ + +#ifdef NEED_RINT +#define rint my_rint +static double rint(double x); +#else /* NEED_RINT */ +extern double rint(double x); +#endif /* NEED_RINT */ + +#ifdef NEED_ISINF +#define isinf my_isinf +static int isinf(double x); +#else /* NEED_ISINF */ +extern int isinf(double x); +#endif /* NEED_ISINF */ + +#endif +/* ========== USER I/O ROUTINES ========== */ + + +#define FLOAT4_MAX FLT_MAX +#define FLOAT4_MIN FLT_MIN +#define FLOAT8_MAX DBL_MAX +#define FLOAT8_MIN DBL_MIN + +/* + check to see if a float4 val is outside of + the FLOAT4_MIN, FLOAT4_MAX bounds. + + raise an elog warning if it is +*/ +static void CheckFloat4Val(double val) +{ + /* defining unsafe floats's will make float4 and float8 ops faster + at the cost of safety, of course! */ +#ifdef UNSAFE_FLOATS + return; +#else + if (fabs(val) > FLOAT4_MAX) + elog(WARN,"\tBad float4 input format -- overflow\n"); + if (val != 0.0 && fabs(val) < FLOAT4_MIN) + elog(WARN,"\tBad float4 input format -- underflow\n"); + return; +#endif /* UNSAFE_FLOATS */ +} + +/* + check to see if a float8 val is outside of + the FLOAT8_MIN, FLOAT8_MAX bounds. + + raise an elog warning if it is +*/ +static void CheckFloat8Val(double val) +{ + /* defining unsafe floats's will make float4 and float8 ops faster + at the cost of safety, of course! */ +#ifdef UNSAFE_FLOATS + return; +#else + if (fabs(val) > FLOAT8_MAX) + elog(WARN,"\tBad float8 input format -- overflow\n"); + if (val != 0.0 && fabs(val) < FLOAT8_MIN) + elog(WARN,"\tBad float8 input format -- underflow\n"); + return; +#endif /* UNSAFE_FLOATS */ +} + +/* + * float4in - converts "num" to float + * restricted syntax: + * {<sp>} [+|-] {digit} [.{digit}] [<exp>] + * where <sp> is a space, digit is 0-9, + * <exp> is "e" or "E" followed by an integer. + */ +float32 float4in(char *num) +{ + float32 result = (float32) palloc(sizeof(float32data)); + double val; + char* endptr; + + errno = 0; + val = strtod(num,&endptr); + if (*endptr != '\0' || errno == ERANGE) + elog(WARN,"\tBad float4 input format\n"); + + /* if we get here, we have a legal double, still need to check to see + if it's a legal float */ + + CheckFloat4Val(val); + + *result = val; + return result; +} + +/* + * float4out - converts a float4 number to a string + * using a standard output format + */ +char *float4out(float32 num) +{ + char *ascii = (char *)palloc(MAXFLOATWIDTH+1); + + if (!num) + return strcpy(ascii, "(null)"); + + sprintf(ascii, "%.*g", FLT_DIG, *num); + return(ascii); +} + + +/* + * float8in - converts "num" to float8 + * restricted syntax: + * {<sp>} [+|-] {digit} [.{digit}] [<exp>] + * where <sp> is a space, digit is 0-9, + * <exp> is "e" or "E" followed by an integer. + */ +float64 float8in(char *num) +{ + float64 result = (float64) palloc(sizeof(float64data)); + double val; + char* endptr; + + errno = 0; + val = strtod(num,&endptr); + if (*endptr != '\0' || errno == ERANGE) + elog(WARN,"\tBad float8 input format\n"); + + CheckFloat8Val(val); + *result = val; + return(result); +} + + +/* + * float8out - converts float8 number to a string + * using a standard output format + */ +char *float8out(float64 num) +{ + char *ascii = (char *)palloc(MAXDOUBLEWIDTH+1); + + if (!num) + return strcpy(ascii, "(null)"); + +#ifndef WIN32 + if (isnan(*num)) + return strcpy(ascii, "NaN"); + if (isinf(*num)) + return strcpy(ascii, "Infinity"); +#else + if (_isnan(*num)) + return strcpy(ascii, "NaN"); + if (!_finite(*num)) + return strcpy(ascii, "Infinity"); +#endif + + sprintf(ascii, "%.*g", DBL_DIG, *num); + return(ascii); +} + +/* ========== PUBLIC ROUTINES ========== */ + + +/* + * ====================== + * FLOAT4 BASE OPERATIONS + * ====================== + */ + +/* + * float4abs - returns a pointer to |arg1| (absolute value) + */ +float32 float4abs(float32 arg1) +{ + float32 result; + double val; + + if (!arg1) + return (float32)NULL; + + val = fabs(*arg1); + + CheckFloat4Val(val); + + result = (float32) palloc(sizeof(float32data)); + *result = val; + return(result); +} + +/* + * float4um - returns a pointer to -arg1 (unary minus) + */ +float32 float4um(float32 arg1) +{ + float32 result; + double val; + + if (!arg1) + return (float32)NULL; + + val = -(*arg1); + CheckFloat4Val(val); + + result = (float32) palloc(sizeof(float32data)); + *result = val; + return(result); +} + +float32 float4larger(float32 arg1, float32 arg2) +{ + float32 result; + + if (!arg1 || !arg2) + return (float32)NULL; + + result = (float32) palloc(sizeof(float32data)); + + *result = ((*arg1 > *arg2) ? *arg1 : *arg2); + return result; +} + +float32 float4smaller(float32 arg1, float32 arg2) +{ + float32 result; + + if (!arg1 || !arg2) + return (float32)NULL; + + result = (float32) palloc(sizeof(float32data)); + + *result = ((*arg1 > *arg2) ? *arg2 : *arg1); + return result; +} + +/* + * ====================== + * FLOAT8 BASE OPERATIONS + * ====================== + */ + +/* + * float8abs - returns a pointer to |arg1| (absolute value) + */ +float64 float8abs(float64 arg1) +{ + float64 result; + double val; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + val = fabs(*arg1); + CheckFloat8Val(val); + *result = val; + return(result); +} + + +/* + * float8um - returns a pointer to -arg1 (unary minus) + */ +float64 float8um(float64 arg1) +{ + float64 result; + double val; + + if (!arg1) + return (float64)NULL; + + val = -(*arg1); + + CheckFloat8Val(val); + result = (float64) palloc(sizeof(float64data)); + *result = val; + return(result); +} + +float64 float8larger(float64 arg1, float64 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = ((*arg1 > *arg2) ? *arg1 : *arg2); + return result; +} + +float64 float8smaller(float64 arg1, float64 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = ((*arg1 > *arg2) ? *arg2 : *arg1); + return result; +} + + +/* + * ==================== + * ARITHMETIC OPERATORS + * ==================== + */ + +/* + * float4pl - returns a pointer to arg1 + arg2 + * float4mi - returns a pointer to arg1 - arg2 + * float4mul - returns a pointer to arg1 * arg2 + * float4div - returns a pointer to arg1 / arg2 + * float4inc - returns a poniter to arg1 + 1.0 + */ +float32 float4pl(float32 arg1, float32 arg2) +{ + float32 result; + double val; + + if (!arg1 || !arg2) + return (float32)NULL; + + val = *arg1 + *arg2; + CheckFloat4Val(val); + + result = (float32) palloc(sizeof(float32data)); + *result = val; + + return(result); +} + +float32 float4mi(float32 arg1, float32 arg2) +{ + float32 result; + double val; + + if (!arg1 || !arg2) + return (float32)NULL; + + val = *arg1 - *arg2; + + CheckFloat4Val(val); + result = (float32) palloc(sizeof(float32data)); + *result = val; + return(result); +} + +float32 float4mul(float32 arg1, float32 arg2) +{ + float32 result; + double val; + + if (!arg1 || !arg2) + return (float32)NULL; + + val = *arg1 * *arg2; + + CheckFloat4Val(val); + result = (float32) palloc(sizeof(float32data)); + *result = val; + return(result); +} + +float32 float4div(float32 arg1, float32 arg2) +{ + float32 result; + double val; + + if (!arg1 || !arg2) + return (float32)NULL; + + if (*arg2 == 0.0) + elog(WARN,"float4div: divide by 0.0 error"); + + val = *arg1 / *arg2; + + CheckFloat4Val(val); + result = (float32) palloc(sizeof(float32data)); + *result = *arg1 / *arg2; + return(result); +} + +float32 float4inc(float32 arg1) +{ + double val; + + if (!arg1) + return (float32)NULL; + + val = *arg1 + (float32data)1.0; + CheckFloat4Val(val); + *arg1 = val; + return arg1; +} + +/* + * float8pl - returns a pointer to arg1 + arg2 + * float8mi - returns a pointer to arg1 - arg2 + * float8mul - returns a pointer to arg1 * arg2 + * float8div - returns a pointer to arg1 / arg2 + * float8inc - returns a pointer to arg1 + 1.0 + */ +float64 float8pl(float64 arg1, float64 arg2) +{ + float64 result; + double val; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + val = *arg1 + *arg2; + CheckFloat8Val(val); + *result = val; + return(result); +} + +float64 float8mi(float64 arg1, float64 arg2) +{ + float64 result; + double val; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + val = *arg1 - *arg2; + CheckFloat8Val(val); + *result = val; + return(result); +} + +float64 float8mul(float64 arg1, float64 arg2) +{ + float64 result; + double val; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + val = *arg1 * *arg2; + CheckFloat8Val(val); + *result = val; + return(result); +} + +float64 float8div(float64 arg1, float64 arg2) +{ + float64 result; + double val; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + if (*arg2 == 0.0) + elog(WARN,"float8div: divide by 0.0 error"); + + val = *arg1 / *arg2; + CheckFloat8Val(val); + *result = val; + return(result); +} + +float64 float8inc(float64 arg1) +{ + double val; + if (!arg1) + return (float64)NULL; + + val = *arg1 + (float64data)1.0; + CheckFloat8Val(val); + *arg1 = val; + return(arg1); +} + + +/* + * ==================== + * COMPARISON OPERATORS + * ==================== + */ + +/* + * float4{eq,ne,lt,le,gt,ge} - float4/float4 comparison operations + */ +long float4eq(float32 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 == *arg2); +} + +long float4ne(float32 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 != *arg2); +} + +long float4lt(float32 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 < *arg2); +} + +long float4le(float32 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 <= *arg2); +} + +long float4gt(float32 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 > *arg2); +} + +long float4ge(float32 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 >= *arg2); +} + +/* + * float8{eq,ne,lt,le,gt,ge} - float8/float8 comparison operations + */ +long float8eq(float64 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 == *arg2); +} + +long float8ne(float64 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 != *arg2); +} + +long float8lt(float64 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 < *arg2); +} + +long float8le(float64 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 <= *arg2); +} + +long float8gt(float64 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 > *arg2); +} + +long float8ge(float64 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 >= *arg2); +} + + +/* + * =================== + * CONVERSION ROUTINES + * =================== + */ + +/* + * ftod - converts a float4 number to a float8 number + */ +float64 ftod(float32 num) +{ + float64 result; + + if (!num) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *num; + return(result); +} + + +/* + * dtof - converts a float8 number to a float4 number + */ +float32 dtof(float64 num) +{ + float32 result; + + if (!num) + return (float32)NULL; + + result = (float32) palloc(sizeof(float32data)); + + *result = *num; + return(result); +} + + +/* + * ======================= + * RANDOM FLOAT8 OPERATORS + * ======================= + */ + +/* + * dround - returns a pointer to ROUND(arg1) + */ +float64 dround(float64 arg1) +{ + float64 result; + double tmp; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp = *arg1; + *result = (float64data) rint(tmp); + return(result); +} + + +/* + * dtrunc - returns a pointer to truncation of arg1, + * arg1 >= 0 ... the greatest integer as float8 less + * than or equal to arg1 + * arg1 < 0 ... the greatest integer as float8 greater + * than or equal to arg1 + */ +float64 dtrunc(float64 arg1) +{ + float64 result; + double tmp; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp = *arg1; + if (*arg1 >= 0) + *result = (float64data) floor(tmp); + else + *result = (float64data) -(floor(-tmp)); + return(result); +} + + +/* + * dsqrt - returns a pointer to square root of arg1 + */ +float64 dsqrt(float64 arg1) +{ + float64 result; + double tmp; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp = *arg1; + *result = (float64data) sqrt(tmp); + return (result); +} + + +/* + * dcbrt - returns a pointer to cube root of arg1 + */ +float64 dcbrt(float64 arg1) +{ + float64 result; + double tmp; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp = *arg1; + *result = (float64data) cbrt(tmp); + return(result); +} + + +/* + * dpow - returns a pointer to pow(arg1,arg2) + */ +float64 dpow(float64 arg1, float64 arg2) +{ + float64 result; + double tmp1, tmp2; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp1 = *arg1; + tmp2 = *arg2; + errno = 0; + *result = (float64data) pow(tmp1, tmp2); + if (errno == ERANGE) + elog(WARN, "pow() returned a floating point out of the range\n"); + + CheckFloat8Val(*result); + return(result); +} + + +/* + * dexp - returns a pointer to the exponential function of arg1 + */ +float64 dexp(float64 arg1) +{ + float64 result; + double tmp; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp = *arg1; + errno = 0; + *result = (float64data) exp(tmp); + if (errno == ERANGE) + elog(WARN, "exp() returned a floating point out of range\n"); + + CheckFloat8Val(*result); + return(result); +} + + +/* + * dlog1 - returns a pointer to the natural logarithm of arg1 + * ("dlog" is already a logging routine...) + */ +float64 dlog1(float64 arg1) +{ + float64 result; + double tmp; + + if (!arg1) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + tmp = *arg1; + if (tmp == 0.0) + elog(WARN, "can't take log of 0!"); + if (tmp < 0) + elog(WARN, "can't take log of a negative number"); + *result = (float64data) log(tmp); + + CheckFloat8Val(*result); + return(result); +} + + +/* + * ==================== + * ARITHMETIC OPERATORS + * ==================== + */ + +/* + * float48pl - returns a pointer to arg1 + arg2 + * float48mi - returns a pointer to arg1 - arg2 + * float48mul - returns a pointer to arg1 * arg2 + * float48div - returns a pointer to arg1 / arg2 + */ +float64 float48pl(float32 arg1, float64 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *arg1 + *arg2; + CheckFloat8Val(*result); + return(result); +} + +float64 float48mi(float32 arg1, float64 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *arg1 - *arg2; + CheckFloat8Val(*result); + return(result); +} + +float64 float48mul(float32 arg1, float64 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *arg1 * *arg2; + CheckFloat8Val(*result); + return(result); +} + +float64 float48div(float32 arg1, float64 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + if (*arg2 == 0.0) + elog(WARN, "float48div: divide by 0.0 error!"); + + *result = *arg1 / *arg2; + CheckFloat8Val(*result); + return(result); +} + +/* + * float84pl - returns a pointer to arg1 + arg2 + * float84mi - returns a pointer to arg1 - arg2 + * float84mul - returns a pointer to arg1 * arg2 + * float84div - returns a pointer to arg1 / arg2 + */ +float64 float84pl(float64 arg1, float32 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *arg1 + *arg2; + CheckFloat8Val(*result); + return(result); +} + +float64 float84mi(float64 arg1, float32 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *arg1 - *arg2; + CheckFloat8Val(*result); + return(result); +} + +float64 float84mul(float64 arg1, float32 arg2) +{ + + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + *result = *arg1 * *arg2; + CheckFloat8Val(*result); + return(result); +} + +float64 float84div(float64 arg1, float32 arg2) +{ + float64 result; + + if (!arg1 || !arg2) + return (float64)NULL; + + result = (float64) palloc(sizeof(float64data)); + + if (*arg2 == 0.0) + elog(WARN, "float48div: divide by 0.0 error!"); + + *result = *arg1 / *arg2; + CheckFloat8Val(*result); + return(result); +} + +/* + * ==================== + * COMPARISON OPERATORS + * ==================== + */ + +/* + * float48{eq,ne,lt,le,gt,ge} - float4/float8 comparison operations + */ +long float48eq(float32 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 == (float)*arg2); +} + +long float48ne(float32 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 != (float)*arg2); +} + +long float48lt(float32 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 < (float)*arg2); +} + +long float48le(float32 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 <= (float)*arg2); +} + +long float48gt(float32 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 > (float)*arg2); +} + +long float48ge(float32 arg1, float64 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return(*arg1 >= (float)*arg2); +} + +/* + * float84{eq,ne,lt,le,gt,ge} - float4/float8 comparison operations + */ +long float84eq(float64 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return((float)*arg1 == *arg2); +} + +long float84ne(float64 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return((float)*arg1 != *arg2); +} + +long float84lt(float64 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return((float)*arg1 < *arg2); +} + +long float84le(float64 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return((float)*arg1 <= *arg2); +} + +long float84gt(float64 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return((float)*arg1 > *arg2); +} + +long float84ge(float64 arg1, float32 arg2) +{ + if (!arg1 || !arg2) + return (long)NULL; + + return((float)*arg1 >= *arg2); +} + +/* ========== PRIVATE ROUTINES ========== */ + +/* From "fdlibm" @ netlib.att.com */ + +#ifdef NEED_RINT + +/* @(#)s_rint.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * rint(x) + * Return x rounded to integral value according to the prevailing + * rounding mode. + * Method: + * Using floating addition. + * Exception: + * Inexact flag raised if x not equal to rint(x). + */ + +#ifdef __STDC__ +static const double +#else + static double +#endif + one = 1.0, + TWO52[2]={ + 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ + -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ + }; + +#ifdef __STDC__ +static double rint(double x) +#else + static double rint(x) + double x; +#endif +{ + int i0,n0,j0,sx; + unsigned i,i1; + double w,t; + n0 = (*((int *)&one)>>29)^1; + i0 = *(n0+(int*)&x); + sx = (i0>>31)&1; + i1 = *(1-n0+(int*)&x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { + if(((i0&0x7fffffff)|i1)==0) return x; + i1 |= (i0&0x0fffff); + i0 &= 0xfffe0000; + i0 |= ((i1|-i1)>>12)&0x80000; + *(n0+(int*)&x)=i0; + w = TWO52[sx]+x; + t = w-TWO52[sx]; + i0 = *(n0+(int*)&t); + *(n0+(int*)&t) = (i0&0x7fffffff)|(sx<<31); + return t; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + i>>=1; + if(((i0&i)|i1)!=0) { + if(j0==19) i1 = 0x40000000; else + i0 = (i0&(~i))|((0x20000)>>j0); + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((unsigned)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + i>>=1; + if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); + } + *(n0+(int*)&x) = i0; + *(1-n0+(int*)&x) = i1; + w = TWO52[sx]+x; + return w-TWO52[sx]; +} + +#endif /* NEED_RINT */ + +#ifdef NEED_CBRT + +static + double + cbrt(x) +double x; +{ + int isneg = (x < 0.0); + double tmpres = pow(fabs(x), (double) 1.0 / (double) 3.0); + + return(isneg ? -tmpres : tmpres); +} + +#endif /* NEED_CBRT */ + +#ifdef NEED_ISINF + +#if defined(PORTNAME_aix) +#ifdef CLASS_CONFLICT +/* we want the math symbol */ +#undef class +#endif /* CLASS_CONFICT */ + +static int isinf(x) + double x; +{ + int fpclass = class(x); + if (fpclass == FP_PLUS_INF) + return(1); + if (fpclass == FP_MINUS_INF) + return(-1); + return(0); +} +#endif /* PORTNAME_aix */ + +#if defined(PORTNAME_ultrix4) +#include <fp_class.h> +static int isinf(x) + double x; +{ + int fpclass = fp_class_d(x); + if (fpclass == FP_POS_INF) + return(1); + if (fpclass == FP_NEG_INF) + return(-1); + return(0); +} +#endif /* PORTNAME_ultrix4 */ + +#if defined(PORTNAME_alpha) +#include <fp_class.h> +static int isinf(x) + double x; +{ + int fpclass = fp_class(x); + if (fpclass == FP_POS_INF) + return(1); + if (fpclass == FP_NEG_INF) + return(-1); + return(0); +} +#endif /* PORTNAME_alpha */ + +#if defined(PORTNAME_sparc_solaris) +#include <ieeefp.h> +static int + isinf(d) +double d; +{ + fpclass_t type = fpclass(d); + switch (type) { + case FP_SNAN: + case FP_QNAN: + case FP_NINF: + case FP_PINF: + return (1); + default: + break; + } + + return (0); +} +#endif /* PORTNAME_sparc_solaris */ + +#if defined(PORTNAME_irix5) +#include <ieeefp.h> +static int + isinf(d) +double d; +{ + fpclass_t type = fpclass(d); + switch (type) { + case FP_SNAN: + case FP_QNAN: + case FP_NINF: + case FP_PINF: + return (1); + default: + break; + } + + return (0); +} +#endif /* PORTNAME_irix5 */ + +#endif /* NEED_ISINF */ diff --git a/src/backend/utils/adt/geo-ops.c b/src/backend/utils/adt/geo-ops.c new file mode 100644 index 00000000000..8262591a050 --- /dev/null +++ b/src/backend/utils/adt/geo-ops.c @@ -0,0 +1,1947 @@ +/*------------------------------------------------------------------------- + * + * geo-ops.c-- + * 2D geometric operations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/geo-ops.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <math.h> +#include <float.h> /* faked on sunos */ +#include <stdio.h> /* for sprintf proto, etc. */ +#include <string.h> + +#include "utils/geo-decls.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#define LDELIM '(' +#define RDELIM ')' +#define DELIM ',' +#define BOXNARGS 4 +#define LSEGNARGS 4 +#define POINTNARGS 2 + +/*********************************************************************** + ** + ** Routines for two-dimensional boxes. + ** + ***********************************************************************/ + +/*---------------------------------------------------------- + * Formatting and conversion routines. + *---------------------------------------------------------*/ + +/* box_in - convert a string to internal form. + * + * str: input string "(f8, f8, f8, f8)" + */ +BOX *box_in(char *str) +{ + double tmp; + char *p, *coord[BOXNARGS]; + int i; + BOX *result; + + if (str == NULL) + elog (WARN," Bad (null) box external representation"); + + if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL) + elog (WARN, "Bad box external representation '%s'",str); + for (i = 0, p = str; *p && i < BOXNARGS && *p != RDELIM; p++) + if (*p == DELIM || (*p == LDELIM && !i)) + coord[i++] = p + 1; + if (i < BOXNARGS - 1) + elog (WARN, "Bad box external representation '%s'", str); + result = PALLOCTYPE(BOX); + result->xh = atof(coord[0]); + result->yh = atof(coord[1]); + result->xl = atof(coord[2]); + result->yl = atof(coord[3]); + if (result->xh < result->xl) { + tmp = result->xh; + result->xh = result->xl; + result->xl = tmp; + } + if (result->yh < result->yl) { + tmp = result->yh; + result->yh = result->yl; + result->yl = tmp; + } + + return(result); +} + +/* box_out - convert a box to external form. + */ +char *box_out(BOX *box) +{ + char *result; + + if (box == NULL) + return(NULL); + result = (char *)PALLOC(80); + (void) sprintf(result, "(%G,%G,%G,%G)", + box->xh, box->yh, box->xl, box->yl); + + return(result); +} + + +/* box_construct - fill in a new box. + */ +BOX *box_construct(double x1, double x2, double y1, double y2) +{ + BOX *result; + + result = PALLOCTYPE(BOX); + return( box_fill(result, x1, x2, y1, y2) ); +} + + +/* box_fill - fill in a static box + */ +BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2) +{ + double tmp; + + result->xh = x1; + result->xl = x2; + result->yh = y1; + result->yl = y2; + if (result->xh < result->xl) { + tmp = result->xh; + result->xh = result->xl; + result->xl = tmp; + } + if (result->yh < result->yl) { + tmp = result->yh; + result->yh = result->yl; + result->yl = tmp; + } + + return(result); +} + + +/* box_copy - copy a box + */ +BOX *box_copy(BOX *box) +{ + BOX *result; + + result = PALLOCTYPE(BOX); + memmove((char *) result, (char *) box, sizeof(BOX)); + + return(result); +} + + +/*---------------------------------------------------------- + * Relational operators for BOXes. + * <, >, <=, >=, and == are based on box area. + *---------------------------------------------------------*/ + +/* box_same - are two boxes identical? + */ +long box_same(BOX *box1, BOX *box2) +{ + return((box1->xh == box2->xh && box1->xl == box2->xl) && + (box1->yh == box2->yh && box1->yl == box2->yl)); +} + +/* box_overlap - does box1 overlap box2? + */ +long box_overlap(BOX *box1, BOX *box2) +{ + return(((box1->xh >= box2->xh && box1->xl <= box2->xh) || + (box2->xh >= box1->xh && box2->xl <= box1->xh)) && + ((box1->yh >= box2->yh && box1->yl <= box2->yh) || + (box2->yh >= box1->yh && box2->yl <= box1->yh)) ); +} + +/* box_overleft - is the right edge of box1 to the left of + * the right edge of box2? + * + * This is "less than or equal" for the end of a time range, + * when time ranges are stored as rectangles. + */ +long box_overleft(BOX *box1, BOX *box2) +{ + return(box1->xh <= box2->xh); +} + +/* box_left - is box1 strictly left of box2? + */ +long box_left(BOX *box1, BOX *box2) +{ + return(box1->xh < box2->xl); +} + +/* box_right - is box1 strictly right of box2? + */ +long box_right(BOX *box1, BOX *box2) +{ + return(box1->xl > box2->xh); +} + +/* box_overright - is the left edge of box1 to the right of + * the left edge of box2? + * + * This is "greater than or equal" for time ranges, when time ranges + * are stored as rectangles. + */ +long box_overright(BOX *box1, BOX *box2) +{ + return(box1->xl >= box2->xl); +} + +/* box_contained - is box1 contained by box2? + */ +long box_contained(BOX *box1, BOX *box2) +{ + return((box1->xh <= box2->xh && box1->xl >= box2->xl && + box1->yh <= box2->yh && box1->yl >= box2->yl)); +} + +/* box_contain - does box1 contain box2? + */ +long box_contain(BOX *box1, BOX *box2) +{ + return((box1->xh >= box2->xh && box1->xl <= box2->xl && + box1->yh >= box2->yh && box1->yl <= box2->yl)); +} + + +/* box_positionop - + * is box1 entirely {above, below } box2? + */ +long box_below(BOX *box1, BOX *box2) +{ + return( box1->yh <= box2->yl ); +} + +long box_above(BOX *box1, BOX *box2) +{ + return( box1->yl >= box2->yh ); +} + + +/* box_relop - is area(box1) relop area(box2), within + * our accuracy constraint? + */ +long box_lt(BOX *box1, BOX *box2) +{ + return( FPlt(box_ar(box1), box_ar(box2)) ); +} + +long box_gt(BOX *box1, BOX *box2) +{ + return( FPgt(box_ar(box1), box_ar(box2)) ); +} + +long box_eq(BOX *box1, BOX *box2) +{ + return( FPeq(box_ar(box1), box_ar(box2)) ); +} + +long box_le(BOX *box1, BOX *box2) +{ + return( FPle(box_ar(box1), box_ar(box2)) ); +} + +long box_ge(BOX *box1, BOX *box2) +{ + return( FPge(box_ar(box1), box_ar(box2)) ); +} + + +/*---------------------------------------------------------- + * "Arithmetic" operators on boxes. + * box_foo returns foo as an object (pointer) that + can be passed between languages. + * box_xx is an internal routine which returns the + * actual value (and cannot be handed back to + * LISP). + *---------------------------------------------------------*/ + +/* box_area - returns the area of the box. + */ +double *box_area(BOX *box) +{ + double *result; + + result = PALLOCTYPE(double); + *result = box_ln(box) * box_ht(box); + + return(result); +} + + +/* box_length - returns the length of the box + * (horizontal magnitude). + */ +double *box_length(BOX *box) +{ + double *result; + + result = PALLOCTYPE(double); + *result = box->xh - box->xl; + + return(result); +} + + +/* box_height - returns the height of the box + * (vertical magnitude). + */ +double *box_height(BOX *box) +{ + double *result; + + result = PALLOCTYPE(double); + *result = box->yh - box->yl; + + return(result); +} + + +/* box_distance - returns the distance between the + * center points of two boxes. + */ +double *box_distance(BOX *box1, BOX *box2) +{ + double *result; + Point *box_center(), *a, *b; + + result = PALLOCTYPE(double); + a = box_center(box1); + b = box_center(box2); + *result = HYPOT(a->x - b->x, a->y - b->y); + + PFREE(a); + PFREE(b); + return(result); +} + + +/* box_center - returns the center point of the box. + */ +Point *box_center(BOX *box) +{ + Point *result; + + result = PALLOCTYPE(Point); + result->x = (box->xh + box->xl) / 2.0; + result->y = (box->yh + box->yl) / 2.0; + + return(result); +} + + +/* box_ar - returns the area of the box. + */ +double box_ar(BOX *box) +{ + return( box_ln(box) * box_ht(box) ); +} + + +/* box_ln - returns the length of the box + * (horizontal magnitude). + */ +double box_ln(BOX *box) +{ + return( box->xh - box->xl ); +} + + +/* box_ht - returns the height of the box + * (vertical magnitude). + */ +double box_ht(BOX *box) +{ + return( box->yh - box->yl ); +} + + +/* box_dt - returns the distance between the + * center points of two boxes. + */ +double box_dt(BOX *box1, BOX *box2) +{ + double result; + Point *box_center(), + *a, *b; + + a = box_center(box1); + b = box_center(box2); + result = HYPOT(a->x - b->x, a->y - b->y); + + PFREE(a); + PFREE(b); + return(result); +} + +/*---------------------------------------------------------- + * Funky operations. + *---------------------------------------------------------*/ + +/* box_intersect - + * returns the overlapping portion of two boxes, + * or NULL if they do not intersect. + */ +BOX *box_intersect(BOX *box1, BOX *box2) +{ + BOX *result; + long box_overlap(); + + if (! box_overlap(box1,box2)) + return(NULL); + result = PALLOCTYPE(BOX); + result->xh = Min(box1->xh, box2->xh); + result->xl = Max(box1->xl, box2->xl); + result->yh = Min(box1->yh, box2->yh); + result->yl = Max(box1->yl, box2->yl); + + return(result); +} + + +/* box_diagonal - + * returns a line segment which happens to be the + * positive-slope diagonal of "box". + * provided, of course, we have LSEGs. + */ +LSEG *box_diagonal(BOX *box) +{ + Point p1, p2; + + p1.x = box->xh; + p1.y = box->yh; + p2.x = box->xl; + p2.y = box->yl; + return( lseg_construct( &p1, &p2 ) ); + +} + +/*********************************************************************** + ** + ** Routines for 2D lines. + ** Lines are not intended to be used as ADTs per se, + ** but their ops are useful tools for other ADT ops. Thus, + ** there are few relops. + ** + ***********************************************************************/ + +/*---------------------------------------------------------- + * Conversion routines from one line formula to internal. + * Internal form: Ax+By+C=0 + *---------------------------------------------------------*/ + +LINE * /* point-slope */ +line_construct_pm(Point *pt, double m) +{ + LINE *result; + + result = PALLOCTYPE(LINE); + /* use "mx - y + yinter = 0" */ + result->A = m; + result->B = -1.0; + result->C = pt->y - m * pt->x; + return(result); +} + + +LINE * /* two points */ +line_construct_pp(Point *pt1, Point *pt2) +{ + LINE *result; + + result = PALLOCTYPE(LINE); + if (FPeq(pt1->x, pt2->x)) { /* vertical */ + /* use "x = C" */ + result->m = 0.0; + result->A = -1.0; + result->B = 0.0; + result->C = pt1->x; + } else { + /* use "mx - y + yinter = 0" */ + result->m = (pt1->y - pt2->y) / (pt1->x - pt2->x); + result->A = result->m; + result->B = -1.0; + result->C = pt1->y - result->m * pt1->x; + } + return(result); +} + + +/*---------------------------------------------------------- + * Relative position routines. + *---------------------------------------------------------*/ + +long line_intersect(LINE *l1, LINE *l2) +{ + return( ! line_parallel(l1, l2) ); +} + +long line_parallel(LINE *l1, LINE *l2) +{ + return( FPeq(l1->m, l2->m) ); +} + +long line_perp(LINE *l1, LINE *l2) +{ + if (l1->m) + return( FPeq(l2->m / l1->m, -1.0) ); + else if (l2->m) + return( FPeq(l1->m / l2->m, -1.0) ); + return(1); /* both 0.0 */ +} + +long line_vertical(LINE *line) +{ + return( FPeq(line->A, -1.0) && FPzero(line->B) ); +} + +long line_horizontal(LINE *line) +{ + return( FPzero(line->m) ); +} + + +long line_eq(LINE *l1, LINE *l2) +{ + double k; + + if (! FPzero(l2->A)) + k = l1->A / l2->A; + else if (! FPzero(l2->B)) + k = l1->B / l2->B; + else if (! FPzero(l2->C)) + k = l1->C / l2->C; + else + k = 1.0; + return( FPeq(l1->A, k * l2->A) && + FPeq(l1->B, k * l2->B) && + FPeq(l1->C, k * l2->C) ); +} + + +/*---------------------------------------------------------- + * Line arithmetic routines. + *---------------------------------------------------------*/ + +double * /* distance between l1, l2 */ +line_distance(LINE *l1, LINE *l2) +{ + double *result; + Point *tmp; + + result = PALLOCTYPE(double); + if (line_intersect(l1, l2)) { + *result = 0.0; + return(result); + } + if (line_vertical(l1)) + *result = fabs(l1->C - l2->C); + else { + tmp = point_construct(0.0, l1->C); + result = dist_pl(tmp, l2); + PFREE(tmp); + } + return(result); +} + +Point * /* point where l1, l2 intersect (if any) */ +line_interpt(LINE *l1, LINE *l2) +{ + Point *result; + double x; + + if (line_parallel(l1, l2)) + return(NULL); + if (line_vertical(l1)) + result = point_construct(l2->m * l1->C + l2->C, l1->C); + else if (line_vertical(l2)) + result = point_construct(l1->m * l2->C + l1->C, l2->C); + else { + x = (l1->C - l2->C) / (l2->A - l1->A); + result = point_construct(x, l1->m * x + l1->C); + } + return(result); +} + +/*********************************************************************** + ** + ** Routines for 2D paths (sequences of line segments, also + ** called `polylines'). + ** + ** This is not a general package for geometric paths, + ** which of course include polygons; the emphasis here + ** is on (for example) usefulness in wire layout. + ** + ***********************************************************************/ + +#define PATHALLOCSIZE(N) \ + (long) ((unsigned) (sizeof(PATH) + \ + (((N)-1) > 0 ? ((N)-1) : 0) \ + * sizeof(Point))) + +/*---------------------------------------------------------- + * String to path / path to string conversion. + * External format: + * "(closed, npts, xcoord, ycoord,... )" + *---------------------------------------------------------*/ + +PATH *path_in(char *str) +{ + double coord; + long field[2]; + char *s; + int ct, i; + PATH *result; + long pathsize; + + if (str == NULL) + elog(WARN, "Bad (null) path external representation"); + + /* read the path header information */ + for (i = 0, s = str; *s && i < 2 && *s != RDELIM; ++s) + if (*s == DELIM || (*s == LDELIM && !i)) + field[i++] = atol(s + 1); + if (i < 1) + elog(WARN, "Bad path external representation '%s'", str); + pathsize = PATHALLOCSIZE(field[1]); + result = (PATH *)palloc(pathsize); + result->length = pathsize; + result->closed = field[0]; + result->npts = field[1]; + + /* read the path points */ + + ct = result->npts * 2; /* two coords for every point */ + for (i = 0; + *s && i < ct && *s != RDELIM; + ++s) { + if (*s == ',') { + coord = atof(s + 1); + if (i % 2) + (result->p[i/2]).y = coord; + else + (result->p[i/2]).x = coord; + ++i; + } + } + if (i % 2 || i < --ct) { + PFREE(result); + elog(WARN, "Bad path external representation '%s'", str); + } + + return(result); +} + + +char *path_out(PATH *path) +{ + char buf[BUFSIZ + 20000], *result, *s; + int i; + char tmp[64]; + + if (path == NULL) + return(NULL); + (void) sprintf(buf,"%c%d,%d", LDELIM, + path->closed, path->npts); + s = buf + strlen(buf); + for (i = 0; i < path->npts; ++i) { + (void) sprintf(tmp, ",%G,%G", + path->p[i].x, path->p[i].y); + (void) strcpy(s, tmp); + s += strlen(tmp); + } + *s++ = RDELIM; + *s = '\0'; + result = (char *)PALLOC(strlen(buf) + 1); + (void) strcpy(result, buf); + + return(result); +} + + +/*---------------------------------------------------------- + * Relational operators. + * These are based on the path cardinality, + * as stupid as that sounds. + * + * Better relops and access methods coming soon. + *---------------------------------------------------------*/ + +long path_n_lt(PATH *p1, PATH *p2) +{ + return( (p1->npts < p2->npts ) ); +} + +long path_n_gt(PATH *p1, PATH *p2) +{ + return( (p1->npts > p2->npts ) ); +} + +long path_n_eq(PATH *p1, PATH *p2) +{ + return( (p1->npts == p2->npts) ); +} + +long path_n_le(PATH *p1, PATH *p2) +{ + return( (p1->npts <= p2->npts ) ); +} + +long path_n_ge(PATH *p1, PATH *p2) +{ + return( (p1->npts >= p2->npts ) ); +} + +/* path_inter - + * Does p1 intersect p2 at any point? + * Use bounding boxes for a quick (O(n)) check, then do a + * O(n^2) iterative edge check. + */ +long path_inter(PATH *p1, PATH *p2) +{ + BOX b1, b2; + int i, j; + LSEG seg1, seg2; + + b1.xh = b1.yh = b2.xh = b2.yh = DBL_MAX; + b1.xl = b1.yl = b2.xl = b2.yl = -DBL_MAX; + for (i = 0; i < p1->npts; ++i) { + b1.xh = Max(p1->p[i].x, b1.xh); + b1.yh = Max(p1->p[i].y, b1.yh); + b1.xl = Min(p1->p[i].x, b1.xl); + b1.yl = Min(p1->p[i].y, b1.yl); + } + for (i = 0; i < p2->npts; ++i) { + b2.xh = Max(p2->p[i].x, b2.xh); + b2.yh = Max(p2->p[i].y, b2.yh); + b2.xl = Min(p2->p[i].x, b2.xl); + b2.yl = Min(p2->p[i].y, b2.yl); + } + if (! box_overlap(&b1, &b2)) + return(0); + + /* pairwise check lseg intersections */ + for (i = 0; i < p1->npts - 1; i++) { + for (j = 0; j < p2->npts - 1; j++) { + statlseg_construct(&seg1, &p1->p[i], &p1->p[i+1]); + statlseg_construct(&seg2, &p2->p[j], &p2->p[j+1]); + if (lseg_intersect(&seg1, &seg2)) + return(1); + } + } + + /* if we dropped through, no two segs intersected */ + return(0); +} + +/* this essentially does a cartesian product of the lsegs in the + two paths, and finds the min distance between any two lsegs */ +double *path_distance(PATH *p1, PATH *p2) +{ + double *min, *tmp; + int i,j; + LSEG seg1, seg2; + + statlseg_construct(&seg1, &p1->p[0], &p1->p[1]); + statlseg_construct(&seg2, &p2->p[0], &p2->p[1]); + min = lseg_distance(&seg1, &seg2); + + for (i = 0; i < p1->npts - 1; i++) + for (j = 0; j < p2->npts - 1; j++) + { + statlseg_construct(&seg1, &p1->p[i], &p1->p[i+1]); + statlseg_construct(&seg2, &p2->p[j], &p2->p[j+1]); + + if (*min < *(tmp = lseg_distance(&seg1, &seg2))) + *min = *tmp; + PFREE(tmp); + } + + return(min); +} + + +/*---------------------------------------------------------- + * "Arithmetic" operations. + *---------------------------------------------------------*/ + +double *path_length(PATH *path) +{ + double *result; + int ct, i; + + result = PALLOCTYPE(double); + ct = path->npts - 1; + for (i = 0; i < ct; ++i) + *result += point_dt(&path->p[i], &path->p[i+1]); + + return(result); +} + + + +double path_ln(PATH *path) +{ + double result; + int ct, i; + + ct = path->npts - 1; + for (result = i = 0; i < ct; ++i) + result += point_dt(&path->p[i], &path->p[i+1]); + + return(result); +} +/*********************************************************************** + ** + ** Routines for 2D points. + ** + ***********************************************************************/ + +/*---------------------------------------------------------- + * String to point, point to string conversion. + * External form: "(x, y)" + *---------------------------------------------------------*/ + +Point *point_in(char *str) +{ + char *coord[POINTNARGS], *p, *r; + int i; + Point *result; + + if (str == NULL) + elog(WARN, "Bad (null) point external representation"); + + if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL) + elog (WARN, "Bad point external representation '%s'",str); + for (i = 0, p++; *p && i < POINTNARGS-1 && *p != RDELIM; p = r+1) + if ((r = (char *)strchr(p, DELIM)) == (char *)NULL) + elog (WARN, "Bad point external representation '%s'",str); + else + coord[i++] = p; + if ((r = (char *)strchr(p, RDELIM)) == (char *)NULL) + elog (WARN, "Bad point external representation '%s'",str); + coord[i++] = p; + + if (i < POINTNARGS - 1) + elog(WARN, "Bad point external representation '%s'",str); + result = PALLOCTYPE(Point); + result->x = atof(coord[0]); + result->y = atof(coord[1]); + return(result); +} + +char *point_out(Point *pt) +{ + char *result; + + if (pt == NULL) + return(NULL); + result = (char *)PALLOC(40); + (void) sprintf(result, "(%G,%G)", pt->x, pt->y); + return(result); +} + + +Point *point_construct(double x, double y) +{ + Point *result; + + result = PALLOCTYPE(Point); + result->x = x; + result->y = y; + return(result); +} + + +Point *point_copy(Point *pt) +{ + Point *result; + + result = PALLOCTYPE(Point); + result->x = pt->x; + result->y = pt->y; + return(result); +} + + +/*---------------------------------------------------------- + * Relational operators for Points. + * Since we do have a sense of coordinates being + * "equal" to a given accuracy (point_vert, point_horiz), + * the other ops must preserve that sense. This means + * that results may, strictly speaking, be a lie (unless + * EPSILON = 0.0). + *---------------------------------------------------------*/ + +long point_left(Point *pt1, Point *pt2) +{ + return( FPlt(pt1->x, pt2->x) ); +} + +long point_right(Point *pt1, Point *pt2) +{ + return( FPgt(pt1->x, pt2->x) ); +} + +long point_above(Point *pt1, Point *pt2) +{ + return( FPgt(pt1->y, pt2->y) ); +} + +long point_below(Point *pt1, Point *pt2) +{ + return( FPlt(pt1->y, pt2->y) ); +} + +long point_vert(Point *pt1, Point *pt2) +{ + return( FPeq( pt1->x, pt2->x ) ); +} + +long point_horiz(Point *pt1, Point *pt2) +{ + return( FPeq( pt1->y, pt2->y ) ); +} + +long point_eq(Point *pt1, Point *pt2) +{ + return( point_horiz(pt1, pt2) && point_vert(pt1, pt2) ); +} + +/*---------------------------------------------------------- + * "Arithmetic" operators on points. + *---------------------------------------------------------*/ + +long pointdist(Point *p1, Point *p2) +{ + long result; + + result = point_dt(p1, p2); + return(result); +} + +double *point_distance(Point *pt1, Point *pt2) +{ + double *result; + + result = PALLOCTYPE(double); + *result = HYPOT( pt1->x - pt2->x, pt1->y - pt2->y ); + return(result); +} + + +double point_dt(Point *pt1, Point *pt2) +{ + return( HYPOT( pt1->x - pt2->x, pt1->y - pt2->y ) ); +} + +double *point_slope(Point *pt1, Point *pt2) +{ + double *result; + + result = PALLOCTYPE(double); + if (point_vert(pt1, pt2)) + *result = DBL_MAX; + else + *result = (pt1->y - pt2->y) / (pt1->x - pt1->x); + return(result); +} + + +double point_sl(Point *pt1, Point *pt2) +{ + return( point_vert(pt1, pt2) + ? DBL_MAX + : (pt1->y - pt2->y) / (pt1->x - pt2->x) ); +} + +/*********************************************************************** + ** + ** Routines for 2D line segments. + ** + ***********************************************************************/ + +/*---------------------------------------------------------- + * String to lseg, lseg to string conversion. + * External form: "(id, info, x1, y1, x2, y2)" + *---------------------------------------------------------*/ + +LSEG *lseg_in(char *str) +{ + char *coord[LSEGNARGS], *p; + int i; + LSEG *result; + + if (str == NULL) + elog (WARN," Bad (null) box external representation"); + + if ((p = (char *)strchr(str, LDELIM)) == (char *)NULL) + elog (WARN, "Bad lseg external representation '%s'",str); + for (i = 0, p = str; *p && i < LSEGNARGS && *p != RDELIM; p++) + if (*p == DELIM || (*p == LDELIM && !i)) + coord[i++] = p + 1; + if (i < LSEGNARGS - 1) + elog (WARN, "Bad lseg external representation '%s'", str); + result = PALLOCTYPE(LSEG); + result->p[0].x = atof(coord[0]); + result->p[0].y = atof(coord[1]); + result->p[1].x = atof(coord[2]); + result->p[1].y = atof(coord[3]); + result->m = point_sl(&result->p[0], &result->p[1]); + + return(result); +} + + +char *lseg_out(LSEG *ls) +{ + char *result; + + if (ls == NULL) + return(NULL); + result = (char *)PALLOC(80); + (void) sprintf(result, "(%G,%G,%G,%G)", + ls->p[0].x, ls->p[0].y, ls->p[1].x, ls->p[1].y); + return(result); +} + + +/* lseg_construct - + * form a LSEG from two Points. + */ +LSEG *lseg_construct(Point *pt1, Point *pt2) +{ + LSEG *result; + + result = PALLOCTYPE(LSEG); + result->p[0].x = pt1->x; + result->p[0].y = pt1->y; + result->p[1].x = pt2->x; + result->p[1].y = pt2->y; + result->m = point_sl(pt1, pt2); + + return(result); +} + +/* like lseg_construct, but assume space already allocated */ +void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2) +{ + lseg->p[0].x = pt1->x; + lseg->p[0].y = pt1->y; + lseg->p[1].x = pt2->x; + lseg->p[1].y = pt2->y; + lseg->m = point_sl(pt1, pt2); +} + +/*---------------------------------------------------------- + * Relative position routines. + *---------------------------------------------------------*/ + +/* + ** find intersection of the two lines, and see if it falls on + ** both segments. + */ +long lseg_intersect(LSEG *l1, LSEG *l2) +{ + LINE *ln; + Point *interpt; + long retval; + + ln = line_construct_pp(&l2->p[0], &l2->p[1]); + interpt = interpt_sl(l1, ln); + + if (interpt != NULL && on_ps(interpt, l2)) /* interpt on l1 and l2 */ + retval = 1; + else retval = 0; + if (interpt != NULL) PFREE(interpt); + PFREE(ln); + return(retval); +} + +long lseg_parallel(LSEG *l1, LSEG *l2) +{ + return( FPeq(l1->m, l2->m) ); +} + +long lseg_perp(LSEG *l1, LSEG *l2) +{ + if (! FPzero(l1->m)) + return( FPeq(l2->m / l1->m, -1.0) ); + else if (! FPzero(l2->m)) + return( FPeq(l1->m / l2->m, -1.0) ); + return(0); /* both 0.0 */ +} + +long lseg_vertical(LSEG *lseg) +{ + return( FPeq(lseg->p[0].x, lseg->p[1].x) ); +} + +long lseg_horizontal(LSEG *lseg) +{ + return( FPeq(lseg->p[0].y, lseg->p[1].y) ); +} + + +long lseg_eq(LSEG *l1, LSEG *l2) +{ + return( FPeq(l1->p[0].x, l2->p[0].x) && + FPeq(l1->p[1].y, l2->p[1].y) && + FPeq(l1->p[0].x, l2->p[0].x) && + FPeq(l1->p[1].y, l2->p[1].y) ); +} + + +/*---------------------------------------------------------- + * Line arithmetic routines. + *---------------------------------------------------------*/ + +/* lseg_distance - + * If two segments don't intersect, then the closest + * point will be from one of the endpoints to the other + * segment. + */ +double *lseg_distance(LSEG *l1, LSEG *l2) +{ + double *d, *result; + + result = PALLOCTYPE(double); + if (lseg_intersect(l1, l2)) { + *result = 0.0; + return(result); + } + *result = DBL_MAX; + d = dist_ps(&l1->p[0], l2); + *result = Min(*result, *d); + PFREE(d); + d = dist_ps(&l1->p[1], l2); + *result = Min(*result, *d); + PFREE(d); + d = dist_ps(&l2->p[0], l1); + *result = Min(*result, *d); + PFREE(d); + d = dist_ps(&l2->p[1], l1); + *result = Min(*result, *d); + PFREE(d); + + return(result); +} + +/* distance between l1, l2 */ +double lseg_dt(LSEG *l1, LSEG *l2) +{ + double *d, result; + + if (lseg_intersect(l1, l2)) + return(0.0); + result = DBL_MAX; + d = dist_ps(&l1->p[0], l2); + result = Min(result, *d); + PFREE(d); + d = dist_ps(&l1->p[1], l2); + result = Min(result, *d); + PFREE(d); + d = dist_ps(&l2->p[0], l1); + result = Min(result, *d); + PFREE(d); + d = dist_ps(&l2->p[1], l1); + result = Min(result, *d); + PFREE(d); + + return(result); +} + + +/* lseg_interpt - + * Find the intersection point of two segments (if any). + * Find the intersection of the appropriate lines; if the + * point is not on a given segment, there is no valid segment + * intersection point at all. + */ +Point *lseg_interpt(LSEG *l1, LSEG *l2) +{ + Point *result; + LINE *tmp1, *tmp2; + + tmp1 = line_construct_pp(&l1->p[0], &l1->p[1]); + tmp2 = line_construct_pp(&l2->p[0], &l2->p[1]); + result = line_interpt(tmp1, tmp2); + if (result) + if (! on_ps(result, l1)) { + PFREE(result); + result = NULL; + } + + PFREE(tmp1); + PFREE(tmp2); + return(result); +} + +/*********************************************************************** + ** + ** Routines for position comparisons of differently-typed + ** 2D objects. + ** + ***********************************************************************/ + +#define ABOVE 1 +#define BELOW 0 +#define UNDEF -1 + + +/*--------------------------------------------------------------------- + * dist_ + * Minimum distance from one object to another. + *-------------------------------------------------------------------*/ + +double *dist_pl(Point *pt, LINE *line) +{ + double *result; + + result = PALLOCTYPE(double); + *result = (line->A * pt->x + line->B * pt->y + line->C) / + HYPOT(line->A, line->B); + + return(result); +} + +double *dist_ps(Point *pt, LSEG *lseg) +{ + double m; /* slope of perp. */ + LINE *ln; + double *result, *tmpdist; + Point *ip; + + /* construct a line that's perpendicular. See if the intersection of + the two lines is on the line segment. */ + if (lseg->p[1].x == lseg->p[0].x) + m = 0; + else if (lseg->p[1].y == lseg->p[0].y) /* slope is infinite */ + m = DBL_MAX; + else m = (-1) * (lseg->p[1].y - lseg->p[0].y) / + (lseg->p[1].x - lseg->p[0].x); + ln = line_construct_pm(pt, m); + + if ((ip = interpt_sl(lseg, ln)) != NULL) + result = point_distance(pt, ip); + else /* intersection is not on line segment, so distance is min + of distance from point to an endpoint */ + { + result = point_distance(pt, &lseg->p[0]); + tmpdist = point_distance(pt, &lseg->p[1]); + if (*tmpdist < *result) *result = *tmpdist; + PFREE (tmpdist); + } + + if (ip != NULL) PFREE(ip); + PFREE(ln); + return (result); +} + + +/* + ** Distance from a point to a path + */ +double *dist_ppth(Point *pt, PATH *path) +{ + double *result; + double *tmp; + int i; + LSEG lseg; + + switch (path->npts) { + case 0: + result = PALLOCTYPE(double); + *result = Abs((double) DBL_MAX); /* +infinity */ + break; + case 1: + result = point_distance(pt, &path->p[0]); + break; + default: + /* + * the distance from a point to a path is the smallest distance + * from the point to any of its constituent segments. + */ + Assert(path->npts > 1); + result = PALLOCTYPE(double); + for (i = 0; i < path->npts - 1; ++i) { + statlseg_construct(&lseg, &path->p[i], &path->p[i+1]); + tmp = dist_ps(pt, &lseg); + if (i == 0 || *tmp < *result) + *result = *tmp; + PFREE(tmp); + } + break; + } + return(result); +} + +double *dist_pb(Point *pt, BOX *box) +{ + Point *tmp; + double *result; + + tmp = close_pb(pt, box); + result = point_distance(tmp, pt); + + PFREE(tmp); + return(result); +} + + +double *dist_sl(LSEG *lseg, LINE *line) +{ + double *result; + + if (inter_sl(lseg, line)) { + result = PALLOCTYPE(double); + *result = 0.0; + } else /* parallel */ + result = dist_pl(&lseg->p[0], line); + + return(result); +} + + +double *dist_sb(LSEG *lseg, BOX *box) +{ + Point *tmp; + double *result; + + tmp = close_sb(lseg, box); + if (tmp == NULL) { + result = PALLOCTYPE(double); + *result = 0.0; + } else { + result = dist_pb(tmp, box); + PFREE(tmp); + } + + return(result); +} + + +double *dist_lb(LINE *line, BOX *box) +{ + Point *tmp; + double *result; + + tmp = close_lb(line, box); + if (tmp == NULL) { + result = PALLOCTYPE(double); + *result = 0.0; + } else { + result = dist_pb(tmp, box); + PFREE(tmp); + } + + return(result); +} + + +/*--------------------------------------------------------------------- + * interpt_ + * Intersection point of objects. + * We choose to ignore the "point" of intersection between + * lines and boxes, since there are typically two. + *-------------------------------------------------------------------*/ + +Point *interpt_sl(LSEG *lseg, LINE *line) +{ + LINE *tmp; + Point *p; + + tmp = line_construct_pp(&lseg->p[0], &lseg->p[1]); + p = line_interpt(tmp, line); + if (p) + if (! on_ps(p, lseg)) { + PFREE(p); + p = NULL; + } + + PFREE(tmp); + return(p); +} + + +/*--------------------------------------------------------------------- + * close_ + * Point of closest proximity between objects. + *-------------------------------------------------------------------*/ + +/* close_pl - + * The intersection point of a perpendicular of the line + * through the point. + */ +Point *close_pl(Point *pt, LINE *line) +{ + Point *result; + LINE *tmp; + double invm; + + result = PALLOCTYPE(Point); + if (FPeq(line->A, -1.0) && FPzero(line->B)) { /* vertical */ + result->x = line->C; + result->y = pt->y; + return(result); + } else if (FPzero(line->m)) { /* horizontal */ + result->x = pt->x; + result->y = line->C; + return(result); + } + /* drop a perpendicular and find the intersection point */ + invm = -1.0 / line->m; + tmp = line_construct_pm(pt, invm); + result = line_interpt(tmp, line); + return(result); +} + + +/* close_ps - + * Take the closest endpoint if the point is left, right, + * above, or below the segment, otherwise find the intersection + * point of the segment and its perpendicular through the point. + */ +Point *close_ps(Point *pt, LSEG *lseg) +{ + Point *result; + LINE *tmp; + double invm; + int xh, yh; + + result = NULL; + xh = lseg->p[0].x < lseg->p[1].x; + yh = lseg->p[0].y < lseg->p[1].y; + if (pt->x < lseg->p[!xh].x) + result = point_copy(&lseg->p[!xh]); + else if (pt->x > lseg->p[xh].x) + result = point_copy(&lseg->p[xh]); + else if (pt->y < lseg->p[!yh].y) + result = point_copy(&lseg->p[!yh]); + else if (pt->y > lseg->p[yh].y) + result = point_copy(&lseg->p[yh]); + if (result) + return(result); + if (FPeq(lseg->p[0].x, lseg->p[1].x)) { /* vertical */ + result->x = lseg->p[0].x; + result->y = pt->y; + return(result); + } else if (FPzero(lseg->m)) { /* horizontal */ + result->x = pt->x; + result->y = lseg->p[0].y; + return(result); + } + + invm = -1.0 / lseg->m; + tmp = line_construct_pm(pt, invm); + result = interpt_sl(lseg, tmp); + return(result); +} + +Point *close_pb(Point *pt, BOX *box) +{ + /* think about this one for a while */ + + return(NULL); +} + +Point *close_sl(LSEG *lseg, LINE *line) +{ + Point *result; + double *d1, *d2; + + result = interpt_sl(lseg, line); + if (result) + return(result); + d1 = dist_pl(&lseg->p[0], line); + d2 = dist_pl(&lseg->p[1], line); + if (d1 < d2) + result = point_copy(&lseg->p[0]); + else + result = point_copy(&lseg->p[1]); + + PFREE(d1); + PFREE(d2); + return(result); +} + +Point *close_sb(LSEG *lseg, BOX *box) +{ + /* think about this one for a while */ + + return(NULL); +} + +Point *close_lb(LINE *line, BOX *box) +{ + /* think about this one for a while */ + + return(NULL); +} + +/*--------------------------------------------------------------------- + * on_ + * Whether one object lies completely within another. + *-------------------------------------------------------------------*/ + +/* on_pl - + * Does the point satisfy the equation? + */ +long on_pl(Point *pt, LINE *line) +{ + return( FPzero(line->A * pt->x + line->B * pt->y + line->C) ); +} + + +/* on_ps - + * Determine colinearity by detecting a triangle inequality. + */ +long on_ps(Point *pt, LSEG *lseg) +{ + return( point_dt(pt, &lseg->p[0]) + point_dt(pt, &lseg->p[1]) + == point_dt(&lseg->p[0], &lseg->p[1]) ); +} + +long on_pb(Point *pt, BOX *box) +{ + return( pt->x <= box->xh && pt->x >= box->xl && + pt->y <= box->yh && pt->y >= box->yl ); +} + +/* on_ppath - + * Whether a point lies within (on) a polyline. + * If open, we have to (groan) check each segment. + * If closed, we use the old O(n) ray method for point-in-polygon. + * The ray is horizontal, from pt out to the right. + * Each segment that crosses the ray counts as an + * intersection; note that an endpoint or edge may touch + * but not cross. + * (we can do p-in-p in lg(n), but it takes preprocessing) + */ +#define NEXT(A) ((A+1) % path->npts) /* cyclic "i+1" */ + +long on_ppath(Point *pt, PATH *path) +{ + int above, next, /* is the seg above the ray? */ + inter, /* # of times path crosses ray */ + hi, /* index inc of higher seg (0,1) */ + i, n; + double a, b, x, + yh, yl, xh, xl; + + if (! path->closed) { /*-- OPEN --*/ + n = path->npts - 1; + a = point_dt(pt, &path->p[0]); + for (i = 0; i < n; i++) { + b = point_dt(pt, &path->p[i+1]); + if (FPeq(a+b, + point_dt(&path->p[i], &path->p[i+1]))) + return(1); + a = b; + } + return(0); + } + + inter = 0; /*-- CLOSED --*/ + above = FPgt(path->p[0].y, pt->y) ? ABOVE : + FPlt(path->p[0].y, pt->y) ? BELOW : UNDEF; + + for (i = 0; i < path->npts; i++) { + hi = path->p[i].y < path->p[NEXT(i)].y; + /* must take care of wrap around to original vertex for closed paths */ + yh = (i+hi < path->npts) ? path->p[i+hi].y : path->p[0].y; + yl = (i+!hi < path->npts) ? path->p[i+!hi].y : path->p[0].y; + hi = path->p[i].x < path->p[NEXT(i)].x; + xh = (i+hi < path->npts) ? path->p[i+hi].x : path->p[0].x; + xl = (i+!hi < path->npts) ? path->p[i+!hi].x : path->p[0].x; + /* skip seg if it doesn't touch the ray */ + + if (FPeq(yh, yl)) /* horizontal seg? */ + if (FPge(pt->x, xl) && FPle(pt->x, xh) && + FPeq(pt->y, yh)) + return(1); /* pt lies on seg */ + else + continue; /* skip other hz segs */ + if (FPlt(yh, pt->y) || /* pt is strictly below seg */ + FPgt(yl, pt->y)) /* strictly above */ + continue; + + /* seg touches the ray, find out where */ + + x = FPeq(xh, xl) /* vertical seg? */ + ? path->p[i].x + : (pt->y - path->p[i].y) / + point_sl(&path->p[i], + &path->p[NEXT(i)]) + + path->p[i].x; + if (FPeq(x, pt->x)) /* pt lies on this seg */ + return(1); + + /* does the seg actually cross the ray? */ + + next = FPgt(path->p[NEXT(i)].y, pt->y) ? ABOVE : + FPlt(path->p[NEXT(i)].y, pt->y) ? BELOW : above; + inter += FPge(x, pt->x) && next != above; + above = next; + } + return( above == UNDEF || /* path is horizontal */ + inter % 2); /* odd # of intersections */ +} + + +long on_sl(LSEG *lseg, LINE *line) +{ + return( on_pl(&lseg->p[0], line) && on_pl(&lseg->p[1], line) ); +} + +long on_sb(LSEG *lseg, BOX *box) +{ + return( on_pb(&lseg->p[0], box) && on_pb(&lseg->p[1], box) ); +} + +/*--------------------------------------------------------------------- + * inter_ + * Whether one object intersects another. + *-------------------------------------------------------------------*/ + +long inter_sl(LSEG *lseg, LINE *line) +{ + Point *tmp; + + tmp = interpt_sl(lseg, line); + if (tmp) { + PFREE(tmp); + return(1); + } + return(0); +} + +long inter_sb(LSEG *lseg, BOX *box) +{ + return(0); +} + +long inter_lb(LINE *line, BOX *box) +{ + return(0); +} + +/*------------------------------------------------------------------ + * The following routines define a data type and operator class for + * POLYGONS .... Part of which (the polygon's bounding box is built on + * top of the BOX data type. + * + * make_bound_box - create the bounding box for the input polygon + *------------------------------------------------------------------*/ + +/* Maximum number of output digits printed */ +#define P_MAXDIG 12 + +/*--------------------------------------------------------------------- + * Make the smallest bounding box for the given polygon. + *---------------------------------------------------------------------*/ +void make_bound_box(POLYGON *poly) +{ + double x1,y1,x2,y2; + int npts = poly->npts; + + if (npts > 0) { + x1 = poly_min((double *)poly->pts, npts); + x2 = poly_max((double *)poly->pts, npts); + y1 = poly_min(((double *)poly->pts)+npts, npts), + y2 = poly_max(((double *)poly->pts)+npts, npts); + box_fill(&(poly->boundbox), x1, x2, y1, y2); + } +} + +/*------------------------------------------------------------------ + * polygon_in - read in the polygon from a string specification + * the string is of the form "(f8,f8,f8,f8,...,f8)" + *------------------------------------------------------------------*/ +POLYGON *poly_in(char *s) +{ + POLYGON *poly; + long points; + double *xp, *yp, strtod(); + int i, size; + + if((points = poly_pt_count(s, ',')) < 0) + elog(WARN, "Bad polygon external representation '%s'", s); + + size = offsetof(POLYGON, pts[0]) + 2 * sizeof(double) * points; + poly = (POLYGON *) PALLOC(size); + memset((char *) poly, 0, size); /* zero any holes */ + + if (!PointerIsValid(poly)) + elog(WARN, "Memory allocation failed, can't input polygon"); + + poly->npts = points; + poly->size = size; + + /* Store all x coords followed by all y coords */ + xp = (double *) &(poly->pts[0]); + yp = (double *) (poly->pts + points*sizeof(double)); + + s++; /* skip LDELIM */ + + for (i=0; i<points; i++,xp++,yp++) + { + *xp = strtod(s, &s); + s++; /* skip delimiter */ + *yp = strtod(s, &s); + s++; /* skip delimiter */ + } + make_bound_box(poly); + return (poly); +} + +/*------------------------------------------------------------- + * poly_pt_count - count the number of points specified in the + * polygon. + *-------------------------------------------------------------*/ +long poly_pt_count(char *s, char delim) +{ + long total = 0; + + if (*s++ != LDELIM) /* no left delimeter */ + return (long) -1; + + while (*s && (*s != RDELIM)) + { + while (*s && (*s != delim)) + s++; + total++; /* found one */ + if (*s) + s++; /* bump s past the delimiter */ + } + + /* if there was no right delimiter OR an odd number of points */ + + if ((*(s-1) != RDELIM) || ((total%2) != 0)) + return (long) -1; + + return (total/2); +} + +/*--------------------------------------------------------------- + * poly_out - convert internal POLYGON representation to the + * character string format "(f8,f8,f8,f8,...f8)" + *---------------------------------------------------------------*/ +char *poly_out(POLYGON *poly) +{ + int i; + double *xp, *yp; + char *output, *outptr; + + /*----------------------------------------------------- + * Get enough space for "(f8,f8,f8,f8,...,f8)" + * which P_MAXDIG+1 for each coordinate plus 2 + * for parens and 1 for the null + *-----------------------------------------------------*/ + output = (char *)PALLOC(2*(P_MAXDIG+1)*poly->npts + 3); + outptr = output; + + if (!output) + elog(WARN, "Memory allocation failed, can't output polygon"); + + *outptr++ = LDELIM; + + xp = (double *) poly->pts; + yp = (double *) (poly->pts + (poly->npts * sizeof(double))); + + sprintf(outptr, "%*g,%*g", P_MAXDIG, *xp++, P_MAXDIG, *yp++); + outptr += (2*P_MAXDIG + 1); + + for (i=1; i<poly->npts; i++,xp++,yp++) + { + sprintf(outptr, ",%*g,%*g", P_MAXDIG, *xp, P_MAXDIG, *yp); + outptr += 2*(P_MAXDIG + 1); + } + *outptr++ = RDELIM; + *outptr = '\0'; + return (output); +} + +/*------------------------------------------------------- + * Find the largest coordinate out of n coordinates + *-------------------------------------------------------*/ +double poly_max(double *coords, int ncoords) +{ + double max; + + max = *coords++; + ncoords--; + while (ncoords--) + { + if (*coords > max) + max = *coords; + coords++; + } + return max; +} + +/*------------------------------------------------------- + * Find the smallest coordinate out of n coordinates + *-------------------------------------------------------*/ +double poly_min(double *coords, int ncoords) +{ + double min; + + min = *coords++; + ncoords--; + while (ncoords--) + { + if (*coords < min) + min = *coords; + coords++; + } + return min; +} + +/*------------------------------------------------------- + * Is polygon A strictly left of polygon B? i.e. is + * the right most point of A left of the left most point + * of B? + *-------------------------------------------------------*/ +long poly_left(POLYGON *polya, POLYGON *polyb) +{ + double right, left; + + if (polya->npts > 0) + right = poly_max((double *)polya->pts, polya->npts); + else + right = polya->boundbox.xh; + if (polyb->npts > 0) + left = poly_min((double *)polyb->pts, polyb->npts); + else + left = polyb->boundbox.xl; + + return (right < left); +} + +/*------------------------------------------------------- + * Is polygon A overlapping or left of polygon B? i.e. is + * the left most point of A left of the right most point + * of B? + *-------------------------------------------------------*/ +long poly_overleft(POLYGON *polya, POLYGON *polyb) +{ + double left, right; + + if (polya->npts > 0) + left = poly_min((double *)polya->pts, polya->npts); + else + left = polya->boundbox.xl; + if (polyb->npts > 0) + right = poly_max((double *)polyb->pts, polyb->npts); + else + right = polyb->boundbox.xh; + + return (left <= right); +} + +/*------------------------------------------------------- + * Is polygon A strictly right of polygon B? i.e. is + * the left most point of A right of the right most point + * of B? + *-------------------------------------------------------*/ +long poly_right(POLYGON *polya, POLYGON *polyb) +{ + double right, left; + + if (polya->npts > 0) + left = poly_min((double *)polya->pts, polya->npts); + else + left = polya->boundbox.xl; + if (polyb->npts > 0) + right = poly_max((double *)polyb->pts, polyb->npts); + else + right = polyb->boundbox.xh; + + return (left > right); +} + +/*------------------------------------------------------- + * Is polygon A overlapping or right of polygon B? i.e. is + * the right most point of A right of the left most point + * of B? + *-------------------------------------------------------*/ +long poly_overright(POLYGON *polya, POLYGON *polyb) +{ + double right, left; + + if (polya->npts > 0) + right = poly_max((double *)polya->pts, polya->npts); + else + right = polya->boundbox.xh; + if (polyb->npts > 0) + left = poly_min((double *)polyb->pts, polyb->npts); + else + left = polyb->boundbox.xl; + + return (right > left); +} + +/*------------------------------------------------------- + * Is polygon A the same as polygon B? i.e. are all the + * points the same? + *-------------------------------------------------------*/ +long poly_same(POLYGON *polya, POLYGON *polyb) +{ + int i; + double *axp, *bxp; /* point to x coordinates for a and b */ + + if (polya->npts != polyb->npts) + return 0; + + axp = (double *)polya->pts; + bxp = (double *)polyb->pts; + + for (i=0; i<polya->npts; axp++, bxp++, i++) + { + if (*axp != *bxp) + return 0; + } + return 1; +} + +/*----------------------------------------------------------------- + * Determine if polygon A overlaps polygon B by determining if + * their bounding boxes overlap. + *-----------------------------------------------------------------*/ +long poly_overlap(POLYGON *polya, POLYGON *polyb) +{ + return box_overlap(&(polya->boundbox), &(polyb->boundbox)); +} + +/*----------------------------------------------------------------- + * Determine if polygon A contains polygon B by determining if A's + * bounding box contains B's bounding box. + *-----------------------------------------------------------------*/ +long poly_contain(POLYGON *polya, POLYGON *polyb) +{ + return box_contain(&(polya->boundbox), &(polyb->boundbox)); +} + +/*----------------------------------------------------------------- + * Determine if polygon A is contained by polygon B by determining + * if A's bounding box is contained by B's bounding box. + *-----------------------------------------------------------------*/ +long poly_contained(POLYGON *polya, POLYGON *polyb) +{ + return box_contained(&(polya->boundbox), &(polyb->boundbox)); +} diff --git a/src/backend/utils/adt/geo-selfuncs.c b/src/backend/utils/adt/geo-selfuncs.c new file mode 100644 index 00000000000..a816e738f53 --- /dev/null +++ b/src/backend/utils/adt/geo-selfuncs.c @@ -0,0 +1,124 @@ +/*------------------------------------------------------------------------- + * + * geo-selfuncs.c-- + * Selectivity routines registered in the operator catalog in the + * "oprrest" and "oprjoin" attributes. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/geo-selfuncs.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + * XXX These are totally bogus. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/attnum.h" +#include "utils/geo-decls.h" /* where function declarations go */ +#include "utils/palloc.h" + +float64 +areasel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 4.0; + return(result); +} + +float64 +areajoinsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 4.0; + return(result); +} + +/* + * Selectivity functions for rtrees. These are bogus -- unless we know + * the actual key distribution in the index, we can't make a good prediction + * of the selectivity of these operators. + * + * In general, rtrees need to search multiple subtrees in order to guarantee + * that all occurrences of the same key have been found. Because of this, + * the heuristic selectivity functions we return are higher than they would + * otherwise be. + */ + +/* + * left_sel -- How likely is a box to be strictly left of (right of, above, + * below) a given box? + */ + +float64 +leftsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 6.0; + return(result); +} + +float64 +leftjoinsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 6.0; + return(result); +} + +/* + * contsel -- How likely is a box to contain (be contained by) a given box? + */ +float64 +contsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 10.0; + return(result); +} + +float64 +contjoinsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 10.0; + return(result); +} diff --git a/src/backend/utils/adt/int.c b/src/backend/utils/adt/int.c new file mode 100644 index 00000000000..cbf4a0cba10 --- /dev/null +++ b/src/backend/utils/adt/int.c @@ -0,0 +1,343 @@ +/*------------------------------------------------------------------------- + * + * int.c-- + * Functions for the built-in integer types. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/int.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * OLD COMMENTS + * I/O routines: + * int2in, int2out, int28in, int28out, int4in, int4out + * Conversion routines: + * itoi + * Boolean operators: + * inteq, intne, intlt, intle, intgt, intge + * Arithmetic operators: + * intpl, intmi, int4mul, intdiv + * + * Arithmetic operators: + * intmod, int4fac + * + * XXX makes massive and possibly unwarranted type promotion assumptions. + * fix me when we figure out what we want to do about ANSIfication... + */ + +#include "postgres.h" +#include "fmgr.h" +#include "utils/builtins.h" /* where the declarations go */ +#include "utils/elog.h" +#include "utils/palloc.h" + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * int2in - converts "num" to short + */ +int32 int2in(char *num) +{ + return((int32) pg_atoi(num, sizeof(int16), '\0')); +} + +/* + * int2out - converts short to "num" + */ +char *int2out(int16 sh) +{ + char *result; + + result = (char *)palloc(7); /* assumes sign, 5 digits, '\0' */ + itoa((int) sh, result); + return(result); +} + +/* + * int28in - converts "num num ..." to internal form + * + * Note: + * Fills any nonexistent digits with NULLs. + */ +int16 *int28in(char *shs) +{ + register int16 (*result)[]; + int nums; + + if (shs == NULL) + return(NULL); + result = (int16 (*)[]) palloc(sizeof(int16 [8])); + if ((nums = sscanf(shs, "%hd%hd%hd%hd%hd%hd%hd%hd", + *result, + *result + 1, + *result + 2, + *result + 3, + *result + 4, + *result + 5, + *result + 6, + *result + 7)) != 8) { + do + (*result)[nums++] = 0; + while (nums < 8); + } + return((int16 *) result); +} + +/* + * int28out - converts internal form to "num num ..." + */ +char *int28out(int16 (*shs)[]) +{ + register int num; + register int16 *sp; + register char *rp; + char *result; + + if (shs == NULL) { + result = (char *)palloc(2); + result[0] = '-'; + result[1] = '\0'; + return(result); + } + rp = result = (char *)palloc(8 * 7); /* assumes sign, 5 digits, ' ' */ + sp = *shs; + for (num = 8; num != 0; num--) { + itoa(*sp++, rp); + while (*++rp != '\0') + ; + *rp++ = ' '; + } + *--rp = '\0'; + return(result); +} + +/* + * int28in - converts "num num ..." to internal form + * + * Note: + * Fills any nonexistent digits with NULLs. + */ +int32 *int44in(char *input_string) +{ + int32 *foo = (int32 *)palloc(4*sizeof(int32)); + register int i = 0; + + i = sscanf(input_string, + "%d, %d, %d, %d", + &foo[0], + &foo[1], + &foo[2], + &foo[3]); + while (i < 4) + foo[i++] = 0; + + return(foo); +} + +/* + * int28out - converts internal form to "num num ..." + */ +char *int44out(int32 an_array[]) +{ + int temp = 4; + char *output_string = NULL; + int i; + + if ( temp > 0 ) { + char *walk; + output_string = (char *)palloc(16*temp); /* assume 15 digits + sign */ + walk = output_string; + for ( i = 0 ; i < temp ; i++ ) { + itoa(an_array[i],walk); + while (*++walk != '\0') + ; + *walk++ = ' '; + } + *--walk = '\0'; + } + return(output_string); +} + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +/* + * int4in - converts "num" to int4 + */ +int32 int4in(char *num) +{ + return(pg_atoi(num, sizeof(int32), '\0')); +} + +/* + * int4out - converts int4 to "num" + */ +char *int4out(int32 l) +{ + char *result; + + result = (char *)palloc(12); /* assumes sign, 10 digits, '\0' */ + ltoa(l, result); + return(result); +} + + +/* + * =================== + * CONVERSION ROUTINES + * =================== + */ + +int32 i2toi4(int16 arg1) +{ + return((int32) arg1); +} + +int16 i4toi2(int32 arg1) +{ + if (arg1< -0x8000) + elog(NOTICE, "i4toi2: \"%d\" causes int2 underflow", arg1); + if (arg1 > 0x7FFF) + elog(NOTICE, "i4toi2: \"%d\" causes int2 overflow", arg1); + + return((int16) arg1); +} + + +/* + * ========================= + * BOOLEAN OPERATOR ROUTINES + * ========================= + */ + +/* + * inteq - returns 1 iff arg1 == arg2 + * intne - returns 1 iff arg1 != arg2 + * intlt - returns 1 iff arg1 < arg2 + * intle - returns 1 iff arg1 <= arg2 + * intgt - returns 1 iff arg1 > arg2 + * intge - returns 1 iff arg1 >= arg2 + */ +int32 int4eq(int32 arg1, int32 arg2) { return(arg1 == arg2); } +int32 int4ne(int32 arg1, int32 arg2) { return(arg1 != arg2); } +int32 int4lt(int32 arg1, int32 arg2) { return(arg1 < arg2); } +int32 int4le(int32 arg1, int32 arg2) { return(arg1 <= arg2); } +int32 int4gt(int32 arg1, int32 arg2) { return(arg1 > arg2); } +int32 int4ge(int32 arg1, int32 arg2) { return(arg1 >= arg2); } + +int32 int2eq(int16 arg1, int16 arg2) { return(arg1 == arg2); } +int32 int2ne(int16 arg1, int16 arg2) { return(arg1 != arg2); } +int32 int2lt(int16 arg1, int16 arg2) { return(arg1 < arg2); } +int32 int2le(int16 arg1, int16 arg2) { return(arg1 <= arg2); } +int32 int2gt(int16 arg1, int16 arg2) { return(arg1 > arg2); } +int32 int2ge(int16 arg1, int16 arg2) { return(arg1 >= arg2); } + +int32 int24eq(int32 arg1, int32 arg2) { return(arg1 == arg2); } +int32 int24ne(int32 arg1, int32 arg2) { return(arg1 != arg2); } +int32 int24lt(int32 arg1, int32 arg2) { return(arg1 < arg2); } +int32 int24le(int32 arg1, int32 arg2) { return(arg1 <= arg2); } +int32 int24gt(int32 arg1, int32 arg2) { return(arg1 > arg2); } +int32 int24ge(int32 arg1, int32 arg2) { return(arg1 >= arg2); } + +int32 int42eq(int32 arg1, int32 arg2) { return(arg1 == arg2); } +int32 int42ne(int32 arg1, int32 arg2) { return(arg1 != arg2); } +int32 int42lt(int32 arg1, int32 arg2) { return(arg1 < arg2); } +int32 int42le(int32 arg1, int32 arg2) { return(arg1 <= arg2); } +int32 int42gt(int32 arg1, int32 arg2) { return(arg1 > arg2); } +int32 int42ge(int32 arg1, int32 arg2) { return(arg1 >= arg2); } + + +int32 keyfirsteq(int16 *arg1, int16 arg2) { return(*arg1 == arg2); } + +/* + * int[24]pl - returns arg1 + arg2 + * int[24]mi - returns arg1 - arg2 + * int[24]mul - returns arg1 * arg2 + * int[24]div - returns arg1 / arg2 + */ +int32 int4um(int32 arg) { return(-arg); } +int32 int4pl(int32 arg1, int32 arg2) { return(arg1 + arg2); } +int32 int4mi(int32 arg1, int32 arg2) { return(arg1 - arg2); } +int32 int4mul(int32 arg1, int32 arg2) { return(arg1 * arg2); } +int32 int4div(int32 arg1, int32 arg2) { return(arg1 / arg2); } +int32 int4inc(int32 arg) { return(arg + (int32)1); } + +int16 int2um(int16 arg) { return(-arg); } +int16 int2pl(int16 arg1, int16 arg2) { return(arg1 + arg2); } +int16 int2mi(int16 arg1, int16 arg2) { return(arg1 - arg2); } +int16 int2mul(int16 arg1, int16 arg2) { return(arg1 * arg2); } +int16 int2div(int16 arg1, int16 arg2) { return(arg1 / arg2); } +int16 int2inc(int16 arg) { return(arg + (int16)1); } + +int32 int24pl(int32 arg1, int32 arg2) { return(arg1 + arg2); } +int32 int24mi(int32 arg1, int32 arg2) { return(arg1 - arg2); } +int32 int24mul(int32 arg1, int32 arg2) { return(arg1 * arg2); } +int32 int24div(int32 arg1, int32 arg2) { return(arg1 / arg2); } + +int32 int42pl(int32 arg1, int32 arg2) { return(arg1 + arg2); } +int32 int42mi(int32 arg1, int32 arg2) { return(arg1 - arg2); } +int32 int42mul(int32 arg1, int32 arg2) { return(arg1 * arg2); } +int32 int42div(int32 arg1, int32 arg2) { return(arg1 / arg2); } + +/* + * int[24]mod - returns arg1 mod arg2 + */ +int32 int4mod(int32 arg1, int32 arg2) { return(arg1 % arg2); } +int32 int2mod(int16 arg1, int16 arg2) { return(arg1 % arg2); } +int32 int24mod(int32 arg1, int32 arg2) { return(arg1 % arg2); } +int32 int42mod(int32 arg1, int32 arg2) { return(arg1 % arg2); } + +/* + * int[24]fac - returns arg1! + */ +int32 int4fac(int32 arg1) +{ + int32 result; + + if (arg1 < 1) + result = 0; + else + for (result = 1; arg1 > 0; --arg1) + result *= arg1; + return(result); +} + +int32 int2fac(int16 arg1) +{ + int16 result; + + if (arg1 < 1) + result = 0; + else + for (result = 1; arg1 > 0; --arg1) + result *= arg1; + return(result); +} + +int16 int2larger(int16 arg1, int16 arg2) +{ + return ((arg1 > arg2) ? arg1 : arg2); +} + +int16 int2smaller(int16 arg1, int16 arg2) +{ + return ((arg1 < arg2) ? arg1 : arg2); +} + +int32 int4larger(int32 arg1, int32 arg2) +{ + return ((arg1 > arg2) ? arg1 : arg2); +} + +int32 int4smaller(int32 arg1, int32 arg2) +{ + return ((arg1 < arg2) ? arg1 : arg2); +} diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c new file mode 100644 index 00000000000..e33e66ee62d --- /dev/null +++ b/src/backend/utils/adt/like.c @@ -0,0 +1,225 @@ +/*------------------------------------------------------------------------- + * + * like.c-- + * like expression handling code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * /usr/local/devel/pglite/cvs/src/backend/utils/adt/like.c,v 1.1 1995/07/30 23:55:36 emkxp01 Exp + * + * + * NOTES + * A big hack of the regexp.c code!! Contributed by + * Keith Parks <emkxp01@mtcc.demon.co.uk> (7/95). + * + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" /* postgres system include file */ +#include "utils/elog.h" /* for logging postgres errors */ +#include "utils/palloc.h" +#include "utils/builtins.h" /* where the function declarations go */ + +int like(char *text, char *p); + +/* + * interface routines called by the function manager + */ + +/* + fixedlen_like: + + a generic fixed length like routine + s - the string to match against (not necessarily null-terminated) + p - the pattern + charlen - the length of the string +*/ +static bool +fixedlen_like(char *s, struct varlena* p, int charlen) +{ + char *sterm, *pterm; + int result; + + if (!s || !p) + return FALSE; + + /* be sure sterm is null-terminated */ + sterm = (char *) palloc(charlen + 1); + memset(sterm, 0, charlen + 1); + strncpy(sterm, s, charlen); + + /* p is a text = varlena, not a string so we have to make + * a string from the vl_data field of the struct. */ + + /* palloc the length of the text + the null character */ + pterm = (char *) palloc(VARSIZE(p) - VARHDRSZ + 1); + memmove(pterm, VARDATA(p), VARSIZE(p) - VARHDRSZ); + *(pterm + VARSIZE(p) - VARHDRSZ) = (char)NULL; + + /* do the regexp matching */ + result = like(sterm, pterm); + + pfree(sterm); + pfree(pterm); + + return ((bool) result); +} + +bool +char2like(uint16 arg1, struct varlena *p) +{ + char *s = (char *) &arg1; + return (fixedlen_like(s, p, 2)); +} + +bool +char2nlike(uint16 arg1, struct varlena *p) +{ + return (!char2like(arg1, p)); +} + +bool +char4like(uint32 arg1, struct varlena *p) +{ + char *s = (char *) &arg1; + return (fixedlen_like(s, p, 4)); +} + +bool +char4nlike(uint32 arg1, struct varlena *p) +{ + return (!char4like(arg1, p)); +} + +bool +char8like(char *s, struct varlena *p) +{ + return (fixedlen_like(s, p, 8)); +} + +bool +char8nlike(char *s, struct varlena *p) +{ + return (!char8like(s, p)); +} + +bool +char16like(char *s, struct varlena *p) +{ + return (fixedlen_like(s, p, 16)); +} +bool +char16nlike(char *s, struct varlena *p) +{ + return (!char16like(s, p)); +} + +bool +namelike(NameData *n, struct varlena *p) +{ + return (fixedlen_like(n->data, p, NAMEDATALEN)); +} + +bool +namenlike(NameData *s, struct varlena *p) +{ + return (!namelike(s, p)); +} + +bool +textlike(struct varlena *s, struct varlena *p) +{ + return (fixedlen_like(VARDATA(s), p, VARSIZE(s) - VARHDRSZ)); +} + +bool textnlike(struct varlena *s, struct varlena *p) +{ + return (!textlike(s, p)); +} + + +/* $Revision: 1.1.1.1 $ +** "like.c" A first attempt at a LIKE operator for Postgres95. +** +** Originally written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986. +** Rich $alz is now <rsalz@bbn.com>. +** Special thanks to Lars Mathiesen <thorinn@diku.dk> for the LABORT code. +** +** This code was shamelessly stolen from the "pql" code by myself and +** slightly modified :) +** +** All references to the word "star" were replaced by "percent" +** All references to the word "wild" were replaced by "like" +** +** All the nice shell RE matching stuff was replaced by just "_" and "%" +** +** As I don't have a copy of the SQL standard handy I wasn't sure whether +** to leave in the '\' escape character handling. (I suspect the standard +** handles "%%" as a single literal percent) +** +** Keith Parks. <keith@mtcc.demon.co.uk> +** +** [SQL92 lets you specify the escape character by saying +** LIKE <pattern> ESCAPE <escape character>. We are a small operation +** so we force you to use '\'. - ay 7/95] +** +*/ + +#define LIKE_TRUE 1 +#define LIKE_FALSE 0 +#define LIKE_ABORT -1 + +/* +** Match text and p, return LIKE_TRUE, LIKE_FALSE, or LIKE_ABORT. +*/ +static int +DoMatch(register char *text, register char *p) +{ + register int matched; + + for ( ; *p; text++, p++) { + if (*text == '\0' && *p != '%') + return LIKE_ABORT; + switch (*p) { + case '\\': + /* Literal match with following character. */ + p++; + /* FALLTHROUGH */ + default: + if (*text != *p) + return LIKE_FALSE; + continue; + case '_': + /* Match anything. */ + continue; + case '%': + while (*++p == '%') + /* Consecutive percents act just like one. */ + continue; + if (*p == '\0') + /* Trailing percent matches everything. */ + return LIKE_TRUE; + while (*text) + if ((matched = DoMatch(text++, p)) != LIKE_FALSE) + return matched; + return LIKE_ABORT; + } + } + + return *text == '\0'; +} + + +/* +** User-level routine. Returns TRUE or FALSE. +*/ +int +like(char *text, char *p) +{ + if (p[0] == '%' && p[1] == '\0') + return TRUE; + return (DoMatch(text, p) == LIKE_TRUE); +} diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c new file mode 100644 index 00000000000..7ba7cc98f7e --- /dev/null +++ b/src/backend/utils/adt/misc.c @@ -0,0 +1,96 @@ +/*------------------------------------------------------------------------- + * + * misc.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/misc.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <sys/file.h> +#include "postgres.h" +#include "utils/datum.h" +#include "catalog/pg_type.h" +#include "utils/builtins.h" + +#if !defined(PORTNAME_linux) && !defined(PORTNAME_BSD44_derived) && \ + !defined(PORTNAME_irix5) && !defined(PORTNAME_bsdi) && !defined(PORTNAME_aix) +extern int random(); +extern int srandom(unsigned); +#endif + + +/*------------------------------------------------------------------------- + * Check if data is Null + */ +bool +NullValue(Datum value, bool *isNull) +{ + if (*isNull) { + *isNull = false; + return(true); + } + return(false); + +} + +/*----------------------------------------------------------------------* + * check if data is not Null * + *--------------------------------------------------------------------- */ +bool +NonNullValue(Datum value, bool *isNull) +{ + if (*isNull) { + *isNull = false; + return(false); + } + return(true); + +} + +/* + * oidrand (oid o, int4 X)- + * takes in an oid and a int4 X, and will return 'true' + * about 1/X of the time. + * Useful for doing random sampling or subsetting. + * if X == 0, this will always return true; + * + * Example use: + * select * from TEMP where oidrand(TEMP.oid, 10) + * will return about 1/10 of the tuples in TEMP + * + */ +bool +oidrand(Oid o, int32 X) +{ + bool result; + + if (X == 0) return true; + + result = (random() % X == 0); + return result; +} + +/* + oidsrand(int32 X) - + seeds the random number generator + always return true +*/ +bool +oidsrand(int32 X) +{ + srand(X); + return true; +} + + + +int32 +userfntest(int i) +{ + return (i); +} diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c new file mode 100644 index 00000000000..fa66ff5fa40 --- /dev/null +++ b/src/backend/utils/adt/nabstime.c @@ -0,0 +1,866 @@ +/*------------------------------------------------------------------------- + * + * nabstime.c-- + * parse almost any absolute date getdate(3) can (& some it can't) + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include "postgres.h" +#include "access/xact.h" +#include "utils/nabstime.h" +#include "utils/palloc.h" + +#define MAXDATEFIELDS 25 + +#define ISSPACE(c) ((c) == ' ' || (c) == '\n' || (c) == '\t') + +/* this is fast but dirty. note the return's in the middle. */ +#define GOBBLE_NUM(cp, c, x, ip) \ + (c) = *(cp)++; \ + if ((c) < '0' || (c) > '9') \ + return -1; /* missing digit */ \ + (x) = (c) - '0'; \ + (c) = *(cp)++; \ + if ((c) >= '0' && (c) <= '9') { \ + (x) = 10*(x) + (c) - '0'; \ + (c) = *(cp)++; \ + } \ + if ((c) != ':' && (c) != '\0' && !ISSPACE(c)) \ + return -1; /* missing colon */ \ + *(ip) = (x) /* N.B.: no semi-colon here */ + +#define EPOCH 1970 +#define DAYS_PER_400YRS (time_t)146097 +#define DAYS_PER_4YRS (time_t)1461 +#define SECS_PER_DAY 86400 +#define SECS_PER_HOUR 3600 +#define DIVBY4(n) ((n) >> 2) +#define YRNUM(c, y) (DIVBY4(DAYS_PER_400YRS*(c)) + DIVBY4(DAYS_PER_4YRS*(y))) +#define DAYNUM(c,y,mon,d) (YRNUM((c), (y)) + mdays[mon] + (d)) +#define EPOCH_DAYNUM DAYNUM(19, 69, 10, 1) /* really January 1, 1970 */ +#define MIN_DAYNUM -24856 /* December 13, 1901 */ +#define MAX_DAYNUM 24854 /* January 18, 2038 */ + +/* definitions for squeezing values into "value" */ +#define ABS_SIGNBIT 0200 +#define VALMASK 0177 +#define NEG(n) ((n)|ABS_SIGNBIT) +#define SIGNEDCHAR(c) ((c)&ABS_SIGNBIT? -((c)&VALMASK): (c)) +#define FROMVAL(tp) (-SIGNEDCHAR((tp)->value) * 10) /* uncompress */ +#define TOVAL(tp, v) ((tp)->value = ((v) < 0? NEG((-(v))/10): (v)/10)) +#define IsLeapYear(yr) ((yr%4) == 0) + +char nmdays[] = { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; +/* days since start of year. mdays[0] is March, mdays[11] is February */ +static short mdays[] = { + 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337 +}; + +/* exports */ +static int dtok_numparsed; + +/* + * to keep this table reasonably small, we divide the lexval for TZ and DTZ + * entries by 10 and truncate the text field at MAXTOKLEN characters. + * the text field is not guaranteed to be NUL-terminated. + */ +static datetkn datetktbl[] = { +/* text token lexval */ +{ "acsst", DTZ, 63}, /* Cent. Australia */ +{ "acst", TZ, 57}, /* Cent. Australia */ +{ "adt", DTZ, NEG(18)}, /* Atlantic Daylight Time */ +{ "aesst", DTZ, 66}, /* E. Australia */ +{ "aest", TZ, 60}, /* Australia Eastern Std Time */ +{ "ahst", TZ, 60}, /* Alaska-Hawaii Std Time */ +{ "am", AMPM, AM}, +{ "apr", MONTH, 4}, +{ "april", MONTH, 4}, +{ "ast", TZ, NEG(24)}, /* Atlantic Std Time (Canada) */ +{ "at", PG_IGNORE, 0}, /* "at" (throwaway) */ +{ "aug", MONTH, 8}, +{ "august", MONTH, 8}, +{ "awsst", DTZ, 54}, /* W. Australia */ +{ "awst", TZ, 48}, /* W. Australia */ +{ "bst", TZ, 6}, /* British Summer Time */ +{ "bt", TZ, 18}, /* Baghdad Time */ +{ "cadt", DTZ, 63}, /* Central Australian DST */ +{ "cast", TZ, 57}, /* Central Australian ST */ +{ "cat", TZ, NEG(60)}, /* Central Alaska Time */ +{ "cct", TZ, 48}, /* China Coast */ +{ "cdt", DTZ, NEG(30)}, /* Central Daylight Time */ +{ "cet", TZ, 6}, /* Central European Time */ +{ "cetdst", DTZ, 12}, /* Central European Dayl.Time */ +{ "cst", TZ, NEG(36)}, /* Central Standard Time */ +{ "dec", MONTH, 12}, +{ "decemb", MONTH, 12}, +{ "dnt", TZ, 6}, /* Dansk Normal Tid */ +{ "dst", PG_IGNORE, 0}, +{ "east", TZ, NEG(60)}, /* East Australian Std Time */ +{ "edt", DTZ, NEG(24)}, /* Eastern Daylight Time */ +{ "eet", TZ, 12}, /* East. Europe, USSR Zone 1 */ +{ "eetdst", DTZ, 18}, /* Eastern Europe */ +{ "est", TZ, NEG(30)}, /* Eastern Standard Time */ +{ "feb", MONTH, 2}, +{ "februa", MONTH, 2}, +{ "fri", PG_IGNORE, 5}, +{ "friday", PG_IGNORE, 5}, +{ "fst", TZ, 6}, /* French Summer Time */ +{ "fwt", DTZ, 12}, /* French Winter Time */ +{ "gmt", TZ, 0}, /* Greenwish Mean Time */ +{ "gst", TZ, 60}, /* Guam Std Time, USSR Zone 9 */ +{ "hdt", DTZ, NEG(54)}, /* Hawaii/Alaska */ +{ "hmt", DTZ, 18}, /* Hellas ? ? */ +{ "hst", TZ, NEG(60)}, /* Hawaii Std Time */ +{ "idle", TZ, 72}, /* Intl. Date Line, East */ +{ "idlw", TZ, NEG(72)}, /* Intl. Date Line, West */ +{ "ist", TZ, 12}, /* Israel */ +{ "it", TZ, 22}, /* Iran Time */ +{ "jan", MONTH, 1}, +{ "januar", MONTH, 1}, +{ "jst", TZ, 54}, /* Japan Std Time,USSR Zone 8 */ +{ "jt", TZ, 45}, /* Java Time */ +{ "jul", MONTH, 7}, +{ "july", MONTH, 7}, +{ "jun", MONTH, 6}, +{ "june", MONTH, 6}, +{ "kst", TZ, 54}, /* Korea Standard Time */ +{ "ligt", TZ, 60}, /* From Melbourne, Australia */ +{ "mar", MONTH, 3}, +{ "march", MONTH, 3}, +{ "may", MONTH, 5}, +{ "mdt", DTZ, NEG(36)}, /* Mountain Daylight Time */ +{ "mest", DTZ, 12}, /* Middle Europe Summer Time */ +{ "met", TZ, 6}, /* Middle Europe Time */ +{ "metdst", DTZ, 12}, /* Middle Europe Daylight Time*/ +{ "mewt", TZ, 6}, /* Middle Europe Winter Time */ +{ "mez", TZ, 6}, /* Middle Europe Zone */ +{ "mon", PG_IGNORE, 1}, +{ "monday", PG_IGNORE, 1}, +{ "mst", TZ, NEG(42)}, /* Mountain Standard Time */ +{ "mt", TZ, 51}, /* Moluccas Time */ +{ "ndt", DTZ, NEG(15)}, /* Nfld. Daylight Time */ +{ "nft", TZ, NEG(21)}, /* Newfoundland Standard Time */ +{ "nor", TZ, 6}, /* Norway Standard Time */ +{ "nov", MONTH, 11}, +{ "novemb", MONTH, 11}, +{ "nst", TZ, NEG(21)}, /* Nfld. Standard Time */ +{ "nt", TZ, NEG(66)}, /* Nome Time */ +{ "nzdt", DTZ, 78}, /* New Zealand Daylight Time */ +{ "nzst", TZ, 72}, /* New Zealand Standard Time */ +{ "nzt", TZ, 72}, /* New Zealand Time */ +{ "oct", MONTH, 10}, +{ "octobe", MONTH, 10}, +{ "on", PG_IGNORE, 0}, /* "on" (throwaway) */ +{ "pdt", DTZ, NEG(42)}, /* Pacific Daylight Time */ +{ "pm", AMPM, PM}, +{ "pst", TZ, NEG(48)}, /* Pacific Standard Time */ +{ "sadt", DTZ, 63}, /* S. Australian Dayl. Time */ +{ "sast", TZ, 57}, /* South Australian Std Time */ +{ "sat", PG_IGNORE, 6}, +{ "saturd", PG_IGNORE, 6}, +{ "sep", MONTH, 9}, +{ "sept", MONTH, 9}, +{ "septem", MONTH, 9}, +{ "set", TZ, NEG(6)}, /* Seychelles Time ?? */ +{ "sst", DTZ, 12}, /* Swedish Summer Time */ +{ "sun", PG_IGNORE, 0}, +{ "sunday", PG_IGNORE, 0}, +{ "swt", TZ, 6}, /* Swedish Winter Time */ +{ "thu", PG_IGNORE, 4}, +{ "thur", PG_IGNORE, 4}, +{ "thurs", PG_IGNORE, 4}, +{ "thursd", PG_IGNORE, 4}, +{ "tue", PG_IGNORE, 2}, +{ "tues", PG_IGNORE, 2}, +{ "tuesda", PG_IGNORE, 2}, +{ "ut", TZ, 0}, +{ "utc", TZ, 0}, +{ "wadt", DTZ, 48}, /* West Australian DST */ +{ "wast", TZ, 42}, /* West Australian Std Time */ +{ "wat", TZ, NEG(6)}, /* West Africa Time */ +{ "wdt", DTZ, 54}, /* West Australian DST */ +{ "wed", PG_IGNORE, 3}, +{ "wednes", PG_IGNORE, 3}, +{ "weds", PG_IGNORE, 3}, +{ "wet", TZ, 0}, /* Western Europe */ +{ "wetdst", DTZ, 6}, /* Western Europe */ +{ "wst", TZ, 48}, /* West Australian Std Time */ +{ "ydt", DTZ, NEG(48)}, /* Yukon Daylight Time */ +{ "yst", TZ, NEG(54)}, /* Yukon Standard Time */ +{ "zp4", TZ, NEG(24)}, /* GMT +4 hours. */ +{ "zp5", TZ, NEG(30)}, /* GMT +5 hours. */ +{ "zp6", TZ, NEG(36)}, /* GMT +6 hours. */ +}; + +static unsigned int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; + +/* + * parse and convert absolute date in timestr (the normal interface) + * + * Returns the number of seconds since epoch (January 1 1970 GMT) + */ +AbsoluteTime +nabstimein(char* timestr) +{ + int tz = 0; + struct tm date; + + if (!timestr) + return INVALID_ABSTIME; + while (ISSPACE(*timestr)) + ++timestr; + + if (!strcasecmp(timestr, "epoch")) + return EPOCH_ABSTIME; + if (!strcasecmp(timestr, "now")) + return GetCurrentTransactionStartTime(); + if (!strcasecmp(timestr, "current")) + return CURRENT_ABSTIME; + if (!strcasecmp(timestr, "infinity")) + return NOEND_ABSTIME; + if (!strcasecmp(timestr, "-infinity")) + return NOSTART_ABSTIME; + if (prsabsdate(timestr, &date, &tz) < 0) + return INVALID_ABSTIME; + return dateconv(&date, tz); +} + +/* + * just parse the absolute date in timestr and get back a broken-out date. + */ +int +prsabsdate(char *timestr, + struct tm *tm, + int *tzp) /* - minutes west */ +{ + register int nf; + char *fields[MAXDATEFIELDS]; + static char delims[] = "- \t\n/,"; + + nf = split(timestr, fields, MAXDATEFIELDS, delims+1); + if (nf > MAXDATEFIELDS) + return -1; + if (tryabsdate(fields, nf, tm, tzp) < 0) { + register char *p = timestr; + + /* + * could be a DEC-date; glue it all back together, split it + * with dash as a delimiter and try again. Yes, this is a + * hack, but so are DEC-dates. + */ + while (--nf > 0) { + while (*p++ != '\0') + ; + p[-1] = ' '; + } + nf = split(timestr, fields, MAXDATEFIELDS, delims); + if (nf > MAXDATEFIELDS) + return -1; + if (tryabsdate(fields, nf, tm, tzp) < 0) + return -1; + } + return 0; +} + +/* + * try to parse pre-split timestr as an absolute date + */ +int +tryabsdate(char *fields[], int nf, struct tm *tm, int *tzp) +{ + register int i; + register datetkn *tp; + register long flg = 0, ty; + int mer = HR24, bigval = -1; +#ifndef USE_POSIX_TIME + struct timeb now; /* the old V7-ism */ + + (void) ftime(&now); + *tzp = now.timezone; +#else /* USE_POSIX_TIME */ +#if defined(PORTNAME_hpux) || \ + defined(PORTNAME_aix) || \ + defined(PORTNAME_irix5) || \ + defined(WIN32) || \ + defined(PORTNAME_sparc_solaris) + tzset(); +#ifndef WIN32 + *tzp = timezone / 60; /* this is an X/Open-ism */ +#else + *tzp = _timezone / 60; /* this is an X/Open-ism */ +#endif /* WIN32 */ +#else /* PORTNAME_hpux || PORTNAME_aix || PORTNAME_sparc_solaris || PORTNAME_irix5 */ + time_t now = time((time_t *) NULL); + struct tm *tmnow = localtime(&now); + + *tzp = - tmnow->tm_gmtoff / 60; /* tm_gmtoff is Sun/DEC-ism */ +#endif /* PORTNAME_hpux || PORTNAME_aix */ +#endif /* USE_POSIX_TIME */ + + tm->tm_mday = tm->tm_mon = tm->tm_year = -1; /* mandatory */ + tm->tm_hour = tm->tm_min = tm->tm_sec = 0; + tm->tm_isdst = -1; /* assume we don't know. */ + dtok_numparsed = 0; + + for (i = 0; i < nf; i++) { + if (fields[i][0] == '\0') + continue; + tp = datetoktype(fields[i], &bigval); + ty = (1L << tp->type) & ~(1L << PG_IGNORE); + if (flg&ty) + return -1; /* repeated type */ + flg |= ty; + switch (tp->type) { + case YEAR: + tm->tm_year = bigval; + break; + case DAY: + tm->tm_mday = bigval; + break; + case MONTH: + tm->tm_mon = tp->value; + break; + case TIME: + if (parsetime(fields[i], tm) < 0) + return -1; + break; + case DTZ: + tm->tm_isdst++; + /* FALLTHROUGH */ + case TZ: + *tzp = FROMVAL(tp); + break; + case PG_IGNORE: + break; + case AMPM: + mer = tp->value; + break; + default: + return -1; /* bad token type: CANTHAPPEN */ + } + } + if (tm->tm_year == -1 || tm->tm_mon == -1 || tm->tm_mday == -1) + return -1; /* missing component */ + if (mer == PM) + tm->tm_hour += 12; + return 0; +} + + +/* return -1 on failure */ +int +parsetime(char *time, struct tm *tm) +{ + register char c; + register int x; + + tm->tm_sec = 0; + GOBBLE_NUM(time, c, x, &tm->tm_hour); + if (c != ':') + return -1; /* only hour; too short */ + GOBBLE_NUM(time, c, x, &tm->tm_min); + if (c != ':') + return 0; /* no seconds; okay */ + GOBBLE_NUM(time, c, x, &tm->tm_sec); + /* this may be considered too strict. garbage at end of time? */ + return (c == '\0' || ISSPACE(c)? 0: -1); +} + + +/* + * split - divide a string into fields, like awk split() + */ +int /* number of fields, including overflow */ +split(char *string, + char *fields[], /* list is not NULL-terminated */ + int nfields, /* number of entries available in fields[] */ + char *sep) /* "" white, "c" single char, "ab" [ab]+ */ +{ + register char *p = string; + register char c; /* latest character */ + register char sepc = sep[0]; + register char sepc2; + register int fn; + register char **fp = fields; + register char *sepp; + register int trimtrail; + + /* white space */ + if (sepc == '\0') { + while ((c = *p++) == ' ' || c == '\t') + continue; + p--; + trimtrail = 1; + sep = " \t"; /* note, code below knows this is 2 long */ + sepc = ' '; + } else + trimtrail = 0; + sepc2 = sep[1]; /* now we can safely pick this up */ + + /* catch empties */ + if (*p == '\0') + return(0); + + /* single separator */ + if (sepc2 == '\0') { + fn = nfields; + for (;;) { + *fp++ = p; + fn--; + if (fn == 0) + break; + while ((c = *p++) != sepc) + if (c == '\0') + return(nfields - fn); + *(p-1) = '\0'; + } + /* we have overflowed the fields vector -- just count them */ + fn = nfields; + for (;;) { + while ((c = *p++) != sepc) + if (c == '\0') + return(fn); + fn++; + } + /* not reached */ + } + + /* two separators */ + if (sep[2] == '\0') { + fn = nfields; + for (;;) { + *fp++ = p; + fn--; + while ((c = *p++) != sepc && c != sepc2) + if (c == '\0') { + if (trimtrail && **(fp-1) == '\0') + fn++; + return(nfields - fn); + } + if (fn == 0) + break; + *(p-1) = '\0'; + while ((c = *p++) == sepc || c == sepc2) + continue; + p--; + } + /* we have overflowed the fields vector -- just count them */ + fn = nfields; + while (c != '\0') { + while ((c = *p++) == sepc || c == sepc2) + continue; + p--; + fn++; + while ((c = *p++) != '\0' && c != sepc && c != sepc2) + continue; + } + /* might have to trim trailing white space */ + if (trimtrail) { + p--; + while ((c = *--p) == sepc || c == sepc2) + continue; + p++; + if (*p != '\0') { + if (fn == nfields+1) + *p = '\0'; + fn--; + } + } + return(fn); + } + + /* n separators */ + fn = 0; + for (;;) { + if (fn < nfields) + *fp++ = p; + fn++; + for (;;) { + c = *p++; + if (c == '\0') + return(fn); + sepp = sep; + while ((sepc = *sepp++) != '\0' && sepc != c) + continue; + if (sepc != '\0') /* it was a separator */ + break; + } + if (fn < nfields) + *(p-1) = '\0'; + for (;;) { + c = *p++; + sepp = sep; + while ((sepc = *sepp++) != '\0' && sepc != c) + continue; + if (sepc == '\0') /* it wasn't a separator */ + break; + } + p--; + } + + /* not reached */ +} + +/* + * Given an AbsoluteTime return the English text version of the date + */ +char * +nabstimeout(AbsoluteTime time) +{ + /* + * Fri Jan 28 23:05:29 1994 PST + * 0 1 2 + * 12345678901234567890123456789 + * + * we allocate some extra -- timezones are usually 3 characters but + * this is not in the POSIX standard... + */ + char buf[40]; + char* result; + + switch (time) { + case EPOCH_ABSTIME: (void) strcpy(buf, "epoch"); break; + case INVALID_ABSTIME: (void) strcpy(buf, "Invalid Abstime"); break; + case CURRENT_ABSTIME: (void) strcpy(buf, "current"); break; + case NOEND_ABSTIME: (void) strcpy(buf, "infinity"); break; + case NOSTART_ABSTIME: (void) strcpy(buf, "-infinity"); break; + default: + /* hack -- localtime happens to work for negative times */ + (void) strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z", + localtime((time_t *) &time)); + break; + } + result = (char*)palloc(strlen(buf) + 1); + strcpy(result, buf); + return result; +} + +/* turn a (struct tm) and a few variables into a time_t, with range checking */ +AbsoluteTime +dateconv(register struct tm *tm, int zone) +{ + tm->tm_wday = tm->tm_yday = 0; + + /* validate, before going out of range on some members */ + if (tm->tm_year < 0 || tm->tm_mon < 1 || tm->tm_mon > 12 || + tm->tm_mday < 1 || tm->tm_hour < 0 || tm->tm_hour >= 24 || + tm->tm_min < 0 || tm->tm_min > 59 || + tm->tm_sec < 0 || tm->tm_sec > 59) + return -1; + + /* + * zone should really be -zone, and tz should be set to tp->value, not + * -tp->value. Or the table could be fixed. + */ + tm->tm_min += zone; /* mktime lets it be out of range */ + + /* convert to seconds */ + return qmktime(tm); +} + + +/* + * near-ANSI qmktime suitable for use by dateconv; not necessarily as paranoid + * as ANSI requires, and it may not canonicalise the struct tm. Ignores tm_wday + * and tm_yday. + */ +time_t +qmktime(struct tm *tp) +{ + register int mon = tp->tm_mon; + register int day = tp->tm_mday, year = tp->tm_year; + register time_t daynum; + time_t secondnum; + register int century; + + /* If it was a 2 digit year */ + if (year < 100) + year += 1900; + + /* + * validate day against days-per-month table, with leap-year + * correction + */ + if (day > nmdays[mon]) + if (mon != 2 || year % 4 == 0 && + (year % 100 != 0 || year % 400 == 0) && day > 29) + return -1; /* day too large for month */ + + /* split year into century and year-of-century */ + century = year / 100; + year %= 100; + /* + * We calculate the day number exactly, assuming the calendar has + * always had the current leap year rules. (The leap year rules are + * to compensate for the fact that the Earth's revolution around the + * Sun takes 365.2425 days). We first need to rotate months so March + * is 0, since we want the last month to have the reduced number of + * days. + */ + if (mon > 2) + mon -= 3; + else { + mon += 9; + if (year == 0) { + century--; + year = 99; + } else + --year; + } + daynum = -EPOCH_DAYNUM + DAYNUM(century, year, mon, day); + + /* check for time out of range */ + if (daynum < MIN_DAYNUM || daynum > MAX_DAYNUM) + return INVALID_ABSTIME; + + /* convert to seconds */ + secondnum = + tp->tm_sec + (tp->tm_min +(daynum*24 + tp->tm_hour)*60)*60; + + /* check for overflow */ + if ((daynum == MAX_DAYNUM && secondnum < 0) || + (daynum == MIN_DAYNUM && secondnum > 0)) + return INVALID_ABSTIME; + + /* check for "current", "infinity", "-infinity" */ + if (!AbsoluteTimeIsReal(secondnum)) + return INVALID_ABSTIME; + + /* daylight correction */ + if (tp->tm_isdst < 0) /* unknown; find out */ + { + struct tm *result; + + /* NT returns NULL for any time before 1/1/70 */ + result = localtime(&secondnum); + if (result == NULL) + return INVALID_ABSTIME; + else + tp->tm_isdst = result->tm_isdst; + } + if (tp->tm_isdst > 0) + secondnum -= 60*60; + + return secondnum; +} + +datetkn * +datetoktype(char *s, int *bigvalp) +{ + register char *cp = s; + register char c = *cp; + static datetkn t; + register datetkn *tp = &t; + + if (isascii(c) && isdigit(c)) { + register int len = strlen(cp); + + if (len > 3 && (cp[1] == ':' || cp[2] == ':')) + tp->type = TIME; + else { + if (bigvalp != NULL) + /* won't fit in tp->value */ + *bigvalp = atoi(cp); + if (len == 4) + tp->type = YEAR; + else if (++dtok_numparsed == 1) + tp->type = DAY; + else + tp->type = YEAR; + } + } else if (c == '-' || c == '+') { + register int val = atoi(cp + 1); + register int hr = val / 100; + register int min = val % 100; + + val = hr*60 + min; + if (c == '-') + val = -val; + tp->type = TZ; + TOVAL(tp, val); + } else { + char lowtoken[TOKMAXLEN+1]; + register char *ltp = lowtoken, *endltp = lowtoken+TOKMAXLEN; + + /* copy to lowtoken to avoid modifying s */ + while ((c = *cp++) != '\0' && ltp < endltp) + *ltp++ = (isascii(c) && isupper(c)? tolower(c): c); + *ltp = '\0'; + tp = datebsearch(lowtoken, datetktbl, szdatetktbl); + if (tp == NULL) { + tp = &t; + tp->type = PG_IGNORE; + } + } + return tp; +} + +/* + * Binary search -- from Knuth (6.2.1) Algorithm B. Special case like this + * is WAY faster than the generic bsearch(). + */ +datetkn * +datebsearch(char *key, datetkn *base, unsigned int nel) +{ + register datetkn *last = base + nel - 1, *position; + register int result; + + while (last >= base) { + position = base + ((last - base) >> 1); + result = key[0] - position->token[0]; + if (result == 0) { + result = strncmp(key, position->token, TOKMAXLEN); + if (result == 0) + return position; + } + if (result < 0) + last = position - 1; + else + base = position + 1; + } + return 0; +} + + +/* + * AbsoluteTimeIsBefore -- true iff time1 is before time2. + */ + +bool +AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2) +{ + AbsoluteTime tm = GetCurrentTransactionStartTime(); + + Assert(AbsoluteTimeIsValid(time1)); + Assert(AbsoluteTimeIsValid(time2)); + + if ((time1 == CURRENT_ABSTIME) || (time2 == CURRENT_ABSTIME)) + return false; + if (time1 == CURRENT_ABSTIME) + return (tm < time2); + if (time2 == CURRENT_ABSTIME) + return (time1 < tm); + + return (time1 < time2); +} + +bool +AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2) +{ + AbsoluteTime tm = GetCurrentTransactionStartTime(); + + Assert(AbsoluteTimeIsValid(time1)); + Assert(AbsoluteTimeIsValid(time2)); + + if ((time1 == CURRENT_ABSTIME) || (time2 == CURRENT_ABSTIME)) + return false; + if (time1 == CURRENT_ABSTIME) + return (tm > time2); + if (time2 == CURRENT_ABSTIME) + return (time1 > tm); + + return (time1 > time2); +} + + +/* + * abstimeeq - returns 1, iff arguments are equal + * abstimene - returns 1, iff arguments are not equal + * abstimelt - returns 1, iff t1 less than t2 + * abstimegt - returns 1, iff t1 greater than t2 + * abstimele - returns 1, iff t1 less than or equal to t2 + * abstimege - returns 1, iff t1 greater than or equal to t2 + * + */ +int32 +abstimeeq(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + return 0; + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + return(t1 == t2); +} + +int32 +abstimene(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + return 0; + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + return(t1 != t2); +} + +int32 +abstimelt(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + return 0; + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + return(t1 < t2); +} + +int32 +abstimegt(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + return 0; + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + return(t1 > t2); +} + +int32 +abstimele(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + return 0; + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + return(t1 <= t2); +} + +int32 +abstimege(AbsoluteTime t1, AbsoluteTime t2) +{ + if (t1 == INVALID_ABSTIME || t2 == INVALID_ABSTIME) + return 0; + if (t1 == CURRENT_ABSTIME) + t1 = GetCurrentTransactionStartTime(); + if (t2 == CURRENT_ABSTIME) + t2 = GetCurrentTransactionStartTime(); + + return(t1 >= t2); +} + + diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c new file mode 100644 index 00000000000..1031b5bb49a --- /dev/null +++ b/src/backend/utils/adt/name.c @@ -0,0 +1,198 @@ +/*------------------------------------------------------------------------- + * + * name.c-- + * Functions for the built-in type "name". + * name replaces char16 and is carefully implemented so that it + * is a string of length NAMEDATALEN. DO NOT use hard-coded constants anywhere + * always use NAMEDATALEN as the symbolic constant! - jolly 8/21/95 + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/name.c,v 1.1.1.1 1996/07/09 06:22:04 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" +#include "utils/builtins.h" /* where the declarations go */ +#include "utils/palloc.h" /* where the declarations go */ + +/***************************************************************************** + * USER I/O ROUTINES (none) * + *****************************************************************************/ + + +/* + * namein - converts "..." to internal representation + * + * Note: + * Currently if strlen(s) < NAMEDATALEN, the extra chars are nulls + */ +NameData *namein(char *s) +{ + NameData *result; + + if (s == NULL) + return(NULL); + result = (NameData*) palloc(NAMEDATALEN); + /* always keep it null-padded */ + memset(result->data, 0, NAMEDATALEN); + (void) strncpy(result->data, s, NAMEDATALEN-1); + return(result); +} + +/* + * nameout - converts internal reprsentation to "..." + */ +char *nameout(NameData *s) +{ + if (s == NULL) + return "-"; + else + return pstrdup(s->data); +} + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +/* + * nameeq - returns 1 iff arguments are equal + * namene - returns 1 iff arguments are not equal + * + * BUGS: + * Assumes that "xy\0\0a" should be equal to "xy\0b". + * If not, can do the comparison backwards for efficiency. + * + * namelt - returns 1 iff a < b + * namele - returns 1 iff a <= b + * namegt - returns 1 iff a < b + * namege - returns 1 iff a <= b + * + */ +int32 nameeq(NameData *arg1, NameData *arg2) +{ + if (!arg1 || !arg2) + return 0; + else + return (strncmp(arg1->data, arg2->data, NAMEDATALEN) == 0); +} + +int32 namene(NameData *arg1, NameData *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return(strncmp(arg1->data, arg2->data, NAMEDATALEN) != 0); +} + +int32 namelt(NameData *arg1, NameData *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) < 0)); +} + +int32 namele(NameData *arg1, NameData *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) <= 0)); +} + +int32 namegt(NameData *arg1, NameData *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + + return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) > 0)); +} + +int32 namege(NameData *arg1, NameData *arg2) +{ + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + + return((int32) (strncmp(arg1->data, arg2->data, NAMEDATALEN) >= 0)); +} + + +/* (see char.c for comparison/operation routines) */ + +int namecpy(Name n1, Name n2) +{ + if (!n1 || !n2) + return(-1); + (void) strncpy(n1->data, n2->data, NAMEDATALEN); + return(0); +} + +int namecat(Name n1, Name n2) +{ + return(namestrcat(n1, n2->data)); /* n2 can't be any longer than n1 */ +} + +int namecmp(Name n1, Name n2) +{ + return(strncmp(n1->data, n2->data, NAMEDATALEN)); +} + +int +namestrcpy(Name name, char *str) +{ + if (!name || !str) + return(-1); + memset(name->data, 0, sizeof(NameData)); + (void) strncpy(name->data, str, NAMEDATALEN); + return(0); +} + +int namestrcat(Name name, char *str) +{ + int i; + char *p, *q; + + if (!name || !str) + return(-1); + for (i = 0, p = name->data; i < NAMEDATALEN && *p; ++i, ++p) + ; + for (q = str; i < NAMEDATALEN; ++i, ++p, ++q) { + *p = *q; + if (!*q) + break; + } + return(0); +} + +int +namestrcmp(Name name, char *str) +{ + if (!name && !str) + return(0); + if (!name) + return(-1); /* NULL < anything */ + if (!str) + return(1); /* NULL < anything */ + return(strncmp(name->data, str, NAMEDATALEN)); +} + +/***************************************************************************** + * PRIVATE ROUTINES * + *****************************************************************************/ + +uint32 +NameComputeLength(Name name) +{ + char *charP; + int length; + + for (length = 0, charP = name->data; + length < NAMEDATALEN && *charP != '\0'; + length++, charP++) { + ; + } + return (uint32)length; +} diff --git a/src/backend/utils/adt/not_in.c b/src/backend/utils/adt/not_in.c new file mode 100644 index 00000000000..47c28c4eda2 --- /dev/null +++ b/src/backend/utils/adt/not_in.c @@ -0,0 +1,124 @@ +/*------------------------------------------------------------------------- + * + * not_in.c-- + * Executes the "not_in" operator for any data type + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + * X HACK WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! X + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + * + * This code is the OLD not-in code that is HACKED + * into place until operators that can have arguments as + * columns are ******REALLY****** implemented!!!!!!!!!!! + * + */ +#include <stdio.h> +#include <string.h> +#include "postgres.h" +#include "access/heapam.h" +#include "access/htup.h" +#include "access/relscan.h" +#include "utils/rel.h" +#include "utils/elog.h" +#include "utils/builtins.h" /* where function decls go */ + +/* ---------------------------------------------------------------- + * + * ---------------------------------------------------------------- + */ +bool +int4notin(int16 not_in_arg, char *relation_and_attr) +{ + Relation relation_to_scan; + int left_side_argument, integer_value; + HeapTuple current_tuple; + HeapScanDesc scan_descriptor; + bool dummy, retval; + int attrid; + char *relation, *attribute; + char my_copy[32]; + Datum value; + NameData relNameData; + ScanKeyData skeyData; + + strcpy(my_copy, relation_and_attr); + + relation = (char *) strtok(my_copy, "."); + attribute = (char *) strtok(NULL, "."); + + + /* fetch tuple OID */ + + left_side_argument = not_in_arg; + + /* Open the relation and get a relation descriptor */ + + namestrcpy(&relNameData,relation); + relation_to_scan = heap_openr(relNameData.data); + attrid = my_varattno(relation_to_scan, attribute); + + /* the last argument should be a ScanKey, not an integer! - jolly*/ + /* it looks like the arguments are out of order, too */ + /* but skeyData is never initialized! does this work?? - ay 2/95 */ + scan_descriptor = heap_beginscan(relation_to_scan, false, NULL, 0, + &skeyData); + + retval = true; + + /* do a scan of the relation, and do the check */ + for (current_tuple = heap_getnext(scan_descriptor, 0, NULL); + current_tuple != NULL && retval; + current_tuple = heap_getnext(scan_descriptor, 0, NULL)) + { + value = PointerGetDatum(heap_getattr(current_tuple, + InvalidBuffer, + (AttrNumber) attrid, + RelationGetTupleDescriptor(relation_to_scan), + &dummy)); + + integer_value = DatumGetInt16(value); + if (left_side_argument == integer_value) + { + retval = false; + } + } + + /* close the relation */ + heap_close(relation_to_scan); + return(retval); +} + +bool oidnotin(Oid the_oid, char *compare) +{ + if (the_oid == InvalidOid) + return false; + return(int4notin(the_oid, compare)); +} + +/* + * XXX + * If varattno (in parser/catalog_utils.h) ever is added to + * cinterface.a, this routine should go away + */ +int my_varattno(Relation rd, char *a) +{ + int i; + + for (i = 0; i < rd->rd_rel->relnatts; i++) { + if (!namestrcmp(&rd->rd_att->attrs[i]->attname, a)) { + return(i+1); + } + } + return(-1); +} + diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c new file mode 100644 index 00000000000..d5e07b5f268 --- /dev/null +++ b/src/backend/utils/adt/numutils.c @@ -0,0 +1,401 @@ +/*------------------------------------------------------------------------- + * + * numutils.c-- + * utility functions for I/O of built-in numeric types. + * + * integer: itoa, ltoa + * floating point: ftoa, atof1 + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/numutils.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <errno.h> +#include <math.h> +#include "postgres.h" +#include "utils/builtins.h" /* where the declarations go */ +#include "utils/elog.h" + +int32 +pg_atoi(char *s, int size, int c) +{ + long l; + char *badp = (char *) NULL; + + Assert(s); + + errno = 0; + l = strtol(s, &badp, 10); + if (errno) /* strtol must set ERANGE */ + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + if (badp && *badp && (*badp != c)) + elog(WARN, "pg_atoi: error in \"%s\": can\'t parse \"%s\"", s, badp); + + switch (size) { + case sizeof(int32): +#ifdef HAS_LONG_LONG + /* won't get ERANGE on these with 64-bit longs... */ + if (l < -0x80000000L) { + errno = ERANGE; + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + } + if (l > 0x7fffffffL) { + errno = ERANGE; + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + } +#endif /* HAS_LONG_LONG */ + break; + case sizeof(int16): + if (l < -0x8000) { + errno = ERANGE; + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + } + if (l > 0x7fff) { + errno = ERANGE; + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + } + break; + case sizeof(int8): + if (l < -0x80) { + errno = ERANGE; + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + } + if (l > 0x7f) { + errno = ERANGE; + elog(WARN, "pg_atoi: error reading \"%s\": %m", s); + } + break; + default: + elog(WARN, "pg_atoi: invalid result size: %d", size); + } + return((int32) l); +} + +/* + * itoa - converts a short int to its string represention + * + * Note: + * previously based on ~ingres/source/gutil/atoi.c + * now uses vendor's sprintf conversion + */ +void +itoa(int i, char *a) +{ + sprintf(a, "%hd", (short)i); +} + +/* + * ltoa - converts a long int to its string represention + * + * Note: + * previously based on ~ingres/source/gutil/atoi.c + * now uses vendor's sprintf conversion + */ +void +ltoa(int32 l, char *a) +{ + sprintf(a, "%d", l); +} + +/* + ** ftoa - FLOATING POINT TO ASCII CONVERSION + ** + ** CODE derived from ingres, ~ingres/source/gutil/ftoa.c + ** + ** 'Value' is converted to an ascii character string and stored + ** into 'ascii'. Ascii should have room for at least 'width' + 1 + ** characters. 'Width' is the width of the output field (max). + ** 'Prec' is the number of characters to put after the decimal + ** point. The format of the output string is controlled by + ** 'format'. + ** + ** 'Format' can be: + ** e or E: "E" format output + ** f or F: "F" format output + ** g or G: "F" format output if it will fit, otherwise + ** use "E" format. + ** n or N: same as G, but decimal points will not always + ** be aligned. + ** + ** If 'format' is upper case, the "E" comes out in upper case; + ** otherwise it comes out in lower case. + ** + ** When the field width is not big enough, it fills the field with + ** stars ("*****") and returns zero. Normal return is the width + ** of the output field (sometimes shorter than 'width'). + */ +int +ftoa(double value, char *ascii, int width, int prec1, char format) +{ +#if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi) + char out[256]; + char fmt[256]; + int ret; + + (void) sprintf(fmt, "%%%d.%d%c", width, prec1, format); + (void) sprintf(out, fmt, value); + if ((ret = strlen(out)) > width) { + memset(ascii, '*', width - 2); + ascii[width] = 0; + return(0); + } + (void) strcpy(ascii, out); + return(ret); +#else + auto int expon; + auto int sign; + register int avail; + register char *a; + register char *p; + char mode; + int lowercase; + int prec; +/* extern char *ecvt(), *fcvt();*/ + + prec = prec1; + mode = format; + lowercase = 'a' - 'A'; + if (mode >= 'a') + mode -= 'a' - 'A'; + else + lowercase = 0; + + if (mode != 'E') { + /* try 'F' style output */ + p = fcvt(value, prec, &expon, &sign); + avail = width; + a = ascii; + + /* output sign */ + if (sign) { + avail--; + *a++ = '-'; + } + + /* output '0' before the decimal point */ + if (expon <= 0) { + *a++ = '0'; + avail--; + } + + /* compute space length left after dec pt and fraction */ + avail -= prec + 1; + if (mode == 'G') + avail -= 4; + + if (avail >= expon) { + + /* it fits. output */ + while (expon > 0) { + /* output left of dp */ + expon--; + if (*p) { + *a++ = *p++; + } else + *a++ = '0'; + } + + /* output fraction (right of dec pt) */ + avail = expon; + goto frac_out; + } + /* won't fit; let's hope for G format */ + } + + if (mode != 'F') { + /* try to do E style output */ + p = ecvt(value, prec + 1, &expon, &sign); + avail = width - 5; + a = ascii; + + /* output the sign */ + if (sign) { + *a++ = '-'; + avail--; + } + } + + /* check for field too small */ + if (mode == 'F' || avail < prec) { + /* sorry joker, you lose */ + a = ascii; + for (avail = width; avail > 0; avail--) + *a++ = '*'; + *a = 0; + return (0); + } + + /* it fits; output the number */ + mode = 'E'; + + /* output the LHS single digit */ + *a++ = *p++; + expon--; + + /* output the rhs */ + avail = 1; + + frac_out: + *a++ = '.'; + while (prec > 0) { + prec--; + if (avail < 0) { + avail++; + *a++ = '0'; + } else { + if (*p) + *a++ = *p++; + else + *a++ = '0'; + } + } + + /* output the exponent */ + if (mode == 'E') { + *a++ = 'E' + lowercase; + if (expon < 0) { + *a++ = '-'; + expon = -expon; + } else + *a++ = '+'; + *a++ = (expon / 10) % 10 + '0'; + *a++ = expon % 10 + '0'; + } + + /* output spaces on the end in G format */ + if (mode == 'G') { + *a++ = ' '; + *a++ = ' '; + *a++ = ' '; + *a++ = ' '; + } + + /* finally, we can return */ + *a = 0; + avail = a - ascii; + return (avail); +#endif /* !PORTNAME_BSD44_derived */ +} + +/* + ** atof1 - ASCII TO FLOATING CONVERSION + ** + ** CODE derived from ~ingres/source/gutil/atof.c + ** + ** Converts the string 'str' to floating point and stores the + ** result into the cell pointed to by 'val'. + ** + ** The syntax which it accepts is pretty much what you would + ** expect. Basically, it is: + ** {<sp>} [+|-] {<sp>} {<digit>} [.{digit}] {<sp>} [<exp>] + ** where <exp> is "e" or "E" followed by an integer, <sp> is a + ** space character, <digit> is zero through nine, [] is zero or + ** one, and {} is zero or more. + ** + ** Parameters: + ** str -- string to convert. + ** val -- pointer to place to put the result (which + ** must be type double). + ** + ** Returns: + ** zero -- ok. + ** -1 -- syntax error. + ** +1 -- overflow (not implemented). + ** + ** Side Effects: + ** clobbers *val. + */ +int +atof1(char *str, double *val) +{ + register char *p; + double v; + double fact; + int minus; + register char c; + int expon; + register int gotmant; + + v = 0.0; + p = str; + minus = 0; + + /* skip leading blanks */ + while ((c = *p) != '\0') { + if (c != ' ') + break; + p++; + } + + /* handle possible sign */ + switch (c) { + case '-': + minus++; + + case '+': + p++; + } + + /* skip blanks after sign */ + while ((c = *p) != '\0') { + if (c != ' ') + break; + p++; + } + + /* start collecting the number to the decimal point */ + gotmant = 0; + for (;;) { + c = *p; + if (c < '0' || c > '9') + break; + v = v * 10.0 + (c - '0'); + gotmant++; + p++; + } + + /* check for fractional part */ + if (c == '.') { + fact = 1.0; + for (;;) { + c = *++p; + if (c < '0' || c > '9') + break; + fact *= 0.1; + v += (c - '0') * fact; + gotmant++; + } + } + + /* skip blanks before possible exponent */ + while ((c = *p) != '\0') { + if (c != ' ') + break; + p++; + } + + /* test for exponent */ + if (c == 'e' || c == 'E') { + p++; + expon = pg_atoi(p, sizeof(expon), '\0'); + if (!gotmant) + v = 1.0; + fact = expon; + v *= pow(10.0, fact); + } else { + /* if no exponent, then nothing */ + if (c != 0) + return (-1); + } + + /* store the result and exit */ + if (minus) + v = -v; + *val = v; + return (0); +} diff --git a/src/backend/utils/adt/oid.c b/src/backend/utils/adt/oid.c new file mode 100644 index 00000000000..d049db8b7f1 --- /dev/null +++ b/src/backend/utils/adt/oid.c @@ -0,0 +1,127 @@ +/*------------------------------------------------------------------------- + * + * oid.c-- + * Functions for the built-in type Oid. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/oid.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/builtins.h" /* where function declarations go */ +#include "utils/elog.h" + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * oid8in - converts "num num ..." to internal form + * + * Note: + * Fills any nonexistent digits with NULL oids. + */ +Oid *oid8in(char *oidString) +{ + register Oid (*result)[]; + int nums; + + if (oidString == NULL) + return(NULL); + result = (Oid (*)[]) palloc(sizeof(Oid [8])); + if ((nums = sscanf(oidString, "%d%d%d%d%d%d%d%d", + *result, + *result + 1, + *result + 2, + *result + 3, + *result + 4, + *result + 5, + *result + 6, + *result + 7)) != 8) { + do + (*result)[nums++] = 0; + while (nums < 8); + } + return((Oid *) result); +} + +/* + * oid8out - converts internal form to "num num ..." + */ +char *oid8out(Oid (*oidArray)[]) +{ + register int num; + register Oid *sp; + register char *rp; + char *result; + + if (oidArray == NULL) { + result = (char *) palloc(2); + result[0] = '-'; + result[1] = '\0'; + return(result); + } + + /* assumes sign, 10 digits, ' ' */ + rp = result = (char *) palloc(8 * 12); + sp = *oidArray; + for (num = 8; num != 0; num--) { + ltoa(*sp++, rp); + while (*++rp != '\0') + ; + *rp++ = ' '; + } + *--rp = '\0'; + return(result); +} + +Oid oidin(char *s) +{ + extern int32 int4in(); + + return(int4in(s)); +} + +char *oidout(Oid o) +{ + extern char *int4out(); + + return(int4out(o)); +} + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +int32 oideq(Oid arg1, Oid arg2) +{ + return(arg1 == arg2); +} + +int32 oidne(Oid arg1, Oid arg2) +{ + return(arg1 != arg2); +} + +int32 oid8eq(Oid arg1[], Oid arg2[]) +{ + return (int32)(memcmp(arg1, arg2, 8 * sizeof(Oid)) == 0); +} + +bool oideqint4(Oid arg1, int32 arg2) +{ +/* oid is unsigned, but int4 is signed */ + return (arg2 >= 0 && arg1 == arg2); +} + +bool int4eqoid(int32 arg1, Oid arg2) +{ +/* oid is unsigned, but int4 is signed */ + return (arg1 >= 0 && arg1 == arg2); +} + diff --git a/src/backend/utils/adt/oidint2.c b/src/backend/utils/adt/oidint2.c new file mode 100644 index 00000000000..4d92821e8d8 --- /dev/null +++ b/src/backend/utils/adt/oidint2.c @@ -0,0 +1,120 @@ +/*------------------------------------------------------------------------- + * + * oidint2.c-- + * Functions for the built-in type "oidint2". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/oidint2.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/builtins.h" /* for pg_atoi() */ +#include "utils/oidcompos.h" /* where function declarations go */ + + +OidInt2 +oidint2in(char *o) +{ + OidInt2 oi; + char *p; + + oi = (OidInt2) palloc(sizeof(OidInt2Data)); + + for (p = o; *p != '\0' && *p != '/'; p++) + continue; + + oi->oi_oid = (Oid) pg_atoi(o, sizeof(Oid), '/'); + if (*p == '\0') { + oi->oi_int2 = 0; + } else { + oi->oi_int2 = (int16) pg_atoi(++p, sizeof(int2), '\0'); + } + + return (oi); +} + +char * +oidint2out(OidInt2 o) +{ + char *r; + + /* + * -2147483647/-32767 + * 0 1 + * 1234567890123456789 + */ + r = (char *) palloc(19); + sprintf(r, "%d/%d", o->oi_oid, o->oi_int2); + + return (r); +} + +bool +oidint2lt(OidInt2 o1, OidInt2 o2) +{ + return + ((bool) (o1->oi_oid < o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int2 < o2->oi_int2))); +} + +bool +oidint2le(OidInt2 o1, OidInt2 o2) +{ + return ((bool) (o1->oi_oid < o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int2 <= o2->oi_int2))); +} + +bool +oidint2eq(OidInt2 o1, OidInt2 o2) +{ + return ((bool) (o1->oi_oid == o2->oi_oid && o1->oi_int2 == o2->oi_int2)); +} + +bool +oidint2ge(OidInt2 o1, OidInt2 o2) +{ + return ((bool) (o1->oi_oid > o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int2 >= o2->oi_int2))); +} + +bool +oidint2gt(OidInt2 o1, OidInt2 o2) +{ + return ((bool) (o1->oi_oid > o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int2 > o2->oi_int2))); +} + +bool +oidint2ne(OidInt2 o1, OidInt2 o2) +{ + return ((bool) (o1->oi_oid != o2->oi_oid || o1->oi_int2 != o2->oi_int2)); +} + +int +oidint2cmp(OidInt2 o1, OidInt2 o2) +{ + if (oidint2lt(o1, o2)) + return (-1); + else if (oidint2eq(o1, o2)) + return (0); + else + return (1); +} + +OidInt2 +mkoidint2(Oid v_oid, uint16 v_int2) +{ + OidInt2 o; + + o = (OidInt2) palloc(sizeof(OidInt2Data)); + o->oi_oid = v_oid; + o->oi_int2 = v_int2; + return (o); +} + diff --git a/src/backend/utils/adt/oidint4.c b/src/backend/utils/adt/oidint4.c new file mode 100644 index 00000000000..d0844411a73 --- /dev/null +++ b/src/backend/utils/adt/oidint4.c @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------- + * + * oidint4.c-- + * Functions for the built-in type "oidint4". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/oidint4.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/builtins.h" +#include "utils/oidcompos.h" /* where function declarations go */ + +OidInt4 oidint4in(char *o) +{ + OidInt4 oi; + char *p; + + oi = (OidInt4) palloc(sizeof(OidInt4Data)); + + for (p = o; *p != '\0' && *p != '/'; p++) + continue; + + oi->oi_oid = (Oid) pg_atoi(o, sizeof(Oid), '/'); + if (*p == '\0') { + oi->oi_int4 = 0; + } else { + oi->oi_int4 = pg_atoi(++p, sizeof(int4), '\0'); + } + + return (oi); +} + +char *oidint4out(OidInt4 o) +{ + char *r; + + /* + * -2147483647/-2147483647 + * 0 1 2 + * 123456789012345678901234 + */ + r = (char *) palloc(24); + sprintf(r, "%d/%d", o->oi_oid, o->oi_int4); + + return (r); +} + +bool oidint4lt(OidInt4 o1, OidInt4 o2) +{ + return + ((bool) (o1->oi_oid < o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int4 < o2->oi_int4))); +} + +bool oidint4le(OidInt4 o1, OidInt4 o2) +{ + return ((bool) (o1->oi_oid < o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int4 <= o2->oi_int4))); +} + +bool oidint4eq(OidInt4 o1, OidInt4 o2) +{ + return ((bool) (o1->oi_oid == o2->oi_oid && o1->oi_int4 == o2->oi_int4)); +} + +bool oidint4ge(OidInt4 o1, OidInt4 o2) +{ + return ((bool) (o1->oi_oid > o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int4 >= o2->oi_int4))); +} + +bool oidint4gt(OidInt4 o1, OidInt4 o2) +{ + return ((bool) (o1->oi_oid > o2->oi_oid || + (o1->oi_oid == o2->oi_oid && o1->oi_int4 > o2->oi_int4))); +} + +bool oidint4ne(OidInt4 o1, OidInt4 o2) +{ + return ((bool) (o1->oi_oid != o2->oi_oid || o1->oi_int4 != o2->oi_int4)); +} + +int oidint4cmp(OidInt4 o1, OidInt4 o2) +{ + if (oidint4lt(o1, o2)) + return (-1); + else if (oidint4eq(o1, o2)) + return (0); + else + return (1); +} + +OidInt4 mkoidint4(Oid v_oid, uint32 v_int4) +{ + OidInt4 o; + + o = (OidInt4) palloc(sizeof(OidInt4Data)); + o->oi_oid = v_oid; + o->oi_int4 = v_int4; + return (o); +} + + + diff --git a/src/backend/utils/adt/oidname.c b/src/backend/utils/adt/oidname.c new file mode 100644 index 00000000000..c4f42674c5f --- /dev/null +++ b/src/backend/utils/adt/oidname.c @@ -0,0 +1,123 @@ +/*------------------------------------------------------------------------- + * + * oidname.c-- + * adt for multiple key indices involving oid and name. Used for cache + * index scans (could also be used in the general case with name). + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/oidname.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> + +#include "postgres.h" +#include "utils/oidcompos.h" /* where function declarations go */ +#include "utils/builtins.h" /* for pg_atoi() */ +#include "utils/elog.h" +#include "utils/palloc.h" + +OidName +oidnamein(char *inStr) +{ + OidName oc; + char *inptr; + + oc = (OidName) palloc(sizeof(OidNameData)); + + memset(oc, 0, sizeof(OidNameData)); + for (inptr = inStr; *inptr && *inptr != ','; inptr++) + ; + + if (*inptr) { + oc->id = (Oid) pg_atoi(inStr, sizeof(Oid), ','); + /* copy one less to ensure null-padding */ + strncpy(oc->name.data,++inptr,NAMEDATALEN-1); + /* namestrcpy(&oc->name, ++inptr); */ + }else + elog(WARN, "Bad input data for type oidname"); + + return oc; +} + +char * +oidnameout(OidName oidname) +{ + char buf[30+NAMEDATALEN]; /* oidname length + oid length + some safety */ + char *res; + + sprintf(buf, "%d,%s", oidname->id, oidname->name.data); + res = pstrdup(buf); + return(res); +} + +bool +oidnamelt(OidName o1, OidName o2) +{ + return (bool) + (o1->id < o2->id || + (o1->id == o2->id && namecmp(&o1->name, &o2->name) < 0)); +} + +bool +oidnamele(OidName o1, OidName o2) +{ + return (bool) + (o1->id < o2->id || + (o1->id == o2->id && namecmp(&o1->name,&o2->name) <= 0)); +} + +bool +oidnameeq(OidName o1, OidName o2) +{ + return (bool) + (o1->id == o2->id && + (namecmp(&o1->name, &o2->name) == 0)); +} + +bool +oidnamene(OidName o1, OidName o2) +{ + return (bool) + (o1->id != o2->id || + (namecmp(&o1->name,&o2->name) != 0)); +} + +bool +oidnamege(OidName o1, OidName o2) +{ + return (bool) (o1->id > o2->id || (o1->id == o2->id && + namecmp(&o1->name, &o2->name) >= 0)); +} + +bool +oidnamegt(OidName o1, OidName o2) +{ + return (bool) (o1->id > o2->id || (o1->id == o2->id && + namecmp(&o1->name, &o2->name) > 0)); +} + +int +oidnamecmp(OidName o1, OidName o2) +{ + if (o1->id == o2->id) + return (namecmp(&o1->name,&o2->name)); + + return (o1->id < o2->id) ? -1 : 1; +} + +OidName +mkoidname(Oid id, char *name) +{ + OidName oidname; + + oidname = (OidName) palloc(sizeof(Oid)+NAMEDATALEN); + + oidname->id = id; + namestrcpy(&oidname->name,name); + return oidname; +} diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c new file mode 100644 index 00000000000..4b5a0089039 --- /dev/null +++ b/src/backend/utils/adt/regexp.c @@ -0,0 +1,343 @@ +/*------------------------------------------------------------------------- + * + * regexp.c-- + * regular expression handling code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regexp.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + * Alistair Crooks added the code for the regex caching + * agc - cached the regular expressions used - there's a good chance + * that we'll get a hit, so this saves a compile step for every + * attempted match. I haven't actually measured the speed improvement, + * but it `looks' a lot quicker visually when watching regression + * test output. + * + * agc - incorporated Keith Bostic's Berkeley regex code into + * the tree for all ports. To distinguish this regex code from any that + * is existent on a platform, I've prepended the string "pg95_" to + * the functions regcomp, regerror, regexec and regfree. + * Fixed a bug that was originally a typo by me, where `i' was used + * instead of `oldest' when compiling regular expressions - benign + * results mostly, although occasionally it bit you... + * + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" /* postgres system include file */ +#include "utils/elog.h" /* for logging postgres errors */ +#include "utils/palloc.h" +#include "utils/builtins.h" /* where the function declarations go */ + +#if defined(DISABLE_XOPEN_NLS) +#undef _XOPEN_SOURCE +#endif /* DISABLE_XOPEN_NLS */ + +#ifndef WIN32 + +#include <sys/types.h> +#include <regex.h> + +#endif /* WIN32 why is this necessary? */ + +/* this is the number of cached regular expressions held. */ +#ifndef MAX_CACHED_RES +#define MAX_CACHED_RES 32 +#endif + +/* this structure describes a cached regular expression */ +struct cached_re_str { + struct varlena *cre_text; /* pattern as a text* */ + char *cre_s; /* pattern as null-terminated string */ + int cre_type; /* compiled-type: extended,icase etc */ + regex_t cre_re; /* the compiled regular expression */ + unsigned long cre_lru; /* lru tag */ +}; + +static int rec = 0; /* # of cached re's */ +static struct cached_re_str rev[MAX_CACHED_RES]; /* cached re's */ +static unsigned long lru; /* system lru tag */ + +/* attempt to compile `re' as an re, then match it against text */ +/* cflags - flag to regcomp indicates case sensitivity */ +static int +RE_compile_and_execute(struct varlena *text_re, char *text, int cflags) +{ + int oldest; + int n; + int i; + char *re; + int regcomp_result; + + re = textout(text_re); + /* find a previously compiled regular expression */ + for (i = 0 ; i < rec ; i++) { + if (rev[i].cre_s) { + if (strcmp(rev[i].cre_s, re) == 0) { + if (rev[i].cre_type == cflags) { + rev[i].cre_lru = ++lru; + pfree(re); + return(pg95_regexec(&rev[i].cre_re, + text, 0, + (regmatch_t *) NULL, 0) == 0); + } + } + } + } + + + + /* we didn't find it - make room in the cache for it */ + if (rec == MAX_CACHED_RES) { + /* cache is full - find the oldest entry */ + for (oldest = 0, i = 1 ; i < rec ; i++) { + if (rev[i].cre_lru < rev[oldest].cre_lru) { + oldest = i; + } + } + } else { + oldest = rec++; + } + + /* if there was an old re, then de-allocate the space it used */ + if (rev[oldest].cre_s != (char *) NULL) { + for (lru = i = 0 ; i < rec ; i++) { + rev[i].cre_lru = + (rev[i].cre_lru - rev[oldest].cre_lru) / 2; + if (rev[i].cre_lru > lru) { + lru = rev[i].cre_lru; + } + } + pg95_regfree(&rev[oldest].cre_re); + /* use malloc/free for the cre_s field because the storage + has to persist across transactions */ + free(rev[oldest].cre_s); + } + + /* compile the re */ + regcomp_result = pg95_regcomp(&rev[oldest].cre_re, re, cflags); + if ( regcomp_result == 0) { + n = strlen(re); + /* use malloc/free for the cre_s field because the storage + has to persist across transactions */ + rev[oldest].cre_s = (char *) malloc(n + 1); + (void) memmove(rev[oldest].cre_s, re, n); + rev[oldest].cre_s[n] = 0; + rev[oldest].cre_text = text_re; + rev[oldest].cre_lru = ++lru; + rev[oldest].cre_type = cflags; + pfree(re); + /* agc - fixed an old typo here */ + return(pg95_regexec(&rev[oldest].cre_re, text, 0, + (regmatch_t *) NULL, 0) == 0); + } else { + char errMsg[1000]; + /* re didn't compile */ + rev[oldest].cre_s = (char *) NULL; + pg95_regerror(regcomp_result, &rev[oldest].cre_re, errMsg, + sizeof(errMsg)); + elog(WARN,"regcomp failed with error %s",errMsg); + } + + /* not reached */ + return(0); +} + + + +/* + * interface routines called by the function manager + */ + +/* + fixedlen_regexeq: + + a generic fixed length regexp routine + s - the string to match against (not necessarily null-terminated) + p - the pattern + charlen - the length of the string +*/ +static bool +fixedlen_regexeq(char *s, struct varlena* p, int charlen, int cflags) +{ + char *sterm; + int result; + + if (!s || !p) + return FALSE; + + /* be sure sterm is null-terminated */ + sterm = (char *) palloc(charlen + 1); + memset(sterm, 0, charlen + 1); + strncpy(sterm, s, charlen); + + result = RE_compile_and_execute(p, sterm, cflags); + + pfree(sterm); + + return ((bool) result); + +} + + +/* + * routines that use the regexp stuff + */ +bool +char2regexeq(uint16 arg1, struct varlena *p) +{ + char *s = (char *) &arg1; + return (fixedlen_regexeq(s, p, 2, REG_EXTENDED)); +} + +bool +char2regexne(uint16 arg1, struct varlena *p) +{ + return (!char2regexeq(arg1, p)); +} + +bool +char4regexeq(uint32 arg1, struct varlena *p) +{ + char *s = (char *) &arg1; + return (fixedlen_regexeq(s, p, 4, REG_EXTENDED)); +} + +bool +char4regexne(uint32 arg1, struct varlena *p) +{ + return (!char4regexeq(arg1, p)); +} + +bool +char8regexeq(char *s, struct varlena *p) +{ + return (fixedlen_regexeq(s, p, 8, REG_EXTENDED)); +} + +bool +char8regexne(char *s, struct varlena *p) +{ + return (!char8regexeq(s, p)); +} + +bool +char16regexeq(char *s, struct varlena *p) +{ + return (fixedlen_regexeq(s, p, 16, REG_EXTENDED)); +} + +bool +char16regexne(char *s, struct varlena *p) +{ + return (!char16regexeq(s, p)); +} + +bool +nameregexeq(NameData *n, struct varlena *p) +{ + return (fixedlen_regexeq(n->data, p, NAMEDATALEN, REG_EXTENDED)); +} +bool +nameregexne(NameData *s, struct varlena *p) +{ + return (!nameregexeq(s, p)); +} + +bool +textregexeq(struct varlena *s, struct varlena *p) +{ + return (fixedlen_regexeq(VARDATA(s), p, VARSIZE(s) - VARHDRSZ, REG_EXTENDED)); +} + +bool +textregexne(struct varlena *s, struct varlena *p) +{ + return (!textregexeq(s, p)); +} + + +/* +* routines that use the regexp stuff, but ignore the case. + * for this, we use the REG_ICASE flag to pg95_regcomp + */ +bool +char2icregexeq(uint16 arg1, struct varlena *p) +{ + char *s = (char *) &arg1; + return (fixedlen_regexeq(s, p, 2, REG_ICASE | REG_EXTENDED)); +} + + +bool +char2icregexne(uint16 arg1, struct varlena *p) +{ + return (!char2icregexeq(arg1, p)); +} + +bool +char4icregexeq(uint32 arg1, struct varlena *p) +{ + char *s = (char *) &arg1; + return (fixedlen_regexeq(s, p, 4, REG_ICASE | REG_EXTENDED )); +} + +bool +char4icregexne(uint32 arg1, struct varlena *p) +{ + return (!char4icregexeq(arg1, p)); +} + +bool +char8icregexeq(char *s, struct varlena *p) +{ + return (fixedlen_regexeq(s, p, 8, REG_ICASE | REG_EXTENDED)); +} + +bool +char8icregexne(char *s, struct varlena *p) +{ + return (!char8icregexeq(s, p)); +} + +bool +char16icregexeq(char *s, struct varlena *p) +{ + return (fixedlen_regexeq(s, p, 16, REG_ICASE | REG_EXTENDED)); +} + +bool +char16icregexne(char *s, struct varlena *p) +{ + return (!char16icregexeq(s, p)); +} + +bool +texticregexeq(struct varlena *s, struct varlena *p) +{ + return (fixedlen_regexeq(VARDATA(s), p, VARSIZE(s) - VARHDRSZ, + REG_ICASE | REG_EXTENDED)); +} + +bool +texticregexne(struct varlena *s, struct varlena *p) +{ + return (!texticregexeq(s, p)); +} + +bool +nameicregexeq(NameData *n, struct varlena *p) +{ + return (fixedlen_regexeq(n->data, p, NAMEDATALEN, + REG_ICASE | REG_EXTENDED)); +} +bool +nameicregexne(NameData *s, struct varlena *p) +{ + return (!nameicregexeq(s, p)); +} + diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c new file mode 100644 index 00000000000..071573fdccc --- /dev/null +++ b/src/backend/utils/adt/regproc.c @@ -0,0 +1,159 @@ +/*------------------------------------------------------------------------- + * + * regproc.c-- + * Functions for the built-in type "RegProcedure". + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" +#include "access/heapam.h" +#include "access/relscan.h" +#include "access/skey.h" +#include "utils/tqual.h" /* for NowTimeQual */ +#include "fmgr.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "catalog/catname.h" +#include "utils/builtins.h" /* where function declarations go */ + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * regprocin - converts "proname" to proid + * + * proid of NULL signifies unknown + */ +int32 regprocin(char *proname) +{ + Relation proc; + HeapScanDesc procscan; + HeapTuple proctup; + ScanKeyData key; + RegProcedure result; + bool isnull; + + if (proname == NULL) + return(0); + proc = heap_openr(ProcedureRelationName); + if (!RelationIsValid(proc)) { + elog(WARN, "regprocin: could not open %s", + ProcedureRelationName); + return(0); + } + ScanKeyEntryInitialize(&key, + (bits16)0, + (AttrNumber)1, + (RegProcedure)F_CHAR16EQ, + (Datum)proname); + + procscan = heap_beginscan(proc, 0, NowTimeQual, 1, &key); + if (!HeapScanIsValid(procscan)) { + heap_close(proc); + elog(WARN, "regprocin: could not being scan of %s", + ProcedureRelationName); + return(0); + } + proctup = heap_getnext(procscan, 0, (Buffer *) NULL); + switch (HeapTupleIsValid(proctup)) { + case 1: + result = (RegProcedure) heap_getattr(proctup, + InvalidBuffer, + ObjectIdAttributeNumber, + RelationGetTupleDescriptor(proc), + &isnull); + if (isnull) { + elog(FATAL, "regprocin: null procedure %s", proname); + } + break; + case 0: + result = (RegProcedure) 0; +#ifdef EBUG + elog(DEBUG, "regprocin: no such procedure %s", proname); +#endif /* defined(EBUG) */ + } + heap_endscan(procscan); + heap_close(proc); + return((int32) result); +} + +/* + * regprocout - converts proid to "proname" + */ +char *regprocout(RegProcedure proid) +{ + Relation proc; + HeapScanDesc procscan; + HeapTuple proctup; + char *result; + ScanKeyData key; + + result = (char *)palloc(NAMEDATALEN); + proc = heap_openr(ProcedureRelationName); + if (!RelationIsValid(proc)) { + elog(WARN, "regprocout: could not open %s", + ProcedureRelationName); + return(0); + } + ScanKeyEntryInitialize(&key, + (bits16)0, + (AttrNumber)ObjectIdAttributeNumber, + (RegProcedure)F_INT4EQ, + (Datum)proid); + + procscan = heap_beginscan(proc, 0, NowTimeQual, 1, &key); + if (!HeapScanIsValid(procscan)) { + heap_close(proc); + elog(WARN, "regprocin: could not being scan of %s", + ProcedureRelationName); + return(0); + } + proctup = heap_getnext(procscan, 0, (Buffer *)NULL); + switch (HeapTupleIsValid(proctup)) { + char *s; + bool isnull; + case 1: + s = (char *) heap_getattr(proctup, InvalidBuffer, 1, + RelationGetTupleDescriptor(proc), &isnull); + if (!isnull) { + strncpy(result, s, 16); + break; + } + elog(FATAL, "regprocout: null procedure %d", proid); + /*FALLTHROUGH*/ + case 0: + memset(result, 0, 16); + result[0] = '-'; +#ifdef EBUG + elog(DEBUG, "regprocout: no such procedure %d", proid); +#endif /* defined(EBUG) */ + } + heap_endscan(procscan); + heap_close(proc); + return(result); +} + + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +Oid RegprocToOid(RegProcedure rp) +{ + return (Oid)rp; +} + +/* (see int.c for comparison/operation routines) */ + + +/* ========== PRIVATE ROUTINES ========== */ + diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c new file mode 100644 index 00000000000..8ee93b768d3 --- /dev/null +++ b/src/backend/utils/adt/selfuncs.c @@ -0,0 +1,585 @@ +/*------------------------------------------------------------------------- + * + * selfuncs.c-- + * Selectivity functions for system catalogs and builtin types + * + * These routines are registered in the operator catalog in the + * "oprrest" and "oprjoin" attributes. + * + * XXX check all the functions--I suspect them to be 1-based. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> + +#include "access/heapam.h" +#include "utils/tqual.h" /* for NowTimeQual */ +#include "fmgr.h" +#include "utils/builtins.h" /* for textout() prototype + and where the declarations go */ +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "catalog/catname.h" +#include "utils/syscache.h" +#include "utils/lsyscache.h" /* for get_oprrest() */ +#include "catalog/pg_statistic.h" + + +/* N is not a valid var/constant or relation id */ +#define NONVALUE(N) ((N) == -1) + +/* + * generalize the test for functional index selectivity request + */ +#define FunctionalSelectivity(nIndKeys,attNum) (attNum==InvalidAttrNumber) + +static int32 getattnvals(Oid relid, AttrNumber attnum); +static void gethilokey(Oid relid, AttrNumber attnum, Oid opid, + char **high, char **low); + + +/* + * eqsel - Selectivity of "=" for any data type. + */ +float64 +eqsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + int32 nvals; + float64 result; + + result = (float64) palloc(sizeof(float64data)); + if (NONVALUE(attno) || NONVALUE(relid)) + *result = 0.1; + else { + nvals = getattnvals(relid, (int) attno); + if (nvals == 0) + *result = 0.0; + else + *result = 1.0 / nvals; + } + return(result); +} + +/* + * neqsel - Selectivity of "!=" for any data type. + */ +float64 +neqsel(Oid opid, + Oid relid, + AttrNumber attno, + char *value, + int32 flag) +{ + float64 result; + + result = eqsel(opid, relid, attno, value, flag); + *result = 1.0 - *result; + return(result); +} + +/* + * intltsel - Selectivity of "<" for integers. + * Should work for both longs and shorts. + */ +float64 +intltsel(Oid opid, + Oid relid, + AttrNumber attno, + int32 value, + int32 flag) +{ + float64 result; + char *highchar, *lowchar; + long val, high, low, top, bottom; + + result = (float64) palloc(sizeof(float64data)); + if (NONVALUE(attno) || NONVALUE(relid)) + *result = 1.0 / 3; + else { + /* XXX val = atol(value);*/ + val = value; + gethilokey(relid, (int) attno, opid, &highchar, &lowchar); + if (*highchar == 'n' || *lowchar == 'n') { + *result = 1.0/3.0; + return (result); + } + high = atol(highchar); + low = atol(lowchar); + if ((flag & SEL_RIGHT && val < low) || + (!(flag & SEL_RIGHT) && val > high)) { + int nvals; + nvals = getattnvals(relid, (int) attno); + if (nvals == 0) + *result = 1.0 / 3.0; + else + *result = 3.0 / nvals; + }else { + bottom = high - low; + if (bottom == 0) + ++bottom; + if (flag & SEL_RIGHT) + top = val - low; + else + top = high - val; + if (top > bottom) + *result = 1.0; + else { + if (top == 0) + ++top; + *result = ((1.0 * top) / bottom); + } + } + } + return(result); +} + +/* + * intgtsel - Selectivity of ">" for integers. + * Should work for both longs and shorts. + */ +float64 +intgtsel(Oid opid, + Oid relid, + AttrNumber attno, + int32 value, + int32 flag) +{ + float64 result; + int notflag; + + if (flag & 0) + notflag = flag & ~SEL_RIGHT; + else + notflag = flag | SEL_RIGHT; + result = intltsel(opid, relid, attno, value, (int32) notflag); + return(result); +} + +/* + * eqjoinsel - Join selectivity of "=" + */ +float64 +eqjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + int32 num1, num2, max; + + result = (float64) palloc(sizeof(float64data)); + if (NONVALUE(attno1) || NONVALUE(relid1) || + NONVALUE(attno2) || NONVALUE(relid2)) + *result = 0.1; + else { + num1 = getattnvals(relid1, (int) attno1); + num2 = getattnvals(relid2, (int) attno2); + max = (num1 > num2) ? num1 : num2; + if (max == 0) + *result = 1.0; + else + *result = 1.0 / max; + } + return(result); +} + +/* + * neqjoinsel - Join selectivity of "!=" + */ +float64 +neqjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + + result = eqjoinsel(opid, relid1, attno1, relid2, attno2); + *result = 1.0 - *result; + return(result); +} + +/* + * intltjoinsel - Join selectivity of "<" + */ +float64 +intltjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 3.0; + return(result); +} + +/* + * intgtjoinsel - Join selectivity of ">" + */ +float64 +intgtjoinsel(Oid opid, + Oid relid1, + AttrNumber attno1, + Oid relid2, + AttrNumber attno2) +{ + float64 result; + + result = (float64) palloc(sizeof(float64data)); + *result = 1.0 / 3.0; + return(result); +} + +/* + * getattnvals - Retrieves the number of values within an attribute. + * + * Note: + * getattnvals and gethilokey both currently use keyed + * relation scans and amgetattr. Alternatively, + * the relation scan could be non-keyed and the tuple + * returned could be cast (struct X *) tuple + tuple->t_hoff. + * The first method is good for testing the implementation, + * but the second may ultimately be faster?!? In any case, + * using the cast instead of amgetattr would be + * more efficient. However, the cast will not work + * for gethilokey which accesses stahikey in struct statistic. + */ +static int32 +getattnvals(Oid relid, AttrNumber attnum) +{ + HeapTuple atp; + int nvals; + + atp = SearchSysCacheTuple(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum), + 0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "getattnvals: no attribute tuple %d %d", + relid, attnum); + return(0); + } + nvals = ((AttributeTupleForm ) GETSTRUCT(atp))->attnvals; + if (nvals > 0) return(nvals); + + atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(relid), + 0,0,0); + /* XXX -- use number of tuples as number of distinctive values + just for now, in case number of distinctive values is + not cached */ + if (!HeapTupleIsValid(atp)) { + elog(WARN, "getattnvals: no relation tuple %d", relid); + return(0); + } + nvals = ((Form_pg_class) GETSTRUCT(atp))->reltuples; + return(nvals); +} + +/* + * gethilokey - Returns a pointer to strings containing + * the high and low keys within an attribute. + * + * Currently returns "0", and "0" in high and low if the statistic + * catalog does not contain the proper tuple. Eventually, the + * statistic demon should have the tuple maintained, and it should + * elog() if the tuple is missing. + * + * XXX Question: is this worth sticking in the catalog caches, + * or will this get invalidated too often? + */ +static void +gethilokey(Oid relid, + AttrNumber attnum, + Oid opid, + char **high, + char **low) +{ + register Relation rdesc; + register HeapScanDesc sdesc; + static ScanKeyData key[3] = { + { 0, Anum_pg_statistic_starelid, F_OIDEQ }, + { 0, Anum_pg_statistic_staattnum, F_INT2EQ }, + { 0, Anum_pg_statistic_staop, F_OIDEQ } + }; + bool isnull; + HeapTuple tuple; + + rdesc = heap_openr(StatisticRelationName); + + key[0].sk_argument = ObjectIdGetDatum(relid); + key[1].sk_argument = Int16GetDatum((int16) attnum); + key[2].sk_argument = ObjectIdGetDatum(opid); + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 3, key); + tuple = heap_getnext(sdesc, 0, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) { + *high = "n"; + *low = "n"; + /* XXX elog(WARN, "gethilokey: statistic tuple not found");*/ + return; + } + *high = textout((struct varlena *) + heap_getattr(tuple, + InvalidBuffer, + Anum_pg_statistic_stahikey, + RelationGetTupleDescriptor(rdesc), + &isnull)); + if (isnull) + elog(DEBUG, "gethilokey: high key is null"); + *low = textout((struct varlena *) + heap_getattr(tuple, + InvalidBuffer, + Anum_pg_statistic_stalokey, + RelationGetTupleDescriptor(rdesc), + &isnull)); + if (isnull) + elog(DEBUG, "gethilokey: low key is null"); + heap_endscan(sdesc); + heap_close(rdesc); +} + +float64 +btreesel(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + float64 result; + float64data resultData; + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now simply assume it's + * 1/3 since functions don't currently + * have selectivity functions + */ + resultData = 1.0 / 3.0; + result = &resultData; + } + else { + result = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + + if (!PointerIsValid(result)) + elog(WARN, "Btree Selectivity: bad pointer"); + if (*result < 0.0 || *result > 1.0) + elog(WARN, "Btree Selectivity: bad value %lf", *result); + + return(result); +} + +float64 +btreenpage(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + float64 temp, result; + float64data tempData; + HeapTuple atp; + int npage; + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now simply assume it's + * 1/3 since functions don't currently + * have selectivity functions + */ + tempData = 1.0 / 3.0; + temp = &tempData; + } + else { + temp = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + atp = SearchSysCacheTuple(RELOID, + ObjectIdGetDatum(indexrelid), + 0,0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "btreenpage: no index tuple %d", indexrelid); + return(0); + } + + npage = ((Form_pg_class) GETSTRUCT(atp))->relpages; + result = (float64)palloc(sizeof(float64data)); + *result = *temp * npage; + return(result); +} + +float64 +hashsel(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + + float64 result; + float64data resultData; + HeapTuple atp; + int ntuples; + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now simply use 1/Number of Tuples + * since functions don't currently + * have selectivity functions + */ + + atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(indexrelid), + 0,0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "hashsel: no index tuple %d", indexrelid); + return(0); + } + ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples; + if (ntuples > 0) { + resultData = 1.0 / (float64data) ntuples; + } + else { + resultData = (float64data) (1.0 / 100.0); + } + result = &resultData; + + } + else { + result = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + + if (!PointerIsValid(result)) + elog(WARN, "Hash Table Selectivity: bad pointer"); + if (*result < 0.0 || *result > 1.0) + elog(WARN, "Hash Table Selectivity: bad value %lf", *result); + + return(result); + + +} + +float64 +hashnpage(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + float64 temp, result; + float64data tempData; + HeapTuple atp; + int npage; + int ntuples; + + atp = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(indexrelid), + 0,0,0); + if (!HeapTupleIsValid(atp)) { + elog(WARN, "hashsel: no index tuple %d", indexrelid); + return(0); + } + + + if (FunctionalSelectivity(nIndexKeys, attributeNumber)) { + /* + * Need to call the functions selectivity + * function here. For now, use 1/Number of Tuples + * since functions don't currently + * have selectivity functions + */ + + ntuples = ((Form_pg_class) GETSTRUCT(atp))->reltuples; + if (ntuples > 0) { + tempData = 1.0 / (float64data) ntuples; + } + else { + tempData = (float64data) (1.0 / 100.0); + } + temp = &tempData; + + } + else { + temp = (float64)fmgr(get_oprrest (operatorObjectId), + (char*)operatorObjectId, + (char*)indrelid, + (char*)attributeNumber, + (char*)constValue, + (char*)constFlag, + NULL); + } + + npage = ((Form_pg_class) GETSTRUCT(atp))->relpages; + result = (float64)palloc(sizeof(float64data)); + *result = *temp * npage; + return(result); +} + + +float64 +rtsel(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + return (btreesel(operatorObjectId, indrelid, attributeNumber, + constValue, constFlag, nIndexKeys, indexrelid)); +} + +float64 +rtnpage(Oid operatorObjectId, + Oid indrelid, + AttrNumber attributeNumber, + char *constValue, + int32 constFlag, + int32 nIndexKeys, + Oid indexrelid) +{ + return (btreenpage(operatorObjectId, indrelid, attributeNumber, + constValue, constFlag, nIndexKeys, indexrelid)); +} diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c new file mode 100644 index 00000000000..9ad8d457820 --- /dev/null +++ b/src/backend/utils/adt/sets.c @@ -0,0 +1,164 @@ +/*------------------------------------------------------------------------- + * + * sets.c-- + * Functions for sets, which are defined by queries. + * Example: a set is defined as being the result of the query + * retrieve (X.all) + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include "postgres.h" +#include "utils/elog.h" +#include "nodes/pg_list.h" /* for LispValue and List */ +#include "access/htup.h" /* for HeapTuple */ +#include "access/heapam.h" +#include "access/relscan.h" +#include "access/xact.h" +#include "catalog/pg_proc.h" /* for Form_pg_proc */ +#include "utils/syscache.h" /* for PROOID */ +#include "catalog/catname.h" /* for ProcedureRelationName */ +#include "catalog/indexing.h" /* for Num_pg_proc_indices */ +#include "storage/lmgr.h" +#include "utils/sets.h" /* for GENERICSETNAME */ +#include "tcop/dest.h" +#include "fmgr.h" + +extern CommandDest whereToSendOutput; /* defined in tcop/postgres.c */ + + +/* + * SetDefine - converts query string defining set to an oid + * + * The query string is used to store the set as a function in + * pg_proc. The name of the function is then changed to use the + * OID of its tuple in pg_proc. + */ +Oid +SetDefine(char *querystr, char *typename) +{ + Oid setoid; + char *procname = GENERICSETNAME; + char *fileName = "-"; + char realprocname[16]; + HeapTuple tup, newtup; + Form_pg_proc proc; + Relation procrel; + int i; + Datum replValue[Natts_pg_proc]; + char replNull[Natts_pg_proc]; + char repl[Natts_pg_proc]; + HeapScanDesc pg_proc_scan; + Buffer buffer; + ItemPointerData ipdata; + + static ScanKeyData oidKey[1] = { + { 0, ObjectIdAttributeNumber, ObjectIdEqualRegProcedure }}; + + + setoid = ProcedureCreate(procname, /* changed below, after oid known */ + true, /* returnsSet */ + typename, /* returnTypeName */ + "sql", /* languageName */ + querystr, /* sourceCode */ + fileName, /* fileName */ + false, /* canCache */ + true, /* trusted */ + 100, /* byte_pct */ + 0, /* perbyte_cpu */ + 0, /* percall_cpu */ + 100, /* outin_ratio */ + NIL, /* argList */ + whereToSendOutput); + /* Since we're still inside this command of the transaction, we can't + * see the results of the procedure definition unless we pretend + * we've started the next command. (Postgres's solution to the + * Halloween problem is to not allow you to see the results of your + * command until you start the next command.) + */ + CommandCounterIncrement(); + tup = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(setoid), + 0,0,0); + if (!HeapTupleIsValid(tup)) + elog(WARN, "setin: unable to define set %s", querystr); + + /* We can tell whether the set was already defined by checking + * the name. If it's GENERICSETNAME, the set is new. If it's + * "set<some oid>" it's already defined. + */ + proc = (Form_pg_proc)GETSTRUCT(tup); + if (!strcmp((char*)procname, (char*)&(proc->proname))) { + /* make the real proc name */ + sprintf(realprocname, "set%u", setoid); + + /* set up the attributes to be modified or kept the same */ + repl[0] = 'r'; + for (i = 1; i < Natts_pg_proc; i++) repl[i] = ' '; + replValue[0] = (Datum)realprocname; + for (i = 1; i < Natts_pg_proc; i++) replValue[i] = (Datum)0; + for (i = 0; i < Natts_pg_proc; i++) replNull[i] = ' '; + + /* change the pg_proc tuple */ + procrel = heap_openr(ProcedureRelationName); + RelationSetLockForWrite(procrel); + fmgr_info(ObjectIdEqualRegProcedure, + &oidKey[0].sk_func, + &oidKey[0].sk_nargs); + oidKey[0].sk_argument = ObjectIdGetDatum(setoid); + pg_proc_scan = heap_beginscan(procrel, + 0, + SelfTimeQual, + 1, + oidKey); + tup = heap_getnext(pg_proc_scan, 0, &buffer); + if (HeapTupleIsValid(tup)) { + newtup = heap_modifytuple(tup, + buffer, + procrel, + replValue, + replNull, + repl); + + /* XXX may not be necessary */ + ItemPointerCopy(&tup->t_ctid, &ipdata); + + setheapoverride(true); + (void) heap_replace(procrel, &ipdata, newtup); + setheapoverride(false); + + setoid = newtup->t_oid; + } else + elog(WARN, "setin: could not find new set oid tuple"); + heap_endscan(pg_proc_scan); + + if (RelationGetRelationTupleForm(procrel)->relhasindex) + { + Relation idescs[Num_pg_proc_indices]; + + CatalogOpenIndices(Num_pg_proc_indices, Name_pg_proc_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_proc_indices, procrel, newtup); + CatalogCloseIndices(Num_pg_proc_indices, idescs); + } + RelationUnsetLockForWrite(procrel); + heap_close(procrel); + } + return setoid; +} + +/* This function is a placeholder. The parser uses the OID of this + * function to fill in the :funcid field of a set. This routine is + * never executed. At runtime, the OID of the actual set is substituted + * into the :funcid. + */ +int +seteval(Oid funcoid) +{ + return 17; +} diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c new file mode 100644 index 00000000000..6690b74c023 --- /dev/null +++ b/src/backend/utils/adt/tid.c @@ -0,0 +1,92 @@ +/*------------------------------------------------------------------------- + * + * tid.c-- + * Functions for the built-in type tuple id + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + * NOTES + * input routine largely stolen from boxin(). + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> +#include "postgres.h" +#include "storage/block.h" +#include "storage/off.h" +#include "storage/itemptr.h" +#include "storage/bufpage.h" + +#include "utils/palloc.h" +#include "utils/builtins.h" /* where function declarations go */ + + +#define LDELIM '(' +#define RDELIM ')' +#define DELIM ',' +#define NTIDARGS 2 + +/* ---------------------------------------------------------------- + * tidin + * ---------------------------------------------------------------- + */ +ItemPointer +tidin(char *str) +{ + char *p, *coord[NTIDARGS]; + int i; + ItemPointer result; + + BlockNumber blockNumber; + OffsetNumber offsetNumber; + + if (str == NULL) + return NULL; + + for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++) + if (*p == DELIM || (*p == LDELIM && !i)) + coord[i++] = p + 1; + + if (i < NTIDARGS - 1) + return NULL; + + blockNumber = (BlockNumber) atoi(coord[0]); + offsetNumber = (OffsetNumber) atoi(coord[1]); + + result = (ItemPointer) palloc(sizeof(ItemPointerData)); + + ItemPointerSet(result, blockNumber, offsetNumber); + + return result; +} + +/* ---------------------------------------------------------------- + * tidout + * ---------------------------------------------------------------- + */ +char * +tidout(ItemPointer itemPtr) +{ + BlockNumber blockNumber; + OffsetNumber offsetNumber; + BlockId blockId; + char buf[32]; + char *str; + + blockId = &(itemPtr->ip_blkid); + + blockNumber = BlockIdGetBlockNumber(blockId); + offsetNumber = itemPtr->ip_posid; + + sprintf(buf, "(%d,%d)", blockNumber, offsetNumber); + + str = (char *) palloc(strlen(buf)+1); + strcpy(str, buf); + + return str; +} diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c new file mode 100644 index 00000000000..341ad94f009 --- /dev/null +++ b/src/backend/utils/adt/varchar.c @@ -0,0 +1,496 @@ +/*------------------------------------------------------------------------- + * + * char.c-- + * Functions for the built-in type char() and varchar(). + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varchar.c,v 1.1.1.1 1996/07/09 06:22:05 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> +#include "postgres.h" +#include "utils/palloc.h" +#include "utils/elog.h" + +/* + * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR() + * is for blank-padded string whose length is specified in CREATE TABLE. + * VARCHAR is for storing string whose length is at most the length specified + * at CREATE TABLE time. + * + * It's hard to implement these types because we cannot figure out what + * the length of the type from the type itself. I change (hopefully all) the + * fmgr calls that invoke input functions of a data type to supply the + * length also. (eg. in INSERTs, we have the tupleDescriptor which contains + * the length of the attributes and hence the exact length of the char() or + * varchar(). We pass this to bpcharin() or varcharin().) In the case where + * we cannot determine the length, we pass in -1 instead and the input string + * must be null-terminated. + * + * We actually implement this as a varlena so that we don't have to pass in + * the length for the comparison functions. (The difference between "text" + * is that we truncate and possibly blank-pad the string at insertion time.) + * + * - ay 6/95 + */ + + +/***************************************************************************** + * bpchar - char() * + *****************************************************************************/ + +/* + * bpcharin - + * converts a string of char() type to the internal representation. + * len is the length specified in () plus 4 bytes. (XXX dummy is here + * because we pass typelem as the second argument for array_in.) + */ +char * +bpcharin(char *s, int dummy, int typlen) +{ + char *result, *r; + int len = typlen - 4; + int i; + + if (s == NULL) + return((char *) NULL); + + if (typlen == -1) { + /* + * this is here because some functions can't supply the typlen + */ + len = strlen(s); + typlen = len + 4; + } + + if (len < 1 || len > 4096) + elog(WARN, "bpcharin: length of char() must be between 1 and 4096"); + + result = (char *) palloc(typlen); + *(int32*)result = typlen; + r = result + 4; + for(i=0; i < len; i++, r++, s++) { + *r = *s; + if (*r == '\0') + break; + } + /* blank pad the string if necessary */ + for(; i < len; i++) { + *r++ = ' '; + } + return(result); +} + +char * +bpcharout(char *s) +{ + char *result; + int len; + + if (s == NULL) { + result = (char *) palloc(2); + result[0] = '-'; + result[1] = '\0'; + } else { + len = *(int32*)s - 4; + result = (char *) palloc(len+1); + strncpy(result, s+4, len); /* these are blank-padded */ + result[len] = '\0'; + } + return(result); +} + +/***************************************************************************** + * varchar - varchar() * + *****************************************************************************/ + +/* + * vcharin - + * converts a string of varchar() type to the internal representation. + * len is the length specified in () plus 4 bytes. (XXX dummy is here + * because we pass typelem as the second argument for array_in.) + */ +char * +varcharin(char *s, int dummy, int typlen) +{ + char *result; + int len = typlen - 4; + + if (s == NULL) + return((char *) NULL); + + if (typlen == -1) { + /* + * this is here because some functions can't supply the typlen + */ + len = strlen(s); + typlen = len + 4; + } + + if (len < 1 || len > 4096) + elog(WARN, "bpcharin: length of char() must be between 1 and 4096"); + + result = (char *) palloc(typlen); + *(int32*)result = typlen; + memset(result+4, 0, len); + (void) strncpy(result+4, s, len); + + return(result); +} + +char * +varcharout(char *s) +{ + char *result; + int len; + + if (s == NULL) { + result = (char *) palloc(2); + result[0] = '-'; + result[1] = '\0'; + } else { + len = *(int32*)s - 4; + result = (char *) palloc(len+1); + memset(result, 0, len+1); + strncpy(result, s+4, len); + } + return(result); +} + +/***************************************************************************** + * Comparison Functions used for bpchar + *****************************************************************************/ + +static int +bcTruelen(char *arg) +{ + char *s = arg + 4; + int i; + int len; + + len = *(int32*)arg - 4; + for(i=len-1; i >= 0; i--) { + if (s[i] != ' ') + break; + } + return (i+1); +} + +int32 +bpchareq(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + if (len1!=len2) + return 0; + + return(strncmp(arg1+4, arg2+4, len1) == 0); +} + +int32 +bpcharne(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + if (len1!=len2) + return 1; + + return(strncmp(arg1+4, arg2+4, len1) != 0); +} + +int32 +bpcharlt(char *arg1, char *arg2) +{ + int len1, len2; + int cmp; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + cmp = strncmp(arg1+4, arg2+4, Min(len1,len2)); + if (cmp == 0) + return (len1<len2); + else + return (cmp < 0); +} + +int32 +bpcharle(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + return(strncmp(arg1+4, arg2+4, Min(len1,len2)) <= 0); +} + +int32 +bpchargt(char *arg1, char *arg2) +{ + int len1, len2; + int cmp; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + cmp = strncmp(arg1+4, arg2+4, Min(len1,len2)); + if (cmp == 0) + return (len1 > len2); + else + return (cmp > 0); +} + +int32 +bpcharge(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + return(strncmp(arg1+4, arg2+4, Min(len1,len2)) >= 0); +} + +int32 +bpcharcmp(char *arg1, char *arg2) +{ + int len1, len2; + + len1 = bcTruelen(arg1); + len2 = bcTruelen(arg2); + + return(strncmp(arg1+4, arg2+4, Min(len1,len2))); +} + +/***************************************************************************** + * Comparison Functions used for varchar + *****************************************************************************/ + +static int +vcTruelen(char *arg) +{ + char *s = arg + 4; + int i; + int len; + + len = *(int32*)arg - 4; + for(i=0; i < len; i++) { + if (*s++ == '\0') + break; + } + return i; +} + +int32 +varchareq(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + if (len1!=len2) + return 0; + + return(strncmp(arg1+4, arg2+4, len1) == 0); +} + +int32 +varcharne(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + if (len1!=len2) + return 1; + + return(strncmp(arg1+4, arg2+4, len1) != 0); +} + +int32 +varcharlt(char *arg1, char *arg2) +{ + int len1, len2; + int cmp; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + cmp = strncmp(arg1+4, arg2+4, Min(len1,len2)); + if (cmp == 0) + return (len1<len2); + else + return (cmp < 0); +} + +int32 +varcharle(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + return(strncmp(arg1+4, arg2+4, Min(len1,len2)) <= 0); +} + +int32 +varchargt(char *arg1, char *arg2) +{ + int len1, len2; + int cmp; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + cmp = strncmp(arg1+4, arg2+4, Min(len1,len2)); + if (cmp == 0) + return (len1 > len2); + else + return (cmp > 0); +} + +int32 +varcharge(char *arg1, char *arg2) +{ + int len1, len2; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + return(strncmp(arg1+4, arg2+4, Min(len1,len2)) >= 0); +} + +int32 +varcharcmp(char *arg1, char *arg2) +{ + int len1, len2; + + len1 = vcTruelen(arg1); + len2 = vcTruelen(arg2); + + return(strncmp(arg1+4, arg2+4, Min(len1,len2))); +} + +/***************************************************************************** + * Hash functions (modified from hashtext in access/hash/hashfunc.c) + *****************************************************************************/ + +uint32 hashbpchar(struct varlena *key) +{ + int keylen; + char *keydata; + uint32 n; + int loop; + + keydata = VARDATA(key); + keylen = bcTruelen((char*)key); + +#define HASHC n = *keydata++ + 65599 * n + + n = 0; + if (keylen > 0) { + loop = (keylen + 8 - 1) >> 3; + + switch (keylen & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } while (--loop); + } + } + return (n); +} + +uint32 hashvarchar(struct varlena *key) +{ + int keylen; + char *keydata; + uint32 n; + int loop; + + keydata = VARDATA(key); + keylen = vcTruelen((char*)key); + +#define HASHC n = *keydata++ + 65599 * n + + n = 0; + if (keylen > 0) { + loop = (keylen + 8 - 1) >> 3; + + switch (keylen & (8 - 1)) { + case 0: + do { /* All fall throughs */ + HASHC; + case 7: + HASHC; + case 6: + HASHC; + case 5: + HASHC; + case 4: + HASHC; + case 3: + HASHC; + case 2: + HASHC; + case 1: + HASHC; + } while (--loop); + } + } + return (n); +} + diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c new file mode 100644 index 00000000000..1ef6f19113e --- /dev/null +++ b/src/backend/utils/adt/varlena.c @@ -0,0 +1,488 @@ +/*------------------------------------------------------------------------- + * + * varlena.c-- + * Functions for the variable-length built-in types. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <ctype.h> +#include <string.h> + +#include "postgres.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/builtins.h" /* where function declarations go */ + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + + +#define VAL(CH) ((CH) - '0') +#define DIG(VAL) ((VAL) + '0') + +/* + * byteain - converts from printable representation of byte array + * + * Non-printable characters must be passed as '\nnn' (octal) and are + * converted to internal form. '\' must be passed as '\\'. + * elog(WARN, ...) if bad form. + * + * BUGS: + * The input is scaned twice. + * The error checking of input is minimal. + */ +struct varlena * +byteain(char *inputText) +{ + register char *tp; + register char *rp; + register int byte; + struct varlena *result; + + if (inputText == NULL) + elog(WARN, "Bad input string for type bytea"); + + for (byte = 0, tp = inputText; *tp != '\0'; byte++) + if (*tp++ == '\\') + { + if (*tp == '\\') + tp++; + else if (!isdigit(*tp++) || + !isdigit(*tp++) || + !isdigit(*tp++)) + elog(WARN, "Bad input string for type bytea"); + } + tp = inputText; + byte += sizeof(int32); /* varlena? */ + result = (struct varlena *) palloc(byte); + result->vl_len = byte; /* varlena? */ + rp = result->vl_dat; + while (*tp != '\0') + if (*tp != '\\' || *++tp == '\\') + *rp++ = *tp++; + else { + byte = VAL(*tp++); + byte <<= 3; + byte += VAL(*tp++); + byte <<= 3; + *rp++ = byte + VAL(*tp++); + } + return(result); +} + +/* + * Shoves a bunch of memory pointed at by bytes into varlena. + * BUGS: Extremely unportable as things shoved can be string + * representations of structs, etc. + */ +struct varlena * +shove_bytes(unsigned char *stuff, int len) +{ + struct varlena *result; + + result = (struct varlena *) palloc(len + sizeof(int32)); + result->vl_len = len; + memmove(result->vl_dat, + stuff + sizeof(int32), + len - sizeof(int32)); + return(result); +} + + + +/* + * byteaout - converts to printable representation of byte array + * + * Non-printable characters are inserted as '\nnn' (octal) and '\' as + * '\\'. + * + * NULL vlena should be an error--returning string with NULL for now. + */ +char * +byteaout(struct varlena *vlena) +{ + register char *vp; + register char *rp; + register int val; /* holds unprintable chars */ + int i; + int len; + static char *result; + + if (vlena == NULL) { + result = (char *) palloc(2); + result[0] = '-'; + result[1] = '\0'; + return(result); + } + vp = vlena->vl_dat; + len = 1; /* empty string has 1 char */ + for (i = vlena->vl_len - sizeof(int32); i != 0; i--, vp++) /* varlena? */ + if (*vp == '\\') + len += 2; + else if (isascii(*vp) && isprint(*vp)) + len++; + else + len += 4; + rp = result = (char *) palloc(len); + vp = vlena->vl_dat; + for (i = vlena->vl_len - sizeof(int32); i != 0; i--) /* varlena? */ + if (*vp == '\\') { + *vp++; + *rp++ = '\\'; + *rp++ = '\\'; + } else if (isascii(*vp) && isprint(*vp)) + *rp++ = *vp++; + else { + val = *vp++; + *rp = '\\'; + rp += 3; + *rp-- = DIG(val & 07); + val >>= 3; + *rp-- = DIG(val & 07); + val >>= 3; + *rp = DIG(val & 03); + rp += 3; + } + *rp = '\0'; + return(result); +} + + +/* + * textin - converts "..." to internal representation + */ +struct varlena * +textin(char *inputText) +{ + struct varlena *result; + int len; + + if (inputText == NULL) + return(NULL); + len = strlen(inputText) + VARHDRSZ; + result = (struct varlena *) palloc(len); + VARSIZE(result) = len; + memmove(VARDATA(result), inputText, len - VARHDRSZ); + return(result); +} + +/* + * textout - converts internal representation to "..." + */ +char * +textout(struct varlena *vlena) +{ + int len; + char *result; + + if (vlena == NULL) { + result = (char *) palloc(2); + result[0] = '-'; + result[1] = '\0'; + return(result); + } + len = VARSIZE(vlena) - VARHDRSZ; + result = (char *) palloc(len + 1); + memmove(result, VARDATA(vlena), len); + result[len] = '\0'; + return(result); +} + + +/* ========== PUBLIC ROUTINES ========== */ + +/* + * textcat - + * takes two text* and returns a text* that is the concatentation of + * the two + */ +text* +textcat(text* t1, text* t2) +{ + int newlen; + char *str1, *str2; + text* result; + + if (t1 == NULL) return t2; + if (t2 == NULL) return t1; + + /* since t1, and t2 are non-null, str1 and str2 must also be non-null */ + str1 = textout(t1); + str2 = textout(t2); + /* we use strlen here to calculate the length because the size fields + of t1, t2 may be longer than necessary to hold the string */ + newlen = strlen(str1) + strlen(str2) + VARHDRSZ; + result = (text*)palloc(newlen); + strcpy(VARDATA(result), str1); + strncat(VARDATA(result), str2, newlen - VARHDRSZ); + /* [TRH] Was: + strcat(VARDATA(result), str2); + which may corrupt the malloc arena due to writing trailing \0. */ + + pfree(str1); + pfree(str2); + return result; +} + +/* + * texteq - returns 1 iff arguments are equal + * textne - returns 1 iff arguments are not equal + */ +int32 +texteq(struct varlena *arg1, struct varlena *arg2) +{ + register int len; + register char *a1p, *a2p; + + if (arg1 == NULL || arg2 == NULL) + return((int32) NULL); + if ((len = arg1->vl_len) != arg2->vl_len) + return((int32) 0); + a1p = arg1->vl_dat; + a2p = arg2->vl_dat; + /* + * Varlenas are stored as the total size (data + size variable) + * followed by the data. The size variable is an int32 so the + * length of the data is the total length less sizeof(int32) + */ + len -= sizeof(int32); + while (len-- != 0) + if (*a1p++ != *a2p++) + return((int32) 0); + return((int32) 1); +} + +int32 +textne(struct varlena *arg1, struct varlena *arg2) +{ + return((int32) !texteq(arg1, arg2)); +} + +int32 +text_lt(struct varlena *arg1, struct varlena *arg2) +{ + int len; + char *a1p, *a2p; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + + a1p = VARDATA(arg1); + a2p = VARDATA(arg2); + + if ((len = arg1->vl_len) > arg2->vl_len) + len = arg2->vl_len; + len -= sizeof(int32); + + while (len != 0 && *a1p == *a2p) + { + a1p++; + a2p++; + len--; + } + if (len) + return (int32) (*a1p < *a2p); + else + return (int32) (arg1->vl_len < arg2->vl_len); +} + +int32 +text_le(struct varlena *arg1, struct varlena *arg2) +{ + int len; + char *a1p, *a2p; + + if (arg1 == NULL || arg2 == NULL) + return((int32) 0); + + a1p = VARDATA(arg1); + a2p = VARDATA(arg2); + + if ((len = arg1->vl_len) > arg2->vl_len) + len = arg2->vl_len; + len -= sizeof(int32); /* varlena! */ + + while (len != 0 && *a1p == *a2p) + { + a1p++; + a2p++; + len--; + } + if (len) + return (int32) (*a1p < *a2p); + else + return ((int32) VARSIZE(arg1) <= VARSIZE(arg2)); +} + +int32 +text_gt(struct varlena *arg1, struct varlena *arg2) +{ + return ((int32) !text_le(arg1, arg2)); +} + +int32 +text_ge(struct varlena *arg1, struct varlena *arg2) +{ + return ((int32) !text_lt(arg1, arg2)); +} + +/*------------------------------------------------------------- + * byteaGetSize + * + * get the number of bytes contained in an instance of type 'bytea' + *------------------------------------------------------------- + */ +int32 +byteaGetSize(struct varlena *v) +{ + register int len; + + len = v->vl_len - sizeof(v->vl_len); + + return(len); +} + +/*------------------------------------------------------------- + * byteaGetByte + * + * this routine treats "bytea" as an array of bytes. + * It returns the Nth byte (a number between 0 and 255) or + * it dies if the length of this array is less than n. + *------------------------------------------------------------- + */ +int32 +byteaGetByte(struct varlena *v, int32 n) +{ + int len; + int byte; + + len = byteaGetSize(v); + + if (n>=len) { + elog(WARN, "byteaGetByte: index (=%d) out of range [0..%d]", + n,len-1); + } + + byte = (unsigned char) (v->vl_dat[n]); + + return((int32) byte); +} + +/*------------------------------------------------------------- + * byteaGetBit + * + * This routine treats a "bytea" type like an array of bits. + * It returns the value of the Nth bit (0 or 1). + * If 'n' is out of range, it dies! + * + *------------------------------------------------------------- + */ +int32 +byteaGetBit(struct varlena *v, int32 n) +{ + int byteNo, bitNo; + int byte; + + byteNo = n/8; + bitNo = n%8; + + byte = byteaGetByte(v, byteNo); + + if (byte & (1<<bitNo)) { + return((int32)1); + } else { + return((int32)0); + } +} +/*------------------------------------------------------------- + * byteaSetByte + * + * Given an instance of type 'bytea' creates a new one with + * the Nth byte set to the given value. + * + *------------------------------------------------------------- + */ +struct varlena * +byteaSetByte(struct varlena *v, int32 n, int32 newByte) +{ + int len; + struct varlena *res; + + len = byteaGetSize(v); + + if (n>=len) { + elog(WARN, + "byteaSetByte: index (=%d) out of range [0..%d]", + n, len-1); + } + + /* + * Make a copy of the original varlena. + */ + res = (struct varlena *) palloc(VARSIZE(v)); + if (res==NULL) { + elog(WARN, "byteaSetByte: Out of memory (%d bytes requested)", + VARSIZE(v)); + } + memmove((char *)res, (char *)v, VARSIZE(v)); + + /* + * Now set the byte. + */ + res->vl_dat[n] = newByte; + + return(res); +} + +/*------------------------------------------------------------- + * byteaSetBit + * + * Given an instance of type 'bytea' creates a new one with + * the Nth bit set to the given value. + * + *------------------------------------------------------------- + */ +struct varlena * +byteaSetBit(struct varlena *v, int32 n, int32 newBit) +{ + struct varlena *res; + int oldByte, newByte; + int byteNo, bitNo; + + /* + * sanity check! + */ + if (newBit != 0 && newBit != 1) { + elog(WARN, "byteaSetByte: new bit must be 0 or 1"); + } + + /* + * get the byte where the bit we want is stored. + */ + byteNo = n / 8; + bitNo = n % 8; + oldByte = byteaGetByte(v, byteNo); + + /* + * calculate the new value for that byte + */ + if (newBit == 0) { + newByte = oldByte & (~(1<<bitNo)); + } else { + newByte = oldByte | (1<<bitNo); + } + + /* + * NOTE: 'byteaSetByte' creates a copy of 'v' & sets the byte. + */ + res = byteaSetByte(v, byteNo, newByte); + + return(res); +} |