aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/Gen_fmgrtab.sh265
-rw-r--r--src/backend/utils/Makefile.inc62
-rw-r--r--src/backend/utils/acl.h163
-rw-r--r--src/backend/utils/adt/Makefile.inc20
-rw-r--r--src/backend/utils/adt/acl.c618
-rw-r--r--src/backend/utils/adt/arrayfuncs.c1375
-rw-r--r--src/backend/utils/adt/arrayutils.c111
-rw-r--r--src/backend/utils/adt/bool.c65
-rw-r--r--src/backend/utils/adt/char.c392
-rw-r--r--src/backend/utils/adt/chunk.c587
-rw-r--r--src/backend/utils/adt/date.c891
-rw-r--r--src/backend/utils/adt/datetimes.c350
-rw-r--r--src/backend/utils/adt/datum.c201
-rw-r--r--src/backend/utils/adt/dt.c58
-rw-r--r--src/backend/utils/adt/filename.c120
-rw-r--r--src/backend/utils/adt/float.c1320
-rw-r--r--src/backend/utils/adt/geo-ops.c1947
-rw-r--r--src/backend/utils/adt/geo-selfuncs.c124
-rw-r--r--src/backend/utils/adt/int.c343
-rw-r--r--src/backend/utils/adt/like.c225
-rw-r--r--src/backend/utils/adt/misc.c96
-rw-r--r--src/backend/utils/adt/nabstime.c866
-rw-r--r--src/backend/utils/adt/name.c198
-rw-r--r--src/backend/utils/adt/not_in.c124
-rw-r--r--src/backend/utils/adt/numutils.c401
-rw-r--r--src/backend/utils/adt/oid.c127
-rw-r--r--src/backend/utils/adt/oidint2.c120
-rw-r--r--src/backend/utils/adt/oidint4.c111
-rw-r--r--src/backend/utils/adt/oidname.c123
-rw-r--r--src/backend/utils/adt/regexp.c343
-rw-r--r--src/backend/utils/adt/regproc.c159
-rw-r--r--src/backend/utils/adt/selfuncs.c585
-rw-r--r--src/backend/utils/adt/sets.c164
-rw-r--r--src/backend/utils/adt/tid.c92
-rw-r--r--src/backend/utils/adt/varchar.c496
-rw-r--r--src/backend/utils/adt/varlena.c488
-rw-r--r--src/backend/utils/array.h166
-rw-r--r--src/backend/utils/bit.h39
-rw-r--r--src/backend/utils/builtins.h433
-rw-r--r--src/backend/utils/cache/Makefile.inc15
-rw-r--r--src/backend/utils/cache/catcache.c1023
-rw-r--r--src/backend/utils/cache/fcache.c297
-rw-r--r--src/backend/utils/cache/inval.c612
-rw-r--r--src/backend/utils/cache/lsyscache.c484
-rw-r--r--src/backend/utils/cache/rel.c77
-rw-r--r--src/backend/utils/cache/relcache.c1795
-rw-r--r--src/backend/utils/cache/syscache.c630
-rw-r--r--src/backend/utils/catcache.h85
-rw-r--r--src/backend/utils/datum.h64
-rw-r--r--src/backend/utils/dynamic_loader.h53
-rw-r--r--src/backend/utils/elog.h38
-rw-r--r--src/backend/utils/error/Makefile.inc14
-rw-r--r--src/backend/utils/error/assert.c64
-rw-r--r--src/backend/utils/error/elog.c237
-rw-r--r--src/backend/utils/error/exc.c183
-rw-r--r--src/backend/utils/error/excabort.c28
-rw-r--r--src/backend/utils/error/excid.c64
-rw-r--r--src/backend/utils/error/format.c40
-rw-r--r--src/backend/utils/exc.h101
-rw-r--r--src/backend/utils/excid.h31
-rw-r--r--src/backend/utils/fcache.h55
-rw-r--r--src/backend/utils/fcache2.h19
-rw-r--r--src/backend/utils/fmgr/Makefile.inc15
-rw-r--r--src/backend/utils/fmgr/dfmgr.c269
-rw-r--r--src/backend/utils/fmgr/fmgr.c254
-rw-r--r--src/backend/utils/fmgrtab.h29
-rw-r--r--src/backend/utils/geo-decls.h248
-rw-r--r--src/backend/utils/hash/Makefile.inc14
-rw-r--r--src/backend/utils/hash/dynahash.c868
-rw-r--r--src/backend/utils/hash/hashfn.c156
-rw-r--r--src/backend/utils/hsearch.h141
-rw-r--r--src/backend/utils/init/Makefile.inc14
-rw-r--r--src/backend/utils/init/enbl.c45
-rw-r--r--src/backend/utils/init/findbe.c251
-rw-r--r--src/backend/utils/init/globals.c108
-rw-r--r--src/backend/utils/init/magic.c167
-rw-r--r--src/backend/utils/init/miscinit.c378
-rw-r--r--src/backend/utils/init/postinit.c648
-rw-r--r--src/backend/utils/inval.h56
-rw-r--r--src/backend/utils/lselect.h40
-rw-r--r--src/backend/utils/lsyscache.h45
-rw-r--r--src/backend/utils/mcxt.h56
-rw-r--r--src/backend/utils/memutils.h281
-rw-r--r--src/backend/utils/mmgr/Makefile.inc15
-rw-r--r--src/backend/utils/mmgr/aset.c381
-rw-r--r--src/backend/utils/mmgr/mcxt.c510
-rw-r--r--src/backend/utils/mmgr/oset.c173
-rw-r--r--src/backend/utils/mmgr/palloc.c117
-rw-r--r--src/backend/utils/mmgr/portalmem.c980
-rw-r--r--src/backend/utils/module.h25
-rw-r--r--src/backend/utils/nabstime.h165
-rw-r--r--src/backend/utils/oidcompos.h52
-rw-r--r--src/backend/utils/palloc.h26
-rw-r--r--src/backend/utils/portal.h97
-rw-r--r--src/backend/utils/psort.h86
-rw-r--r--src/backend/utils/rel.h170
-rw-r--r--src/backend/utils/rel2.h23
-rw-r--r--src/backend/utils/relcache.h47
-rw-r--r--src/backend/utils/sets.h22
-rw-r--r--src/backend/utils/sort/Makefile.inc14
-rw-r--r--src/backend/utils/sort/lselect.c365
-rw-r--r--src/backend/utils/sort/psort.c617
-rw-r--r--src/backend/utils/syscache.h89
-rw-r--r--src/backend/utils/time/Makefile.inc14
-rw-r--r--src/backend/utils/time/tqual.c815
-rw-r--r--src/backend/utils/tqual.h55
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 */