mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Postgres95 1.01 Distribution - Virgin Sources
This commit is contained in:
265
src/backend/utils/Gen_fmgrtab.sh
Normal file
265
src/backend/utils/Gen_fmgrtab.sh
Normal file
@@ -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
|
||||
62
src/backend/utils/Makefile.inc
Normal file
62
src/backend/utils/Makefile.inc
Normal file
@@ -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
|
||||
163
src/backend/utils/acl.h
Normal file
163
src/backend/utils/acl.h
Normal file
@@ -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 */
|
||||
|
||||
20
src/backend/utils/adt/Makefile.inc
Normal file
20
src/backend/utils/adt/Makefile.inc
Normal file
@@ -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
|
||||
|
||||
618
src/backend/utils/adt/acl.c
Normal file
618
src/backend/utils/adt/acl.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
1375
src/backend/utils/adt/arrayfuncs.c
Normal file
1375
src/backend/utils/adt/arrayfuncs.c
Normal file
File diff suppressed because it is too large
Load Diff
111
src/backend/utils/adt/arrayutils.c
Normal file
111
src/backend/utils/adt/arrayutils.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
65
src/backend/utils/adt/bool.c
Normal file
65
src/backend/utils/adt/bool.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
392
src/backend/utils/adt/char.c
Normal file
392
src/backend/utils/adt/char.c
Normal file
@@ -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));
|
||||
}
|
||||
587
src/backend/utils/adt/chunk.c
Normal file
587
src/backend/utils/adt/chunk.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
891
src/backend/utils/adt/date.c
Normal file
891
src/backend/utils/adt/date.c
Normal file
@@ -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 */
|
||||
|
||||
}
|
||||
350
src/backend/utils/adt/datetimes.c
Normal file
350
src/backend/utils/adt/datetimes.c
Normal file
@@ -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);
|
||||
}
|
||||
201
src/backend/utils/adt/datum.c
Normal file
201
src/backend/utils/adt/datum.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
58
src/backend/utils/adt/dt.c
Normal file
58
src/backend/utils/adt/dt.c
Normal file
@@ -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) */
|
||||
120
src/backend/utils/adt/filename.c
Normal file
120
src/backend/utils/adt/filename.c
Normal file
@@ -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));
|
||||
}
|
||||
1320
src/backend/utils/adt/float.c
Normal file
1320
src/backend/utils/adt/float.c
Normal file
File diff suppressed because it is too large
Load Diff
1947
src/backend/utils/adt/geo-ops.c
Normal file
1947
src/backend/utils/adt/geo-ops.c
Normal file
File diff suppressed because it is too large
Load Diff
124
src/backend/utils/adt/geo-selfuncs.c
Normal file
124
src/backend/utils/adt/geo-selfuncs.c
Normal file
@@ -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);
|
||||
}
|
||||
343
src/backend/utils/adt/int.c
Normal file
343
src/backend/utils/adt/int.c
Normal file
@@ -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);
|
||||
}
|
||||
225
src/backend/utils/adt/like.c
Normal file
225
src/backend/utils/adt/like.c
Normal file
@@ -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);
|
||||
}
|
||||
96
src/backend/utils/adt/misc.c
Normal file
96
src/backend/utils/adt/misc.c
Normal file
@@ -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);
|
||||
}
|
||||
866
src/backend/utils/adt/nabstime.c
Normal file
866
src/backend/utils/adt/nabstime.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
198
src/backend/utils/adt/name.c
Normal file
198
src/backend/utils/adt/name.c
Normal file
@@ -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;
|
||||
}
|
||||
124
src/backend/utils/adt/not_in.c
Normal file
124
src/backend/utils/adt/not_in.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
401
src/backend/utils/adt/numutils.c
Normal file
401
src/backend/utils/adt/numutils.c
Normal file
@@ -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);
|
||||
}
|
||||
127
src/backend/utils/adt/oid.c
Normal file
127
src/backend/utils/adt/oid.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
120
src/backend/utils/adt/oidint2.c
Normal file
120
src/backend/utils/adt/oidint2.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
111
src/backend/utils/adt/oidint4.c
Normal file
111
src/backend/utils/adt/oidint4.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
123
src/backend/utils/adt/oidname.c
Normal file
123
src/backend/utils/adt/oidname.c
Normal file
@@ -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;
|
||||
}
|
||||
343
src/backend/utils/adt/regexp.c
Normal file
343
src/backend/utils/adt/regexp.c
Normal file
@@ -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));
|
||||
}
|
||||
|
||||
159
src/backend/utils/adt/regproc.c
Normal file
159
src/backend/utils/adt/regproc.c
Normal file
@@ -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 ========== */
|
||||
|
||||
585
src/backend/utils/adt/selfuncs.c
Normal file
585
src/backend/utils/adt/selfuncs.c
Normal file
@@ -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));
|
||||
}
|
||||
164
src/backend/utils/adt/sets.c
Normal file
164
src/backend/utils/adt/sets.c
Normal file
@@ -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;
|
||||
}
|
||||
92
src/backend/utils/adt/tid.c
Normal file
92
src/backend/utils/adt/tid.c
Normal file
@@ -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;
|
||||
}
|
||||
496
src/backend/utils/adt/varchar.c
Normal file
496
src/backend/utils/adt/varchar.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
488
src/backend/utils/adt/varlena.c
Normal file
488
src/backend/utils/adt/varlena.c
Normal file
@@ -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);
|
||||
}
|
||||
166
src/backend/utils/array.h
Normal file
166
src/backend/utils/array.h
Normal file
@@ -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 */
|
||||
39
src/backend/utils/bit.h
Normal file
39
src/backend/utils/bit.h
Normal file
@@ -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 */
|
||||
433
src/backend/utils/builtins.h
Normal file
433
src/backend/utils/builtins.h
Normal file
@@ -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 */
|
||||
15
src/backend/utils/cache/Makefile.inc
vendored
Normal file
15
src/backend/utils/cache/Makefile.inc
vendored
Normal file
@@ -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
|
||||
|
||||
1023
src/backend/utils/cache/catcache.c
vendored
Normal file
1023
src/backend/utils/cache/catcache.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
297
src/backend/utils/cache/fcache.c
vendored
Normal file
297
src/backend/utils/cache/fcache.c
vendored
Normal file
@@ -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!");
|
||||
}
|
||||
}
|
||||
612
src/backend/utils/cache/inval.c
vendored
Normal file
612
src/backend/utils/cache/inval.c
vendored
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
484
src/backend/utils/cache/lsyscache.c
vendored
Normal file
484
src/backend/utils/cache/lsyscache.c
vendored
Normal file
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
77
src/backend/utils/cache/rel.c
vendored
Normal file
77
src/backend/utils/cache/rel.c
vendored
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
1795
src/backend/utils/cache/relcache.c
vendored
Normal file
1795
src/backend/utils/cache/relcache.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
630
src/backend/utils/cache/syscache.c
vendored
Normal file
630
src/backend/utils/cache/syscache.c
vendored
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
85
src/backend/utils/catcache.h
Normal file
85
src/backend/utils/catcache.h
Normal file
@@ -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 */
|
||||
64
src/backend/utils/datum.h
Normal file
64
src/backend/utils/datum.h
Normal file
@@ -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 */
|
||||
53
src/backend/utils/dynamic_loader.h
Normal file
53
src/backend/utils/dynamic_loader.h
Normal file
@@ -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 */
|
||||
38
src/backend/utils/elog.h
Normal file
38
src/backend/utils/elog.h
Normal file
@@ -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 */
|
||||
14
src/backend/utils/error/Makefile.inc
Normal file
14
src/backend/utils/error/Makefile.inc
Normal file
@@ -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
|
||||
64
src/backend/utils/error/assert.c
Normal file
64
src/backend/utils/error/assert.c
Normal file
@@ -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);
|
||||
}
|
||||
237
src/backend/utils/error/elog.c
Normal file
237
src/backend/utils/error/elog.c
Normal file
@@ -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
|
||||
183
src/backend/utils/error/exc.c
Normal file
183
src/backend/utils/error/exc.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
28
src/backend/utils/error/excabort.c
Normal file
28
src/backend/utils/error/excabort.c
Normal file
@@ -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
|
||||
}
|
||||
64
src/backend/utils/error/excid.c
Normal file
64
src/backend/utils/error/excid.c
Normal file
@@ -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 */
|
||||
40
src/backend/utils/error/format.c
Normal file
40
src/backend/utils/error/format.c
Normal file
@@ -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);
|
||||
}
|
||||
101
src/backend/utils/exc.h
Normal file
101
src/backend/utils/exc.h
Normal file
@@ -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 */
|
||||
31
src/backend/utils/excid.h
Normal file
31
src/backend/utils/excid.h
Normal file
@@ -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 */
|
||||
55
src/backend/utils/fcache.h
Normal file
55
src/backend/utils/fcache.h
Normal file
@@ -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 */
|
||||
19
src/backend/utils/fcache2.h
Normal file
19
src/backend/utils/fcache2.h
Normal file
@@ -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 */
|
||||
15
src/backend/utils/fmgr/Makefile.inc
Normal file
15
src/backend/utils/fmgr/Makefile.inc
Normal file
@@ -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
|
||||
|
||||
269
src/backend/utils/fmgr/dfmgr.c
Normal file
269
src/backend/utils/fmgr/dfmgr.c
Normal file
@@ -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);
|
||||
}
|
||||
254
src/backend/utils/fmgr/fmgr.c
Normal file
254
src/backend/utils/fmgr/fmgr.c
Normal file
@@ -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));
|
||||
}
|
||||
29
src/backend/utils/fmgrtab.h
Normal file
29
src/backend/utils/fmgrtab.h
Normal file
@@ -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 */
|
||||
248
src/backend/utils/geo-decls.h
Normal file
248
src/backend/utils/geo-decls.h
Normal file
@@ -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 */
|
||||
14
src/backend/utils/hash/Makefile.inc
Normal file
14
src/backend/utils/hash/Makefile.inc
Normal file
@@ -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
|
||||
868
src/backend/utils/hash/dynahash.c
Normal file
868
src/backend/utils/hash/dynahash.c
Normal file
@@ -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);
|
||||
}
|
||||
156
src/backend/utils/hash/hashfn.c
Normal file
156
src/backend/utils/hash/hashfn.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
141
src/backend/utils/hsearch.h
Normal file
141
src/backend/utils/hsearch.h
Normal file
@@ -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 */
|
||||
14
src/backend/utils/init/Makefile.inc
Normal file
14
src/backend/utils/init/Makefile.inc
Normal file
@@ -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
|
||||
45
src/backend/utils/init/enbl.c
Normal file
45
src/backend/utils/init/enbl.c
Normal file
@@ -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));
|
||||
}
|
||||
251
src/backend/utils/init/findbe.c
Normal file
251
src/backend/utils/init/findbe.c
Normal file
@@ -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);
|
||||
}
|
||||
108
src/backend/utils/init/globals.c
Normal file
108
src/backend/utils/init/globals.c
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
167
src/backend/utils/init/magic.c
Normal file
167
src/backend/utils/init/magic.c
Normal file
@@ -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);
|
||||
}
|
||||
378
src/backend/utils/init/miscinit.c
Normal file
378
src/backend/utils/init/miscinit.c
Normal file
@@ -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);
|
||||
}
|
||||
648
src/backend/utils/init/postinit.c
Normal file
648
src/backend/utils/init/postinit.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
56
src/backend/utils/inval.h
Normal file
56
src/backend/utils/inval.h
Normal file
@@ -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 */
|
||||
|
||||
40
src/backend/utils/lselect.h
Normal file
40
src/backend/utils/lselect.h
Normal file
@@ -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 */
|
||||
45
src/backend/utils/lsyscache.h
Normal file
45
src/backend/utils/lsyscache.h
Normal file
@@ -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 */
|
||||
|
||||
56
src/backend/utils/mcxt.h
Normal file
56
src/backend/utils/mcxt.h
Normal file
@@ -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 */
|
||||
281
src/backend/utils/memutils.h
Normal file
281
src/backend/utils/memutils.h
Normal file
@@ -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 */
|
||||
15
src/backend/utils/mmgr/Makefile.inc
Normal file
15
src/backend/utils/mmgr/Makefile.inc
Normal file
@@ -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
|
||||
|
||||
381
src/backend/utils/mmgr/aset.c
Normal file
381
src/backend/utils/mmgr/aset.c
Normal file
@@ -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);
|
||||
}
|
||||
510
src/backend/utils/mmgr/mcxt.c
Normal file
510
src/backend/utils/mmgr/mcxt.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
173
src/backend/utils/mmgr/oset.c
Normal file
173
src/backend/utils/mmgr/oset.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
117
src/backend/utils/mmgr/palloc.c
Normal file
117
src/backend/utils/mmgr/palloc.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
980
src/backend/utils/mmgr/portalmem.c
Normal file
980
src/backend/utils/mmgr/portalmem.c
Normal file
@@ -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)));
|
||||
}
|
||||
|
||||
25
src/backend/utils/module.h
Normal file
25
src/backend/utils/module.h
Normal file
@@ -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 */
|
||||
165
src/backend/utils/nabstime.h
Normal file
165
src/backend/utils/nabstime.h
Normal file
@@ -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 */
|
||||
52
src/backend/utils/oidcompos.h
Normal file
52
src/backend/utils/oidcompos.h
Normal file
@@ -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 */
|
||||
26
src/backend/utils/palloc.h
Normal file
26
src/backend/utils/palloc.h
Normal file
@@ -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 */
|
||||
|
||||
97
src/backend/utils/portal.h
Normal file
97
src/backend/utils/portal.h
Normal file
@@ -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 */
|
||||
86
src/backend/utils/psort.h
Normal file
86
src/backend/utils/psort.h
Normal file
@@ -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 */
|
||||
170
src/backend/utils/rel.h
Normal file
170
src/backend/utils/rel.h
Normal file
@@ -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 */
|
||||
23
src/backend/utils/rel2.h
Normal file
23
src/backend/utils/rel2.h
Normal file
@@ -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 */
|
||||
47
src/backend/utils/relcache.h
Normal file
47
src/backend/utils/relcache.h
Normal file
@@ -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 */
|
||||
22
src/backend/utils/sets.h
Normal file
22
src/backend/utils/sets.h
Normal file
@@ -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 */
|
||||
14
src/backend/utils/sort/Makefile.inc
Normal file
14
src/backend/utils/sort/Makefile.inc
Normal file
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user