diff options
Diffstat (limited to 'src/backend/utils')
106 files changed, 29318 insertions, 0 deletions
diff --git a/src/backend/utils/Gen_fmgrtab.sh b/src/backend/utils/Gen_fmgrtab.sh new file mode 100644 index 00000000000..f6af3aa80d4 --- /dev/null +++ b/src/backend/utils/Gen_fmgrtab.sh @@ -0,0 +1,265 @@ +#!/bin/sh +#------------------------------------------------------------------------- +# +# Gen_fmgrtab.sh-- +# shell script to generate fmgr.h and fmgrtab.c from pg_proc.h +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ +# +# NOTES +# Passes any -D options on to cpp prior to generating the list +# of internal functions. These come from BKIOPTS. +# +#------------------------------------------------------------------------- + +# cpp is usually in one of these places... +PATH=/usr/lib:/lib:/usr/ccs/lib:$PATH + +BKIOPTS='' +if [ $? != 0 ] +then + echo `basename $0`: Bad option + exit 1 +fi + +# +# Pass on any -D declarations, throwing away any other command +# line switches. +# +for opt in $* +do + case $opt in + -D) BKIOPTS="$BKIOPTS -D$2"; shift; shift;; + -D*) BKIOPTS="$BKIOPTS $1";shift;; + --) shift; break;; + -*) shift;; + esac +done + +INFILE=$1 +RAWFILE=fmgr.raw +HFILE=fmgr.h +TABCFILE=fmgrtab.c + +# +# Generate the file containing raw pg_proc tuple data +# (but only for "internal" language procedures...). +# +# Unlike genbki.sh, which can run through cpp last, we have to +# deal with preprocessor statements first (before we sort the +# function table by oid). +# +awk ' +BEGIN { raw = 0; } +/^DATA/ { print; next; } +/^BKI_BEGIN/ { raw = 1; next; } +/^BKI_END/ { raw = 0; next; } +raw == 1 { print; next; }' $INFILE | \ +sed -e 's/^.*OID[^=]*=[^0-9]*//' \ + -e 's/(//g' \ + -e 's/[ ]*).*$//' | \ +awk ' +/^#/ { print; next; } +$4 == "11" { print; next; }' | \ +cpp $BKIOPTS | \ +egrep '^[0-9]' | \ +sort -n > $RAWFILE + +# +# Generate fmgr.h +# +cat > $HFILE <<FuNkYfMgRsTuFf +/*------------------------------------------------------------------------- + * + * $HFILE-- + * Definitions for using internal procedures. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: Gen_fmgrtab.sh,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + * NOTES + * ****************************** + * *** DO NOT EDIT THIS FILE! *** + * ****************************** + * + * It has been GENERATED by $0 + * from $1 + * + *------------------------------------------------------------------------- + */ +#ifndef FMGR_H +#define FMGR_H + +#include "postgres.h" /* for some prototype typedefs */ + +/* + * Maximum number of arguments for a built-in function. + * + * XXX note that you cannot call a function with more than 8 + * arguments from the user level since the catalogs only + * store 8 argument type values for type-checking ... + */ +#define MAXFMGRARGS 9 + +typedef struct { + char *data[MAXFMGRARGS]; +} FmgrValues; + +/* + * defined in fmgr.c + */ +extern char *fmgr_c(func_ptr user_fn, Oid func_id, int n_arguments, + FmgrValues *values, bool *isNull); +extern void fmgr_info(Oid procedureId, func_ptr *function, int *nargs); +extern char *fmgr(Oid procedureId, ... ); +extern char *fmgr_ptr(func_ptr user_fn, Oid func_id, ... ); +extern char *fmgr_array_args(Oid procedureId, int nargs, + char *args[], bool *isNull); + +/* + * defined in dfmgr.c + */ +extern func_ptr fmgr_dynamic(Oid procedureId, int *pronargs); +extern void load_file(char *filename); + + +/* + * For performance reasons, we often want to simply jump through a + * a function pointer (if it's valid, that is). These calls have + * been macroized so we can run them through a routine that does + * sanity-checking (and so we can track them down more easily when + * we must). + */ +#ifdef TRACE_FMGR_PTR +#define FMGR_PTR2(FP, FID, ARG1, ARG2) \ + fmgr_ptr(FP, FID, 2, ARG1, ARG2) +#else +#define FMGR_PTR2(FP, FID, ARG1, ARG2) \ + ((FP) ? (*((func_ptr)(FP)))(ARG1, ARG2) : fmgr(FID, ARG1, ARG2)) +#endif + +/* + * Flags for the builtin oprrest selectivity routines. + */ +#define SEL_CONSTANT 1 /* constant does not vary (not a parameter) */ +#define SEL_RIGHT 2 /* constant appears to right of operator */ + +FuNkYfMgRsTuFf +awk '{ print $2, $1; }' $RAWFILE | \ +tr '[a-z]' '[A-Z]' | \ +sed -e 's/^/#define F_/' >> $HFILE +cat >> $HFILE <<FuNkYfMgRsTuFf + +#endif /* FMGR_H */ +FuNkYfMgRsTuFf + +# +# Generate fmgr function table file. +# +# Print out the bogus function declarations, then the table that +# refers to them. +# +cat > $TABCFILE <<FuNkYfMgRtAbStUfF +/*------------------------------------------------------------------------- + * + * $TABCFILE-- + * The function manager's table of internal functions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/Attic/Gen_fmgrtab.sh,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $ + * + * NOTES + * + * ****************************** + * *** DO NOT EDIT THIS FILE! *** + * ****************************** + * + * It has been GENERATED by $0 + * from $1 + * + * We lie here to cc about the return type and arguments of the + * builtin functions; all ld cares about is the fact that it + * will need to resolve an external function reference. + * + *------------------------------------------------------------------------- + */ +#ifdef WIN32 +#include <limits.h> +#else +# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi) +# include <machine/limits.h> +# define MAXINT INT_MAX +# else +# include <values.h> /* for MAXINT */ +# endif /* PORTNAME_BSD44_derived || PORTNAME_bsdi */ +#endif /* WIN32 */ + +#include "utils/fmgrtab.h" + +FuNkYfMgRtAbStUfF +awk '{ print "extern char *" $2 "();"; }' $RAWFILE >> $TABCFILE +cat >> $TABCFILE <<FuNkYfMgRtAbStUfF + +static FmgrCall fmgr_builtins[] = { +FuNkYfMgRtAbStUfF +awk '{ printf (" {%d , %d , %s, \"%s\" },\n"), $1, $8, $2, $2 }' $RAWFILE >> $TABCFILE +cat >> $TABCFILE <<FuNkYfMgRtAbStUfF + /* guardian value */ +#ifndef WIN32 + { MAXINT, 0, (func_ptr) NULL } +#else + { INT_MAX, 0, (func_ptr) NULL } +#endif /* WIN32 */ +}; + +static int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrCall)) - 1; + +FmgrCall *fmgr_isbuiltin(Oid id) +{ + register int i; + int low = 0; + int high = fmgr_nbuiltins; + + low = 0; + high = fmgr_nbuiltins; + while (low <= high) { + i = low + (high - low) / 2; + if (id == fmgr_builtins[i].proid) + break; + else if (id > fmgr_builtins[i].proid) + low = i + 1; + else + high = i - 1; + } + if (id == fmgr_builtins[i].proid) + return(&fmgr_builtins[i]); + return((FmgrCall *) NULL); +} + +func_ptr fmgr_lookupByName(char *name) +{ + int i; + for (i=0;i<fmgr_nbuiltins;i++) { + if (strcmp(name,fmgr_builtins[i].funcName) == 0) + return(fmgr_builtins[i].func); + } + return((func_ptr) NULL); +} + +FuNkYfMgRtAbStUfF + +rm -f $RAWFILE + +# ---------------- +# all done +# ---------------- +exit 0 diff --git a/src/backend/utils/Makefile.inc b/src/backend/utils/Makefile.inc new file mode 100644 index 00000000000..47179a67f51 --- /dev/null +++ b/src/backend/utils/Makefile.inc @@ -0,0 +1,62 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for the utilities modules +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ +# +#------------------------------------------------------------------------- + +utilsdir= $(CURDIR)/utils +VPATH:= $(VPATH):$(utilsdir):\ + $(utilsdir)/adt:$(utilsdir)/cache:$(utilsdir)/error:$(utilsdir)/fmgr:\ + $(utilsdir)/hash:$(utilsdir)/init:$(utilsdir)/mmgr:$(utilsdir)/sort:\ + $(utilsdir)/time + +SUBSRCS= +include $(utilsdir)/adt/Makefile.inc +include $(utilsdir)/cache/Makefile.inc +include $(utilsdir)/error/Makefile.inc +include $(utilsdir)/fmgr/Makefile.inc +include $(utilsdir)/hash/Makefile.inc +include $(utilsdir)/init/Makefile.inc +include $(utilsdir)/mmgr/Makefile.inc +include $(utilsdir)/sort/Makefile.inc +include $(utilsdir)/time/Makefile.inc +SRCS_UTILS:= $(SUBSRCS) fmgrtab.c + +GENFMGRTAB= $(utilsdir)/Gen_fmgrtab.sh +#GENFMGRTABFILES= fmgr.h fmgrtab.c +GENFMGRTABFILES= fmgrtab.c + +# +# BKIOPTS is set in ../catalog/Makefile.inc and sets the -D flags for +# the DATA(...); statements. Hence, ../catalog/Makefile.inc had better +# get slurped in prior to this Makefile.inc, or BKIOPTS should be set +# in a higher directory level. +# +$(GENFMGRTABFILES): $(GENFMGRTAB) $(catdir)/pg_proc.h + cd $(objdir); \ + sh $(SHOPTS) $(GENFMGRTAB) $(BKIOPTS) $(catdir)/pg_proc.h + +$(objdir)/fmgrtab.o: fmgrtab.c + $(cc_inobjdir) + +POSTGRES_DEPEND+= ${GENFMGRTABFILES} + +# +#${PROG}: ${GENFMGRTABFILES} +# + +CLEANFILES+= $(GENFMGRTABFILES) + +HEADERS+= acl.h array.h bit.h builtins.h catcache.h datum.h \ + dynamic_loader.h elog.h exc.h excid.h fcache.h fmgrtab.h \ + geo-decls.h hsearch.h inval.h lselect.h lsyscache.h mcxt.h \ + memutils.h module.h nabstime.h oidcompos.h palloc.h \ + portal.h psort.h rel.h rel2.h relcache.h sets.h \ + syscache.h tqual.h diff --git a/src/backend/utils/acl.h b/src/backend/utils/acl.h new file mode 100644 index 00000000000..fec5a8ed382 --- /dev/null +++ b/src/backend/utils/acl.h @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------- + * + * acl.h-- + * Definition of (and support for) access control list data structures. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: acl.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + * NOTES + * For backward-compatability purposes we have to allow there + * to be a null ACL in a pg_class tuple. This will be defined as + * meaning "no protection" (i.e., old catalogs get old semantics). + * + * The AclItems in an ACL array are currently kept in sorted order. + * Things will break hard if you change that without changing the + * code wherever this is included. + * + *------------------------------------------------------------------------- + */ +#ifndef ACL_H +#define ACL_H + +#include "postgres.h" +#include "utils/array.h" +#include "nodes/parsenodes.h" /* for ChangeACLStmt */ + +/* + * AclId system identifier for the user, group, etc. + * XXX currently UNIX uid for users... + */ +typedef uint32 AclId; +#define ACL_ID_WORLD 0 /* XXX only idtype should be checked */ + +/* + * AclIdType tag that describes if the AclId is a user, group, etc. + */ +typedef uint8 AclIdType; +#define ACL_IDTYPE_WORLD 0x00 +#define ACL_IDTYPE_UID 0x01 /* user id - from pg_user */ +#define ACL_IDTYPE_GID 0x02 /* group id - from pg_group */ + +/* + * AclMode the actual permissions + * XXX should probably use bit.h routines. + * XXX should probably also stuff the modechg cruft in the + * high bits, too. + */ +typedef uint8 AclMode; +#define ACL_NO 0 /* no permissions */ +#define ACL_AP (1<<0) /* append */ +#define ACL_RD (1<<1) /* read */ +#define ACL_WR (1<<2) /* write (append/delete/replace) */ +#define ACL_RU (1<<3) /* place rules */ +#define N_ACL_MODES 4 + +#define ACL_MODECHG_ADD 1 +#define ACL_MODECHG_DEL 2 +#define ACL_MODECHG_EQL 3 + +/* change this line if you want to set the default acl permission */ +#define ACL_WORLD_DEFAULT (ACL_RD) +/* #define ACL_WORLD_DEFAULT (ACL_RD|ACL_WR|ACL_AP|ACL_RU) */ +#define ACL_OWNER_DEFAULT (ACL_RD|ACL_WR|ACL_AP|ACL_RU) + +/* + * AclItem + */ +typedef struct AclItem { + AclId ai_id; + AclIdType ai_idtype; + AclMode ai_mode; +} AclItem; +/* Note: if the size of AclItem changes, + change the aclitem typlen in pg_type.h */ + +/* + * The value of the first dimension-array element. Since these arrays + * always have a lower-bound of 0, this is the same as the number of + * elements in the array. + */ +#define ARR_DIM0(a) (((unsigned *) (((char *) a) + sizeof(ArrayType)))[0]) + +/* + * Acl a one-dimensional POSTGRES array of AclItem + */ +typedef ArrayType Acl; +#define ACL_NUM(ACL) ARR_DIM0(ACL) +#define ACL_DAT(ACL) ((AclItem *) ARR_DATA_PTR(ACL)) +#define ACL_N_SIZE(N) \ + ((unsigned) (ARR_OVERHEAD(1) + ((N) * sizeof(AclItem)))) +#define ACL_SIZE(ACL) ARR_SIZE(ACL) + +/* + * IdList a one-dimensional POSTGRES array of AclId + */ +typedef ArrayType IdList; +#define IDLIST_NUM(IDL) ARR_DIM0(IDL) +#define IDLIST_DAT(IDL) ((AclId *) ARR_DATA_PTR(IDL)) +#define IDLIST_N_SIZE(N) \ + ((unsigned) (ARR_OVERHEAD(1) + ((N) * sizeof(AclId)))) +#define IDLIST_SIZE(IDL) ARR_SIZE(IDL) + +#define ACL_MODECHG_STR "+-=" /* list of valid characters */ +#define ACL_MODECHG_ADD_CHR '+' +#define ACL_MODECHG_DEL_CHR '-' +#define ACL_MODECHG_EQL_CHR '=' +#define ACL_MODE_STR "arwR" /* list of valid characters */ +#define ACL_MODE_AP_CHR 'a' +#define ACL_MODE_RD_CHR 'r' +#define ACL_MODE_WR_CHR 'w' +#define ACL_MODE_RU_CHR 'R' + +/* we use this warning string both for non-existent tables and + insufficient privilege so non-privileged users cannot ascertain whether + the class exists or not */ +#define ACL_NO_PRIV_WARNING "Either no such class or insufficient privilege" + +/* + * Enable ACL execution tracing and table dumps + */ +/*#define ACLDEBUG_TRACE*/ + +/* + * routines used internally (parser, etc.) + */ +extern char *aclparse(char *s, AclItem *aip, unsigned *modechg); +extern Acl *aclownerdefault(AclId ownerid); +extern Acl *acldefault(); +extern Acl *aclinsert3(Acl *old_acl, AclItem *mod_aip, unsigned modechg); + +extern char* aclmakepriv(char* old_privlist, char new_priv); +extern char* aclmakeuser(char* user_type, char* user); +extern ChangeACLStmt* makeAclStmt(char* privs, List* rel_list, char* grantee, + char grant_or_revoke); + +/* + * exported routines (from acl.c) + */ +extern Acl *makeacl(int n); +extern AclItem *aclitemin(char *s); +extern char *aclitemout(AclItem *aip); +extern Acl *aclinsert(Acl *old_acl, AclItem *mod_aip); +extern Acl *aclremove(Acl *old_acl, AclItem *mod_aip); +extern int32 aclcontains(Acl *acl, AclItem *aip); + +/* + * prototypes for functions in aclchk.c + */ +extern void ChangeAcl(char *relname, AclItem *mod_aip, unsigned modechg); +extern AclId get_grosysid(char *groname); +extern char *get_groname(AclId grosysid); +extern int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode); + +/* XXX move these elsewhere -pma */ +extern int32 pg_aclcheck(char *relname, char *usename, AclMode mode); +extern int32 pg_ownercheck(char *usename, char *value, int cacheid); +extern int32 pg_func_ownercheck(char *usename, char *funcname, + int nargs, Oid *arglist); + +#endif /* ACL_H */ + 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); +} diff --git a/src/backend/utils/array.h b/src/backend/utils/array.h new file mode 100644 index 00000000000..c370072c4f8 --- /dev/null +++ b/src/backend/utils/array.h @@ -0,0 +1,166 @@ +/*------------------------------------------------------------------------- + * + * array.h-- + * Utilities for the new array code. Contain prototypes from the + * following files: + * utils/adt/arrayfuncs.c + * utils/adt/arrayutils.c + * utils/adt/chunk.c + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: array.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + * NOTES + * XXX the data array should be LONGALIGN'd -- notice that the array + * allocation code does not allocate the extra space required for this, + * even though the array-packing code does the LONGALIGNs. + * + *------------------------------------------------------------------------- + */ +#ifndef ARRAY_H +#define ARRAY_H + +#include <stdio.h> /* for FILE (XXX should use File) */ +#include "utils/memutils.h" + +typedef struct { + int size; /* total array size (in bytes) */ + int ndim; /* # of dimensions */ + int flags; /* implementation flags */ +} ArrayType; + +/* + * bitmask of ArrayType flags field: + * 1st bit - large object flag + * 2nd bit - chunk flag (array is chunked if set) + * 3rd,4th,&5th bit - large object type (used only if bit 1 is set) + */ +#define ARR_LOB_FLAG (0x1) +#define ARR_CHK_FLAG (0x2) +#define ARR_OBJ_MASK (0x1c) + +#define ARR_FLAGS(a) ((ArrayType *) a)->flags +#define ARR_SIZE(a) (((ArrayType *) a)->size) + +#define ARR_NDIM(a) (((ArrayType *) a)->ndim) +#define ARR_NDIM_PTR(a) (&(((ArrayType *) a)->ndim)) + +#define ARR_IS_LO(a) \ + (((ArrayType *) a)->flags & ARR_LOB_FLAG) +#define SET_LO_FLAG(f,a) \ + (((ArrayType *) a)->flags |= ((f) ? ARR_LOB_FLAG : 0x0)) + +#define ARR_IS_CHUNKED(a) \ + (((ArrayType *) a)->flags & ARR_CHK_FLAG) +#define SET_CHUNK_FLAG(f,a) \ + (((ArrayType *) a)->flags |= ((f) ? ARR_CHK_FLAG : 0x0)) + +#define ARR_OBJ_TYPE(a) \ + ((ARR_FLAGS(a) & ARR_OBJ_MASK) >> 2) +#define SET_OBJ_TYPE(f,a) \ + ((ARR_FLAGS(a)&= ~ARR_OBJ_MASK), (ARR_FLAGS(a)|=((f<<2)&ARR_OBJ_MASK))) + +/* + * ARR_DIMS returns a pointer to an array of array dimensions (number of + * elements along the various array axes). + * + * ARR_LBOUND returns a pointer to an array of array lower bounds. + * + * That is: if the third axis of an array has elements 5 through 10, then + * ARR_DIMS(a)[2] == 6 and ARR_LBOUND[2] == 5. + * + * Unlike C, the default lower bound is 1. + */ +#define ARR_DIMS(a) \ + ((int *) (((char *) a) + sizeof(ArrayType))) +#define ARR_LBOUND(a) \ + ((int *) (((char *) a) + sizeof(ArrayType) + \ + (sizeof(int) * (((ArrayType *) a)->ndim)))) + +/* + * Returns a pointer to the actual array data. + */ +#define ARR_DATA_PTR(a) \ + (((char *) a) + \ + DOUBLEALIGN(sizeof(ArrayType) + 2 * (sizeof(int) * (a)->ndim))) + +/* + * The total array header size for an array of dimension n (in bytes). + */ +#define ARR_OVERHEAD(n) \ + (DOUBLEALIGN(sizeof(ArrayType) + 2 * (n) * sizeof(int))) + +/*------------------------------------------------------------------------ + * Miscellaneous helper definitions and routines for arrayfuncs.c + *------------------------------------------------------------------------ + */ + +/* #if defined(PORTNAME_irix5) */ +/* #define RETURN_NULL {*isNull = true; return(0); }*/ +/* #else*/ /* PORTNAME_irix5 */ +#define RETURN_NULL {*isNull = true; return(0); } +/* #endif */ /* PORTNAME_irix5 */ +#define NAME_LEN 30 +#define MAX_BUFF_SIZE (1 << 13) + +typedef struct { + char lo_name[NAME_LEN]; + int C[MAXDIM]; +} CHUNK_INFO; + +/* + * prototypes for functions defined in arrayfuncs.c + */ +extern char *array_in(char *string, Oid element_type); +extern char *array_out(ArrayType *v, Oid element_type); +extern char *array_dims(ArrayType *v, bool *isNull); +extern Datum array_ref(ArrayType *array, int n, int indx[], int reftype, + int elmlen, int arraylen, bool *isNull); +extern Datum array_clip(ArrayType *array, int n, int upperIndx[], + int lowerIndx[], int reftype, int len, bool *isNull); +extern char *array_set(ArrayType *array, int n, int indx[], char *dataPtr, + int reftype, int elmlen, int arraylen, bool *isNull); +extern char *array_assgn(ArrayType *array, int n, int upperIndx[], + int lowerIndx[], ArrayType *newArr, int reftype, + int len, bool *isNull); +extern int array_eq (ArrayType *array1, ArrayType *array2); +extern SanityCheckInput(int ndim, int n, int dim[], int lb[], int indx[]); +extern char *array_seek(char *ptr, int eltsize, int nitems); +extern int array_read(char *destptr, int eltsize, int nitems, char *srcptr); +extern int _LOtransfer(char **destfd, int size, int nitems, char **srcfd, + int isSrcLO, int isDestLO); + +extern char * _array_newLO(int *fd, int flag); + + +/* + * prototypes for functions defined in arrayutils.c + * [these names seem to be too generic. Add prefix for arrays? -- AY] + */ + +extern int GetOffset(int n, int dim[], int lb[], int indx[]); +extern int getNitems(int n, int a[]); +extern int compute_size(int st[], int endp[], int n, int base); +extern void mda_get_offset_values(int n, int dist[], int PC[], int span[]); +extern void mda_get_range(int n, int span[], int st[], int endp[]); +extern void mda_get_prod(int n, int range[], int P[]); +extern int tuple2linear(int n, int tup[], int scale[]); +extern void array2chunk_coord(int n, int C[], int a_coord[], int c_coord[]); +extern int next_tuple(int n, int curr[], int span[]); + +/* + * prototypes for functions defined in chunk.c + */ +extern char * _ChunkArray(int fd, FILE *afd, int ndim, int dim[], int baseSize, + int *nbytes, char *chunkfile); +extern int GetChunkSize(FILE *fd, int ndim, int dim[MAXDIM], int baseSize, + int d[MAXDIM]); +extern int _ReadChunkArray(int st[], int endp[], int bsize, int fp, + char *destfp, ArrayType *array, int isDestLO, bool *isNull); +extern struct varlena *_ReadChunkArray1El(int st[], int bsize, int fp, + ArrayType *array, bool *isNull); + + +#endif /* ARRAY_H */ diff --git a/src/backend/utils/bit.h b/src/backend/utils/bit.h new file mode 100644 index 00000000000..c817d7b69b0 --- /dev/null +++ b/src/backend/utils/bit.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * bit.h-- + * Standard bit array definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: bit.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef BIT_H +#define BIT_H + +typedef bits8 *BitArray; +typedef uint32 BitIndex; + +#define BitsPerByte 8 + +/* + * BitArraySetBit -- + * Sets (to 1) the value of a bit in a bit array. + */ +extern void BitArraySetBit(BitArray bitArray, BitIndex bitIndex); + +/* + * BitArrayClearBit -- + * Clears (to 0) the value of a bit in a bit array. + */ +extern void BitArrayClearBit(BitArray bitArray, BitIndex bitIndex); + +/* + * BitArrayBitIsSet -- + * True iff the bit is set (1) in a bit array. + */ +extern bool BitArrayBitIsSet(BitArray bitArray, BitIndex bitIndex); + +#endif /* BIT_H */ diff --git a/src/backend/utils/builtins.h b/src/backend/utils/builtins.h new file mode 100644 index 00000000000..2379c59adb8 --- /dev/null +++ b/src/backend/utils/builtins.h @@ -0,0 +1,433 @@ +/*------------------------------------------------------------------------- + * + * builtins.h-- + * Declarations for operations on built-in types. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: builtins.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + * NOTES + * This should normally only be included by fmgr.h. + * Under no circumstances should it ever be included before + * including fmgr.h! + * + *------------------------------------------------------------------------- + */ +#ifndef BUILTINS_H +#define BUILTINS_H + +#include "postgres.h" + +#include "storage/itemptr.h" + +#include "storage/large_object.h" + +#include "utils/geo-decls.h" + +/* + * Defined in adt/ + */ +/* bool.c */ +extern int32 boolin(char *b); +extern char *boolout(long b); +extern int32 booleq(int8 arg1, int8 arg2); +extern int32 boolne(int8 arg1, int8 arg2); + +/* char.c */ +extern int32 charin(char *ch); +extern char *charout(int32 ch); +extern int32 cidin(char *s); +extern char *cidout(int32 c); +extern char *char16in(char *s); +extern char *char16out(char *s); +extern int32 chareq(int8 arg1, int8 arg2); +extern int32 charne(int8 arg1, int8 arg2); +extern int32 charlt(int8 arg1, int8 arg2); +extern int32 charle(int8 arg1, int8 arg2); +extern int32 chargt(int8 arg1, int8 arg2); +extern int32 charge(int8 arg1, int8 arg2); +extern int8 charpl(int8 arg1, int8 arg2); +extern int8 charmi(int8 arg1, int8 arg2); +extern int8 charmul(int8 arg1, int8 arg2); +extern int8 chardiv(int8 arg1, int8 arg2); +extern int32 cideq(int8 arg1, int8 arg2); +extern int32 char16eq(char *arg1, char *arg2); +extern int32 char16ne(char *arg1, char *arg2); +extern int32 char16lt(char *arg1, char *arg2); +extern int32 char16le(char *arg1, char *arg2); +extern int32 char16gt(char *arg1, char *arg2); +extern int32 char16ge(char *arg1, char *arg2); +extern uint16 char2in(char *s); +extern char *char2out(uint16 s); +extern int32 char2eq(uint16 a, uint16 b); +extern int32 char2ne(uint16 a, uint16 b); +extern int32 char2lt(uint16 a, uint16 b); +extern int32 char2le(uint16 a, uint16 b); +extern int32 char2gt(uint16 a, uint16 b); +extern int32 char2ge(uint16 a, uint16 b); +extern int32 char2cmp(uint16 a, uint16 b); +extern uint32 char4in(char *s); +extern char *char4out(uint32 s); +extern int32 char4eq(uint32 a, uint32 b); +extern int32 char4ne(uint32 a, uint32 b); +extern int32 char4lt(uint32 a, uint32 b); +extern int32 char4le(uint32 a, uint32 b); +extern int32 char4gt(uint32 a, uint32 b); +extern int32 char4ge(uint32 a, uint32 b); +extern int32 char4cmp(uint32 a, uint32 b); +extern char *char8in(char *s); +extern char *char8out(char *s); +extern int32 char8eq(char *arg1, char *arg2); +extern int32 char8ne(char *arg1, char *arg2); +extern int32 char8lt(char *arg1, char *arg2); +extern int32 char8le(char *arg1, char *arg2); +extern int32 char8gt(char *arg1, char *arg2); +extern int32 char8ge(char *arg1, char *arg2); +extern int32 char8cmp(char *arg1, char *arg2); + +/* int.c */ +extern int32 int2in(char *num); +extern char *int2out(int16 sh); +extern int16 *int28in(char *shs); +extern char *int28out(int16 (*shs)[]); +extern int32 *int44in(char *input_string); +extern char *int44out(int32 an_array[]); +extern int32 int4in(char *num); +extern char *int4out(int32 l); +extern int32 i2toi4(int16 arg1); +extern int16 i4toi2(int32 arg1); +extern int32 int4eq(int32 arg1, int32 arg2); +extern int32 int4ne(int32 arg1, int32 arg2); +extern int32 int4lt(int32 arg1, int32 arg2); +extern int32 int4le(int32 arg1, int32 arg2); +extern int32 int4gt(int32 arg1, int32 arg2); +extern int32 int4ge(int32 arg1, int32 arg2); +extern int32 int2eq(int16 arg1, int16 arg2); +extern int32 int2ne(int16 arg1, int16 arg2); +extern int32 int2lt(int16 arg1, int16 arg2); +extern int32 int2le(int16 arg1, int16 arg2); +extern int32 int2gt(int16 arg1, int16 arg2); +extern int32 int2ge(int16 arg1, int16 arg2); +extern int32 int24eq(int32 arg1, int32 arg2); +extern int32 int24ne(int32 arg1, int32 arg2); +extern int32 int24lt(int32 arg1, int32 arg2); +extern int32 int24le(int32 arg1, int32 arg2); +extern int32 int24gt(int32 arg1, int32 arg2); +extern int32 int24ge(int32 arg1, int32 arg2); +extern int32 int42eq(int32 arg1, int32 arg2); +extern int32 int42ne(int32 arg1, int32 arg2); +extern int32 int42lt(int32 arg1, int32 arg2); +extern int32 int42le(int32 arg1, int32 arg2); +extern int32 int42gt(int32 arg1, int32 arg2); +extern int32 int42ge(int32 arg1, int32 arg2); +extern int32 keyfirsteq(int16 *arg1, int16 arg2); +extern int32 int4um(int32 arg); +extern int32 int4pl(int32 arg1, int32 arg2); +extern int32 int4mi(int32 arg1, int32 arg2); +extern int32 int4mul(int32 arg1, int32 arg2); +extern int32 int4div(int32 arg1, int32 arg2); +extern int32 int4inc(int32 arg); +extern int16 int2um(int16 arg); +extern int16 int2pl(int16 arg1, int16 arg2); +extern int16 int2mi(int16 arg1, int16 arg2); +extern int16 int2mul(int16 arg1, int16 arg2); +extern int16 int2div(int16 arg1, int16 arg2); +extern int16 int2inc(int16 arg); +extern int32 int24pl(int32 arg1, int32 arg2); +extern int32 int24mi(int32 arg1, int32 arg2); +extern int32 int24mul(int32 arg1, int32 arg2); +extern int32 int24div(int32 arg1, int32 arg2); +extern int32 int42pl(int32 arg1, int32 arg2); +extern int32 int42mi(int32 arg1, int32 arg2); +extern int32 int42mul(int32 arg1, int32 arg2); +extern int32 int42div(int32 arg1, int32 arg2); +extern int32 int4mod(int32 arg1, int32 arg2); +extern int32 int2mod(int16 arg1, int16 arg2); +extern int32 int24mod(int32 arg1, int32 arg2); +extern int32 int42mod(int32 arg1, int32 arg2); +extern int32 int4fac(int32 arg1); +extern int32 int2fac(int16 arg1); +extern int16 int2larger(int16 arg1, int16 arg2); +extern int16 int2smaller(int16 arg1, int16 arg2); +extern int32 int4larger(int32 arg1, int32 arg2); +extern int32 int4smaller(int32 arg1, int32 arg2); + +/* name.c */ +extern NameData *namein(char *s); +extern char *nameout(NameData *s); +extern int32 nameeq(NameData *arg1, NameData *arg2); +extern int32 namene(NameData *arg1, NameData *arg2); +extern int32 namelt(NameData *arg1, NameData *arg2); +extern int32 namele(NameData *arg1, NameData *arg2); +extern int32 namegt(NameData *arg1, NameData *arg2); +extern int32 namege(NameData *arg1, NameData *arg2); +extern int namecmp(Name n1, Name n2); +extern int namecpy(Name n1, Name n2); +extern int namecat(Name n1, Name n2); +extern int namestrcpy(Name name, char *str); +extern int namestrcat(Name name, char *str); +extern int namestrcmp(Name name, char *str); +extern uint32 NameComputeLength(Name name); + +/* numutils.c */ +/* XXX hack. HP-UX has a ltoa (with different arguments) already. */ +#ifdef PORTNAME_hpux +#define ltoa pg_ltoa +#endif /* PORTNAME_hpux */ +extern int32 pg_atoi(char *s, int size, int c); +extern void itoa(int i, char *a); +extern void ltoa(int32 l, char *a); +extern int ftoa(double value, char *ascii, int width, int prec1, char format); +extern int atof1(char *str, double *val); + +/* + * Per-opclass comparison functions for new btrees. These are + * stored in pg_amproc and defined in nbtree/ + */ +extern int32 btint2cmp(); +extern int32 btint4cmp(); +extern int32 btint24cmp(); +extern int32 btint42cmp(); +extern int32 btfloat4cmp(); +extern int32 btfloat8cmp(); +extern int32 btoidcmp(); +extern int32 btabstimecmp(); +extern int32 btcharcmp(); +extern int32 btchar16cmp(); +extern int32 bttextcmp(); + +/* + * RTree code. + * Defined in access/index-rtree/ + */ +extern char *rtinsert(); +extern char *rtdelete(); +extern char *rtgettuple(); +extern char *rtbeginscan(); +extern void rtendscan(); +extern void rtreebuild(); +extern void rtmarkpos(); +extern void rtrestrpos(); +extern void rtrescan(); +extern void rtbuild(); + +/* support routines for the rtree access method, by opclass */ +extern BOX *rt_box_union(); +extern BOX *rt_box_inter(); +extern float *rt_box_size(); +extern float *rt_bigbox_size(); +extern float *rt_poly_size(); +extern POLYGON *rt_poly_union(); +extern POLYGON *rt_poly_inter(); + +/* projection utilities */ +/* extern char *GetAttributeByName(); + extern char *GetAttributeByNum(); , + in executor/executor.h*/ + + +extern int32 pqtest(); + +/* arrayfuncs.c */ +#include "utils/array.h" + +/* date.c */ +extern int32 reltimein(char *timestring); +extern char *reltimeout(int32 timevalue); +extern TimeInterval tintervalin(char *intervalstr); +extern char *tintervalout(TimeInterval interval); +extern TimeInterval mktinterval(AbsoluteTime t1, AbsoluteTime t2); +extern AbsoluteTime timepl(AbsoluteTime t1, RelativeTime t2); +extern AbsoluteTime timemi(AbsoluteTime t1, RelativeTime t2); +/* extern RelativeTime abstimemi(AbsoluteTime t1, AbsoluteTime t2); static*/ +extern int ininterval(AbsoluteTime t, TimeInterval interval); +extern RelativeTime intervalrel(TimeInterval interval); +extern AbsoluteTime timenow(void); +extern int32 reltimeeq(RelativeTime t1, RelativeTime t2); +extern int32 reltimene(RelativeTime t1, RelativeTime t2); +extern int32 reltimelt(RelativeTime t1, RelativeTime t2); +extern int32 reltimegt(RelativeTime t1, RelativeTime t2); +extern int32 reltimele(RelativeTime t1, RelativeTime t2); +extern int32 reltimege(RelativeTime t1, RelativeTime t2); +extern int32 intervaleq(TimeInterval i1, TimeInterval i2); +extern int32 intervalleneq(TimeInterval i, RelativeTime t); +extern int32 intervallenne(TimeInterval i, RelativeTime t); +extern int32 intervallenlt(TimeInterval i, RelativeTime t); +extern int32 intervallengt(TimeInterval i, RelativeTime t); +extern int32 intervallenle(TimeInterval i, RelativeTime t); +extern int32 intervallenge(TimeInterval i, RelativeTime t); +extern int32 intervalct(TimeInterval i1, TimeInterval i2); +extern int32 intervalov(TimeInterval i1, TimeInterval i2); +extern AbsoluteTime intervalstart(TimeInterval i); +extern AbsoluteTime intervalend(TimeInterval i); +extern int isreltime(char *timestring, int *sign, long *quantity, int *unitnr); + +/* dt.c */ +extern int32 dtin(char *datetime); +extern char *dtout(int32 datetime); + +/* filename.c */ +extern char *filename_in(char *file); +extern char *filename_out(char *s); + +/* float.c */ +extern float32 float4in(char *num); +extern char *float4out(float32 num); +extern float64 float8in(char *num); +extern char *float8out(float64 num); +extern float32 float4abs(float32 arg1); +extern float32 float4um(float32 arg1); +extern float32 float4larger(float32 arg1, float32 arg2); +extern float32 float4smaller(float32 arg1, float32 arg2); +extern float64 float8abs(float64 arg1); +extern float64 float8um(float64 arg1); +extern float64 float8larger(float64 arg1, float64 arg2); +extern float64 float8smaller(float64 arg1, float64 arg2); +extern float32 float4pl(float32 arg1, float32 arg2); +extern float32 float4mi(float32 arg1, float32 arg2); +extern float32 float4mul(float32 arg1, float32 arg2); +extern float32 float4div(float32 arg1, float32 arg2); +extern float32 float4inc(float32 arg1); +extern float64 float8pl(float64 arg1, float64 arg2); +extern float64 float8mi(float64 arg1, float64 arg2); +extern float64 float8mul(float64 arg1, float64 arg2); +extern float64 float8div(float64 arg1, float64 arg2); +extern float64 float8inc(float64 arg1); +extern long float4eq(float32 arg1, float32 arg2); +extern long float4ne(float32 arg1, float32 arg2); +extern long float4lt(float32 arg1, float32 arg2); +extern long float4le(float32 arg1, float32 arg2); +extern long float4gt(float32 arg1, float32 arg2); +extern long float4ge(float32 arg1, float32 arg2); +extern long float8eq(float64 arg1, float64 arg2); +extern long float8ne(float64 arg1, float64 arg2); +extern long float8lt(float64 arg1, float64 arg2); +extern long float8le(float64 arg1, float64 arg2); +extern long float8gt(float64 arg1, float64 arg2); +extern long float8ge(float64 arg1, float64 arg2); +extern float64 ftod(float32 num); +extern float32 dtof(float64 num); +extern float64 dround(float64 arg1); +extern float64 dtrunc(float64 arg1); +extern float64 dsqrt(float64 arg1); +extern float64 dcbrt(float64 arg1); +extern float64 dpow(float64 arg1, float64 arg2); +extern float64 dexp(float64 arg1); +extern float64 dlog1(float64 arg1); +extern float64 float48pl(float32 arg1, float64 arg2); +extern float64 float48mi(float32 arg1, float64 arg2); +extern float64 float48mul(float32 arg1, float64 arg2); +extern float64 float48div(float32 arg1, float64 arg2); +extern float64 float84pl(float64 arg1, float32 arg2); +extern float64 float84mi(float64 arg1, float32 arg2); +extern float64 float84mul(float64 arg1, float32 arg2); +extern float64 float84div(float64 arg1, float32 arg2); +extern long float48eq(float32 arg1, float64 arg2); +extern long float48ne(float32 arg1, float64 arg2); +extern long float48lt(float32 arg1, float64 arg2); +extern long float48le(float32 arg1, float64 arg2); +extern long float48gt(float32 arg1, float64 arg2); +extern long float48ge(float32 arg1, float64 arg2); +extern long float84eq(float64 arg1, float32 arg2); +extern long float84ne(float64 arg1, float32 arg2); +extern long float84lt(float64 arg1, float32 arg2); +extern long float84le(float64 arg1, float32 arg2); +extern long float84gt(float64 arg1, float32 arg2); +extern long float84ge(float64 arg1, float32 arg2); + +/* geo-ops.c, geo-selfuncs.c */ +#include "utils/geo-decls.h" + +/* misc.c */ +extern bool NullValue(Datum value, bool *isNull); +extern bool NonNullValue(Datum value, bool *isNull); +extern int32 userfntest(int i); + +/* not_in.c */ +extern bool int4notin(int16 not_in_arg, char *relation_and_attr); +extern bool oidnotin(Oid the_oid, char *compare); +extern int my_varattno(Relation rd, char *a); + +/* oid.c */ +extern Oid *oid8in(char *oidString); +extern char *oid8out(Oid (*oidArray)[]); +extern Oid oidin(char *s); +extern char *oidout(Oid o); +extern int32 oideq(Oid arg1, Oid arg2); +extern int32 oidne(Oid arg1, Oid arg2); +extern int32 oid8eq(Oid arg1[], Oid arg2[]); + +/* regexp.c */ +extern bool char2regexeq(uint16 arg1, struct varlena *p); +extern bool char2regexne(uint16 arg1, struct varlena *p); +extern bool char4regexeq(uint32 arg1, struct varlena *p); +extern bool char4regexne(uint32 arg1, struct varlena *p); +extern bool char8regexeq(char *s, struct varlena *p); +extern bool char8regexne(char *s, struct varlena *p); +extern bool char16regexeq(char *s, struct varlena *p); +extern bool char16regexne(char *s, struct varlena *p); +extern bool textregexeq(struct varlena *s, struct varlena *p); +extern bool textregexne(struct varlena *s, struct varlena *p); +extern bool char2icregexeq(uint16 arg1, struct varlena *p); +extern bool char2icregexne(uint16 arg1, struct varlena *p); +extern bool char4icregexeq(uint32 arg1, struct varlena *p); +extern bool char4icregexne(uint32 arg1, struct varlena *p); +extern bool char8icregexeq(char *s, struct varlena *p); +extern bool char8icregexne(char *s, struct varlena *p); +extern bool char16icregexeq(char *s, struct varlena *p); +extern bool char16icregexne(char *s, struct varlena *p); +extern bool nameicregexeq(NameData *s, struct varlena *p); +extern bool nameicregexne(NameData *s, struct varlena *p); +extern bool texticregexeq(struct varlena *s, struct varlena *p); +extern bool texticregexne(struct varlena *s, struct varlena *p); + + +/* regproc.c */ +extern int32 regprocin(char *proname); +extern char *regprocout(RegProcedure proid); +extern Oid RegprocToOid(RegProcedure rp); + +/* selfuncs.c */ +extern float64 eqsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 neqsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 intltsel(Oid opid, Oid relid, AttrNumber attno, int32 value, int32 flag); +extern float64 intgtsel(Oid opid, Oid relid, AttrNumber attno, int32 value, int32 flag); +extern float64 eqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); +extern float64 neqjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); +extern float64 intltjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); +extern float64 intgtjoinsel(Oid opid, Oid relid1, AttrNumber attno1, Oid relid2, AttrNumber attno2); +extern float64 btreesel(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid); +extern float64 btreenpage(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid); +extern float64 hashsel(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid); +extern float64 hashnpage(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid); +extern float64 rtsel(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid); +extern float64 rtnpage(Oid operatorOid, Oid indrelid, AttrNumber attributeNumber, char *constValue, int32 constFlag, int32 nIndexKeys, Oid indexrelid); + +/* tid.c */ +extern ItemPointer tidin(char *str); +extern char *tidout(ItemPointer itemPtr); + +/* varlena.c */ +extern struct varlena *byteain(char *inputText); +extern struct varlena *shove_bytes(unsigned char *stuff, int len); +extern char *byteaout(struct varlena *vlena); +extern struct varlena *textin(char *inputText); +extern char *textout(struct varlena *vlena); +extern int32 texteq(struct varlena *arg1, struct varlena *arg2); +extern int32 textne(struct varlena *arg1, struct varlena *arg2); +extern int32 text_lt(struct varlena *arg1, struct varlena *arg2); +extern int32 text_le(struct varlena *arg1, struct varlena *arg2); +extern int32 text_gt(struct varlena *arg1, struct varlena *arg2); +extern int32 text_ge(struct varlena *arg1, struct varlena *arg2); +extern int32 byteaGetSize(struct varlena *v); +extern int32 byteaGetByte(struct varlena *v, int32 n); +extern int32 byteaGetBit(struct varlena *v, int32 n); +extern struct varlena *byteaSetByte(struct varlena *v, int32 n, int32 newByte); +extern struct varlena *byteaSetBit(struct varlena *v, int32 n, int32 newBit); + +/* acl.c */ +#include "utils/acl.h" + +#endif /* BUILTINS_H */ diff --git a/src/backend/utils/cache/Makefile.inc b/src/backend/utils/cache/Makefile.inc new file mode 100644 index 00000000000..c6a688e5e83 --- /dev/null +++ b/src/backend/utils/cache/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/cache +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= catcache.c inval.c rel.c relcache.c syscache.c lsyscache.c fcache.c + diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c new file mode 100644 index 00000000000..93ea9be8282 --- /dev/null +++ b/src/backend/utils/cache/catcache.c @@ -0,0 +1,1023 @@ +/*------------------------------------------------------------------------- + * + * catcache.c-- + * System catalog cache for tuples matching a key. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * Notes: + * XXX This needs to use exception.h to handle recovery when + * an abort occurs during DisableCache. + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" +#include "access/heapam.h" +#include "access/genam.h" +#include "utils/builtins.h" +#include "utils/tqual.h" +#include "storage/bufpage.h" +#include "access/valid.h" +#include "miscadmin.h" +#include "utils/portal.h" +#include "utils/catcache.h" +#include "fmgr.h" /* for F_BOOLEQ, etc. DANGER */ +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" +#include "utils/rel.h" +#include "catalog/pg_type.h" /* for OID of int28 type */ +#include "lib/dllist.h" + +/* ---------------- + * variables, macros and other stuff + * + * note CCSIZE allocates 51 buckets .. one was already allocated in + * the catcache structure. + * ---------------- + */ + +#ifdef CACHEDEBUG +#define CACHE1_elog(a,b) elog(a,b) +#define CACHE2_elog(a,b,c) elog(a,b,c) +#define CACHE3_elog(a,b,c,d) elog(a,b,c,d) +#define CACHE4_elog(a,b,c,d,e) elog(a,b,c,d,e) +#define CACHE5_elog(a,b,c,d,e,f) elog(a,b,c,d,e,f) +#define CACHE6_elog(a,b,c,d,e,f,g) elog(a,b,c,d,e,f,g) +#else +#define CACHE1_elog(a,b) +#define CACHE2_elog(a,b,c) +#define CACHE3_elog(a,b,c,d) +#define CACHE4_elog(a,b,c,d,e) +#define CACHE5_elog(a,b,c,d,e,f) +#define CACHE6_elog(a,b,c,d,e,f,g) +#endif + +CatCache *Caches = NULL; +GlobalMemory CacheCxt; + +static int DisableCache; + +/* ---------------- + * EQPROC is used in CatalogCacheInitializeCache + * XXX this should be replaced by catalog lookups soon + * ---------------- + */ +static long eqproc[] = { + F_BOOLEQ, 0l, F_CHAREQ, F_CHAR16EQ, 0l, + F_INT2EQ, F_KEYFIRSTEQ, F_INT4EQ, 0l, F_TEXTEQ, + F_OIDEQ, 0l, 0l, 0l, F_OID8EQ +}; + +#define EQPROC(SYSTEMTYPEOID) eqproc[(SYSTEMTYPEOID)-16] + +/* ---------------------------------------------------------------- + * internal support functions + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * CatalogCacheInitializeCache + * -------------------------------- + */ +#ifdef CACHEDEBUG +#define CatalogCacheInitializeCache_DEBUG1 \ + elog(DEBUG, "CatalogCacheInitializeCache: cache @%08lx", cache); \ + if (relation) \ + elog(DEBUG, "CatalogCacheInitializeCache: called w/relation(inval)"); \ + else \ + elog(DEBUG, "CatalogCacheInitializeCache: called w/relname %s", \ + cache->cc_relname) +#define CatalogCacheInitializeCache_DEBUG2 \ + if (cache->cc_key[i] > 0) { \ + elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d, %d", \ + i+1, cache->cc_nkeys, cache->cc_key[i], \ + relation->rd_att->attrs[cache->cc_key[i] - 1]->attlen); \ + } else { \ + elog(DEBUG, "CatalogCacheInitializeCache: load %d/%d w/%d", \ + i+1, cache->cc_nkeys, cache->cc_key[i]); \ + } +#else +#define CatalogCacheInitializeCache_DEBUG1 +#define CatalogCacheInitializeCache_DEBUG2 +#endif + +void +CatalogCacheInitializeCache(struct catcache *cache, + Relation relation) +{ + MemoryContext oldcxt; + short didopen = 0; + short i; + TupleDesc tupdesc; + + CatalogCacheInitializeCache_DEBUG1; + + /* ---------------- + * first switch to the cache context so our allocations + * do not vanish at the end of a transaction + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * If no relation was passed we must open it to get access to + * its fields. If one of the other caches has already opened + * it we use heap_open() instead of heap_openr() + * ---------------- + */ + if (! RelationIsValid(relation)) { + struct catcache *cp; + /* ---------------- + * scan the caches to see if any other cache has opened the relation + * ---------------- + */ + for (cp = Caches; cp; cp = cp->cc_next) { + if (strncmp(cp->cc_relname, cache->cc_relname, NAMEDATALEN) == 0) { + if (cp->relationId != InvalidOid) + break; + } + } + + /* ---------------- + * open the relation by name or by id + * ---------------- + */ + if (cp) + relation = heap_open(cp->relationId); + else + { + relation = heap_openr(cache->cc_relname); + } + + didopen = 1; + } + + /* ---------------- + * initialize the cache's relation id + * ---------------- + */ + Assert(RelationIsValid(relation)); + cache->relationId = RelationGetRelationId(relation); + tupdesc = cache->cc_tupdesc = RelationGetTupleDescriptor(relation); + + CACHE3_elog(DEBUG, "CatalogCacheInitializeCache: relid %d, %d keys", + cache->relationId, cache->cc_nkeys); + + /* ---------------- + * initialize cache's key information + * ---------------- + */ + for (i = 0; i < cache->cc_nkeys; ++i) { + CatalogCacheInitializeCache_DEBUG2; + + if (cache->cc_key[i] > 0) { + + /* + * Yoiks. The implementation of the hashing code and the + * implementation of int28's are at loggerheads. The right + * thing to do is to throw out the implementation of int28's + * altogether; until that happens, we do the right thing here + * to guarantee that the hash key generator doesn't try to + * dereference an int2 by mistake. + */ + + if (tupdesc->attrs[cache->cc_key[i]-1]->atttypid == INT28OID) + cache->cc_klen[i] = sizeof (short); + else + cache->cc_klen[i] = tupdesc->attrs[cache->cc_key[i]-1]->attlen; + + cache->cc_skey[i].sk_procedure = + EQPROC(tupdesc->attrs[cache->cc_key[i]-1]->atttypid); + + fmgr_info(cache->cc_skey[i].sk_procedure, + (func_ptr *) &cache->cc_skey[i].sk_func, + (int *) &cache->cc_skey[i].sk_nargs); + + CACHE5_elog(DEBUG, "CatalogCacheInit %16s %d %d %x", + &relation->rd_rel->relname, + i, + tupdesc->attrs[ cache->cc_key[i]-1 ]->attlen, + cache); + } + } + + /* ---------------- + * close the relation if we opened it + * ---------------- + */ + if (didopen) + heap_close(relation); + + /* ---------------- + * initialize index information for the cache. this + * should only be done once per cache. + * ---------------- + */ + if (cache->cc_indname != NULL && cache->indexId == InvalidOid) + { + if (RelationGetRelationTupleForm(relation)->relhasindex) + { + /* + * If the index doesn't exist we are in trouble. + */ + relation = index_openr( cache->cc_indname); + Assert(relation); + cache->indexId = RelationGetRelationId(relation); + index_close(relation); + } + else + cache->cc_indname = NULL; + } + + /* ---------------- + * return to the proper memory context + * ---------------- + */ + MemoryContextSwitchTo(oldcxt); +} + +/* -------------------------------- + * CatalogCacheSetId + * + * XXX temporary function + * -------------------------------- + */ +void +CatalogCacheSetId(CatCache *cacheInOutP, int id) +{ + Assert(id == InvalidCatalogCacheId || id >= 0); + cacheInOutP->id = id; +} + +/* ---------------- + * comphash -- + * Compute a hash value, somehow. + * + * XXX explain algorithm here. + * + * l is length of the attribute value, v + * v is the attribute value ("Datum") + * ---------------- + */ +long +comphash(long l, register char *v) +{ + long i; + NameData n; + + CACHE3_elog(DEBUG, "comphash (%d,%x)", l, v); + + switch (l) { + case 1: + case 2: + case 4: + return((long) v); + } + + if (l == NAMEDATALEN) { + /* if it's a name, make sure that the values + are null-padded. + + Note that this other fixed-length types can also have + the same typelen so this may break them - XXX + */ + namestrcpy(&n,v); + v = n.data; + } else + if (l < 0) + l = VARSIZE(v); + + i = 0; + while (l--) { + i += *v++; + } + return(i); +} + +/* -------------------------------- + * CatalogCacheComputeHashIndex + * -------------------------------- + */ +Index +CatalogCacheComputeHashIndex(struct catcache *cacheInP) +{ + Index hashIndex; + hashIndex = 0x0; + CACHE6_elog(DEBUG,"CatalogCacheComputeHashIndex %s %d %d %d %x", + cacheInP->cc_relname, + cacheInP->cc_nkeys, + cacheInP->cc_klen[0], + cacheInP->cc_klen[1], + cacheInP); + + switch (cacheInP->cc_nkeys) { + case 4: + hashIndex ^= comphash(cacheInP->cc_klen[3], + (char*)cacheInP->cc_skey[3].sk_argument) << 9; + /* FALLTHROUGH */ + case 3: + hashIndex ^= comphash(cacheInP->cc_klen[2], + (char*)cacheInP->cc_skey[2].sk_argument) << 6; + /* FALLTHROUGH */ + case 2: + hashIndex ^= comphash(cacheInP->cc_klen[1], + (char*)cacheInP->cc_skey[1].sk_argument) << 3; + /* FALLTHROUGH */ + case 1: + hashIndex ^= comphash(cacheInP->cc_klen[0], + (char*)cacheInP->cc_skey[0].sk_argument); + break; + default: + elog(FATAL, "CCComputeHashIndex: %d cc_nkeys", cacheInP->cc_nkeys); + break; + } + hashIndex %= cacheInP->cc_size; + return (hashIndex); +} + +/* -------------------------------- + * CatalogCacheComputeTupleHashIndex + * -------------------------------- + */ +Index +CatalogCacheComputeTupleHashIndex(struct catcache *cacheInOutP, + Relation relation, + HeapTuple tuple) +{ + bool isNull = '\0'; + if (cacheInOutP->relationId == InvalidOid) + CatalogCacheInitializeCache(cacheInOutP, relation); + switch (cacheInOutP->cc_nkeys) { + case 4: + cacheInOutP->cc_skey[3].sk_argument = + (cacheInOutP->cc_key[3] == ObjectIdAttributeNumber) + ? (Datum)tuple->t_oid + : (Datum)fastgetattr(tuple, + cacheInOutP->cc_key[3], + RelationGetTupleDescriptor(relation), + &isNull); + Assert(!isNull); + /* FALLTHROUGH */ + case 3: + cacheInOutP->cc_skey[2].sk_argument = + (cacheInOutP->cc_key[2] == ObjectIdAttributeNumber) + ? (Datum)tuple->t_oid + : (Datum)fastgetattr(tuple, + cacheInOutP->cc_key[2], + RelationGetTupleDescriptor(relation), + &isNull); + Assert(!isNull); + /* FALLTHROUGH */ + case 2: + cacheInOutP->cc_skey[1].sk_argument = + (cacheInOutP->cc_key[1] == ObjectIdAttributeNumber) + ? (Datum)tuple->t_oid + : (Datum)fastgetattr(tuple, + cacheInOutP->cc_key[1], + RelationGetTupleDescriptor(relation), + &isNull); + Assert(!isNull); + /* FALLTHROUGH */ + case 1: + cacheInOutP->cc_skey[0].sk_argument = + (cacheInOutP->cc_key[0] == ObjectIdAttributeNumber) + ? (Datum)tuple->t_oid + : (Datum)fastgetattr(tuple, + cacheInOutP->cc_key[0], + RelationGetTupleDescriptor(relation), + &isNull); + Assert(!isNull); + break; + default: + elog(FATAL, "CCComputeTupleHashIndex: %d cc_nkeys", + cacheInOutP->cc_nkeys + ); + break; + } + + return + CatalogCacheComputeHashIndex(cacheInOutP); +} + +/* -------------------------------- + * CatCacheRemoveCTup + * -------------------------------- + */ +void +CatCacheRemoveCTup(CatCache *cache, Dlelem *elt) +{ + CatCTup *ct; + CatCTup *other_ct; + Dlelem *other_elt; + + if (elt) + ct = (CatCTup*) DLE_VAL(elt); + else + return; + + other_elt = ct->ct_node; + other_ct = (CatCTup*)DLE_VAL(other_elt); + DLRemove(other_elt); + DLFreeElem(other_elt); + free(other_ct); + DLRemove(elt); + DLFreeElem(elt); + free(ct); + --cache->cc_ntup; +} + +/* -------------------------------- + * CatalogCacheIdInvalidate() + * + * Invalidate a tuple given a cache id. In this case the id should always + * be found (whether the cache has opened its relation or not). Of course, + * if the cache has yet to open its relation, there will be no tuples so + * no problem. + * -------------------------------- + */ +void +CatalogCacheIdInvalidate(int cacheId, /* XXX */ + Index hashIndex, + ItemPointer pointer) +{ + CatCache *ccp; + CatCTup *ct; + Dlelem *elt; + MemoryContext oldcxt; + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(hashIndex < NCCBUCK); + Assert(ItemPointerIsValid(pointer)); + CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: called"); + + /* ---------------- + * switch to the cache context for our memory allocations + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * inspect every cache that could contain the tuple + * ---------------- + */ + for (ccp = Caches; ccp; ccp = ccp->cc_next) { + if (cacheId != ccp->id) + continue; + /* ---------------- + * inspect the hash bucket until we find a match or exhaust + * ---------------- + */ + for (elt = DLGetHead(ccp->cc_cache[hashIndex]); + elt; + elt = DLGetSucc(elt)) + { + ct = (CatCTup*) DLE_VAL(elt); + if (ItemPointerEquals(pointer, &ct->ct_tup->t_ctid)) + break; + } + + /* ---------------- + * if we found a matching tuple, invalidate it. + * ---------------- + */ + + if (elt) { + CatCacheRemoveCTup(ccp, elt); + + CACHE1_elog(DEBUG, "CatalogCacheIdInvalidate: invalidated"); + } + + if (cacheId != InvalidCatalogCacheId) + break; + } + + /* ---------------- + * return to the proper memory context + * ---------------- + */ + MemoryContextSwitchTo(oldcxt); + /* sendpm('I', "Invalidated tuple"); */ +} + +/* ---------------------------------------------------------------- + * public functions + * + * ResetSystemCache + * InitIndexedSysCache + * InitSysCache + * SearchSysCache + * RelationInvalidateCatalogCacheTuple + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * ResetSystemCache + * -------------------------------- + */ +void +ResetSystemCache() +{ + MemoryContext oldcxt; + struct catcache *cache; + + /* ---------------- + * sanity checks + * ---------------- + */ + CACHE1_elog(DEBUG, "ResetSystemCache called"); + if (DisableCache) { + elog(WARN, "ResetSystemCache: Called while cache disabled"); + return; + } + + /* ---------------- + * first switch to the cache context so our allocations + * do not vanish at the end of a transaction + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * here we purge the contents of all the caches + * + * for each system cache + * for each hash bucket + * for each tuple in hash bucket + * remove the tuple + * ---------------- + */ + for (cache = Caches; PointerIsValid(cache); cache = cache->cc_next) { + int hash; + for (hash = 0; hash < NCCBUCK; hash += 1) { + Dlelem *elt, *nextelt; + for (elt = DLGetHead(cache->cc_cache[hash]); elt; elt = nextelt) { + nextelt = DLGetSucc(elt); + CatCacheRemoveCTup(cache, elt); + if (cache->cc_ntup == -1) + elog(WARN, "ResetSystemCache: cc_ntup<0 (software error)"); + } + } + cache->cc_ntup = 0; /* in case of WARN error above */ + } + + CACHE1_elog(DEBUG, "end of ResetSystemCache call"); + + /* ---------------- + * back to the old context before we return... + * ---------------- + */ + MemoryContextSwitchTo(oldcxt); +} + +/* -------------------------------- + * InitIndexedSysCache + * + * This allocates and initializes a cache for a system catalog relation. + * Actually, the cache is only partially initialized to avoid opening the + * relation. The relation will be opened and the rest of the cache + * structure initialized on the first access. + * -------------------------------- + */ +#ifdef CACHEDEBUG +#define InitSysCache_DEBUG1 \ +elog(DEBUG, "InitSysCache: rid=%d id=%d nkeys=%d size=%d\n", \ + cp->relationId, cp->id, cp->cc_nkeys, cp->cc_size); \ + for (i = 0; i < nkeys; i += 1) { \ + elog(DEBUG, "InitSysCache: key=%d len=%d skey=[%d %d %d %d]\n", \ + cp->cc_key[i], cp->cc_klen[i], \ + cp->cc_skey[i].sk_flags, \ + cp->cc_skey[i].sk_attno, \ + cp->cc_skey[i].sk_procedure, \ + cp->cc_skey[i].sk_argument); \ + } +#else +#define InitSysCache_DEBUG1 +#endif + +CatCache* +InitSysCache(char *relname, + char *iname, + int id, + int nkeys, + int key[], + HeapTuple (*iScanfuncP)()) +{ + CatCache *cp; + register int i; + MemoryContext oldcxt; + + char *indname; + + indname = (iname) ? iname : NULL; + + /* ---------------- + * first switch to the cache context so our allocations + * do not vanish at the end of a transaction + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * allocate a new cache structure + * ---------------- + */ + cp = (CatCache *)palloc(sizeof(CatCache)); + memset((char*)cp, 0, sizeof(CatCache)); + + /* ---------------- + * initialize the cache buckets (each bucket is a list header) + * and the LRU tuple list + * ---------------- + */ + for (i = 0; i <= NCCBUCK; ++i) { + cp->cc_cache[i] = DLNewList(); + } + + cp->cc_lrulist = DLNewList(); + + /* ---------------- + * Caches is the pointer to the head of the list of all the + * system caches. here we add the new cache to the top of the list. + * ---------------- + */ + cp->cc_next = Caches; /* list of caches (single link) */ + Caches = cp; + + /* ---------------- + * initialize the cache's relation information for the relation + * corresponding to this cache and initialize some of the the new + * cache's other internal fields. + * ---------------- + */ + cp->relationId = InvalidOid; + cp->indexId = InvalidOid; + cp->cc_relname = relname; + cp->cc_indname = indname; + cp->cc_tupdesc = (TupleDesc) NULL; + cp->id = id; + cp->cc_maxtup = MAXTUP; + cp->cc_size = NCCBUCK; + cp->cc_nkeys = nkeys; + cp->cc_iscanfunc = iScanfuncP; + + /* ---------------- + * initialize the cache's key information + * ---------------- + */ + for (i = 0; i < nkeys; ++i) { + cp->cc_key[i] = key[i]; + if (!key[i]) { + elog(FATAL, "InitSysCache: called with 0 key[%d]", i); + } + if (key[i] < 0) { + if (key[i] != ObjectIdAttributeNumber) { + elog(FATAL, "InitSysCache: called with %d key[%d]", key[i], i); + } else { + cp->cc_klen[i] = sizeof(Oid); + /* + * ScanKeyEntryData and struct skey are equivalent. It looks + * like a move was made to obsolete struct skey, but it + * didn't reach this file. Someday we should clean up this + * code and consolidate to ScanKeyEntry - mer 10 Nov 1991 + */ + ScanKeyEntryInitialize(&cp->cc_skey[i], + (bits16)0, + (AttrNumber)key[i], + (RegProcedure)F_OIDEQ, + (Datum)0); + continue; + } + } + + cp->cc_skey[i].sk_attno = key[i]; + } + + /* ---------------- + * all done. new cache is initialized. print some debugging + * information, if appropriate. + * ---------------- + */ + InitSysCache_DEBUG1; + + /* ---------------- + * back to the old context before we return... + * ---------------- + */ + MemoryContextSwitchTo(oldcxt); + return(cp); +} + + +/* -------------------------------- + * SearchSysCache + * + * This call searches a system cache for a tuple, opening the relation + * if necessary (the first access to a particular cache). + * -------------------------------- + */ +HeapTuple +SearchSysCache(struct catcache *cache, + Datum v1, + Datum v2, + Datum v3, + Datum v4) +{ + unsigned hash; + CatCTup *ct; + CatCTup *nct; + CatCTup *nct2; + Dlelem *elt; + HeapTuple ntp; + Buffer buffer; + + Relation relation; + MemoryContext oldcxt; + + /* ---------------- + * sanity checks + * ---------------- + */ + if (cache->relationId == InvalidOid) + CatalogCacheInitializeCache(cache, NULL); + + /* ---------------- + * initialize the search key information + * ---------------- + */ + cache->cc_skey[0].sk_argument = v1; + cache->cc_skey[1].sk_argument = v2; + cache->cc_skey[2].sk_argument = v3; + cache->cc_skey[3].sk_argument = v4; + + /* ---------------- + * find the hash bucket in which to look for the tuple + * ---------------- + */ + hash = CatalogCacheComputeHashIndex(cache); + + /* ---------------- + * scan the hash bucket until we find a match or exhaust our tuples + * ---------------- + */ + for (elt = DLGetHead(cache->cc_cache[hash]); + elt; + elt = DLGetSucc(elt)) + { + ct = (CatCTup*)DLE_VAL(elt); + /* ---------------- + * see if the cached tuple matches our key. + * (should we be worried about time ranges? -cim 10/2/90) + * ---------------- + */ + if (heap_keytest(ct->ct_tup, + cache->cc_tupdesc, + cache->cc_nkeys, + cache->cc_skey)) + break; + } + + /* ---------------- + * if we found a tuple in the cache, move it to the top of the + * lru list, and return it. + * ---------------- + */ + if (elt) { + Dlelem* old_lru_elt; + old_lru_elt = ((CatCTup*)DLE_VAL(elt))->ct_node; + DLRemove(old_lru_elt); + DLAddHead(cache->cc_lrulist, old_lru_elt); + +#ifdef CACHEDEBUG + relation = heap_open(cache->relationId); + CACHE3_elog(DEBUG, "SearchSysCache(%s): found in bucket %d", + RelationGetRelationName(relation), hash); + heap_close(relation); +#endif /* CACHEDEBUG */ + + return (ct->ct_tup); + } + + /* ---------------- + * Tuple was not found in cache, so we have to try and + * retrieve it directly from the relation. If it's found, + * we add it to the cache. We must avoid recursion here, + * so we disable cache operations. If operations are + * currently disabled and we couldn't find the requested item + * in the cache, then this may be a recursive request, and we + * abort with an error. + * ---------------- + */ + + if (DisableCache) { + elog(WARN, "SearchSysCache: Called while cache disabled"); + return((HeapTuple) NULL); + } + + /* ---------------- + * open the relation associated with the cache + * ---------------- + */ + relation = heap_open(cache->relationId); + CACHE2_elog(DEBUG, "SearchSysCache(%s)", + RelationGetRelationName(relation)); + + /* ---------------- + * DisableCache and then switch to the cache memory context. + * ---------------- + */ + DisableCache = 1; + + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * Scan the relation to find the tuple. If there's an index, and + * if this isn't bootstrap (initdb) time, use the index. + * ---------------- + */ + CACHE2_elog(DEBUG, "SearchSysCache: performing scan (override==%d)", + heapisoverride()); + + if ((RelationGetRelationTupleForm(relation))->relhasindex + && !IsBootstrapProcessingMode()) + { + Assert(cache->cc_iscanfunc); + switch(cache->cc_nkeys) + { + case 4: ntp = cache->cc_iscanfunc(relation,v1,v2,v3,v4); break; + case 3: ntp = cache->cc_iscanfunc(relation,v1,v2,v3); break; + case 2: ntp = cache->cc_iscanfunc(relation,v1,v2); break; + case 1: ntp = cache->cc_iscanfunc(relation,v1); break; + } + } + else + { + HeapScanDesc sd; + + sd = heap_beginscan(relation, 0, NowTimeQual, + cache->cc_nkeys, cache->cc_skey); + + ntp = heap_getnext(sd, 0, &buffer); + + if (HeapTupleIsValid(ntp)) { + CACHE1_elog(DEBUG, "SearchSysCache: found tuple"); + ntp = heap_copytuple(ntp); + } + + heap_endscan(sd); + } + + DisableCache = 0; + + /* ---------------- + * scan is complete. if tup is valid, we copy it and add the copy to + * the cache. + * ---------------- + */ + if (HeapTupleIsValid(ntp)) { + /* ---------------- + * allocate a new cache tuple holder, store the pointer + * to the heap tuple there and initialize the list pointers. + * ---------------- + */ + Dlelem *lru_elt; + + /* this is a little cumbersome here because we want the Dlelem's + in both doubly linked lists to point to one another. + That makes it easier to remove something from both the cache bucket + and the lru list at the same time */ + nct = (CatCTup*) malloc(sizeof(CatCTup)); + nct->ct_tup = ntp; + elt = DLNewElem(nct); + nct2 = (CatCTup*) malloc(sizeof(CatCTup)); + nct2->ct_tup = ntp; + lru_elt = DLNewElem(nct2); + nct2->ct_node = elt; + nct->ct_node = lru_elt; + + DLAddHead(cache->cc_lrulist, lru_elt); + DLAddHead(cache->cc_cache[hash], elt); + + /* ---------------- + * deal with hash bucket overflow + * ---------------- + */ + if (++cache->cc_ntup > cache->cc_maxtup) { + CatCTup *ct; + elt = DLGetTail(cache->cc_lrulist); + ct = (CatCTup *) DLE_VAL(elt); + + if (ct != nct) { + CACHE2_elog(DEBUG, "SearchSysCache(%s): Overflow, LRU removal", + RelationGetRelationName(relation)); + + CatCacheRemoveCTup(cache, elt); + + } + } + + CACHE4_elog(DEBUG, "SearchSysCache(%s): Contains %d/%d tuples", + RelationGetRelationName(relation), + cache->cc_ntup, cache->cc_maxtup); + CACHE3_elog(DEBUG, "SearchSysCache(%s): put in bucket %d", + RelationGetRelationName(relation), hash); + } + + /* ---------------- + * close the relation, switch back to the original memory context + * and return the tuple we found (or NULL) + * ---------------- + */ + heap_close(relation); + + MemoryContextSwitchTo(oldcxt); + return ntp; +} + +/* -------------------------------- + * RelationInvalidateCatalogCacheTuple() + * + * Invalidate a tuple from a specific relation. This call determines the + * cache in question and calls CatalogCacheIdInvalidate(). It is -ok- + * if the relation cannot be found, it simply means this backend has yet + * to open it. + * -------------------------------- + */ +void +RelationInvalidateCatalogCacheTuple(Relation relation, + HeapTuple tuple, + void (*function)()) +{ + struct catcache *ccp; + MemoryContext oldcxt; + Oid relationId; + + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(RelationIsValid(relation)); + Assert(HeapTupleIsValid(tuple)); + CACHE1_elog(DEBUG, "RelationInvalidateCatalogCacheTuple: called"); + + /* ---------------- + * switch to the cache memory context + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * for each cache + * if the cache contains tuples from the specified relation + * call the invalidation function on the tuples + * in the proper hash bucket + * ---------------- + */ + relationId = RelationGetRelationId(relation); + + for (ccp = Caches; ccp; ccp = ccp->cc_next) { + if (relationId != ccp->relationId) + continue; + + /* OPT inline simplification of CatalogCacheIdInvalidate */ + if (!PointerIsValid(function)) { + function = CatalogCacheIdInvalidate; + } + + (*function)(ccp->id, + CatalogCacheComputeTupleHashIndex(ccp, relation, tuple), + &tuple->t_ctid); + + heap_close(relation); + } + + /* ---------------- + * return to the proper memory context + * ---------------- + */ + MemoryContextSwitchTo(oldcxt); + + /* sendpm('I', "Invalidated tuple"); */ +} + diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c new file mode 100644 index 00000000000..070f457c280 --- /dev/null +++ b/src/backend/utils/cache/fcache.c @@ -0,0 +1,297 @@ +/*------------------------------------------------------------------------- + * + * fcache.c-- + * Code for the 'function cache' used in Oper and Func nodes.... + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "c.h" +#include "access/htup.h" +#include "utils/catcache.h" +#include "utils/syscache.h" +#include "catalog/pg_type.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_language.h" +#include "catalog/pg_class.h" +#include "parser/parsetree.h" /* for getrelname() */ +#include "utils/builtins.h" +#include "utils/fcache.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "nodes/primnodes.h" +#include "nodes/execnodes.h" + +static Oid GetDynamicFuncArgType(Var *arg, ExprContext *econtext); +static FunctionCachePtr init_fcache(Oid foid, + bool use_syscache, + List *argList, + ExprContext *econtext); + +/*----------------------------------------------------------------- + * + * Initialize the 'FunctionCache' given the PG_PROC oid. + * + * + * NOTE: This function can be called when the system cache is being + * initialized. Therefore, use_syscache should ONLY be true + * when the function return type is interesting (ie: set_fcache). + *----------------------------------------------------------------- + */ +#define FuncArgTypeIsDynamic(arg) \ + (IsA(arg,Var) && ((Var*)arg)->varattno == InvalidAttrNumber) + +static Oid +GetDynamicFuncArgType(Var *arg, ExprContext *econtext) +{ + char *relname; + int rtid; + HeapTuple tup; + + Assert(IsA(arg,Var)); + + rtid = ((Var*)arg)->varno; + relname = (char*)getrelname(rtid, econtext->ecxt_range_table); + + + tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(relname), + 0,0,0); + if (!tup) + elog(WARN, "Lookup failed on type tuple for class %s", + relname); + + return tup->t_oid; +} + +static FunctionCachePtr +init_fcache(Oid foid, + bool use_syscache, + List *argList, + ExprContext *econtext) +{ + HeapTuple procedureTuple; + HeapTuple typeTuple; + Form_pg_proc procedureStruct; + TypeTupleForm typeStruct; + FunctionCachePtr retval; + text *tmp; + int nargs; + + /* ---------------- + * get the procedure tuple corresponding to the given + * functionOid. If this fails, returnValue has been + * pre-initialized to "null" so we just return it. + * ---------------- + */ + retval = (FunctionCachePtr) palloc(sizeof(FunctionCache)); + + if (!use_syscache) + elog(WARN, "what the ????, init the fcache without the catalogs?"); + + procedureTuple = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(foid), + 0,0,0); + + if (!HeapTupleIsValid(procedureTuple)) + elog(WARN, + "init_fcache: %s %d", + "Cache lookup failed for procedure", foid); + + /* ---------------- + * get the return type from the procedure tuple + * ---------------- + */ + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + + /* ---------------- + * get the type tuple corresponding to the return type + * If this fails, returnValue has been pre-initialized + * to "null" so we just return it. + * ---------------- + */ + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(procedureStruct->prorettype), + 0,0,0); + + if (!HeapTupleIsValid(typeTuple)) + elog(WARN, + "init_fcache: %s %d", + "Cache lookup failed for type", + (procedureStruct)->prorettype); + + /* ---------------- + * get the type length and by-value from the type tuple and + * save the information in our one element cache. + * ---------------- + */ + typeStruct = (TypeTupleForm) GETSTRUCT(typeTuple); + + retval->typlen = (typeStruct)->typlen; + if ((typeStruct)->typrelid == InvalidOid) { + /* The return type is not a relation, so just use byval */ + retval->typbyval = (typeStruct)->typbyval ? true : false ; + } else { + /* This is a hack. We assume here that any function returning + * a relation returns it by reference. This needs to be + * fixed. + */ + retval->typbyval = false; + } + retval->foid = foid; + retval->language = procedureStruct->prolang; + retval->func_state = (char *)NULL; + retval->setArg = NULL; + retval->hasSetArg = false; + retval->oneResult = ! procedureStruct->proretset; + retval->istrusted = procedureStruct->proistrusted; + + /* + * If we are returning exactly one result then we have to copy + * tuples and by reference results because we have to end the execution + * before we return the results. When you do this everything allocated + * by the executor (i.e. slots and tuples) is freed. + */ + if ((retval->language == SQLlanguageId) && + (retval->oneResult) && + !(retval->typbyval)) + { + Form_pg_class relationStruct; + HeapTuple relationTuple; + TupleDesc td; + TupleTableSlot *slot; + + slot = makeNode(TupleTableSlot); + slot->ttc_shouldFree = true; + slot->ttc_descIsNew = true; + slot->ttc_tupleDescriptor = (TupleDesc) NULL; + slot->ttc_buffer = InvalidBuffer; + slot->ttc_whichplan = -1; + retval->funcSlot = (Pointer)slot; + + relationTuple = (HeapTuple) + SearchSysCacheTuple(RELNAME, + PointerGetDatum(&typeStruct->typname), + 0,0,0); + + if (relationTuple) + { + relationStruct = (Form_pg_class)GETSTRUCT(relationTuple); + td = CreateTemplateTupleDesc(relationStruct->relnatts); + } + else + td = CreateTemplateTupleDesc(1); + + ((TupleTableSlot*)retval->funcSlot)->ttc_tupleDescriptor = td; + } + else + retval->funcSlot = (char *)NULL; + + nargs = procedureStruct->pronargs; + retval->nargs = nargs; + + if (nargs > 0) + { + Oid *argTypes; + + retval->nullVect = (bool *)palloc((retval->nargs)*sizeof(bool)); + + if (retval->language == SQLlanguageId) + { + int i; + List *oneArg; + + retval->argOidVect = + (Oid *)palloc(retval->nargs*sizeof(Oid)); + argTypes = procedureStruct->proargtypes; + memmove(retval->argOidVect, + argTypes, + (retval->nargs)*sizeof(Oid)); + + for (i=0; + argList; + i++, argList = lnext(argList)) + { + oneArg = lfirst(argList); + if (FuncArgTypeIsDynamic(oneArg)) + retval->argOidVect[i] = GetDynamicFuncArgType((Var*)oneArg, + econtext); + } + } + else + retval->argOidVect = (Oid *)NULL; + } + else + { + retval->argOidVect = (Oid *)NULL; + retval->nullVect = (BoolPtr)NULL; + } + + /* + * XXX this is the first varlena in the struct. If the order + * changes for some reason this will fail. + */ + if (procedureStruct->prolang == SQLlanguageId) + { + retval->src = textout(&(procedureStruct->prosrc)); + retval->bin = (char *) NULL; + } + else + { + + /* + * I'm not sure that we even need to do this at all. + */ + + /* + * We do for untrusted functions. + */ + + if (procedureStruct->proistrusted) + retval->bin = (char *) NULL; + else { + tmp = (text *) + SearchSysCacheGetAttribute(PROOID, + Anum_pg_proc_probin, + ObjectIdGetDatum(foid), + 0,0,0); + retval->bin = textout(tmp); + } + retval->src = (char *) NULL; + } + + + + + if (retval->language != SQLlanguageId) + fmgr_info(foid, &(retval->func), &(retval->nargs)); + else + retval->func = (func_ptr)NULL; + + + return(retval); +} + +void +setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext) +{ + Func *fnode; + Oper *onode; + FunctionCachePtr fcache; + + fcache = init_fcache(foid, true, argList, econtext); + + if (IsA(node,Oper)) { + onode = (Oper*) node; + onode->op_fcache = fcache; + }else if (IsA(node,Func)) { + fnode = (Func*) node; + fnode->func_fcache = fcache; + }else { + elog(WARN, "init_fcache: node must be Oper or Func!"); + } +} diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c new file mode 100644 index 00000000000..7bd214d085b --- /dev/null +++ b/src/backend/utils/cache/inval.c @@ -0,0 +1,612 @@ +/*------------------------------------------------------------------------- + * + * inval.c-- + * POSTGRES cache invalidation dispatcher code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * Note - this code is real crufty... + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "access/heapam.h" /* XXX to support hacks below */ +#include "access/htup.h" +#include "catalog/catalog.h" +#include "storage/bufpage.h" +#include "storage/buf.h" /* XXX for InvalidBuffer */ +#include "storage/ipc.h" +#include "storage/sinval.h" +#include "utils/catcache.h" +#include "utils/inval.h" +#include "utils/elog.h" +#include "utils/rel.h" +#include "utils/relcache.h" +#include "catalog/catname.h" /* XXX to support hacks below */ +#include "utils/syscache.h" /* XXX to support the hacks below */ + +/* ---------------- + * private invalidation structures + * ---------------- + */ +typedef struct CatalogInvalidationData { + Index cacheId; + Index hashIndex; + ItemPointerData pointerData; +} CatalogInvalidationData; + +typedef struct RelationInvalidationData { + Oid relationId; + Oid objectId; +} RelationInvalidationData; + +typedef union AnyInvalidation { + CatalogInvalidationData catalog; + RelationInvalidationData relation; +} AnyInvalidation; + +typedef struct InvalidationMessageData { + char kind; + AnyInvalidation any; +} InvalidationMessageData; + +typedef InvalidationMessageData *InvalidationMessage; + +/* ---------------- + * variables and macros + * ---------------- + */ +static LocalInvalid Invalid = EmptyLocalInvalid; /* XXX global */ +static bool RefreshWhenInvalidate = false; + +Oid MyRelationRelationId = InvalidOid; +Oid MyAttributeRelationId = InvalidOid; +Oid MyAMRelationId = InvalidOid; +Oid MyAMOPRelationId = InvalidOid; + +#define ValidateHacks() \ + if (!OidIsValid(MyRelationRelationId)) getmyrelids() + +/* ---------------------------------------------------------------- + * "local" invalidation support functions + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * InvalidationEntryAllocate-- + * Allocates an invalidation entry. + * -------------------------------- + */ +InvalidationEntry +InvalidationEntryAllocate(uint16 size) +{ + InvalidationEntryData *entryDataP; + entryDataP = (InvalidationEntryData *) + malloc(sizeof (char *) + size); /* XXX alignment */ + entryDataP->nextP = NULL; + return ((Pointer) &entryDataP->userData); +} + +/* -------------------------------- + * LocalInvalidRegister -- + * Returns a new local cache invalidation state containing a new entry. + * -------------------------------- + */ +LocalInvalid +LocalInvalidRegister(LocalInvalid invalid, + InvalidationEntry entry) +{ + Assert(PointerIsValid(entry)); + + ((InvalidationUserData *)entry)->dataP[-1] = + (InvalidationUserData *)invalid; + + return (entry); +} + +/* -------------------------------- + * LocalInvalidInvalidate-- + * Processes, then frees all entries in a local cache + * invalidation state. + * -------------------------------- + */ +void +LocalInvalidInvalidate(LocalInvalid invalid, void (*function)()) +{ + InvalidationEntryData *entryDataP; + + while (PointerIsValid(invalid)) { + entryDataP = (InvalidationEntryData *) + &((InvalidationUserData *)invalid)->dataP[-1]; + + if (PointerIsValid(function)) { + (*function)((Pointer) &entryDataP->userData); + } + + invalid = (Pointer) entryDataP->nextP; + + /* help catch errors */ + entryDataP->nextP = (InvalidationUserData *) NULL; + + free((Pointer)entryDataP); + } +} + +/* ---------------------------------------------------------------- + * private support functions + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * CacheIdRegisterLocalInvalid + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define CacheIdRegisterLocalInvalid_DEBUG1 \ +elog(DEBUG, "CacheIdRegisterLocalInvalid(%d, %d, [%d, %d])", \ + cacheId, hashIndex, ItemPointerGetBlockNumber(pointer), \ + ItemPointerGetOffsetNumber(pointer)) +#else +#define CacheIdRegisterLocalInvalid_DEBUG1 +#endif /* INVALIDDEBUG */ + +static void +CacheIdRegisterLocalInvalid(Index cacheId, + Index hashIndex, + ItemPointer pointer) +{ + InvalidationMessage message; + + /* ---------------- + * debugging stuff + * ---------------- + */ + CacheIdRegisterLocalInvalid_DEBUG1; + + /* ---------------- + * create a message describing the system catalog tuple + * we wish to invalidate. + * ---------------- + */ + message = (InvalidationMessage) + InvalidationEntryAllocate(sizeof (InvalidationMessageData)); + + message->kind = 'c'; + message->any.catalog.cacheId = cacheId; + message->any.catalog.hashIndex = hashIndex; + + ItemPointerCopy(pointer, &message->any.catalog.pointerData); + + /* ---------------- + * Note: Invalid is a global variable + * ---------------- + */ + Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message); +} + +/* -------------------------------- + * RelationIdRegisterLocalInvalid + * -------------------------------- + */ +static void +RelationIdRegisterLocalInvalid(Oid relationId, Oid objectId) +{ + InvalidationMessage message; + + /* ---------------- + * debugging stuff + * ---------------- + */ +#ifdef INVALIDDEBUG + elog(DEBUG, "RelationRegisterLocalInvalid(%d, %d)", relationId, + objectId); +#endif /* defined(INVALIDDEBUG) */ + + /* ---------------- + * create a message describing the relation descriptor + * we wish to invalidate. + * ---------------- + */ + message = (InvalidationMessage) + InvalidationEntryAllocate(sizeof (InvalidationMessageData)); + + message->kind = 'r'; + message->any.relation.relationId = relationId; + message->any.relation.objectId = objectId; + + /* ---------------- + * Note: Invalid is a global variable + * ---------------- + */ + Invalid = LocalInvalidRegister(Invalid, (InvalidationEntry)message); +} + +/* -------------------------------- + * getmyrelids + * -------------------------------- + */ +void +getmyrelids() +{ + HeapTuple tuple; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(RelationRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyRelationRelationId = tuple->t_oid; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(AttributeRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyAttributeRelationId = tuple->t_oid; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(AccessMethodRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyAMRelationId = tuple->t_oid; + + tuple = SearchSysCacheTuple(RELNAME, + PointerGetDatum(AccessMethodOperatorRelationName), + 0,0,0); + Assert(HeapTupleIsValid(tuple)); + MyAMOPRelationId = tuple->t_oid; +} + +/* -------------------------------- + * CacheIdInvalidate + * + * This routine can invalidate an tuple in a system catalog cache + * or a cached relation descriptor. You pay your money and you + * take your chances... + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define CacheIdInvalidate_DEBUG1 \ +elog(DEBUG, "CacheIdInvalidate(%d, %d, 0x%x[%d])", cacheId, hashIndex,\ + pointer, ItemPointerIsValid(pointer)) +#else +#define CacheIdInvalidate_DEBUG1 +#endif /* defined(INVALIDDEBUG) */ + +static void +CacheIdInvalidate(Index cacheId, + Index hashIndex, + ItemPointer pointer) +{ + /* ---------------- + * assume that if the item pointer is valid, then we are + * invalidating an item in the specified system catalog cache. + * ---------------- + */ + if (ItemPointerIsValid(pointer)) { + CatalogCacheIdInvalidate(cacheId, hashIndex, pointer); + return; + } + + CacheIdInvalidate_DEBUG1; + + ValidateHacks(); /* XXX */ + + /* ---------------- + * if the cacheId is the oid of any of the tuples in the + * following system relations, then assume we are invalidating + * a relation descriptor + * ---------------- + */ + if (cacheId == MyRelationRelationId) { + RelationIdInvalidateRelationCacheByRelationId(hashIndex); + return; + } + + if (cacheId == MyAttributeRelationId) { + RelationIdInvalidateRelationCacheByRelationId(hashIndex); + return; + } + + if (cacheId == MyAMRelationId) { + RelationIdInvalidateRelationCacheByAccessMethodId(hashIndex); + return; + } + + if (cacheId == MyAMOPRelationId) { + RelationIdInvalidateRelationCacheByAccessMethodId(InvalidOid); + return; + } + + /* ---------------- + * Yow! the caller asked us to invalidate something else. + * ---------------- + */ + elog(FATAL, "CacheIdInvalidate: cacheId=%d relation id?", cacheId); +} + +/* -------------------------------- + * ResetSystemCaches + * + * this blows away all tuples in the system catalog caches and + * all the cached relation descriptors (and closes the files too). + * -------------------------------- + */ +static void +ResetSystemCaches() +{ + ResetSystemCache(); + RelationCacheInvalidate(false); +} + +/* -------------------------------- + * InvalidationMessageRegisterSharedInvalid + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define InvalidationMessageRegisterSharedInvalid_DEBUG1 \ +elog(DEBUG,\ + "InvalidationMessageRegisterSharedInvalid(c, %d, %d, [%d, %d])",\ + message->any.catalog.cacheId,\ + message->any.catalog.hashIndex,\ + ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\ + ItemPointerGetOffsetNumber(&message->any.catalog.pointerData)) +#define InvalidationMessageRegisterSharedInvalid_DEBUG2 \ + elog(DEBUG, \ + "InvalidationMessageRegisterSharedInvalid(r, %d, %d)", \ + message->any.relation.relationId, \ + message->any.relation.objectId) +#else +#define InvalidationMessageRegisterSharedInvalid_DEBUG1 +#define InvalidationMessageRegisterSharedInvalid_DEBUG2 +#endif /* INVALIDDEBUG */ + +static void +InvalidationMessageRegisterSharedInvalid(InvalidationMessage message) +{ + Assert(PointerIsValid(message)); + + switch (message->kind) { + case 'c': /* cached system catalog tuple */ + InvalidationMessageRegisterSharedInvalid_DEBUG1; + + RegisterSharedInvalid(message->any.catalog.cacheId, + message->any.catalog.hashIndex, + &message->any.catalog.pointerData); + break; + + case 'r': /* cached relation descriptor */ + InvalidationMessageRegisterSharedInvalid_DEBUG2; + + RegisterSharedInvalid(message->any.relation.relationId, + message->any.relation.objectId, + (ItemPointer) NULL); + break; + + default: + elog(FATAL, + "InvalidationMessageRegisterSharedInvalid: `%c' kind", + message->kind); + } +} + +/* -------------------------------- + * InvalidationMessageCacheInvalidate + * -------------------------------- + */ +#ifdef INVALIDDEBUG +#define InvalidationMessageCacheInvalidate_DEBUG1 \ +elog(DEBUG, "InvalidationMessageCacheInvalidate(c, %d, %d, [%d, %d])",\ + message->any.catalog.cacheId,\ + message->any.catalog.hashIndex,\ + ItemPointerGetBlockNumber(&message->any.catalog.pointerData),\ + ItemPointerGetOffsetNumber(&message->any.catalog.pointerData)) +#define InvalidationMessageCacheInvalidate_DEBUG2 \ + elog(DEBUG, "InvalidationMessageCacheInvalidate(r, %d, %d)", \ + message->any.relation.relationId, \ + message->any.relation.objectId) +#else +#define InvalidationMessageCacheInvalidate_DEBUG1 +#define InvalidationMessageCacheInvalidate_DEBUG2 +#endif /* defined(INVALIDDEBUG) */ + +static void +InvalidationMessageCacheInvalidate(InvalidationMessage message) +{ + Assert(PointerIsValid(message)); + + switch (message->kind) { + case 'c': /* cached system catalog tuple */ + InvalidationMessageCacheInvalidate_DEBUG1; + + CatalogCacheIdInvalidate(message->any.catalog.cacheId, + message->any.catalog.hashIndex, + &message->any.catalog.pointerData); + break; + + case 'r': /* cached relation descriptor */ + InvalidationMessageCacheInvalidate_DEBUG2; + + /* XXX ignore this--is this correct ??? */ + break; + + default: + elog(FATAL, "InvalidationMessageCacheInvalidate: `%c' kind", + message->kind); + } +} + +/* -------------------------------- + * RelationInvalidateRelationCache + * -------------------------------- + */ +static void +RelationInvalidateRelationCache(Relation relation, + HeapTuple tuple, + void (*function)()) +{ + Oid relationId; + Oid objectId; + + /* ---------------- + * get the relation object id + * ---------------- + */ + ValidateHacks(); /* XXX */ + relationId = RelationGetRelationId(relation); + + /* ---------------- + * + * ---------------- + */ + if (relationId == MyRelationRelationId) { + objectId = tuple->t_oid; + } else if (relationId == MyAttributeRelationId) { + objectId = ((AttributeTupleForm)GETSTRUCT(tuple))->attrelid; + } else if (relationId == MyAMRelationId) { + objectId = tuple->t_oid; + } else if (relationId == MyAMOPRelationId) { + ; /* objectId is unused */ + } else + return; + + /* ---------------- + * can't handle immediate relation descriptor invalidation + * ---------------- + */ + Assert(PointerIsValid(function)); + + (*function)(relationId, objectId); +} + +/* + * DiscardInvalid -- + * Causes the invalidated cache state to be discarded. + * + * Note: + * This should be called as the first step in processing a transaction. + * This should be called while waiting for a query from the front end + * when other backends are active. + */ +void +DiscardInvalid() +{ + /* ---------------- + * debugging stuff + * ---------------- + */ +#ifdef INVALIDDEBUG + elog(DEBUG, "DiscardInvalid called"); +#endif /* defined(INVALIDDEBUG) */ + + InvalidateSharedInvalid(CacheIdInvalidate, ResetSystemCaches); +} + +/* + * RegisterInvalid -- + * Causes registration of invalidated state with other backends iff true. + * + * Note: + * This should be called as the last step in processing a transaction. + */ +void +RegisterInvalid(bool send) +{ + /* ---------------- + * debugging stuff + * ---------------- + */ +#ifdef INVALIDDEBUG + elog(DEBUG, "RegisterInvalid(%d) called", send); +#endif /* defined(INVALIDDEBUG) */ + + /* ---------------- + * Note: Invalid is a global variable + * ---------------- + */ + if (send) + LocalInvalidInvalidate(Invalid, + InvalidationMessageRegisterSharedInvalid); + else + LocalInvalidInvalidate(Invalid, + InvalidationMessageCacheInvalidate); + + Invalid = EmptyLocalInvalid; +} + +/* + * SetRefreshWhenInvalidate -- + * Causes the local caches to be immediately refreshed iff true. + */ +void +SetRefreshWhenInvalidate(bool on) +{ +#ifdef INVALIDDEBUG + elog(DEBUG, "RefreshWhenInvalidate(%d) called", on); +#endif /* defined(INVALIDDEBUG) */ + + RefreshWhenInvalidate = on; +} + +/* + * RelationIdInvalidateHeapTuple -- + * Causes the given tuple in a relation to be invalidated. + * + * Note: + * Assumes object id is valid. + * Assumes tuple is valid. + */ +#ifdef INVALIDDEBUG +#define RelationInvalidateHeapTuple_DEBUG1 \ +elog(DEBUG, "RelationInvalidateHeapTuple(%.16s, [%d,%d])", \ + RelationGetRelationName(relation), \ + ItemPointerGetBlockNumber(&tuple->t_ctid), \ + ItemPointerGetOffsetNumber(&tuple->t_ctid)) +#else +#define RelationInvalidateHeapTuple_DEBUG1 +#endif /* defined(INVALIDDEBUG) */ + +void +RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple) +{ + /* ---------------- + * sanity checks + * ---------------- + */ + Assert(RelationIsValid(relation)); + Assert(HeapTupleIsValid(tuple)); + + if (IsBootstrapProcessingMode()) + return; + /* ---------------- + * this only works for system relations now + * ---------------- + */ + if (! IsSystemRelationName(RelationGetRelationTupleForm(relation)->relname.data)) + return; + + /* ---------------- + * debugging stuff + * ---------------- + */ + RelationInvalidateHeapTuple_DEBUG1; + + /* ---------------- + * + * ---------------- + */ + RelationInvalidateCatalogCacheTuple(relation, + tuple, + CacheIdRegisterLocalInvalid); + + RelationInvalidateRelationCache(relation, + tuple, + RelationIdRegisterLocalInvalid); + + if (RefreshWhenInvalidate) + RelationInvalidateCatalogCacheTuple(relation, + tuple, + (void (*)()) NULL); +} + diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c new file mode 100644 index 00000000000..b7fc5e26201 --- /dev/null +++ b/src/backend/utils/cache/lsyscache.c @@ -0,0 +1,484 @@ +/*------------------------------------------------------------------------- + * + * lsyscache.c-- + * Routines to access information within system caches + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + * NOTES + * Eventually, the index information should go through here, too. + * + * Most of these routines call SearchSysCacheStruct() and thus simply + * (1) allocate some space for the return struct and (2) call it. + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" + +#include "nodes/pg_list.h" +#include "utils/syscache.h" +#include "utils/lsyscache.h" +#include "access/tupmacs.h" +#include "utils/rel.h" +#include "utils/palloc.h" +#include "utils/elog.h" +#include "access/attnum.h" +#include "access/heapam.h" + +#include "catalog/pg_amop.h" +#include "catalog/pg_type.h" + +/* ---------- AMOP CACHES ---------- */ + +/* + * op_class - + * + * Return t iff operator 'opno' is in operator class 'opclass'. + * + */ +bool +op_class(Oid opno, int32 opclass, Oid amopid) +{ + FormData_pg_amop amoptup; + + if (SearchSysCacheStruct(AMOPOPID, + (char *) &amoptup, + ObjectIdGetDatum(opclass), + ObjectIdGetDatum(opno), + ObjectIdGetDatum(amopid), + 0)) + return(true); + else + return(false); +} + +/* ---------- ATTRIBUTE CACHES ---------- */ + +/* + * get_attname - + * + * Given the relation id and the attribute number, + * return the "attname" field from the attribute relation. + * + */ +char* +get_attname(Oid relid, AttrNumber attnum) +{ + FormData_pg_attribute att_tup; + char *retval; + + if (SearchSysCacheStruct(ATTNUM, + (char*)&att_tup, + ObjectIdGetDatum(relid), + UInt16GetDatum(attnum), + 0,0)) { + retval = pstrdup(att_tup.attname.data); + + return(retval); + } + else + return(NULL); +} + +/* + * get_attnum - + * + * Given the relation id and the attribute name, + * return the "attnum" field from the attribute relation. + * + */ +AttrNumber +get_attnum(Oid relid, char *attname) +{ + FormData_pg_attribute att_tup; + + if (SearchSysCacheStruct(ATTNAME, (char *) &att_tup, + ObjectIdGetDatum(relid), + PointerGetDatum(attname), + 0,0)) + return(att_tup.attnum); + else + return(InvalidAttrNumber); +} + +/* + * get_atttype - + * + * Given the relation OID and the attribute number with the relation, + * return the attribute type OID. + * + */ +Oid +get_atttype(Oid relid, AttrNumber attnum) +{ + AttributeTupleForm att_tup = (AttributeTupleForm)palloc(sizeof(*att_tup)); + + if (SearchSysCacheStruct(ATTNUM, + (char *) att_tup, + ObjectIdGetDatum(relid), + UInt16GetDatum(attnum), + 0,0)) + return(att_tup->atttypid); + else + return((Oid)NULL); +} + +/* This routine uses the attname instead of the attnum because it + * replaces the routine find_atttype, which is called sometimes when + * only the attname, not the attno, is available. + */ +bool +get_attisset(Oid relid, char *attname) +{ + HeapTuple htup; + AttrNumber attno; + AttributeTupleForm att_tup; + + attno = get_attnum(relid, attname); + + htup = SearchSysCacheTuple(ATTNAME, + ObjectIdGetDatum(relid), + PointerGetDatum(attname), + 0,0); + if (!HeapTupleIsValid(htup)) + elog(WARN, "get_attisset: no attribute %.16s in relation %d", + attname, relid); + if (heap_attisnull(htup, attno)) + return(false); + else { + att_tup = (AttributeTupleForm)GETSTRUCT(htup); + return(att_tup->attisset); + } +} + +/* ---------- INDEX CACHE ---------- */ + +/* watch this space... + */ + +/* ---------- OPERATOR CACHE ---------- */ + +/* + * get_opcode - + * + * Returns the regproc id of the routine used to implement an + * operator given the operator uid. + * + */ +RegProcedure +get_opcode(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprcode); + else + return((RegProcedure)NULL); +} + +/* + * get_opname - + * returns the name of the operator with the given opno + * + * Note: return the struct so that it gets copied. + */ +char* +get_opname(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return (pstrdup(optup.oprname.data)); + else { + elog(WARN, "can't look up operator %d\n", opno); + return NULL; + } +} + +/* + * op_mergesortable - + * + * Returns the left and right sort operators and types corresponding to a + * mergesortable operator, or nil if the operator is not mergesortable. + * + */ +bool +op_mergesortable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0) && + optup.oprlsortop && + optup.oprrsortop && + optup.oprleft == ltype && + optup.oprright == rtype) { + + *leftOp = ObjectIdGetDatum(optup.oprlsortop); + *rightOp = ObjectIdGetDatum(optup.oprrsortop); + return TRUE; + } else { + return FALSE; + } +} + +/* + * op_hashjoinable-- + * + * Returns the hash operator corresponding to a hashjoinable operator, + * or nil if the operator is not hashjoinable. + * + */ +Oid +op_hashjoinable(Oid opno, Oid ltype, Oid rtype) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0) && + optup.oprcanhash && + optup.oprleft == ltype && + optup.oprright == rtype) + return(opno); + else + return(InvalidOid); +} + +/* + * get_commutator - + * + * Returns the corresponding commutator of an operator. + * + */ +Oid +get_commutator(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprcom); + else + return((Oid)NULL); +} + +HeapTuple +get_operator_tuple(Oid opno) +{ + HeapTuple optup; + + if ((optup = SearchSysCacheTuple(OPROID, + ObjectIdGetDatum(opno), + 0,0,0))) + return(optup); + else + return((HeapTuple)NULL); +} + +/* + * get_negator - + * + * Returns the corresponding negator of an operator. + * + */ +Oid +get_negator(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprnegate); + else + return((Oid)NULL); +} + +/* + * get_oprrest - + * + * Returns procedure id for computing selectivity of an operator. + * + */ +RegProcedure +get_oprrest(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprrest ); + else + return((RegProcedure) NULL); +} + +/* + * get_oprjoin - + * + * Returns procedure id for computing selectivity of a join. + * + */ +RegProcedure +get_oprjoin(Oid opno) +{ + FormData_pg_operator optup; + + if (SearchSysCacheStruct(OPROID, (char *) &optup, + ObjectIdGetDatum(opno), + 0,0,0)) + return(optup.oprjoin); + else + return((RegProcedure)NULL); +} + +/* ---------- RELATION CACHE ---------- */ + +/* + * get_relnatts - + * + * Returns the number of attributes for a given relation. + * + */ +int +get_relnatts(Oid relid) +{ + FormData_pg_class reltup; + + if (SearchSysCacheStruct(RELOID, (char *) &reltup, + ObjectIdGetDatum(relid), + 0,0,0)) + return(reltup.relnatts); + else + return(InvalidAttrNumber); +} + +/* + * get_rel_name - + * + * Returns the name of a given relation. + * + */ +char* +get_rel_name(Oid relid) +{ + FormData_pg_class reltup; + + if ((SearchSysCacheStruct(RELOID, + (char*)&reltup, + ObjectIdGetDatum(relid), + 0,0,0))) { + return (pstrdup(reltup.relname.data)); + } else + return(NULL); +} + +/* ---------- TYPE CACHE ---------- */ + +/* + * get_typlen - + * + * Given the type OID, return the length of the type. + * + */ +int16 +get_typlen(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) + return(typtup.typlen); + else + return((int16)NULL); +} + +/* + * get_typbyval - + * + * Given the type OID, determine whether the type is returned by value or + * not. Returns 1 if by value, 0 if by reference. + * + */ +bool +get_typbyval(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) + return((bool)typtup.typbyval); + else + return(false); +} + +/* + * get_typbyval - + * + * Given the type OID, determine whether the type is returned by value or + * not. Returns 1 if by value, 0 if by reference. + * + */ +char +get_typalign(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) + return(typtup.typalign); + else + return ('i'); +} + +/* + * get_typdefault - + * + * Given the type OID, return the default value of the ADT. + * + */ +struct varlena * +get_typdefault(Oid typid) +{ + struct varlena *typdefault = + (struct varlena *)TypeDefaultRetrieve (typid); + return(typdefault); +} + +/* + * get_typtype - + * + * Given the type OID, find if it is a basic type, a named relation + * or the generic type 'relation'. + * It returns the null char if the cache lookup fails... + * + */ +char +get_typtype(Oid typid) +{ + TypeTupleFormData typtup; + + if (SearchSysCacheStruct(TYPOID, (char *) &typtup, + ObjectIdGetDatum(typid), + 0,0,0)) { + return(typtup.typtype); + } else { + return('\0'); + } +} + diff --git a/src/backend/utils/cache/rel.c b/src/backend/utils/cache/rel.c new file mode 100644 index 00000000000..33eabad1a85 --- /dev/null +++ b/src/backend/utils/cache/rel.c @@ -0,0 +1,77 @@ +/*------------------------------------------------------------------------- + * + * rel.c-- + * POSTGRES relation descriptor code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/rel.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* #define RELREFDEBUG 1 */ + +#include "postgres.h" +#include "miscadmin.h" +#include "access/istrat.h" +#include "access/tupdesc.h" +#include "utils/rel.h" +#include "storage/fd.h" + + +/* + * RelationIsValid is now a macro in rel.h -cim 4/27/91 + * + * Many of the RelationGet...() functions are now macros in rel.h + * -mer 3/2/92 + */ + +/* + * RelationGetIndexStrategy -- + * Returns index strategy for a relation. + * + * Note: + * Assumes relation descriptor is valid. + * Assumes relation descriptor is for an index relation. + */ +IndexStrategy +RelationGetIndexStrategy(Relation relation) +{ + return relation->rd_istrat; +} + +/* + * RelationSetIndexSupport -- + * Sets index strategy and support info for a relation. + * + * Note: + * Assumes relation descriptor is a valid pointer to sufficient space. + * Assumes index strategy is valid. Assumes support is valid if non- + * NULL. + */ +/* ---------------- + * RelationSetIndexSupport + * + * This routine saves two pointers -- one to the IndexStrategy, and + * one to the RegProcs that support the indexed access method. These + * pointers are stored in the space following the attribute data in the + * reldesc. + * + * NEW: the index strategy and support are now stored in real fields + * at the end of the structure - jolly + * ---------------- + */ +void +RelationSetIndexSupport(Relation relation, + IndexStrategy strategy, + RegProcedure *support) +{ + Assert(PointerIsValid(relation)); + Assert(IndexStrategyIsValid(strategy)); + + relation->rd_istrat = strategy; + relation->rd_support = support; +} + diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c new file mode 100644 index 00000000000..4e3f28491b7 --- /dev/null +++ b/src/backend/utils/cache/relcache.c @@ -0,0 +1,1795 @@ +/*------------------------------------------------------------------------- + * + * relcache.c-- + * POSTGRES relation descriptor cache code + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.1.1.1 1996/07/09 06:22:06 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * INTERFACE ROUTINES + * RelationInitialize - initialize relcache + * RelationIdCacheGetRelation - get a reldesc from the cache (id) + * RelationNameCacheGetRelation - get a reldesc from the cache (name) + * RelationIdGetRelation - get a reldesc by relation id + * RelationNameGetRelation - get a reldesc by relation name + * RelationClose - close an open relation + * RelationFlushRelation - flush relation information + * + * NOTES + * This file is in the process of being cleaned up + * before I add system attribute indexing. -cim 1/13/91 + * + * The following code contains many undocumented hacks. Please be + * careful.... + * + */ +#include <stdio.h> /* for sprintf() */ +#include <errno.h> +#include <sys/file.h> +#include <string.h> + +#include "postgres.h" +#include "miscadmin.h" + +#include "access/attnum.h" +#include "access/genam.h" +#include "access/heapam.h" +#include "access/htup.h" +#include "access/istrat.h" +#include "access/itup.h" +#include "access/skey.h" +#include "utils/builtins.h" +#include "utils/tqual.h" /* for NowTimeQual */ +#include "access/tupdesc.h" +#include "access/tupmacs.h" +#include "access/xact.h" + +#include "storage/buf.h" +#include "storage/fd.h" /* for SEEK_ */ +#include "storage/lmgr.h" +#include "storage/bufmgr.h" + + +#include "lib/hasht.h" + +#include "utils/memutils.h" +#include "utils/elog.h" +#include "utils/mcxt.h" +#include "utils/rel.h" +#include "utils/relcache.h" +#include "utils/hsearch.h" +#include "utils/palloc.h" +#include "utils/relcache.h" + +#include "catalog/catname.h" +#include "catalog/catalog.h" +#include "utils/syscache.h" + +#include "catalog/pg_attribute.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_index.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_class.h" +#include "catalog/pg_rewrite.h" +#include "catalog/pg_type.h" + +#include "catalog/pg_variable.h" +#include "catalog/pg_log.h" +#include "catalog/pg_time.h" +#include "catalog/indexing.h" +#include "catalog/index.h" +#include "fmgr.h" + +/* ---------------- + * defines + * ---------------- + */ +#define private static +#define INIT_FILENAME "pg_internal.init" + +/* ---------------- + * externs + * ---------------- + */ +extern bool AMI_OVERRIDE; /* XXX style */ +extern GlobalMemory CacheCxt; /* from utils/cache/catcache.c */ + +/* ---------------- + * hardcoded tuple descriptors. see lib/backend/catalog/pg_attribute.h + * ---------------- + */ +FormData_pg_attribute Desc_pg_class[Natts_pg_class] = { Schema_pg_class }; +FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = { Schema_pg_attribute }; +FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = { Schema_pg_proc }; +FormData_pg_attribute Desc_pg_type[Natts_pg_type] = { Schema_pg_type }; +FormData_pg_attribute Desc_pg_variable[Natts_pg_variable] = { Schema_pg_variable }; +FormData_pg_attribute Desc_pg_log[Natts_pg_log] = { Schema_pg_log }; +FormData_pg_attribute Desc_pg_time[Natts_pg_time] = { Schema_pg_time }; + +/* ---------------- + * global variables + * + * Relations are cached two ways, by name and by id, + * thus there are two hash tables for referencing them. + * ---------------- + */ +HTAB *RelationNameCache; +HTAB *RelationIdCache; + +/* ---------------- + * RelationBuildDescInfo exists so code can be shared + * between RelationIdGetRelation() and RelationNameGetRelation() + * ---------------- + */ +typedef struct RelationBuildDescInfo { + int infotype; /* lookup by id or by name */ +#define INFO_RELID 1 +#define INFO_RELNAME 2 + union { + Oid info_id; /* relation object id */ + char *info_name; /* relation name */ + } i; +} RelationBuildDescInfo; + +typedef struct relidcacheent { + Oid reloid; + Relation reldesc; +} RelIdCacheEnt; + +typedef struct relnamecacheent { + NameData relname; + Relation reldesc; +} RelNameCacheEnt; + +/* ----------------- + * macros to manipulate name cache and id cache + * ----------------- + */ +#define RelationCacheInsert(RELATION) \ + { RelIdCacheEnt *idhentry; RelNameCacheEnt *namehentry; \ + char *relname; Oid reloid; bool found; \ + relname = (RELATION->rd_rel->relname).data; \ + namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \ + relname, \ + HASH_ENTER, \ + &found); \ + if (namehentry == NULL) { \ + elog(FATAL, "can't insert into relation descriptor cache"); \ + } \ + if (found && !IsBootstrapProcessingMode()) { \ + /* used to give notice -- now just keep quiet */ ; \ + } \ + namehentry->reldesc = RELATION; \ + reloid = RELATION->rd_id; \ + idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ + (char *)&reloid, \ + HASH_ENTER, \ + &found); \ + if (idhentry == NULL) { \ + elog(FATAL, "can't insert into relation descriptor cache"); \ + } \ + if (found && !IsBootstrapProcessingMode()) { \ + /* used to give notice -- now just keep quiet */ ; \ + } \ + idhentry->reldesc = RELATION; \ + } +#define RelationNameCacheLookup(NAME, RELATION) \ + { RelNameCacheEnt *hentry; bool found; \ + hentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \ + (char *)NAME,HASH_FIND,&found); \ + if (hentry == NULL) { \ + elog(FATAL, "error in CACHE"); \ + } \ + if (found) { \ + RELATION = hentry->reldesc; \ + } \ + else { \ + RELATION = NULL; \ + } \ + } +#define RelationIdCacheLookup(ID, RELATION) \ + { RelIdCacheEnt *hentry; bool found; \ + hentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ + (char *)&(ID),HASH_FIND, &found); \ + if (hentry == NULL) { \ + elog(FATAL, "error in CACHE"); \ + } \ + if (found) { \ + RELATION = hentry->reldesc; \ + } \ + else { \ + RELATION = NULL; \ + } \ + } +#define RelationCacheDelete(RELATION) \ + { RelNameCacheEnt *namehentry; RelIdCacheEnt *idhentry; \ + char *relname; Oid reloid; bool found; \ + relname = (RELATION->rd_rel->relname).data; \ + namehentry = (RelNameCacheEnt*)hash_search(RelationNameCache, \ + relname, \ + HASH_REMOVE, \ + &found); \ + if (namehentry == NULL) { \ + elog(FATAL, "can't delete from relation descriptor cache"); \ + } \ + if (!found) { \ + elog(NOTICE, "trying to delete a reldesc that does not exist."); \ + } \ + reloid = RELATION->rd_id; \ + idhentry = (RelIdCacheEnt*)hash_search(RelationIdCache, \ + (char *)&reloid, \ + HASH_REMOVE, &found); \ + if (idhentry == NULL) { \ + elog(FATAL, "can't delete from relation descriptor cache"); \ + } \ + if (!found) { \ + elog(NOTICE, "trying to delete a reldesc that does not exist."); \ + } \ + } + +/* non-export function prototypes */ +static void formrdesc(char *relationName, u_int natts, + FormData_pg_attribute att[]); + +static void RelationFlushIndexes(Relation *r, Oid accessMethodId); + +static char *BuildDescInfoError(RelationBuildDescInfo buildinfo); +static HeapTuple ScanPgRelation(RelationBuildDescInfo buildinfo); +static HeapTuple scan_pg_rel_seq(RelationBuildDescInfo buildinfo); +static HeapTuple scan_pg_rel_ind(RelationBuildDescInfo buildinfo); +static Relation AllocateRelationDesc(u_int natts, Form_pg_class relp); +static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, + Relation relation, AttributeTupleForm attp, u_int natts); +static void build_tupdesc_seq(RelationBuildDescInfo buildinfo, + Relation relation, AttributeTupleForm attp, u_int natts); +static void build_tupdesc_ind(RelationBuildDescInfo buildinfo, + Relation relation, AttributeTupleForm attp, u_int natts); +static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo); +static void IndexedAccessMethodInitialize(Relation relation); + +/* ---------------------------------------------------------------- + * RelationIdGetRelation() and RelationNameGetRelation() + * support functions + * ---------------------------------------------------------------- + */ + + +/* -------------------------------- + * BuildDescInfoError returns a string appropriate to + * the buildinfo passed to it + * -------------------------------- + */ +static char * +BuildDescInfoError(RelationBuildDescInfo buildinfo) +{ + static char errBuf[64]; + + memset(errBuf, 0, (int) sizeof(errBuf)); + switch(buildinfo.infotype) { + case INFO_RELID: + sprintf(errBuf, "(relation id %d)", buildinfo.i.info_id); + break; + case INFO_RELNAME: + sprintf(errBuf, "(relation name %.*s)", NAMEDATALEN, buildinfo.i.info_name); + break; + } + + return errBuf; +} + +/* -------------------------------- + * ScanPgRelation + * + * this is used by RelationBuildDesc to find a pg_class + * tuple matching either a relation name or a relation id + * as specified in buildinfo. + * -------------------------------- + */ +static HeapTuple +ScanPgRelation(RelationBuildDescInfo buildinfo) +{ + /* + * If this is bootstrap time (initdb), then we can't use the system + * catalog indices, because they may not exist yet. Otherwise, we + * can, and do. + */ + + if (IsBootstrapProcessingMode()) + return (scan_pg_rel_seq(buildinfo)); + else + return (scan_pg_rel_ind(buildinfo)); +} + +static HeapTuple +scan_pg_rel_seq(RelationBuildDescInfo buildinfo) +{ + HeapTuple pg_class_tuple; + HeapTuple return_tuple; + Relation pg_class_desc; + HeapScanDesc pg_class_scan; + ScanKeyData key; + Buffer buf; + + /* ---------------- + * form a scan key + * ---------------- + */ + switch (buildinfo.infotype) { + case INFO_RELID: + ScanKeyEntryInitialize(&key, 0, + ObjectIdAttributeNumber, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(buildinfo.i.info_id)); + break; + + case INFO_RELNAME: + ScanKeyEntryInitialize(&key, 0, + Anum_pg_class_relname, + Character16EqualRegProcedure, + NameGetDatum(buildinfo.i.info_name)); + break; + + default: + elog(WARN, "ScanPgRelation: bad buildinfo"); + return NULL; + } + + /* ---------------- + * open pg_class and fetch a tuple + * ---------------- + */ + pg_class_desc = heap_openr(RelationRelationName); + if (!IsInitProcessingMode()) + RelationSetLockForRead(pg_class_desc); + pg_class_scan = + heap_beginscan(pg_class_desc, 0, NowTimeQual, 1, &key); + pg_class_tuple = heap_getnext(pg_class_scan, 0, &buf); + + /* ---------------- + * get set to return tuple + * ---------------- + */ + if (! HeapTupleIsValid(pg_class_tuple)) { + return_tuple = pg_class_tuple; + } else { + /* ------------------ + * a satanic bug used to live here: pg_class_tuple used to be + * returned here without having the corresponding buffer pinned. + * so when the buffer gets replaced, all hell breaks loose. + * this bug is discovered and killed by wei on 9/27/91. + * ------------------- + */ + return_tuple = (HeapTuple) palloc((Size) pg_class_tuple->t_len); + memmove((char *) return_tuple, + (char *) pg_class_tuple, + (int) pg_class_tuple->t_len); + ReleaseBuffer(buf); + } + + /* all done */ + heap_endscan(pg_class_scan); + if (!IsInitProcessingMode()) + RelationUnsetLockForRead(pg_class_desc); + heap_close(pg_class_desc); + + return return_tuple; +} + +static HeapTuple +scan_pg_rel_ind(RelationBuildDescInfo buildinfo) +{ + Relation pg_class_desc; + HeapTuple return_tuple; + + pg_class_desc = heap_openr(RelationRelationName); + if (!IsInitProcessingMode()) + RelationSetLockForRead(pg_class_desc); + + switch (buildinfo.infotype) { + case INFO_RELID: + return_tuple = ClassOidIndexScan(pg_class_desc, buildinfo.i.info_id); + break; + + case INFO_RELNAME: + return_tuple = ClassNameIndexScan(pg_class_desc, + buildinfo.i.info_name); + break; + + default: + elog(WARN, "ScanPgRelation: bad buildinfo"); + } + + /* all done */ + if (!IsInitProcessingMode()) + RelationUnsetLockForRead(pg_class_desc); + heap_close(pg_class_desc); + + return return_tuple; +} + +/* ---------------- + * AllocateRelationDesc + * + * This is used to allocate memory for a new relation descriptor + * and initialize the rd_rel field. + * ---------------- + */ +static Relation +AllocateRelationDesc(u_int natts, Form_pg_class relp) +{ + Relation relation; + Size len; + Form_pg_class relationTupleForm; + + /* ---------------- + * allocate space for the relation tuple form + * ---------------- + */ + relationTupleForm = (Form_pg_class) + palloc((Size) (sizeof(FormData_pg_class))); + + memmove((char *) relationTupleForm, (char *) relp, CLASS_TUPLE_SIZE); + + /* ---------------- + * allocate space for new relation descriptor + */ + len = sizeof(RelationData) + 10; /* + 10 is voodoo XXX mao */ + + relation = (Relation) palloc(len); + + /* ---------------- + * clear new reldesc + * ---------------- + */ + memset((char *) relation, 0, len); + + /* initialize attribute tuple form */ + relation->rd_att = CreateTemplateTupleDesc(natts); + + /*and initialize relation tuple form */ + relation->rd_rel = relationTupleForm; + + return relation; +} + +/* -------------------------------- + * RelationBuildTupleDesc + * + * Form the relation's tuple descriptor from information in + * the pg_attribute system catalog. + * -------------------------------- + */ +static void +RelationBuildTupleDesc(RelationBuildDescInfo buildinfo, + Relation relation, + AttributeTupleForm attp, + u_int natts) +{ + /* + * If this is bootstrap time (initdb), then we can't use the system + * catalog indices, because they may not exist yet. Otherwise, we + * can, and do. + */ + + if (IsBootstrapProcessingMode()) + build_tupdesc_seq(buildinfo, relation, attp, natts); + else + build_tupdesc_ind(buildinfo, relation, attp, natts); +} + +static void +build_tupdesc_seq(RelationBuildDescInfo buildinfo, + Relation relation, + AttributeTupleForm attp, + u_int natts) +{ + HeapTuple pg_attribute_tuple; + Relation pg_attribute_desc; + HeapScanDesc pg_attribute_scan; + ScanKeyData key; + int need; + + /* ---------------- + * form a scan key + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, + Anum_pg_attribute_attrelid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relation->rd_id)); + + /* ---------------- + * open pg_attribute and begin a scan + * ---------------- + */ + pg_attribute_desc = heap_openr(AttributeRelationName); + pg_attribute_scan = + heap_beginscan(pg_attribute_desc, 0, NowTimeQual, 1, &key); + + /* ---------------- + * add attribute data to relation->rd_att + * ---------------- + */ + need = natts; + pg_attribute_tuple = heap_getnext(pg_attribute_scan, 0, (Buffer *) NULL); + while (HeapTupleIsValid(pg_attribute_tuple) && need > 0) { + attp = (AttributeTupleForm) GETSTRUCT(pg_attribute_tuple); + + if (attp->attnum > 0) { + relation->rd_att->attrs[attp->attnum - 1] = + (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); + + memmove((char *) (relation->rd_att->attrs[attp->attnum - 1]), + (char *) attp, + ATTRIBUTE_TUPLE_SIZE); + need--; + } + pg_attribute_tuple = heap_getnext(pg_attribute_scan, + 0, (Buffer *) NULL); + } + + if (need > 0) + elog(WARN, "catalog is missing %d attribute%s for relid %d", + need, (need == 1 ? "" : "s"), relation->rd_id); + + /* ---------------- + * end the scan and close the attribute relation + * ---------------- + */ + heap_endscan(pg_attribute_scan); + heap_close(pg_attribute_desc); +} + +static void +build_tupdesc_ind(RelationBuildDescInfo buildinfo, + Relation relation, + AttributeTupleForm attp, + u_int natts) +{ + Relation attrel; + HeapTuple atttup; + int i; + + attrel = heap_openr(AttributeRelationName); + + for (i = 1; i <= relation->rd_rel->relnatts; i++) { + + atttup = (HeapTuple) AttributeNumIndexScan(attrel, relation->rd_id, i); + + if (!HeapTupleIsValid(atttup)) + elog(WARN, "cannot find attribute %d of relation %.16s", i, + &(relation->rd_rel->relname.data[0])); + attp = (AttributeTupleForm) GETSTRUCT(atttup); + + relation->rd_att->attrs[i - 1] = + (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE); + + memmove((char *) (relation->rd_att->attrs[i - 1]), + (char *) attp, + ATTRIBUTE_TUPLE_SIZE); + } + + heap_close(attrel); +} + +/* -------------------------------- + * RelationBuildRuleLock + * + * Form the relation's rewrite rules from information in + * the pg_rewrite system catalog. + * -------------------------------- + */ +static void +RelationBuildRuleLock(Relation relation) +{ + HeapTuple pg_rewrite_tuple; + Relation pg_rewrite_desc; + TupleDesc pg_rewrite_tupdesc; + HeapScanDesc pg_rewrite_scan; + ScanKeyData key; + RuleLock *rulelock; + int numlocks; + RewriteRule **rules; + int maxlocks; + + /* ---------------- + * form an array to hold the rewrite rules (the array is extended if + * necessary) + * ---------------- + */ + maxlocks = 4; + rules = (RewriteRule **)palloc(sizeof(RewriteRule*)*maxlocks); + numlocks = 0; + + /* ---------------- + * form a scan key + * ---------------- + */ + ScanKeyEntryInitialize(&key, 0, + Anum_pg_rewrite_ev_class, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relation->rd_id)); + + /* ---------------- + * open pg_attribute and begin a scan + * ---------------- + */ + pg_rewrite_desc = heap_openr(RewriteRelationName); + pg_rewrite_scan = + heap_beginscan(pg_rewrite_desc, 0, NowTimeQual, 1, &key); + pg_rewrite_tupdesc = + RelationGetTupleDescriptor(pg_rewrite_desc); + + /* ---------------- + * add attribute data to relation->rd_att + * ---------------- + */ + while ((pg_rewrite_tuple = heap_getnext(pg_rewrite_scan, 0, + (Buffer *) NULL)) != NULL) { + bool isnull; + char *ruleaction = NULL; + char *rule_evqual_string; + RewriteRule *rule; + + rule = (RewriteRule *)palloc(sizeof(RewriteRule)); + + rule->ruleId = pg_rewrite_tuple->t_oid; + + /* XXX too lazy to fix the type cast problem + * (see rewriteDefine.c:121) + */ + rule->event = + (CmdType)((char)heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_ev_type, pg_rewrite_tupdesc, + &isnull) - 48); + rule->attrno = + (AttrNumber)heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_ev_attr, pg_rewrite_tupdesc, + &isnull); + rule->isInstead = + (bool)heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_is_instead, pg_rewrite_tupdesc, + &isnull); + + ruleaction = + heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_action, pg_rewrite_tupdesc, + &isnull); + rule_evqual_string = + heap_getattr(pg_rewrite_tuple, InvalidBuffer, + Anum_pg_rewrite_ev_qual, pg_rewrite_tupdesc, + &isnull); + + ruleaction = textout((struct varlena *)ruleaction); + rule_evqual_string = textout((struct varlena *)rule_evqual_string); + + rule->actions = (List*)stringToNode(ruleaction); + rule->qual = (Node*)stringToNode(rule_evqual_string); + + rules[numlocks++] = rule; + if (numlocks==maxlocks) { + maxlocks *= 2; + rules = + (RewriteRule **)repalloc(rules, sizeof(RewriteRule*)*maxlocks); + } + } + + /* ---------------- + * end the scan and close the attribute relation + * ---------------- + */ + heap_endscan(pg_rewrite_scan); + heap_close(pg_rewrite_desc); + + /* ---------------- + * form a RuleLock and insert into relation + * ---------------- + */ + rulelock = (RuleLock *)palloc(sizeof(RuleLock)); + rulelock->numLocks = numlocks; + rulelock->rules = rules; + + relation->rd_rules = rulelock; + return; +} + + +/* -------------------------------- + * RelationBuildDesc + * + * To build a relation descriptor, we have to allocate space, + * open the underlying unix file and initialize the following + * fields: + * + * File rd_fd; open file descriptor + * int rd_nblocks; number of blocks in rel + * it will be set in ambeginscan() + * uint16 rd_refcnt; reference count + * Form_pg_am rd_am; AM tuple + * Form_pg_class rd_rel; RELATION tuple + * Oid rd_id; relations's object id + * Pointer lockInfo; ptr. to misc. info. + * TupleDesc rd_att; tuple desciptor + * + * Note: rd_ismem (rel is in-memory only) is currently unused + * by any part of the system. someday this will indicate that + * the relation lives only in the main-memory buffer pool + * -cim 2/4/91 + * -------------------------------- + */ +static Relation +RelationBuildDesc(RelationBuildDescInfo buildinfo) +{ + File fd; + Relation relation; + u_int natts; + Oid relid; + Oid relam; + Form_pg_class relp; + AttributeTupleForm attp = NULL; + + MemoryContext oldcxt; + + HeapTuple pg_class_tuple; + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * find the tuple in pg_class corresponding to the given relation id + * ---------------- + */ + pg_class_tuple = ScanPgRelation(buildinfo); + + /* ---------------- + * if no such tuple exists, return NULL + * ---------------- + */ + if (! HeapTupleIsValid(pg_class_tuple)) { + + MemoryContextSwitchTo(oldcxt); + + return NULL; + } + + /* ---------------- + * get information from the pg_class_tuple + * ---------------- + */ + relid = pg_class_tuple->t_oid; + relp = (Form_pg_class) GETSTRUCT(pg_class_tuple); + natts = relp->relnatts; + + /* ---------------- + * allocate storage for the relation descriptor, + * initialize relation->rd_rel and get the access method id. + * ---------------- + */ + relation = AllocateRelationDesc(natts, relp); + relam = relation->rd_rel->relam; + + /* ---------------- + * initialize the relation's relation id (relation->rd_id) + * ---------------- + */ + relation->rd_id = relid; + + /* ---------------- + * initialize relation->rd_refcnt + * ---------------- + */ + RelationSetReferenceCount(relation, 1); + + /* ---------------- + * normal relations are not nailed into the cache + * ---------------- + */ + relation->rd_isnailed = false; + + /* ---------------- + * initialize the access method information (relation->rd_am) + * ---------------- + */ + if (OidIsValid(relam)) { + relation->rd_am = (Form_pg_am) + AccessMethodObjectIdGetAccessMethodTupleForm(relam); + } + + /* ---------------- + * initialize the tuple descriptor (relation->rd_att). + * remember, rd_att is an array of attribute pointers that lives + * off the end of the relation descriptor structure so space was + * already allocated for it by AllocateRelationDesc. + * ---------------- + */ + RelationBuildTupleDesc(buildinfo, relation, attp, natts); + + /* ---------------- + * initialize rules that affect this relation + * ---------------- + */ + if (relp->relhasrules) { + RelationBuildRuleLock(relation); + } else { + relation->rd_rules = NULL; + } + + /* ---------------- + * initialize index strategy and support information for this relation + * ---------------- + */ + if (OidIsValid(relam)) { + IndexedAccessMethodInitialize(relation); + } + + /* ---------------- + * initialize the relation lock manager information + * ---------------- + */ + RelationInitLockInfo(relation); /* see lmgr.c */ + + /* ---------------- + * open the relation and assign the file descriptor returned + * by the storage manager code to rd_fd. + * ---------------- + */ + fd = smgropen(relp->relsmgr, relation); + + Assert (fd >= -1); + if (fd == -1) + elog(NOTICE, "RelationIdBuildRelation: smgropen(%s): %m", + &relp->relname); + + relation->rd_fd = fd; + + /* ---------------- + * insert newly created relation into proper relcaches, + * restore memory context and return the new reldesc. + * ---------------- + */ + + RelationCacheInsert(relation); + + /* ------------------- + * free the memory allocated for pg_class_tuple + * and for lock data pointed to by pg_class_tuple + * ------------------- + */ + pfree(pg_class_tuple); + + MemoryContextSwitchTo(oldcxt); + + return relation; +} + +static void +IndexedAccessMethodInitialize(Relation relation) +{ + IndexStrategy strategy; + RegProcedure *support; + int natts; + Size stratSize; + Size supportSize; + uint16 relamstrategies; + uint16 relamsupport; + + natts = relation->rd_rel->relnatts; + relamstrategies = relation->rd_am->amstrategies; + stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies); + strategy = (IndexStrategy) palloc(stratSize); + relamsupport = relation->rd_am->amsupport; + + if (relamsupport > 0) { + supportSize = natts * (relamsupport * sizeof (RegProcedure)); + support = (RegProcedure *) palloc(supportSize); + } else { + support = (RegProcedure *) NULL; + } + + IndexSupportInitialize(strategy, support, + relation->rd_att->attrs[0]->attrelid, + relation->rd_rel->relam, + relamstrategies, relamsupport, natts); + + RelationSetIndexSupport(relation, strategy, support); +} + +/* -------------------------------- + * formrdesc + * + * This is a special version of RelationBuildDesc() + * used by RelationInitialize() in initializing the + * relcache. The system relation descriptors built + * here are all nailed in the descriptor caches, for + * bootstrapping. + * -------------------------------- + */ +static void +formrdesc(char *relationName, + u_int natts, + FormData_pg_attribute att[]) +{ + Relation relation; + Size len; + int i; + + /* ---------------- + * allocate new relation desc + * ---------------- + */ + len = sizeof (RelationData); + relation = (Relation) palloc(len); + memset((char *)relation, 0,len); + + /* ---------------- + * don't open the unix file yet.. + * ---------------- + */ + relation->rd_fd = -1; + + /* ---------------- + * initialize reference count + * ---------------- + */ + RelationSetReferenceCount(relation, 1); + + /* ---------------- + * initialize relation tuple form + * ---------------- + */ + relation->rd_rel = (Form_pg_class) + palloc((Size) (sizeof(*relation->rd_rel))); + memset(relation->rd_rel, 0, sizeof(FormData_pg_class)); + namestrcpy(&relation->rd_rel->relname, relationName); + + /* ---------------- + initialize attribute tuple form + */ + relation->rd_att = CreateTemplateTupleDesc(natts); + + /* + * For debugging purposes, it's important to distinguish between + * shared and non-shared relations, even at bootstrap time. There's + * code in the buffer manager that traces allocations that has to + * know about this. + */ + + if (IsSystemRelationName(relationName)) { + relation->rd_rel->relowner = 6; /* XXX use sym const */ + relation->rd_rel->relisshared = + IsSharedSystemRelationName(relationName); + } else { + relation->rd_rel->relowner = InvalidOid; /* XXX incorrect*/ + relation->rd_rel->relisshared = false; + } + + relation->rd_rel->relpages = 1; /* XXX */ + relation->rd_rel->reltuples = 1; /* XXX */ + relation->rd_rel->relkind = RELKIND_RELATION; + relation->rd_rel->relarch = 'n'; + relation->rd_rel->relnatts = (uint16) natts; + relation->rd_isnailed = true; + + /* ---------------- + * initialize tuple desc info + * ---------------- + */ + for (i = 0; i < natts; i++) { + relation->rd_att->attrs[i] = + (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); + + memset((char *)relation->rd_att->attrs[i], 0, + ATTRIBUTE_TUPLE_SIZE); + memmove((char *)relation->rd_att->attrs[i], + (char *)&att[i], + ATTRIBUTE_TUPLE_SIZE); + } + + /* ---------------- + * initialize relation id + * ---------------- + */ + relation->rd_id = relation->rd_att->attrs[0]->attrelid; + + /* ---------------- + * add new reldesc to relcache + * ---------------- + */ + RelationCacheInsert(relation); + /* + * Determining this requires a scan on pg_class, but to do the + * scan the rdesc for pg_class must already exist. Therefore + * we must do the check (and possible set) after cache insertion. + */ + relation->rd_rel->relhasindex = + CatalogHasIndex(relationName, relation->rd_id); +} + + +/* ---------------------------------------------------------------- + * Relation Descriptor Lookup Interface + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * RelationIdCacheGetRelation + * + * only try to get the reldesc by looking up the cache + * do not go to the disk. this is used by BlockPrepareFile() + * and RelationIdGetRelation below. + * -------------------------------- + */ +Relation +RelationIdCacheGetRelation(Oid relationId) +{ + Relation rd; + + RelationIdCacheLookup(relationId, rd); + + if (RelationIsValid(rd)) { + if (rd->rd_fd == -1) { + rd->rd_fd = smgropen(rd->rd_rel->relsmgr, rd); + Assert(rd->rd_fd != -1); + } + + RelationIncrementReferenceCount(rd); + RelationSetLockForDescriptorOpen(rd); + + } + + return(rd); +} + +/* -------------------------------- + * RelationNameCacheGetRelation + * -------------------------------- + */ +Relation +RelationNameCacheGetRelation(char *relationName) +{ + Relation rd; + NameData name; + + /* make sure that the name key used for hash lookup is properly + null-padded */ + memset(&name,0, NAMEDATALEN); + namestrcpy(&name, relationName); + RelationNameCacheLookup(name.data, rd); + + if (RelationIsValid(rd)) { + if (rd->rd_fd == -1) { + rd->rd_fd = smgropen(rd->rd_rel->relsmgr, rd); + Assert(rd->rd_fd != -1); + } + + RelationIncrementReferenceCount(rd); + RelationSetLockForDescriptorOpen(rd); + + } + + return(rd); +} + +/* -------------------------------- + * RelationIdGetRelation + * + * return a relation descriptor based on its id. + * return a cached value if possible + * -------------------------------- + */ +Relation +RelationIdGetRelation(Oid relationId) +{ + Relation rd; + RelationBuildDescInfo buildinfo; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_RelationIdGetRelation); + IncrHeapAccessStat(global_RelationIdGetRelation); + + /* ---------------- + * first try and get a reldesc from the cache + * ---------------- + */ + rd = RelationIdCacheGetRelation(relationId); + if (RelationIsValid(rd)) + return rd; + + /* ---------------- + * no reldesc in the cache, so have RelationBuildDesc() + * build one and add it. + * ---------------- + */ + buildinfo.infotype = INFO_RELID; + buildinfo.i.info_id = relationId; + + rd = RelationBuildDesc(buildinfo); + return + rd; +} + +/* -------------------------------- + * RelationNameGetRelation + * + * return a relation descriptor based on its name. + * return a cached value if possible + * -------------------------------- + */ +Relation +RelationNameGetRelation(char *relationName) +{ + Relation rd; + RelationBuildDescInfo buildinfo; + + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_RelationNameGetRelation); + IncrHeapAccessStat(global_RelationNameGetRelation); + + /* ---------------- + * first try and get a reldesc from the cache + * ---------------- + */ + rd = RelationNameCacheGetRelation(relationName); + if (RelationIsValid(rd)) + return rd; + + /* ---------------- + * no reldesc in the cache, so have RelationBuildDesc() + * build one and add it. + * ---------------- + */ + buildinfo.infotype = INFO_RELNAME; + buildinfo.i.info_name = relationName; + + rd = RelationBuildDesc(buildinfo); + return rd; +} + +/* ---------------- + * old "getreldesc" interface. + * ---------------- + */ +Relation +getreldesc(char *relationName) +{ + /* ---------------- + * increment access statistics + * ---------------- + */ + IncrHeapAccessStat(local_getreldesc); + IncrHeapAccessStat(global_getreldesc); + + return RelationNameGetRelation(relationName); +} + +/* ---------------------------------------------------------------- + * cache invalidation support routines + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * RelationClose - close an open relation + * -------------------------------- + */ +void +RelationClose(Relation relation) +{ + /* Note: no locking manipulations needed */ + RelationDecrementReferenceCount(relation); +} + +/* -------------------------------- + * RelationFlushRelation + * + * Actually blows away a relation... RelationFree doesn't do + * anything anymore. + * -------------------------------- + */ +void +RelationFlushRelation(Relation *relationPtr, + bool onlyFlushReferenceCountZero) +{ + int i; + AttributeTupleForm *p; + MemoryContext oldcxt; + Relation relation = *relationPtr; + + if (relation->rd_isnailed) { + /* this is a nailed special relation for bootstraping */ + return; + } + + if (!onlyFlushReferenceCountZero || + RelationHasReferenceCountZero(relation)) { + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + RelationCacheDelete(relation); + + FileInvalidate(RelationGetSystemPort(relation)); + + i = relation->rd_rel->relnatts - 1; + p = &relation->rd_att->attrs[i]; + while ((i -= 1) >= 0) { + pfree(*p--); + } + +#if 0 + if (relation->rd_rules) { + int j; + for(j=0; j < relation->rd_rules->numLocks; j++) { + pfree(relation->rd_rules->rules[j]); + } + pfree(relation->rd_rules->rules); + pfree(relation->rd_rules); + } +#endif + + pfree(RelationGetLockInfo(relation)); + pfree(RelationGetRelationTupleForm(relation)); + pfree(relation); + + MemoryContextSwitchTo(oldcxt); + } +} + +/* -------------------------------- + * RelationIdInvalidateRelationCacheByRelationId + * -------------------------------- + */ +void +RelationIdInvalidateRelationCacheByRelationId(Oid relationId) +{ + Relation relation; + + RelationIdCacheLookup(relationId, relation); + + /* + * "local" relations are invalidated by RelationPurgeLocalRelation. + * (This is to make LocalBufferSync's life easier: want the descriptor + * to hang around for a while. In fact, won't we want this for + * BufferSync also? But I'll leave it for now since I don't want to + * break anything.) - ay 3/95 + */ + if (PointerIsValid(relation) && !relation->rd_islocal) { + /* + * The boolean onlyFlushReferenceCountZero in RelationFlushReln() + * should be set to true when we are incrementing the command + * counter and to false when we are starting a new xaction. This + * can be determined by checking the current xaction status. + */ + RelationFlushRelation(&relation, CurrentXactInProgress()); + } +} + +/* -------------------------------- + * RelationIdInvalidateRelationCacheByAccessMethodId + * + * RelationFlushIndexes is needed for use with HashTableWalk.. + * -------------------------------- + */ +static void +RelationFlushIndexes(Relation *r, + Oid accessMethodId) +{ + Relation relation = *r; + + if (!RelationIsValid(relation)) { + elog(NOTICE, "inval call to RFI"); + return; + } + + if (relation->rd_rel->relkind == RELKIND_INDEX && /* XXX style */ + (!OidIsValid(accessMethodId) || + relation->rd_rel->relam == accessMethodId)) + { + RelationFlushRelation(&relation, false); + } +} + +void +RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId) +{ +# if 0 + /* + * 25 aug 1992: mao commented out the ht walk below. it should be + * doing the right thing, in theory, but flushing reldescs for index + * relations apparently doesn't work. we want to cut 4.0.1, and i + * don't want to introduce new bugs. this code never executed before, + * so i'm turning it off for now. after the release is cut, i'll + * fix this up. + */ + + HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushIndexes, + accessMethodId); +# else + return; +# endif +} + +/* + * RelationCacheInvalidate + * + * Will blow away either all the cached relation descriptors or + * those that have a zero reference count. + * + */ +void +RelationCacheInvalidate(bool onlyFlushReferenceCountZero) +{ + HashTableWalk(RelationNameCache, (HashtFunc) RelationFlushRelation, + onlyFlushReferenceCountZero); + + /* + * nailed-in reldescs will still be in the cache... + * 7 hardwired heaps + 3 hardwired indices == 10 total. + */ + if (!onlyFlushReferenceCountZero) { + Assert(RelationNameCache->hctl->nkeys == 10); + Assert(RelationIdCache->hctl->nkeys == 10); + } +} + + +/* + * newlyCreatedRelns - + * relations created during this transaction. We need to keep track of + * these + */ +static List *newlyCreatedRelns = NULL; + + +/* -------------------------------- + * RelationRegisterRelation - + * register the Relation descriptor of a newly created relation + * with the relation descriptor Cache. + * -------------------------------- + */ +void +RelationRegisterRelation(Relation relation) +{ + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + if (oldcxt != (MemoryContext)CacheCxt) + elog(NOIND,"RelationRegisterRelation: WARNING: Context != CacheCxt"); + + RelationCacheInsert(relation); + + RelationInitLockInfo(relation); + + /* + * we've just created the relation. It is invisible to anyone else + * before the transaction is committed. Setting rd_islocal allows us + * to use the local buffer manager for select/insert/etc before the end + * of transaction. (We also need to keep track of relations + * created during a transaction and does the necessary clean up at + * the end of the transaction.) - ay 3/95 + */ + relation->rd_islocal = TRUE; + newlyCreatedRelns = lcons(relation, newlyCreatedRelns); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * RelationPurgeLocalRelation - + * find all the Relation descriptors marked rd_islocal and reset them. + * This should be called at the end of a transaction (commit/abort) when + * the "local" relations will become visible to others and the multi-user + * buffer pool should be used. + */ +void +RelationPurgeLocalRelation(bool xactCommitted) +{ + MemoryContext oldcxt; + + if (newlyCreatedRelns==NULL) + return; + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + while (newlyCreatedRelns) { + List *l = newlyCreatedRelns; + Relation reln = lfirst(l); + + Assert(reln!=NULL && reln->rd_islocal); + + if (!xactCommitted) { + /* + * remove the file if we abort. This is so that files for + * tables created inside a transaction block get removed. + */ + smgrunlink(reln->rd_rel->relsmgr, reln); + } + + reln->rd_islocal = FALSE; + + if (!IsBootstrapProcessingMode()) + RelationFlushRelation(&reln, FALSE); + + newlyCreatedRelns = lnext(newlyCreatedRelns); + pfree(l); + } + + MemoryContextSwitchTo(oldcxt); +} + +/* -------------------------------- + * RelationInitialize + * + * This initializes the relation descriptor cache. + * -------------------------------- + */ + +#define INITRELCACHESIZE 400 + +void +RelationInitialize() +{ + MemoryContext oldcxt; + HASHCTL ctl; + + /* ---------------- + * switch to cache memory context + * ---------------- + */ + if (!CacheCxt) + CacheCxt = CreateGlobalMemory("Cache"); + + oldcxt = MemoryContextSwitchTo((MemoryContext)CacheCxt); + + /* ---------------- + * create global caches + * ---------------- + */ + memset(&ctl,0, (int) sizeof(ctl)); + ctl.keysize = sizeof(NameData); + ctl.datasize = sizeof(Relation); + RelationNameCache = hash_create(INITRELCACHESIZE, &ctl, HASH_ELEM); + + ctl.keysize = sizeof(Oid); + ctl.hash = tag_hash; + RelationIdCache = hash_create(INITRELCACHESIZE, &ctl, + HASH_ELEM | HASH_FUNCTION); + + /* ---------------- + * initialize the cache with pre-made relation descriptors + * for some of the more important system relations. These + * relations should always be in the cache. + * ---------------- + */ + formrdesc(RelationRelationName, Natts_pg_class, Desc_pg_class); + formrdesc(AttributeRelationName, Natts_pg_attribute, Desc_pg_attribute); + formrdesc(ProcedureRelationName, Natts_pg_proc, Desc_pg_proc); + formrdesc(TypeRelationName, Natts_pg_type, Desc_pg_type); + formrdesc(VariableRelationName, Natts_pg_variable, Desc_pg_variable); + formrdesc(LogRelationName, Natts_pg_log, Desc_pg_log); + formrdesc(TimeRelationName, Natts_pg_time, Desc_pg_time); + + /* + * If this isn't initdb time, then we want to initialize some index + * relation descriptors, as well. The descriptors are for pg_attnumind + * (to make building relation descriptors fast) and possibly others, + * as they're added. + */ + + if (!IsBootstrapProcessingMode()) + init_irels(); + + MemoryContextSwitchTo(oldcxt); +} + +/* + * init_irels(), write_irels() -- handle special-case initialization of + * index relation descriptors. + * + * In late 1992, we started regularly having databases with more than + * a thousand classes in them. With this number of classes, it became + * critical to do indexed lookups on the system catalogs. + * + * Bootstrapping these lookups is very hard. We want to be able to + * use an index on pg_attribute, for example, but in order to do so, + * we must have read pg_attribute for the attributes in the index, + * which implies that we need to use the index. + * + * In order to get around the problem, we do the following: + * + * + When the database system is initialized (at initdb time), we + * don't use indices on pg_attribute. We do sequential scans. + * + * + When the backend is started up in normal mode, we load an image + * of the appropriate relation descriptors, in internal format, + * from an initialization file in the data/base/... directory. + * + * + If the initialization file isn't there, then we create the + * relation descriptor using sequential scans and write it to + * the initialization file for use by subsequent backends. + * + * This is complicated and interferes with system changes, but + * performance is so bad that we're willing to pay the tax. + */ + +/* pg_attnumind, pg_classnameind, pg_classoidind */ +#define Num_indices_bootstrap 3 + +void +init_irels() +{ + Size len; + int nread; + File fd; + Relation irel[Num_indices_bootstrap]; + Relation ird; + Form_pg_am am; + Form_pg_class relform; + IndexStrategy strat; + RegProcedure *support; + int i; + int relno; + + if ((fd = FileNameOpenFile(INIT_FILENAME, O_RDONLY, 0600)) < 0) { + write_irels(); + return; + } + + (void) FileSeek(fd, 0L, SEEK_SET); + + for (relno = 0; relno < Num_indices_bootstrap; relno++) { + /* first read the relation descriptor length*/ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + ird = irel[relno] = (Relation) palloc(len); + memset(ird, 0, len); + + /* then, read the Relation structure */ + if ((nread = FileRead(fd, (char*)ird, len)) != len) { + write_irels(); + return; + } + + /* the file descriptor is not yet opened */ + ird->rd_fd = -1; + + /* lock info is not initialized */ + ird->lockInfo = (char *) NULL; + + /* next, read the access method tuple form */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + am = (Form_pg_am) palloc(len); + if ((nread = FileRead(fd, (char*)am, len)) != len) { + write_irels(); + return; + } + + ird->rd_am = am; + + /* next read the relation tuple form */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + relform = (Form_pg_class) palloc(len); + if ((nread = FileRead(fd, (char*)relform, len)) != len) { + write_irels(); + return; + } + + ird->rd_rel = relform; + + /* initialize attribute tuple forms */ + ird->rd_att = CreateTemplateTupleDesc(relform->relnatts); + + /* next read all the attribute tuple form data entries */ + len = ATTRIBUTE_TUPLE_SIZE; + for (i = 0; i < relform->relnatts; i++) { + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + ird->rd_att->attrs[i] = (AttributeTupleForm) palloc(len); + + if ((nread = FileRead(fd, (char*)ird->rd_att->attrs[i], len)) != len) { + write_irels(); + return; + } + } + + /* next, read the index strategy map */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + strat = (IndexStrategy) palloc(len); + if ((nread = FileRead(fd, (char*)strat, len)) != len) { + write_irels(); + return; + } + + /* oh, for god's sake... */ +#define SMD(i) strat[0].strategyMapData[i].entry[0] + + /* have to reinit the function pointers in the strategy maps */ + for (i = 0; i < am->amstrategies; i++) + fmgr_info(SMD(i).sk_procedure, + &(SMD(i).sk_func), &(SMD(i).sk_nargs)); + + + /* use a real field called rd_istrat instead of the + bogosity of hanging invisible fields off the end of a structure + - jolly */ + ird->rd_istrat = strat; + + /* finally, read the vector of support procedures */ + if ((nread = FileRead(fd, (char*)&len, sizeof(int))) != sizeof(int)) { + write_irels(); + return; + } + + support = (RegProcedure *) palloc(len); + if ((nread = FileRead(fd, (char*)support, len)) != len) { + write_irels(); + return; + } + + /* + p += sizeof(IndexStrategy); + *((RegProcedure **) p) = support; + */ + + ird->rd_support = support; + + RelationCacheInsert(ird); + } +} + +void +write_irels() +{ + int len; + int nwritten; + File fd; + Relation irel[Num_indices_bootstrap]; + Relation ird; + Form_pg_am am; + Form_pg_class relform; + IndexStrategy strat; + RegProcedure *support; + ProcessingMode oldmode; + int i; + int relno; + RelationBuildDescInfo bi; + + fd = FileNameOpenFile(INIT_FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600); + if (fd < 0) + elog(FATAL, "cannot create init file %s", INIT_FILENAME); + + (void) FileSeek(fd, 0L, SEEK_SET); + + /* + * Build a relation descriptor for pg_attnumind without resort to the + * descriptor cache. In order to do this, we set ProcessingMode + * to Bootstrap. The effect of this is to disable indexed relation + * searches -- a necessary step, since we're trying to instantiate + * the index relation descriptors here. + */ + + oldmode = GetProcessingMode(); + SetProcessingMode(BootstrapProcessing); + + bi.infotype = INFO_RELNAME; + bi.i.info_name = AttributeNumIndex; + irel[0] = RelationBuildDesc(bi); + irel[0]->rd_isnailed = true; + + bi.i.info_name = ClassNameIndex; + irel[1] = RelationBuildDesc(bi); + irel[1]->rd_isnailed = true; + + bi.i.info_name = ClassOidIndex; + irel[2] = RelationBuildDesc(bi); + irel[2]->rd_isnailed = true; + + SetProcessingMode(oldmode); + + /* nail the descriptor in the cache */ + for (relno = 0; relno < Num_indices_bootstrap; relno++) { + ird = irel[relno]; + + /* save the volatile fields in the relation descriptor */ + am = ird->rd_am; + ird->rd_am = (Form_pg_am) NULL; + relform = ird->rd_rel; + ird->rd_rel = (Form_pg_class) NULL; + strat = ird->rd_istrat; + support = ird->rd_support; + + /* first write the relation descriptor , excluding strategy and support */ + len = sizeof(RelationData); + + /* first, write the relation descriptor length */ + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- descriptor length"); + + /* next, write out the Relation structure */ + if ((nwritten = FileWrite(fd, (char*) ird, len)) != len) + elog(FATAL, "cannot write init file -- reldesc"); + + /* next, write the access method tuple form */ + len = sizeof(FormData_pg_am); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- am tuple form length"); + + if ((nwritten = FileWrite(fd, (char*) am, len)) != len) + elog(FATAL, "cannot write init file -- am tuple form"); + + /* next write the relation tuple form */ + len = sizeof(FormData_pg_class); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- relation tuple form length"); + + if ((nwritten = FileWrite(fd, (char*) relform, len)) != len) + elog(FATAL, "cannot write init file -- relation tuple form"); + + /* next, do all the attribute tuple form data entries */ + len = ATTRIBUTE_TUPLE_SIZE; + for (i = 0; i < relform->relnatts; i++) { + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- length of attdesc %d", i); + if ((nwritten = FileWrite(fd, (char*) ird->rd_att->attrs[i], len)) + != len) + elog(FATAL, "cannot write init file -- attdesc %d", i); + } + + /* next, write the index strategy map */ + len = AttributeNumberGetIndexStrategySize(relform->relnatts, + am->amstrategies); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- strategy map length"); + + if ((nwritten = FileWrite(fd, (char*) strat, len)) != len) + elog(FATAL, "cannot write init file -- strategy map"); + + /* finally, write the vector of support procedures */ + len = relform->relnatts * (am->amsupport * sizeof(RegProcedure)); + if ((nwritten = FileWrite(fd, (char*) &len, sizeof(int))) + != sizeof(int)) + elog(FATAL, "cannot write init file -- support vector length"); + + if ((nwritten = FileWrite(fd, (char*) support, len)) != len) + elog(FATAL, "cannot write init file -- support vector"); + + /* restore volatile fields */ + ird->rd_am = am; + ird->rd_rel = relform; + } + + (void) FileClose(fd); +} diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c new file mode 100644 index 00000000000..36b46d9b99e --- /dev/null +++ b/src/backend/utils/cache/syscache.c @@ -0,0 +1,630 @@ +/*------------------------------------------------------------------------- + * + * syscache.c-- + * System cache management routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/cache/syscache.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * NOTES + * These routines allow the parser/planner/executor to perform + * rapid lookups on the contents of the system catalogs. + * + * see catalog/syscache.h for a list of the cache id's + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include "access/heapam.h" +#include "access/htup.h" +#include "catalog/catname.h" +#include "utils/catcache.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "nodes/pg_list.h" + +/* ---------------- + * hardwired attribute information comes from system catalog files. + * ---------------- + */ +#include "catalog/pg_am.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_group.h" +#include "catalog/pg_index.h" +#include "catalog/pg_inherits.h" +#include "catalog/pg_language.h" +#include "catalog/pg_opclass.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_class.h" +#include "catalog/pg_type.h" +#include "catalog/pg_rewrite.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_user.h" +#include "storage/large_object.h" +#include "catalog/pg_listener.h" + +extern bool AMI_OVERRIDE; /* XXX style */ + +#include "utils/syscache.h" +#include "catalog/indexing.h" + +typedef HeapTuple (*ScanFunc)(); + +/* ---------------- + * Warning: cacheinfo[] below is changed, then be sure and + * update the magic constants in syscache.h! + * ---------------- + */ +static struct cachedesc cacheinfo[] = { + { AccessMethodOperatorRelationName, /* AMOPOPID */ + 3, + { Anum_pg_amop_amopclaid, + Anum_pg_amop_amopopr, + Anum_pg_amop_amopid, + 0 }, + sizeof(FormData_pg_amop), + NULL, + (ScanFunc) NULL }, + { AccessMethodOperatorRelationName, /* AMOPSTRATEGY */ + 3, + { Anum_pg_amop_amopid, + Anum_pg_amop_amopclaid, + Anum_pg_amop_amopstrategy, + 0 }, + sizeof(FormData_pg_amop), + NULL, + (ScanFunc) NULL }, + { AttributeRelationName, /* ATTNAME */ + 2, + { Anum_pg_attribute_attrelid, + Anum_pg_attribute_attname, + 0, + 0 }, + ATTRIBUTE_TUPLE_SIZE, + AttributeNameIndex, + (ScanFunc) AttributeNameIndexScan }, + { AttributeRelationName, /* ATTNUM */ + 2, + { Anum_pg_attribute_attrelid, + Anum_pg_attribute_attnum, + 0, + 0 }, + ATTRIBUTE_TUPLE_SIZE, + AttributeNumIndex, + (ScanFunc) AttributeNumIndexScan }, + { IndexRelationName, /* INDEXRELID */ + 1, + { Anum_pg_index_indexrelid, + 0, + 0, + 0 }, + offsetof(FormData_pg_index, indpred), + NULL, + NULL }, + { LanguageRelationName, /* LANNAME */ + 1, + { Anum_pg_language_lanname, + 0, + 0, + 0 }, + offsetof(FormData_pg_language, lancompiler), + NULL, + NULL }, + { OperatorRelationName, /* OPRNAME */ + 4, + { Anum_pg_operator_oprname, + Anum_pg_operator_oprleft, + Anum_pg_operator_oprright, + Anum_pg_operator_oprkind }, + sizeof(FormData_pg_operator), + NULL, + NULL }, + { OperatorRelationName, /* OPROID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + sizeof(FormData_pg_operator), + NULL, + (ScanFunc) NULL }, + { ProcedureRelationName, /* PRONAME */ + 3, + { Anum_pg_proc_proname, + Anum_pg_proc_pronargs, + Anum_pg_proc_proargtypes, + 0 }, + offsetof(FormData_pg_proc, prosrc), + ProcedureNameIndex, + (ScanFunc) ProcedureNameIndexScan }, + { ProcedureRelationName, /* PROOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + offsetof(FormData_pg_proc, prosrc), + ProcedureOidIndex, + (ScanFunc) ProcedureOidIndexScan }, + { RelationRelationName, /* RELNAME */ + 1, + { Anum_pg_class_relname, + 0, + 0, + 0 }, + CLASS_TUPLE_SIZE, + ClassNameIndex, + (ScanFunc) ClassNameIndexScan }, + { RelationRelationName, /* RELOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + CLASS_TUPLE_SIZE, + ClassOidIndex, + (ScanFunc) ClassOidIndexScan }, + { TypeRelationName, /* TYPNAME */ + 1, + { Anum_pg_type_typname, + 0, + 0, + 0 }, + offsetof(TypeTupleFormData,typalign)+sizeof(char), + TypeNameIndex, + TypeNameIndexScan }, + { TypeRelationName, /* TYPOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0}, + offsetof(TypeTupleFormData,typalign)+sizeof(char), + TypeOidIndex, + TypeOidIndexScan }, + { AccessMethodRelationName, /* AMNAME */ + 1, + { Anum_pg_am_amname, + 0, + 0, + 0}, + sizeof(FormData_pg_am), + NULL, + NULL }, + { OperatorClassRelationName, /* CLANAME */ + 1, + { Anum_pg_opclass_opcname, + 0, + 0, + 0}, + sizeof(FormData_pg_opclass), + NULL, + NULL }, + { IndexRelationName, /* INDRELIDKEY */ + 2, + { Anum_pg_index_indrelid, + Anum_pg_index_indkey, + 0, + 0}, + offsetof(FormData_pg_index, indpred), + NULL, + (ScanFunc) NULL }, + { InheritsRelationName, /* INHRELID */ + 2, + { Anum_pg_inherits_inhrel, + Anum_pg_inherits_inhseqno, + 0, + 0}, + sizeof(FormData_pg_inherits), + NULL, + (ScanFunc) NULL }, + { RewriteRelationName, /* RULOID */ + 1, + { ObjectIdAttributeNumber, + 0, + 0, + 0 }, + offsetof(FormData_pg_rewrite, ev_qual), + NULL, + (ScanFunc) NULL }, + { AggregateRelationName, /*AGGNAME*/ + 2, + { Anum_pg_aggregate_aggname, + Anum_pg_aggregate_aggbasetype, + 0, + 0 }, + offsetof(FormData_pg_aggregate, agginitval1), + NULL, + (ScanFunc) NULL }, + { ListenerRelationName, /* LISTENREL */ + 2, + { Anum_pg_listener_relname, + Anum_pg_listener_pid, + 0, + 0 }, + sizeof(FormData_pg_listener), + NULL, + (ScanFunc) NULL }, + { UserRelationName, /* USENAME */ + 1, + { Anum_pg_user_usename, + 0, + 0, + 0 }, + sizeof(FormData_pg_user), + NULL, + (ScanFunc) NULL }, + { UserRelationName, /* USESYSID */ + 1, + { Anum_pg_user_usesysid, + 0, + 0, + 0 }, + sizeof(FormData_pg_user), + NULL, + (ScanFunc) NULL }, + { GroupRelationName, /* GRONAME */ + 1, + { Anum_pg_group_groname, + 0, + 0, + 0 }, + offsetof(FormData_pg_group, grolist[0]), + NULL, + (ScanFunc) NULL }, + { GroupRelationName, /* GROSYSID */ + 1, + { Anum_pg_group_grosysid, + 0, + 0, + 0 }, + offsetof(FormData_pg_group, grolist[0]), + NULL, + (ScanFunc) NULL }, + { RewriteRelationName, /* REWRITENAME */ + 1, + { Anum_pg_rewrite_rulename, + 0, + 0, + 0 }, + offsetof(FormData_pg_rewrite, ev_qual), + NULL, + (ScanFunc) NULL }, + { ProcedureRelationName, /* PROSRC */ + 1, + { Anum_pg_proc_prosrc, + 0, + 0, + 0 }, + offsetof(FormData_pg_proc, prosrc), + ProcedureSrcIndex, + (ScanFunc) ProcedureSrcIndexScan } +}; + +static struct catcache *SysCache[lengthof(cacheinfo)]; +static int32 SysCacheSize = lengthof(cacheinfo); + + +/* + * zerocaches-- + * + * Make sure the SysCache structure is zero'd. + */ +void +zerocaches() +{ + memset((char *) SysCache, 0, SysCacheSize * sizeof(struct catcache *)); +} + +/* + * Note: + * This function was written because the initialized catalog caches + * are used to determine which caches may contain tuples which need + * to be invalidated in other backends. + */ +void +InitCatalogCache() +{ + int cacheId; /* XXX type */ + + if (!AMI_OVERRIDE) { + for (cacheId = 0; cacheId < SysCacheSize; cacheId += 1) { + + Assert(!PointerIsValid((Pointer)SysCache[cacheId])); + + SysCache[cacheId] = + InitSysCache(cacheinfo[cacheId].name, + cacheinfo[cacheId].indname, + cacheId, + cacheinfo[cacheId].nkeys, + cacheinfo[cacheId].key, + cacheinfo[cacheId].iScanFunc); + if (!PointerIsValid((char *)SysCache[cacheId])) { + elog(WARN, + "InitCatalogCache: Can't init cache %.16s(%d)", + cacheinfo[cacheId].name, + cacheId); + } + + } + } +} + +/* + * SearchSysCacheTuple-- + * + * A layer on top of SearchSysCache that does the initialization and + * key-setting for you. + * + * Returns the tuple if one is found, NULL if not. + * + * XXX The tuple that is returned is NOT supposed to be pfree'd! + */ +HeapTuple +SearchSysCacheTuple(int cacheId, /* cache selection code */ + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + register HeapTuple tp; + + if (cacheId < 0 || cacheId >= SysCacheSize) { + elog(WARN, "SearchSysCacheTuple: Bad cache id %d", cacheId); + return((HeapTuple) NULL); + } + + if (!AMI_OVERRIDE) { + Assert(PointerIsValid(SysCache[cacheId])); + } else { + if (!PointerIsValid(SysCache[cacheId])) { + SysCache[cacheId] = + InitSysCache(cacheinfo[cacheId].name, + cacheinfo[cacheId].indname, + cacheId, + cacheinfo[cacheId].nkeys, + cacheinfo[cacheId].key, + cacheinfo[cacheId].iScanFunc); + if (!PointerIsValid(SysCache[cacheId])) { + elog(WARN, + "InitCatalogCache: Can't init cache %.16s(%d)", + cacheinfo[cacheId].name, + cacheId); + } + + } + } + + tp = SearchSysCache(SysCache[cacheId], key1, key2, key3, key4); + if (!HeapTupleIsValid(tp)) { +#ifdef CACHEDEBUG + elog(DEBUG, + "SearchSysCacheTuple: Search %s(%d) %d %d %d %d failed", + (*cacheinfo[cacheId].name)->data, + cacheId, key1, key2, key3, key4); +#endif + return((HeapTuple) NULL); + } + return(tp); +} + +/* + * SearchSysCacheStruct-- + * Fills 's' with the information retrieved by calling SearchSysCache() + * with arguments key1...key4. Retrieves only the portion of the tuple + * which is not variable-length. + * + * NOTE: we are assuming that non-variable-length fields in the system + * catalogs will always be defined! + * + * Returns 1L if a tuple was found, 0L if not. + */ +int32 +SearchSysCacheStruct(int cacheId, /* cache selection code */ + char *returnStruct, /* (preallocated!) */ + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + HeapTuple tp; + + if (!PointerIsValid(returnStruct)) { + elog(WARN, "SearchSysCacheStruct: No receiving struct"); + return(0); + } + tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4); + if (!HeapTupleIsValid(tp)) + return(0); + memmove(returnStruct, (char *) GETSTRUCT(tp), cacheinfo[cacheId].size); + return(1); +} + + +/* + * SearchSysCacheGetAttribute-- + * Returns the attribute corresponding to 'attributeNumber' for + * a given cached tuple. + * + * XXX This re-opens a relation, so this is slower. + * + * [callers all assume this returns a (struct varlena *). -ay 10/94] + */ +void * +SearchSysCacheGetAttribute(int cacheId, + AttrNumber attributeNumber, + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + HeapTuple tp; + char *cacheName; + Relation relation; + int32 attributeLength, attributeByValue; + bool isNull; + char *attributeValue; + void *returnValue; + + tp = SearchSysCacheTuple(cacheId, key1, key2, key3, key4); + cacheName = cacheinfo[cacheId].name; + + if (!HeapTupleIsValid(tp)) { +#ifdef CACHEDEBUG + elog(DEBUG, + "SearchSysCacheGetAttribute: Lookup in %s(%d) failed", + cacheName, cacheId); +#endif /* defined(CACHEDEBUG) */ + return(NULL); + } + + relation = heap_openr(cacheName); + + if (attributeNumber < 0 && + attributeNumber > FirstLowInvalidHeapAttributeNumber) { + attributeLength = heap_sysattrlen(attributeNumber); + attributeByValue = heap_sysattrbyval(attributeNumber); + } else if (attributeNumber > 0 && + attributeNumber <= relation->rd_rel->relnatts) { + attributeLength = + relation->rd_att->attrs[attributeNumber-1]->attlen; + attributeByValue = + relation->rd_att->attrs[attributeNumber-1]->attbyval; + } else { + elog(WARN, + "SearchSysCacheGetAttribute: Bad attr # %d in %s(%d)", + attributeNumber, cacheName, cacheId); + return(NULL); + } + + attributeValue = heap_getattr(tp, + (Buffer) 0, + attributeNumber, + RelationGetTupleDescriptor(relation), + &isNull); + + if (isNull) { + /* + * Used to be an elog(DEBUG, ...) here and a claim that it should + * be a FATAL error, I don't think either is warranted -mer 6/9/92 + */ + return(NULL); + } + + if (attributeByValue) { + returnValue = (void *)attributeValue; + } else { + char *tmp; + int size = (attributeLength < 0) + ? VARSIZE((struct varlena *) attributeValue) /* variable length */ + : attributeLength; /* fixed length */ + + tmp = (char *) palloc(size); + memmove(tmp, attributeValue, size); + returnValue = (void *)tmp; + } + + heap_close(relation); + return(returnValue); +} + +/* + * TypeDefaultRetrieve-- + * + * Given a type OID, return the typdefault field associated with that + * type. The typdefault is returned as the car of a dotted pair which + * is passed to TypeDefaultRetrieve by the calling routine. + * + * Returns a fixnum for types which are passed by value and a ppreserve'd + * vectori for types which are not. + * + * [identical to get_typdefault, expecting a (struct varlena *) as ret val. + * some day, either of the functions should be removed -ay 10/94] + */ +void * +TypeDefaultRetrieve(Oid typId) +{ + HeapTuple typeTuple; + TypeTupleForm type; + int32 typByVal, typLen; + struct varlena *typDefault; + int32 dataSize; + void *returnValue; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(typId), + 0,0,0); + + if (!HeapTupleIsValid(typeTuple)) { +#ifdef CACHEDEBUG + elog(DEBUG, "TypeDefaultRetrieve: Lookup in %s(%d) failed", + (*cacheinfo[TYPOID].name)->data, TYPOID); +#endif /* defined(CACHEDEBUG) */ + return(NULL); + } + + type = (TypeTupleForm) GETSTRUCT(typeTuple); + typByVal = type->typbyval; + typLen = type->typlen; + + typDefault = (struct varlena *) + SearchSysCacheGetAttribute(TYPOID, + Anum_pg_type_typdefault, + ObjectIdGetDatum(typId), + 0,0,0); + + if (typDefault == (struct varlena *)NULL) { +#ifdef CACHEDEBUG + elog(DEBUG, "TypeDefaultRetrieve: No extractable typdefault", + (*cacheinfo[TYPOID].name)->data, TYPOID); +#endif /* defined(CACHEDEBUG) */ + return (NULL); + + } + + dataSize = VARSIZE(typDefault) - VARHDRSZ; + + if (typByVal) { + int8 i8; + int16 i16; + int32 i32; + + if (dataSize == typLen) { + switch (typLen) { + case sizeof(int8): + memmove((char *) &i8, VARDATA(typDefault), sizeof(int8)); + i32 = i8; + break; + case sizeof(int16): + memmove((char *) &i16, VARDATA(typDefault), sizeof(int16)); + i32 = i16; + break; + case sizeof(int32): + memmove((char *) &i32, VARDATA(typDefault), sizeof(int32)); + break; + } + returnValue = (void *)i32; + } else { + returnValue = NULL; + } + } else { + if ((typLen < 0 && dataSize < 0) || dataSize != typLen) + returnValue = NULL; + else { + returnValue = (void *)palloc(VARSIZE(typDefault)); + memmove((char *) returnValue, + (char *) typDefault, + (int) VARSIZE(typDefault)); + } + } + + return(returnValue); +} + + diff --git a/src/backend/utils/catcache.h b/src/backend/utils/catcache.h new file mode 100644 index 00000000000..daecf2ff30a --- /dev/null +++ b/src/backend/utils/catcache.h @@ -0,0 +1,85 @@ +/*------------------------------------------------------------------------- + * + * catcache.h-- + * Low-level catalog cache definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: catcache.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef CATCACHE_H +#define CATCACHE_H + +/* #define CACHEDEBUG turns DEBUG elogs on */ + +#include "postgres.h" + +#include "access/skey.h" +#include "access/htup.h" +#include "utils/rel.h" +#include "nodes/memnodes.h" +#include "lib/dllist.h" + +/* + * struct catctup: tuples in the cache. + * struct catcache: information for managing a cache. + */ + +typedef struct catctup { + HeapTuple ct_tup; /* A pointer to a tuple */ + Dlelem *ct_node; /* points to LRU list is the CatCTup is in the cache, + else, points to the cache if the CatCTup is in + LRU list */ +} CatCTup; + +/* voodoo constants */ +#define NCCBUCK 500 /* CatCache buckets*/ +#define MAXTUP 300 /* Maximum # of tuples cached per cache */ + +typedef struct catcache { + Oid relationId; + Oid indexId; + char *cc_relname; /* relation name for defered open */ + char *cc_indname; /* index name for defered open */ + HeapTuple (*cc_iscanfunc)(); /* index scanfunction */ + TupleDesc cc_tupdesc; /* tuple descriptor from reldesc */ + int id; /* XXX could be improved -hirohama */ + short cc_ntup; /* # of tuples in this cache */ + short cc_maxtup; /* max # of tuples allowed (LRU)*/ + short cc_nkeys; + short cc_size; + short cc_key[4]; + short cc_klen[4]; + ScanKeyData cc_skey[4]; + struct catcache *cc_next; + Dllist *cc_lrulist; /* LRU list, most recent first */ + Dllist *cc_cache[NCCBUCK+1]; +} CatCache; + +#define InvalidCatalogCacheId (-1) + +extern struct catcache *Caches; +extern GlobalMemory CacheCxt; + +extern void CatalogCacheInitializeCache(struct catcache *cache, + Relation relation); +extern void CatalogCacheSetId(CatCache *cacheInOutP, int id); +extern long comphash(long l, char *v); +extern Index CatalogCacheComputeHashIndex(struct catcache *cacheInP); +extern Index CatalogCacheComputeTupleHashIndex(struct catcache *cacheInOutP, + Relation relation, HeapTuple tuple); +extern void CatCacheRemoveCTup(CatCache *cache, Dlelem *e); +extern void CatalogCacheIdInvalidate(int cacheId, Index hashIndex, + ItemPointer pointer); +extern void ResetSystemCache(void); +extern CatCache *InitSysCache(char *relname, char *indname, int id, int nkeys, + int key[], HeapTuple (*iScanfuncP)()); +extern HeapTuple SearchSysCache(struct catcache *cache, Datum v1, Datum v2, + Datum v3, Datum v4); +extern void RelationInvalidateCatalogCacheTuple(Relation relation, + HeapTuple tuple, void (*function)()); + +#endif /* CATCACHE_H */ diff --git a/src/backend/utils/datum.h b/src/backend/utils/datum.h new file mode 100644 index 00000000000..09a28518067 --- /dev/null +++ b/src/backend/utils/datum.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * datum.h-- + * POSTGRES abstract data type datum representation definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: datum.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef DATUM_H +#define DATUM_H + +#include "postgres.h" + +/*-------------------------------------------------------- + * SOME NOT VERY PORTABLE ROUTINES ??? + *-------------------------------------------------------- + * + * 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 thr + * 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. + */ + +/*--------------- + * datumGetSize + * find the "real" length of a datum + */ +extern Size datumGetSize(Datum value, Oid type, bool byVal, Size len); + +/*--------------- + * datumCopy + * make a copy of a datum. + */ +extern Datum datumCopy(Datum value, Oid type, bool byVal, Size len); + +/*--------------- + * datumFree + * free space that *might* have been palloced by "datumCopy" + */ +extern void datumFree(Datum value, Oid type, bool byVal, Size len); + +/*--------------- + * datumIsEqual + * return true if thwo datums are equal, false otherwise. + * XXX : See comments in the code for restrictions! + */ +extern bool datumIsEqual(Datum value1, Datum value2, Oid type, + bool byVal, Size len); + +#endif /* DATUM_H */ diff --git a/src/backend/utils/dynamic_loader.h b/src/backend/utils/dynamic_loader.h new file mode 100644 index 00000000000..c0532bdcbac --- /dev/null +++ b/src/backend/utils/dynamic_loader.h @@ -0,0 +1,53 @@ +/*------------------------------------------------------------------------- + * + * dynamic_loader.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: dynamic_loader.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef DYNAMIC_LOADER_H +#define DYNAMIC_LOADER_H + +#ifdef MIN +#undef MIN +#undef MAX +#endif /* MIN */ + +#ifdef WIN32 +#define MAXPATHLEN 250 +#endif + +#include <sys/param.h> /* for MAXPATHLEN */ +#include <sys/types.h> /* for dev_t, ino_t, etc. */ +#ifdef WIN32 +#include <wchar.h> +#endif + +/* + * List of dynamically loaded files. + */ + +typedef struct df_files { + char filename[MAXPATHLEN]; /* Full pathname of file */ +#ifdef WIN32 + _dev_t device; /* Device file is on */ + _ino_t inode; /* Inode number of file */ +#else + dev_t device; /* Device file is on */ + ino_t inode; /* Inode number of file */ +#endif /* WIN32 */ + void *handle; /* a handle for pg_dl* functions */ + struct df_files *next; +} DynamicFileList; + +extern void *pg_dlopen(char *filename); +extern func_ptr pg_dlsym(void *handle, char *funcname); +extern void pg_dlclose(void *handle); +extern char *pg_dlerror(void); + +#endif /* DYNAMIC_LOADER_H */ diff --git a/src/backend/utils/elog.h b/src/backend/utils/elog.h new file mode 100644 index 00000000000..bf858fc7ad3 --- /dev/null +++ b/src/backend/utils/elog.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * elog.h-- + * POSTGRES error logging definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: elog.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef ELOG_H +#define ELOG_H + +#define NOTICE 0 /* random info - no special action */ +#define WARN -1 /* Warning error - return to known state */ +#define FATAL 1 /* Fatal error - abort process */ +#define DEBUG -2 /* debug message */ +#define NOIND -3 /* debug message, don't indent as far */ + +#define PTIME 0x100 /* prepend time to message */ +#define POS 0x200 /* prepend source position to message */ +#define USERMSG 0x400 /* send message to user */ +#define TERM 0x800 /* send message to terminal */ +#define DBLOG 0x1000 /* put message in per db log */ +#define SLOG 0x2000 /* put message in system log */ +#define ABORT 0x4000 /* abort process after logging */ + +#define ELOG_MAXLEN 4096 + + +/* uncomment the following if you want your elog's to be timestamped */ +/* #define ELOG_TIMESTAMPS */ + +extern void elog(int lev, char *fmt, ...); + +#endif /* ELOG_H */ diff --git a/src/backend/utils/error/Makefile.inc b/src/backend/utils/error/Makefile.inc new file mode 100644 index 00000000000..2c3d469669b --- /dev/null +++ b/src/backend/utils/error/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/error +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= assert.c elog.c exc.c excabort.c excid.c format.c diff --git a/src/backend/utils/error/assert.c b/src/backend/utils/error/assert.c new file mode 100644 index 00000000000..ac2de1631d1 --- /dev/null +++ b/src/backend/utils/error/assert.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * assert.c-- + * Assert code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/assert.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * NOTE + * This should eventually work with elog(), dlog(), etc. + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> + +#include "c.h" /* where the declaration goes */ +#include "utils/module.h" + +#include "utils/exc.h" + +int +ExceptionalCondition(char* conditionName, + Exception *exceptionP, + char* detail, + char* fileName, + int lineNumber) +{ + extern char* ExcFileName; /* XXX */ + extern Index ExcLineNumber; /* XXX */ + + ExcFileName = fileName; + ExcLineNumber = lineNumber; + + if (!PointerIsValid(conditionName) + || !PointerIsValid(fileName) + || !PointerIsValid(exceptionP)) { + fprintf(stderr, "ExceptionalCondition: bad arguments\n"); + + ExcAbort(exceptionP, + (ExcDetail)detail, + (ExcData)NULL, + (ExcMessage)NULL); + } else { + fprintf(stderr, + "%s(\"%s:%s\", File: \"%s\", Line: %d)\n", + exceptionP->message, conditionName, detail, + fileName, lineNumber); + } + + /* + * XXX Depending on the Exception and tracing conditions, you will + * XXX want to stop here immediately and maybe dump core. + * XXX This may be especially true for Assert(), etc. + */ + + /* TraceDump(); dump the trace stack */ + + /* XXX FIXME: detail is lost */ + ExcRaise(exceptionP, (ExcDetail)0, (ExcData)NULL, conditionName); + return(0); +} diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c new file mode 100644 index 00000000000..d5bef1ab1ba --- /dev/null +++ b/src/backend/utils/error/elog.c @@ -0,0 +1,237 @@ +/*------------------------------------------------------------------------- + * + * elog.c-- + * error logger + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#ifndef O_RDONLY +#include <sys/file.h> +#endif /* O_RDONLY */ +#include <sys/types.h> +#include <stdarg.h> +#include <errno.h> + +#include "postgres.h" +#include "miscadmin.h" +#include "utils/elog.h" +#include "libpq/libpq.h" +#include "storage/proc.h" + +static int Debugfile = -1; +static int Err_file = -1; +static int ElogDebugIndentLevel = 0; + +extern char OutputFileName[]; +#ifdef WIN32 +extern jmp_buf Warn_restart; +#endif + +/* + * elog -- + * Old error logging function. + */ +void +elog(int lev, char *fmt, ... ) +{ + va_list ap; + char buf[ELOG_MAXLEN], line[ELOG_MAXLEN]; + register char *bp, *cp; + extern int errno, sys_nerr; +#if !defined(PORTNAME_BSD44_derived) && !defined(PORTNAME_bsdi) + extern char *sys_errlist[]; +#endif /* !PORTNAME_BSD44_derived */ +#ifndef PG_STANDALONE + extern FILE *Pfout; +#endif /* !PG_STANDALONE */ + time_t tim, time(); + int len; + int i = 0; + + va_start(ap, fmt); + if (lev == DEBUG && Debugfile < 0) { + return; + } + switch (lev) { + case NOIND: + i = ElogDebugIndentLevel-1; + if (i < 0) i = 0; + if (i > 30) i = i%30; + cp = "DEBUG:"; + break; + case DEBUG: + i = ElogDebugIndentLevel; + if (i < 0) i = 0; + if (i > 30) i = i%30; + cp = "DEBUG:"; + break; + case NOTICE: + cp = "NOTICE:"; + break; + case WARN: + cp = "WARN:"; + break; + default: + sprintf(line, "FATAL %d:", lev); + cp = line; + } +#ifdef ELOG_TIMESTAMPS + time(&tim); + strcat(strcpy(buf, cp), ctime(&tim)+4); + bp = buf+strlen(buf)-6; + *bp++ = ':'; +#else + strcpy(buf,cp); + bp = buf+strlen(buf); +#endif + while (i-- >0) *bp++ = ' '; + for (cp = fmt; *cp; cp++) + if (*cp == '%' && *(cp+1) == 'm') { + if (errno < sys_nerr && errno >= 0) + strcpy(bp, sys_errlist[errno]); + else + sprintf(bp, "error %d", errno); + bp += strlen(bp); + cp++; + } else + *bp++ = *cp; + *bp = '\0'; + vsprintf(line, buf, ap); + va_end(ap); + len = strlen(strcat(line, "\n")); + if (Debugfile > -1) + write(Debugfile, line, len); + if (lev == DEBUG || lev == NOIND) + return; + + /* + * If there's an error log file other than our channel to the + * front-end program, write to it first. This is important + * because there's a bug in the socket code on ultrix. If the + * front end has gone away (so the channel to it has been closed + * at the other end), then writing here can cause this backend + * to exit without warning -- that is, write() does an exit(). + * In this case, our only hope of finding out what's going on + * is if Err_file was set to some disk log. This is a major pain. + */ + + if (Err_file > -1 && Debugfile != Err_file) { + if (write(Err_file, line, len) < 0) { + write(open("/dev/console", O_WRONLY, 0666), line, len); + fflush(stdout); + fflush(stderr); + exitpg(lev); + } + fsync(Err_file); + } + +#ifndef PG_STANDALONE + /* Send IPC message to the front-end program */ + if (Pfout != NULL && lev > DEBUG) { + /* notices are not exactly errors, handle it differently */ + if (lev == NOTICE) + pq_putnchar("N", 1); + else + pq_putnchar("E", 1); + /* pq_putint(-101, 4);*/ /* should be query id */ + pq_putstr(line); + pq_flush(); + } +#endif /* !PG_STANDALONE */ + + if (lev == WARN) { + ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */ +#ifndef WIN32 + kill(getpid(), 1); /* abort to traffic cop */ + pause(); +#else + longjmp(Warn_restart, 1); +#endif /* WIN32 */ + /* + * The pause(3) is just to avoid race conditions where the + * thread of control on an MP system gets past here (i.e., + * the signal is not received instantaneously). + */ + } + + if (lev == FATAL) { + /* + * Assume that if we have detected the failure we can + * exit with a normal exit status. This will prevent + * the postmaster from cleaning up when it's not needed. + */ + fflush(stdout); + fflush(stderr); + ProcReleaseSpins(NULL); /* get rid of spinlocks we hold */ + ProcReleaseLocks(); /* get rid of real locks we hold */ + exitpg(0); + } + + if (lev > FATAL) { + fflush(stdout); + fflush(stderr); + exitpg(lev); + } +} + +#ifndef PG_STANDALONE +int +DebugFileOpen() +{ + int fd, istty; + + Err_file = Debugfile = -1; + ElogDebugIndentLevel = 0; + + if (OutputFileName[0]) { + OutputFileName[MAXPGPATH-1] = '\0'; + if ((fd = open(OutputFileName, O_CREAT|O_APPEND|O_WRONLY, + 0666)) < 0) + elog(FATAL, "DebugFileOpen: open of %s: %m", + OutputFileName); + istty = isatty(fd); + (void) close(fd); + /* If the file is a tty and we're running under the + * postmaster, try to send stdout there as well (if it + * isn't a tty then stderr will block out stdout, so we + * may as well let stdout go wherever it was going before). + */ + if (istty && + IsUnderPostmaster && + !freopen(OutputFileName, "a", stdout)) + elog(FATAL, "DebugFileOpen: %s reopen as stdout: %m", + OutputFileName); + if (!freopen(OutputFileName, "a", stderr)) + elog(FATAL, "DebugFileOpen: %s reopen as stderr: %m", + OutputFileName); + Err_file = Debugfile = fileno(stderr); + return(Debugfile); + } +#ifndef WIN32 + /* If no filename was specified, send debugging output to stderr. + * If stderr has been hosed, try to open a file. + */ + fd = fileno(stderr); + if (fcntl(fd, F_GETFD, 0) < 0) { + sprintf(OutputFileName, "%s/pg.errors.%d", + GetPGData(), getpid()); + fd = open(OutputFileName, O_CREAT|O_APPEND|O_WRONLY, 0666); + } +#endif /* WIN32 */ + if (fd < 0) + elog(FATAL, "DebugFileOpen: could not open debugging file"); + + Err_file = Debugfile = fd; + return(Debugfile); +} +#endif diff --git a/src/backend/utils/error/exc.c b/src/backend/utils/error/exc.c new file mode 100644 index 00000000000..35c5db9118f --- /dev/null +++ b/src/backend/utils/error/exc.c @@ -0,0 +1,183 @@ +/*------------------------------------------------------------------------- + * + * exc.c-- + * POSTGRES exception handling code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/exc.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + * NOTE + * XXX this code needs improvement--check for state violations and + * XXX reset after handling an exception. + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* XXX use own I/O routines */ +#include <errno.h> +#include "utils/exc.h" +#include "storage/ipc.h" + +/* + * Global Variables + */ +static bool ExceptionHandlingEnabled = false; + +char* ExcFileName = NULL; +Index ExcLineNumber = 0; + +ExcFrame *ExcCurFrameP = NULL; + +static ExcProc *ExcUnCaughtP = NULL; + +extern char* ProgramName; + +/* + * Exported Functions + */ + +/* + * EnableExceptionHandling -- + * Enables/disables the exception handling system. + * + * Note: + * This must be called before any exceptions occur. I.e., call this first! + * This routine will not return if an error is detected. + * This does not follow the usual Enable... protocol. + * This should be merged more closely with the error logging and tracing + * packages. + * + * Exceptions: + * none + */ +/* + * Excection handling should be supported by the language, thus there should + * be no need to explicitly enable exception processing. + * + * This function should probably not be called, ever. Currently it does + * almost nothing. If there is a need for this intialization and checking. + * then this function should be converted to the new-style Enable code and + * called by all the other module Enable functions. + */ +void +EnableExceptionHandling(bool on) +{ + if (on == ExceptionHandlingEnabled) { + /* XXX add logging of failed state */ + exitpg(255); + /* ExitPostgres(FatalExitStatus); */ + } + + if (on) { /* initialize */ + ; + } else { /* cleanup */ + ExcFileName = NULL; + ExcLineNumber = 0; + ExcCurFrameP = NULL; + ExcUnCaughtP = NULL; + } + + ExceptionHandlingEnabled = on; +} + +void +ExcPrint(Exception *excP, + ExcDetail detail, + ExcData data, + ExcMessage message) +{ + extern int errno; + extern int sys_nerr; +#if !defined(PORTNAME_BSD44_derived) && !defined(PORTNAME_bsdi) + extern char *sys_errlist[]; +#endif /* !PORTNAME_BSD44_derived */ + +#ifdef lint + data = data; +#endif + + (void) fflush(stdout); /* In case stderr is buffered */ + +#if 0 + if (ProgramName != NULL && *ProgramName != '\0') + (void) fprintf(stderr, "%s: ", ProgramName); +#endif + + if (message != NULL) + (void) fprintf(stderr, "%s", message); + else if (excP->message != NULL) + (void) fprintf(stderr, "%s", excP->message); + else +#ifdef lint + (void) fprintf(stderr, "UNNAMED EXCEPTION 0x%lx", excP); +#else + (void) fprintf(stderr, "UNNAMED EXCEPTION 0x%lx", (long)excP); +#endif + + (void) fprintf(stderr, " (%ld)", detail); + + if (errno > 0 && errno < sys_nerr && + sys_errlist[errno] != NULL && sys_errlist[errno][0] != '\0') + (void) fprintf(stderr, " [%s]", sys_errlist[errno]); + else if (errno != 0) + (void) fprintf(stderr, " [Error %d]", errno); + + (void) fprintf(stderr, "\n"); + + (void) fflush(stderr); +} + +ExcProc * +ExcGetUnCaught() +{ + return (ExcUnCaughtP); +} + +ExcProc * +ExcSetUnCaught(ExcProc *newP) +{ + ExcProc *oldP = ExcUnCaughtP; + + ExcUnCaughtP = newP; + + return (oldP); +} + +void +ExcUnCaught(Exception *excP, + ExcDetail detail, + ExcData data, + ExcMessage message) +{ + ExcPrint(excP, detail, data, message); + + ExcAbort(excP, detail, data, message); +} + +void +ExcRaise(Exception *excP, + ExcDetail detail, + ExcData data, + ExcMessage message) +{ + register ExcFrame *efp; + + efp = ExcCurFrameP; + if (efp == NULL) { + if (ExcUnCaughtP != NULL) + (*ExcUnCaughtP)(excP, detail, data, message); + + ExcUnCaught(excP, detail, data, message); + } else { + efp->id = excP; + efp->detail = detail; + efp->data = data; + efp->message = message; + + ExcCurFrameP = efp->link; + + longjmp(efp->context, 1); + } +} diff --git a/src/backend/utils/error/excabort.c b/src/backend/utils/error/excabort.c new file mode 100644 index 00000000000..d1b14361ea5 --- /dev/null +++ b/src/backend/utils/error/excabort.c @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * excabort.c-- + * Default exception abort code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/excabort.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "utils/exc.h" /* where function declarations go */ + +void +ExcAbort(const Exception *excP, + ExcDetail detail, + ExcData data, + ExcMessage message) +{ +#ifdef __SABER__ + saber_stop(); +#else + /* dump core */ + abort(); +#endif +} diff --git a/src/backend/utils/error/excid.c b/src/backend/utils/error/excid.c new file mode 100644 index 00000000000..4d87c16344f --- /dev/null +++ b/src/backend/utils/error/excid.c @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * excid.c-- + * POSTGRES known exception identifier code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/excid.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "utils/excid.h" + +/***************************************************************************** + * Generic Recoverable Exceptions * + *****************************************************************************/ + + +/* + * FailedAssertion -- + * Indicates an Assert(...) failed. + */ +Exception FailedAssertion = { "Failed Assertion" }; + +/* + * BadState -- + * Indicates a function call request is inconsistent with module state. + */ +Exception BadState = { "Bad State for Function Call" }; + +/* + * BadArg -- + * Indicates a function call argument or arguments is out-of-bounds. + */ +Exception BadArg = { "Bad Argument to Function Call" }; + +/***************************************************************************** + * Specific Recoverable Exceptions * + *****************************************************************************/ + +/* + * BadAllocSize -- + * Indicates that an allocation request is of unreasonable size. + */ +Exception BadAllocSize = { "Too Large Allocation Request" }; + +/* + * ExhaustedMemory -- + * Indicates an dynamic memory allocation failed. + */ +Exception ExhaustedMemory = { "Memory Allocation Failed" }; + +/* + * Unimplemented -- + * Indicates a function call request requires unimplemented code. + */ +Exception Unimplemented = { "Unimplemented Functionality" }; + +Exception CatalogFailure = {"Catalog failure"}; /* XXX inconsistent */ +Exception InternalError = {"Internal Error"}; /* XXX inconsistent */ +Exception SemanticError = {"Semantic Error"}; /* XXX inconsistent */ +Exception SystemError = {"System Error"}; /* XXX inconsistent */ diff --git a/src/backend/utils/error/format.c b/src/backend/utils/error/format.c new file mode 100644 index 00000000000..99a5d15af57 --- /dev/null +++ b/src/backend/utils/error/format.c @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * format.c-- + * a wrapper around code that does what vsprintf does. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/error/Attic/format.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <stdarg.h> +#include "c.h" + +#define FormMaxSize 1024 +#define FormMinSize (FormMaxSize / 8) + +static char FormBuf[FormMaxSize]; + + +/* ---------------- + * form + * ---------------- + */ +char * +form(char *fmt, ... ) +{ + va_list args; + + va_start(args, fmt); + + (void) vsprintf(FormBuf, fmt, args); + + va_end(args); + + return (FormBuf); +} diff --git a/src/backend/utils/exc.h b/src/backend/utils/exc.h new file mode 100644 index 00000000000..487ecca8374 --- /dev/null +++ b/src/backend/utils/exc.h @@ -0,0 +1,101 @@ +/*------------------------------------------------------------------------- + * + * exc.h-- + * POSTGRES exception handling definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: exc.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef EXC_H +#define EXC_H + +#include "c.h" /* for Exception, etc. */ +#include <setjmp.h> + +extern char *ExcFileName; +extern Index ExcLineNumber; + +/* + * ExcMessage and Exception are now defined in c.h + */ + +#if defined(PORTNAME_linux) \ +|| defined(PORTNAME_hpux) \ +|| defined(PORTNAME_next)\ +|| defined(WIN32) +typedef jmp_buf ExcContext; +#else +typedef sigjmp_buf ExcContext; +#endif + +typedef Exception* ExcId; +typedef long ExcDetail; +typedef char* ExcData; + +typedef struct ExcFrame { + struct ExcFrame *link; + ExcContext context; + ExcId id; + ExcDetail detail; + ExcData data; + ExcMessage message; +} ExcFrame; + +extern ExcFrame* ExcCurFrameP; + +#define ExcBegin() \ + { \ + ExcFrame exception; \ + \ + exception.link = ExcCurFrameP; \ + if (sigsetjmp(exception.context, 1) == 0) { \ + ExcCurFrameP = &exception; \ + { +#define ExcExcept() \ + } \ + ExcCurFrameP = exception.link; \ + } else { \ + { +#define ExcEnd() \ + } \ + } \ + } + +#define raise4(x, t, d, message) \ + ExcRaise(&(x), (ExcDetail)(t), (ExcData)(d), (ExcMessage)(message)) + +#define reraise() \ + raise4(*exception.id,exception.detail,exception.data,exception.message) + +typedef void ExcProc(Exception*, ExcDetail, ExcData, ExcMessage); + + +/* + * prototypes for functions in exc.c + */ +extern void EnableExceptionHandling(bool on); +extern void ExcPrint(Exception *excP, ExcDetail detail, ExcData data, + ExcMessage message); +extern ExcProc *ExcGetUnCaught(); +extern ExcProc *ExcSetUnCaught(ExcProc *newP); +extern void ExcUnCaught(Exception *excP, ExcDetail detail, ExcData data, + ExcMessage message); +extern void ExcUnCaught(Exception *excP, ExcDetail detail, ExcData data, + ExcMessage message); +extern void ExcRaise(Exception *excP, + ExcDetail detail, + ExcData data, + ExcMessage message); + + +/* + * prototypes for functions in excabort.c + */ +extern void ExcAbort(const Exception *excP, ExcDetail detail, ExcData data, + ExcMessage message); + +#endif /* EXC_H */ diff --git a/src/backend/utils/excid.h b/src/backend/utils/excid.h new file mode 100644 index 00000000000..a0d5edc6f93 --- /dev/null +++ b/src/backend/utils/excid.h @@ -0,0 +1,31 @@ +/*------------------------------------------------------------------------- + * + * excid.h-- + * POSTGRES known exception identifier definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: excid.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef EXCID_H +#define EXCID_H + +#include "c.h" +#include "utils/exc.h" /* for Exception */ + +extern Exception FailedAssertion; +extern Exception BadState; +extern Exception BadArg; +extern Exception BadAllocSize; +extern Exception ExhaustedMemory; +extern Exception Unimplemented; + +extern Exception CatalogFailure; /* XXX inconsistent naming style */ +extern Exception InternalError; /* XXX inconsistent naming style */ +extern Exception SemanticError; /* XXX inconsistent naming style */ +extern Exception SystemError; /* XXX inconsistent naming style */ + +#endif /* EXCID_H */ diff --git a/src/backend/utils/fcache.h b/src/backend/utils/fcache.h new file mode 100644 index 00000000000..a7a83452ba0 --- /dev/null +++ b/src/backend/utils/fcache.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * fcache.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fcache.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef FCACHE_H +#define FCACHE_H + +#include "fmgr.h" + +typedef struct +{ + int typlen; /* length of the return type */ + int typbyval; /* true if return type is pass by value */ + func_ptr func; /* address of function to call (for c funcs) */ + Oid foid; /* oid of the function in pg_proc */ + Oid language; /* oid of the language in pg_language */ + int nargs; /* number of arguments */ + + /* Might want to make these two arrays of size MAXFUNCARGS */ + + Oid *argOidVect; /* oids of all the arguments */ + bool *nullVect; /* keep track of null arguments */ + + char *src; /* source code of the function */ + char *bin; /* binary object code ?? */ + char *func_state; /* fuction_state struct for execution */ + + bool oneResult; /* true we only want 1 result from the + * function + */ + bool hasSetArg; /* true if func is part of a nested dot expr + * whose argument is func returning a set ugh! + */ + + Pointer funcSlot; /* if one result we need to copy it before we + * end execution of the function and free stuff + */ + + char *setArg; /* current argument for nested dot execution + * Nested dot expressions mean we have funcs + * whose argument is a set of tuples + */ + + bool istrusted; /* trusted fn? */ +} FunctionCache, *FunctionCachePtr; + +#endif /* FCACHE_H */ diff --git a/src/backend/utils/fcache2.h b/src/backend/utils/fcache2.h new file mode 100644 index 00000000000..3f149aee307 --- /dev/null +++ b/src/backend/utils/fcache2.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * fcache2.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fcache2.h,v 1.1.1.1 1996/07/09 06:22:01 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef FCACHE2_H +#define FCACHE2_H + +extern void +setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext); + +#endif /* FCACHE2_H */ diff --git a/src/backend/utils/fmgr/Makefile.inc b/src/backend/utils/fmgr/Makefile.inc new file mode 100644 index 00000000000..19615544317 --- /dev/null +++ b/src/backend/utils/fmgr/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/fmgr +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/fmgr/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= dfmgr.c fmgr.c + diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c new file mode 100644 index 00000000000..e835d9451d6 --- /dev/null +++ b/src/backend/utils/fmgr/dfmgr.c @@ -0,0 +1,269 @@ +/*------------------------------------------------------------------------- + * + * dfmgr.c-- + * Dynamic function manager code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/dfmgr.c,v 1.1.1.1 1996/07/09 06:22:07 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> + +#include "postgres.h" + +#include "fmgr.h" /* generated by Gen_fmgrtab.sh */ +#include "utils/dynamic_loader.h" +#include "utils/elog.h" +#include "utils/builtins.h" +#include "access/heapam.h" +#include "nodes/pg_list.h" + +#include "port-protos.h" /* system specific function prototypes */ + +#include "catalog/catname.h" +#include "utils/syscache.h" +#include "catalog/pg_proc.h" + +static DynamicFileList *file_list = (DynamicFileList *) NULL; +static DynamicFileList *file_tail = (DynamicFileList *) NULL; + +#define NOT_EQUAL(A, B) (((A).st_ino != (B).inode) \ + || ((A).st_dev != (B).device)) + +static Oid procedureId_save = -1; +static int pronargs_save; +static func_ptr user_fn_save = (func_ptr) NULL; +static func_ptr handle_load(char *filename, char *funcname); + +func_ptr +fmgr_dynamic(Oid procedureId, int *pronargs) +{ + HeapTuple procedureTuple; + Form_pg_proc procedureStruct; + char *proname; + char *probinattr, *probinstring; + func_ptr user_fn, handle_load(); + Relation rdesc; + bool isnull; + + if (procedureId == procedureId_save) { + *pronargs = pronargs_save; + return(user_fn_save); + } + + /* + * The procedure isn't a builtin, so we'll have to do a catalog + * lookup to find its pg_proc entry. + */ + procedureTuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(procedureId), + 0,0,0); + if (!HeapTupleIsValid(procedureTuple)) { + elog(WARN, "fmgr: Cache lookup failed for procedure %d\n", + procedureId); + return((func_ptr) NULL); + } + + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + proname = procedureStruct->proname.data; + pronargs_save = *pronargs = procedureStruct->pronargs; + + /* + * Extract the procedure info from the pg_proc tuple. + * Since probin is varlena, do a amgetattr() on the procedure + * tuple. To do that, we need the rdesc for the procedure + * relation, so... + */ + + /* open pg_procedure */ + + rdesc = heap_openr(ProcedureRelationName); + if (!RelationIsValid(rdesc)) { + elog(WARN, "fmgr: Could not open relation %s", + ProcedureRelationName); + return((func_ptr) NULL); + } + probinattr = heap_getattr(procedureTuple, (Buffer) 0, + Anum_pg_proc_probin, + RelationGetTupleDescriptor(rdesc), &isnull); + if (!PointerIsValid(probinattr) /*|| isnull*/) { + heap_close(rdesc); + elog(WARN, "fmgr: Could not extract probin for %d from %s", + procedureId, ProcedureRelationName); + return((func_ptr) NULL); + } + probinstring = textout((struct varlena *) probinattr); + + user_fn = handle_load(probinstring, proname); + + procedureId_save = procedureId; + user_fn_save = user_fn; + + return(user_fn); +} + +static func_ptr +handle_load(char *filename, char *funcname) +{ + DynamicFileList *file_scanner = (DynamicFileList *) NULL; + func_ptr retval = (func_ptr) NULL; + char *load_error; +#ifdef WIN32 + struct _stat stat_buf; +#else + struct stat stat_buf; +#endif /* WIN32 */ + + /* + * Do this because loading files may screw up the dynamic function + * manager otherwise. + */ + procedureId_save = -1; + + /* + * Scan the list of loaded FILES to see if the function + * has been loaded. + */ + + if (filename != (char *) NULL) { + for (file_scanner = file_list; + file_scanner != (DynamicFileList *) NULL + && file_scanner->filename != (char *) NULL + && strcmp(filename, file_scanner->filename) != 0; + file_scanner = file_scanner->next) + ; + if (file_scanner == (DynamicFileList *) NULL) { + if (stat(filename, &stat_buf) == -1) { + elog(WARN, "stat failed on file %s", filename); + } + + for (file_scanner = file_list; + file_scanner != (DynamicFileList *) NULL + && (NOT_EQUAL(stat_buf, *file_scanner)); + file_scanner = file_scanner->next) + ; + /* + * Same files - different paths (ie, symlink or link) + */ + if (file_scanner != (DynamicFileList *) NULL) + (void) strcpy(file_scanner->filename, filename); + + } + } else { + file_scanner = (DynamicFileList *) NULL; + } + + /* + * File not loaded yet. + */ + + if (file_scanner == (DynamicFileList *) NULL) { + if (file_list == (DynamicFileList *) NULL) { + file_list = (DynamicFileList *) + malloc(sizeof(DynamicFileList)); + file_scanner = file_list; + } else { + file_tail->next = (DynamicFileList *) + malloc(sizeof(DynamicFileList)); + file_scanner = file_tail->next; + } + memset((char *) file_scanner, 0, sizeof(DynamicFileList)); + + (void) strcpy(file_scanner->filename, filename); +#ifndef WIN32 + file_scanner->device = stat_buf.st_dev; + file_scanner->inode = stat_buf.st_ino; +#endif /* WIN32 */ + file_scanner->next = (DynamicFileList *) NULL; + + file_scanner->handle = pg_dlopen(filename); + if (file_scanner->handle == (void *)NULL) { + load_error = pg_dlerror(); + if (file_scanner == file_list) { + file_list = (DynamicFileList *) NULL; + } else { + file_tail->next = (DynamicFileList *) NULL; + } + + free((char *) file_scanner); + elog(WARN, "Load of file %s failed: %s", filename, load_error); + } + + /* + * Just load the file - we are done with that so return. + */ + file_tail = file_scanner; + + if (funcname == (char *) NULL) + return((func_ptr) NULL); + } + + retval = pg_dlsym(file_scanner->handle, funcname); + + if (retval == (func_ptr) NULL) { + elog(WARN, "Can't find function %s in file %s", funcname, filename); + } + + return(retval); +} + +/* + * This function loads files by the following: + * + * If the file is already loaded: + * o Zero out that file's loaded space (so it doesn't screw up linking) + * o Free all space associated with that file + * o Free that file's descriptor. + * + * Now load the file by calling handle_load with a NULL argument as the + * function. + */ +void +load_file(char *filename) +{ + DynamicFileList *file_scanner, *p; +#ifdef WIN32 + struct _stat stat_buf; +#else + struct stat stat_buf; +#endif /* WIN32 */ + + int done = 0; + + if (stat(filename, &stat_buf) == -1) { + elog(WARN, "stat failed on file %s", filename); + } + + if (file_list != (DynamicFileList *) NULL + && !NOT_EQUAL(stat_buf, *file_list)) { + file_scanner = file_list; + file_list = file_list->next; + pg_dlclose(file_scanner->handle); + free((char *) file_scanner); + } else if (file_list != (DynamicFileList *) NULL) { + file_scanner = file_list; + while (!done) { + if (file_scanner->next == (DynamicFileList *) NULL) { + done = 1; + } else if (!NOT_EQUAL(stat_buf, *(file_scanner->next))) { + done = 1; + } else { + file_scanner = file_scanner->next; + } + } + + if (file_scanner->next != (DynamicFileList *) NULL) { + p = file_scanner->next; + file_scanner->next = file_scanner->next->next; + pg_dlclose(file_scanner->handle); + free((char *)p); + } + } + handle_load(filename, (char *) NULL); +} diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c new file mode 100644 index 00000000000..6ae276944ee --- /dev/null +++ b/src/backend/utils/fmgr/fmgr.c @@ -0,0 +1,254 @@ +/*------------------------------------------------------------------------- + * + * fmgr.c-- + * Interface routines for the table-driven function manager. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <stdarg.h> + +#include "postgres.h" + +/* these 2 files are generated by Gen_fmgrtab.sh; contain the declarations */ +#include "fmgr.h" +#include "utils/fmgrtab.h" + +#include "nodes/pg_list.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_language.h" +#include "utils/syscache.h" +#include "nodes/params.h" + +#include "utils/elog.h" + + +char * +fmgr_c(func_ptr user_fn, + Oid func_id, + int n_arguments, + FmgrValues *values, + bool *isNull) +{ + char *returnValue = (char *) NULL; + + + if (user_fn == (func_ptr) NULL) { + /* + * a NULL func_ptr denotes untrusted function (in postgres 4.2). + * Untrusted functions have very limited use and is clumsy. We + * just get rid of it. + */ + elog(WARN, "internal error: untrusted function not supported."); + } + + switch (n_arguments) { + case 0: + returnValue = (*user_fn)(); + break; + case 1: + /* NullValue() uses isNull to check if args[0] is NULL */ + returnValue = (*user_fn)(values->data[0], isNull); + break; + case 2: + returnValue = (*user_fn)(values->data[0], values->data[1]); + break; + case 3: + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2]); + break; + case 4: + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2], values->data[3]); + break; + case 5: + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2], values->data[3], + values->data[4]); + break; + case 6: + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2], values->data[3], + values->data[4], values->data[5]); + break; + case 7: + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2], values->data[3], + values->data[4], values->data[5], + values->data[6]); + break; + case 8: + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2], values->data[3], + values->data[4], values->data[5], + values->data[6], values->data[7]); + break; + case 9: + /* + * XXX Note that functions with >8 arguments can only be + * called from inside the system, not from the user level, + * since the catalogs only store 8 argument types for user + * type-checking! + */ + returnValue = (*user_fn)(values->data[0], values->data[1], + values->data[2], values->data[3], + values->data[4], values->data[5], + values->data[6], values->data[7], + values->data[8]); + break; + default: + elog(WARN, "fmgr_c: function %d: too many arguments (%d > %d)", + func_id, n_arguments, MAXFMGRARGS); + break; + } + return(returnValue); +} + +void +fmgr_info(Oid procedureId, func_ptr *function, int *nargs) +{ + func_ptr user_fn; + FmgrCall *fcp; + HeapTuple procedureTuple; + FormData_pg_proc *procedureStruct; + Oid language; + + if (!(fcp = fmgr_isbuiltin(procedureId))) { + procedureTuple = SearchSysCacheTuple(PROOID, + ObjectIdGetDatum(procedureId), + 0,0,0); + if (!HeapTupleIsValid(procedureTuple)) { + elog(WARN, "fmgr_info: function %d: cache lookup failed\n", + procedureId); + } + procedureStruct = (FormData_pg_proc *) + GETSTRUCT(procedureTuple); + if (!procedureStruct->proistrusted) { + *function = (func_ptr) NULL; + *nargs = procedureStruct->pronargs; + return; + } + language = procedureStruct->prolang; + switch (language) { + case INTERNALlanguageId: + user_fn = fmgr_lookupByName(procedureStruct->proname.data); + if (!user_fn) + elog(WARN, "fmgr_info: function %s: not in internal table", + procedureStruct->proname.data); + break; + case ClanguageId: + user_fn = fmgr_dynamic(procedureId, nargs); + break; + case SQLlanguageId: + user_fn = (func_ptr) NULL; + *nargs = procedureStruct->pronargs; + break; + default: + elog(WARN, "fmgr_info: function %d: unknown language %d", + procedureId, language); + } + } else { + user_fn = fcp->func; + *nargs = fcp->nargs; + } + *function = user_fn; +} + +/* + * fmgr - return the value of a function call + * + * If the function is a system routine, it's compiled in, so call + * it directly. + * + * Otherwise pass it to the the appropriate 'language' function caller. + * + * Returns the return value of the invoked function if succesful, + * 0 if unsuccessful. + */ +char * +fmgr(Oid procedureId, ... ) +{ + va_list pvar; + register i; + int pronargs; + FmgrValues values; + func_ptr user_fn; + bool isNull = false; + + va_start(pvar, procedureId); + + fmgr_info(procedureId, &user_fn, &pronargs); + + if (pronargs > MAXFMGRARGS) { + elog(WARN, "fmgr: function %d: too many arguments (%d > %d)", + procedureId, pronargs, MAXFMGRARGS); + } + for (i = 0; i < pronargs; ++i) + values.data[i] = va_arg(pvar, char *); + va_end(pvar); + + /* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */ + return(fmgr_c(user_fn, procedureId, pronargs, &values, + &isNull)); +} + +/* + * This is just a version of fmgr() in which the hacker can prepend a C + * function pointer. This routine is not normally called; generally, + * if you have all of this information you're likely to just jump through + * the pointer, but it's available for use with macros in fmgr.h if you + * want this routine to do sanity-checking for you. + * + * func_ptr, func_id, n_arguments, args... + */ +char * +fmgr_ptr(func_ptr user_fn, Oid func_id, ...) +{ + va_list pvar; + register i; + int n_arguments; + FmgrValues values; + bool isNull = false; + + va_start(pvar, func_id); + n_arguments = va_arg(pvar, int); + if (n_arguments > MAXFMGRARGS) { + elog(WARN, "fmgr_ptr: function %d: too many arguments (%d > %d)", + func_id, n_arguments, MAXFMGRARGS); + } + for (i = 0; i < n_arguments; ++i) + values.data[i] = va_arg(pvar, char *); + va_end(pvar); + + /* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */ + return(fmgr_c(user_fn, func_id, n_arguments, &values, + &isNull)); +} + +/* + * This routine is not well thought out. When I get around to adding a + * function pointer field to FuncIndexInfo, it will be replace by calls + * to fmgr_c(). + */ +char * +fmgr_array_args(Oid procedureId, int nargs, char *args[], bool *isNull) +{ + func_ptr user_fn; + int true_arguments; + + fmgr_info(procedureId, &user_fn, &true_arguments); + + /* XXX see WAY_COOL_ORTHOGONAL_FUNCTIONS */ + return + (fmgr_c(user_fn, + procedureId, + true_arguments, + (FmgrValues*)args, + isNull)); +} diff --git a/src/backend/utils/fmgrtab.h b/src/backend/utils/fmgrtab.h new file mode 100644 index 00000000000..e700f2c82cc --- /dev/null +++ b/src/backend/utils/fmgrtab.h @@ -0,0 +1,29 @@ +/*------------------------------------------------------------------------- + * + * fmgrtab.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: fmgrtab.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef FMGRTAB_H +#define FMGRTAB_H + +#include "postgres.h" /* for ObjectId */ +#include "fmgr.h" /* genearated by Gen_fmgrtab.sh */ + +typedef struct { + Oid proid; + uint16 nargs; + func_ptr func; + char* funcName; +} FmgrCall; + +extern FmgrCall *fmgr_isbuiltin(Oid id); +extern func_ptr fmgr_lookupByName(char* name); + +#endif /* FMGRTAB_H */ diff --git a/src/backend/utils/geo-decls.h b/src/backend/utils/geo-decls.h new file mode 100644 index 00000000000..914f0d995de --- /dev/null +++ b/src/backend/utils/geo-decls.h @@ -0,0 +1,248 @@ +/*------------------------------------------------------------------------- + * + * geo-decls.h-- + * Declarations for various 2D constructs. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: geo-decls.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + * NOTE + * These routines do *not* use the float types from adt/. + * + * XXX These routines were not written by a numerical analyst. + * + *------------------------------------------------------------------------- + */ +#ifndef GEO_DECLS_H +#define GEO_DECLS_H + +/*#ifndef FmgrIncluded -- seems like always included. (it's FMgrIncluded) AY */ + +/*-------------------------------------------------------------------- + * Useful floating point utilities and constants. + *-------------------------------------------------------------------*/ + +#include <math.h> +#include "c.h" + +#define EPSILON 1.0E-06 + +#define FPzero(A) (fabs(A) <= EPSILON) +#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON) +#define FPlt(A,B) ((B) - (A) > EPSILON) +#define FPle(A,B) ((A) - (B) <= EPSILON) +#define FPgt(A,B) ((A) - (B) > EPSILON) +#define FPge(A,B) ((B) - (A) <= EPSILON) + +#define HYPOT(A, B) sqrt((A) * (A) + (B) * (B)) + +/*-------------------------------------------------------------------- + * Memory management. + *-------------------------------------------------------------------*/ + +#define PALLOC(SIZE) palloc(SIZE) +#define PFREE(P) pfree(P) +#define PALLOCTYPE(TYPE) (TYPE *) PALLOC(sizeof(TYPE)) + +/*#endif !FmgrIncluded */ + +/*--------------------------------------------------------------------- + * Point - (x,y) + *-------------------------------------------------------------------*/ +typedef struct { + double x, y; +} Point; + + +/*--------------------------------------------------------------------- + * LSEG - A straight line, specified by endpoints. + *-------------------------------------------------------------------*/ +typedef struct { + Point p[2]; + + double m; /* precomputed to save time, not in tuple */ +} LSEG; + + +/*--------------------------------------------------------------------- + * PATH - Specified by vertex points. + *-------------------------------------------------------------------*/ +typedef struct { + int32 length; /* XXX varlena */ + int32 npts; + int32 closed; /* is this a closed polygon? */ + int32 dummy; /* padding to make it double align */ + Point p[1]; /* variable length array of POINTs */ +} PATH; + + +/*--------------------------------------------------------------------- + * LINE - Specified by its general equation (Ax+By+C=0). + * If there is a y-intercept, it is C, which + * incidentally gives a freebie point on the line + * (if B=0, then C is the x-intercept). + * Slope m is precalculated to save time; if + * the line is not vertical, m == A. + *-------------------------------------------------------------------*/ +typedef struct { + double A, B, C; + double m; +} LINE; + + +/*--------------------------------------------------------------------- + * BOX - Specified by two corner points, which are + * sorted to save calculation time later. + *-------------------------------------------------------------------*/ +typedef struct { + double xh, yh, xl, yl; /* high and low coords */ +} BOX; + +/*--------------------------------------------------------------------- + * POLYGON - Specified by an array of doubles defining the points, + * keeping the number of points and the bounding box for + * speed purposes. + *-------------------------------------------------------------------*/ +typedef struct { + int32 size; /* XXX varlena */ + int32 npts; + BOX boundbox; + char pts[1]; +} POLYGON; + + +/* + * in geo-ops.h + */ +extern BOX *box_in(char *str); +extern char *box_out(BOX *box); +extern BOX *box_construct(double x1, double x2, double y1, double y2); +extern BOX *box_fill(BOX *result, double x1, double x2, double y1, double y2); +extern BOX *box_copy(BOX *box); +extern long box_same(BOX *box1, BOX *box2); +extern long box_overlap(BOX *box1, BOX *box2); +extern long box_overleft(BOX *box1, BOX *box2); +extern long box_left(BOX *box1, BOX *box2); +extern long box_right(BOX *box1, BOX *box2); +extern long box_overright(BOX *box1, BOX *box2); +extern long box_contained(BOX *box1, BOX *box2); +extern long box_contain(BOX *box1, BOX *box2); +extern long box_below(BOX *box1, BOX *box2); +extern long box_above(BOX *box1, BOX *box2); +extern long box_lt(BOX *box1, BOX *box2); +extern long box_gt(BOX *box1, BOX *box2); +extern long box_eq(BOX *box1, BOX *box2); +extern long box_le(BOX *box1, BOX *box2); +extern long box_ge(BOX *box1, BOX *box2); +extern double *box_area(BOX *box); +extern double *box_length(BOX *box); +extern double *box_height(BOX *box); +extern double *box_distance(BOX *box1, BOX *box2); +extern Point *box_center(BOX *box); +extern double box_ar(BOX *box); +extern double box_ln(BOX *box); +extern double box_ht(BOX *box); +extern double box_dt(BOX *box1, BOX *box2); +extern BOX *box_intersect(BOX *box1, BOX *box2); +extern LSEG *box_diagonal(BOX *box); +extern LINE *line_construct_pm(Point *pt, double m); +extern LINE *line_construct_pp(Point *pt1, Point *pt2); +extern long line_intersect(LINE *l1, LINE *l2); +extern long line_parallel(LINE *l1, LINE *l2); +extern long line_perp(LINE *l1, LINE *l2); +extern long line_vertical(LINE *line); +extern long line_horizontal(LINE *line); +extern long line_eq(LINE *l1, LINE *l2); +extern double *line_distance(LINE *l1, LINE *l2); +extern Point *line_interpt(LINE *l1, LINE *l2); +extern PATH *path_in(char *str); +extern char *path_out(PATH *path); +extern long path_n_lt(PATH *p1, PATH *p2); +extern long path_n_gt(PATH *p1, PATH *p2); +extern long path_n_eq(PATH *p1, PATH *p2); +extern long path_n_le(PATH *p1, PATH *p2); +extern long path_n_ge(PATH *p1, PATH *p2); +extern long path_inter(PATH *p1, PATH *p2); +extern double *path_distance(PATH *p1, PATH *p2); +extern double *path_length(PATH *path); +extern double path_ln(PATH *path); +extern Point *point_in(char *str); +extern char *point_out(Point *pt); +extern Point *point_construct(double x, double y); +extern Point *point_copy(Point *pt); +extern long point_left(Point *pt1, Point *pt2); +extern long point_right(Point *pt1, Point *pt2); +extern long point_above(Point *pt1, Point *pt2); +extern long point_below(Point *pt1, Point *pt2); +extern long point_vert(Point *pt1, Point *pt2); +extern long point_horiz(Point *pt1, Point *pt2); +extern long point_eq(Point *pt1, Point *pt2); +extern long pointdist(Point *p1, Point *p2); +extern double *point_distance(Point *pt1, Point *pt2); +extern double point_dt(Point *pt1, Point *pt2); +extern double *point_slope(Point *pt1, Point *pt2); +extern double point_sl(Point *pt1, Point *pt2); +extern LSEG *lseg_in(char *str); +extern char *lseg_out(LSEG *ls); +extern LSEG *lseg_construct(Point *pt1, Point *pt2); +extern void statlseg_construct(LSEG *lseg, Point *pt1, Point *pt2); +extern long lseg_intersect(LSEG *l1, LSEG *l2); +extern long lseg_parallel(LSEG *l1, LSEG *l2); +extern long lseg_perp(LSEG *l1, LSEG *l2); +extern long lseg_vertical(LSEG *lseg); +extern long lseg_horizontal(LSEG *lseg); +extern long lseg_eq(LSEG *l1, LSEG *l2); +extern double *lseg_distance(LSEG *l1, LSEG *l2); +extern double lseg_dt(LSEG *l1, LSEG *l2); +extern Point *lseg_interpt(LSEG *l1, LSEG *l2); +extern double *dist_pl(Point *pt, LINE *line); +extern double *dist_ps(Point *pt, LSEG *lseg); +extern double *dist_ppth(Point *pt, PATH *path); +extern double *dist_pb(Point *pt, BOX *box); +extern double *dist_sl(LSEG *lseg, LINE *line); +extern double *dist_sb(LSEG *lseg, BOX *box); +extern double *dist_lb(LINE *line, BOX *box); +extern Point *interpt_sl(LSEG *lseg, LINE *line); +extern Point *close_pl(Point *pt, LINE *line); +extern Point *close_ps(Point *pt, LSEG *lseg); +extern Point *close_pb(Point *pt, BOX *box); +extern Point *close_sl(LSEG *lseg, LINE *line); +extern Point *close_sb(LSEG *lseg, BOX *box); +extern Point *close_lb(LINE *line, BOX *box); +extern long on_pl(Point *pt, LINE *line); +extern long on_ps(Point *pt, LSEG *lseg); +extern long on_pb(Point *pt, BOX *box); +extern long on_ppath(Point *pt, PATH *path); +extern long on_sl(LSEG *lseg, LINE *line); +extern long on_sb(LSEG *lseg, BOX *box); +extern long inter_sl(LSEG *lseg, LINE *line); +extern long inter_sb(LSEG *lseg, BOX *box); +extern long inter_lb(LINE *line, BOX *box); +extern void make_bound_box(POLYGON *poly); +extern POLYGON *poly_in(char *s); +extern long poly_pt_count(char *s, char delim); +extern char *poly_out(POLYGON *poly); +extern double poly_max(double *coords, int ncoords); +extern double poly_min(double *coords, int ncoords); +extern long poly_left(POLYGON *polya, POLYGON *polyb); +extern long poly_overleft(POLYGON *polya, POLYGON *polyb); +extern long poly_right(POLYGON *polya, POLYGON *polyb); +extern long poly_overright(POLYGON *polya, POLYGON *polyb); +extern long poly_same(POLYGON *polya, POLYGON *polyb); +extern long poly_overlap(POLYGON *polya, POLYGON *polyb); +extern long poly_contain(POLYGON *polya, POLYGON *polyb); +extern long poly_contained(POLYGON *polya, POLYGON *polyb); + +/* geo-selfuncs.c */ +#if 0 /* FIX ME! */ +extern float64 areasel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 areajoinsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 leftsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 leftjoinsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 contsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +extern float64 contjoinsel(Oid opid, Oid relid, AttrNumber attno, char *value, int32 flag); +#endif + +#endif /* GEO_DECLS_H */ diff --git a/src/backend/utils/hash/Makefile.inc b/src/backend/utils/hash/Makefile.inc new file mode 100644 index 00000000000..3f9710488e5 --- /dev/null +++ b/src/backend/utils/hash/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/hash +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/hash/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= dynahash.c hashfn.c diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c new file mode 100644 index 00000000000..66477f55675 --- /dev/null +++ b/src/backend/utils/hash/dynahash.c @@ -0,0 +1,868 @@ +/*------------------------------------------------------------------------- + * + * dynahash.c-- + * dynamic hashing + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * + * Dynamic hashing, after CACM April 1988 pp 446-457, by Per-Ake Larson. + * Coded into C, with minor code improvements, and with hsearch(3) interface, + * by ejp@ausmelb.oz, Jul 26, 1988: 13:16; + * also, hcreate/hdestroy routines added to simulate hsearch(3). + * + * These routines simulate hsearch(3) and family, with the important + * difference that the hash table is dynamic - can grow indefinitely + * beyond its original size (as supplied to hcreate()). + * + * Performance appears to be comparable to that of hsearch(3). + * The 'source-code' options referred to in hsearch(3)'s 'man' page + * are not implemented; otherwise functionality is identical. + * + * Compilation controls: + * DEBUG controls some informative traces, mainly for debugging. + * HASH_STATISTICS causes HashAccesses and HashCollisions to be maintained; + * when combined with HASH_DEBUG, these are displayed by hdestroy(). + * + * Problems & fixes to ejp@ausmelb.oz. WARNING: relies on pre-processor + * concatenation property, in probably unnecessary code 'optimisation'. + * + * Modified margo@postgres.berkeley.edu February 1990 + * added multiple table interface + * Modified by sullivan@postgres.berkeley.edu April 1990 + * changed ctl structure for shared memory + */ +# include <stdio.h> +# include <sys/types.h> +# include <string.h> +# include "postgres.h" +# include "utils/hsearch.h" +#ifndef FRONTEND +# include "nodes/memnodes.h" +# include "utils/mcxt.h" +#endif /* !FRONTEND */ +# include "utils/palloc.h" +# include "utils/elog.h" + +/* + * Fast arithmetic, relying on powers of 2, + * and on pre-processor concatenation property + */ + +# define MOD(x,y) ((x) & ((y)-1)) + +/* + * external routines + */ + +/* + * Private function prototypes + */ +static long *DynaHashAlloc(unsigned int size); +static void DynaHashFree(Pointer ptr); +static int hash_clear(HTAB *hashp); +static uint32 call_hash(HTAB *hashp, char *k, int len); +static SEG_OFFSET seg_alloc(HTAB *hashp); +static int bucket_alloc(HTAB *hashp); +static int dir_realloc(HTAB *hashp); + +typedef long * ((*dhalloc_ptr)()); + +#ifndef FRONTEND +/* ---------------- + * memory allocation routines + * + * for postgres: all hash elements have to be in + * the global cache context. Otherwise the postgres + * garbage collector is going to corrupt them. -wei + * + * ??? the "cache" memory context is intended to store only + * system cache information. The user of the hashing + * routines should specify which context to use or we + * should create a separate memory context for these + * hash routines. For now I have modified this code to + * do the latter -cim 1/19/91 + * ---------------- + */ +GlobalMemory DynaHashCxt = (GlobalMemory) NULL; + +static long * +DynaHashAlloc(unsigned int size) +{ + if (! DynaHashCxt) + DynaHashCxt = CreateGlobalMemory("DynaHash"); + + return (long *) + MemoryContextAlloc((MemoryContext)DynaHashCxt, size); +} + +static void +DynaHashFree(Pointer ptr) +{ + MemoryContextFree((MemoryContext)DynaHashCxt, ptr); +} + +#define MEM_ALLOC DynaHashAlloc +#define MEM_FREE DynaHashFree + +#else /* FRONTEND */ + +#define MEM_ALLOC palloc +#define MEM_FREE pfree + +#endif /* FRONTEND */ + +/* ---------------- + * Internal routines + * ---------------- + */ + +static int expand_table(); +static int hdefault(); +static int init_htab(); + + +/* + * pointer access macros. Shared memory implementation cannot + * store pointers in the hash table data structures because + * pointer values will be different in different address spaces. + * these macros convert offsets to pointers and pointers to offsets. + * Shared memory need not be contiguous, but all addresses must be + * calculated relative to some offset (segbase). + */ + +#define GET_SEG(hp,seg_num)\ + (SEGMENT) (((unsigned long) (hp)->segbase) + (hp)->dir[seg_num]) + +#define GET_BUCKET(hp,bucket_offs)\ + (ELEMENT *) (((unsigned long) (hp)->segbase) + bucket_offs) + +#define MAKE_HASHOFFSET(hp,ptr)\ + ( ((unsigned long) ptr) - ((unsigned long) (hp)->segbase) ) + +# if HASH_STATISTICS +static long hash_accesses, hash_collisions, hash_expansions; +# endif + +/************************** CREATE ROUTINES **********************/ + +HTAB * +hash_create(int nelem, HASHCTL *info, int flags) +{ + register HHDR * hctl; + HTAB * hashp; + + + hashp = (HTAB *) MEM_ALLOC((unsigned long) sizeof(HTAB)); + memset(hashp, 0, sizeof(HTAB)); + + if ( flags & HASH_FUNCTION ) { + hashp->hash = info->hash; + } else { + /* default */ + hashp->hash = string_hash; + } + + if ( flags & HASH_SHARED_MEM ) { + /* ctl structure is preallocated for shared memory tables */ + + hashp->hctl = (HHDR *) info->hctl; + hashp->segbase = (char *) info->segbase; + hashp->alloc = info->alloc; + hashp->dir = (SEG_OFFSET *)info->dir; + + /* hash table already exists, we're just attaching to it */ + if (flags & HASH_ATTACH) { + return(hashp); + } + + } else { + /* setup hash table defaults */ + + hashp->alloc = (dhalloc_ptr) MEM_ALLOC; + hashp->dir = NULL; + hashp->segbase = NULL; + + } + + if (! hashp->hctl) { + hashp->hctl = (HHDR *) hashp->alloc((unsigned long)sizeof(HHDR)); + if (! hashp->hctl) { + return(0); + } + } + + if ( !hdefault(hashp) ) return(0); + hctl = hashp->hctl; +#ifdef HASH_STATISTICS + hctl->accesses = hctl->collisions = 0; +#endif + + if ( flags & HASH_BUCKET ) { + hctl->bsize = info->bsize; + hctl->bshift = my_log2(info->bsize); + } + if ( flags & HASH_SEGMENT ) { + hctl->ssize = info->ssize; + hctl->sshift = my_log2(info->ssize); + } + if ( flags & HASH_FFACTOR ) { + hctl->ffactor = info->ffactor; + } + + /* + * SHM hash tables have fixed maximum size (allocate + * a maximal sized directory). + */ + if ( flags & HASH_DIRSIZE ) { + hctl->max_dsize = my_log2(info->max_size); + hctl->dsize = my_log2(info->dsize); + } + /* hash table now allocates space for key and data + * but you have to say how much space to allocate + */ + if ( flags & HASH_ELEM ) { + hctl->keysize = info->keysize; + hctl->datasize = info->datasize; + } + if ( flags & HASH_ALLOC ) { + hashp->alloc = info->alloc; + } + + if ( init_htab (hashp, nelem ) ) { + hash_destroy(hashp); + return(0); + } + return(hashp); +} + +/* + Allocate and initialize an HTAB structure + */ +static int +hdefault(HTAB *hashp) +{ + HHDR *hctl; + + memset(hashp->hctl, 0, sizeof(HHDR)); + + hctl = hashp->hctl; + hctl->bsize = DEF_BUCKET_SIZE; + hctl->bshift = DEF_BUCKET_SHIFT; + hctl->ssize = DEF_SEGSIZE; + hctl->sshift = DEF_SEGSIZE_SHIFT; + hctl->dsize = DEF_DIRSIZE; + hctl->ffactor = DEF_FFACTOR; + hctl->nkeys = 0; + hctl->nsegs = 0; + + /* I added these MS. */ + + /* default memory allocation for hash buckets */ + hctl->keysize = sizeof(char *); + hctl->datasize = sizeof(char *); + + /* table has no fixed maximum size */ + hctl->max_dsize = NO_MAX_DSIZE; + + /* garbage collection for HASH_REMOVE */ + hctl->freeBucketIndex = INVALID_INDEX; + + return(1); +} + + +static int +init_htab (HTAB *hashp, int nelem) +{ + register SEG_OFFSET *segp; + register int nbuckets; + register int nsegs; + int l2; + HHDR *hctl; + + hctl = hashp->hctl; + /* + * Divide number of elements by the fill factor and determine a desired + * number of buckets. Allocate space for the next greater power of + * two number of buckets + */ + nelem = (nelem - 1) / hctl->ffactor + 1; + + l2 = my_log2(nelem); + nbuckets = 1 << l2; + + hctl->max_bucket = hctl->low_mask = nbuckets - 1; + hctl->high_mask = (nbuckets << 1) - 1; + + nsegs = (nbuckets - 1) / hctl->ssize + 1; + nsegs = 1 << my_log2(nsegs); + + if ( nsegs > hctl->dsize ) { + hctl->dsize = nsegs; + } + + /* Use two low order bits of points ???? */ + /* + if ( !(hctl->mem = bit_alloc ( nbuckets )) ) return(-1); + if ( !(hctl->mod = bit_alloc ( nbuckets )) ) return(-1); + */ + + /* allocate a directory */ + if (!(hashp->dir)) { + hashp->dir = + (SEG_OFFSET *)hashp->alloc(hctl->dsize * sizeof(SEG_OFFSET)); + if (! hashp->dir) + return(-1); + } + + /* Allocate initial segments */ + for (segp = hashp->dir; hctl->nsegs < nsegs; hctl->nsegs++, segp++ ) { + *segp = seg_alloc(hashp); + if ( *segp == (SEG_OFFSET)0 ) { + hash_destroy(hashp); + return (0); + } + } + +# if HASH_DEBUG + fprintf(stderr, "%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n", + "init_htab:", + "TABLE POINTER ", hashp, + "BUCKET SIZE ", hctl->bsize, + "BUCKET SHIFT ", hctl->bshift, + "DIRECTORY SIZE ", hctl->dsize, + "SEGMENT SIZE ", hctl->ssize, + "SEGMENT SHIFT ", hctl->sshift, + "FILL FACTOR ", hctl->ffactor, + "MAX BUCKET ", hctl->max_bucket, + "HIGH MASK ", hctl->high_mask, + "LOW MASK ", hctl->low_mask, + "NSEGS ", hctl->nsegs, + "NKEYS ", hctl->nkeys ); +# endif + return (0); +} + +/********************** DESTROY ROUTINES ************************/ + +static int +hash_clear(HTAB *hashp) +{ + elog(NOTICE,"hash_clear not implemented\n"); + return 0; +} + + +void +hash_destroy (HTAB *hashp) +{ + /* cannot destroy a shared memory hash table */ + Assert(! hashp->segbase); + + if (hashp != NULL) { + register SEG_OFFSET segNum; + SEGMENT segp; + int nsegs = hashp->hctl->nsegs; + int j; + BUCKET_INDEX *elp,p,q; + ELEMENT *curr; + + for (segNum = 0; nsegs > 0; nsegs--, segNum++) { + + segp = GET_SEG(hashp,segNum); + for (j = 0, elp = segp; j < hashp->hctl->ssize; j++, elp++) { + for ( p = *elp; p != INVALID_INDEX; p = q ){ + curr = GET_BUCKET(hashp,p); + q = curr->next; + MEM_FREE((char *) curr); + } + } + free((char *)segp); + } + (void) MEM_FREE( (char *) hashp->dir); + (void) MEM_FREE( (char *) hashp->hctl); + hash_stats("destroy",hashp); + (void) MEM_FREE( (char *) hashp); + } +} + +void +hash_stats(char *where, HTAB *hashp) +{ +# if HASH_STATISTICS + + fprintf(stderr,"%s: this HTAB -- accesses %ld collisions %ld\n", + where,hashp->hctl->accesses,hashp->hctl->collisions); + + fprintf(stderr,"hash_stats: keys %ld keysize %ld maxp %d segmentcount %d\n", + hashp->hctl->nkeys, hashp->hctl->keysize, + hashp->hctl->max_bucket, hashp->hctl->nsegs); + fprintf(stderr,"%s: total accesses %ld total collisions %ld\n", + where, hash_accesses, hash_collisions); + fprintf(stderr,"hash_stats: total expansions %ld\n", + hash_expansions); + +# endif + +} + +/*******************************SEARCH ROUTINES *****************************/ + +static uint32 +call_hash(HTAB *hashp, char *k, int len) +{ + long hash_val, bucket; + HHDR *hctl; + + hctl = hashp->hctl; + hash_val = hashp->hash(k, len); + + bucket = hash_val & hctl->high_mask; + if ( bucket > hctl->max_bucket ) { + bucket = bucket & hctl->low_mask; + } + + return(bucket); +} + +/* + * hash_search -- look up key in table and perform action + * + * action is one of HASH_FIND/HASH_ENTER/HASH_REMOVE + * + * RETURNS: NULL if table is corrupted, a pointer to the element + * found/removed/entered if applicable, TRUE otherwise. + * foundPtr is TRUE if we found an element in the table + * (FALSE if we entered one). + */ +long * +hash_search(HTAB *hashp, + char *keyPtr, + HASHACTION action, /* + * HASH_FIND / HASH_ENTER / HASH_REMOVE + * HASH_FIND_SAVE / HASH_REMOVE_SAVED + */ + bool *foundPtr) +{ + uint32 bucket; + long segment_num; + long segment_ndx; + SEGMENT segp; + register ELEMENT *curr; + HHDR *hctl; + BUCKET_INDEX currIndex; + BUCKET_INDEX *prevIndexPtr; + char * destAddr; + static struct State { + ELEMENT *currElem; + BUCKET_INDEX currIndex; + BUCKET_INDEX *prevIndex; + } saveState; + + Assert((hashp && keyPtr)); + Assert((action == HASH_FIND) || (action == HASH_REMOVE) || (action == HASH_ENTER) || (action == HASH_FIND_SAVE) || (action == HASH_REMOVE_SAVED)); + + hctl = hashp->hctl; + +# if HASH_STATISTICS + hash_accesses++; + hashp->hctl->accesses++; +# endif + if (action == HASH_REMOVE_SAVED) + { + curr = saveState.currElem; + currIndex = saveState.currIndex; + prevIndexPtr = saveState.prevIndex; + /* + * Try to catch subsequent errors + */ + Assert(saveState.currElem && !(saveState.currElem = 0)); + } + else + { + bucket = call_hash(hashp, keyPtr, hctl->keysize); + segment_num = bucket >> hctl->sshift; + segment_ndx = bucket & ( hctl->ssize - 1 ); + + segp = GET_SEG(hashp,segment_num); + + Assert(segp); + + prevIndexPtr = &segp[segment_ndx]; + currIndex = *prevIndexPtr; + /* + * Follow collision chain + */ + for (curr = NULL;currIndex != INVALID_INDEX;) { + /* coerce bucket index into a pointer */ + curr = GET_BUCKET(hashp,currIndex); + + if (! memcmp((char *)&(curr->key), keyPtr, hctl->keysize)) { + break; + } + prevIndexPtr = &(curr->next); + currIndex = *prevIndexPtr; +# if HASH_STATISTICS + hash_collisions++; + hashp->hctl->collisions++; +# endif + } + } + + /* + * if we found an entry or if we weren't trying + * to insert, we're done now. + */ + *foundPtr = (bool) (currIndex != INVALID_INDEX); + switch (action) { + case HASH_ENTER: + if (currIndex != INVALID_INDEX) + return(&(curr->key)); + break; + case HASH_REMOVE: + case HASH_REMOVE_SAVED: + if (currIndex != INVALID_INDEX) { + Assert(hctl->nkeys > 0); + hctl->nkeys--; + + /* add the bucket to the freelist for this table. */ + *prevIndexPtr = curr->next; + curr->next = hctl->freeBucketIndex; + hctl->freeBucketIndex = currIndex; + + /* better hope the caller is synchronizing access to + * this element, because someone else is going to reuse + * it the next time something is added to the table + */ + return (&(curr->key)); + } + return((long *) TRUE); + case HASH_FIND: + if (currIndex != INVALID_INDEX) + return(&(curr->key)); + return((long *)TRUE); + case HASH_FIND_SAVE: + if (currIndex != INVALID_INDEX) + { + saveState.currElem = curr; + saveState.prevIndex = prevIndexPtr; + saveState.currIndex = currIndex; + return(&(curr->key)); + } + return((long *)TRUE); + default: + /* can't get here */ + return (NULL); + } + + /* + If we got here, then we didn't find the element and + we have to insert it into the hash table + */ + Assert(currIndex == INVALID_INDEX); + + /* get the next free bucket */ + currIndex = hctl->freeBucketIndex; + if (currIndex == INVALID_INDEX) { + + /* no free elements. allocate another chunk of buckets */ + if (! bucket_alloc(hashp)) { + return(NULL); + } + currIndex = hctl->freeBucketIndex; + } + Assert(currIndex != INVALID_INDEX); + + curr = GET_BUCKET(hashp,currIndex); + hctl->freeBucketIndex = curr->next; + + /* link into chain */ + *prevIndexPtr = currIndex; + + /* copy key and data */ + destAddr = (char *) &(curr->key); + memmove(destAddr,keyPtr,hctl->keysize); + curr->next = INVALID_INDEX; + + /* let the caller initialize the data field after + * hash_search returns. + */ + /* memmove(destAddr,keyPtr,hctl->keysize+hctl->datasize);*/ + + /* + * Check if it is time to split the segment + */ + if (++hctl->nkeys / (hctl->max_bucket+1) > hctl->ffactor) { + /* + fprintf(stderr,"expanding on '%s'\n",keyPtr); + hash_stats("expanded table",hashp); + */ + if (! expand_table(hashp)) + return(NULL); + } + return (&(curr->key)); +} + +/* + * hash_seq -- sequentially search through hash table and return + * all the elements one by one, return NULL on error and + * return TRUE in the end. + * + */ +long * +hash_seq(HTAB *hashp) +{ + static uint32 curBucket = 0; + static BUCKET_INDEX curIndex; + ELEMENT *curElem; + long segment_num; + long segment_ndx; + SEGMENT segp; + HHDR *hctl; + + if (hashp == NULL) + { + /* + * reset static state + */ + curBucket = 0; + curIndex = INVALID_INDEX; + return((long *) NULL); + } + + hctl = hashp->hctl; + while (curBucket <= hctl->max_bucket) { + if (curIndex != INVALID_INDEX) { + curElem = GET_BUCKET(hashp, curIndex); + curIndex = curElem->next; + if (curIndex == INVALID_INDEX) /* end of this bucket */ + ++curBucket; + return(&(curElem->key)); + } + + /* + * initialize the search within this bucket. + */ + segment_num = curBucket >> hctl->sshift; + segment_ndx = curBucket & ( hctl->ssize - 1 ); + + /* + * first find the right segment in the table directory. + */ + segp = GET_SEG(hashp, segment_num); + if (segp == NULL) + /* this is probably an error */ + return((long *) NULL); + + /* + * now find the right index into the segment for the first + * item in this bucket's chain. if the bucket is not empty + * (its entry in the dir is valid), we know this must + * correspond to a valid element and not a freed element + * because it came out of the directory of valid stuff. if + * there are elements in the bucket chains that point to the + * freelist we're in big trouble. + */ + curIndex = segp[segment_ndx]; + + if (curIndex == INVALID_INDEX) /* empty bucket */ + ++curBucket; + } + + return((long *) TRUE); /* out of buckets */ +} + + +/********************************* UTILITIES ************************/ +static int +expand_table(HTAB *hashp) +{ + HHDR *hctl; + SEGMENT old_seg,new_seg; + long old_bucket, new_bucket; + long new_segnum, new_segndx; + long old_segnum, old_segndx; + ELEMENT *chain; + BUCKET_INDEX *old,*newbi; + register BUCKET_INDEX chainIndex,nextIndex; + +#ifdef HASH_STATISTICS + hash_expansions++; +#endif + + hctl = hashp->hctl; + new_bucket = ++hctl->max_bucket; + old_bucket = (hctl->max_bucket & hctl->low_mask); + + new_segnum = new_bucket >> hctl->sshift; + new_segndx = MOD ( new_bucket, hctl->ssize ); + + if ( new_segnum >= hctl->nsegs ) { + + /* Allocate new segment if necessary */ + if (new_segnum >= hctl->dsize) { + dir_realloc(hashp); + } + if (! (hashp->dir[new_segnum] = seg_alloc(hashp))) { + return (0); + } + hctl->nsegs++; + } + + + if ( new_bucket > hctl->high_mask ) { + /* Starting a new doubling */ + hctl->low_mask = hctl->high_mask; + hctl->high_mask = new_bucket | hctl->low_mask; + } + + /* + * Relocate records to the new bucket + */ + old_segnum = old_bucket >> hctl->sshift; + old_segndx = MOD(old_bucket, hctl->ssize); + + old_seg = GET_SEG(hashp,old_segnum); + new_seg = GET_SEG(hashp,new_segnum); + + old = &old_seg[old_segndx]; + newbi = &new_seg[new_segndx]; + for (chainIndex = *old; + chainIndex != INVALID_INDEX; + chainIndex = nextIndex){ + + chain = GET_BUCKET(hashp,chainIndex); + nextIndex = chain->next; + if ( call_hash(hashp, + (char *)&(chain->key), + hctl->keysize) == old_bucket ) { + *old = chainIndex; + old = &chain->next; + } else { + *newbi = chainIndex; + newbi = &chain->next; + } + chain->next = INVALID_INDEX; + } + return (1); +} + + +static int +dir_realloc(HTAB *hashp) +{ + register char *p; + char **p_ptr; + long old_dirsize; + long new_dirsize; + + + if (hashp->hctl->max_dsize != NO_MAX_DSIZE) + return (0); + + /* Reallocate directory */ + old_dirsize = hashp->hctl->dsize * sizeof ( SEGMENT * ); + new_dirsize = old_dirsize << 1; + + p_ptr = (char **) hashp->dir; + p = (char *) hashp->alloc((unsigned long) new_dirsize ); + if (p != NULL) { + memmove(p, *p_ptr, old_dirsize ); + memset ( *p_ptr + old_dirsize, 0, new_dirsize-old_dirsize ); + (void) free( (char *)*p_ptr); + *p_ptr = p; + hashp->hctl->dsize = new_dirsize; + return(1); + } + return (0); + +} + + +static SEG_OFFSET +seg_alloc(HTAB * hashp) +{ + SEGMENT segp; + SEG_OFFSET segOffset; + + + segp = (SEGMENT) hashp->alloc((unsigned long) + sizeof(SEGMENT)*hashp->hctl->ssize); + + if (! segp) { + return(0); + } + + memset((char *)segp, 0, + (long) sizeof(SEGMENT)*hashp->hctl->ssize); + + segOffset = MAKE_HASHOFFSET(hashp,segp); + return(segOffset); +} + +/* + * allocate some new buckets and link them into the free list + */ +static int +bucket_alloc(HTAB *hashp) +{ + int i; + ELEMENT *tmpBucket; + long bucketSize; + BUCKET_INDEX tmpIndex,lastIndex; + + bucketSize = + sizeof(BUCKET_INDEX) + hashp->hctl->keysize + hashp->hctl->datasize; + + /* make sure its aligned correctly */ + bucketSize += sizeof(long *) - (bucketSize % sizeof(long *)); + + /* tmpIndex is the shmem offset into the first bucket of + * the array. + */ + tmpBucket = (ELEMENT *) + hashp->alloc((unsigned long) BUCKET_ALLOC_INCR*bucketSize); + + if (! tmpBucket) { + return(0); + } + + tmpIndex = MAKE_HASHOFFSET(hashp,tmpBucket); + + /* set the freebucket list to point to the first bucket */ + lastIndex = hashp->hctl->freeBucketIndex; + hashp->hctl->freeBucketIndex = tmpIndex; + + /* initialize each bucket to point to the one behind it */ + for (i=0;i<(BUCKET_ALLOC_INCR-1);i++) { + tmpBucket = GET_BUCKET(hashp,tmpIndex); + tmpIndex += bucketSize; + tmpBucket->next = tmpIndex; + } + + /* the last bucket points to the old freelist head (which is + * probably invalid or we wouldnt be here) + */ + tmpBucket->next = lastIndex; + + return(1); +} + +/* calculate the log base 2 of num */ +int +my_log2(long num) +{ + int i = 1; + int limit; + + for ( i = 0, limit = 1; limit < num; limit = 2 * limit, i++ ); + return (i); +} diff --git a/src/backend/utils/hash/hashfn.c b/src/backend/utils/hash/hashfn.c new file mode 100644 index 00000000000..2a25b8a59e1 --- /dev/null +++ b/src/backend/utils/hash/hashfn.c @@ -0,0 +1,156 @@ +/*------------------------------------------------------------------------- + * + * hashfn.c-- + * + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/hash/hashfn.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "utils/hsearch.h" + +/* + * Assume that we've already split the bucket to which this + * key hashes, calculate that bucket, and check that in fact + * we did already split it. + */ +long +string_hash(char *key, int keysize) +{ + int h; + register unsigned char *k = (unsigned char *) key; + + h = 0; + /* + * Convert string to integer + */ + while (*k) + h = h * PRIME1 ^ (*k++ - ' '); + h %= PRIME2; + + return (h); +} + + +long +tag_hash(int *key, int keysize) +{ + register long h = 0; + + /* + * Convert tag to integer; Use four byte chunks in a "jump table" + * to go a little faster. Currently the maximum keysize is 16 + * (mar 17 1992) I have put in cases for up to 24. Bigger than + * this will resort to the old behavior of the for loop. (see the + * default case). + */ + switch (keysize) + { + case 6*sizeof(int): + h = h * PRIME1 ^ (*key); + key++; + /* fall through */ + + case 5*sizeof(int): + h = h * PRIME1 ^ (*key); + key++; + /* fall through */ + + case 4*sizeof(int): + h = h * PRIME1 ^ (*key); + key++; + /* fall through */ + + case 3*sizeof(int): + h = h * PRIME1 ^ (*key); + key++; + /* fall through */ + + case 2*sizeof(int): + h = h * PRIME1 ^ (*key); + key++; + /* fall through */ + + case sizeof(int): + h = h * PRIME1 ^ (*key); + key++; + break; + + default: + for(; keysize > (sizeof(int)-1); keysize -= sizeof(int), key++) + h = h * PRIME1 ^ (*key); + /* + * now let's grab the last few bytes of the tag if the tag + * has (size % 4) != 0 (which it sometimes will on a sun3). + */ + if (keysize) + { + char *keytmp = (char *)key; + + switch (keysize) + { + case 3: + h = h * PRIME1 ^ (*keytmp); + keytmp++; + /* fall through */ + case 2: + h = h * PRIME1 ^ (*keytmp); + keytmp++; + /* fall through */ + case 1: + h = h * PRIME1 ^ (*keytmp); + break; + } + } + break; + } + + h %= PRIME2; + return (h); +} + +/* + * This is INCREDIBLY ugly, but fast. + * We break the string up into 8 byte units. On the first time + * through the loop we get the "leftover bytes" (strlen % 8). + * On every other iteration, we perform 8 HASHC's so we handle + * all 8 bytes. Essentially, this saves us 7 cmp & branch + * instructions. If this routine is heavily used enough, it's + * worth the ugly coding + */ +long +disk_hash(char *key) +{ + register int n = 0; + register char *str = key; + register int len = strlen(key); + register int loop; + +#define HASHC n = *str++ + 65599 * n + + if (len > 0) { + loop = (len + 8 - 1) >> 3; + + switch(len & (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/hsearch.h b/src/backend/utils/hsearch.h new file mode 100644 index 00000000000..d12664e4683 --- /dev/null +++ b/src/backend/utils/hsearch.h @@ -0,0 +1,141 @@ +/*------------------------------------------------------------------------- + * + * hsearch.h-- + * for hashing in the new buffer manager + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: hsearch.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef HSEARCH_H +#define HSEARCH_H + +#include "postgres.h" + +/* + * Constants + */ +# define DEF_BUCKET_SIZE 256 +# define DEF_BUCKET_SHIFT 8 /* log2(BUCKET) */ +# define DEF_SEGSIZE 256 +# define DEF_SEGSIZE_SHIFT 8 /* log2(SEGSIZE) */ +# define DEF_DIRSIZE 256 +# define PRIME1 37 +# define PRIME2 1048583 +# define DEF_FFACTOR 1 +# define SPLTMAX 8 + + +/* + * Hash bucket is actually bigger than this. Key field can have + * variable length and a variable length data field follows it. + */ +typedef struct element { + unsigned long next; /* secret from user */ + long key; +} ELEMENT; + +typedef unsigned long BUCKET_INDEX; +/* segment is an array of bucket pointers */ +typedef BUCKET_INDEX *SEGMENT; +typedef unsigned long SEG_OFFSET; + +typedef struct hashhdr { + long bsize; /* Bucket/Page Size */ + long bshift; /* Bucket shift */ + long dsize; /* Directory Size */ + long ssize; /* Segment Size */ + long sshift; /* Segment shift */ + long max_bucket; /* ID of Maximum bucket in use */ + long high_mask; /* Mask to modulo into entire table */ + long low_mask; /* Mask to modulo into lower half of table */ + long ffactor; /* Fill factor */ + long nkeys; /* Number of keys in hash table */ + long nsegs; /* Number of allocated segments */ + long keysize; /* hash key length in bytes */ + long datasize; /* elem data length in bytes */ + long max_dsize; /* 'dsize' limit if directory is fixed size */ + BUCKET_INDEX freeBucketIndex; + /* index of first free bucket */ +#ifdef HASH_STATISTICS + long accesses; + long collisions; +#endif +} HHDR; + +typedef struct htab { + HHDR *hctl; /* shared control information */ + long (*hash)(); /* Hash Function */ + char *segbase; /* segment base address for + * calculating pointer values + */ + SEG_OFFSET *dir; /* 'directory' of segm starts */ + long *(*alloc)(); /* memory allocator + * (long * for alignment reasons) + */ + +} HTAB; + +typedef struct hashctl { + long bsize; /* Bucket Size */ + long ssize; /* Segment Size */ + long dsize; /* Dirsize Size */ + long ffactor; /* Fill factor */ + long (*hash)(); /* Hash Function */ + long keysize; /* hash key length in bytes */ + long datasize; /* elem data length in bytes */ + long max_size; /* limit to dsize if directory size is limited */ + long *segbase; /* base for calculating bucket + seg ptrs */ + long * (*alloc)(); /* memory allocation function */ + long *dir; /* directory if allocated already */ + long *hctl; /* location of header information in shd mem */ +} HASHCTL; + +/* Flags to indicate action for hctl */ +#define HASH_BUCKET 0x001 /* Setting bucket size */ +#define HASH_SEGMENT 0x002 /* Setting segment size */ +#define HASH_DIRSIZE 0x004 /* Setting directory size */ +#define HASH_FFACTOR 0x008 /* Setting fill factor */ +#define HASH_FUNCTION 0x010 /* Set user defined hash function */ +#define HASH_ELEM 0x020 /* Setting key/data size */ +#define HASH_SHARED_MEM 0x040 /* Setting shared mem const */ +#define HASH_ATTACH 0x080 /* Do not initialize hctl */ +#define HASH_ALLOC 0x100 /* Setting memory allocator */ + + +/* seg_alloc assumes that INVALID_INDEX is 0*/ +#define INVALID_INDEX (0) +#define NO_MAX_DSIZE (-1) +/* number of hash buckets allocated at once */ +#define BUCKET_ALLOC_INCR (30) + +/* hash_search operations */ +typedef enum { + HASH_FIND, + HASH_ENTER, + HASH_REMOVE, + HASH_FIND_SAVE, + HASH_REMOVE_SAVED +} HASHACTION; + +/* + * prototypes from functions in dynahash.c + */ +extern HTAB *hash_create(int nelem, HASHCTL *info, int flags); +extern void hash_destroy(HTAB *hashp); +extern void hash_stats(char *where, HTAB *hashp); +extern long *hash_search(HTAB *hashp, char *keyPtr, HASHACTION action, + bool *foundPtr); +extern long *hash_seq(HTAB *hashp); + +/* + * prototypes from functions in hashfn.c + */ +extern long string_hash(char *key, int keysize); +extern long tag_hash(int *key, int keysize); +extern long disk_hash(char *key); + +#endif /* HSEARCH_H */ diff --git a/src/backend/utils/init/Makefile.inc b/src/backend/utils/init/Makefile.inc new file mode 100644 index 00000000000..d773037f36d --- /dev/null +++ b/src/backend/utils/init/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/init +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= enbl.c findbe.c globals.c magic.c miscinit.c postinit.c diff --git a/src/backend/utils/init/enbl.c b/src/backend/utils/init/enbl.c new file mode 100644 index 00000000000..995ab9d9567 --- /dev/null +++ b/src/backend/utils/init/enbl.c @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * enbl.c-- + * POSTGRES module enable and disable support code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/enbl.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "c.h" +#include "utils/module.h" /* where the declarations go */ + +/* + * BypassEnable -- + * False iff enable/disable processing is required given on and "*countP." + * + * Note: + * As a side-effect, *countP is modified. It should be 0 initially. + * + * Exceptions: + * BadState if called with pointer to value 0 and false. + * BadArg if "countP" is invalid pointer. + * BadArg if on is invalid. + */ +bool +BypassEnable(int *enableCountInOutP, bool on) +{ + AssertArg(PointerIsValid(enableCountInOutP)); + AssertArg(BoolIsValid(on)); + + if (on) { + *enableCountInOutP += 1; + return ((bool)(*enableCountInOutP >= 2)); + } + + AssertState(*enableCountInOutP >= 1); + + *enableCountInOutP -= 1; + + return ((bool)(*enableCountInOutP >= 1)); +} diff --git a/src/backend/utils/init/findbe.c b/src/backend/utils/init/findbe.c new file mode 100644 index 00000000000..4a82105490b --- /dev/null +++ b/src/backend/utils/init/findbe.c @@ -0,0 +1,251 @@ +/*------------------------------------------------------------------------- + * + * findbe.c -- + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/findbe.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#ifndef WIN32 +#include <grp.h> +#else +#include <windows.h> +#endif /* WIN32 */ +#include <pwd.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "c.h" +#include "miscadmin.h" /* for DebugLvl */ + +#ifndef S_IRUSR /* XXX [TRH] should be in a header */ +# define S_IRUSR S_IREAD +# define S_IWUSR S_IWRITE +# define S_IXUSR S_IEXEC +# define S_IRGRP ((S_IRUSR)>>3) +# define S_IWGRP ((S_IWUSR)>>3) +# define S_IXGRP ((S_IXUSR)>>3) +# define S_IROTH ((S_IRUSR)>>6) +# define S_IWOTH ((S_IWUSR)>>6) +# define S_IXOTH ((S_IXUSR)>>6) +#endif + +/* + * ValidateBackend -- validate "path" as a POSTGRES executable file + * + * returns 0 if the file is found and no error is encountered. + * -1 if the regular file "path" does not exist or cannot be executed. + * -2 if the file is otherwise valid but cannot be read. + */ +int +ValidateBackend(char *path) +{ +#ifndef WIN32 + struct stat buf; + uid_t euid; + struct group *gp; + struct passwd *pwp; + int i; + int is_r = 0; + int is_x = 0; + int in_grp = 0; +#else + DWORD file_attributes; +#endif /* WIN32 */ + + /* + * Ensure that the file exists and is a regular file. + * + * XXX if you have a broken system where stat() looks at the symlink + * instead of the underlying file, you lose. + */ + if (strlen(path) >= MAXPGPATH) { + if (DebugLvl > 1) + fprintf(stderr, "ValidateBackend: pathname \"%s\" is too long\n", + path); + return(-1); + } + +#ifndef WIN32 + if (stat(path, &buf) < 0) { + if (DebugLvl > 1) + fprintf(stderr, "ValidateBackend: can't stat \"%s\"\n", + path); + return(-1); + } + if (!(buf.st_mode & S_IFREG)) { + if (DebugLvl > 1) + fprintf(stderr, "ValidateBackend: \"%s\" is not a regular file\n", + path); + return(-1); + } + + /* + * Ensure that we are using an authorized backend. + * + * XXX I'm open to suggestions here. I would like to enforce ownership + * of backends by user "postgres" but people seem to like to run + * as users other than "postgres"... + */ + + /* + * Ensure that the file is both executable and readable (required for + * dynamic loading). + * + * We use the effective uid here because the backend will not have + * executed setuid() by the time it calls this routine. + */ + euid = geteuid(); + if (euid == buf.st_uid) { + is_r = buf.st_mode & S_IRUSR; + is_x = buf.st_mode & S_IXUSR; + if (DebugLvl > 1 && !(is_r && is_x)) + fprintf(stderr, "ValidateBackend: \"%s\" is not user read/execute\n", + path); + return(is_x ? (is_r ? 0 : -2) : -1); + } + pwp = getpwuid(euid); + if (pwp) { + if (pwp->pw_gid == buf.st_gid) { + ++in_grp; + } else if (pwp->pw_name && + (gp = getgrgid(buf.st_gid))) { + for (i = 0; gp->gr_mem[i]; ++i) { + if (!strcmp(gp->gr_mem[i], pwp->pw_name)) { + ++in_grp; + break; + } + } + } + if (in_grp) { + is_r = buf.st_mode & S_IRGRP; + is_x = buf.st_mode & S_IXGRP; + if (DebugLvl > 1 && !(is_r && is_x)) + fprintf(stderr, "ValidateBackend: \"%s\" is not group read/execute\n", + path); + return(is_x ? (is_r ? 0 : -2) : -1); + } + } + is_r = buf.st_mode & S_IROTH; + is_x = buf.st_mode & S_IXOTH; + if (DebugLvl > 1 && !(is_r && is_x)) + fprintf(stderr, "ValidateBackend: \"%s\" is not other read/execute\n", + path); + return(is_x ? (is_r ? 0 : -2) : -1); +#else + file_attributes = GetFileAttributes(path); + if(file_attributes != 0xFFFFFFFF) + return(0); + else + return(-1); +#endif /* WIN32 */ +} + +/* + * FindBackend -- find an absolute path to a valid backend executable + * + * The reason we have to work so hard to find an absolute path is that + * we need to feed the backend server the location of its actual + * executable file -- otherwise, we can't do dynamic loading. + */ +int +FindBackend(char *backend, char *argv0) +{ + char buf[MAXPGPATH + 2]; + char *p; + char *path, *startp, *endp; + int pathlen; + +#ifdef WIN32 + strcpy(backend, argv0); + return(0); +#endif /* WIN32 */ + + /* + * for the postmaster: + * First try: use the backend that's located in the same directory + * as the postmaster, if it was invoked with an explicit path. + * Presumably the user used an explicit path because it wasn't in + * PATH, and we don't want to use incompatible executables. + * + * This has the neat property that it works for installed binaries, + * old source trees (obj/support/post{master,gres}) and new marc + * source trees (obj/post{master,gres}) because they all put the + * two binaries in the same place. + * + * for the backend server: + * First try: if we're given some kind of path, use it (making sure + * that a relative path is made absolute before returning it). + */ + if (argv0 && (p = strrchr(argv0, '/')) && *++p) { + if (*argv0 == '/' || !getcwd(buf, MAXPGPATH)) + buf[0] = '\0'; + else + (void) strcat(buf, "/"); + (void) strcat(buf, argv0); + p = strrchr(buf, '/'); + (void) strcpy(++p, "postgres"); + if (!ValidateBackend(buf)) { + (void) strncpy(backend, buf, MAXPGPATH); + if (DebugLvl) + fprintf(stderr, "FindBackend: found \"%s\" using argv[0]\n", + backend); + return(0); + } + fprintf(stderr, "FindBackend: invalid backend \"%s\"\n", + buf); + return(-1); + } + + /* + * Second try: since no explicit path was supplied, the user must + * have been relying on PATH. We'll use the same PATH. + */ + if ((p = getenv("PATH")) && *p) { + if (DebugLvl) + fprintf(stderr, "FindBackend: searching PATH ...\n"); + pathlen = strlen(p); + path = malloc(pathlen + 1); + (void) strcpy(path, p); + for (startp = path, endp = strchr(path, ':'); + startp && *startp; + startp = endp + 1, endp = strchr(startp, ':')) { + if (startp == endp) /* it's a "::" */ + continue; + if (endp) + *endp = '\0'; + if (*startp == '/' || !getcwd(buf, MAXPGPATH)) + buf[0] = '\0'; + (void) strcat(buf, startp); + (void) strcat(buf, "/postgres"); + switch (ValidateBackend(buf)) { + case 0: /* found ok */ + (void) strncpy(backend, buf, MAXPGPATH); + if (DebugLvl) + fprintf(stderr, "FindBackend: found \"%s\" using PATH\n", + backend); + free(path); + return(0); + case -1: /* wasn't even a candidate, keep looking */ + break; + case -2: /* found but disqualified */ + fprintf(stderr, "FindBackend: could not read backend \"%s\"\n", + buf); + free(path); + return(-1); + } + if (!endp) /* last one */ + break; + } + free(path); + } + + fprintf(stderr, "FindBackend: could not find a backend to execute...\n"); + return(-1); +} diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c new file mode 100644 index 00000000000..ad8a75a90a2 --- /dev/null +++ b/src/backend/utils/init/globals.c @@ -0,0 +1,108 @@ +/*------------------------------------------------------------------------- + * + * globals.c-- + * global variable declarations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.1.1.1 1996/07/09 06:22:08 scrappy Exp $ + * + * NOTES + * Globals used all over the place should be declared here and not + * in other modules. + * + *------------------------------------------------------------------------- + */ +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/file.h> +#include <sys/types.h> +#include <math.h> + +#include "postgres.h" +#include "miscadmin.h" /* where the declarations go */ + +#include "access/heapam.h" +#include "utils/tqual.h" +#include "storage/sinval.h" +#include "storage/sinvaladt.h" +#include "storage/lmgr.h" +#include "utils/elog.h" + +#include "catalog/catname.h" + +int Portfd = -1; +int Noversion = 0; +int Quiet = 1; + +int MasterPid; +char* DataDir; + +char OutputFileName[MAXPGPATH] = ""; + +BackendId MyBackendId; +BackendTag MyBackendTag; + +char *UserName = NULL; +char *DatabaseName = NULL; +char *DatabasePath = NULL; + +bool MyDatabaseIdIsInitialized = false; +Oid MyDatabaseId = InvalidOid; +bool TransactionInitWasProcessed = false; + +bool IsUnderPostmaster = false; +bool IsPostmaster = false; + +short DebugLvl = 0; + +char *IndexedCatalogNames[] = { + AttributeRelationName, + ProcedureRelationName, + TypeRelationName, + RelationRelationName, + 0 +}; + + +/* ---------------- + * we just do a linear search now so there's no requirement that the list + * be ordered. The list is so small it shouldn't make much difference. + * make sure the list is null-terminated + * - jolly 8/19/95 + * + * OLD COMMENT + * WARNING WARNING WARNING WARNING WARNING WARNING + * + * keep SharedSystemRelationNames[] in SORTED order! A binary search + * is done on it in catalog.c! + * + * XXX this is a serious hack which should be fixed -cim 1/26/90 + * ---------------- + */ +char *SharedSystemRelationNames[] = { + DatabaseRelationName, + DefaultsRelationName, + DemonRelationName, + GroupRelationName, + HostsRelationName, + LogRelationName, + MagicRelationName, + ServerRelationName, + TimeRelationName, + UserRelationName, + VariableRelationName, + 0 +}; + +/* set up global variables, pointers, etc. */ +void InitGlobals() +{ + MasterPid = getpid(); + DataDir = GetPGData(); +} + + diff --git a/src/backend/utils/init/magic.c b/src/backend/utils/init/magic.c new file mode 100644 index 00000000000..8e93ff51f5e --- /dev/null +++ b/src/backend/utils/init/magic.c @@ -0,0 +1,167 @@ +/*------------------------------------------------------------------------- + * + * magic.c-- + * magic number management routines + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/Attic/magic.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * NOTES + * XXX eventually, should be able to handle version identifiers + * of length != 4. + * + * STANDALONE CODE - do not use error routines as this code is linked with + * stuff that does not cinterface.a + *------------------------------------------------------------------------- + */ +#include <sys/file.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <string.h> +#include <stdio.h> + +#include "postgres.h" + +#include "utils/elog.h" +#include "miscadmin.h" /* for global decls */ + +#include "storage/fd.h" /* for O_ */ + +static char Pg_verfile[] = PG_VERFILE; + + +/* + * private function prototypes + */ +static void PathSetVersionFilePath(char path[], char filepathbuf[]); + +/* + * DatabaseMetaGunkIsConsistent + * + * Returns 1 iff all version numbers and ownerships are consistent. + * + * Note that we have to go through the whole rigmarole of generating the path + * and checking the existence of the database whether Noversion is set or not. + */ +int +DatabaseMetaGunkIsConsistent(char *database, char *path) +{ + int isValid; +#ifndef WIN32 + struct stat statbuf; +#else + struct _stat statbuf; +#endif + + /* XXX We haven't changed PG_VERSION since 1.1! */ +#ifndef WIN32 + isValid = ValidPgVersion(DataDir); + sprintf(path, "%s/base/%s", DataDir, database); + isValid = ValidPgVersion(path) || isValid; +#endif /* WIN32 */ + + if (stat(path, &statbuf) < 0) + elog(FATAL, "database %s does not exist, bailing out...", + database); + + return(isValid); +} + + +/* + * ValidPgVersion - verifies the consistency of the database + * + * Returns 1 iff the catalog version number (from the version number file + * in the directory specified in "path") is consistent with the backend + * version number. + */ +int +ValidPgVersion(char *path) +{ + int fd; + char version[4], buf[MAXPGPATH+1]; +#ifndef WIN32 + struct stat statbuf; +#else + struct _stat statbuf; +#endif + u_short my_euid = geteuid(); + + PathSetVersionFilePath(path, buf); + + if (stat(buf, &statbuf) >= 0) { + if (statbuf.st_uid != my_euid && my_euid != 0) + elog(FATAL, + "process userid (%d) != database owner (%d)", + my_euid, statbuf.st_uid); + } else + return(0); + + if ((fd = open(buf, O_RDONLY, 0)) < 0) { + if (!Noversion) + elog(DEBUG, "ValidPgVersion: %s: %m", buf); + return(0); + } + + if (read(fd, version, 4) < 4 || + !isascii(version[0]) || !isdigit(version[0]) || + version[1] != '.' || + !isascii(version[2]) || !isdigit(version[2]) || + version[3] != '\n') + elog(FATAL, "ValidPgVersion: %s: bad format", buf); + if (version[2] != '0' + PG_VERSION || + version[0] != '0' + PG_RELEASE) { + if (!Noversion) + elog(DEBUG, + "ValidPgVersion: should be %d.%d not %c.%c", + PG_RELEASE, PG_VERSION, version[0], version[2]); + close(fd); + return(0); + } + close(fd); + return(1); +} + + +/* + * SetPgVersion - writes the version to a database directory + */ +void +SetPgVersion(char *path) +{ + int fd; + char version[4], buf[MAXPGPATH+1]; + + PathSetVersionFilePath(path, buf); + + if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) + elog(FATAL, "SetPgVersion: %s: %m", buf); + + version[0] = '0' + PG_RELEASE; + version[1] = '.'; + version[2] = '0' + PG_VERSION; + version[3] = '\n'; + if (write(fd, version, 4) != 4) + elog(WARN, "SetPgVersion: %s: %m", buf); + + close(fd); +} + + +/* + * PathSetVersionFilePath + * + * Destructively change "filepathbuf" to contain the concatenation of "path" + * and the name of the version file name. + */ +static void +PathSetVersionFilePath(char *path, char *filepathbuf) +{ + if (strlen(path) > (MAXPGPATH - sizeof(Pg_verfile) - 1)) + elog(FATAL, "PathSetVersionFilePath: %s: path too long"); + (void) sprintf(filepathbuf, "%s%c%s", path, SEP_CHAR, Pg_verfile); +} diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c new file mode 100644 index 00000000000..6f19210048f --- /dev/null +++ b/src/backend/utils/init/miscinit.c @@ -0,0 +1,378 @@ +/*------------------------------------------------------------------------- + * + * miscinit.c-- + * miscellanious initialization support stuff + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/miscinit.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include <sys/param.h> /* for MAXPATHLEN */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <stdio.h> +#ifndef WIN32 +#include <grp.h> /* for getgrgid */ +#include <pwd.h> /* for getpwuid */ +#endif /* WIN32 */ + +#include "postgres.h" + +#include "utils/portal.h" /* for EnablePortalManager, etc. */ +#include "utils/exc.h" /* for EnableExceptionHandling, etc. */ +#include "utils/mcxt.h" /* for EnableMemoryContext, etc. */ +#include "utils/elog.h" +#include "utils/builtins.h" + +#include "miscadmin.h" /* where the declarations go */ + +#include "catalog/catname.h" +#include "catalog/pg_user.h" +#include "catalog/pg_proc.h" +#include "utils/syscache.h" + +#include "storage/fd.h" /* for O_ */ + +/* + * EnableAbortEnvVarName -- + * Enables system abort iff set to a non-empty string in environment. + */ +#define EnableAbortEnvVarName "POSTGRESABORT" + +extern char *getenv(const char *name); /* XXX STDLIB */ + +/* from globals.c */ +extern char *DatabaseName; +extern char *UserName; +extern char *DatabasePath; + + +/* + * Define USE_ENVIRONMENT to get PGDATA, etc. from environment variables. + * This is the default on UNIX platforms. + */ +#ifndef WIN32 +#define USE_ENVIRONMENT +#endif + +/* ---------------------------------------------------------------- + * some of the 19 ways to leave postgres + * ---------------------------------------------------------------- + */ + +/* + * ExitPostgres -- + * Exit POSTGRES with a status code. + * + * Note: + * This function never returns. + * ... + * + * Side effects: + * ... + * + * Exceptions: + * none + */ +void +ExitPostgres(ExitStatus status) +{ +#ifdef __SABER__ + saber_stop(); +#endif + exitpg(status); +} + +/* + * AbortPostgres -- + * Abort POSTGRES dumping core. + * + * Note: + * This function never returns. + * ... + * + * Side effects: + * Core is dumped iff EnableAbortEnvVarName is set to a non-empty string. + * ... + * + * Exceptions: + * none + */ +void +AbortPostgres() +{ + char *abortValue = getenv(EnableAbortEnvVarName); + +#ifdef __SABER__ + saber_stop(); +#endif + + if (PointerIsValid(abortValue) && abortValue[0] != '\0') + abort(); + else + exitpg(FatalExitStatus); +} + +/* ---------------- + * StatusBackendExit + * ---------------- + */ +void +StatusBackendExit(int status) +{ + /* someday, do some real cleanup and then call the LISP exit */ + /* someday, call StatusPostmasterExit if running without postmaster */ + exitpg(status); +} + +/* ---------------- + * StatusPostmasterExit + * ---------------- + */ +void +StatusPostmasterExit(int status) +{ + /* someday, do some real cleanup and then call the LISP exit */ + exitpg(status); +} + +/* ---------------------------------------------------------------- + * processing mode support stuff (used to be in pmod.c) + * ---------------------------------------------------------------- + */ +static ProcessingMode Mode = NoProcessing; + +/* + * IsNoProcessingMode -- + * True iff processing mode is NoProcessing. + */ +bool +IsNoProcessingMode() +{ + return ((bool)(Mode == NoProcessing)); +} + +/* + * IsBootstrapProcessingMode -- + * True iff processing mode is BootstrapProcessing. + */ +bool +IsBootstrapProcessingMode() +{ + return ((bool)(Mode == BootstrapProcessing)); +} + +/* + * IsInitProcessingMode -- + * True iff processing mode is InitProcessing. + */ +bool +IsInitProcessingMode() +{ + return ((bool)(Mode == InitProcessing)); +} + +/* + * IsNormalProcessingMode -- + * True iff processing mode is NormalProcessing. + */ +bool +IsNormalProcessingMode() +{ + return ((bool)(Mode == NormalProcessing)); +} + +/* + * SetProcessingMode -- + * Sets mode of processing as specified. + * + * Exceptions: + * BadArg if called with invalid mode. + * + * Note: + * Mode is NoProcessing before the first time this is called. + */ +void +SetProcessingMode(ProcessingMode mode) +{ + AssertArg(mode == NoProcessing || mode == BootstrapProcessing || + mode == InitProcessing || mode == NormalProcessing); + + Mode = mode; +} + +ProcessingMode +GetProcessingMode() +{ + return (Mode); +} + +/* ---------------------------------------------------------------- + * database path / name support stuff + * ---------------------------------------------------------------- + */ + +/* + * GetDatabasePath -- + * Returns path to database. + * + */ +char* +GetDatabasePath() +{ + return DatabasePath; +} + +/* + * GetDatabaseName -- + * Returns name of database. + */ +char* +GetDatabaseName() +{ + return DatabaseName; +} + +void +SetDatabasePath(char *path) +{ + /* use malloc since this is done before memory contexts are set up */ + if (DatabasePath) + free(DatabasePath); + DatabasePath = malloc(strlen(path)+1); + strcpy(DatabasePath, path); +} + +void +SetDatabaseName(char *name) +{ + if (DatabaseName) + free (DatabaseName); + DatabaseName = malloc(strlen(name)+1); + strcpy(DatabaseName, name); +} + +/* ---------------- + * GetPgUserName and SetPgUserName + * + * SetPgUserName must be called before InitPostgres, since the setuid() + * is done there. + * ---------------- + */ +char* +GetPgUserName() +{ + return UserName; +} + +void +SetPgUserName() +{ +#ifndef NO_SECURITY + char *p; + struct passwd *pw; + + if (IsUnderPostmaster) { + /* use the (possibly) authenticated name that's provided */ + if (!(p = getenv("PG_USER"))) + elog(FATAL, "SetPgUserName: PG_USER environment variable unset"); + } else { + /* setuid() has not yet been done, see above comment */ + if (!(pw = getpwuid(geteuid()))) + elog(FATAL, "SetPgUserName: no entry in passwd file"); + p = pw->pw_name; + } + if (UserName) + free(UserName); + UserName = malloc(strlen(p)+1); + strcpy(UserName, p); +#endif /* NO_SECURITY */ + +#ifdef WIN32 + /* XXX We'll figure out how to get the user name later */ + if (UserName) + free(UserName); + UserName = malloc(strlen(p)+1); + strcpy(UserName, "postgres"); +#endif /* WIN32 */ + +} + +/* ---------------------------------------------------------------- + * GetUserId and SetUserId + * ---------------------------------------------------------------- + */ +static Oid UserId = InvalidOid; + +Oid +GetUserId() +{ + Assert(OidIsValid(UserId)); + return(UserId); +} + +void +SetUserId() +{ + HeapTuple userTup; + char *userName; + + Assert(!OidIsValid(UserId)); /* only once */ + + /* + * Don't do scans if we're bootstrapping, none of the system + * catalogs exist yet, and they should be owned by postgres + * anyway. + */ + if (IsBootstrapProcessingMode()) { + UserId = geteuid(); + return; + } + + userName = GetPgUserName(); + userTup = SearchSysCacheTuple(USENAME, PointerGetDatum(userName), + 0,0,0); + if (!HeapTupleIsValid(userTup)) + elog(FATAL, "SetUserId: user \"%s\" is not in \"%s\"", + userName, + UserRelationName); + UserId = (Oid) ((Form_pg_user) GETSTRUCT(userTup))->usesysid; +} + +/* ---------------- + * GetPGHome + * + * Get POSTGRESHOME from environment, or return default. + * ---------------- + */ +char * +GetPGHome() +{ +#ifdef USE_ENVIRONMENT + char *h; + + if ((h = getenv("POSTGRESHOME")) != (char *) NULL) + return (h); +#endif /* USE_ENVIRONMENT */ + return (POSTGRESDIR); + +} + +char * +GetPGData() +{ +#ifdef USE_ENVIRONMENT + char *p; + + if ((p = getenv("PGDATA")) != (char *) NULL) { + return (p); + } +#endif /* USE_ENVIRONMENT */ + return (PGDATADIR); +} diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c new file mode 100644 index 00000000000..eab6c76cdcf --- /dev/null +++ b/src/backend/utils/init/postinit.c @@ -0,0 +1,648 @@ +/*------------------------------------------------------------------------- + * + * postinit.c-- + * postgres initialization utilities + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * NOTES + * InitPostgres() is the function called from PostgresMain + * which does all non-trival initialization, mainly by calling + * all the other initialization functions. InitPostgres() + * is only used within the "postgres" backend and so that routine + * is in tcop/postgres.c InitPostgres() is needed in cinterface.a + * because things like the bootstrap backend program need it. Hence + * you find that in this file... + * + * If you feel the need to add more initialization code, it should be + * done in InitPostgres() or someplace lower. Do not start + * putting stuff in PostgresMain - if you do then someone + * will have to clean it up later, and it's not going to be me! + * -cim 10/3/90 + * + *------------------------------------------------------------------------- + */ +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/file.h> +#include <sys/types.h> +#include <math.h> + +#include "postgres.h" + +#include "machine.h" /* for BLCKSZ, for InitMyDatabaseId() + * and where the decarations for this file go + */ +#include "access/heapam.h" +#include "access/xact.h" +#include "storage/bufmgr.h" +#include "access/transam.h" /* XXX dependency problem */ +#include "utils/tqual.h" +#include "utils/syscache.h" +#include "storage/bufpage.h" /* for page layout, for InitMyDatabaseId() */ +#include "storage/sinval.h" +#include "storage/sinvaladt.h" +#include "storage/lmgr.h" + +#include "miscadmin.h" /* for global decls */ +#include "utils/portal.h" /* for EnablePortalManager, etc. */ + +#include "utils/exc.h" /* for EnableExceptionHandling, etc. */ +#include "fmgr.h" /* for EnableDynamicFunctionManager, etc. */ +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" /* for EnableMemoryContext, etc. */ + +#include "catalog/catname.h" +#include "catalog/pg_database.h" + +#include "port-protos.h" +#include "libpq/libpq-be.h" + + +static IPCKey PostgresIpcKey; + + +#ifndef private +#ifndef EBUG +#define private static +#else /* !defined(EBUG) */ +#define private +#endif /* !defined(EBUG) */ +#endif /* !defined(private) */ + +/* ---------------------------------------------------------------- + * InitPostgres support + * ---------------------------------------------------------------- + */ + +/* -------------------------------- + * InitMyDatabaseId() -- Find and record the OID of the database we are + * to open. + * + * The database's oid forms half of the unique key for the system + * caches and lock tables. We therefore want it initialized before + * we open any relations, since opening relations puts things in the + * cache. To get around this problem, this code opens and scans the + * pg_database relation by hand. + * + * This algorithm relies on the fact that first attribute in the + * pg_database relation schema is the database name. It also knows + * about the internal format of tuples on disk and the length of + * the datname attribute. It knows the location of the pg_database + * file. + * + * This code is called from InitDatabase(), after we chdir() to the + * database directory but before we open any relations. + * -------------------------------- + */ +void +InitMyDatabaseId() +{ + int dbfd; + int fileflags; + int nbytes; + int max, i; + HeapTuple tup; + Page pg; + PageHeader ph; + char *dbfname; + Form_pg_database tup_db; + + /* + * At bootstrap time, we don't need to check the oid of the database + * in use, since we're not using shared memory. This is lucky, since + * the database may not be in the tables yet. + */ + + if (IsBootstrapProcessingMode()) { + LockDisable(true); + return; + } + + dbfname = (char *) palloc(strlen(DataDir) + strlen("pg_database") + 2); + sprintf(dbfname, "%s%cpg_database", DataDir, SEP_CHAR); + fileflags = O_RDONLY; +#ifdef WIN32 + fileflags |= _O_BINARY; +#endif /* WIN32 */ + + if ((dbfd = open(dbfname, O_RDONLY, 0666)) < 0) + elog(FATAL, "Cannot open %s", dbfname); + + pfree(dbfname); + + /* ---------------- + * read and examine every page in pg_database + * + * Raw I/O! Read those tuples the hard way! Yow! + * + * Why don't we use the access methods or move this code + * someplace else? This is really pg_database schema dependent + * code. Perhaps it should go in lib/catalog/pg_database? + * -cim 10/3/90 + * + * mao replies 4 apr 91: yeah, maybe this should be moved to + * lib/catalog. however, we CANNOT use the access methods since + * those use the buffer cache, which uses the relation cache, which + * requires that the dbid be set, which is what we're trying to do + * here. + * ---------------- + */ + pg = (Page) palloc(BLCKSZ); + ph = (PageHeader) pg; + + while ((nbytes = read(dbfd, pg, BLCKSZ)) == BLCKSZ) { + max = PageGetMaxOffsetNumber(pg); + + /* look at each tuple on the page */ + for (i = 0; i <= max; i++) { + int offset; + + /* if it's a freed tuple, ignore it */ + if (!(ph->pd_linp[i].lp_flags & LP_USED)) + continue; + + /* get a pointer to the tuple itself */ + offset = (int) ph->pd_linp[i].lp_off; + tup = (HeapTuple) (((char *) pg) + offset); + + /* + * if the tuple has been deleted (the database was destroyed), + * skip this tuple. XXX warning, will robinson: violation of + * transaction semantics happens right here. we should check + * to be sure that the xact that deleted this tuple actually + * committed. only way to do this at init time is to paw over + * the log relation by hand, too. let's be optimistic. + * + * XXX This is an evil type cast. tup->t_xmax is char[5] while + * TransactionId is struct * { char data[5] }. It works but + * if data is ever moved and no longer the first field this + * will be broken!! -mer 11 Nov 1991. + */ + if (TransactionIdIsValid((TransactionId)tup->t_xmax)) + continue; + + /* + * Okay, see if this is the one we want. + * XXX 1 july 91: mao and mer discover that tuples now squash + * t_bits. Why is this? + * + * 24 july 92: mer realizes that the t_bits field is only + * used in the event of null values. If no + * fields are null we reduce the header size + * by doing the squash. t_hoff tells you exactly + * how big the header actually is. use the PC + * means of getting at sys cat attrs. + */ + tup_db = (Form_pg_database)GETSTRUCT(tup); + + if (strncmp(GetDatabaseName(), + &(tup_db->datname.data[0]), + 16) == 0) + { + MyDatabaseId = tup->t_oid; + goto done; + } + } + } + + done: + (void) close(dbfd); + pfree(pg); + + if (!OidIsValid(MyDatabaseId)) + elog(FATAL, + "Database %s does not exist in %s", + GetDatabaseName(), + DatabaseRelationName); +} + +/* + * DoChdirAndInitDatabaseNameAndPath -- + * Sets current directory appropriately for given path and name. + * + * Arguments: + * Path and name are invalid if it invalid as a string. + * Path is "badly formated" if it is not a string containing a path + * to a writable directory. + * Name is "badly formated" if it contains more than 16 characters or if + * it is a bad file name (e.g., it contains a '/' or an 8-bit character). + * + * Side effects: + * Initially, DatabasePath and DatabaseName are invalid. They are + * set to valid strings before this function returns. + * + * Exceptions: + * BadState if called more than once. + * BadArg if both path and name are "badly formated" or invalid. + * BadArg if path and name are both "inconsistent" and valid. + */ +/* ---------------- + * DoChdirAndInitDatabaseNameAndPath + * + * this just chdir's to the proper data/base directory + * XXX clean this up more. + * + * XXX The following code is an incorrect of the semantics + * XXX described in the header file. Handling of defaults + * XXX should happen here, too. + * ---------------- + */ +void +DoChdirAndInitDatabaseNameAndPath(char *name, /* name of database */ + char *path) /* full path to database */ +{ + /* ---------------- + * check the path + * ---------------- + */ + if (path) + SetDatabasePath(path); + else + elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: path:%s is not valid", + path); + + /* ---------------- + * check the name + * ---------------- + */ + if (name) + SetDatabaseName(name); + else + elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: name:%s is not valid", + name); + + /* ---------------- + * change to the directory, or die trying. + * + * XXX unless the path hasn't been set because we're bootstrapping. + * HP-UX doesn't like chdir("") so check for that case before + * doing anything drastic. + * ---------------- + */ + if (*path && (chdir(path) < 0)) + elog(FATAL, "DoChdirAndInitDatabaseNameAndPath: chdir(\"%s\"): %m", + path); +} + +/* -------------------------------- + * InitUserid + * + * initializes crap associated with the user id. + * -------------------------------- + */ +void +InitUserid() +{ + setuid(geteuid()); + SetUserId(); +} + +/* -------------------------------- + * InitCommunication + * + * This routine initializes stuff needed for ipc, locking, etc. + * it should be called something more informative. + * + * Note: + * This does not set MyBackendId. MyBackendTag is set, however. + * -------------------------------- + */ +void +InitCommunication() +{ + char *getenv(); /* XXX style */ + char *postid; + char *postport; + IPCKey key; + + /* ---------------- + * try and get the backend tag from POSTID + * ---------------- + */ + MyBackendId = -1; + + postid = getenv("POSTID"); + if (!PointerIsValid(postid)) { + MyBackendTag = -1; + } else { + MyBackendTag = atoi(postid); + Assert(MyBackendTag >= 0); + } + + /* ---------------- + * try and get the ipc key from POSTPORT + * ---------------- + */ + postport = getenv("POSTPORT"); + + if (PointerIsValid(postport)) { + SystemPortAddress address = atoi(postport); + + if (address == 0) + elog(FATAL, "InitCommunication: invalid POSTPORT"); + + if (MyBackendTag == -1) + elog(FATAL, "InitCommunication: missing POSTID"); + + key = SystemPortAddressCreateIPCKey(address); + + /* + * Enable this if you are trying to force the backend to run as if it + * is running under the postmaster. + * + * This goto forces Postgres to attach to shared memory instead of + * using malloc'ed memory (which is the normal behavior if run + * directly). + * + * To enable emulation, run the following shell commands (in addition + * to enabling this goto) + * + * % setenv POSTID 1 + * % setenv POSTPORT 4321 + * % postmaster & + * % kill -9 %1 + * + * Upon doing this, Postmaster will have allocated the shared memory + * resources that Postgres will attach to if you enable + * EMULATE_UNDER_POSTMASTER. + * + * This comment may well age with time - it is current as of + * 8 January 1990 + * + * Greg + */ + +#ifdef EMULATE_UNDER_POSTMASTER + + goto forcesharedmemory; + +#endif + + } else if (IsUnderPostmaster) { + elog(FATAL, + "InitCommunication: under postmaster and POSTPORT not set"); + } else { + /* ---------------- + * assume we're running a postgres backend by itself with + * no front end or postmaster. + * ---------------- + */ + if (MyBackendTag == -1) { + MyBackendTag = 1; + } + + key = PrivateIPCKey; + } + + /* ---------------- + * initialize shared memory and semaphores appropriately. + * ---------------- + */ +#ifdef EMULATE_UNDER_POSTMASTER + + forcesharedmemory: + +#endif + + PostgresIpcKey = key; + AttachSharedMemoryAndSemaphores(key); +} + + +/* -------------------------------- + * InitStdio + * + * this routine consists of a bunch of code fragments + * that used to be randomly scattered through cinit(). + * they all seem to do stuff associated with io. + * -------------------------------- + */ +void +InitStdio() +{ + (void) DebugFileOpen(); +} + +/* -------------------------------- + * InitPostgres -- + * Initialize POSTGRES. + * + * Note: + * Be very careful with the order of calls in the InitPostgres function. + * -------------------------------- + */ +bool PostgresIsInitialized = false; +extern int NBuffers; + +/* + * this global is used by wei for testing his code, but must be declared + * here rather than in postgres.c so that it's defined for cinterface.a + * applications. + */ + +/*int testFlag = 0;*/ +int lockingOff = 0; + +/* + */ +void +InitPostgres(char *name) /* database name */ +{ + bool bootstrap; /* true if BootstrapProcessing */ + + /* ---------------- + * see if we're running in BootstrapProcessing mode + * ---------------- + */ + bootstrap = IsBootstrapProcessingMode(); + + /* ---------------- + * turn on the exception handler. Note: we cannot use elog, Assert, + * AssertState, etc. until after exception handling is on. + * ---------------- + */ + EnableExceptionHandling(true); + + /* ---------------- + * A stupid check to make sure we don't call this more than once. + * But things like ReinitPostgres() get around this by just diddling + * the PostgresIsInitialized flag. + * ---------------- + */ + AssertState(!PostgresIsInitialized); + + /* ---------------- + * Memory system initialization. + * (we may call palloc after EnableMemoryContext()) + * + * Note EnableMemoryContext() must happen before EnablePortalManager(). + * ---------------- + */ + EnableMemoryContext(true); /* initializes the "top context" */ + EnablePortalManager(true); /* memory for portal/transaction stuff */ + + /* ---------------- + * initialize the backend local portal stack used by + * internal PQ function calls. see src/lib/libpq/be-dumpdata.c + * This is different from the "portal manager" so this goes here. + * -cim 2/12/91 + * ---------------- + */ + be_portalinit(); + + /* ---------------- + * attach to shared memory and semaphores, and initialize our + * input/output/debugging file descriptors. + * ---------------- + */ + InitCommunication(); + InitStdio(); + + /* + * initialize the local buffer manager + */ + InitLocalBuffer(); + + if (!TransactionFlushEnabled()) + on_exitpg(FlushBufferPool, (caddr_t) NULL); + + /* ---------------- + * check for valid "meta gunk" (??? -cim 10/5/90) and change to + * database directory. + * + * Note: DatabaseName, MyDatabaseName, and DatabasePath are all + * initialized with DatabaseMetaGunkIsConsistent(), strncpy() and + * DoChdirAndInitDatabase() below! XXX clean this crap up! + * -cim 10/5/90 + * ---------------- + */ + { + char myPath[MAXPGPATH] = "."; /* DatabasePath points here! */ + + /* ---------------- + * DatabaseMetaGunkIsConsistent fills in myPath, but what about + * when bootstrap or Noversion is true?? -cim 10/5/90 + * ---------------- + */ + + if (! bootstrap && + ! DatabaseMetaGunkIsConsistent(name, myPath) && + ! Noversion) { + elog(NOTICE, "InitPostgres: could not locate valid PG_VERSION\n"); + elog(NOTICE, "files for %s and %s.", DataDir, name); + elog(FATAL, "Have you run initdb/createdb and set PGDATA properly?"); + } + + /* ---------------- + * ok, we've figured out myName and myPath, now save these + * and chdir to myPath. + * ---------------- + */ + DoChdirAndInitDatabaseNameAndPath(name, myPath); + } + + /* ******************************** + * code after this point assumes we are in the proper directory! + * ******************************** + */ + + /* ---------------- + * initialize the database id used for system caches and lock tables + * ---------------- + */ + InitMyDatabaseId(); + + smgrinit(); + + /* ---------------- + * initialize the transaction system and the relation descriptor + * cache. Note we have to make certain the lock manager is off while + * we do this. + * ---------------- + */ + AmiTransactionOverride(IsBootstrapProcessingMode()); + LockDisable(true); + + /* + * Part of the initialization processing done here sets a read + * lock on pg_log. Since locking is disabled the set doesn't have + * intended effect of locking out writers, but this is ok, since + * we only lock it to examine AMI transaction status, and this is + * never written after initdb is done. -mer 15 June 1992 + */ + RelationInitialize(); /* pre-allocated reldescs created here */ + InitializeTransactionSystem(); /* pg_log,etc init/crash recovery here */ + + LockDisable(false); + + /* ---------------- + * anyone knows what this does? something having to do with + * system catalog cache invalidation in the case of multiple + * backends, I think -cim 10/3/90 + * Sets up MyBackendId a unique backend identifier. + * ---------------- + */ + InitSharedInvalidationState(); + + /* ---------------- + * Set up a per backend process in shared memory. Must be done after + * InitSharedInvalidationState() as it relies on MyBackendId being + * initialized already. XXX -mer 11 Aug 1991 + * ---------------- + */ + InitProcess(PostgresIpcKey); + + if (MyBackendId > MaxBackendId || MyBackendId <= 0) { + elog(FATAL, "cinit2: bad backend id %d (%d)", + MyBackendTag, + MyBackendId); + } + + /* ---------------- + * initialize the access methods. + * ---------------- + */ + initam(); + + /* ---------------- + * initialize all the system catalog caches. + * ---------------- + */ + zerocaches(); + InitCatalogCache(); + + /* ---------------- + * set ourselves to the proper user id and figure out our postgres + * user id. If we ever add security so that we check for valid + * postgres users, we might do it here. + * ---------------- + */ + InitUserid(); + + /* ---------------- + * ok, all done, now let's make sure we don't do it again. + * ---------------- + */ + PostgresIsInitialized = true; +/* on_exitpg(DestroyLocalRelList, (caddr_t) NULL); */ + + /* ---------------- + * Done with "InitPostgres", now change to NormalProcessing unless + * we're in BootstrapProcessing mode. + * ---------------- + */ + if (!bootstrap) + SetProcessingMode(NormalProcessing); +/* if (testFlag || lockingOff) */ + if (lockingOff) + LockDisable(true); +} + + diff --git a/src/backend/utils/inval.h b/src/backend/utils/inval.h new file mode 100644 index 00000000000..72a664395ae --- /dev/null +++ b/src/backend/utils/inval.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * inval.h-- + * POSTGRES cache invalidation dispatcher definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: inval.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef INVAL_H +#define INVAL_H + +#include "postgres.h" +#include "access/htup.h" +#include "utils/rel.h" + +extern void DiscardInvalid(void); + +extern void RegisterInvalid(bool send); + +extern void SetRefreshWhenInvalidate(bool on); + +extern void RelationInvalidateHeapTuple(Relation relation, HeapTuple tuple); + +/* + * POSTGRES local cache invalidation definitions. (originates from linval.h) + */ +typedef struct InvalidationUserData { + struct InvalidationUserData *dataP[1]; /* VARIABLE LENGTH */ +} InvalidationUserData; /* VARIABLE LENGTH STRUCTURE */ + +typedef struct InvalidationEntryData { + InvalidationUserData *nextP; + InvalidationUserData userData; /* VARIABLE LENGTH ARRAY */ +} InvalidationEntryData; /* VARIABLE LENGTH STRUCTURE */ + +typedef Pointer InvalidationEntry; + +typedef InvalidationEntry LocalInvalid; + +#define EmptyLocalInvalid NULL + +extern InvalidationEntry InvalidationEntryAllocate(uint16 size); + +extern LocalInvalid LocalInvalidRegister(LocalInvalid invalid, + InvalidationEntry entry); + +extern void LocalInvalidInvalidate(LocalInvalid invalid, void (*function)()); + +extern void getmyrelids(void); + +#endif /* INVAL_H */ + diff --git a/src/backend/utils/lselect.h b/src/backend/utils/lselect.h new file mode 100644 index 00000000000..095da056e11 --- /dev/null +++ b/src/backend/utils/lselect.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * lselect.h-- + * definitions for the replacement selection algorithm. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lselect.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef LSELECT_H +#define LSELECT_H + +#include "c.h" +#include "access/htup.h" + +struct leftist { + short lt_dist; /* distance to leaf/empty node */ + short lt_devnum; /* device number of tuple */ + HeapTuple lt_tuple; + struct leftist *lt_left; + struct leftist *lt_right; +}; + +extern struct leftist *Tuples; + +extern struct leftist *lmerge(struct leftist *pt, struct leftist *qt); +extern HeapTuple gettuple(struct leftist **treep, short *devnum); +extern int puttuple(struct leftist **treep, HeapTuple newtuple, int devnum); +extern void dumptuples(FILE *file); +extern int tuplecmp(HeapTuple ltup, HeapTuple rtup); + +#ifdef EBUG +extern void checktree(struct leftist *tree); +extern int checktreer(struct leftist *tree, int level); +#endif /* EBUG */ + +#endif /* LSELECT_H */ diff --git a/src/backend/utils/lsyscache.h b/src/backend/utils/lsyscache.h new file mode 100644 index 00000000000..27f18570dac --- /dev/null +++ b/src/backend/utils/lsyscache.h @@ -0,0 +1,45 @@ +/*------------------------------------------------------------------------- + * + * lsyscache.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: lsyscache.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef LSYSCACHE_H +#define LSYSCACHE_H + +#include "access/htup.h" + +extern bool op_class(Oid opid, int32 opclass, Oid amopid); +extern char *get_attname(Oid relid, AttrNumber attnum); +extern AttrNumber get_attnum(Oid relid, char *attname); +extern Oid get_atttype(Oid relid, AttrNumber attnum); +extern bool get_attisset(Oid relid, char *attname); +extern RegProcedure get_opcode(Oid opid); +extern char *get_opname(Oid opid); +extern bool op_mergesortable(Oid opid, Oid ltype, Oid rtype, + Oid *leftOp, Oid *rightOp); +extern Oid op_hashjoinable(Oid opid, Oid ltype, Oid rtype); +extern Oid get_commutator(Oid opid); +extern HeapTuple get_operator_tuple(Oid opno); +extern Oid get_negator(Oid opid); +extern RegProcedure get_oprrest(Oid opid); +extern RegProcedure get_oprjoin(Oid opid); +extern int get_relnatts(Oid relid); +extern char *get_rel_name(Oid relid); +extern struct varlena * get_relstub(Oid relid, int no, bool *islast); +extern Oid get_ruleid(char *rulename); +extern Oid get_eventrelid(Oid ruleid); +extern int16 get_typlen(Oid typid); +extern char get_typalign(Oid typid); +extern bool get_typbyval(Oid typid); +extern struct varlena *get_typdefault(Oid typid); +extern char get_typtype(Oid typid); + +#endif /* LSYSCACHE_H */ + diff --git a/src/backend/utils/mcxt.h b/src/backend/utils/mcxt.h new file mode 100644 index 00000000000..d10019166ef --- /dev/null +++ b/src/backend/utils/mcxt.h @@ -0,0 +1,56 @@ +/*------------------------------------------------------------------------- + * + * mcxt.h-- + * POSTGRES memory context definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: mcxt.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef MCXT_H +#define MCXT_H + +#include "c.h" + +#include "nodes/memnodes.h" +#include "nodes/nodes.h" + +extern MemoryContext CurrentMemoryContext; +extern MemoryContext TopMemoryContext; + + +/* + * MaxAllocSize -- + * Arbitrary limit on size of allocations. + * + * Note: + * There is no guarantee that allocations smaller than MaxAllocSize + * will succeed. Allocation requests larger than MaxAllocSize will + * be summarily denied. + * + * This value should not be referenced except in one place in the code. + * + * XXX This should be defined in a file of tunable constants. + */ +#define MaxAllocSize (0xfffffff) /* 16G - 1 */ + +/* + * prototypes for functions in mcxt.c + */ +extern void EnableMemoryContext(bool on); +extern Pointer MemoryContextAlloc(MemoryContext context, Size size); +extern Pointer MemoryContextRealloc(MemoryContext context, + Pointer pointer, + Size size); +extern void MemoryContextFree(MemoryContext context, Pointer pointer); +extern char *MemoryContextGetName(MemoryContext context); +extern Size PointerGetAllocSize(Pointer pointer); +extern MemoryContext MemoryContextSwitchTo(MemoryContext context); +extern GlobalMemory CreateGlobalMemory(char *name); +extern void GlobalMemoryDestroy(GlobalMemory context); + + +#endif /* MCXT_H */ diff --git a/src/backend/utils/memutils.h b/src/backend/utils/memutils.h new file mode 100644 index 00000000000..9f94cfafabd --- /dev/null +++ b/src/backend/utils/memutils.h @@ -0,0 +1,281 @@ +/*------------------------------------------------------------------------- + * + * memutils.h-- + * this file contains general memory alignment, allocation + * and manipulation stuff that used to be spread out + * between the following files: + * + * align.h alignment macros + * aset.h memory allocation set stuff + * oset.h (used by aset.h) + * (bit.h bit array type / extern) + * clib.h mem routines + * limit.h max bits/byte, etc. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: memutils.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + * NOTES + * some of the information in this file will be moved to + * other files, (like MaxHeapTupleSize and MaxAttributeSize). + * + *------------------------------------------------------------------------- + */ +#ifndef MEMUTILS_H +#define MEMUTILS_H + +#include "c.h" + +#if 0 +/***************************************************************************** + * align.h - alignment macros * + **************************************************************************** + [TRH] Let the compiler decide what alignment it uses instead of +tending + we know better. + GCC (at least v2.5.8 and up) has an __alignof__ keyword. + However, we cannot use it here since on some architectures it reports + just a _recommended_ alignment instead of the actual alignment used in + padding structures (or at least, this is how I understand gcc's +s...) + So define a macro that gives us the _actual_ alignment inside a struct. + {{note: assumes that alignment size is always a power of 2.}} + */ +#define _ALIGNSIZE(TYPE) offsetof(struct { char __c; TYPE __t;}, __t) +#define _ALIGN(TYPE, LEN) \ + (((long)(LEN) + (_ALIGNSIZE(TYPE) - 1)) & ~(_ALIGNSIZE(TYPE) - 1)) +#define SHORTALIGN(LEN) _ALIGN(short, (LEN)) +#define INTALIGN(LEN) _ALIGN(int, (LEN)) +#define LONGALIGN(LEN) _ALIGN(long, (LEN)) +#define DOUBLEALIGN(LEN) _ALIGN(double, (LEN)) +#define MAXALIGN(LEN) _ALIGN(double, (LEN)) + +#endif /* 0 */ + +/* + * SHORTALIGN(LEN) - length (or address) aligned for shorts + */ +#define SHORTALIGN(LEN)\ + (((long)(LEN) + (sizeof (short) - 1)) & ~(sizeof (short) - 1)) + +#define INTALIGN(LEN)\ + (((long)(LEN) + (sizeof (int) - 1)) & ~(sizeof (int) -1)) + +/* + * LONGALIGN(LEN) - length (or address) aligned for longs + */ +#if defined(sun) && ! defined(sparc) +#define LONGALIGN(LEN) SHORTALIGN(LEN) +#elif defined (PORTNAME_alpha) +#define LONGALIGN(LEN)\ + (((long)(LEN) + (sizeof (int) - 1)) & ~(sizeof (int) -1)) +#else +#define LONGALIGN(LEN)\ + (((long)(LEN) + (sizeof (long) - 1)) & ~(sizeof (long) -1)) +#endif + +#define DOUBLEALIGN(LEN)\ + (((long)(LEN) + (sizeof (double) - 1)) & ~(sizeof (double) -1)) + +#define MAXALIGN(LEN)\ + (((long)(LEN) + (sizeof (double) - 1)) & ~(sizeof (double) -1)) + +/***************************************************************************** + * bit.h * + *****************************************************************************/ +#include "utils/bit.h" + +/***************************************************************************** + * oset.h -- Fixed format ordered set definitions. * + *****************************************************************************/ +/* Note: + * Fixed format ordered sets are <EXPLAIN>. + * XXX This is a preliminary version. Work is needed to explain + * XXX semantics of the external definitions. Otherwise, the + * XXX functional interface should not change. + * + * Identification: + * $Header: /cvsroot/pgsql/src/backend/utils/Attic/memutils.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + */ + +typedef struct OrderedElemData OrderedElemData; +typedef OrderedElemData* OrderedElem; + +typedef struct OrderedSetData OrderedSetData; +typedef OrderedSetData* OrderedSet; + +struct OrderedElemData { + OrderedElem next; /* Next elem or &this->set->dummy */ + OrderedElem prev; /* Previous elem or &this->set->head */ + OrderedSet set; /* Parent set */ +}; + +struct OrderedSetData { + OrderedElem head; /* First elem or &this->dummy */ + OrderedElem dummy; /* (hack) Terminator == NULL */ + OrderedElem tail; /* Last elem or &this->head */ + Offset offset; /* Offset from struct base to elem */ + /* this could be signed short int! */ +}; + +extern void OrderedSetInit(OrderedSet set, Offset offset); +extern bool OrderedSetContains(OrderedSet set, OrderedElem elem); +extern Pointer OrderedSetGetHead(OrderedSet set); +extern Pointer OrderedSetGetTail(OrderedSet set); +extern Pointer OrderedElemGetPredecessor(OrderedElem elem); +extern Pointer OrderedElemGetSuccessor(OrderedElem elem); +extern void OrderedElemPop(OrderedElem elem); +extern void OrderedElemPushInto(OrderedElem elem, OrderedSet Set); + +/***************************************************************************** + * aset.h -- Allocation set definitions. * + *****************************************************************************/ +/* + * Description: + * An allocation set is a set containing allocated elements. When + * an allocation is requested for a set, memory is allocated and a + * pointer is returned. Subsequently, this memory may be freed or + * reallocated. In addition, an allocation set may be reset which + * will cause all allocated memory to be freed. + * + * Allocations may occur in four different modes. The mode of + * allocation does not affect the behavior of allocations except in + * terms of performance. The allocation mode is set at the time of + * set initialization. Once the mode is chosen, it cannot be changed + * unless the set is reinitialized. + * + * "Dynamic" mode forces all allocations to occur in a heap. This + * is a good mode to use when small memory segments are allocated + * and freed very frequently. This is a good choice when allocation + * characteristics are unknown. This is the default mode. + * + * "Static" mode attemts to allocate space as efficiently as possible + * without regard to freeing memory. This mode should be chosen only + * when it is known that many allocations will occur but that very + * little of the allocated memory will be explicitly freed. + * + * "Tunable" mode is a hybrid of dynamic and static modes. The + * tunable mode will use static mode allocation except when the + * allocation request exceeds a size limit supplied at the time of set + * initialization. "Big" objects are allocated using dynamic mode. + * + * "Bounded" mode attempts to allocate space efficiently given a limit + * on space consumed by the allocation set. This restriction can be + * considered a "soft" restriction, because memory segments will + * continue to be returned after the limit is exceeded. The limit is + * specified at the time of set initialization like for tunable mode. + * + * Note: + * Allocation sets are not automatically reset on a system reset. + * Higher level code is responsible for cleaning up. + * + * There may other modes in the future. + */ + +/* + * AllocPointer -- + * Aligned pointer which may be a member of an allocation set. + */ +typedef Pointer AllocPointer; + +/* + * AllocMode -- + * Mode of allocation for an allocation set. + * + * Note: + * See above for a description of the various nodes. + */ +typedef enum AllocMode { + DynamicAllocMode, /* always dynamically allocate */ + StaticAllocMode, /* always "statically" allocate */ + TunableAllocMode, /* allocations are "tuned" */ + BoundedAllocMode /* allocations bounded to fixed usage */ +} AllocMode; + +#define DefaultAllocMode DynamicAllocMode + +/* + * AllocSet -- + * Allocation set. + */ +typedef struct AllocSetData { + OrderedSetData setData; + /* Note: this will change in the future to support other modes */ +} AllocSetData; + +typedef AllocSetData *AllocSet; + +/* + * AllocPointerIsValid -- + * True iff pointer is valid allocation pointer. + */ +#define AllocPointerIsValid(pointer) PointerIsValid(pointer) + +/* + * AllocSetIsValid -- + * True iff set is valid allocation set. + */ +#define AllocSetIsValid(set) PointerIsValid(set) + +extern void AllocSetInit(AllocSet set, AllocMode mode, Size limit); + +extern void AllocSetReset(AllocSet set); + +extern bool AllocSetContains(AllocSet set, AllocPointer pointer); +extern AllocPointer AllocSetAlloc(AllocSet set, Size size); +extern void AllocSetFree(AllocSet set, AllocPointer pointer); +extern AllocPointer AllocSetRealloc(AllocSet set, AllocPointer pointer, + Size size); + +extern int AllocSetIterate(AllocSet set, + void (*function)(AllocPointer pointer)); + +extern int AllocSetCount(AllocSet set); + +extern void AllocPointerDump(AllocPointer pointer); +extern void AllocSetDump(AllocSet set); + +/***************************************************************************** + * clib.h -- Standard C library definitions * + *****************************************************************************/ +/* + * Note: + * This file is OPERATING SYSTEM dependent!!! + * + */ +/* #include <memory.h> */ +/* use <string.h> because it's ANSI */ +#include <string.h> + +/* + * LibCCopyLength is only used within this file. -cim 6/12/90 + * + */ +typedef int LibCCopyLength; + +typedef CLibCopyLength; + +/* + * MemoryCopy -- + * Copies fixed length block of memory to another. + */ +#define MemoryCopy(toBuffer, fromBuffer, length)\ + memcpy(toBuffer, fromBuffer, length) + +/***************************************************************************** + * limit.h -- POSTGRES limit definitions. * + *****************************************************************************/ + +#define MaxBitsPerByte 8 + +typedef uint32 AttributeSize; /* XXX should be defined elsewhere */ + +#define MaxHeapTupleSize 0x7fffffff +#define MaxAttributeSize 0x7fffffff + +#define MaxIndexAttributeNumber 7 + + +#endif /* MEMUTILS_H */ diff --git a/src/backend/utils/mmgr/Makefile.inc b/src/backend/utils/mmgr/Makefile.inc new file mode 100644 index 00000000000..6a4a67a81d1 --- /dev/null +++ b/src/backend/utils/mmgr/Makefile.inc @@ -0,0 +1,15 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/mmgr +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/mmgr/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= aset.c mcxt.c palloc.c portalmem.c oset.c + diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c new file mode 100644 index 00000000000..bbb7cd66f20 --- /dev/null +++ b/src/backend/utils/mmgr/aset.c @@ -0,0 +1,381 @@ +/*------------------------------------------------------------------------- + * + * aset.c-- + * Allocation set definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/aset.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * NOTE + * XXX This is a preliminary implementation which lacks fail-fast + * XXX validity checking of arguments. + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include "c.h" +#include "utils/excid.h" /* for ExhaustedMemory */ +#include "utils/memutils.h" /* where funnction declarations go */ +#include "utils/elog.h" +#include "utils/palloc.h" + +#undef AllocSetReset +#undef malloc +#undef free + +/* + * Internal type definitions + */ + +/* + * AllocElem -- + * Allocation element. + */ +typedef struct AllocElemData { + OrderedElemData elemData; /* elem in AllocSet */ + Size size; +} AllocElemData; + +typedef AllocElemData *AllocElem; + + +/* + * Private method definitions + */ + +/* + * AllocPointerGetAllocElem -- + * Returns allocation (internal) elem given (external) pointer. + */ +#define AllocPointerGetAllocElem(pointer) (&((AllocElem)(pointer))[-1]) + +/* + * AllocElemGetAllocPointer -- + * Returns allocation (external) pointer given (internal) elem. + */ +#define AllocElemGetAllocPointer(alloc) ((AllocPointer)&(alloc)[1]) + +/* + * AllocElemIsValid -- + * True iff alloc is valid. + */ +#define AllocElemIsValid(alloc) PointerIsValid(alloc) + +/* non-export function prototypes */ +static AllocPointer AllocSetGetFirst(AllocSet set); +static AllocPointer AllocPointerGetNext(AllocPointer pointer); + +/* + * Public routines + */ + +/* + * AllocPointerIsValid(pointer) + * AllocSetIsValid(set) + * + * .. are now macros in aset.h -cim 4/27/91 + */ + +/* + * AllocSetInit -- + * Initializes given allocation set. + * + * Note: + * The semantics of the mode are explained above. Limit is ignored + * for dynamic and static modes. + * + * Exceptions: + * BadArg if set is invalid pointer. + * BadArg if mode is invalid. + */ +void +AllocSetInit(AllocSet set, AllocMode mode, Size limit) +{ + AssertArg(PointerIsValid(set)); + AssertArg((int)DynamicAllocMode <= (int)mode); + AssertArg((int)mode <= (int)BoundedAllocMode); + + /* + * XXX mode is currently ignored and treated as DynamicAllocMode. + * XXX limit is also ignored. This affects this whole file. + */ + + OrderedSetInit(&set->setData, offsetof(AllocElemData, elemData)); +} + +/* + * AllocSetReset -- + * Frees memory which is allocated in the given set. + * + * Exceptions: + * BadArg if set is invalid. + */ +void +AllocSetReset(AllocSet set) +{ + AllocPointer pointer; + + AssertArg(AllocSetIsValid(set)); + + while (AllocPointerIsValid(pointer = AllocSetGetFirst(set))) { + AllocSetFree(set, pointer); + } +} + +void +AllocSetReset_debug(char *file, int line, AllocSet set) +{ + AllocPointer pointer; + + AssertArg(AllocSetIsValid(set)); + + while (AllocPointerIsValid(pointer = AllocSetGetFirst(set))) { + AllocSetFree(set, pointer); + } +} + +/* + * AllocSetContains -- + * True iff allocation set contains given allocation element. + * + * Exceptions: + * BadArg if set is invalid. + * BadArg if pointer is invalid. + */ +bool +AllocSetContains(AllocSet set, AllocPointer pointer) +{ + AssertArg(AllocSetIsValid(set)); + AssertArg(AllocPointerIsValid(pointer)); + + return (OrderedSetContains(&set->setData, + &AllocPointerGetAllocElem(pointer)->elemData)); +} + +/* + * AllocSetAlloc -- + * Returns pointer to allocated memory of given size; memory is added + * to the set. + * + * Exceptions: + * BadArg if set is invalid. + * MemoryExhausted if allocation fails. + */ +AllocPointer +AllocSetAlloc(AllocSet set, Size size) +{ + AllocElem alloc; + + AssertArg(AllocSetIsValid(set)); + + /* allocate */ + alloc = (AllocElem)malloc(sizeof (*alloc) + size); + + if (!PointerIsValid(alloc)) { + elog (FATAL, "palloc failure: memory exhausted"); + } + + /* add to allocation list */ + OrderedElemPushInto(&alloc->elemData, &set->setData); + + /* set size */ + alloc->size = size; + + return (AllocElemGetAllocPointer(alloc)); +} + +/* + * AllocSetFree -- + * Frees allocated memory; memory is removed from the set. + * + * Exceptions: + * BadArg if set is invalid. + * BadArg if pointer is invalid. + * BadArg if pointer is not member of set. + */ +void +AllocSetFree(AllocSet set, AllocPointer pointer) +{ + AllocElem alloc; + + /* AssertArg(AllocSetIsValid(set)); */ + /* AssertArg(AllocPointerIsValid(pointer)); */ + AssertArg(AllocSetContains(set, pointer)); + + alloc = AllocPointerGetAllocElem(pointer); + + /* remove from allocation set */ + OrderedElemPop(&alloc->elemData); + + /* free storage */ + free(alloc); +} + +/* + * AllocSetRealloc -- + * Returns new pointer to allocated memory of given size; this memory + * is added to the set. Memory associated with given pointer is copied + * into the new memory, and the old memory is freed. + * + * Exceptions: + * BadArg if set is invalid. + * BadArg if pointer is invalid. + * BadArg if pointer is not member of set. + * MemoryExhausted if allocation fails. + */ +AllocPointer +AllocSetRealloc(AllocSet set, AllocPointer pointer, Size size) +{ + AllocPointer newPointer; + AllocElem alloc; + + /* AssertArg(AllocSetIsValid(set)); */ + /* AssertArg(AllocPointerIsValid(pointer)); */ + AssertArg(AllocSetContains(set, pointer)); + + /* + * Calling realloc(3) directly is not be possible (unless we use + * our own hacked version of malloc) since we must keep the + * allocations in the allocation set. + */ + + alloc = AllocPointerGetAllocElem(pointer); + + /* allocate new pointer */ + newPointer = AllocSetAlloc(set, size); + + /* fill new memory */ + memmove(newPointer, pointer, (alloc->size < size) ? alloc->size : size); + + /* free old pointer */ + AllocSetFree(set, pointer); + + return (newPointer); +} + +/* + * AllocSetIterate -- + * Returns size of set. Iterates through set elements calling function + * (if valid) on each. + * + * Note: + * This was written as an aid to debugging. It is intended for + * debugging use only. + * + * Exceptions: + * BadArg if set is invalid. + */ +int +AllocSetIterate(AllocSet set, + void (*function)(AllocPointer pointer)) +{ + int count = 0; + AllocPointer pointer; + + AssertArg(AllocSetIsValid(set)); + + for (pointer = AllocSetGetFirst(set); + AllocPointerIsValid(pointer); + pointer = AllocPointerGetNext(pointer)) { + + if (PointerIsValid(function)) { + (*function)(pointer); + } + count += 1; + } + + return (count); +} + +int +AllocSetCount(AllocSet set) +{ + int count = 0; + AllocPointer pointer; + + AssertArg(AllocSetIsValid(set)); + + for (pointer = AllocSetGetFirst(set); + AllocPointerIsValid(pointer); + pointer = AllocPointerGetNext(pointer)) { + count++; + } + return count; +} + +/* + * Private routines + */ + +/* + * AllocSetGetFirst -- + * Returns "first" allocation pointer in a set. + * + * Note: + * Assumes set is valid. + */ +static AllocPointer +AllocSetGetFirst(AllocSet set) +{ + AllocElem alloc; + + alloc = (AllocElem)OrderedSetGetHead(&set->setData); + + if (!AllocElemIsValid(alloc)) { + return (NULL); + } + + return (AllocElemGetAllocPointer(alloc)); +} + +/* + * AllocPointerGetNext -- + * Returns "successor" allocation pointer. + * + * Note: + * Assumes pointer is valid. + */ +static AllocPointer +AllocPointerGetNext(AllocPointer pointer) +{ + AllocElem alloc; + + alloc = (AllocElem) + OrderedElemGetSuccessor(&AllocPointerGetAllocElem(pointer)->elemData); + + if (!AllocElemIsValid(alloc)) { + return (NULL); + } + + return (AllocElemGetAllocPointer(alloc)); +} + + +/* + * Debugging routines + */ + +/* + * XXX AllocPointerDump -- + * Displays allocated pointer. + */ +void +AllocPointerDump(AllocPointer pointer) +{ + printf("\t%-10d@ %0#x\n", ((long*)pointer)[-1], pointer); /* XXX */ +} + +/* + * AllocSetDump -- + * Displays allocated set. + */ +void +AllocSetDump(AllocSet set) +{ + int count; + count = AllocSetIterate(set, AllocPointerDump); + printf("\ttotal %d allocations\n", count); +} diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c new file mode 100644 index 00000000000..d9095af11f4 --- /dev/null +++ b/src/backend/utils/mmgr/mcxt.c @@ -0,0 +1,510 @@ +/*------------------------------------------------------------------------- + * + * mcxt.c-- + * POSTGRES memory context code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/mcxt.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <stdio.h> /* XXX for printf debugging */ + +#include "postgres.h" + +#include "utils/memutils.h" +#include "utils/module.h" +#include "utils/excid.h" + +#include "nodes/memnodes.h" +#include "nodes/nodes.h" + +#include "utils/mcxt.h" +#include "utils/elog.h" + +#include "utils/palloc.h" + +#undef MemoryContextAlloc +#undef MemoryContextFree +#undef malloc +#undef free + +/* + * Global State + */ +static int MemoryContextEnableCount = 0; +#define MemoryContextEnabled (MemoryContextEnableCount > 0) + +static OrderedSetData ActiveGlobalMemorySetData; /* uninitialized */ +#define ActiveGlobalMemorySet (&ActiveGlobalMemorySetData) + +/* + * description of allocated memory representation goes here + */ + +#define PSIZE(PTR) (*((int32 *)(PTR) - 1)) +#define PSIZEALL(PTR) (*((int32 *)(PTR) - 1) + sizeof (int32)) +#define PSIZESKIP(PTR) ((char *)((int32 *)(PTR) + 1)) +#define PSIZEFIND(PTR) ((char *)((int32 *)(PTR) - 1)) +#define PSIZESPACE(LEN) ((LEN) + sizeof (int32)) + +/* + * AllocSizeIsValid -- + * True iff 0 < size and size <= MaxAllocSize. + */ +#define AllocSizeIsValid(size) (0 < (size) && (size) <= MaxAllocSize) + +/***************************************************************************** + * GLOBAL MEMORY * + *****************************************************************************/ + +/* + * CurrentMemoryContext -- + * Memory context for general global allocations. + */ +MemoryContext CurrentMemoryContext = NULL; + +/***************************************************************************** + * PRIVATE DEFINITIONS * + *****************************************************************************/ + +static Pointer GlobalMemoryAlloc(GlobalMemory this, Size size); +static void GlobalMemoryFree(GlobalMemory this, Pointer pointer); +static Pointer GlobalMemoryRealloc(GlobalMemory this, Pointer pointer, + Size size); +static char *GlobalMemoryGetName(GlobalMemory this); +static void GlobalMemoryDump(GlobalMemory this); +static void DumpGlobalMemories(void); + + +/* + * Global Memory Methods + */ + +static struct MemoryContextMethodsData GlobalContextMethodsData = { + GlobalMemoryAlloc, /* Pointer (*)(this, uint32) palloc */ + GlobalMemoryFree, /* void (*)(this, Pointer) pfree */ + GlobalMemoryRealloc, /* Pointer (*)(this, Pointer) repalloc */ + GlobalMemoryGetName, /* char* (*)(this) getName */ + GlobalMemoryDump /* void (*)(this) dump */ +}; + +/* + * Note: + * TopGlobalMemory is handled specially because of bootstrapping. + */ +/* extern bool EqualGlobalMemory(); */ + +static struct GlobalMemory TopGlobalMemoryData = { + T_GlobalMemory, /* NodeTag tag */ + &GlobalContextMethodsData, /* ContextMethods method */ + { { 0 } }, /* uninitialized + * OrderedSetData allocSetD + */ + "TopGlobal", /* char* name */ + { 0 } /* uninitialized OrderedElemData elemD */ +}; + +/* + * TopMemoryContext -- + * Memory context for general global allocations. + * + * Note: + * Don't use this memory context for random allocations. If you + * allocate something here, you are expected to clean it up when + * appropriate. + */ +MemoryContext TopMemoryContext = (MemoryContext)&TopGlobalMemoryData; + + + + +/* + * Module State + */ + +/* + * EnableMemoryContext -- + * Enables/disables memory management and global contexts. + * + * Note: + * This must be called before creating contexts or allocating memory. + * This must be called before other contexts are created. + * + * Exceptions: + * BadArg if on is invalid. + * BadState if on is false when disabled. + */ +void +EnableMemoryContext(bool on) +{ + static bool processing = false; + + AssertState(!processing); + AssertArg(BoolIsValid(on)); + + if (BypassEnable(&MemoryContextEnableCount, on)) { + return; + } + + processing = true; + + if (on) { /* initialize */ + /* initialize TopGlobalMemoryData.setData */ + AllocSetInit(&TopGlobalMemoryData.setData, DynamicAllocMode, + (Size)0); + + /* make TopGlobalMemoryData member of ActiveGlobalMemorySet */ + OrderedSetInit(ActiveGlobalMemorySet, + offsetof(struct GlobalMemory, elemData)); + OrderedElemPushInto(&TopGlobalMemoryData.elemData, + ActiveGlobalMemorySet); + + /* initialize CurrentMemoryContext */ + CurrentMemoryContext = TopMemoryContext; + + } else { /* cleanup */ + GlobalMemory context; + + /* walk the list of allocations */ + while (PointerIsValid(context = (GlobalMemory) + OrderedSetGetHead(ActiveGlobalMemorySet))) { + + if (context == &TopGlobalMemoryData) { + /* don't free it and clean it last */ + OrderedElemPop(&TopGlobalMemoryData.elemData); + } else { + GlobalMemoryDestroy(context); + } + /* what is needed for the top? */ + } + + /* + * Freeing memory here should be safe as this is called + * only after all modules which allocate in TopMemoryContext + * have been disabled. + */ + + /* step through remaining allocations and log */ + /* AllocSetStep(...); */ + + /* deallocate whatever is left */ + AllocSetReset(&TopGlobalMemoryData.setData); + } + + processing = false; +} + +/* + * MemoryContextAlloc -- + * Returns pointer to aligned allocated memory in the given context. + * + * Note: + * none + * + * Exceptions: + * BadState if called before InitMemoryManager. + * BadArg if context is invalid or if size is 0. + * BadAllocSize if size is larger than MaxAllocSize. + */ +Pointer +MemoryContextAlloc(MemoryContext context, Size size) +{ + AssertState(MemoryContextEnabled); + AssertArg(MemoryContextIsValid(context)); + + LogTrap(!AllocSizeIsValid(size), BadAllocSize, + ("size=%d [0x%x]", size, size)); + + return (context->method->alloc(context, size)); +} + +/* + * MemoryContextFree -- + * Frees allocated memory referenced by pointer in the given context. + * + * Note: + * none + * + * Exceptions: + * ??? + * BadArgumentsErr if firstTime is true for subsequent calls. + */ +void +MemoryContextFree(MemoryContext context, Pointer pointer) +{ + AssertState(MemoryContextEnabled); + AssertArg(MemoryContextIsValid(context)); + AssertArg(PointerIsValid(pointer)); + + context->method->free_p(context, pointer); +} + +/* + * MemoryContextRelloc -- + * Returns pointer to aligned allocated memory in the given context. + * + * Note: + * none + * + * Exceptions: + * ??? + * BadArgumentsErr if firstTime is true for subsequent calls. + */ +Pointer +MemoryContextRealloc(MemoryContext context, + Pointer pointer, + Size size) +{ + AssertState(MemoryContextEnabled); + AssertArg(MemoryContextIsValid(context)); + AssertArg(PointerIsValid(pointer)); + + LogTrap(!AllocSizeIsValid(size), BadAllocSize, + ("size=%d [0x%x]", size, size)); + + return (context->method->realloc(context, pointer, size)); +} + +/* + * MemoryContextGetName -- + * Returns pointer to aligned allocated memory in the given context. + * + * Note: + * none + * + * Exceptions: + * ??? + * BadArgumentsErr if firstTime is true for subsequent calls. + */ +char* +MemoryContextGetName(MemoryContext context) +{ + AssertState(MemoryContextEnabled); + AssertArg(MemoryContextIsValid(context)); + + return (context->method->getName(context)); +} + +/* + * PointerGetAllocSize -- + * Returns size of aligned allocated memory given pointer to it. + * + * Note: + * none + * + * Exceptions: + * ??? + * BadArgumentsErr if firstTime is true for subsequent calls. + */ +Size +PointerGetAllocSize(Pointer pointer) +{ + AssertState(MemoryContextEnabled); + AssertArg(PointerIsValid(pointer)); + + return (PSIZE(pointer)); +} + +/* + * MemoryContextSwitchTo -- + * Returns the current context; installs the given context. + * + * Note: + * none + * + * Exceptions: + * BadState if called when disabled. + * BadArg if context is invalid. + */ +MemoryContext +MemoryContextSwitchTo(MemoryContext context) +{ + MemoryContext old; + + AssertState(MemoryContextEnabled); + AssertArg(MemoryContextIsValid(context)); + + old = CurrentMemoryContext; + CurrentMemoryContext = context; + return (old); +} + +/* + * External Functions + */ +/* + * CreateGlobalMemory -- + * Returns new global memory context. + * + * Note: + * Assumes name is static. + * + * Exceptions: + * BadState if called when disabled. + * BadState if called outside TopMemoryContext (TopGlobalMemory). + * BadArg if name is invalid. + */ +GlobalMemory +CreateGlobalMemory(char *name) /* XXX MemoryContextName */ +{ + GlobalMemory context; + MemoryContext savecxt; + + AssertState(MemoryContextEnabled); + + savecxt = MemoryContextSwitchTo(TopMemoryContext); + + context = (GlobalMemory)newNode(sizeof(struct GlobalMemory), T_GlobalMemory); + context->method = &GlobalContextMethodsData; + context->name = name; /* assumes name is static */ + AllocSetInit(&context->setData, DynamicAllocMode, (Size)0); + + /* link the context */ + OrderedElemPushInto(&context->elemData, ActiveGlobalMemorySet); + + MemoryContextSwitchTo(savecxt); + return (context); +} + +/* + * GlobalMemoryDestroy -- + * Destroys given global memory context. + * + * Exceptions: + * BadState if called when disabled. + * BadState if called outside TopMemoryContext (TopGlobalMemory). + * BadArg if context is invalid GlobalMemory. + * BadArg if context is TopMemoryContext (TopGlobalMemory). + */ +void +GlobalMemoryDestroy(GlobalMemory context) +{ + AssertState(MemoryContextEnabled); + AssertArg(IsA(context,GlobalMemory)); + AssertArg(context != &TopGlobalMemoryData); + + AllocSetReset(&context->setData); + + /* unlink and delete the context */ + OrderedElemPop(&context->elemData); + MemoryContextFree(TopMemoryContext, (Pointer)context); +} + +/***************************************************************************** + * PRIVATE * + *****************************************************************************/ + +/* + * GlobalMemoryAlloc -- + * Returns pointer to aligned space in the global context. + * + * Exceptions: + * ExhaustedMemory if allocation fails. + */ +static Pointer +GlobalMemoryAlloc(GlobalMemory this, Size size) +{ + return (AllocSetAlloc(&this->setData, size)); +} + +/* + * GlobalMemoryFree -- + * Frees allocated memory in the global context. + * + * Exceptions: + * BadContextErr if current context is not the global context. + * BadArgumentsErr if pointer is invalid. + */ +static void +GlobalMemoryFree(GlobalMemory this, + Pointer pointer) +{ + AllocSetFree(&this->setData, pointer); +} + +/* + * GlobalMemoryRealloc -- + * Returns pointer to aligned space in the global context. + * + * Note: + * Memory associated with the pointer is freed before return. + * + * Exceptions: + * BadContextErr if current context is not the global context. + * BadArgumentsErr if pointer is invalid. + * NoMoreMemoryErr if allocation fails. + */ +static Pointer +GlobalMemoryRealloc(GlobalMemory this, + Pointer pointer, + Size size) +{ + return (AllocSetRealloc(&this->setData, pointer, size)); +} + +/* + * GlobalMemoryGetName -- + * Returns name string for context. + * + * Exceptions: + * ??? + */ +static char* +GlobalMemoryGetName(GlobalMemory this) +{ + return (this->name); +} + +/* + * GlobalMemoryDump -- + * Dumps global memory context for debugging. + * + * Exceptions: + * ??? + */ +static void +GlobalMemoryDump(GlobalMemory this) +{ + GlobalMemory context; + + printf("--\n%s:\n", GlobalMemoryGetName(this)); + + context = (GlobalMemory)OrderedElemGetPredecessor(&this->elemData); + if (PointerIsValid(context)) { + printf("\tpredecessor=%s\n", GlobalMemoryGetName(context)); + } + + context = (GlobalMemory)OrderedElemGetSuccessor(&this->elemData); + if (PointerIsValid(context)) { + printf("\tsucessor=%s\n", GlobalMemoryGetName(context)); + } + + AllocSetDump(&this->setData); /* XXX is this right interface */ +} + +/* + * DumpGlobalMemories -- + * Dumps all global memory contexts for debugging. + * + * Exceptions: + * ??? + */ +static void +DumpGlobalMemories() +{ + GlobalMemory context; + + context = (GlobalMemory)OrderedSetGetHead(&ActiveGlobalMemorySetData); + + while (PointerIsValid(context)) { + GlobalMemoryDump(context); + + context = (GlobalMemory)OrderedElemGetSuccessor( + &context->elemData); + } +} + diff --git a/src/backend/utils/mmgr/oset.c b/src/backend/utils/mmgr/oset.c new file mode 100644 index 00000000000..478fe1516ab --- /dev/null +++ b/src/backend/utils/mmgr/oset.c @@ -0,0 +1,173 @@ +/*------------------------------------------------------------------------- + * + * oset.c-- + * Fixed format ordered set definitions. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/Attic/oset.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + * NOTE + * XXX This is a preliminary implementation which lacks fail-fast + * XXX validity checking of arguments. + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include "utils/memutils.h" /* where declarations of this file goes */ + +static Pointer OrderedElemGetBase(OrderedElem elem); +static void OrderedElemInit(OrderedElem elem, OrderedSet set); +static void OrderedElemPush(OrderedElem elem); +static void OrderedElemPushHead(OrderedElem elem); + +/* + * OrderedElemGetBase -- + * Returns base of enclosing structure. + */ +static Pointer +OrderedElemGetBase(OrderedElem elem) +{ + if (elem == (OrderedElem) NULL) + return (Pointer) NULL; + + return ((Pointer)((char*)(elem) - (elem)->set->offset)); +} + +/* + * OrderedSetInit -- + */ +void +OrderedSetInit(OrderedSet set, Offset offset) +{ + set->head = (OrderedElem)&set->dummy; + set->dummy = NULL; + set->tail = (OrderedElem)&set->head; + set->offset = offset; +} + +/* + * OrderedElemInit -- + */ +static void +OrderedElemInit(OrderedElem elem, OrderedSet set) +{ + elem->set = set; + /* mark as unattached */ + elem->next = NULL; + elem->prev = NULL; +} + +/* + * OrderedSetContains -- + * True iff ordered set contains given element. + */ +bool +OrderedSetContains(OrderedSet set, OrderedElem elem) +{ + return ((bool)(elem->set == set && (elem->next || elem->prev))); +} + +/* + * OrderedSetGetHead -- + */ +Pointer +OrderedSetGetHead(OrderedSet set) +{ + register OrderedElem elem; + + elem = set->head; + if (elem->next) { + return (OrderedElemGetBase(elem)); + } + return (NULL); +} + +/* + * OrderedSetGetTail -- + */ +Pointer +OrderedSetGetTail(OrderedSet set) +{ + register OrderedElem elem; + + elem = set->tail; + if (elem->prev) { + return (OrderedElemGetBase(elem)); + } + return (NULL); +} + +/* + * OrderedElemGetPredecessor -- + */ +Pointer +OrderedElemGetPredecessor(OrderedElem elem) +{ + elem = elem->prev; + if (elem->prev) { + return (OrderedElemGetBase(elem)); + } + return (NULL); +} + +/* + * OrderedElemGetSuccessor -- + */ +Pointer +OrderedElemGetSuccessor(OrderedElem elem) +{ + elem = elem->next; + if (elem->next) { + return (OrderedElemGetBase(elem)); + } + return (NULL); +} + +/* + * OrderedElemPop -- + */ +void +OrderedElemPop(OrderedElem elem) +{ + elem->next->prev = elem->prev; + elem->prev->next = elem->next; + /* assignments used only for error detection */ + elem->next = NULL; + elem->prev = NULL; +} + +/* + * OrderedElemPushInto -- + */ +void +OrderedElemPushInto(OrderedElem elem, OrderedSet set) +{ + OrderedElemInit(elem, set); + OrderedElemPush(elem); +} + +/* + * OrderedElemPush -- + */ +static void +OrderedElemPush(OrderedElem elem) +{ + OrderedElemPushHead(elem); +} + +/* + * OrderedElemPushHead -- + */ +static void +OrderedElemPushHead(OrderedElem elem) +{ + elem->next = elem->set->head; + elem->prev = (OrderedElem)&elem->set->head; + elem->next->prev = elem; + elem->prev->next = elem; +} + diff --git a/src/backend/utils/mmgr/palloc.c b/src/backend/utils/mmgr/palloc.c new file mode 100644 index 00000000000..725cf171c85 --- /dev/null +++ b/src/backend/utils/mmgr/palloc.c @@ -0,0 +1,117 @@ +/*------------------------------------------------------------------------- + * + * palloc.c-- + * POSTGRES memory allocator code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/Attic/palloc.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include "c.h" + +#include "utils/mcxt.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "nodes/memnodes.h" + +#include "utils/palloc.h" + +/* ---------------------------------------------------------------- + * User library functions + * ---------------------------------------------------------------- + */ + +#undef palloc +#undef pfree +#undef MemoryContextAlloc +#undef MemoryContextFree +#undef malloc +#undef free + +/* define PALLOC_IS_MALLOC if you want palloc to go straight to the + raw malloc, without concern for the extra bookkeeping needed to + ensure garbage is collected at the end of transactions - jolly 1/12/94 */ + + +/* + * palloc -- + * Returns pointer to aligned memory of specified size. + * + * Exceptions: + * BadArgument if size < 1 or size >= MaxAllocSize. + * ExhaustedMemory if allocation fails. + * NonallocatedPointer if pointer was not returned by palloc or repalloc + * or may have been freed already. + * + * pfree -- + * Frees memory associated with pointer returned from palloc or repalloc. + * + * Exceptions: + * BadArgument if pointer is invalid. + * FreeInWrongContext if pointer was allocated in a different "context." + * NonallocatedPointer if pointer was not returned by palloc or repalloc + * or may have been subsequently freed. + */ +void* +palloc(Size size) +{ +#ifdef PALLOC_IS_MALLOC + return malloc(size); +#else + return (MemoryContextAlloc(CurrentMemoryContext, size)); +#endif /* PALLOC_IS_MALLOC */ +} + +void +pfree(void *pointer) +{ +#ifdef PALLOC_IS_MALLOC + free(pointer); +#else + MemoryContextFree(CurrentMemoryContext, pointer); +#endif /* PALLOC_IS_MALLOC */ +} + +/* + * repalloc -- + * Returns pointer to aligned memory of specified size. + * + * Side effects: + * The returned memory is first filled with the contents of *pointer + * up to the minimum of size and psize(pointer). Pointer is freed. + * + * Exceptions: + * BadArgument if pointer is invalid or size < 1 or size >= MaxAllocSize. + * ExhaustedMemory if allocation fails. + * NonallocatedPointer if pointer was not returned by palloc or repalloc + * or may have been freed already. + */ +void * +repalloc(void *pointer, Size size) +{ +#ifdef PALLOC_IS_MALLOC + return realloc(pointer, size); +#else + return (MemoryContextRealloc(CurrentMemoryContext, pointer, size)); +#endif +} + +/* pstrdup + allocates space for and copies a string + just like strdup except it uses palloc instead of malloc */ +char* +pstrdup(char* string) +{ + char *nstr; + + nstr = strcpy((char *)palloc(strlen(string)+1), string); + return nstr; +} + + + diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c new file mode 100644 index 00000000000..b43e85a0793 --- /dev/null +++ b/src/backend/utils/mmgr/portalmem.c @@ -0,0 +1,980 @@ +/*------------------------------------------------------------------------- + * + * portalmem.c-- + * backend portal memory context management stuff + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.1.1.1 1996/07/09 06:22:09 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * NOTES + * Do not confuse "Portal" with "PortalEntry" (or "PortalBuffer"). + * When a PQexec() routine is run, the resulting tuples + * find their way into a "PortalEntry". The contents of the resulting + * "PortalEntry" can then be inspected by other PQxxx functions. + * + * A "Portal" is a structure used to keep track of queries of the + * form: + * retrieve portal FOO ( blah... ) where blah... + * + * When the backend sees a "retrieve portal" query, it allocates + * a "PortalD" structure, plans the query and then stores the query + * in the portal without executing it. Later, when the backend + * sees a + * fetch 1 into FOO + * + * the system looks up the portal named "FOO" in the portal table, + * gets the planned query and then calls the executor with a feature of + * '(EXEC_FOR 1). The executor then runs the query and returns a single + * tuple. The problem is that we have to hold onto the state of the + * portal query until we see a "close p". This means we have to be + * careful about memory management. + * + * Having said all that, here is what a PortalD currently looks like: + * + * struct PortalD { + * char* name; + * classObj(PortalVariableMemory) variable; + * classObj(PortalHeapMemory) heap; + * List queryDesc; + * EState state; + * void (*cleanup) ARGS((Portal portal)); + * }; + * + * I hope this makes things clearer to whoever reads this -cim 2/22/91 + * + * Here is an old comment taken from nodes/memnodes.h + * + * MemoryContext -- + * A logical context in which memory allocations occur. + * + * The types of memory contexts can be thought of as members of the + * following inheritance hierarchy with properties summarized below. + * + * Node + * | + * MemoryContext___ + * / \ + * GlobalMemory PortalMemoryContext + * / \ + * PortalVariableMemory PortalHeapMemory + * + * Flushed at Flushed at Checkpoints + * Transaction Portal + * Commit Close + * + * GlobalMemory n n n + * PortalVariableMemory n y n + * PortalHeapMemory y y y * + * + */ +#include <stdio.h> /* for sprintf() */ +#include <string.h> /* for strlen, strncpy */ + +#include "c.h" + +#include "lib/hasht.h" +#include "utils/module.h" +#include "utils/excid.h" /* for Unimplemented */ +#include "utils/elog.h" +#include "utils/mcxt.h" +#include "utils/hsearch.h" + +#include "nodes/memnodes.h" +#include "nodes/nodes.h" +#include "nodes/pg_list.h" +#include "nodes/execnodes.h" /* for EState */ + +#include "utils/portal.h" + +/* ---------------- + * ALLOCFREE_ERROR_ABORT + * define this if you want a core dump when you try to + * free memory already freed -cim 2/9/91 + * ---------------- + */ +#undef ALLOCFREE_ERROR_ABORT + +/* ---------------- + * Global state + * ---------------- + */ + +static int PortalManagerEnableCount = 0; +#define MAX_PORTALNAME_LEN 64 /* XXX LONGALIGNable value */ + +typedef struct portalhashent { + char portalname[MAX_PORTALNAME_LEN]; + Portal portal; +} PortalHashEnt; + +#define PortalManagerEnabled (PortalManagerEnableCount >= 1) + +static HTAB *PortalHashTable = NULL; +#define PortalHashTableLookup(NAME, PORTAL) \ + { PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \ + memset(key, 0, MAX_PORTALNAME_LEN); \ + sprintf(key, "%s", NAME); \ + hentry = (PortalHashEnt*)hash_search(PortalHashTable, \ + key, HASH_FIND, &found); \ + if (hentry == NULL) \ + elog(FATAL, "error in PortalHashTable"); \ + if (found) \ + PORTAL = hentry->portal; \ + else \ + PORTAL = NULL; \ + } +#define PortalHashTableInsert(PORTAL) \ + { PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \ + memset(key, 0, MAX_PORTALNAME_LEN); \ + sprintf(key, "%s", PORTAL->name); \ + hentry = (PortalHashEnt*)hash_search(PortalHashTable, \ + key, HASH_ENTER, &found); \ + if (hentry == NULL) \ + elog(FATAL, "error in PortalHashTable"); \ + if (found) \ + elog(NOTICE, "trying to insert a portal name that exists."); \ + hentry->portal = PORTAL; \ + } +#define PortalHashTableDelete(PORTAL) \ + { PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \ + memset(key, 0, MAX_PORTALNAME_LEN); \ + sprintf(key, "%s", PORTAL->name); \ + hentry = (PortalHashEnt*)hash_search(PortalHashTable, \ + key, HASH_REMOVE, &found); \ + if (hentry == NULL) \ + elog(FATAL, "error in PortalHashTable"); \ + if (!found) \ + elog(NOTICE, "trying to delete portal name that does not exist."); \ + } + +static GlobalMemory PortalMemory = NULL; +static char PortalMemoryName[] = "Portal"; + +static Portal BlankPortal = NULL; + +/* ---------------- + * Internal class definitions + * ---------------- + */ +typedef struct HeapMemoryBlockData { + AllocSetData setData; + FixedItemData itemData; +} HeapMemoryBlockData; + +typedef HeapMemoryBlockData *HeapMemoryBlock; + +#define HEAPMEMBLOCK(context) \ + ((HeapMemoryBlock)(context)->block) + +/* ---------------------------------------------------------------- + * Variable and heap memory methods + * ---------------------------------------------------------------- + */ +/* ---------------- + * PortalVariableMemoryAlloc + * ---------------- + */ +static Pointer +PortalVariableMemoryAlloc(PortalVariableMemory this, + Size size) +{ + return (AllocSetAlloc(&this->setData, size)); +} + +/* ---------------- + * PortalVariableMemoryFree + * ---------------- + */ +static void +PortalVariableMemoryFree(PortalVariableMemory this, + Pointer pointer) +{ + AllocSetFree(&this->setData, pointer); +} + +/* ---------------- + * PortalVariableMemoryRealloc + * ---------------- + */ +static Pointer +PortalVariableMemoryRealloc(PortalVariableMemory this, + Pointer pointer, + Size size) +{ + return (AllocSetRealloc(&this->setData, pointer, size)); +} + +/* ---------------- + * PortalVariableMemoryGetName + * ---------------- + */ +static char* +PortalVariableMemoryGetName(PortalVariableMemory this) +{ + return (form("%s-var", PortalVariableMemoryGetPortal(this)->name)); +} + +/* ---------------- + * PortalVariableMemoryDump + * ---------------- + */ +static void +PortalVariableMemoryDump(PortalVariableMemory this) +{ + printf("--\n%s:\n", PortalVariableMemoryGetName(this)); + + AllocSetDump(&this->setData); /* XXX is this the right interface */ +} + +/* ---------------- + * PortalHeapMemoryAlloc + * ---------------- + */ +static Pointer +PortalHeapMemoryAlloc(PortalHeapMemory this, + Size size) +{ + HeapMemoryBlock block = HEAPMEMBLOCK(this); + + AssertState(PointerIsValid(block)); + + return (AllocSetAlloc(&block->setData, size)); +} + +/* ---------------- + * PortalHeapMemoryFree + * ---------------- + */ +static void +PortalHeapMemoryFree(PortalHeapMemory this, + Pointer pointer) +{ + HeapMemoryBlock block = HEAPMEMBLOCK(this); + + AssertState(PointerIsValid(block)); + + if (AllocSetContains(&block->setData, pointer)) + AllocSetFree(&block->setData, pointer); + else { + elog(NOTICE, + "PortalHeapMemoryFree: 0x%x not in alloc set!", + pointer); +#ifdef ALLOCFREE_ERROR_ABORT + Assert(AllocSetContains(&block->setData, pointer)); +#endif /* ALLOCFREE_ERROR_ABORT*/ + } +} + +/* ---------------- + * PortalHeapMemoryRealloc + * ---------------- + */ +static Pointer +PortalHeapMemoryRealloc(PortalHeapMemory this, + Pointer pointer, + Size size) +{ + HeapMemoryBlock block = HEAPMEMBLOCK(this); + + AssertState(PointerIsValid(block)); + + return (AllocSetRealloc(&block->setData, pointer, size)); +} + +/* ---------------- + * PortalHeapMemoryGetName + * ---------------- + */ +static char* +PortalHeapMemoryGetName(PortalHeapMemory this) +{ + return (form("%s-heap", PortalHeapMemoryGetPortal(this)->name)); +} + +/* ---------------- + * PortalHeapMemoryDump + * ---------------- + */ +static void +PortalHeapMemoryDump(PortalHeapMemory this) +{ + HeapMemoryBlock block; + + printf("--\n%s:\n", PortalHeapMemoryGetName(this)); + + /* XXX is this the right interface */ + if (PointerIsValid(this->block)) + AllocSetDump(&HEAPMEMBLOCK(this)->setData); + + /* dump the stack too */ + for (block = (HeapMemoryBlock)FixedStackGetTop(&this->stackData); + PointerIsValid(block); + block = (HeapMemoryBlock) + FixedStackGetNext(&this->stackData, (Pointer)block)) { + + printf("--\n"); + AllocSetDump(&block->setData); + } +} + +/* ---------------------------------------------------------------- + * variable / heap context method tables + * ---------------------------------------------------------------- + */ +static struct MemoryContextMethodsData PortalVariableContextMethodsData = { + PortalVariableMemoryAlloc, /* Pointer (*)(this, uint32) palloc */ + PortalVariableMemoryFree, /* void (*)(this, Pointer) pfree */ + PortalVariableMemoryRealloc,/* Pointer (*)(this, Pointer) repalloc */ + PortalVariableMemoryGetName,/* char* (*)(this) getName */ + PortalVariableMemoryDump /* void (*)(this) dump */ +}; + +static struct MemoryContextMethodsData PortalHeapContextMethodsData = { + PortalHeapMemoryAlloc, /* Pointer (*)(this, uint32) palloc */ + PortalHeapMemoryFree, /* void (*)(this, Pointer) pfree */ + PortalHeapMemoryRealloc, /* Pointer (*)(this, Pointer) repalloc */ + PortalHeapMemoryGetName, /* char* (*)(this) getName */ + PortalHeapMemoryDump /* void (*)(this) dump */ +}; + + +/* ---------------------------------------------------------------- + * private internal support routines + * ---------------------------------------------------------------- + */ +/* ---------------- + * CreateNewBlankPortal + * ---------------- + */ +static void +CreateNewBlankPortal() +{ + Portal portal; + + AssertState(!PortalIsValid(BlankPortal)); + + /* + * make new portal structure + */ + portal = (Portal) + MemoryContextAlloc((MemoryContext)PortalMemory, sizeof *portal); + + /* + * initialize portal variable context + */ + NodeSetTag((Node*)&portal->variable, T_PortalVariableMemory); + AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size)0); + portal->variable.method = &PortalVariableContextMethodsData; + + /* + * initialize portal heap context + */ + NodeSetTag((Node*)&portal->heap, T_PortalHeapMemory); + portal->heap.block = NULL; + FixedStackInit(&portal->heap.stackData, + offsetof (HeapMemoryBlockData, itemData)); + portal->heap.method = &PortalHeapContextMethodsData; + + /* + * set bogus portal name + */ + portal->name = "** Blank Portal **"; + + /* initialize portal query */ + portal->queryDesc = NULL; + portal->attinfo = NULL; + portal->state = NULL; + portal->cleanup = NULL; + + /* + * install blank portal + */ + BlankPortal = portal; +} + +bool +PortalNameIsSpecial(char *pname) +{ + if (strcmp(pname, VACPNAME) == 0) + return true; + return false; +} + +/* + * This routine is used to collect all portals created in this xaction + * and then destroy them. There is a little trickiness required as a + * result of the dynamic hashing interface to getting every hash entry + * sequentially. Its use of static variables requires that we get every + * entry *before* we destroy anything (destroying updates the hashtable + * and screws up the sequential walk of the table). -mer 17 Aug 1992 + */ +void +CollectNamedPortals(Portal *portalP, int destroy) +{ + static Portal *portalList = (Portal *)NULL; + static int listIndex = 0; + static int maxIndex = 9; + + if (portalList == (Portal *)NULL) + portalList = (Portal *)malloc(10*sizeof(Portal)); + + if (destroy != 0) + { + int i; + + for (i = 0; i < listIndex; i++) + PortalDestroy(&portalList[i]); + listIndex = 0; + } + else + { + Assert(portalP); + Assert(*portalP); + + /* + * Don't delete special portals, up to portal creator to do this + */ + if (PortalNameIsSpecial((*portalP)->name)) + return; + + portalList[listIndex] = *portalP; + listIndex++; + if (listIndex == maxIndex) + { + portalList = (Portal *) + realloc(portalList, (maxIndex+11)*sizeof(Portal)); + maxIndex += 10; + } + } + return; +} + +void +AtEOXact_portals() +{ + HashTableWalk(PortalHashTable, CollectNamedPortals, 0); + CollectNamedPortals(NULL, 1); +} + +/* ---------------- + * PortalDump + * ---------------- + */ +static void +PortalDump(Portal *thisP) +{ + /* XXX state/argument checking here */ + + PortalVariableMemoryDump(PortalGetVariableMemory(*thisP)); + PortalHeapMemoryDump(PortalGetHeapMemory(*thisP)); +} + +/* ---------------- + * DumpPortals + * ---------------- + */ +static void +DumpPortals() +{ + /* XXX state checking here */ + + HashTableWalk(PortalHashTable, PortalDump, 0); +} + +/* ---------------------------------------------------------------- + * public portal interface functions + * ---------------------------------------------------------------- + */ +/* + * EnablePortalManager -- + * Enables/disables the portal management module. + */ +void +EnablePortalManager(bool on) +{ + static bool processing = false; + HASHCTL ctl; + + AssertState(!processing); + AssertArg(BoolIsValid(on)); + + if (BypassEnable(&PortalManagerEnableCount, on)) + return; + + processing = true; + + if (on) { /* initialize */ + EnableMemoryContext(true); + + PortalMemory = CreateGlobalMemory(PortalMemoryName); + + ctl.keysize = MAX_PORTALNAME_LEN; + ctl.datasize = sizeof(Portal); + + /* use PORTALS_PER_USER, defined in utils/portal.h + * as a guess of how many hash table entries to create, initially + */ + PortalHashTable = hash_create(PORTALS_PER_USER * 3, &ctl, HASH_ELEM); + + CreateNewBlankPortal(); + + } else { /* cleanup */ + if (PortalIsValid(BlankPortal)) { + PortalDestroy(&BlankPortal); + MemoryContextFree((MemoryContext)PortalMemory, + (Pointer)BlankPortal); + BlankPortal = NULL; + } + /* + * Each portal must free its non-memory resources specially. + */ + HashTableWalk(PortalHashTable, PortalDestroy, 0); + hash_destroy(PortalHashTable); + PortalHashTable = NULL; + + GlobalMemoryDestroy(PortalMemory); + PortalMemory = NULL; + + EnableMemoryContext(true); + } + + processing = false; +} + +/* + * GetPortalByName -- + * Returns a portal given a portal name; returns blank portal given + * NULL; returns invalid portal if portal not found. + * + * Exceptions: + * BadState if called when disabled. + */ +Portal +GetPortalByName(char *name) +{ + Portal portal; + + AssertState(PortalManagerEnabled); + + if (PointerIsValid(name)) { + PortalHashTableLookup(name, portal); + } + else { + if (!PortalIsValid(BlankPortal)) + CreateNewBlankPortal(); + portal = BlankPortal; + } + + return (portal); +} + +/* + * BlankPortalAssignName -- + * Returns former blank portal as portal with given name. + * + * Side effect: + * All references to the former blank portal become incorrect. + * + * Exceptions: + * BadState if called when disabled. + * BadState if called without an intervening call to GetPortalByName(NULL). + * BadArg if portal name is invalid. + * "WARN" if portal name is in use. + */ +Portal +BlankPortalAssignName(char *name) /* XXX PortalName */ +{ + Portal portal; + uint16 length; + + AssertState(PortalManagerEnabled); + AssertState(PortalIsValid(BlankPortal)); + AssertArg(PointerIsValid(name)); /* XXX PortalName */ + + portal = GetPortalByName(name); + if (PortalIsValid(portal)) { + elog(NOTICE, "BlankPortalAssignName: portal %s already exists", name); + return (portal); + } + + /* + * remove blank portal + */ + portal = BlankPortal; + BlankPortal = NULL; + + /* + * initialize portal name + */ + length = 1 + strlen(name); + portal->name = (char*) + MemoryContextAlloc((MemoryContext)&portal->variable, length); + + strncpy(portal->name, name, length); + + /* + * put portal in table + */ + PortalHashTableInsert(portal); + + return (portal); +} + +/* + * PortalSetQuery -- + * Attaches a "query" to portal. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal is invalid. + * BadArg if queryDesc is "invalid." + * BadArg if state is "invalid." + */ +void +PortalSetQuery(Portal portal, + QueryDesc *queryDesc, + TupleDesc attinfo, + EState *state, + void (*cleanup)(Portal portal)) +{ + AssertState(PortalManagerEnabled); + AssertArg(PortalIsValid(portal)); + AssertArg(IsA((Node*)state,EState)); + + portal->queryDesc = queryDesc; + portal->state = state; + portal->attinfo = attinfo; + portal->cleanup = cleanup; +} + +/* + * PortalGetQueryDesc -- + * Returns query attached to portal. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal is invalid. + */ +QueryDesc * +PortalGetQueryDesc(Portal portal) +{ + AssertState(PortalManagerEnabled); + AssertArg(PortalIsValid(portal)); + + return (portal->queryDesc); +} + +/* + * PortalGetState -- + * Returns state attached to portal. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal is invalid. + */ +EState * +PortalGetState(Portal portal) +{ + AssertState(PortalManagerEnabled); + AssertArg(PortalIsValid(portal)); + + return (portal->state); +} + +/* + * CreatePortal -- + * Returns a new portal given a name. + * + * Note: + * This is expected to be of very limited usability. See instead, + * BlankPortalAssignName. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal name is invalid. + * "WARN" if portal name is in use. + */ +Portal +CreatePortal(char *name) /* XXX PortalName */ +{ + Portal portal; + uint16 length; + + AssertState(PortalManagerEnabled); + AssertArg(PointerIsValid(name)); /* XXX PortalName */ + + portal = GetPortalByName(name); + if (PortalIsValid(portal)) { + elog(NOTICE, "CreatePortal: portal %s already exists", name); + return (portal); + } + + /* make new portal structure */ + portal = (Portal) + MemoryContextAlloc((MemoryContext)PortalMemory, sizeof *portal); + + /* initialize portal variable context */ + NodeSetTag((Node*)&portal->variable, T_PortalVariableMemory); + AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size)0); + portal->variable.method = &PortalVariableContextMethodsData; + + /* initialize portal heap context */ + NodeSetTag((Node*)&portal->heap, T_PortalHeapMemory); + portal->heap.block = NULL; + FixedStackInit(&portal->heap.stackData, + offsetof (HeapMemoryBlockData, itemData)); + portal->heap.method = &PortalHeapContextMethodsData; + + /* initialize portal name */ + length = 1 + strlen(name); + portal->name = (char*) + MemoryContextAlloc((MemoryContext)&portal->variable, length); + strncpy(portal->name, name, length); + + /* initialize portal query */ + portal->queryDesc = NULL; + portal->attinfo = NULL; + portal->state = NULL; + portal->cleanup = NULL; + + /* put portal in table */ + PortalHashTableInsert(portal); + + /* Trap(PointerIsValid(name), Unimplemented); */ + return (portal); +} + +/* + * PortalDestroy -- + * Destroys portal. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal is invalid. + */ +void +PortalDestroy(Portal *portalP) +{ + Portal portal = *portalP; + + AssertState(PortalManagerEnabled); + AssertArg(PortalIsValid(portal)); + + /* remove portal from table if not blank portal */ + if (portal != BlankPortal) + PortalHashTableDelete(portal); + + /* reset portal */ + if (PointerIsValid(portal->cleanup)) + (*portal->cleanup)(portal); + + PortalResetHeapMemory(portal); + MemoryContextFree((MemoryContext)&portal->variable, + (Pointer)portal->name); + AllocSetReset(&portal->variable.setData); /* XXX log */ + + if (portal != BlankPortal) + MemoryContextFree((MemoryContext)PortalMemory, (Pointer)portal); +} + +/* ---------------- + * PortalResetHeapMemory -- + * Resets portal's heap memory context. + * + * Someday, Reset, Start, and End can be optimized by keeping a global + * portal module stack of free HeapMemoryBlock's. This will make Start + * and End be fast. + * + * Exceptions: + * BadState if called when disabled. + * BadState if called when not in PortalHeapMemory context. + * BadArg if mode is invalid. + * ---------------- + */ +void +PortalResetHeapMemory(Portal portal) +{ + PortalHeapMemory context; + MemoryContext currentContext; + + context = PortalGetHeapMemory(portal); + + if (PointerIsValid(context->block)) { + /* save present context */ + currentContext = MemoryContextSwitchTo((MemoryContext)context); + + do { + EndPortalAllocMode(); + } while (PointerIsValid(context->block)); + + /* restore context */ + (void) MemoryContextSwitchTo(currentContext); + } +} + +/* + * StartPortalAllocMode -- + * Starts a new block of portal heap allocation using mode and limit; + * the current block is disabled until EndPortalAllocMode is called. + * + * Note: + * Note blocks may be stacked and restored arbitarily. + * The semantics of mode and limit are described in aset.h. + * + * Exceptions: + * BadState if called when disabled. + * BadState if called when not in PortalHeapMemory context. + * BadArg if mode is invalid. + */ +void +StartPortalAllocMode(AllocMode mode, Size limit) +{ + PortalHeapMemory context; + + AssertState(PortalManagerEnabled); + AssertState(IsA(CurrentMemoryContext,PortalHeapMemory)); + /* AssertArg(AllocModeIsValid); */ + + context = (PortalHeapMemory)CurrentMemoryContext; + + /* stack current mode */ + if (PointerIsValid(context->block)) + FixedStackPush(&context->stackData, context->block); + + /* allocate and initialize new block */ + context->block = + MemoryContextAlloc( + (MemoryContext)PortalHeapMemoryGetVariableMemory(context), + sizeof (HeapMemoryBlockData) ); + + /* XXX careful, context->block has never been stacked => bad state */ + + AllocSetInit(&HEAPMEMBLOCK(context)->setData, mode, limit); +} + +/* + * EndPortalAllocMode -- + * Ends current block of portal heap allocation; previous block is + * reenabled. + * + * Note: + * Note blocks may be stacked and restored arbitarily. + * + * Exceptions: + * BadState if called when disabled. + * BadState if called when not in PortalHeapMemory context. + */ +void +EndPortalAllocMode() +{ + PortalHeapMemory context; + + AssertState(PortalManagerEnabled); + AssertState(IsA(CurrentMemoryContext,PortalHeapMemory)); + + context = (PortalHeapMemory)CurrentMemoryContext; + AssertState(PointerIsValid(context->block)); /* XXX Trap(...) */ + + /* free current mode */ + AllocSetReset(&HEAPMEMBLOCK(context)->setData); + MemoryContextFree((MemoryContext)PortalHeapMemoryGetVariableMemory(context), + context->block); + + /* restore previous mode */ + context->block = FixedStackPop(&context->stackData); +} + +/* + * PortalGetVariableMemory -- + * Returns variable memory context for a given portal. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal is invalid. + */ +PortalVariableMemory +PortalGetVariableMemory(Portal portal) +{ + return (&portal->variable); +} + +/* + * PortalGetHeapMemory -- + * Returns heap memory context for a given portal. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if portal is invalid. + */ +PortalHeapMemory +PortalGetHeapMemory(Portal portal) +{ + return (&portal->heap); +} + +/* + * PortalVariableMemoryGetPortal -- + * Returns portal containing given variable memory context. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if context is invalid. + */ +Portal +PortalVariableMemoryGetPortal(PortalVariableMemory context) +{ + return ((Portal)((char *)context - offsetof (PortalD, variable))); +} + +/* + * PortalHeapMemoryGetPortal -- + * Returns portal containing given heap memory context. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if context is invalid. + */ +Portal +PortalHeapMemoryGetPortal(PortalHeapMemory context) +{ + return ((Portal)((char *)context - offsetof (PortalD, heap))); +} + +/* + * PortalVariableMemoryGetHeapMemory -- + * Returns heap memory context associated with given variable memory. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if context is invalid. + */ +PortalHeapMemory +PortalVariableMemoryGetHeapMemory(PortalVariableMemory context) +{ + return ((PortalHeapMemory)((char *)context + - offsetof (PortalD, variable) + + offsetof (PortalD, heap))); +} + +/* + * PortalHeapMemoryGetVariableMemory -- + * Returns variable memory context associated with given heap memory. + * + * Exceptions: + * BadState if called when disabled. + * BadArg if context is invalid. + */ +PortalVariableMemory +PortalHeapMemoryGetVariableMemory(PortalHeapMemory context) +{ + return ((PortalVariableMemory)((char *)context + - offsetof (PortalD, heap) + + offsetof (PortalD, variable))); +} + diff --git a/src/backend/utils/module.h b/src/backend/utils/module.h new file mode 100644 index 00000000000..9ad62d0f0ce --- /dev/null +++ b/src/backend/utils/module.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * module.h-- + * this file contains general "module" stuff that used to be + * spread out between the following files: + * + * enbl.h module enable stuff + * trace.h module trace stuff (now gone) + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: module.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef MODULE_H +#define MODULE_H + +/* + * prototypes for functions in init/enbl.c + */ +extern bool BypassEnable(int *enableCountInOutP, bool on); + +#endif /* MODULE_H */ diff --git a/src/backend/utils/nabstime.h b/src/backend/utils/nabstime.h new file mode 100644 index 00000000000..68857656e9b --- /dev/null +++ b/src/backend/utils/nabstime.h @@ -0,0 +1,165 @@ +/*------------------------------------------------------------------------- + * + * nabstime.h-- + * Definitions for the "new" abstime code. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: nabstime.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef NABSTIME_H +#define NABSTIME_H + +#include <sys/types.h> +#include <time.h> +#if !defined(PORTNAME_irix5) +#include <sys/timeb.h> +#endif +#include "miscadmin.h" /* for SystemTime */ + +/* ---------------------------------------------------------------- + * time types + support macros + * + * + * ---------------------------------------------------------------- + */ +typedef int32 AbsoluteTime; +typedef int32 RelativeTime; + +typedef struct { + int32 status; + AbsoluteTime data[2]; +} TimeIntervalData; +typedef TimeIntervalData *TimeInterval; + +#define EPOCH_ABSTIME ((AbsoluteTime) 0) +#define INVALID_ABSTIME ((AbsoluteTime) 2147483647) /* 2^31 - 1 */ +#define CURRENT_ABSTIME ((AbsoluteTime) 2147483646) /* 2^31 - 2 */ +#define NOEND_ABSTIME ((AbsoluteTime) 2147483645) /* 2^31 - 3 */ + + +#if defined(PORTNAME_aix) +/* + * AIX considers 2147483648 == -2147483648 (since they have the same bit + * representation) but uses a different sign sense in a comparison to + * these integer constants depending on whether the constant is signed + * or not! + */ +#include <values.h> +/*#define NOSTART_ABSTIME ((AbsoluteTime) HIBITI) */ /* - 2^31 */ +#define NOSTART_ABSTIME ((AbsoluteTime) INT_MIN) +#else +/*#define NOSTART_ABSTIME ((AbsoluteTime) 2147483648)*/ /* - 2^31 */ +#define NOSTART_ABSTIME ((AbsoluteTime) -2147483647) /* - 2^31 */ +#endif /* PORTNAME_aix */ + +#define INVALID_RELTIME ((RelativeTime) 2147483647) /* 2^31 - 1 */ + +/* ---------------- + * time support macros (from tim.h) + * ---------------- + */ + +#define AbsoluteTimeIsValid(time) \ + ((bool) ((time) != INVALID_ABSTIME)) + +#define AbsoluteTimeIsReal(time) \ + ((bool) (((AbsoluteTime) time) < NOEND_ABSTIME && \ + ((AbsoluteTime) time) > NOSTART_ABSTIME)) + +/* have to include this because EPOCH_ABSTIME used to be invalid - yuk */ +#define AbsoluteTimeIsBackwardCompatiblyValid(time) \ + ((bool) (((AbsoluteTime) time) != INVALID_ABSTIME && \ + ((AbsoluteTime) time) > EPOCH_ABSTIME)) + +#define AbsoluteTimeIsBackwardCompatiblyReal(time) \ + ((bool) (((AbsoluteTime) time) < NOEND_ABSTIME && \ + ((AbsoluteTime) time) > NOSTART_ABSTIME && \ + ((AbsoluteTime) time) > EPOCH_ABSTIME)) + +#define RelativeTimeIsValid(time) \ + ((bool) (((RelativeTime) time) != INVALID_RELTIME)) + +#define GetCurrentAbsoluteTime() \ + ((AbsoluteTime) getSystemTime()) + +/* + * getSystemTime -- + * Returns system time. + */ +#define getSystemTime() \ + ((time_t) (time(0l))) + + +/* + * Meridian: am, pm, or 24-hour style. + */ +#define AM 0 +#define PM 1 +#define HR24 2 + +/* can't have more of these than there are bits in an unsigned long */ +#define MONTH 1 +#define YEAR 2 +#define DAY 3 +#define TIME 4 +#define TZ 5 +#define DTZ 6 +#define PG_IGNORE 7 +#define AMPM 8 +/* below here are unused so far */ +#define SECONDS 9 +#define MONTHS 10 +#define YEARS 11 +#define NUMBER 12 +/* these are only for relative dates */ +#define ABS_BEFORE 13 +#define ABS_AFTER 14 +#define AGO 15 + + +#define SECS(n) ((time_t)(n)) +#define MINS(n) ((time_t)(n) * SECS(60)) +#define HOURS(n) ((time_t)(n) * MINS(60)) /* 3600 secs */ +#define DAYS(n) ((time_t)(n) * HOURS(24)) /* 86400 secs */ +/* months and years are not constant length, must be specially dealt with */ + +#define TOKMAXLEN 6 /* only this many chars are stored in datetktbl */ + +/* keep this struct small; it gets used a lot */ +typedef struct { +#if defined(PORTNAME_aix) + char *token; +#else + char token[TOKMAXLEN]; +#endif /* PORTNAME_aix */ + char type; + char value; /* this may be unsigned, alas */ +} datetkn; + +/* + * nabstime.c prototypes + */ +extern AbsoluteTime nabstimein(char *timestr); +extern int prsabsdate(char *timestr, struct tm *tm, int *tzp); +extern int tryabsdate(char *fields[], int nf, struct tm *tm, int *tzp); +extern int parsetime(char *time, struct tm *tm); +extern int split(char *string, char *fields[], int nfields, char *sep); +extern char *nabstimeout(AbsoluteTime time); +extern AbsoluteTime dateconv(struct tm *tm, int zone); +extern time_t qmktime(struct tm *tp); +extern datetkn *datetoktype(char *s, int *bigvalp); +extern datetkn *datebsearch(char *key, datetkn *base, unsigned int nel); +extern bool AbsoluteTimeIsBefore(AbsoluteTime time1, AbsoluteTime time2); +extern bool AbsoluteTimeIsAfter(AbsoluteTime time1, AbsoluteTime time2); +extern int32 abstimeeq(AbsoluteTime t1, AbsoluteTime t2); +extern int32 abstimene(AbsoluteTime t1, AbsoluteTime t2); +extern int32 abstimelt(AbsoluteTime t1, AbsoluteTime t2); +extern int32 abstimegt(AbsoluteTime t1, AbsoluteTime t2); +extern int32 abstimele(AbsoluteTime t1, AbsoluteTime t2); +extern int32 abstimege(AbsoluteTime t1, AbsoluteTime t2); + +#endif /* NABSTIME_H */ diff --git a/src/backend/utils/oidcompos.h b/src/backend/utils/oidcompos.h new file mode 100644 index 00000000000..18c1f2ac55a --- /dev/null +++ b/src/backend/utils/oidcompos.h @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * oidcompos.h-- + * prototype file for the oid {char16,int4} composite type functions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: oidcompos.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef OIDCOMPOS_H +#define OIDCOMPOS_H + +/* oidint4.c */ +OidInt4 oidint4in(char *o); +char *oidint4out(OidInt4 o); +bool oidint4lt(OidInt4 o1, OidInt4 o2); +bool oidint4le(OidInt4 o1, OidInt4 o2); +bool oidint4eq(OidInt4 o1, OidInt4 o2); +bool oidint4ge(OidInt4 o1, OidInt4 o2); +bool oidint4gt(OidInt4 o1, OidInt4 o2); +bool oidint4ne(OidInt4 o1, OidInt4 o2); +int oidint4cmp(OidInt4 o1, OidInt4 o2); +OidInt4 mkoidint4(Oid v_oid, uint32 v_int4); + +/* oidint2.c */ +OidInt2 oidint2in(char *o); +char *oidint2out(OidInt2 o); +bool oidint2lt(OidInt2 o1, OidInt2 o2); +bool oidint2le(OidInt2 o1, OidInt2 o2); +bool oidint2eq(OidInt2 o1, OidInt2 o2); +bool oidint2ge(OidInt2 o1, OidInt2 o2); +bool oidint2gt(OidInt2 o1, OidInt2 o2); +bool oidint2ne(OidInt2 o1, OidInt2 o2); +int oidint2cmp(OidInt2 o1, OidInt2 o2); +OidInt2 mkoidint2(Oid v_oid, uint16 v_int2); + +/* oidname.c */ +OidName oidnamein(char *inStr); +char *oidnameout(OidName oidname); +bool oidnamelt(OidName o1, OidName o2); +bool oidnamele(OidName o1, OidName o2); +bool oidnameeq(OidName o1, OidName o2); +bool oidnamene(OidName o1, OidName o2); +bool oidnamege(OidName o1, OidName o2); +bool oidnamegt(OidName o1, OidName o2); +int oidnamecmp(OidName o1, OidName o2); +OidName mkoidname(Oid id, char *name); + +#endif /* OIDCOMPOS_H */ diff --git a/src/backend/utils/palloc.h b/src/backend/utils/palloc.h new file mode 100644 index 00000000000..f27f5f598ae --- /dev/null +++ b/src/backend/utils/palloc.h @@ -0,0 +1,26 @@ +/*------------------------------------------------------------------------- + * + * palloc.h-- + * POSTGRES memory allocator definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: palloc.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PALLOC_H +#define PALLOC_H + +#include "c.h" + +extern void* palloc(Size size); +extern void pfree(void *pointer); +extern void *repalloc(void *pointer, Size size); + +/* like strdup except uses palloc */ +extern char* pstrdup(char* pointer); + +#endif /* PALLOC_H */ + diff --git a/src/backend/utils/portal.h b/src/backend/utils/portal.h new file mode 100644 index 00000000000..4e3e42c9146 --- /dev/null +++ b/src/backend/utils/portal.h @@ -0,0 +1,97 @@ +/*------------------------------------------------------------------------- + * + * portal.h-- + * POSTGRES portal definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: portal.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +/* + * Note: + * A portal is an abstraction which represents the execution state of + * a running query (or a fixed sequence of queries). The "blank portal" is + * a portal with an InvalidName. This blank portal is in existance except + * between calls to BlankPortalAssignName and GetPortalByName(NULL). + * + * Note: + * now that PQ calls can be made from within a backend, a portal + * may also be used to keep track of the tuples resulting + * from the execution of a query. In this case, entryIndex + */ +#ifndef PORTAL_H +#define PORTAL_H + +#include "c.h" + +#include "nodes/execnodes.h" /* for EState */ +#include "nodes/memnodes.h" +#include "nodes/nodes.h" +#include "nodes/pg_list.h" +#include "nodes/plannodes.h" /* for Plan */ +#include "executor/execdesc.h" + +typedef struct PortalBlockData { + AllocSetData setData; + FixedItemData itemData; +} PortalBlockData; + +typedef PortalBlockData *PortalBlock; + +typedef struct PortalD PortalD; +typedef PortalD *Portal; + +struct PortalD { + char *name; /* XXX PortalName */ + struct PortalVariableMemory variable; + struct PortalHeapMemory heap; + QueryDesc *queryDesc; + TupleDesc attinfo; + EState *state; + void (*cleanup)(); +}; + +/* + * PortalIsValid -- + * True iff portal is valid. + */ +#define PortalIsValid(p) PointerIsValid(p) + +/* + * Special portals (well, their names anyway) + */ +#define VACPNAME "<vacuum>" + +extern bool PortalNameIsSpecial(char *pname); +extern void CollectNamedPortals(Portal *portalP, int destroy); +extern void AtEOXact_portals(void); +extern void EnablePortalManager(bool on); +extern Portal GetPortalByName(char *name); +extern Portal BlankPortalAssignName(char *name); +extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc, + TupleDesc attinfo, EState *state, + void (*cleanup)(Portal portal)); +extern QueryDesc *PortalGetQueryDesc(Portal portal); +extern EState *PortalGetState(Portal portal); +extern Portal CreatePortal(char *name); +extern void PortalDestroy(Portal *portalP); +extern void PortalResetHeapMemory(Portal portal); +extern void StartPortalAllocMode(AllocMode mode, Size limit); +extern void EndPortalAllocMode(void); +extern PortalVariableMemory PortalGetVariableMemory(Portal portal); +extern PortalHeapMemory PortalGetHeapMemory(Portal portal); +extern Portal PortalVariableMemoryGetPortal(PortalVariableMemory context); +extern Portal PortalHeapMemoryGetPortal(PortalHeapMemory context); +extern PortalHeapMemory PortalVariableMemoryGetHeapMemory(PortalVariableMemory context); +extern PortalVariableMemory PortalHeapMemoryGetVariableMemory(PortalHeapMemory context); + +/* estimate of the maximum number of open portals a user would have, + * used in initially sizing the PortalHashTable in EnablePortalManager() + */ +#define PORTALS_PER_USER 10 + + +#endif /* PORTAL_H */ diff --git a/src/backend/utils/psort.h b/src/backend/utils/psort.h new file mode 100644 index 00000000000..f6997538141 --- /dev/null +++ b/src/backend/utils/psort.h @@ -0,0 +1,86 @@ +/*------------------------------------------------------------------------- + * + * psort.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: psort.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PSORT_H +#define PSORT_H + +#define SORTMEM (1 << 18) /* 1/4 M - any static memory */ +#define MAXTAPES 7 /* 7--See Fig. 70, p273 */ +#define TAPEEXT "pg_psort.XXXXXX" /* TEMPDIR/TAPEEXT */ +#define FREE(x) free((char *) x) + +struct tape { + int tp_dummy; /* (D) */ + int tp_fib; /* (A) */ + FILE *tp_file; /* (TAPE) */ + struct tape *tp_prev; +}; + +struct cmplist { + int cp_attn; /* attribute number */ + int cp_num; /* comparison function code */ + int cp_rev; /* invert comparison flag */ + struct cmplist *cp_next; /* next in chain */ +}; + +extern int Nkeys; +extern ScanKey key; +extern int SortMemory; /* free memory */ +extern Relation SortRdesc; +extern struct leftist *Tuples; + +#ifdef EBUG +#include <stdio.h> +#include "utils/elog.h" +#include "storage/buf.h" +#include "storage/bufmgr.h" + +#define PDEBUG(PROC, S1)\ +elog(DEBUG, "%s:%d>> PROC: %s.", __FILE__, __LINE__, S1) + +#define PDEBUG2(PROC, S1, D1)\ +elog(DEBUG, "%s:%d>> PROC: %s %d.", __FILE__, __LINE__, S1, D1) + +#define PDEBUG4(PROC, S1, D1, S2, D2)\ +elog(DEBUG, "%s:%d>> PROC: %s %d, %s %d.", __FILE__, __LINE__, S1, D1, S2, D2) + +#define VDEBUG(VAR, FMT)\ +elog(DEBUG, "%s:%d>> VAR =FMT", __FILE__, __LINE__, VAR) + +#define ASSERT(EXPR, STR)\ +if (!(EXPR)) elog(FATAL, "%s:%d>> %s", __FILE__, __LINE__, STR) + +#define TRACE(VAL, CODE)\ +if (1) CODE; else + +#else +#define PDEBUG(MSG) +#define VDEBUG(VAR, FMT) +#define ASSERT(EXPR, MSG) +#define TRACE(VAL, CODE) +#endif + +/* psort.c */ +extern void psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key); +extern void initpsort(void); +extern void resetpsort(void); +extern void initialrun(Relation rdesc); +extern bool createrun(HeapScanDesc sdesc, FILE *file); +extern HeapTuple tuplecopy(HeapTuple tup, Relation rdesc, Buffer b); +extern FILE *mergeruns(void); +extern void merge(struct tape *dest); +extern void endpsort(Relation rdesc, FILE *file); +extern FILE *gettape(void); +extern void resettape(FILE *file); +extern void destroytape(FILE *file); + +#endif /* PSORT_H */ diff --git a/src/backend/utils/rel.h b/src/backend/utils/rel.h new file mode 100644 index 00000000000..d1d5a78dba7 --- /dev/null +++ b/src/backend/utils/rel.h @@ -0,0 +1,170 @@ +/*------------------------------------------------------------------------- + * + * rel.h-- + * POSTGRES relation descriptor definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rel.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef REL_H +#define REL_H + +#include "postgres.h" + +#include "storage/fd.h" +#include "access/strat.h" +#include "access/tupdesc.h" + +#include "catalog/pg_am.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_class.h" + +#include "rewrite/prs2lock.h" + +typedef struct RelationData { + File rd_fd; /* open file descriptor */ + int rd_nblocks; /* number of blocks in rel */ + uint16 rd_refcnt; /* reference count */ + bool rd_islocal; /* uses the local buffer mgr */ + bool rd_isnailed; /* rel is nailed in cache */ + Form_pg_am rd_am; /* AM tuple */ + Form_pg_class rd_rel; /* RELATION tuple */ + Oid rd_id; /* relations's object id */ + Pointer lockInfo; /* ptr. to misc. info. */ + TupleDesc rd_att; /* tuple desciptor */ + RuleLock *rd_rules; /* rewrite rules */ + IndexStrategy rd_istrat; + RegProcedure* rd_support; +} RelationData; + +typedef RelationData *Relation; + +/* ---------------- + * RelationPtr is used in the executor to support index scans + * where we have to keep track of several index relations in an + * array. -cim 9/10/89 + * ---------------- + */ +typedef Relation *RelationPtr; + +#define InvalidRelation ((Relation)NULL) + +typedef char ArchiveMode; + +/* + * RelationIsValid -- + * True iff relation descriptor is valid. + */ +#define RelationIsValid(relation) PointerIsValid(relation) + +/* + * RelationGetSystemPort -- + * Returns system port of a relation. + * + * Note: + * Assumes relation descriptor is valid. + */ +#define RelationGetSystemPort(relation) ((relation)->rd_fd) + +/* + * RelationGetLockInfo -- + * Returns the lock information structure in the reldesc + * + */ +#define RelationGetLockInfo(relation) ((relation)->lockInfo) + +/* + * RelationHasReferenceCountZero -- + * True iff relation reference count is zero. + * + * Note: + * Assumes relation descriptor is valid. + */ +#define RelationHasReferenceCountZero(relation) \ + ((bool)((relation)->rd_refcnt == 0)) + +/* + * RelationSetReferenceCount -- + * Sets relation reference count. + */ +#define RelationSetReferenceCount(relation,count) ((relation)->rd_refcnt = count) + +/* + * RelationIncrementReferenceCount -- + * Increments relation reference count. + */ +#define RelationIncrementReferenceCount(relation) ((relation)->rd_refcnt += 1); + +/* + * RelationDecrementReferenceCount -- + * Decrements relation reference count. + */ +#define RelationDecrementReferenceCount(relation) ((relation)->rd_refcnt -= 1) + +/* + * RelationGetAccessMethodTupleForm -- + * Returns access method attribute values for a relation. + * + * Note: + * Assumes relation descriptor is valid. + */ +#define RelationGetAccessMethodTupleForm(relation) ((relation)->rd_am) + +/* + * RelationGetRelationTupleForm -- + * Returns relation attribute values for a relation. + * + * Note: + * Assumes relation descriptor is valid. + */ +#define RelationGetRelationTupleForm(relation) ((relation)->rd_rel) + + +/* + * RelationGetRelationId -- + * + * returns the object id of the relation + * + */ +#define RelationGetRelationId(relation) ((relation)->rd_id) + +/* + * RelationGetFile -- + * + * Returns the open File decscriptor + */ +#define RelationGetFile(relation) ((relation)->rd_fd) + + +/* + * RelationGetRelationName -- + * + * Returns a Relation Name + */ +#define RelationGetRelationName(relation) (&(relation)->rd_rel->relname) + +/* + * RelationGetRelationName -- + * + * Returns a the number of attributes. + */ +#define RelationGetNumberOfAttributes(relation) ((relation)->rd_rel->relnatts) + +/* + * RelationGetTupleDescriptor -- + * Returns tuple descriptor for a relation. + * + * Note: + * Assumes relation descriptor is valid. + */ +#define RelationGetTupleDescriptor(relation) ((relation)->rd_att) + +extern IndexStrategy RelationGetIndexStrategy(Relation relation); + +extern void RelationSetIndexSupport(Relation relation, IndexStrategy strategy, + RegProcedure *support); +#endif /* REL_H */ diff --git a/src/backend/utils/rel2.h b/src/backend/utils/rel2.h new file mode 100644 index 00000000000..1e3d9881e73 --- /dev/null +++ b/src/backend/utils/rel2.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * rel2.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: rel2.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef TMP_REL2_H +#define TMP_REL2_H + +#include "access/istrat.h" + +extern IndexStrategy RelationGetIndexStrategy(Relation relation); + +extern void RelationSetIndexSupport(Relation relation, IndexStrategy strategy, + RegProcedure *support); + +#endif /* TMP_REL2_H */ diff --git a/src/backend/utils/relcache.h b/src/backend/utils/relcache.h new file mode 100644 index 00000000000..61e71ff4edf --- /dev/null +++ b/src/backend/utils/relcache.h @@ -0,0 +1,47 @@ +/*------------------------------------------------------------------------- + * + * relcache.h-- + * Relation descriptor cache definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: relcache.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef RELCACHE_H +#define RELCACHE_H + +#include <sys/types.h> + +#include "postgres.h" +#include "utils/rel.h" + +/* + * relation lookup routines + */ +extern Relation RelationIdCacheGetRelation(Oid relationId); +extern Relation RelationNameCacheGetRelation(char *relationName); +extern Relation RelationIdGetRelation(Oid relationId); +extern Relation RelationNameGetRelation(char *relationName); +extern Relation getreldesc(char *relationName); + +extern void RelationClose(Relation relation); +extern void RelationFlushRelation(Relation *relationPtr, + bool onlyFlushReferenceCountZero); +extern void RelationIdInvalidateRelationCacheByRelationId(Oid relationId); + +extern void +RelationIdInvalidateRelationCacheByAccessMethodId(Oid accessMethodId); + +extern void RelationCacheInvalidate(bool onlyFlushReferenceCountZero); + +extern void RelationRegisterRelation(Relation relation); +extern void RelationPurgeLocalRelation(bool xactComitted); +extern void RelationInitialize(); +extern void init_irels(); +extern void write_irels(); + + +#endif /* RELCACHE_H */ diff --git a/src/backend/utils/sets.h b/src/backend/utils/sets.h new file mode 100644 index 00000000000..5fc1ec1a416 --- /dev/null +++ b/src/backend/utils/sets.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * sets.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: sets.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef SETS_H +#define SETS_H + +/* Temporary name of set, before SetDefine changes it. */ +#define GENERICSETNAME "zyxset" + +extern Oid SetDefine(char *querystr, char *typename); +extern int seteval(Oid funcoid); + +#endif /* SETS_H */ diff --git a/src/backend/utils/sort/Makefile.inc b/src/backend/utils/sort/Makefile.inc new file mode 100644 index 00000000000..4775407acac --- /dev/null +++ b/src/backend/utils/sort/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/sort +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= lselect.c psort.c diff --git a/src/backend/utils/sort/lselect.c b/src/backend/utils/sort/lselect.c new file mode 100644 index 00000000000..1f92e48d838 --- /dev/null +++ b/src/backend/utils/sort/lselect.c @@ -0,0 +1,365 @@ +/*------------------------------------------------------------------------- + * + * lselect.c-- + * leftist tree selection algorithm (linked priority queue--Knuth, Vol.3, + * pp.150-52) + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/lselect.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include <stdio.h> + +#include "c.h" + +#include "storage/buf.h" +#include "access/skey.h" +#include "access/heapam.h" +#include "access/htup.h" +#include "utils/rel.h" + +#include "utils/psort.h" +#include "utils/lselect.h" + +extern Relation SortRdesc; /* later static */ + +/* + * PUTTUP - writes the next tuple + * ENDRUN - mark end of run + * GETLEN - reads the length of the next tuple + * ALLOCTUP - returns space for the new tuple + * SETTUPLEN - stores the length into the tuple + * GETTUP - reads the tuple + * + * Note: + * LEN field must be a short; FP is a stream + */ + +#define PUTTUP(TUP, FP) fwrite((char *)TUP, (TUP)->t_len, 1, FP) +#define ENDRUN(FP) fwrite((char *)&shortzero, sizeof (shortzero), 1, FP) +#define GETLEN(LEN, FP) fread(&(LEN), sizeof (shortzero), 1, FP) +#define ALLOCTUP(LEN) ((HeapTuple)malloc((unsigned)LEN)) +#define GETTUP(TUP, LEN, FP)\ + fread((char *)(TUP) + sizeof (shortzero), 1, (LEN) - sizeof (shortzero), FP) +#define SETTUPLEN(TUP, LEN) (TUP)->t_len = LEN + +/* + * USEMEM - record use of memory + * FREEMEM - record freeing of memory + * FULLMEM - 1 iff a tuple will fit + */ + +#define USEMEM(AMT) SortMemory -= (AMT) +#define FREEMEM(AMT) SortMemory += (AMT) +#define LACKMEM() (SortMemory <= BLCKSZ) /* not accurate */ + +/* + * lmerge - merges two leftist trees into one + * + * Note: + * Enforcing the rule that pt->lt_dist >= qt->lt_dist may + * simplifify much of the code. Removing recursion will not + * speed up code significantly. + */ +struct leftist * +lmerge(struct leftist *pt, struct leftist *qt) +{ + register struct leftist *root, *majorLeftist, *minorLeftist; + int dist; + + if (tuplecmp(pt->lt_tuple, qt->lt_tuple)) { + root = pt; + majorLeftist = qt; + } else { + root = qt; + majorLeftist = pt; + } + if (root->lt_left == NULL) + root->lt_left = majorLeftist; + else { + if ((minorLeftist = root->lt_right) != NULL) + majorLeftist = lmerge(majorLeftist, minorLeftist); + if ((dist = root->lt_left->lt_dist) < majorLeftist->lt_dist) { + root->lt_dist = 1 + dist; + root->lt_right = root->lt_left; + root->lt_left = majorLeftist; + } else { + root->lt_dist = 1 + majorLeftist->lt_dist; + root->lt_right = majorLeftist; + } + } + return(root); +} + +static struct leftist * +linsert(struct leftist *root, struct leftist *new1) +{ + register struct leftist *left, *right; + + if (! tuplecmp(root->lt_tuple, new1->lt_tuple)) { + new1->lt_left = root; + return(new1); + } + left = root->lt_left; + right = root->lt_right; + if (right == NULL) { + if (left == NULL) + root->lt_left = new1; + else { + root->lt_right = new1; + root->lt_dist = 2; + } + return(root); + } + right = linsert(right, new1); + if (right->lt_dist < left->lt_dist) { + root->lt_dist = 1 + left->lt_dist; + root->lt_left = right; + root->lt_right = left; + } else { + root->lt_dist = 1 + right->lt_dist; + root->lt_right = right; + } + return(root); +} + +/* + * gettuple - returns tuple at top of tree (Tuples) + * + * Returns: + * tuple at top of tree, NULL if failed ALLOC() + * *devnum is set to the devnum of tuple returned + * *treep is set to the new tree + * + * Note: + * *treep must not be NULL + * NULL is currently never returned BUG + */ +HeapTuple +gettuple(struct leftist **treep, + short *devnum) /* device from which tuple came */ +{ + register struct leftist *tp; + HeapTuple tup; + + tp = *treep; + tup = tp->lt_tuple; + *devnum = tp->lt_devnum; + if (tp->lt_dist == 1) /* lt_left == NULL */ + *treep = tp->lt_left; + else + *treep = lmerge(tp->lt_left, tp->lt_right); + + FREEMEM(sizeof (struct leftist)); + FREE(tp); + return(tup); +} + +/* + * puttuple - inserts new tuple into tree + * + * Returns: + * NULL iff failed ALLOC() + * + * Note: + * Currently never returns NULL BUG + */ +int +puttuple(struct leftist **treep, HeapTuple newtuple, int devnum) +{ + register struct leftist *new1; + register struct leftist *tp; + + new1 = (struct leftist *) malloc((unsigned) sizeof (struct leftist)); + USEMEM(sizeof (struct leftist)); + new1->lt_dist = 1; + new1->lt_devnum = devnum; + new1->lt_tuple = newtuple; + new1->lt_left = NULL; + new1->lt_right = NULL; + if ((tp = *treep) == NULL) + *treep = new1; + else + *treep = linsert(tp, new1); + return(1); +} + + +/* + * dumptuples - stores all the tuples in tree into file + */ +void +dumptuples(FILE *file) +{ + register struct leftist *tp; + register struct leftist *newp; + HeapTuple tup; + + tp = Tuples; + while (tp != NULL) { + tup = tp->lt_tuple; + if (tp->lt_dist == 1) /* lt_right == NULL */ + newp = tp->lt_left; + else + newp = lmerge(tp->lt_left, tp->lt_right); + FREEMEM(sizeof (struct leftist)); + FREE(tp); + PUTTUP(tup, file); + FREEMEM(tup->t_len); + FREE(tup); + tp = newp; + } + Tuples = NULL; +} + +/* + * tuplecmp - Compares two tuples with respect CmpList + * + * Returns: + * 1 if left < right ;0 otherwise + * Assumtions: + */ +int +tuplecmp(HeapTuple ltup, HeapTuple rtup) +{ + register char *lattr, *rattr; + int nkey = 0; + extern int Nkeys; + extern ScanKey Key; + int result = 0; + bool isnull; + + if (ltup == (HeapTuple)NULL) + return(0); + if (rtup == (HeapTuple)NULL) + return(1); + while (nkey < Nkeys && !result) { + lattr = heap_getattr(ltup, InvalidBuffer, + Key[nkey].sk_attno, + RelationGetTupleDescriptor(SortRdesc), + &isnull); + if (isnull) + return(0); + rattr = heap_getattr(rtup, InvalidBuffer, + Key[nkey].sk_attno, + RelationGetTupleDescriptor(SortRdesc), + &isnull); + if (isnull) + return(1); + if (Key[nkey].sk_flags & SK_COMMUTE) { + if (!(result = (long) (*Key[nkey].sk_func) (rattr, lattr))) + result = -(long) (*Key[nkey].sk_func) (lattr, rattr); + } else if (!(result = (long) (*Key[nkey].sk_func) (lattr, rattr))) + result = -(long) (*Key[nkey].sk_func) (rattr, lattr); + nkey++; + } + return (result == 1); +} + +#ifdef EBUG +void +checktree(struct leftist *tree) +{ + int lnodes; + int rnodes; + + if (tree == NULL) { + puts("Null tree."); + return; + } + lnodes = checktreer(tree->lt_left, 1); + rnodes = checktreer(tree->lt_right, 1); + if (lnodes < 0) { + lnodes = -lnodes; + puts("0:\tBad left side."); + } + if (rnodes < 0) { + rnodes = -rnodes; + puts("0:\tBad right side."); + } + if (lnodes == 0) { + if (rnodes != 0) + puts("0:\tLeft and right reversed."); + if (tree->lt_dist != 1) + puts("0:\tDistance incorrect."); + } else if (rnodes == 0) { + if (tree->lt_dist != 1) + puts("0:\tDistance incorrect."); + } else if (tree->lt_left->lt_dist < tree->lt_right->lt_dist) { + puts("0:\tLeft and right reversed."); + if (tree->lt_dist != 1 + tree->lt_left->lt_dist) + puts("0:\tDistance incorrect."); + } else if (tree->lt_dist != 1+ tree->lt_right->lt_dist) + puts("0:\tDistance incorrect."); + if (lnodes > 0) + if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple)) + printf("%d:\tLeft child < parent.\n"); + if (rnodes > 0) + if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple)) + printf("%d:\tRight child < parent.\n"); + printf("Tree has %d nodes\n", 1 + lnodes + rnodes); +} + +int +checktreer(struct leftist *tree, int level) +{ + int lnodes, rnodes; + int error = 0; + + if (tree == NULL) + return(0); + lnodes = checktreer(tree->lt_left, level + 1); + rnodes = checktreer(tree->lt_right, level + 1); + if (lnodes < 0) { + error = 1; + lnodes = -lnodes; + printf("%d:\tBad left side.\n", level); + } + if (rnodes < 0) { + error = 1; + rnodes = -rnodes; + printf("%d:\tBad right side.\n", level); + } + if (lnodes == 0) { + if (rnodes != 0) { + error = 1; + printf("%d:\tLeft and right reversed.\n", level); + } + if (tree->lt_dist != 1) { + error = 1; + printf("%d:\tDistance incorrect.\n", level); + } + } else if (rnodes == 0) { + if (tree->lt_dist != 1) { + error = 1; + printf("%d:\tDistance incorrect.\n", level); + } + } else if (tree->lt_left->lt_dist < tree->lt_right->lt_dist) { + error = 1; + printf("%d:\tLeft and right reversed.\n", level); + if (tree->lt_dist != 1 + tree->lt_left->lt_dist) + printf("%d:\tDistance incorrect.\n", level); + } else if (tree->lt_dist != 1+ tree->lt_right->lt_dist) { + error = 1; + printf("%d:\tDistance incorrect.\n", level); + } + if (lnodes > 0) + if (tuplecmp(tree->lt_left->lt_tuple, tree->lt_tuple)) { + error = 1; + printf("%d:\tLeft child < parent.\n"); + } + if (rnodes > 0) + if (tuplecmp(tree->lt_right->lt_tuple, tree->lt_tuple)) { + error = 1; + printf("%d:\tRight child < parent.\n"); + } + if (error) + return(-1 + -lnodes + -rnodes); + return(1 + lnodes + rnodes); +} +#endif diff --git a/src/backend/utils/sort/psort.c b/src/backend/utils/sort/psort.c new file mode 100644 index 00000000000..8e7187b434f --- /dev/null +++ b/src/backend/utils/sort/psort.c @@ -0,0 +1,617 @@ +/*------------------------------------------------------------------------- + * + * psort.c-- + * Polyphase merge sort. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/sort/Attic/psort.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + * NOTES + * Sorts the first relation into the second relation. The sort may + * not be called twice simultaneously. + * + * Use the tape-splitting method (Knuth, Vol. III, pp281-86) in the future. + * + * Arguments? Variables? + * MAXMERGE, MAXTAPES + *------------------------------------------------------------------------- + */ +#include <stdio.h> +#include <math.h> + +#include "c.h" + +#include "executor/execdebug.h" +#include "access/heapam.h" +#include "access/htup.h" +#include "access/relscan.h" +#include "access/skey.h" +#include "utils/tqual.h" /* for NowTimeQual */ + +#include "storage/buf.h" +#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "utils/portal.h" /* for {Start,End}PortalAllocMode */ +#include "utils/elog.h" +#include "utils/rel.h" + +#include "utils/psort.h" +#include "utils/lselect.h" + +#include "storage/fd.h" + +#define TEMPDIR "./" + +int Nkeys; +ScanKey Key; +int SortMemory; + +static int TapeRange; /* number of tapes - 1 (T) */ +static int Level; /* (l) */ +static int TotalDummy; /* summation of tp_dummy */ +static struct tape Tape[MAXTAPES]; +static long shortzero = 0; /* used to delimit runs */ +static struct tuple *LastTuple = NULL; /* last output */ + +static int BytesRead; /* to keep track of # of IO */ +static int BytesWritten; + +Relation SortRdesc; /* current tuples in memory */ +struct leftist *Tuples; /* current tuples in memory */ + +/* + * psort - polyphase merge sort entry point + */ +void +psort(Relation oldrel, Relation newrel, int nkeys, ScanKey key) +{ + AssertArg(nkeys >= 1); + AssertArg(key[0].sk_attno != 0); + AssertArg(key[0].sk_procedure != 0); + + Nkeys = nkeys; + Key = key; + SortMemory = 0; + SortRdesc = oldrel; + BytesRead = 0; + BytesWritten = 0; + /* + * may not be the best place. + * + * Pass 0 for the "limit" as the argument is currently ignored. + * Previously, only one arg was passed. -mer 12 Nov. 1991 + */ + StartPortalAllocMode(StaticAllocMode, (Size)0); + initpsort(); + initialrun(oldrel); + /* call finalrun(newrel, mergerun()) instead */ + endpsort(newrel, mergeruns()); + EndPortalAllocMode(); + NDirectFileRead += (int)ceil((double)BytesRead / BLCKSZ); + NDirectFileWrite += (int)ceil((double)BytesWritten / BLCKSZ); +} + +/* + * TAPENO - number of tape in Tape + */ + +#define TAPENO(NODE) (NODE - Tape) +#define TUPLENO(TUP) ((TUP == NULL) ? -1 : (int) TUP->t_iid) + +/* + * initpsort - initializes the tapes + * - (polyphase merge Alg.D(D1)--Knuth, Vol.3, p.270) + * Returns: + * number of allocated tapes + */ +void +initpsort() +{ + register int i; + register struct tape *tp; + + /* + ASSERT(ntapes >= 3 && ntapes <= MAXTAPES, + "initpsort: Invalid number of tapes to initialize.\n"); + */ + + tp = Tape; + for (i = 0; i < MAXTAPES && (tp->tp_file = gettape()) != NULL; i++) { + tp->tp_dummy = 1; + tp->tp_fib = 1; + tp->tp_prev = tp - 1; + tp++; + } + TapeRange = --tp - Tape; + tp->tp_dummy = 0; + tp->tp_fib = 0; + Tape[0].tp_prev = tp; + + if (TapeRange <= 1) + elog(WARN, "initpsort: Could only allocate %d < 3 tapes\n", + TapeRange + 1); + + Level = 1; + TotalDummy = TapeRange; + + SortMemory = SORTMEM; + LastTuple = NULL; + Tuples = NULL; +} + +/* + * resetpsort - resets (frees) malloc'd memory for an aborted Xaction + * + * Not implemented yet. + */ +void +resetpsort() +{ + ; +} + +/* + * PUTTUP - writes the next tuple + * ENDRUN - mark end of run + * GETLEN - reads the length of the next tuple + * ALLOCTUP - returns space for the new tuple + * SETTUPLEN - stores the length into the tuple + * GETTUP - reads the tuple + * + * Note: + * LEN field must be a short; FP is a stream + */ + +#define PUTTUP(TUP, FP)\ + BytesWritten += (TUP)->t_len; \ + fwrite((char *)TUP, (TUP)->t_len, 1, FP) +#define ENDRUN(FP) fwrite((char *)&shortzero, sizeof (shortzero), 1, FP) +#define GETLEN(LEN, FP) fread((char *)&(LEN), sizeof (shortzero), 1, FP) +#define ALLOCTUP(LEN) ((HeapTuple)malloc((unsigned)LEN)) +#define GETTUP(TUP, LEN, FP)\ + IncrProcessed(); \ + BytesRead += (LEN) - sizeof (shortzero); \ + fread((char *)(TUP) + sizeof (shortzero), (LEN) - sizeof (shortzero), 1, FP) +#define SETTUPLEN(TUP, LEN) (TUP)->t_len = LEN + + /* + * USEMEM - record use of memory + * FREEMEM - record freeing of memory + * FULLMEM - 1 iff a tuple will fit + */ + +#define USEMEM(AMT) SortMemory -= (AMT) +#define FREEMEM(AMT) SortMemory += (AMT) +#define LACKMEM() (SortMemory <= BLCKSZ) /* not accurate */ +#define TRACEMEM(FUNC) +#define TRACEOUT(FUNC, TUP) + +/* + * initialrun - distributes tuples from the relation + * - (replacement selection(R2-R3)--Knuth, Vol.3, p.257) + * - (polyphase merge Alg.D(D2-D4)--Knuth, Vol.3, p.271) + * + * Explaination: + * Tuples are distributed to the tapes as in Algorithm D. + * A "tuple" with t_size == 0 is used to mark the end of a run. + * + * Note: + * The replacement selection algorithm has been modified + * to go from R1 directly to R3 skipping R2 the first time. + * + * Maybe should use closer(rdesc) before return + * Perhaps should adjust the number of tapes if less than n. + * used--v. likely to have problems in mergeruns(). + * Must know if should open/close files before each + * call to psort()? If should--messy?? + * + * Possible optimization: + * put the first xxx runs in quickly--problem here since + * I (perhaps prematurely) combined the 2 algorithms. + * Also, perhaps allocate tapes when needed. Split into 2 funcs. + */ +void +initialrun(Relation rdesc) +{ + /* register struct tuple *tup; */ + register struct tape *tp; + HeapScanDesc sdesc; + int baseruns; /* D:(a) */ + int morepasses; /* EOF */ + + sdesc = heap_beginscan(rdesc, 0, NowTimeQual, 0, + (ScanKey)NULL); + tp = Tape; + + if ((bool)createrun(sdesc, tp->tp_file) != false) + morepasses = 0; + else + morepasses = 1 + (Tuples != NULL); /* (T != N) ? 2 : 1 */ + + for ( ; ; ) { + tp->tp_dummy--; + TotalDummy--; + if (tp->tp_dummy < (tp + 1)->tp_dummy) + tp++; + else if (tp->tp_dummy != 0) + tp = Tape; + else { + Level++; + baseruns = Tape[0].tp_fib; + for (tp = Tape; tp - Tape < TapeRange; tp++) { + TotalDummy += + (tp->tp_dummy = baseruns + + (tp + 1)->tp_fib + - tp->tp_fib); + tp->tp_fib = baseruns + + (tp + 1)->tp_fib; + } + tp = Tape; /* D4 */ + } /* D3 */ + if (morepasses) + if (--morepasses) { + dumptuples(tp->tp_file); + ENDRUN(tp->tp_file); + continue; + } else + break; + if ((bool)createrun(sdesc, tp->tp_file) == false) + morepasses = 1 + (Tuples != NULL); + /* D2 */ + } + for (tp = Tape + TapeRange; tp >= Tape; tp--) + rewind(tp->tp_file); /* D. */ + heap_endscan(sdesc); +} + +/* + * createrun - places the next run on file + * + * Uses: + * Tuples, which should contain any tuples for this run + * + * Returns: + * FALSE iff process through end of relation + * Tuples contains the tuples for the following run upon exit + */ +bool +createrun(HeapScanDesc sdesc, FILE *file) +{ + register HeapTuple lasttuple; + register HeapTuple btup, tup; + struct leftist *nextrun; + Buffer b; + bool foundeor; + short junk; + + lasttuple = NULL; + nextrun = NULL; + foundeor = false; + for ( ; ; ) { + while (LACKMEM() && Tuples != NULL) { + if (lasttuple != NULL) { + FREEMEM(lasttuple->t_len); + FREE(lasttuple); + TRACEMEM(createrun); + } + lasttuple = tup = gettuple(&Tuples, &junk); + PUTTUP(tup, file); + TRACEOUT(createrun, tup); + } + if (LACKMEM()) + break; + btup = heap_getnext(sdesc, 0, &b); + if (!HeapTupleIsValid(btup)) { + foundeor = true; + break; + } + IncrProcessed(); + tup = tuplecopy(btup, sdesc->rs_rd, b); + USEMEM(tup->t_len); + TRACEMEM(createrun); + if (lasttuple != NULL && tuplecmp(tup, lasttuple)) + puttuple(&nextrun, tup, 0); + else + puttuple(&Tuples, tup, 0); + ReleaseBuffer(b); + } + if (lasttuple != NULL) { + FREEMEM(lasttuple->t_len); + FREE(lasttuple); + TRACEMEM(createrun); + } + dumptuples(file); + ENDRUN(file); + /* delimit the end of the run */ + Tuples = nextrun; + return((bool)! foundeor); /* XXX - works iff bool is {0,1} */ +} + +/* + * tuplecopy - see also tuple.c:palloctup() + * + * This should eventually go there under that name? And this will + * then use malloc directly (see version -r1.2). + */ +HeapTuple +tuplecopy(HeapTuple tup, Relation rdesc, Buffer b) +{ + HeapTuple rettup; + + if (!HeapTupleIsValid(tup)) { + return(NULL); /* just in case */ + } + rettup = (HeapTuple)malloc(tup->t_len); + memmove((char *)rettup, (char *)tup, tup->t_len); /* XXX */ + return(rettup); +} + +/* + * mergeruns - merges all runs from input tapes + * (polyphase merge Alg.D(D6)--Knuth, Vol.3, p271) + * + * Returns: + * file of tuples in order + */ +FILE * +mergeruns() +{ + register struct tape *tp; + + tp = Tape + TapeRange; + merge(tp); + rewind(tp->tp_file); + while (--Level != 0) { + tp = tp->tp_prev; + rewind(tp->tp_file); + /* resettape(tp->tp_file); -not sufficient */ + merge(tp); + rewind(tp->tp_file); + } + return(tp->tp_file); +} + +/* + * merge - handles a single merge of the tape + * (polyphase merge Alg.D(D5)--Knuth, Vol.3, p271) + */ +void +merge(struct tape *dest) +{ + register HeapTuple tup; + register struct tape *lasttp; /* (TAPE[P]) */ + register struct tape *tp; + struct leftist *tuples; + FILE *destfile; + int times; /* runs left to merge */ + int outdummy; /* complete dummy runs */ + short fromtape; + long tuplen; + + lasttp = dest->tp_prev; + times = lasttp->tp_fib; + for (tp = lasttp ; tp != dest; tp = tp->tp_prev) + tp->tp_fib -= times; + tp->tp_fib += times; + /* Tape[].tp_fib (A[]) is set to proper exit values */ + + if (TotalDummy < TapeRange) /* no complete dummy runs */ + outdummy = 0; + else { + outdummy = TotalDummy; /* a large positive number */ + for (tp = lasttp; tp != dest; tp = tp->tp_prev) + if (outdummy > tp->tp_dummy) + outdummy = tp->tp_dummy; + for (tp = lasttp; tp != dest; tp = tp->tp_prev) + tp->tp_dummy -= outdummy; + tp->tp_dummy += outdummy; + TotalDummy -= outdummy * TapeRange; + /* do not add the outdummy runs yet */ + times -= outdummy; + } + destfile = dest->tp_file; + while (times-- != 0) { /* merge one run */ + tuples = NULL; + if (TotalDummy == 0) + for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) { + GETLEN(tuplen, tp->tp_file); + tup = ALLOCTUP(tuplen); + USEMEM(tuplen); + TRACEMEM(merge); + SETTUPLEN(tup, tuplen); + GETTUP(tup, tuplen, tp->tp_file); + puttuple(&tuples, tup, TAPENO(tp)); + } + else { + for (tp = dest->tp_prev; tp != dest; tp = tp->tp_prev) { + if (tp->tp_dummy != 0) { + tp->tp_dummy--; + TotalDummy--; + } else { + GETLEN(tuplen, tp->tp_file); + tup = ALLOCTUP(tuplen); + USEMEM(tuplen); + TRACEMEM(merge); + SETTUPLEN(tup, tuplen); + GETTUP(tup, tuplen, tp->tp_file); + puttuple(&tuples, tup, TAPENO(tp)); + } + } + } + while (tuples != NULL) { + /* possible optimization by using count in tuples */ + tup = gettuple(&tuples, &fromtape); + PUTTUP(tup, destfile); + FREEMEM(tup->t_len); + FREE(tup); + TRACEMEM(merge); + GETLEN(tuplen, Tape[fromtape].tp_file); + if (tuplen == 0) + ; + else { + tup = ALLOCTUP(tuplen); + USEMEM(tuplen); + TRACEMEM(merge); + SETTUPLEN(tup, tuplen); + GETTUP(tup, tuplen, Tape[fromtape].tp_file); + puttuple(&tuples, tup, fromtape); + } + } + ENDRUN(destfile); + } + TotalDummy += outdummy; +} + +/* + * endpsort - creates the new relation and unlinks the tape files + */ +void +endpsort(Relation rdesc, FILE *file) +{ + register struct tape *tp; + register HeapTuple tup; + long tuplen; + + if (! feof(file)) + while (GETLEN(tuplen, file) && tuplen != 0) { + tup = ALLOCTUP(tuplen); + SortMemory += tuplen; + SETTUPLEN(tup, tuplen); + GETTUP(tup, tuplen, file); + heap_insert(rdesc, tup); + FREE(tup); + SortMemory -= tuplen; + } + for (tp = Tape + TapeRange; tp >= Tape; tp--) + destroytape(tp->tp_file); +} + +/* + * gettape - handles access temporary files in polyphase merging + * + * Optimizations: + * If guarenteed that only one sort running/process, + * can simplify the file generation--and need not store the + * name for later unlink. + */ + +struct tapelst { + char *tl_name; + int tl_fd; + struct tapelst *tl_next; +}; + +static struct tapelst *Tapes = NULL; +static char Tempfile[MAXPGPATH] = TEMPDIR; + +/* + * gettape - returns an open stream for writing/reading + * + * Returns: + * Open stream for writing/reading. + * NULL if unable to open temporary file. + */ +FILE * +gettape() +{ + register struct tapelst *tp; + FILE *file; + static int tapeinit = 0; + char *mktemp(); + + tp = (struct tapelst *)malloc((unsigned)sizeof (struct tapelst)); + if (!tapeinit) { + Tempfile[sizeof (TEMPDIR) - 1] = '/'; + memmove(Tempfile + sizeof(TEMPDIR), TAPEEXT, sizeof (TAPEEXT)); + tapeinit = 1; + } + tp->tl_name = malloc((unsigned)sizeof(Tempfile)); + /* + * now, copy template with final null into malloc'd space + */ + memmove(tp->tl_name, Tempfile, sizeof (TEMPDIR) + sizeof (TAPEEXT)); + mktemp(tp->tl_name); + + AllocateFile(); + file = fopen(tp->tl_name, "w+"); + if (file == NULL) { + /* XXX this should not happen */ + FreeFile(); + FREE(tp->tl_name); + FREE(tp); + return(NULL); + } + + tp->tl_fd = fileno(file); + tp->tl_next = Tapes; + Tapes = tp; + return(file); +} + +/* + * resettape - resets the tape to size 0 + */ +void +resettape(FILE *file) +{ + register struct tapelst *tp; + register int fd; + + Assert(PointerIsValid(file)); + + fd = fileno(file); + for (tp = Tapes; tp != NULL && tp->tl_fd != fd; tp = tp->tl_next) + ; + if (tp == NULL) + elog(WARN, "resettape: tape not found"); + + file = freopen(tp->tl_name, "w+", file); + if (file == NULL) { + elog(FATAL, "could not freopen temporary file"); + } +} + +/* + * distroytape - unlinks the tape + * + * Efficiency note: + * More efficient to destroy more recently allocated tapes first. + * + * Possible bugs: + * Exits instead of returning status, if given invalid tape. + */ +void +destroytape(FILE *file) +{ + register struct tapelst *tp, *tq; + register int fd; + + if ((tp = Tapes) == NULL) + elog(FATAL, "destroytape: tape not found"); + + if ((fd = fileno(file)) == tp->tl_fd) { + Tapes = tp->tl_next; + fclose(file); + FreeFile(); + unlink(tp->tl_name); + FREE(tp->tl_name); + FREE(tp); + } else + for ( ; ; ) { + if (tp->tl_next == NULL) + elog(FATAL, "destroytape: tape not found"); + if (tp->tl_next->tl_fd == fd) { + fclose(file); + FreeFile(); + tq = tp->tl_next; + tp->tl_next = tq->tl_next; + unlink(tq->tl_name); + FREE((tq->tl_name)); + FREE(tq); + break; + } + tp = tp->tl_next; + } +} diff --git a/src/backend/utils/syscache.h b/src/backend/utils/syscache.h new file mode 100644 index 00000000000..beea5969404 --- /dev/null +++ b/src/backend/utils/syscache.h @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------- + * + * syscache.h-- + * System catalog cache definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: syscache.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef SYSCACHE_H +#define SYSCACHE_H + +/*#define CACHEDEBUG*/ /* turns DEBUG elogs on */ + +#include "postgres.h" +#include "access/htup.h" +#include "nodes/pg_list.h" + +/* + * Declarations for util/syscache.c. + * + * SysCache identifiers. + * + * The order of these must match the order + * they are entered into the structure cacheinfo[] in syscache.c + * The best thing to do is to add yours at the END, because some + * code assumes that certain caches are at certain places in this + * array. + */ + +#define AMOPOPID 0 +#define AMOPSTRATEGY 1 +#define ATTNAME 2 +#define ATTNUM 3 +#define INDEXRELID 4 +#define LANNAME 5 +#define OPRNAME 6 +#define OPROID 7 +#define PRONAME 8 +#define PROOID 9 +#define RELNAME 10 +#define RELOID 11 +#define TYPNAME 12 +#define TYPOID 13 +#define AMNAME 14 +#define CLANAME 15 +#define INDRELIDKEY 16 +#define INHRELID 17 +#define RULOID 18 +#define AGGNAME 19 +#define LISTENREL 20 +#define USENAME 21 +#define USESYSID 22 +#define GRONAME 23 +#define GROSYSID 24 +#define REWRITENAME 25 +#define PROSRC 26 + +/* ---------------- + * struct cachedesc: information needed for a call to InitSysCache() + * ---------------- + */ +struct cachedesc { + char *name; /* this is Name * so that we can initialize it */ + int nkeys; + int key[4]; + int size; /* sizeof(appropriate struct) */ + char *indname; /* index relation for this cache, if exists */ + HeapTuple (*iScanFunc)(); /* function to handle index scans */ +}; + +extern void zerocaches(void); +extern void InitCatalogCache(void); +extern HeapTuple SearchSysCacheTuple(int cacheId, Datum key1, Datum key2, + Datum key3, Datum key4); +extern int32 SearchSysCacheStruct(int cacheId, char *returnStruct, + Datum key1, Datum key2, Datum key3, Datum key4); +extern void *SearchSysCacheGetAttribute(int cacheId, + AttrNumber attributeNumber, + Datum key1, + Datum key2, + Datum key3, + Datum key4); +extern void *TypeDefaultRetrieve(Oid typId); + +#endif /* SYSCACHE_H */ diff --git a/src/backend/utils/time/Makefile.inc b/src/backend/utils/time/Makefile.inc new file mode 100644 index 00000000000..5e5d842e348 --- /dev/null +++ b/src/backend/utils/time/Makefile.inc @@ -0,0 +1,14 @@ +#------------------------------------------------------------------------- +# +# Makefile.inc-- +# Makefile for utils/time +# +# Copyright (c) 1994, Regents of the University of California +# +# +# IDENTIFICATION +# $Header: /cvsroot/pgsql/src/backend/utils/time/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ +# +#------------------------------------------------------------------------- + +SUBSRCS+= tqual.c diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c new file mode 100644 index 00000000000..dd115b69885 --- /dev/null +++ b/src/backend/utils/time/tqual.c @@ -0,0 +1,815 @@ +/*------------------------------------------------------------------------- + * + * tqual.c-- + * POSTGRES time qualification code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.1.1.1 1996/07/09 06:22:10 scrappy Exp $ + * + *------------------------------------------------------------------------- + */ + +/* #define TQUALDEBUG 1 */ + +#include "postgres.h" + +#include "access/htup.h" +#include "access/xact.h" +#include "storage/bufmgr.h" +#include "access/transam.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/nabstime.h" + +#include "utils/tqual.h" + +/* + * TimeQualMode -- + * Mode indicator for treatment of time qualifications. + */ +typedef uint16 TimeQualMode; + +#define TimeQualAt 0x1 +#define TimeQualNewer 0x2 +#define TimeQualOlder 0x4 +#define TimeQualAll 0x8 + +#define TimeQualMask 0xf + +#define TimeQualEvery 0x0 +#define TimeQualRange (TimeQualNewer | TimeQualOlder) +#define TimeQualAllAt (TimeQualAt | TimeQualAll) + +typedef struct TimeQualData { + AbsoluteTime start; + AbsoluteTime end; + TimeQualMode mode; +} TimeQualData; + +typedef TimeQualData *InternalTimeQual; + +static TimeQualData SelfTimeQualData; +TimeQual SelfTimeQual = (Pointer)&SelfTimeQualData; + +extern bool PostgresIsInitialized; + +/* + * XXX Transaction system override hacks start here + */ +#ifndef GOODAMI + +static TransactionId HeapSpecialTransactionId = InvalidTransactionId; +static CommandId HeapSpecialCommandId = FirstCommandId; + +void +setheapoverride(bool on) +{ + if (on) { + TransactionIdStore(GetCurrentTransactionId(), + &HeapSpecialTransactionId); + HeapSpecialCommandId = GetCurrentCommandId(); + } else { + HeapSpecialTransactionId = InvalidTransactionId; + } +} + +/* static */ +bool +heapisoverride() +{ + if (!TransactionIdIsValid(HeapSpecialTransactionId)) { + return (false); + } + + if (!TransactionIdEquals(GetCurrentTransactionId(), + HeapSpecialTransactionId) || + GetCurrentCommandId() != HeapSpecialCommandId) { + HeapSpecialTransactionId = InvalidTransactionId; + + return (false); + } + return (true); +} + +#endif /* !defined(GOODAMI) */ +/* + * XXX Transaction system override hacks end here + */ + +static bool HeapTupleSatisfiesItself(HeapTuple tuple); +static bool HeapTupleSatisfiesNow(HeapTuple tuple); +static bool HeapTupleSatisfiesSnapshotInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual); +static bool HeapTupleSatisfiesUpperBoundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual); +static bool HeapTupleSatisfiesUpperUnboundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual); + + + +/* + * TimeQualIsValid -- + * True iff time qualification is valid. + */ +bool +TimeQualIsValid(TimeQual qual) +{ + bool hasStartTime; + + if (!PointerIsValid(qual) || qual == SelfTimeQual) { + return (true); + } + + if (((InternalTimeQual)qual)->mode & ~TimeQualMask) { + return (false); + } + + if (((InternalTimeQual)qual)->mode & TimeQualAt) { + return (AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->start)); + } + + hasStartTime = false; + + if (((InternalTimeQual)qual)->mode & TimeQualNewer) { + if (!AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->start)) { + return (false); + } + hasStartTime = true; + } + + if (((InternalTimeQual)qual)->mode & TimeQualOlder) { + if (!AbsoluteTimeIsBackwardCompatiblyValid(((InternalTimeQual)qual)->end)) { + return (false); + } + if (hasStartTime) { + return ((bool)!AbsoluteTimeIsBefore( + ((InternalTimeQual)qual)->end, + ((InternalTimeQual)qual)->start)); + } + } + return (true); +} + +/* + * TimeQualIsLegal -- + * True iff time qualification is legal. + * I.e., true iff time qualification does not intersects the future, + * relative to the transaction start time. + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIsLegal(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (true); + } + + /* TimeQualAt */ + if (((InternalTimeQual)qual)->mode & TimeQualAt) { + AbsoluteTime a, b; + + a = ((InternalTimeQual)qual)->start; + b = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsAfter(a, b)) + return (false); + else + return (true); + } + + /* TimeQualOlder or TimeQualRange */ + if (((InternalTimeQual)qual)->mode & TimeQualOlder) { + AbsoluteTime a, b; + + a = ((InternalTimeQual)qual)->end; + b = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsAfter(a, b)) + return (false); + else + return (true); + } + + /* TimeQualNewer */ + if (((InternalTimeQual)qual)->mode & TimeQualNewer) { + AbsoluteTime a, b; + + a = ((InternalTimeQual)qual)->start; + b = GetCurrentTransactionStartTime(); + + if (AbsoluteTimeIsAfter(a, b)) + return (false); + else + return (true); + } + + /* TimeQualEvery */ + return (true); +} + +/* + * TimeQualIncludesNow -- + * True iff time qualification includes "now." + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIncludesNow(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (true); + } + + if (((InternalTimeQual)qual)->mode & TimeQualAt) { + return (false); + } + if (((InternalTimeQual)qual)->mode & TimeQualOlder && + !AbsoluteTimeIsAfter( + ((InternalTimeQual)qual)->end, + GetCurrentTransactionStartTime())) { + + return (false); + } + return (true); +} + +/* + * TimeQualIncludesPast -- + * True iff time qualification includes some time in the past. + * + * Note: + * Assumes time qualification is valid. + * XXX may not be needed? + */ +bool +TimeQualIncludesPast(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + /* otherwise, must check archive (setting locks as appropriate) */ + return (true); +} + +/* + * TimeQualIsSnapshot -- + * True iff time qualification is a snapshot qualification. + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIsSnapshot(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + return ((bool)!!(((InternalTimeQual)qual)->mode & TimeQualAt)); +} + +/* + * TimeQualIsRanged -- + * True iff time qualification is a ranged qualification. + * + * Note: + * Assumes time qualification is valid. + */ +bool +TimeQualIsRanged(TimeQual qual) +{ + Assert(TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + return ((bool)!(((InternalTimeQual)qual)->mode & TimeQualAt)); +} + +/* + * TimeQualIndicatesDisableValidityChecking -- + * True iff time qualification indicates validity checking should be + * disabled. + * + * Note: + * XXX This should not be implemented since this does not make sense. + */ +bool +TimeQualIndicatesDisableValidityChecking(TimeQual qual) +{ + Assert (TimeQualIsValid(qual)); + + if (qual == NowTimeQual || qual == SelfTimeQual) { + return (false); + } + + if (((InternalTimeQual)qual)->mode & TimeQualAll) { + return (true); + } + return (false); +} + +/* + * TimeQualGetSnapshotTime -- + * Returns time for a snapshot time qual. + * + * Note: + * Assumes time qual is valid snapshot time qual. + */ +AbsoluteTime +TimeQualGetSnapshotTime(TimeQual qual) +{ + Assert(TimeQualIsSnapshot(qual)); + + return (((InternalTimeQual)qual)->start); +} + +/* + * TimeQualGetStartTime -- + * Returns start time for a ranged time qual. + * + * Note: + * Assumes time qual is valid ranged time qual. + */ +AbsoluteTime +TimeQualGetStartTime(TimeQual qual) +{ + Assert(TimeQualIsRanged(qual)); + + return (((InternalTimeQual)qual)->start); +} + +/* + * TimeQualGetEndTime -- + * Returns end time for a ranged time qual. + * + * Note: + * Assumes time qual is valid ranged time qual. + */ +AbsoluteTime +TimeQualGetEndTime(TimeQual qual) +{ + Assert(TimeQualIsRanged(qual)); + + return (((InternalTimeQual)qual)->end); +} + +/* + * TimeFormSnapshotTimeQual -- + * Returns snapshot time qual for a time. + * + * Note: + * Assumes time is valid. + */ +TimeQual +TimeFormSnapshotTimeQual(AbsoluteTime time) +{ + InternalTimeQual qual; + + Assert(AbsoluteTimeIsBackwardCompatiblyValid(time)); + + qual = (InternalTimeQual)palloc(sizeof *qual); + + qual->start = time; + qual->end = INVALID_ABSTIME; + qual->mode = TimeQualAt; + + return ((TimeQual)qual); +} + +/* + * TimeFormRangedTimeQual -- + * Returns ranged time qual for a pair of times. + * + * Note: + * If start time is invalid, it is regarded as the epoch. + * If end time is invalid, it is regarded as "now." + * Assumes start time is before (or the same as) end time. + */ +TimeQual +TimeFormRangedTimeQual(AbsoluteTime startTime, + AbsoluteTime endTime) +{ + InternalTimeQual qual; + + qual = (InternalTimeQual)palloc(sizeof *qual); + + qual->start = startTime; + qual->end = endTime; + qual->mode = TimeQualEvery; + + if (AbsoluteTimeIsBackwardCompatiblyValid(startTime)) { + qual->mode |= TimeQualNewer; + } + if (AbsoluteTimeIsBackwardCompatiblyValid(endTime)) { + qual->mode |= TimeQualOlder; + } + + return ((TimeQual)qual); +} + +/* + * HeapTupleSatisfiesTimeQual -- + * True iff heap tuple satsifies a time qual. + * + * Note: + * Assumes heap tuple is valid. + * Assumes time qual is valid. + * XXX Many of the checks may be simplified and still remain correct. + * XXX Partial answers to the checks may be cached in an ItemId. + */ +bool +HeapTupleSatisfiesTimeQual(HeapTuple tuple, TimeQual qual) +{ +/* extern TransactionId AmiTransactionId; */ + + Assert(HeapTupleIsValid(tuple)); + Assert(TimeQualIsValid(qual)); + + if (TransactionIdEquals(tuple->t_xmax, AmiTransactionId)) + return(false); + + if (qual == SelfTimeQual || heapisoverride()) { + return (HeapTupleSatisfiesItself(tuple)); + } + + if (qual == NowTimeQual) { + return (HeapTupleSatisfiesNow(tuple)); + } + + if (!TimeQualIsLegal(qual)) { + elog(WARN, "HeapTupleSatisfiesTimeQual: illegal time qual"); + } + + if (TimeQualIndicatesDisableValidityChecking(qual)) { + elog(WARN, "HeapTupleSatisfiesTimeQual: no disabled validity checking (yet)"); + } + + if (TimeQualIsSnapshot(qual)) { + return (HeapTupleSatisfiesSnapshotInternalTimeQual(tuple, + (InternalTimeQual)qual)); + } + + if (TimeQualIncludesNow(qual)) { + return (HeapTupleSatisfiesUpperUnboundedInternalTimeQual(tuple, + (InternalTimeQual)qual)); + } + + return (HeapTupleSatisfiesUpperBoundedInternalTimeQual(tuple, + (InternalTimeQual)qual)); +} + +/* + * HeapTupleSatisfiesItself -- + * True iff heap tuple is valid for "itself." + * + * Note: + * Assumes heap tuple is valid. + */ +/* + * The satisfaction of "itself" requires the following: + * + * ((Xmin == my-transaction && (Xmax is null [|| Xmax != my-transaction)]) + * || + * + * (Xmin is committed && + * (Xmax is null || (Xmax != my-transaction && Xmax is not committed))) + */ +static bool +HeapTupleSatisfiesItself(HeapTuple tuple) +{ + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) && + !TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + } + /* the tuple was inserted validly */ + + if (AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + return (false); + } + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) { + return (false); + } + + return ((bool)!TransactionIdDidCommit((TransactionId)tuple->t_xmax)); +} + +/* + * HeapTupleSatisfiesNow -- + * True iff heap tuple is valid "now." + * + * Note: + * Assumes heap tuple is valid. + */ +/* + * The satisfaction of "now" requires the following: + * + * ((Xmin == my-transaction && Cmin != my-command && + * (Xmax is null || (Xmax == my-transaction && Cmax != my-command))) + * || + * + * (Xmin is committed && + * (Xmax is null || (Xmax == my-transaction && Cmax == my-command) || + * (Xmax is not committed && Xmax != my-transaction)))) + * + * mao says 17 march 1993: the tests in this routine are correct; + * if you think they're not, you're wrong, and you should think + * about it again. i know, it happened to me. we don't need to + * check commit time against the start time of this transaction + * because 2ph locking protects us from doing the wrong thing. + * if you mess around here, you'll break serializability. the only + * problem with this code is that it does the wrong thing for system + * catalog updates, because the catalogs aren't subject to 2ph, so + * the serializability guarantees we provide don't extend to xacts + * that do catalog accesses. this is unfortunate, but not critical. + */ +static bool +HeapTupleSatisfiesNow(HeapTuple tuple) +{ + if (AMI_OVERRIDE) + return true; + /* + * If the transaction system isn't yet initialized, then we assume + * that transactions committed. We only look at system catalogs + * during startup, so this is less awful than it seems, but it's + * still pretty awful. + */ + + if (!PostgresIsInitialized) + return ((bool)(TransactionIdIsValid((TransactionId)tuple->t_xmin) && + !TransactionIdIsValid((TransactionId)tuple->t_xmax))); + + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) + && CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + return (false); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) + && !CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + Assert(TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)); + + if (CommandIdIsCurrentCommandId(tuple->t_cmax)) { + return (true); + } + } + + /* + * this call is VERY expensive - requires a log table lookup. + */ + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + } + + /* by here, the inserting transaction has committed */ + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) { + return (false); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + return (true); + } + + /* by here, deleting transaction has committed */ + return (false); +} + +/* + * HeapTupleSatisfiesSnapshotInternalTimeQual -- + * True iff heap tuple is valid at the snapshot time qualification. + * + * Note: + * Assumes heap tuple is valid. + * Assumes internal time qualification is valid snapshot qualification. + */ +/* + * The satisfaction of Rel[T] requires the following: + * + * (Xmin is committed && Tmin <= T && + * (Xmax is null || (Xmax is not committed && Xmax != my-transaction) || + * Tmax >= T)) + */ +static bool +HeapTupleSatisfiesSnapshotInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual) +{ + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + + tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin); + } + + if (AbsoluteTimeIsBefore(TimeQualGetSnapshotTime((TimeQual)qual), tuple->t_tmin)) { + return (false); + } + /* the tuple was inserted validly before the snapshot time */ + + if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax) || + !TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + + return (true); + } + + tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax); + } + + return ((bool) + AbsoluteTimeIsAfter(tuple->t_tmax, + TimeQualGetSnapshotTime((TimeQual)qual))); +} + +/* + * HeapTupleSatisfiesUpperBoundedInternalTimeQual -- + * True iff heap tuple is valid within a upper bounded time qualification. + * + * Note: + * Assumes heap tuple is valid. + * Assumes time qualification is valid ranged qualification with fixed + * upper bound. + */ +/* + * The satisfaction of [T1,T2] requires the following: + * + * (Xmin is committed && Tmin <= T2 && + * (Xmax is null || (Xmax is not committed && Xmax != my-transaction) || + * T1 is null || Tmax >= T1)) + */ +static bool +HeapTupleSatisfiesUpperBoundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual) +{ + /* + * XXX Several evil casts are made in this routine. Casting XID to be + * TransactionId works only because TransactionId->data is the first + * (and only) field of the structure. + */ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + + tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin); + } + + if (AbsoluteTimeIsBefore(TimeQualGetEndTime((TimeQual)qual), tuple->t_tmin)) { + return (false); + } + /* the tuple was inserted validly before the range end */ + + if (!AbsoluteTimeIsBackwardCompatiblyValid(TimeQualGetStartTime((TimeQual)qual))) { + return (true); + } + + if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax) || + !TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + + return (true); + } + + tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax); + } + + return ((bool)AbsoluteTimeIsAfter(tuple->t_tmax, + TimeQualGetStartTime((TimeQual)qual))); +} + +/* + * HeapTupleSatisfiesUpperUnboundedInternalTimeQual -- + * True iff heap tuple is valid within a upper bounded time qualification. + * + * Note: + * Assumes heap tuple is valid. + * Assumes time qualification is valid ranged qualification with no + * upper bound. + */ +/* + * The satisfaction of [T1,] requires the following: + * + * ((Xmin == my-transaction && Cmin != my-command && + * (Xmax is null || (Xmax == my-transaction && Cmax != my-command))) + * || + * + * (Xmin is committed && + * (Xmax is null || (Xmax == my-transaction && Cmax == my-command) || + * (Xmax is not committed && Xmax != my-transaction) || + * T1 is null || Tmax >= T1))) + */ +static bool +HeapTupleSatisfiesUpperUnboundedInternalTimeQual(HeapTuple tuple, + InternalTimeQual qual) +{ + if (!AbsoluteTimeIsBackwardCompatiblyValid(tuple->t_tmin)) { + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) && + CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + return (false); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmin) && + !CommandIdIsCurrentCommandId(tuple->t_cmin)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + Assert(TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)); + + return ((bool) !CommandIdIsCurrentCommandId(tuple->t_cmax)); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmin)) { + return (false); + } + + tuple->t_tmin = TransactionIdGetCommitTime(tuple->t_xmin); + } + /* the tuple was inserted validly */ + + if (!AbsoluteTimeIsBackwardCompatiblyValid(TimeQualGetStartTime((TimeQual)qual))) { + return (true); + } + + if (!AbsoluteTimeIsBackwardCompatiblyReal(tuple->t_tmax)) { + + if (!TransactionIdIsValid((TransactionId)tuple->t_xmax)) { + return (true); + } + + if (TransactionIdIsCurrentTransactionId((TransactionId)tuple->t_xmax)) { + return (CommandIdIsCurrentCommandId(tuple->t_cmin)); + } + + if (!TransactionIdDidCommit((TransactionId)tuple->t_xmax)) { + return (true); + } + + tuple->t_tmax = TransactionIdGetCommitTime(tuple->t_xmax); + } + + return ((bool)AbsoluteTimeIsAfter(tuple->t_tmax, + TimeQualGetStartTime((TimeQual)qual))); +} diff --git a/src/backend/utils/tqual.h b/src/backend/utils/tqual.h new file mode 100644 index 00000000000..294fb18a3e6 --- /dev/null +++ b/src/backend/utils/tqual.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------------- + * + * tqual.h-- + * POSTGRES time qualification definitions. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: tqual.h,v 1.1.1.1 1996/07/09 06:22:02 scrappy Exp $ + * + * NOTE + * It may be desirable to allow time qualifications to indicate + * relative times. + * + *------------------------------------------------------------------------- + */ +#ifndef TQUAL_H +#define TQUAL_H + +#include "postgres.h" +#include "utils/nabstime.h" +#include "access/htup.h" + +typedef struct TimeQualSpace { + char data[12]; +} TimeQualSpace; + +typedef Pointer TimeQual; + +/* Tuples valid as of StartTransactionCommand */ +#define NowTimeQual ((TimeQual) NULL) + +/* As above, plus updates in this command */ +extern TimeQual SelfTimeQual; + +extern void setheapoverride(bool on); +extern bool heapisoverride(void); + +extern bool TimeQualIsValid(TimeQual qual); +extern bool TimeQualIsLegal(TimeQual qual); +extern bool TimeQualIncludesNow(TimeQual qual); +extern bool TimeQualIncludesPast(TimeQual qual); +extern bool TimeQualIsSnapshot(TimeQual qual); +extern bool TimeQualIsRanged(TimeQual qual); +extern bool TimeQualIndicatesDisableValidityChecking(TimeQual qual); +extern AbsoluteTime TimeQualGetSnapshotTime(TimeQual qual); +extern AbsoluteTime TimeQualGetStartTime(TimeQual qual); +extern AbsoluteTime TimeQualGetEndTime(TimeQual qual); +extern TimeQual TimeFormSnapshotTimeQual(AbsoluteTime time); +extern TimeQual TimeFormRangedTimeQual(AbsoluteTime startTime, + AbsoluteTime endTime); +extern bool HeapTupleSatisfiesTimeQual(HeapTuple tuple, TimeQual qual); + + +#endif /* TQUAL_H */ |