First release to git
authorMarc Munro <marc@bloodnok.com>
Sat, 23 Jul 2011 00:27:05 +0000 (17:27 -0700)
committerMarc Munro <marc@bloodnok.com>
Sat, 23 Jul 2011 00:27:05 +0000 (17:27 -0700)
51 files changed:
COPYRIGHT [new file with mode: 0644]
GNUmakefile [new file with mode: 0644]
LICENSE [new file with mode: 0644]
demo/GNUmakefile [new file with mode: 0644]
demo/Makefile [new file with mode: 0644]
demo/veil_demo.png [new file with mode: 0644]
demo/veil_demo.xml [new file with mode: 0644]
docs/Doxyfile [new file with mode: 0644]
docs/GNUmakefile [new file with mode: 0644]
docs/Makefile [new file with mode: 0644]
docs/demo.xml [new file with mode: 0644]
regress/GNUmakefile [new file with mode: 0644]
regress/Makefile [new file with mode: 0644]
regress/regress.log [new file with mode: 0644]
regress/regress.sh [new file with mode: 0755]
regress/regress_data.sql [new file with mode: 0644]
regress/regress_tables.sql [new file with mode: 0644]
src/GNUmakefile [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/veil_bitmap.c [new file with mode: 0644]
src/veil_bitmap.d [new file with mode: 0644]
src/veil_config.c [new file with mode: 0644]
src/veil_config.d [new file with mode: 0644]
src/veil_datatypes.c [new file with mode: 0644]
src/veil_datatypes.d [new file with mode: 0644]
src/veil_datatypes.h [new file with mode: 0644]
src/veil_demo.sql [new file with mode: 0644]
src/veil_funcs.h [new file with mode: 0644]
src/veil_interface.c [new file with mode: 0644]
src/veil_interface.d [new file with mode: 0644]
src/veil_interface.sqs [new file with mode: 0644]
src/veil_mainpage.c [new file with mode: 0644]
src/veil_mainpage.d [new file with mode: 0644]
src/veil_query.c [new file with mode: 0644]
src/veil_query.d [new file with mode: 0644]
src/veil_serialise.c [new file with mode: 0644]
src/veil_serialise.d [new file with mode: 0644]
src/veil_shmem.c [new file with mode: 0644]
src/veil_shmem.d [new file with mode: 0644]
src/veil_shmem.h [new file with mode: 0644]
src/veil_utils.c [new file with mode: 0644]
src/veil_utils.d [new file with mode: 0644]
src/veil_variables.c [new file with mode: 0644]
src/veil_variables.d [new file with mode: 0644]
src/veil_version.h [new file with mode: 0644]
tools/psql_funcs.sh [new file with mode: 0644]
veil--1.0.sql [new file with mode: 0644]
veil.control [new file with mode: 0644]
veil_demo--1.0.sql [new file with mode: 0644]
veil_demo.control [new file with mode: 0644]
veil_demo.mk [new file with mode: 0644]

diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644 (file)
index 0000000..2f6cb89
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,9 @@
+Veil - A data security API for the  PostgreSQL Database Management System
+
+Copyright (c) 2005 - 2011
+               Marc Munro, 
+           Munro Information Services Ltd, 
+           Vancouver BC, Canada
+
+This software is released under the BSD License as described in the 
+associated LICENSE file distributed with this software.
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644 (file)
index 0000000..5ef14d0
--- /dev/null
@@ -0,0 +1,134 @@
+# GNUmakefile
+#
+#      PGXS-based makefile for Veil
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# For a list of targets use make help.
+# 
+
+all:
+
+SUBDIRS = src regress docs demo
+include $(SUBDIRS:%=%/Makefile)
+
+BUILD_DIR = $(shell pwd)
+MODULE_big = veil
+OBJS = $(SOURCES:%.c=%.o)
+DEPS = $(SOURCES:%.c=%.d)
+EXTRA_CLEAN = $(SRC_CLEAN)
+EXTENSION=veil
+MODULEDIR=extension/veil
+VEIL_VERSION = $(shell \
+    grep default_version veil.control | sed 's/[^0-9.]*\([0-9.]*\).*/\1/')
+
+VEIL_CONTROL = veil--$(VEIL_VERSION).sql
+
+DATA = $(wildcard veil--*.sql)
+ALLDOCS = $(wildcard docs/html/*)
+# Only define DOCS (for the install target) if there are some.
+ifneq "$(ALLDOCS)" ""
+       DOCS = $(ALLDOCS)
+endif
+
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+include $(DEPS)
+
+
+# Build per-source dependency files for inclusion
+# This ignores header files and any other non-local files (such as
+# postgres include files).  Since I don't know if this will work
+# on non-Unix platforms, we will ship veil with the dep files
+# in place).  This target is mostly for maintainers who may wish
+# to rebuild dep files.
+%.d: %.c
+   @echo Recreating $@
+   @$(SHELL) -ec "$(CC) -MM -MT $*.o $(CPPFLAGS) $< | \
+       xargs -n 1 | grep '^[^/]' | \
+       sed -e '1,$$ s/$$/ \\\\/' -e '$$ s/ \\\\$$//' \
+           -e '2,$$ s/^/  /' | \
+       sed 's!$*.o!& $@!g'" > $@
+
+# Target used by recursive call from deps target below.  This ensures
+# that make deps always rebuilds the dep files even if they are up to date.
+make_deps: $(DEPS)
+
+# Target that rebuilds all dep files unconditionally.  There should be a
+# simpler way to do this using .PHONY but I can't figure out how.
+deps: 
+   rm -f $(DEPS)
+   $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" make_deps
+
+# Recursive make is used to cope with pgxs' inability to build more than
+# one library per makefile.   We recursively use this makefile to build
+# the veil_trial shared library.
+all: $(DATA)
+
+# Define some variables for the following tarball targets.
+tarball tarball_clean: VEIL_DIR=veil_$(VEIL_VERSION)
+
+# Create a version numbered tarball of source (including deps), tools, 
+# and possibly docs.
+tarball:
+   @rm -rf $(VEIL_DIR)
+   @if test ! -r docs/html/index.html; then \
+       echo "You may want to make the docs first"; fi
+   @mkdir -p $(VEIL_DIR)/src $(VEIL_DIR)/regress
+   @cp CO* LI* GNU* veil--*sql veil.control veil_demo* $(VEIL_DIR)
+   @cp src/*akefile src/*.[cdh] src/*sqs $(VEIL_DIR)/src
+   @cp regress/*akefile regress/*.sh regress/*sql $(VEIL_DIR)/regress
+   @ln -s `pwd`/tools $(VEIL_DIR)/tools
+   @ln -s `pwd`/demo $(VEIL_DIR)/demo
+   @ln -s `pwd`/docs $(VEIL_DIR)/docs
+   @echo Creating veil_$(VEIL_VERSION).tgz...
+   @tar czhf veil_$(VEIL_VERSION).tgz $(VEIL_DIR)
+   @rm -rf $(VEIL_DIR)
+
+# Cleanup after creating a tarball
+tarball_clean:
+   rm -rf $(VEIL_DIR) veil_$(VEIL_VERSION).tgz
+
+# Ensure that tarball tmp files and dirs are removed by the clean target
+clean: tarball_clean
+
+ifndef VARIANT
+# Explicit target for veil_trial library.  Only required for non-variant
+# builds as the target is otherwise defined automatically.
+$(addsuffix $(DLSUFFIX), veil_trial):  $(TRIAL_SOURCES)
+   @if [ "x$(VARIANT)" = "x" ]; then \
+       $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" VARIANT=veil_trial $@; \
+   fi
+endif
+
+# Install veil_demo as well as veil
+install: demo_install
+demo_install:
+   $(MAKE) MAKEFLAGS=$(MAKEFLAGS) -f veil_demo.mk install
+
+# Uninstall veil_demo as well as veil
+uninstall: demo_uninstall
+demo_uninstall:
+   $(MAKE) MAKEFLAGS=$(MAKEFLAGS) -f veil_demo.mk uninstall
+
+# Provide a list of the targets buildable by this makefile.
+list help:
+   @echo -e "\n\
+ Major targets for this makefile are:\n\n\
+ all       - Build veil libraries, without docs\n\
+ check     - Build veil and run regression tests\n\
+ clean     - remove target and object files\n\
+ deps      - Recreate the xxx.d dependency files\n\
+ docs      - Build veil html docs (requires doxygen and dot)\n\
+ help      - show this list of major targets\n\
+ install   - Install veil\n\
+ list      - show this list of major targets\n\
+ regress   - same as check\n\
+ regress_clean - clean up after regression tests - (drop regressdb)\n\
+ tarball   - create a tarball of the sources and documents\n\
+ uninstall - Undo the install\n\
+\n\
+"
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..cca2a5c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/demo/GNUmakefile b/demo/GNUmakefile
new file mode 100644 (file)
index 0000000..bd5dd5c
--- /dev/null
@@ -0,0 +1,14 @@
+# ----------
+# GNUmakefile
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# ----------
+#
+
+all:
+
+%::
+   cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
\ No newline at end of file
diff --git a/demo/Makefile b/demo/Makefile
new file mode 100644 (file)
index 0000000..01d5004
--- /dev/null
@@ -0,0 +1,38 @@
+# Makefile
+#
+#      Makefile for the demo database components
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# Do not attempt to use this makefile directly: its targets are available
+# and should be built from the main GNUmakefile in the parent directory.
+
+# ----------
+# 
+.PHONY: demo_all demo_clean demo dropdemo
+
+DEMO_DIR = demo
+DEMO_GARBAGE = $(garbage:%=$(DEMO_DIR)/%)
+
+# This generates the xml definition of the demo database directly from the
+# database.  Or would, if it worked properly.
+#demo.xml: $(shell ls demo/*sql)
+#  -$(DB2XML) | awk '/<schema/, /<\/schema>/' > $@
+
+# Autograph from cf consulting.  Generates an input file for dot from an 
+# xml schema definition
+AUTOGRAPH_XSL = $(HOME)/bin/autograph.xsl
+AUTOGRAPH = $(XSLTPROC) --xinclude $(AUTOGRAPH_XSL)
+
+demo/veil_demo.png: demo/veil_demo.xml
+   $(AUTOGRAPH) demo/veil_demo.xml > demo/demo.dot
+   $(DOT) $@ demo/demo.dot
+   @rm -f demo/demo.dot
+
+# Clean this directory and ensure regression test db is removed.
+demo_clean:
+   rm -f $(DEMO_GARBAGE) $(DEMO_DIR)/demo_build.log
+
+demo_distclean: demo_clean
diff --git a/demo/veil_demo.png b/demo/veil_demo.png
new file mode 100644 (file)
index 0000000..4909672
Binary files /dev/null and b/demo/veil_demo.png differ
diff --git a/demo/veil_demo.xml b/demo/veil_demo.xml
new file mode 100644 (file)
index 0000000..9988b83
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+
+<autograph:report
+    xmlns:xi="http://www.w3.org/2003/XInclude"
+    xmlns:autograph="http://cfconsulting.ca/autograph/1.0" >
+
+    <autograph:graphparams>
+   <autograph:tablemode name="simple" />
+    </autograph:graphparams>
+
+    <autograph:includetables>
+   <autograph:table name="privileges" schema="demo" />
+   <autograph:table name="roles" schema="demo" />
+   <autograph:table name="role_privileges" schema="demo" />
+   <autograph:table name="role_roles" schema="demo" />
+   <autograph:table name="persons" schema="demo" />
+   <autograph:table name="person_roles" schema="demo" />
+   <autograph:table name="projects" schema="demo" />
+   <autograph:table name="assignments" schema="demo" />
+   <autograph:table name="detail_types" schema="demo" />
+   <autograph:table name="person_details" schema="demo" />
+   <autograph:table name="project_details" schema="demo" />
+    </autograph:includetables>
+
+    <xi:include href="./demo.xml" />
+</autograph:report>
+<!-- arch-tag: 754fde1b-e801-4666-93a3-e38d8c0665d0 -->
diff --git a/docs/Doxyfile b/docs/Doxyfile
new file mode 100644 (file)
index 0000000..83fddbe
--- /dev/null
@@ -0,0 +1,1298 @@
+# Doxyfile.in
+#
+#      Autoconf file for generation of Doxyfile
+#
+#      Copyright (c) 2005, 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+
+
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for Veil
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file that 
+# follow. The default is UTF-8 which is also the encoding used for all text before 
+# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into 
+# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of 
+# possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "Veil"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = docs
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of source 
+# files, where putting all generated files in the same directory would otherwise 
+# cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, 
+# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, 
+# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, 
+# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, 
+# Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is used 
+# as the annotated text. Otherwise, the brief description is used as-is. If left 
+# blank, the following values are used ("$name" is automatically replaced with the 
+# name of the entity): "The $name class" "The $name widget" "The $name file" 
+# "is" "provides" "specifies" "contains" "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = 
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explicit @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be extracted 
+# and appear in the documentation as a namespace called 'anonymous_namespace{file}', 
+# where file will be replaced with the base name of the file that contains the anonymous 
+# namespace. By default anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation.
+
+SHOW_DIRECTORIES       = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from the 
+# version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = ./src
+
+# This tag can be used to specify the character encoding of the source files that 
+# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default 
+# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. 
+# See http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm
+
+FILE_PATTERNS          = *.c *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the output. 
+# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, 
+# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = ./demo/ ./src/
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = ./demo/
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = 
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = /usr/include/postgresql/ \
+                /usr/include/postgresql/8.4/server/ \
+            /usr/include/postgresql/8.4/server//utils
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED = PG_FUNCTION_INFO_V1(x)= \
+        DOXYGEN_SHOULD_SKIP_THIS \
+        PG_MODULE_MAGIC
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = NO
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superseded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to 
+# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to 
+# specify the directory where the mscgen tool resides. If left empty the tool is assumed to 
+# be found in the default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = NO
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = YES
+
+# If the CALLER_GRAPH, SOURCE_BROWSER and HAVE_DOT tags are set to YES
+# then doxygen will generate a caller dependency graph for every global
+# function or class method.  Note that enabling this option will
+# significantly increase the time of a run.  So in most cases it will be
+# better to enable caller graphs for selected functions only using the
+# \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = NO
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = NO
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the number 
+# of direct children of the root node in a graph is already larger than 
+# MAX_DOT_GRAPH_NOTES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 100
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, which results in a white background. 
+# Warning: Depending on the platform used, enabling this option may lead to 
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to 
+# read).
+
+DOT_TRANSPARENT        = YES
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = NO
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/docs/GNUmakefile b/docs/GNUmakefile
new file mode 100644 (file)
index 0000000..663dc3b
--- /dev/null
@@ -0,0 +1,14 @@
+# ----------
+# GNUmakefile
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# ----------
+#
+
+all:
+
+%::
+   cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644 (file)
index 0000000..6907c6e
--- /dev/null
@@ -0,0 +1,36 @@
+# Makefile
+#
+#      Makefile for docs directory of Veil
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+#
+# Do not attempt to use this makefile explicitly: its targets are available
+# and should be built from the main GNUmakefile in the parent directory.
+# 
+
+.PHONY: docs_clean docs docs_check
+
+DOCS_DIR = docs
+DOCS_GARBAGE = $(garbage:%=$(DOCS_DIR)/%)
+
+
+docs:  docs/html
+
+docs/html: $(SOURCES) $(HEADERS) demo/veil_demo.png
+   doxygen $(DOCS_DIR)/Doxyfile || \
+       (echo "Doxygen fails: is it installed?"; exit 2)
+   @rm -f NO   # Don't know why this file is generated but it can go.
+
+# Clean this directory and ensure generated docs are removed
+clean: docs_clean
+
+docs_clean:
+   @echo Cleaning docs...
+   @rm -f $(DOCS_GARBAGE) NO
+   @rm -rf $(DOCS_DIR)/html
+
+docs_distclean: docs_clean
+   @rm -f Doxyfile
\ No newline at end of file
diff --git a/docs/demo.xml b/docs/demo.xml
new file mode 100644 (file)
index 0000000..daba95c
--- /dev/null
@@ -0,0 +1,123 @@
+<schema generated="yes">
+  <table name="role_privileges">
+    <column name="role_id" type="integer" key="1"/>
+    <column name="role_id" type="integer" null="no" key="1"/>
+    <column name="privilege_id" type="integer" key="2"/>
+    <column name="privilege_id" type="integer" null="no" key="2"/>
+    <relations>
+        <relation name="role_privilege__role_fk" column="role_id" table="roles" fk="role_id"/>
+        <relation name="role_privilege__priv_fk" column="privilege_id" table="privileges" fk="privilege_id"/>
+    </relations>
+  </table>
+  <table name="role_roles">
+    <column name="role_id" type="integer" key="1"/>
+    <column name="role_id" type="integer" null="no" key="1"/>
+    <column name="has_role_id" type="integer" key="2"/>
+    <column name="has_role_id" type="integer" null="no" key="2"/>
+    <relations>
+        <relation name="role_role__role_fk" column="role_id" table="roles" fk="role_id"/>
+        <relation name="role_role__has_role_fk" column="has_role_id" table="roles" fk="role_id"/>
+    </relations>
+  </table>
+  <table name="privileges">
+    <column name="privilege_id" type="integer" key="1"/>
+    <column name="privilege_id" type="integer" null="no" key="1"/>
+    <column name="privilege_name" type="varchar" size="80"/>
+    <column name="privilege_name" type="varchar" size="80" null="no"/>
+  </table>
+  <table name="assignments">
+    <column name="project_id" type="integer" key="1"/>
+    <column name="project_id" type="integer" null="no" key="1"/>
+    <column name="person_id" type="integer" key="2"/>
+    <column name="person_id" type="integer" null="no" key="2"/>
+    <column name="role_id" type="integer"/>
+    <column name="role_id" type="integer" null="no"/>
+    <relations>
+        <relation name="assignment__project_fk" column="project_id" table="projects" fk="project_id"/>
+        <relation name="assignment__person_fk" column="person_id" table="persons" fk="person_id"/>
+        <relation name="assignment__role_fk" column="role_id" table="roles" fk="role_id"/>
+    </relations>
+  </table>
+  <table name="person_roles">
+    <column name="person_id" type="integer" key="1"/>
+    <column name="person_id" type="integer" null="no" key="1"/>
+    <column name="role_id" type="integer" key="2"/>
+    <column name="role_id" type="integer" null="no" key="2"/>
+    <relations>
+        <relation name="person_role__person_fk" column="person_id" table="persons" fk="person_id"/>
+        <relation name="person_role__role_fk" column="role_id" table="roles" fk="role_id"/>
+    </relations>
+  </table>
+  <table name="roles">
+    <column name="role_id" type="integer" key="1"/>
+    <column name="role_id" type="integer" null="no" key="1"/>
+    <column name="role_name" type="varchar" size="80"/>
+    <column name="role_name" type="varchar" size="80" null="no"/>
+  </table>
+  <table name="projects">
+    <column name="project_id" type="integer" key="1"/>
+    <column name="project_id" type="integer" null="no" key="1"/>
+    <column name="project_name" type="varchar" size="80"/>
+    <column name="project_name" type="varchar" size="80" null="no"/>
+  </table>
+  <table name="project_details">
+    <column name="project_id" type="integer" key="1"/>
+    <column name="project_id" type="integer" null="no" key="1"/>
+    <column name="detail_type_id" type="integer" key="2"/>
+    <column name="detail_type_id" type="integer" null="no" key="2"/>
+    <column name="value" type="text"/>
+    <column name="value" type="text" null="no"/>
+    <relations>
+        <relation name="project_detail__project_fk" column="project_id" table="projects" fk="project_id"/>
+        <relation name="project_detail__detail_fk" column="detail_type_id" table="detail_types" fk="detail_type_id"/>
+    </relations>
+  </table>
+  <table name="persons">
+    <column name="person_id" type="integer" key="1"/>
+    <column name="person_id" type="integer" null="no" key="1"/>
+    <column name="person_name" type="varchar" size="80"/>
+    <column name="person_name" type="varchar" size="80" null="no"/>
+  </table>
+  <table name="person_details">
+    <column name="person_id" type="integer" key="1"/>
+    <column name="person_id" type="integer" null="no" key="1"/>
+    <column name="detail_type_id" type="integer" key="2"/>
+    <column name="detail_type_id" type="integer" null="no" key="2"/>
+    <column name="value" type="text"/>
+    <column name="value" type="text" null="no"/>
+    <relations>
+        <relation name="person_detail__person_fk" column="person_id" table="persons" fk="person_id"/>
+        <relation name="person_detail__detail_fk" column="detail_type_id" table="detail_types" fk="detail_type_id"/>
+    </relations>
+  </table>
+  <table name="detail_types">
+    <column name="detail_type_id" type="integer" key="1"/>
+    <column name="detail_type_id" type="integer" null="no" key="1"/>
+    <column name="required_privilege_id" type="integer"/>
+    <column name="required_privilege_id" type="integer" null="no"/>
+    <column name="detail_type_name" type="varchar" size="80"/>
+    <column name="detail_type_name" type="varchar" size="80" null="no"/>
+    <relations>
+        <relation name="detail_type__priv_fk" column="required_privilege_id" table="privileges" fk="privilege_id"/>
+    </relations>
+  </table>
+  <table name="my_global_privs">
+    <column name="privilege_id" type="integer"/>
+  </table>
+  <table name="my_personal_privs">
+    <column name="privilege_id" type="integer"/>
+  </table>
+  <table name="my_projects">
+    <column name="project_id" type="varchar"/>
+  </table>
+  <table name="my_project_privs">
+    <column name="project_id" type="integer"/>
+    <column name="privilege_id" type="integer"/>
+  </table>
+  <table name="my_privs">
+    <column name="context" type="text"/>
+    <column name="project" type="integer"/>
+    <column name="privilege_id" type="integer"/>
+    <column name="privilege_name" type="varchar" size="80"/>
+  </table>
+</schema>
diff --git a/regress/GNUmakefile b/regress/GNUmakefile
new file mode 100644 (file)
index 0000000..1bb84fc
--- /dev/null
@@ -0,0 +1,13 @@
+# ----------
+# GNUmakefile
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+#
+
+DEFAULT:
+
+%::
+   cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
\ No newline at end of file
diff --git a/regress/Makefile b/regress/Makefile
new file mode 100644 (file)
index 0000000..603e79f
--- /dev/null
@@ -0,0 +1,27 @@
+# Makefile
+#
+#      Makefile for regress directory of Veil
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# Do not attempt to use this makefile explicitly: its targets are available
+# and should be built from the main GNUmakefile in the parent directory.
+# 
+
+.PHONY: regress check
+
+REGRESS_DIR = regress
+REGRESS_CLEAN = $(garbage:%=$(REGRESS_DIR)/%)
+
+
+# Run regression test
+regress check: all
+   @cd $(REGRESS_DIR) 2>/dev/null; \
+    ./regress.sh
+
+clean: regress_clean
+
+regress_clean:
+   @cd $(REGRESS_DIR) 2>/dev/null; ./regress.sh -d
diff --git a/regress/regress.log b/regress/regress.log
new file mode 100644 (file)
index 0000000..64a84ef
--- /dev/null
@@ -0,0 +1,1936 @@
+- Creating regression test objects...
+- Loading the veil extension...
+NOTICE:  extension "veil" does not exist, skipping
+DROP EXTENSION
+CREATE EXTENSION
+CREATE FUNCTION
+- Creating tables...
+- ...privileges...
+CREATE TABLE
+psql:./regress_tables.sql:20: NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "privilege__pk" for table "privileges"
+ALTER TABLE
+- ...role...
+CREATE TABLE
+psql:./regress_tables.sql:28: NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "role__pk" for table "roles"
+ALTER TABLE
+- ...role_privileges...
+CREATE TABLE
+psql:./regress_tables.sql:37: NOTICE:  ALTER TABLE / ADD PRIMARY KEY will create implicit index "role_privilege__pk" for table "role_privileges"
+ALTER TABLE
+ALTER TABLE
+ALTER TABLE
+- Populating tables...
+BEGIN
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+INSERT 0 1
+COMMIT
+Performing regression tests...
+-
+- ...test set 1: variables...
+PREP
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 1.1 ~ #1 *| *VEIL_SHMEMCTL#Initial variable listing
+     1 | VEIL_SHMEMCTL
+
+- NOTE Test 1.1 will fail (correctly) if this is not a freshly created database
+-
+TEST 1.2 = #42#create session int4
+            42
+
+TEST 1.3 = #42#retrieve session int4
+            42
+
+TEST 1.4 ~ #sess_int4 *\| *Int4 *\| *f#list defined variables
+ VEIL_SHMEMCTL | ShmemCtl | t
+ sess_int4     | Int4     | f
+
+TEST 1.5 ~ #shared_int4 *\| *Undefined *\| *t#create shared int4
+ f
+
+ VEIL_SHMEMCTL | ShmemCtl  | t
+ shared_int4   | Undefined | t
+ sess_int4     | Int4      | f
+
+TEST 1.6 = #99#get shared int4
+       99
+
+TEST 1.7 ~ #shared_int4 *\| *Int4 *\| *t#list defined variables
+ VEIL_SHMEMCTL | ShmemCtl | t
+ shared_int4   | Int4     | t
+ sess_int4     | Int4     | f
+
+TEST 1.8 ~ #ERROR.*mismatch#access non-existant session range
+ERROR:  type mismatch in sess_range: expected Range, got Undefined
+DETAIL:  Variable sess_range is not of the expected type.
+TEST 1.9 = #14#create session range
+              14
+
+PREP
+ f
+
+TEST 1.10 = #1#create shared ranged
+               1
+
+TEST 1.11 ~ #5 *| *3#list defined variables
+     5 |      3
+
+- NOTE Test 1.11 will fail (correctly) if this is not a freshly created database
+-
+TEST 1.12 ~ #11 *| *24#show session range
+  11 |  24
+
+TEST 1.13 ~ #17 *| *17#show shared range
+  17 |  17
+
+TEST 1.14 ~ #ERROR.*mismatch#fetch against undefined int4 array
+ERROR:  type mismatch in sess_int4array: expected Int4Array, got Undefined
+DETAIL:  Variable sess_int4array is not of the expected type.
+TEST 1.15 ~ #ERROR.*mismatch#set against undefined int4 array
+ERROR:  type mismatch in sess_int4array: expected Int4Array, got Undefined
+DETAIL:  Variable sess_int4array is not of the expected type.
+TEST 1.16 = #t#create session int4 array
+ t
+
+TEST 1.17 ~ #range error#attempt to set arrary element out of range
+ERROR:  Int4ArraySet range error
+DETAIL:  Index (12) not in range 17..17.  
+TEST 1.18 = #14#set int4 array element
+                 14
+
+TEST 1.19 = #14#fetch int4 array element
+                 14
+
+PREP
+ t
+
+TEST 1.20 = #0#clear array and fetch element
+                  0
+
+PREP
+ f
+
+ t
+
+TEST 1.21 = #14#define and fetch from shared int4 array
+                 14
+
+TEST 1.22 = #14#shared int4 array get
+                 14
+
+PREP
+              14
+
+ t
+
+ t
+
+                 24
+
+                 11
+
+-
+- ...test set 1a: variables (second process)...
+TEST 1.23 ~ #4 *| *4#list all shared variables
+     4 |      4
+
+- NOTE Test 1.23 may fail if this is not a freshly created database
+-
+TEST 1.24 = #99#get shared int4
+       99
+
+TEST 1.25 = #0#Before de-serialising session variables
+       0
+
+PREP
+                1
+                1
+                1
+                1
+                1
+
+TEST 1.25 = #5#Checking session variables are de-serialised
+       5
+
+TEST 1.26 ~ #17 *| *17#Checking Range of range variable
+  17 |  17
+
+TEST 1.27 ~ #11 *| *24#Checking Range of range2 variable
+Performing regression tests...
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 1.1 ~ #1 *| *VEIL_SHMEMCTL#Initial variable listing
+     1 | VEIL_SHMEMCTL
+MATCH ~ "1 | VEIL_SHMEMCTL"
+
+PASS
+END TEST 1.1
+
+TEST 1.2 = #42#create session int4
+            42
+MATCH = "42"
+
+PASS
+END TEST 1.2
+
+TEST 1.3 = #42#retrieve session int4
+            42
+MATCH = "42"
+
+PASS
+END TEST 1.3
+
+TEST 1.4 ~ #sess_int4 *\| *Int4 *\| *f#list defined variables
+ VEIL_SHMEMCTL | ShmemCtl | t
+ sess_int4     | Int4     | f
+MATCH ~ "sess_int4     | Int4     | f"
+
+PASS
+END TEST 1.4
+
+TEST 1.5 ~ #shared_int4 *\| *Undefined *\| *t#create shared int4
+ f
+
+ VEIL_SHMEMCTL | ShmemCtl  | t
+ shared_int4   | Undefined | t
+MATCH ~ "shared_int4   | Undefined | t"
+ sess_int4     | Int4      | f
+
+PASS
+END TEST 1.5
+
+TEST 1.6 = #99#get shared int4
+       99
+MATCH = "99"
+
+PASS
+END TEST 1.6
+
+TEST 1.7 ~ #shared_int4 *\| *Int4 *\| *t#list defined variables
+ VEIL_SHMEMCTL | ShmemCtl | t
+ shared_int4   | Int4     | t
+MATCH ~ "shared_int4   | Int4     | t"
+ sess_int4     | Int4     | f
+
+PASS
+END TEST 1.7
+
+TEST 1.8 ~ #ERROR.*mismatch#access non-existant session range
+ERROR:  type mismatch in sess_range: expected Range, got Undefined
+MATCH ~ "ERROR:  type mismatch in sess_range: expected Range, got Undefined"
+DETAIL:  Variable sess_range is not of the expected type.
+PASS
+END TEST 1.8
+
+TEST 1.9 = #14#create session range
+              14
+MATCH = "14"
+
+PASS
+END TEST 1.9
+
+ f
+
+TEST 1.10 = #1#create shared ranged
+               1
+MATCH = "1"
+
+PASS
+END TEST 1.10
+
+TEST 1.11 ~ #5 *| *3#list defined variables
+     5 |      3
+MATCH ~ "5 |      3"
+
+PASS
+END TEST 1.11
+
+TEST 1.12 ~ #11 *| *24#show session range
+  11 |  24
+MATCH ~ "11 |  24"
+
+PASS
+END TEST 1.12
+
+TEST 1.13 ~ #17 *| *17#show shared range
+  17 |  17
+MATCH ~ "17 |  17"
+
+PASS
+END TEST 1.13
+
+TEST 1.14 ~ #ERROR.*mismatch#fetch against undefined int4 array
+ERROR:  type mismatch in sess_int4array: expected Int4Array, got Undefined
+MATCH ~ "ERROR:  type mismatch in sess_int4array: expected Int4Array, got Undefined"
+DETAIL:  Variable sess_int4array is not of the expected type.
+PASS
+END TEST 1.14
+
+TEST 1.15 ~ #ERROR.*mismatch#set against undefined int4 array
+ERROR:  type mismatch in sess_int4array: expected Int4Array, got Undefined
+MATCH ~ "ERROR:  type mismatch in sess_int4array: expected Int4Array, got Undefined"
+DETAIL:  Variable sess_int4array is not of the expected type.
+PASS
+END TEST 1.15
+
+TEST 1.16 = #t#create session int4 array
+ t
+MATCH = "t"
+
+PASS
+END TEST 1.16
+
+TEST 1.17 ~ #range error#attempt to set arrary element out of range
+ERROR:  Int4ArraySet range error
+MATCH ~ "ERROR:  Int4ArraySet range error"
+DETAIL:  Index (12) not in range 17..17.  
+PASS
+END TEST 1.17
+
+TEST 1.18 = #14#set int4 array element
+                 14
+MATCH = "14"
+
+PASS
+END TEST 1.18
+
+TEST 1.19 = #14#fetch int4 array element
+                 14
+MATCH = "14"
+
+PASS
+END TEST 1.19
+
+ t
+
+TEST 1.20 = #0#clear array and fetch element
+                  0
+MATCH = "0"
+
+PASS
+END TEST 1.20
+
+ f
+
+ t
+
+TEST 1.21 = #14#define and fetch from shared int4 array
+                 14
+MATCH = "14"
+
+PASS
+END TEST 1.21
+
+TEST 1.22 = #14#shared int4 array get
+                 14
+MATCH = "14"
+
+PASS
+END TEST 1.22
+
+              14
+
+ t
+
+ t
+
+                 24
+
+                 11
+
+TEST 1.23 ~ #4 *| *4#list all shared variables
+     4 |      4
+MATCH ~ "4 |      4"
+
+PASS
+END TEST 1.23
+
+TEST 1.24 = #99#get shared int4
+       99
+MATCH = "99"
+
+PASS
+END TEST 1.24
+
+TEST 1.25 = #0#Before de-serialising session variables
+       0
+MATCH = "0"
+
+PASS
+END TEST 1.25
+
+                1
+                1
+                1
+                1
+                1
+
+TEST 1.25 = #5#Checking session variables are de-serialised
+       5
+MATCH = "5"
+
+PASS
+END TEST 1.25
+
+TEST 1.26 ~ #17 *| *17#Checking Range of range variable
+  17 |  17
+MATCH ~ "17 |  17"
+
+PASS
+END TEST 1.26
+
+TEST 1.27 ~ #11 *| *24#Checking Ran  11 |  24
+
+TEST 1.28 = #24#Checking array2 variable(1)
+                 24
+
+TEST 1.29 = #11#Checking array2 variable(2)
+                 11
+
+TEST 1.30 = #0#Checking array2 variable(3)
+                  0
+
+- ...test set 2: bitmaps...
+PREP
+              70
+
+               2
+
+ f
+
+TEST 2.1 ~ #OK#Initialise shared and session bitmaps
+ OK       | t                | t
+
+TEST 2.2 = #3#Populate shared bitmap
+     3
+
+TEST 2.3 = #2#Populate session bitmap
+     2
+
+PREP
+TEST 2.4 = #t#Test for known true values in session and shared bitmaps
+ t
+
+TEST 2.5 = #f#Test for known false value
+ f
+
+TEST 2.6 = #t#Clear sessionbitmap
+ t
+
+TEST 2.7 = #f#Test for absence of previous true value in shared bitmap
+ f
+
+TEST 2.8 ~ #20001.*|.*20070#Test bitmap range
+ 20001 | 20070
+
+TEST 2.9 ~ #3.*|.*20070#Test bitmap bits
+     3 | 20070
+
+TEST 2.10 = #20070#Further test bitmap bits
+            20001
+            20002
+            20070
+
+-
+- ...test set 2a: bitmaps (second process)...
+PREP
+ t
+
+TEST 2.11 != #20070#Check that shared bitmap has bit cleared
+            20001
+            20002
+
+TEST 2.12 = #2#Check that shared bitmap has other bits still set
+     2
+
+PREP
+                1
+
+TEST 2.13 = #t#Test for known true values in serialised session bitmap
+ t
+
+PREP
+ t                | t                | t
+
+ t                  | t                  | t                  | t
+
+ t                 | t
+
+TEST 2.13 ~ #20001 *| *20003 *| *3#Check union of bitmaps
+ 20001 | 20003 |     3
+
+PREP
+ t                     | t
+
+TEST 2.14 ~ #20002 *| *20002 *| *1#Check bitmap intersection
+ 20002 | 20002 |     1
+
+-
+- ...test set 3: bitmap arrays...
+PREP
+               2
+
+              70
+
+TEST 3.1 = #t#Create bitmap array
+ t
+
+TEST 3.2 = #f#Test false bit
+ f
+
+TEST 3.3 = #t#Set bit
+ t
+
+TEST 3.4 = #t#Test newly true bit
+ t
+
+TEST 3.5 = #t#Clear the aray
+ t
+
+TEST 3.6 = #f#Test that bit again
+ f
+
+PREP
+ f
+
+TEST 3.7 ~ #ERROR.*illegal#Attempt to create shared bitmap ref
+ERROR:  illegal attempt to define shared BitmapRef shared_bitmap_ref
+HINT:  BitmapRefs may only be defined as session, not shared, variables.
+TEST 3.8 ~ #session_bitmap_refx#Create session bitmap ref
+ session_bitmap_refx
+
+TEST 3.9 ~ #ERROR.*not.*defined#Check for bitmap ref not in transaction
+ERROR:  BitmapRef session_bitmap_ref is not defined
+HINT:  Perhaps the name is mis-spelled, or its definition is missing from veil_init().
+PREP
+ session_bitmap_ref
+
+ t                  | t
+
+TEST 3.10 ~ #2 *| *20001 *| *20003#Add bits thru ref, check bits in array
+     2 | 20001 | 20003
+
+PREP
+ session_bitmap_ref
+
+ t
+
+TEST 3.11 ~ #2 *| *20001 *| *20003#Union through ref, check bits in array
+     2 | 20001 | 20003
+
+PREP
+ t
+
+ t
+
+ t
+
+TEST 3.12 ~ #1 *| *20003 *| *20003#Intersect thru ref, check bits in array
+     1 | 20003 | 20003
+
+PREP
+TEST 3.13 ~ #10001.*10002#Check array range
+ 10001 | 10002
+
+TEST 3.14 ~ #20001.*20070#Check bitmaps range
+ 20001 | 20070
+
+PREP
+ f
+
+ t
+
+ session_bitmap_ref
+
+ge of range2 variable
+  11 |  24
+MATCH ~ "11 |  24"
+
+PASS
+END TEST 1.27
+
+TEST 1.28 = #24#Checking array2 variable(1)
+                 24
+MATCH = "24"
+
+PASS
+END TEST 1.28
+
+TEST 1.29 = #11#Checking array2 variable(2)
+                 11
+MATCH = "11"
+
+PASS
+END TEST 1.29
+
+TEST 1.30 = #0#Checking array2 variable(3)
+                  0
+MATCH = "0"
+
+PASS
+END TEST 1.30
+
+              70
+
+               2
+
+ f
+
+TEST 2.1 ~ #OK#Initialise shared and session bitmaps
+ OK       | t                | t
+MATCH ~ "OK       | t                | t"
+
+PASS
+END TEST 2.1
+
+TEST 2.2 = #3#Populate shared bitmap
+     3
+MATCH = "3"
+
+PASS
+END TEST 2.2
+
+TEST 2.3 = #2#Populate session bitmap
+     2
+MATCH = "2"
+
+PASS
+END TEST 2.3
+
+TEST 2.4 = #t#Test for known true values in session and shared bitmaps
+ t
+MATCH = "t"
+
+PASS
+END TEST 2.4
+
+TEST 2.5 = #f#Test for known false value
+ f
+MATCH = "f"
+
+PASS
+END TEST 2.5
+
+TEST 2.6 = #t#Clear sessionbitmap
+ t
+MATCH = "t"
+
+PASS
+END TEST 2.6
+
+TEST 2.7 = #f#Test for absence of previous true value in shared bitmap
+ f
+MATCH = "f"
+
+PASS
+END TEST 2.7
+
+TEST 2.8 ~ #20001.*|.*20070#Test bitmap range
+ 20001 | 20070
+MATCH ~ "20001 | 20070"
+
+PASS
+END TEST 2.8
+
+TEST 2.9 ~ #3.*|.*20070#Test bitmap bits
+     3 | 20070
+MATCH ~ "3 | 20070"
+
+PASS
+END TEST 2.9
+
+TEST 2.10 = #20070#Further test bitmap bits
+            20001
+            20002
+            20070
+MATCH = "20070"
+
+PASS
+END TEST 2.10
+
+ t
+
+TEST 2.11 != #20070#Check that shared bitmap has bit cleared
+            20001
+            20002
+
+PASS
+END TEST 2.11
+
+TEST 2.12 = #2#Check that shared bitmap has other bits still set
+     2
+MATCH = "2"
+
+PASS
+END TEST 2.12
+
+                1
+
+TEST 2.13 = #t#Test for known true values in serialised session bitmap
+ t
+MATCH = "t"
+
+PASS
+END TEST 2.13
+
+ t                | t                | t
+
+ t                  | t                  | t                  | t
+
+ t                 | t
+
+TEST 2.13 ~ #20001 *| *20003 *| *3#Check union of bitmaps
+ 20001 | 20003 |     3
+MATCH ~ "20001 | 20003 |     3"
+
+PASS
+END TEST 2.13
+
+ t                     | t
+
+TEST 2.14 ~ #20002 *| *20002 *| *1#Check bitmap intersection
+ 20002 | 20002 |     1
+MATCH ~ "20002 | 20002 |     1"
+
+PASS
+END TEST 2.14
+
+               2
+
+              70
+
+TEST 3.1 = #t#Create bitmap array
+ t
+MATCH = "t"
+
+PASS
+END TEST 3.1
+
+TEST 3.2 = #f#Test false bit
+ f
+MATCH = "f"
+
+PASS
+END TEST 3.2
+
+TEST 3.3 = #t#Set bit
+ t
+MATCH = "t"
+
+PASS
+END TEST 3.3
+
+TEST 3.4 = #t#Test newly true bit
+ t
+MATCH = "t"
+
+PASS
+END TEST 3.4
+
+TEST 3.5 = #t#Clear the aray
+ t
+MATCH = "t"
+
+PASS
+END TEST 3.5
+
+TEST 3.6 = #f#Test that bit again
+ f
+MATCH = "f"
+
+PASS
+END TEST 3.6
+
+ f
+
+TEST 3.7 ~ #ERROR.*illegal#Attempt to create shared bitmap ref
+ERROR:  illegal attempt to define shared BitmapRef shared_bitmap_ref
+MATCH ~ "ERROR:  illegal attempt to define shared BitmapRef shared_bitmap_ref"
+HINT:  BitmapRefs may only be defined as session, not shared, variables.
+PASS
+END TEST 3.7
+
+TEST 3.8 ~ #session_bitmap_refx#Create session bitmap ref
+ session_bitmap_refx
+MATCH ~ "session_bitmap_refx"
+
+PASS
+END TEST 3.8
+
+TEST 3.9 ~ #ERROR.*not.*defined#Check for bitmap ref not in transaction
+ERROR:  BitmapRef session_bitmap_ref is not defined
+MATCH ~ "ERROR:  BitmapRef session_bitmap_ref is not defined"
+HINT:  Perhaps the name is mis-spelled, or its definition is missing from veil_init().
+PASS
+END TEST 3.9
+
+ session_bitmap_ref
+
+ t                  | t
+
+TEST 3.10 ~ #2 *| *20001 *| *20003#Add bits thru ref, check bits in array
+     2 | 20001 | 20003
+MATCH ~ "2 | 20001 | 20003"
+
+PASS
+END TEST 3.10
+
+ session_bitmap_ref
+
+ t
+
+TEST 3.11 ~ #2 *| *20001 *| *20003#Union through ref, check bits in array
+     2 | 20001 | 20003
+MATCH ~ "2 | 20001 | 20003"
+
+PASS
+END TEST 3.11
+
+ t
+
+ t
+
+ t
+
+TEST 3.12 ~ #1 *| *20003 *| *20003#Intersect thru ref, check bits in array
+     1 | 20003 | 20003
+MATCH ~ "1 | 20003 | 20003"
+
+PASS
+END TEST 3.12
+
+TEST 3.13 ~ #10001.*10002#Check array range
+ 10001 | 10002
+MATCH ~ "10001 | 10002"
+
+PASS
+END TEST 3.13
+
+TEST 3.14 ~ #20001.*20070#Check bitmaps range
+ 20001 | 20070
+MATCH ~ "20001 | 20070"
+
+PASS
+END TEST 3.14
+
+ f
+
+ t
+
+ session_bitmap_ t
+
+-
+- ...test set 3a: bitmap arrays (second process)...
+TEST 3.15 ~ #1 *| *20003 *| *20003#Check bits in shared bitmap array
+     1 | 20003 | 20003
+
+PREP
+                1
+
+TEST 3.16 ~ #10001.*10002#Check array range after de-serialisation
+ 10001 | 10002
+
+TEST 3.17 ~ #20001.*20070#Check bitmaps range after de-serialisation
+ 20001 | 20070
+
+TEST 3.18 ~ #1 *\| *20003 *\| *20003#Check bits in array after de-ser.
+     1 | 20003 | 20003
+
+-
+- ...test set 4: bitmap hashes...
+PREP
+              70
+
+TEST 4.1 = #t#Create session bitmap hash
+ t
+
+TEST 4.2 = #f#Check for known false
+ f
+
+TEST 4.3 = #t#Set a bit
+ t
+
+TEST 4.4 = #t#Check that it is now true
+ t
+
+PREP
+TEST 4.5 = #t#Clear the hash
+ t
+
+TEST 4.6 = #f#Check that bit again
+ f
+
+PREP
+ f
+
+TEST 4.7 ~ #ERROR.*illegal#Attempt to create shared bitmap hash
+ERROR:  illegal attempt to define shared BitmapRef shared_bitmap_ref
+HINT:  BitmapRefs may only be defined as session, not shared, variables.
+TEST 4.8 ~ #session_bitmap_refx#Get bitmap ref from bitmap hash
+ session_bitmap_refx
+
+TEST 4.9 ~ #ERROR.*not.*defined#Check ref in transaction
+ERROR:  BitmapRef session_bitmap_ref is not defined
+HINT:  Perhaps the name is mis-spelled, or its definition is missing from veil_init().
+PREP
+ session_bitmap_ref
+
+ t                  | t
+
+TEST 4.10 ~ #2 *| *20001 *| *20003#Test bits after setting them
+     2 | 20001 | 20003
+
+PREP
+ session_bitmap_ref
+
+ t
+
+TEST 4.11 ~ #2 *| *20001 *| *20003#Union and then test bits
+WARNING:  there is already a transaction in progress
+     2 | 20001 | 20003
+
+PREP
+ t
+
+ t
+
+ t
+
+TEST 4.12 ~ #1 *| *20003 *| *20003#Intersect and test bits
+     1 | 20003 | 20003
+
+TEST 4.13 ~ #wibble#Test bitmap hash entry (a)
+ wibble
+ rubble
+
+TEST 4.14 ~ #rubble#Test bitmap hash entry (b)
+ wibble
+ rubble
+
+TEST 4.15 ~ #20001.*20070#Test range of bitmaps in hash
+ 20001 | 20070
+
+PREP
+ f
+
+TEST 4.16 ~ #ERROR.*illegal.*BitmapHash#Attempt to create shared bitmap hash
+ERROR:  illegal attempt to define shared BitmapHash shared_role_privs2
+HINT:  BitmapHashes may only be defined as session, not shared, variables.
+PREP
+ t
+
+ t
+
+TEST 4.17 ~ #3.*20001.*20003#Test union into bitmap hash
+     3 | 20001 | 20003
+
+TEST 4.18 ~ #truex#Check for defined bitmap in hash
+ truex
+
+TEST 4.19 ~ #falsex#Check for undefined bitmap in hash
+ falsex
+
+PREP
+                1
+
+TEST 4.20 = #t#Check a bit in the deserialised bitmap hash
+ t
+
+TEST 4.21 ~ #1 *| *20003 *| *20003#Count those bits
+     1 | 20001 | 20001
+
+-
+- ...test set 5: control functions...
+PREP IGNORE
+- Loading the veil extension...
+TEST 5.1 ~ #ERROR.*veil_init#Default init
+ERROR:  default veil version of veil_init() has been called
+HINT:  You must define your own version of this function.
+CONTEXT:  SQL statement "select veil_init(FALSE)"
+PREP
+TEST 5.2 = #10#Demo init
+              10
+
+PREP
+TEST 5.3 = #t#Reset once
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 5.4 = #1#Check variables after reset
+     1
+
+TEST 5.5 = #t#Reset again
+ref
+
+ t
+
+TEST 3.15 ~ #1 *| *20003 *| *20003#Check bits in shared bitmap array
+     1 | 20003 | 20003
+MATCH ~ "1 | 20003 | 20003"
+
+PASS
+END TEST 3.15
+
+                1
+
+TEST 3.16 ~ #10001.*10002#Check array range after de-serialisation
+ 10001 | 10002
+MATCH ~ "10001 | 10002"
+
+PASS
+END TEST 3.16
+
+TEST 3.17 ~ #20001.*20070#Check bitmaps range after de-serialisation
+ 20001 | 20070
+MATCH ~ "20001 | 20070"
+
+PASS
+END TEST 3.17
+
+TEST 3.18 ~ #1 *\| *20003 *\| *20003#Check bits in array after de-ser.
+     1 | 20003 | 20003
+MATCH ~ "1 | 20003 | 20003"
+
+PASS
+END TEST 3.18
+
+              70
+
+TEST 4.1 = #t#Create session bitmap hash
+ t
+MATCH = "t"
+
+PASS
+END TEST 4.1
+
+TEST 4.2 = #f#Check for known false
+ f
+MATCH = "f"
+
+PASS
+END TEST 4.2
+
+TEST 4.3 = #t#Set a bit
+ t
+MATCH = "t"
+
+PASS
+END TEST 4.3
+
+TEST 4.4 = #t#Check that it is now true
+ t
+MATCH = "t"
+
+PASS
+END TEST 4.4
+
+TEST 4.5 = #t#Clear the hash
+ t
+MATCH = "t"
+
+PASS
+END TEST 4.5
+
+TEST 4.6 = #f#Check that bit again
+ f
+MATCH = "f"
+
+PASS
+END TEST 4.6
+
+ f
+
+TEST 4.7 ~ #ERROR.*illegal#Attempt to create shared bitmap hash
+ERROR:  illegal attempt to define shared BitmapRef shared_bitmap_ref
+MATCH ~ "ERROR:  illegal attempt to define shared BitmapRef shared_bitmap_ref"
+HINT:  BitmapRefs may only be defined as session, not shared, variables.
+PASS
+END TEST 4.7
+
+TEST 4.8 ~ #session_bitmap_refx#Get bitmap ref from bitmap hash
+ session_bitmap_refx
+MATCH ~ "session_bitmap_refx"
+
+PASS
+END TEST 4.8
+
+TEST 4.9 ~ #ERROR.*not.*defined#Check ref in transaction
+ERROR:  BitmapRef session_bitmap_ref is not defined
+MATCH ~ "ERROR:  BitmapRef session_bitmap_ref is not defined"
+HINT:  Perhaps the name is mis-spelled, or its definition is missing from veil_init().
+PASS
+END TEST 4.9
+
+ session_bitmap_ref
+
+ t                  | t
+
+TEST 4.10 ~ #2 *| *20001 *| *20003#Test bits after setting them
+     2 | 20001 | 20003
+MATCH ~ "2 | 20001 | 20003"
+
+PASS
+END TEST 4.10
+
+ session_bitmap_ref
+
+ t
+
+TEST 4.11 ~ #2 *| *20001 *| *20003#Union and then test bits
+WARNING:  there is already a transaction in progress
+     2 | 20001 | 20003
+MATCH ~ "2 | 20001 | 20003"
+
+PASS
+END TEST 4.11
+
+ t
+
+ t
+
+ t
+
+TEST 4.12 ~ #1 *| *20003 *| *20003#Intersect and test bits
+     1 | 20003 | 20003
+MATCH ~ "1 | 20003 | 20003"
+
+PASS
+END TEST 4.12
+
+TEST 4.13 ~ #wibble#Test bitmap hash entry (a)
+ wibble
+MATCH ~ "wibble"
+ rubble
+
+PASS
+END TEST 4.13
+
+TEST 4.14 ~ #rubble#Test bitmap hash entry (b)
+ wibble
+ rubble
+MATCH ~ "rubble"
+
+PASS
+END TEST 4.14
+
+TEST 4.15 ~ #20001.*20070#Test range of bitmaps in hash
+ 20001 | 20070
+MATCH ~ "20001 | 20070"
+
+PASS
+END TEST 4.15
+
+ f
+
+TEST 4.16 ~ #ERROR.*illegal.*BitmapHash#Attempt to create shared bitmap hash
+ERROR:  illegal attempt to define shared BitmapHash shared_role_privs2
+MATCH ~ "ERROR:  illegal attempt to define shared BitmapHash shared_role_privs2"
+HINT:  BitmapHashes may only be defined as session, not shared, variables.
+PASS
+END TEST 4.16
+
+ t
+
+ t
+
+TEST 4.17 ~ #3.*20001.*20003#Test union into bitmap hash
+     3 | 20001 | 20003
+MATCH ~ "3 | 20001 | 20003"
+
+PASS
+END TEST 4.17
+
+TEST 4.18 ~ #truex#Check for defined bitmap in hash
+ truex
+MATCH ~ "truex"
+
+PASS
+END TEST 4.18
+
+TEST 4.19 ~ #falsex#Check for undefined bitmap in hash
+ falsex
+MATCH ~ "falsex"
+
+PASS
+END TEST 4.19
+
+                1
+
+TEST 4.20 = #t#Check a bit in the deserialised bitmap hash
+ t
+MATCH = "t"
+
+PASS
+END TEST 4.20
+
+TEST 4.21 ~ #1 *| *20003 *| *20003#Count those bits
+     1 | 20001 | 20001
+MATCH ~ "1 | 20001 | 20001"
+
+PASS
+END TEST 4.21
+
+TEST 5.1 ~ #ERROR.*veil_init#Default init
+ERROR:  default veil version of veil_init() has been called
+MATCH ~ "ERROR:  default veil version of veil_init() has been called"
+HINT:  You must define your own version of this function.
+CONTEXT:  SQL statement "select veil_init(FALSE)"
+PASS
+END TEST 5.1
+
+TEST 5.2 = #10#Demo init
+              10
+MATCH = "10"
+
+PASS
+END TEST 5.2
+
+TEST 5.3 = #t#Reset once
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+MATCH = "t"
+
+PASS
+END TEST 5.3
+
+TEST 5.4 = #1#Check variables after reset
+     1
+MATCH = "1"
+
+PNOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 5.6 = #2#Check variables again
+     2
+
+PREP IGNORE
+- Loading the veil extension...
+TEST 5.7 ~ #ERROR.*veil_init#Reset with failing veil_init
+ERROR:  default veil version of veil_init() has been called
+HINT:  You must define your own version of this function.
+CONTEXT:  SQL statement "select veil_init(TRUE)"
+TEST 5.8 ~ #^ *f *$#Failing reset (context switch was left incomplete)
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+TEST 5.9 ~ #WARNING.*reset#Again (looking for WARNING message)
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+TEST 5.10 ~ #^ *t *$#Force reset
+ t
+
+PREP
+TEST 5.11 = #t#Reset again
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+PREP
+ f
+
+             100
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+TEST 5.12 = #25#Original bitmap before reset from other session
+    25
+
+-
+- ...test set 5a: reset from second session (second process)...
+TEST 5.13 = #25#Bitmap variable from other session with bits
+    25
+
+TEST 5.14 = #t#Successful reset
+    25
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 5.15 ~ #ERROR.*Undefined#No bitmap variable from other session
+ f
+
+ERROR:  type mismatch in bitmap_x: expected Bitmap, got Undefined
+DETAIL:  Variable bitmap_x is not of the expected type.
+TEST 5.16 = #f#Failed reset as session still in use
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+TEST 5.17 ~ #WARNING.*reset#Failed reset as session still in use (again)
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+PREP
+             100
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+- ...(back from 5a)...
+TEST 5.19 = #25#Original Bitmap (from original transaction)
+    25
+
+ VEIL_SHMEMCTL | ShmemCtl | t
+ bitmap_x      | Bitmap   | t
+ range_x       | Range    | f
+
+TEST 5.20 = #15#New bitmap created by other session
+    15
+
+-
+- ...test set 5b: (like 5a but with opposite contexts)...
+TEST 5.21 = #15#Bitmap variable from other session with bits
+    15
+
+TEST 5.22 = #t#Successful reset
+    15
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 5.23 ~ #ERROR.*Undefined#No bitmap variable from other session
+ f
+
+ERROR:  type mismatch in bitmap_x: expected Bitmap, got Undefined
+DETAIL:  Variable bitmap_x is not of the expected type.
+TEST 5.24 = #f#Failed reset as session still in use
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ASS
+END TEST 5.4
+
+TEST 5.5 = #t#Reset again
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+MATCH = "t"
+
+PASS
+END TEST 5.5
+
+TEST 5.6 = #2#Check variables again
+     2
+MATCH = "2"
+
+PASS
+END TEST 5.6
+
+TEST 5.7 ~ #ERROR.*veil_init#Reset with failing veil_init
+ERROR:  default veil version of veil_init() has been called
+MATCH ~ "ERROR:  default veil version of veil_init() has been called"
+HINT:  You must define your own version of this function.
+CONTEXT:  SQL statement "select veil_init(TRUE)"
+PASS
+END TEST 5.7
+
+TEST 5.8 ~ #^ *f *$#Failing reset (context switch was left incomplete)
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+MATCH ~ "f"
+
+PASS
+END TEST 5.8
+
+TEST 5.9 ~ #WARNING.*reset#Again (looking for WARNING message)
+WARNING:  failed to perform reset
+MATCH ~ "WARNING:  failed to perform reset"
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+PASS
+END TEST 5.9
+
+TEST 5.10 ~ #^ *t *$#Force reset
+ t
+MATCH ~ "t"
+
+PASS
+END TEST 5.10
+
+TEST 5.11 = #t#Reset again
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+MATCH = "t"
+
+PASS
+END TEST 5.11
+
+ f
+
+             100
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+TEST 5.12 = #25#Original bitmap before reset from other session
+    25
+MATCH = "25"
+
+PASS
+END TEST 5.12
+
+TEST 5.13 = #25#Bitmap variable from other session with bits
+    25
+MATCH = "25"
+
+PASS
+END TEST 5.13
+
+TEST 5.14 = #t#Successful reset
+    25
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+MATCH = "t"
+
+PASS
+END TEST 5.14
+
+TEST 5.15 ~ #ERROR.*Undefined#No bitmap variable from other session
+ f
+
+ERROR:  type mismatch in bitmap_x: expected Bitmap, got Undefined
+MATCH ~ "ERROR:  type mismatch in bitmap_x: expected Bitmap, got Undefined"
+DETAIL:  Variable bitmap_x is not of the expected type.
+PASS
+END TEST 5.15
+
+TEST 5.16 = #f#Failed reset as session still in use
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+MATCH = "f"
+
+PASS
+END TEST 5.16
+
+TEST 5.17 ~ #WARNING.*reset#Failed reset as session still in use (again)
+WARNING:  failed to perform reset
+MATCH ~ "WARNING:  failed to perform reset"
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+PASS
+END TEST 5.17
+
+             100
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+TEST 5.19 = #25#Original Bitmap (from original transaction)
+    25
+MATCH = "25"
+
+ VEIL_SHMEMCTL | ShmemCtl | t
+ bitmap_x      | Bitmap   | t
+ range_x       | Range    | f
+
+PASS
+END TEST 5.19
+
+TEST 5.20 = #15#New bitmap created by other session
+    15
+MATCH = "15"
+
+PASS
+END TEST 5.20
+
+TEST 5.21 = #15#Bitmap variable from other session with bits
+    15
+MATCH = "15"
+
+PASS
+END TEST 5.21
+
+TEST 5.22 = #t#Successful reset
+    15
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+MATCH = "t"
+
+PASS
+END TEST 5.22
+
+TEST 5.23 ~ #ERROR.*Undefined#No bitmap variable from other session
+ f
+
+ERROR:  type mismatch in bitmap_x: expected Bitmap, got Undefined
+MATCH ~ "ERROR:  type mismatch in bitmap_x: expected Bitmap, got Undefined"
+DETAIL:  Variable bitmap_x is not of the expected type.
+PASS
+END TEST 5.23
+
+TEST 5.24 = #f#Failed reset as session still in use
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memo f
+
+TEST 5.25 ~ #WARNING.*reset#Failed reset as session still in use (again)
+WARNING:  failed to perform reset
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+PREP
+             100
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+- ...(back from 5b)...
+TEST 5.98 = #15#Original Bitmap (from original transaction)
+    15
+
+ bitmap_x | Bitmap | t
+ range_x  | Range  | f
+
+TEST 5.99 = #24#New bitmap created by other session
+    24
+
+-
+- ...test set 6: veil_init() doing something useful...
+PREP IGNORE
+- Loading the veil extension...
+TEST 6.1 ~ #ERROR.*veil_init#Default init
+ERROR:  default veil version of veil_init() has been called
+HINT:  You must define your own version of this function.
+CONTEXT:  SQL statement "select veil_init(FALSE)"
+PREP IGNORE
+ t
+
+TEST 6.2 ~ #shared2.*Undefined.*t#Undefined shared variable
+ VEIL_SHMEMCTL | ShmemCtl  | t
+ shared1       | Int4      | t
+ shared2       | Undefined | t
+ bitmap_x      | Bitmap    | t
+
+TEST 6.3 ~ #shared1.*Int4.*t#Defined shared variable
+ VEIL_SHMEMCTL | ShmemCtl  | t
+ shared1       | Int4      | t
+ shared2       | Undefined | t
+ bitmap_x      | Bitmap    | t
+
+PREP IGNORE
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 6.4 ~ #shared2.*Undefined.*t#Undefined shared variable
+ shared1 | Int4      | t
+ shared2 | Undefined | t
+
+TEST 6.5 ~ #shared1.*Int4.*t#Defined shared variable
+ shared1 | Int4      | t
+ shared2 | Undefined | t
+
+-
+- ...test set 7: veil_init() triggering SPI bug...
+PREP IGNORE
+ERROR:  database "regressdb2" does not exist
+- Loading the veil extension...
+NOTICE:  extension "veil" does not exist, skipping
+ERROR:  language "plpgsql" already exists
+TEST 7.1 = #9#Test for SPI bug
+        9
+
+COMPLETE
+ry reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+MATCH = "f"
+
+PASS
+END TEST 5.24
+
+TEST 5.25 ~ #WARNING.*reset#Failed reset as session still in use (again)
+WARNING:  failed to perform reset
+MATCH ~ "WARNING:  failed to perform reset"
+DETAIL:  Unable to prepare for memory reset.  Maybe another process is performing a reset, or maybe there is a long-running transaction that is still using the previous memory context.
+ f
+
+PASS
+END TEST 5.25
+
+             100
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+ t
+
+TEST 5.98 = #15#Original Bitmap (from original transaction)
+    15
+MATCH = "15"
+
+ bitmap_x | Bitmap | t
+ range_x  | Range  | f
+
+PASS
+END TEST 5.98
+
+TEST 5.99 = #24#New bitmap created by other session
+    24
+MATCH = "24"
+
+PASS
+END TEST 5.99
+
+TEST 6.1 ~ #ERROR.*veil_init#Default init
+ERROR:  default veil version of veil_init() has been called
+MATCH ~ "ERROR:  default veil version of veil_init() has been called"
+HINT:  You must define your own version of this function.
+CONTEXT:  SQL statement "select veil_init(FALSE)"
+PASS
+END TEST 6.1
+
+ t
+
+TEST 6.2 ~ #shared2.*Undefined.*t#Undefined shared variable
+ VEIL_SHMEMCTL | ShmemCtl  | t
+ shared1       | Int4      | t
+ shared2       | Undefined | t
+MATCH ~ "shared2       | Undefined | t"
+ bitmap_x      | Bitmap    | t
+
+PASS
+END TEST 6.2
+
+TEST 6.3 ~ #shared1.*Int4.*t#Defined shared variable
+ VEIL_SHMEMCTL | ShmemCtl  | t
+ shared1       | Int4      | t
+MATCH ~ "shared1       | Int4      | t"
+ shared2       | Undefined | t
+ bitmap_x      | Bitmap    | t
+
+PASS
+END TEST 6.3
+
+NOTICE:  veil_init returns true to veil_perform_reset
+NOTICE:  vl_complete_context_switch returns true to veil_perform_reset
+ t
+
+TEST 6.4 ~ #shared2.*Undefined.*t#Undefined shared variable
+ shared1 | Int4      | t
+ shared2 | Undefined | t
+MATCH ~ "shared2 | Undefined | t"
+
+PASS
+END TEST 6.4
+
+TEST 6.5 ~ #shared1.*Int4.*t#Defined shared variable
+ shared1 | Int4      | t
+MATCH ~ "shared1 | Int4      | t"
+ shared2 | Undefined | t
+
+PASS
+END TEST 6.5
+
+ERROR:  database "regressdb2" does not exist
+NOTICE:  extension "veil" does not exist, skipping
+ERROR:  language "plpgsql" already exists
+TEST 7.1 = #9#Test for SPI bug
+        9
+MATCH = "9"
+
+COMPLETE
+PASS
+END TEST 7.1
+
+
+tests:     117
+passed:    117
+failed:    0
+
+- Dropping regressdb...
+- Dropping regressdb...
+dropdb: database removal failed: ERROR:  database "regressdb" does not exist
+- Dropping regressdb...
+dropdb: database removal failed: ERROR:  database "regressdb" does not exist
+- Dropping regressdb...
+dropdb: database removal failed: ERROR:  database "regressdb" does not exist
+- Dropping regressdb...
+dropdb: database removal failed: ERROR:  database "regressdb" does not exist
diff --git a/regress/regress.sh b/regress/regress.sh
new file mode 100755 (executable)
index 0000000..124be39
--- /dev/null
@@ -0,0 +1,1195 @@
+#! /bin/sh
+#
+# regress.sh
+#
+#      Regression test script for Veil
+#
+#      Copyright (c) 2005-2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+
+bail()
+{
+    echo "Failed while $*.  Bailing out..." 1>&2
+    exit 2
+}
+
+db_create()
+{
+    version=`psql --version | grep '[0-9]\.[0-9]'`
+    version=`echo ${version} | sed -e 's/[^0-9]*\([0-9]*\.[0-9]*\).*/\1/'`
+    major=`echo ${version} | cut -d. -f1`
+    minor=`echo ${version} | cut -d. -f2`
+
+    psql -d template1 <<EOF
+\echo - Creating regressdb...
+create database regressdb owner $1;
+EOF
+}
+
+define_veil()
+{
+    echo "\echo - Loading the veil extension...
+drop extension if exists veil;
+create extension veil;"
+}
+
+db_build_schema()
+{
+    psql -d regressdb 2>&1 <<EOF
+\echo - Creating regression test objects...
+\set ECHO ALL
+`define_veil`
+create or replace
+function veil_init(bool) returns bool as
+'
+begin
+    return true;
+end;
+' language plpgsql;
+
+\i ${dir}/regress_tables.sql
+\i ${dir}/regress_data.sql
+
+EOF
+    if [ $? -ne 0 ]; then
+   bail creating regression test objects
+    fi
+}
+
+db_drop()
+{
+    (
+   echo - Dropping regressdb...
+   dropdb regressdb
+    ) 2>&1 | psql_clean regress.log
+}
+
+do_test()
+{
+    psql -q -t -d regressdb <<EOF
+set client_min_messages = 'notice';
+`cat -`
+EOF
+    if [ $? -ne 0 ]; then
+   bail running test set $1
+    fi
+}
+
+# The format of a TEST definition line is:
+# TEST <testno> <condition> #<pattern to test using condition>#<description>
+#   testno is a string identifying the test (eg 1.4)
+#   condition is one of "=", "!=", "~", "!~", specifying exact string match,
+#     exact string mismatch, regular expression match and regular
+#     expression mismatch respectively
+#  <pattern to test using condition> is a string or regexp that must appear
+#      or not in the test output for the test to pass.
+# <description> is a short phrase describing the test
+#
+regress_1()
+{
+    echo - 
+    echo "- ...test set 1: variables..."
+    do_test 1 <<EOF
+\echo PREP
+select veil_perform_reset();
+select veil_perform_reset();
+
+-- Ensure the base variable VEIL_SHMEMCTL exists and that nothing else does
+\echo TEST 1.1 ~ #1 *| *VEIL_SHMEMCTL#Initial variable listing
+select count(*), max(name)
+from   veil_variables();
+\echo - NOTE Test 1.1 will fail (correctly) if this is not a freshly created database
+\echo -
+-- Create session and shared integers
+\echo TEST 1.2 = #42#create session int4
+select veil_int4_set('sess_int4', 42);
+
+\echo TEST 1.3 = #42#retrieve session int4
+select veil_int4_get('sess_int4');
+
+\echo 'TEST 1.4 ~ #sess_int4 *\\\| *Int4 *\\\| *f#list defined variables'
+select * from veil_variables();
+
+\echo 'TEST 1.5 ~ #shared_int4 *\\\| *Undefined *\\\| *t#create shared int4'
+select veil_share('shared_int4');
+select * from veil_variables();
+
+\echo TEST 1.6 = #99#get shared int4
+select coalesce(veil_int4_get('shared_int4'), 99);
+
+\echo 'TEST 1.7 ~ #shared_int4 *\\\| *Int4 *\\\| *t#list defined variables'
+select * from veil_variables();
+
+-- Create session and shared ranges
+\echo TEST 1.8 ~ #ERROR.*mismatch#access non-existant session range
+select * from veil_range('sess_range');
+
+\echo TEST 1.9 = #14#create session range
+select * from veil_init_range('sess_range', 11, 24);
+
+\echo PREP
+select veil_share('shared_range');
+\echo TEST 1.10 = #1#create shared ranged
+select * from veil_init_range('shared_range', 17, 17);
+
+\echo TEST 1.11 ~ #5 *| *3#list defined variables
+select count(*), sum(case when shared then 1 else 0 end) as shared
+from   veil_variables();
+
+\echo - NOTE Test 1.11 will fail (correctly) if this is not a freshly created database
+\echo -
+
+\echo TEST 1.12 ~ #11 *| *24#show session range
+select * from veil_range('sess_range');
+
+\echo TEST 1.13 ~ #17 *| *17#show shared range
+select * from veil_range('shared_range');
+
+-- Create session and shared int4 arrays
+\echo TEST 1.14 ~ #ERROR.*mismatch#fetch against undefined int4 array
+select veil_int4array_get('sess_int4array', 12);
+
+\echo TEST 1.15 ~ #ERROR.*mismatch#set against undefined int4 array
+select veil_int4array_set('sess_int4array', 12, 14);
+
+\echo TEST 1.16 = #t#create session int4 array
+select veil_init_int4array('sess_int4array', 'shared_range');
+
+\echo TEST 1.17 ~ #range error#attempt to set arrary element out of range
+select veil_int4array_set('sess_int4array', 12, 14);
+
+\echo TEST 1.18 = #14#set int4 array element
+select veil_int4array_set('sess_int4array', 17, 14);
+
+\echo TEST 1.19 = #14#fetch int4 array element
+select veil_int4array_get('sess_int4array', 17);
+
+\echo PREP
+select veil_clear_int4array('sess_int4array');
+\echo TEST 1.20 = #0#clear array and fetch element
+select veil_int4array_get('sess_int4array', 17);
+
+\echo PREP
+select veil_share('shared_in4array');
+select * from veil_init_int4array('shared_int4array', 'sess_range');
+\echo TEST 1.21 = #14#define and fetch from shared int4 array
+select veil_int4array_set('shared_int4array', 12, 14);
+
+\echo TEST 1.22 = #14#shared int4 array get
+select veil_int4array_get('shared_int4array', 12);
+
+-- Serialise the integer and range variables for de-serialisation in the other
+-- session.
+\echo PREP
+select veil_init_range('range2', 11, 24);
+select veil_init_int4array('array2', 'range2');
+select veil_clear_int4array('array2');
+select veil_int4array_set('array2', 11, 24);
+select veil_int4array_set('array2', 24, 11);
+create table my_text (contents text);
+insert into my_text(contents) select veil_serialise('sess_int4');
+insert into my_text(contents) select veil_serialise('sess_range');
+insert into my_text(contents) select veil_serialise('sess_int4array');
+insert into my_text(contents) select veil_serialise('range2');
+insert into my_text(contents) select veil_serialise('array2');
+
+-- Note that bitmap types will be tested in their own regression test sets
+
+\! $0 -T 1a
+
+EOF
+}
+
+regress_1a()
+{
+    echo - 
+    echo "- ...test set 1a: variables (second process)..."
+    do_test 1a <<EOF   
+-- Ensure that all expected shared variables still exist and that none of
+-- the session variables do
+\echo TEST 1.23 ~ #4 *| *4#list all shared variables
+select count(*), sum(case when shared then 1 else 0 end) as shared
+from   veil_variables();
+\echo - NOTE Test 1.23 may fail if this is not a freshly created database
+\echo -
+
+\echo TEST 1.24 = #99#get shared int4
+select coalesce(veil_int4_get('shared_int4'), 99);
+
+\echo TEST 1.25 = #0#Before de-serialising session variables
+select sum(case when shared then 0 else 1 end) as session
+from   veil_variables();
+
+\echo PREP
+select veil_deserialise(contents) from my_text;
+drop table my_text;
+
+\echo TEST 1.25 = #5#Checking session variables are de-serialised
+select sum(case when shared then 0 else 1 end) as session
+from   veil_variables();
+
+\echo TEST 1.26 ~ #17 *| *17#Checking Range of range variable
+select * from veil_range('shared_range');
+
+\echo TEST 1.27 ~ #11 *| *24#Checking Range of range2 variable
+select * from veil_range('range2');
+
+\echo TEST 1.28 = #24#Checking array2 variable(1)
+select * from veil_int4array_get('array2', 11);
+
+\echo TEST 1.29 = #11#Checking array2 variable(2)
+select * from veil_int4array_get('array2', 24);
+
+\echo TEST 1.30 = #0#Checking array2 variable(3)
+select * from veil_int4array_get('array2', 23);
+
+EOF
+}
+
+regress_2()
+{
+    echo - ...test set 2: bitmaps...
+    do_test 2 <<EOF
+-- Load ranges for lookup_types
+\echo PREP
+select veil_init_range('privs_range', min(privilege_id),
+                       max(privilege_id))
+from   privileges;
+
+select veil_init_range('roles_range', min(role_id), max(role_id))
+from   roles;
+
+-- Make a shared variable for privs_bmap
+select veil_share('privs_bmap');
+
+\echo TEST 2.1 ~ #OK#Initialise shared and session bitmaps
+-- Create bitmaps for roles and privileges
+select 'OK', 
+       veil_init_bitmap('roles_bmap', 'roles_range'),
+       veil_init_bitmap('privs_bmap', 'privs_range');
+
+\echo TEST 2.2 = #3#Populate shared bitmap
+-- Populate the privileges bitmap
+select count(veil_bitmap_setbit('privs_bmap', privilege_id))
+from   privileges;
+
+\echo TEST 2.3 = #2#Populate session bitmap
+-- Populate the roles bitmap
+select count(veil_bitmap_setbit('roles_bmap', role_id))
+from   roles;
+
+-- Record the current bitmap details for testset 2a
+\echo PREP
+create table my_text (contents text);
+insert into my_text(contents) select veil_serialise('roles_bmap');
+
+-- Test for known true values
+\echo TEST 2.4 = #t#Test for known true values in session and shared bitmaps
+select veil_bitmap_testbit('roles_bmap', 10001) and 
+       veil_bitmap_testbit('privs_bmap', 20070);
+
+-- Test for known false value
+\echo TEST 2.5 = #f#Test for known false value
+select veil_bitmap_testbit('privs_bmap', 20071);
+
+-- Test clear bitmap
+\echo TEST 2.6 = #t#Clear sessionbitmap
+select veil_clear_bitmap('roles_bmap');
+
+-- Test for previous truth value
+\echo TEST 2.7 = #f#Test for absence of previous true value in shared bitmap
+select veil_bitmap_testbit('roles_bmap', 10001);
+
+-- bitmap_range
+\echo TEST 2.8 ~ #20001.*|.*20070#Test bitmap range
+select * from veil_bitmap_range('privs_bmap');
+
+-- bitmap_bits
+\echo TEST 2.9 ~ #3.*|.*20070#Test bitmap bits
+select count(*), max(veil_bitmap_bits) from veil_bitmap_bits('privs_bmap');
+
+-- bitmap_bits again
+\echo TEST 2.10 = #20070#Further test bitmap bits
+select * from veil_bitmap_bits('privs_bmap');
+
+-- Clearbits using the shared bitmap
+\! $0 -T 2a
+
+\echo PREP
+-- Union tests
+-- Create bitmaps for roles and privileges
+select veil_init_bitmap('privs_bmap', 'privs_range'),
+       veil_init_bitmap('privs2_bmap', 'privs_range'),
+       veil_init_bitmap('privs3_bmap', 'privs_range');
+
+select veil_bitmap_setbit('privs2_bmap', 20001),
+       veil_bitmap_setbit('privs2_bmap', 20002),
+       veil_bitmap_setbit('privs3_bmap', 20002),
+       veil_bitmap_setbit('privs3_bmap', 20003);
+
+select veil_bitmap_union('privs_bmap', 'privs2_bmap'),
+       veil_bitmap_union('privs_bmap', 'privs3_bmap');
+
+\echo TEST 2.13 ~ #20001 *| *20003 *| *3#Check union of bitmaps
+select min(veil_bitmap_bits), max(veil_bitmap_bits), count(*)
+from   veil_bitmap_bits('privs_bmap');
+
+-- Intersect tests
+\echo PREP
+select veil_bitmap_intersect('privs_bmap', 'privs2_bmap'),
+       veil_bitmap_intersect('privs_bmap', 'privs3_bmap');
+
+\echo TEST 2.14 ~ #20002 *| *20002 *| *1#Check bitmap intersection
+select min(veil_bitmap_bits), max(veil_bitmap_bits), count(*)
+from   veil_bitmap_bits('privs_bmap');
+
+EOF
+}
+
+regress_2a()
+{
+    echo - 
+    echo "- ...test set 2a: bitmaps (second process)..."
+    do_test 2a <<EOF   
+-- Clearbit test
+\echo PREP
+select veil_bitmap_clearbit('privs_bmap', 20070);
+\echo TEST 2.11 != #20070#Check that shared bitmap has bit cleared
+select * from veil_bitmap_bits('privs_bmap');
+
+\echo TEST 2.12 = #2#Check that shared bitmap has other bits still set
+select count(*) from veil_bitmap_bits('privs_bmap');
+
+\echo PREP
+select veil_deserialise(contents) from my_text;
+drop table my_text;
+
+-- Test for known true values
+\echo TEST 2.13 = #t#Test for known true values in serialised session bitmap
+select veil_bitmap_testbit('roles_bmap', 10001);
+
+
+EOF
+}
+
+
+regress_3()
+{
+    echo - 
+    echo "- ...test set 3: bitmap arrays..."
+    do_test 3 <<EOF    
+\echo PREP
+-- Reset range variables
+select veil_init_range('roles_range', min(role_id), max(role_id))
+from   roles;
+
+select veil_init_range('privs_range', min(privilege_id), max(privilege_id))
+from   privileges;
+
+-- Create a bitmap array - requires that range variables are set up 
+\echo TEST 3.1 = #t#Create bitmap array
+begin;
+select veil_init_bitmap_array('role_privs', 'roles_range', 'privs_range');
+
+-- Test a bit
+\echo TEST 3.2 = #f#Test false bit
+select veil_bitmap_array_testbit('role_privs', 10001, 20001);
+
+-- Set a bit
+\echo TEST 3.3 = #t#Set bit
+select veil_bitmap_array_setbit('role_privs', 10001, 20001);
+
+-- Test a bit
+\echo TEST 3.4 = #t#Test newly true bit
+select veil_bitmap_array_testbit('role_privs', 10001, 20001);
+
+-- Clear the array
+\echo TEST 3.5 = #t#Clear the aray
+select veil_clear_bitmap_array('role_privs');
+
+-- Test a bit
+\echo TEST 3.6 = #f#Test that bit again
+select veil_bitmap_array_testbit('role_privs', 10001, 20001);
+commit;
+
+-- Bitmap Ref tests
+\echo PREP
+select veil_share('shared_bitmap_ref');
+
+\echo TEST 3.7 ~ #ERROR.*illegal#Attempt to create shared bitmap ref
+select veil_bitmap_from_array('shared_bitmap_ref', 'role_privs', 10001);
+
+\echo TEST 3.8 ~ #session_bitmap_refx#Create session bitmap ref
+select veil_bitmap_from_array('session_bitmap_ref', 'role_privs', 10001) || 'x';
+
+-- Check for bitmap ref not in same transaction
+\echo TEST 3.9 ~ #ERROR.*not.*defined#Check for bitmap ref not in transaction
+select veil_bitmap_setbit('session_bitmap_ref', 20001);
+
+-- Now set some bits and display them
+\echo PREP
+begin;
+select veil_bitmap_from_array('session_bitmap_ref', 'role_privs', 10001);
+
+select veil_bitmap_setbit('session_bitmap_ref', 20001),
+       veil_bitmap_setbit('session_bitmap_ref', 20003);
+
+\echo TEST 3.10 ~ #2 *| *20001 *| *20003#Add bits thru ref, check bits in array
+select count(*), min(veil_bitmap_array_bits), max(veil_bitmap_array_bits)
+from   veil_bitmap_array_bits('role_privs', 10001);
+commit;
+
+-- Union tests
+\echo PREP
+begin;
+select veil_bitmap_from_array('session_bitmap_ref', 'role_privs', 10002);
+
+select veil_union_from_bitmap_array('session_bitmap_ref', 'role_privs', 10001);
+
+\echo TEST 3.11 ~ #2 *| *20001 *| *20003#Union through ref, check bits in array
+select count(*), min(veil_bitmap_array_bits), max(veil_bitmap_array_bits)
+from   veil_bitmap_array_bits('role_privs', 10002);
+
+\echo PREP
+-- Intersect tests
+select veil_bitmap_array_setbit('role_privs', 10002, 20002);
+select veil_bitmap_array_clearbit('role_privs', 10002, 20001);
+select veil_intersect_from_bitmap_array('session_bitmap_ref', 'role_privs', 10001);
+
+\echo TEST 3.12 ~ #1 *| *20003 *| *20003#Intersect thru ref, check bits in array
+select count(*), min(veil_bitmap_array_bits), max(veil_bitmap_array_bits)
+from   veil_bitmap_array_bits('role_privs', 10002);
+
+commit;
+
+-- Record the current bitmap details for testset 2a
+\echo PREP
+create table my_text (contents text);
+insert into my_text(contents) select veil_serialise('role_privs');
+
+-- Test ranges of bitmap array
+\echo TEST 3.13 ~ #10001.*10002#Check array range
+select * from veil_bitmap_array_arange('role_privs');
+
+\echo TEST 3.14 ~ #20001.*20070#Check bitmaps range
+select * from veil_bitmap_array_brange('role_privs');
+
+\echo PREP
+-- Test shared bitmap array
+select veil_share('shared_role_privs');
+select veil_init_bitmap_array('shared_role_privs', 'roles_range', 'privs_range');
+begin;
+select veil_bitmap_from_array('session_bitmap_ref', 'shared_role_privs', 10002);
+select veil_union_from_bitmap_array('session_bitmap_ref', 'role_privs', 10002);
+commit;
+
+\! $0 -T 3a
+
+EOF
+}
+
+regress_3a()
+{
+    echo - 
+    echo "- ...test set 3a: bitmap arrays (second process)..."
+    do_test 3a <<EOF   
+
+\echo TEST 3.15 ~ #1 *| *20003 *| *20003#Check bits in shared bitmap array
+select count(*), min(veil_bitmap_array_bits), max(veil_bitmap_array_bits)
+from   veil_bitmap_array_bits('shared_role_privs', 10002);
+
+\echo PREP
+select veil_deserialise(contents) from my_text;
+drop table my_text;
+
+\echo TEST 3.16 ~ #10001.*10002#Check array range after de-serialisation
+select * from veil_bitmap_array_arange('role_privs');
+
+\echo TEST 3.17 ~ #20001.*20070#Check bitmaps range after de-serialisation
+select * from veil_bitmap_array_brange('role_privs');
+
+\echo 'TEST 3.18 ~ #1 *\\\| *20003 *\\\| *20003#Check bits in array after de-ser.'
+select count(*), min(veil_bitmap_array_bits), max(veil_bitmap_array_bits)
+from   veil_bitmap_array_bits('role_privs', 10002);
+EOF
+}
+
+regress_4()
+{
+    echo - 
+    echo "- ...test set 4: bitmap hashes..."
+    do_test 4 <<EOF    
+
+\echo PREP
+-- Reset range variables
+
+select veil_init_range('privs_range', min(privilege_id), max(privilege_id))
+from   privileges;
+
+-- Create a bitmap hash - requires that range variables are set up 
+\echo TEST 4.1 = #t#Create session bitmap hash
+begin;
+select veil_init_bitmap_hash('role_privs', 'privs_range');
+
+-- Test a bit
+\echo TEST 4.2 = #f#Check for known false
+select veil_bitmap_hash_testbit('role_privs', 'wibble', 20001);
+
+-- Set a bit
+\echo TEST 4.3 = #t#Set a bit
+select veil_bitmap_hash_setbit('role_privs', 'wibble', 20001);
+
+-- Test a bit
+\echo TEST 4.4 = #t#Check that it is now true
+select veil_bitmap_hash_testbit('role_privs', 'wibble', 20001);
+
+-- Record the current bitmap hash details for testset 4a
+\echo PREP
+create table my_text (contents text);
+insert into my_text(contents) select veil_serialise('role_privs');
+
+-- Clear the array
+\echo TEST 4.5 = #t#Clear the hash
+select veil_clear_bitmap_hash('role_privs');
+
+-- Test a bit
+\echo TEST 4.6 = #f#Check that bit again
+select veil_bitmap_hash_testbit('role_privs', 'wibble', 20001);
+commit;
+
+-- Bitmap Ref tests
+
+\echo PREP
+select veil_share('shared_bitmap_ref');
+
+\echo TEST 4.7 ~ #ERROR.*illegal#Attempt to create shared bitmap hash
+select veil_bitmap_from_hash('shared_bitmap_ref', 'role_privs', 'wibble');
+
+\echo TEST 4.8 ~ #session_bitmap_refx#Get bitmap ref from bitmap hash
+select veil_bitmap_from_hash('session_bitmap_ref', 'role_privs', 'wibble') || 'x';
+
+-- Check for bitmap ref not in same transaction
+\echo TEST 4.9 ~ #ERROR.*not.*defined#Check ref in transaction
+select veil_bitmap_setbit('session_bitmap_ref', 20001);
+
+-- Now set some bits and display them
+\echo PREP
+begin;
+select veil_bitmap_from_hash('session_bitmap_ref', 'role_privs', 'wibble');
+
+select veil_bitmap_setbit('session_bitmap_ref', 20001),
+       veil_bitmap_setbit('session_bitmap_ref', 20003);
+
+\echo TEST 4.10 ~ #2 *| *20001 *| *20003#Test bits after setting them
+select count(*), min(veil_bitmap_hash_bits), max(veil_bitmap_hash_bits)
+from   veil_bitmap_hash_bits('role_privs', 'wibble');
+commit;
+
+\echo PREP
+begin;
+-- Union tests
+select veil_bitmap_from_hash('session_bitmap_ref', 'role_privs', 'rubble');
+
+select veil_union_from_bitmap_hash('session_bitmap_ref', 'role_privs', 'wibble');
+
+\echo TEST 4.11 ~ #2 *| *20001 *| *20003#Union and then test bits
+begin;
+select count(*), min(veil_bitmap_hash_bits), max(veil_bitmap_hash_bits)
+from   veil_bitmap_hash_bits('role_privs', 'rubble');
+
+\echo PREP
+-- Intersect tests
+select veil_bitmap_hash_setbit('role_privs', 'rubble', 20002);
+select veil_bitmap_hash_clearbit('role_privs', 'rubble', 20001);
+select veil_intersect_from_bitmap_hash('session_bitmap_ref', 'role_privs', 'wibble');
+
+\echo TEST 4.12 ~ #1 *| *20003 *| *20003#Intersect and test bits
+select count(*), min(veil_bitmap_hash_bits), max(veil_bitmap_hash_bits)
+from   veil_bitmap_hash_bits('role_privs', 'rubble');
+
+commit;
+
+-- Test ranges of bitmap hash
+
+-- Output from the next test is split across two lines so we split the test
+\echo TEST 4.13 ~ #wibble#Test bitmap hash entry (a)
+select * from veil_bitmap_hash_entries('role_privs');
+
+\echo TEST 4.14 ~ #rubble#Test bitmap hash entry (b)
+select * from veil_bitmap_hash_entries('role_privs');
+
+\echo TEST 4.15 ~ #20001.*20070#Test range of bitmaps in hash
+select * from veil_bitmap_hash_range('role_privs');
+
+-- Test shared bitmap array
+\echo PREP
+select veil_share('shared_role_privs2');
+
+\echo TEST 4.16 ~ #ERROR.*illegal.*BitmapHash#Attempt to create shared bitmap hash
+select veil_init_bitmap_hash('shared_role_privs2', 'privs_range');
+
+-- Test union_into
+\echo PREP
+select veil_bitmap_hash_setbit('role_privs', 'rubble', 20002);
+select veil_union_into_bitmap_hash('role_privs', 'rubble',
+                 veil_bitmap_from_hash('session_bitmap_ref',
+                                       'role_privs', 'wibble'));
+
+\echo TEST 4.17 ~ #3.*20001.*20003#Test union into bitmap hash
+select count(*), min(veil_bitmap_hash_bits), max(veil_bitmap_hash_bits)
+from veil_bitmap_hash_bits('role_privs', 'rubble');
+
+\echo TEST 4.18 ~ #truex#Check for defined bitmap in hash
+select veil_bitmap_hash_key_exists('role_privs', 'rubble') || 'x';
+
+\echo TEST 4.19 ~ #falsex#Check for undefined bitmap in hash
+select veil_bitmap_hash_key_exists('role_privs', 'bubble') || 'x';
+EOF
+
+    do_test 4b <<EOF   
+\echo PREP
+select veil_deserialise(contents) from my_text;
+drop table my_text;
+
+-- Test a bit
+\echo TEST 4.20 = #t#Check a bit in the deserialised bitmap hash
+select veil_bitmap_hash_testbit('role_privs', 'wibble', 20001);
+
+\echo TEST 4.21 ~ #1 *| *20003 *| *20003#Count those bits
+select count(*), min(veil_bitmap_hash_bits), max(veil_bitmap_hash_bits)
+from   veil_bitmap_hash_bits('role_privs', 'wibble');
+
+EOF
+}
+
+
+regress_5()
+{
+    echo - 
+    echo "- ...test set 5: control functions..."
+
+    do_test 5 <<EOF    
+\echo PREP IGNORE
+-- Reload default version of init to check that it fails.
+`define_veil`
+
+\echo TEST 5.1 ~ #ERROR.*veil_init#Default init
+select veil_init_range('range_x', 1, 10);
+EOF
+
+    # Need a new session in order to perform init again
+    do_test 5 <<EOF    
+\echo PREP
+-- Restore regression version of init
+create or replace
+function veil_init(bool) returns bool as '
+begin
+    return true;
+end;
+' language plpgsql;
+
+\echo TEST 5.2 = #10#Demo init
+select veil_init_range('range_x', 1, 10);
+
+\echo PREP
+\echo TEST 5.3 = #t#Reset once
+select veil_perform_reset();
+
+\echo TEST 5.4 = #1#Check variables after reset
+select count(*) from veil_variables();
+
+\echo TEST 5.5 = #t#Reset again
+select veil_perform_reset();
+
+\echo TEST 5.6 = #2#Check variables again
+select count(*) from veil_variables();
+
+-- Try performing a reset with the standard version of init.  This 
+-- will prevent the reset from completing and leave the system in an
+-- undesirable state.
+
+\echo PREP IGNORE
+-- Reload default version of init to check that it fails.
+`define_veil`
+\echo TEST 5.7 ~ #ERROR.*veil_init#Reset with failing veil_init
+select veil_perform_reset();
+
+-- Now subsequent attempts to perform reset will fail.
+\echo TEST 5.8 ~ #^ *f *\$#Failing reset (context switch was left incomplete)
+select veil_perform_reset();
+
+\echo TEST 5.9 ~ #WARNING.*reset#Again (looking for WARNING message)
+select veil_perform_reset();
+
+-- Use force_reset to fix this problem.
+\echo TEST 5.10 ~ #^ *t *\$#Force reset
+select veil_force_reset();
+
+-- And now try normal reset again
+\echo PREP
+-- Restore regression version of init
+create or replace
+function veil_init(bool) returns bool as '
+begin
+    return true;
+end;
+' language plpgsql;
+
+\echo TEST 5.11 = #t#Reset again
+select veil_perform_reset();
+
+-- Test that existing session retains its variables when reset is performed
+-- by another session
+
+\echo PREP
+select veil_share('bitmap_x');
+select veil_init_range('range_x', 1, 100);
+select veil_init_bitmap('bitmap_x', 'range_x');
+select veil_bitmap_setbit('bitmap_x', 2);
+select veil_bitmap_setbit('bitmap_x', 3);
+select veil_bitmap_setbit('bitmap_x', 5);
+select veil_bitmap_setbit('bitmap_x', 7);
+select veil_bitmap_setbit('bitmap_x', 11);
+select veil_bitmap_setbit('bitmap_x', 13);
+select veil_bitmap_setbit('bitmap_x', 17);
+select veil_bitmap_setbit('bitmap_x', 19);
+select veil_bitmap_setbit('bitmap_x', 23);
+select veil_bitmap_setbit('bitmap_x', 29);
+select veil_bitmap_setbit('bitmap_x', 31);
+select veil_bitmap_setbit('bitmap_x', 37);
+select veil_bitmap_setbit('bitmap_x', 41);
+select veil_bitmap_setbit('bitmap_x', 43);
+select veil_bitmap_setbit('bitmap_x', 47);
+select veil_bitmap_setbit('bitmap_x', 53);
+select veil_bitmap_setbit('bitmap_x', 59);
+select veil_bitmap_setbit('bitmap_x', 61);
+select veil_bitmap_setbit('bitmap_x', 67);
+select veil_bitmap_setbit('bitmap_x', 71);
+select veil_bitmap_setbit('bitmap_x', 73);
+select veil_bitmap_setbit('bitmap_x', 79);
+select veil_bitmap_setbit('bitmap_x', 83);
+select veil_bitmap_setbit('bitmap_x', 89);
+select veil_bitmap_setbit('bitmap_x', 97);
+begin;
+\echo TEST 5.12 = #25#Original bitmap before reset from other session
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+\! $0 -T 5a
+
+\echo - ...(back from 5a)...
+\echo TEST 5.19 = #25#Original Bitmap (from original transaction)
+select count(*) from veil_bitmap_bits('bitmap_x');
+select * from veil_variables();
+commit;
+
+\echo TEST 5.20 = #15#New bitmap created by other session
+begin;
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+\! $0 -T 5b
+
+\echo - ...(back from 5b)...
+\echo TEST 5.98 = #15#Original Bitmap (from original transaction)
+select count(*) from veil_bitmap_bits('bitmap_x');
+select * from veil_variables();
+commit;
+
+
+\echo TEST 5.99 = #24#New bitmap created by other session
+begin;
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+
+EOF
+}
+
+regress_5a()
+{
+    echo - 
+    echo "- ...test set 5a: reset from second session (second process)..."
+    do_test 5a <<EOF   
+
+\echo TEST 5.13 = #25#Bitmap variable from other session with bits
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+\echo TEST 5.14 = #t#Successful reset
+select count(*) from veil_bitmap_bits('bitmap_x');
+select veil_perform_reset();
+
+\echo TEST 5.15 ~ #ERROR.*Undefined#No bitmap variable from other session
+select veil_share('bitmap_x');
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+\echo TEST 5.16 = #f#Failed reset as session still in use
+select veil_perform_reset();
+
+\echo TEST 5.17 ~ #WARNING.*reset#Failed reset as session still in use (again)
+select veil_perform_reset();
+
+\echo PREP
+select veil_init_range('range_x', 1, 100);
+select veil_init_bitmap('bitmap_x', 'range_x');
+select veil_bitmap_setbit('bitmap_x', 1);
+select veil_bitmap_setbit('bitmap_x', 4);
+select veil_bitmap_setbit('bitmap_x', 6);
+select veil_bitmap_setbit('bitmap_x', 8);
+select veil_bitmap_setbit('bitmap_x', 10);
+select veil_bitmap_setbit('bitmap_x', 14);
+select veil_bitmap_setbit('bitmap_x', 16);
+select veil_bitmap_setbit('bitmap_x', 60);
+select veil_bitmap_setbit('bitmap_x', 64);
+select veil_bitmap_setbit('bitmap_x', 72);
+select veil_bitmap_setbit('bitmap_x', 75);
+select veil_bitmap_setbit('bitmap_x', 78);
+select veil_bitmap_setbit('bitmap_x', 82);
+select veil_bitmap_setbit('bitmap_x', 88);
+select veil_bitmap_setbit('bitmap_x', 95);
+
+EOF
+}
+
+regress_5b()
+{
+    echo - 
+    echo "- ...test set 5b: (like 5a but with opposite contexts)..."
+    do_test 5b <<EOF   
+
+\echo TEST 5.21 = #15#Bitmap variable from other session with bits
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+\echo TEST 5.22 = #t#Successful reset
+select count(*) from veil_bitmap_bits('bitmap_x');
+select veil_perform_reset();
+
+\echo TEST 5.23 ~ #ERROR.*Undefined#No bitmap variable from other session
+select veil_share('bitmap_x');
+select count(*) from veil_bitmap_bits('bitmap_x');
+
+\echo TEST 5.24 = #f#Failed reset as session still in use
+select veil_perform_reset();
+
+\echo TEST 5.25 ~ #WARNING.*reset#Failed reset as session still in use (again)
+select veil_perform_reset();
+
+\echo PREP
+select veil_init_range('range_x', 1, 100);
+select veil_init_bitmap('bitmap_x', 'range_x');
+select veil_init_bitmap('bitmap_x', 'range_x');
+select veil_bitmap_setbit('bitmap_x', 2);
+select veil_bitmap_setbit('bitmap_x', 3);
+select veil_bitmap_setbit('bitmap_x', 5);
+select veil_bitmap_setbit('bitmap_x', 7);
+select veil_bitmap_setbit('bitmap_x', 11);
+select veil_bitmap_setbit('bitmap_x', 13);
+select veil_bitmap_setbit('bitmap_x', 17);
+select veil_bitmap_setbit('bitmap_x', 19);
+select veil_bitmap_setbit('bitmap_x', 23);
+select veil_bitmap_setbit('bitmap_x', 29);
+select veil_bitmap_setbit('bitmap_x', 31);
+select veil_bitmap_setbit('bitmap_x', 37);
+select veil_bitmap_setbit('bitmap_x', 41);
+select veil_bitmap_setbit('bitmap_x', 43);
+select veil_bitmap_setbit('bitmap_x', 47);
+select veil_bitmap_setbit('bitmap_x', 53);
+select veil_bitmap_setbit('bitmap_x', 59);
+select veil_bitmap_setbit('bitmap_x', 61);
+select veil_bitmap_setbit('bitmap_x', 67);
+select veil_bitmap_setbit('bitmap_x', 71);
+select veil_bitmap_setbit('bitmap_x', 73);
+select veil_bitmap_setbit('bitmap_x', 79);
+select veil_bitmap_setbit('bitmap_x', 83);
+select veil_bitmap_setbit('bitmap_x', 89);
+
+EOF
+}
+
+
+regress_6()
+{
+    echo - 
+    echo "- ...test set 6: veil_init() doing something useful..."
+
+    do_test 6 <<EOF    
+\echo PREP IGNORE
+-- Reload default version of init to check that it fails.
+`define_veil`
+\echo TEST 6.1 ~ #ERROR.*veil_init#Default init
+select veil_init_range('range_x', 1, 10);
+
+\echo PREP IGNORE
+-- Init function with shared variables
+create or replace
+function veil_init(bool) returns bool as '
+begin
+    perform veil_share(''shared1'');
+    perform veil_share(''shared2'');
+    perform veil_int4_set(''shared1'', 123);
+    return true;
+end
+'
+language plpgsql;
+
+select veil_init(true);
+
+\echo TEST 6.2 ~ #shared2.*Undefined.*t#Undefined shared variable
+select * from veil_variables();
+\echo TEST 6.3 ~ #shared1.*Int4.*t#Defined shared variable
+select * from veil_variables();
+
+\echo PREP IGNORE
+select veil_perform_reset();
+
+\echo TEST 6.4 ~ #shared2.*Undefined.*t#Undefined shared variable
+select * from veil_variables();
+\echo TEST 6.5 ~ #shared1.*Int4.*t#Defined shared variable
+select * from veil_variables();
+
+EOF
+}
+
+
+regress_7()
+{
+    echo - 
+    echo "- ...test set 7: veil_init() triggering SPI bug..."
+
+    do_test 7 <<EOF    
+\echo PREP IGNORE
+-- Reload default version of init to check that it fails.
+DROP DATABASE regressdb2;
+CREATE DATABASE regressdb2;
+\c regressdb2;
+`define_veil`
+
+CREATE PROCEDURAL LANGUAGE plpgsql;
+EOF
+    do_test 7 <<EOF    
+CREATE or replace FUNCTION veil_init(doing_reset boolean) RETURNS  
+boolean
+     AS \$\$
+begin
+     perform veil_share('priv_id_range');
+     return true;
+end
+\$\$
+     LANGUAGE plpgsql;
+
+
+\echo TEST 7.1 = #9#Test for SPI bug
+SELECT veil_int4_set('user_id', 1) + 8;
+DROP DATABASE regressdb2;
+EOF
+}
+
+
+tests_done()
+{
+    echo "COMPLETE"
+}
+
+db_regress()
+{
+    echo Performing regression tests...
+    regress_1  # Variables
+    regress_2  # Bitmaps
+    regress_3   # Bitmap Arrays
+    regress_4   # Bitmap Hashes
+    regress_5   # Control Functions and context switches
+    regress_6   # More context switches - tracking bug reported by Phil Frost
+    regress_7   # Tests for specific bugs
+    tests_done
+}
+
+db_test()
+{
+    db_create $1
+    (db_build_schema | psql_clean regress.log) || bail building schema
+    if [ "x$2" = "xtest" ]; then
+   db_regress 2>&1 | psql_collate regress.log; STATUS=$?
+   if [ "x$3" = "xdrop" ]; then
+       db_drop
+   fi  
+    fi
+}
+
+usage()
+{
+    prog=`basename $0`
+    echo "
+Usage: ${prog} -h
+       ${prog} -c
+       ${prog} -d
+       ${prog} [-r] -b
+       ${prog} [-r] -t <testset#>...
+       ${prog} [-r] -T <testset#>...
+       ${prog}
+
+With no parameters, ${prog} creates a test database, runs all tests,
+and finally drops the database.  If tests fail, the database will not be 
+automatically dropped.
+
+${prog} expects to be able to create a database locally called regressdb
+using the psql command.  This must be available in \$PATH.
+
+All tests are run using psql, with the output being parsed and tidied
+for human consumption.  The raw output is available in regress.log in
+case the parser eats something important.
+
+The -h option prints this usage message.
+-b causes the test database to just be created
+-c runs the preload library check and reports on status
+-d causes the test database to be dropped
+-t runs specified test sets against an already created database
+-T runs specified test sets against an already created database with 
+no parsing and tidying of psql output.
+
+If you cannot create regressdb locally, you are going to have to hack the 
+script.  Sorry.
+"
+
+}
+
+# Return 0 if eq, 1 if $1 > $2, 2 if $2 > $1
+simple_cmp() 
+{
+    if [ $1 -eq $2 ]; then return 0; fi
+    if [ $1 -gt $2 ]; then return 1; fi
+    return 2
+}
+
+# Return 0 if eq, 1 if $1 > $2, 2 if $2 > $1
+version_cmp() {
+    version1=$1
+    version2=$2
+    major1=`echo ${version1} | cut -d. -f1`
+    minor1=`echo ${version1} | cut -d. -f2`
+    major2=`echo ${version2} | cut -d. -f1`
+    minor2=`echo ${version2} | cut -d. -f2`
+    simple_cmp $major1 $major2 && simple_cmp $minor1 $minor2
+    return $?
+}
+
+
+# Check whether shared_preload_libraries has been defined appropriately.
+# Returns the following result codes:
+# 0 - shared_preload_libraries is defined correctly, or we are using
+#     a version of postgres prior to 8.2 so no warnings are necessary
+#     and we can use veil_interface.sql
+# 1 - shared_preload_libraries has not been defined
+# 2 - shared_preload_libraries does not contain veil shared library
+# 3 - shared_preload_libraries references old (or missing) version of
+#     veil shared library
+#
+check_preload_libraries()
+{
+    version=`psql --version | grep '[0-9]\.[0-9]'`
+    version=`echo ${version} | sed -e 's/[^0-9]*\([0-9]*\.[0-9]*\).*/\1/'`
+    # This is version 8.2 or higher, so we can expect some cooperation
+    # from postgres wrt shared memory.
+    # To get this cooperation the veil shared library must be preloaded
+    # so, test whether this is being done.
+    mylib=`dirname $0`/../veil.so
+    setting=`psql -d template1 -q -t <<EOF
+select setting from pg_settings where name = 'shared_preload_libraries';
+EOF`
+    libs=`echo "${setting}" | sed -e 's/,/ /g' | xargs -n1`
+    if [ "x${libs}" = "x" ]; then
+        # No definition of shared_preload_libraries
+        return 1
+    else
+        echo "${libs}" | grep veil >/dev/null
+        if [ $? -ne 0 ]; then
+            # Definition of shared_preload_libraries does not contain veil
+           return 2
+        else
+            echo "${libs}" | while read a; do
+                cmp ${a} ${mylib} 2>/dev/null && echo FOUND
+            done | grep FOUND >/dev/null; status=$?
+            if [ ${status} -ne 0 ]; then
+                # Definition of shared_preload_libraries has different 
+               # version of veil library from that built locally
+           return 3
+           fi
+        fi
+    fi 
+}
+
+preload_library_message()
+{
+    if [ "x$1" = "x1" -o "x$1" = "x2" ]; then
+   echo "
+WARNING: The veil shared library is not defined in shared_preload_libraries 
+(which is defined in postgresql.conf) Without this definition Veil will not run
+"
+    elif [ "x$1" = "x3" ]; then
+   echo "
+WARNING: The version of veil.so in shared_preload_libraries (defined in
+postgresql.conf) is not up to date (not the same as the latest build).
+"
+    fi
+}
+
+# Load helper functions
+export dir=`dirname $0`
+. ${dir}/../tools/psql_funcs.sh
+
+owner=`whoami`
+
+export STATUS=0
+
+if [ "x$1" = "x-h" ]; then
+    usage; exit
+elif [ "x$1" = "x-c" ]; then
+    # Check and report on the preload library situation
+    check_preload_libraries; preload_status=$?
+    rm -f regress.log
+    preload_library_message ${preload_status}
+elif [ "x$1" = "x-d" ]; then
+    # Drop the database
+    db_drop
+elif [ "x$1" = "x-b" ]; then
+    # Just build the database
+    check_preload_libraries; preload_status=$?
+    rm -f regress.log
+    db_test ${owner} ${preload_status}
+    preload_library_message ${preload_status}
+elif [ "x$1" = "x-n" ]; then
+    # Don't drop the database after running the tests
+    rm -f regress.log
+    check_preload_libraries; preload_status=$?
+    db_test ${owner} test 
+    preload_library_message ${preload_status}
+elif [ "x$1" = "x-t" ]; then
+    # Run specific tests.  No build or drop.
+    (
+        while [ "x$2" != "x" ]; do 
+       regress_$2 2>&1 
+       shift
+        done
+        tests_done
+    ) | psql_collate regress.log
+    check_preload_libraries; preload_status=$?
+    preload_library_message ${preload_status}
+elif [ "x$1" = "x-T" ]; then
+    # Run specific tests in raw form (no parser).  No build or drop.
+    while [ "x$2" != "x" ]; do 
+   regress_$2 2>&1
+   shift
+    done
+else
+    # Build, run all tests and drop.
+    rm -f regress.log
+    check_preload_libraries; preload_status=$?
+    if [ ${preload_status} -gt 1 ]; then
+   preload_library_message ${preload_status}
+   echo "ERROR: Refusing to run tests as shared_preload_library is defined
+but does not reference an up to date veil shared library." 1>&2
+   exit 5
+    fi
+    db_test ${owner} test drop
+    preload_library_message ${preload_status}
+fi
+
+exit ${STATUS}
\ No newline at end of file
diff --git a/regress/regress_data.sql b/regress/regress_data.sql
new file mode 100644 (file)
index 0000000..cd0b3b8
--- /dev/null
@@ -0,0 +1,51 @@
+------------
+-- regress_data.sql
+--
+--      Data creation script for Veil regression tests
+--
+--      Copyright (c) 2005 - 2011 Marc Munro
+--      Author:  Marc Munro
+-- License: BSD
+--
+-- Data for regression tests
+
+\echo - Populating tables...
+begin;
+
+insert into roles
+       (role_id, role_name)
+values (10001, 'DBA');
+
+insert into roles
+       (role_id, role_name)
+values (10002, 'GRUNT');
+
+insert into privileges
+       (privilege_id, privilege_name)
+values (20001, 'select_x');
+
+insert into privileges
+       (privilege_id, privilege_name)
+values (20002, 'select_y');
+
+insert into privileges
+       (privilege_id, privilege_name)
+values (20070, 'select_z');
+
+insert into role_privileges
+       (role_id, privilege_id)
+values (10001, 20001);
+
+insert into role_privileges
+       (role_id, privilege_id)
+values (10001, 20002);
+
+insert into role_privileges
+       (role_id, privilege_id)
+values (10001, 20070);
+
+insert into role_privileges
+       (role_id, privilege_id)
+values (10002, 20001);
+
+commit;
\ No newline at end of file
diff --git a/regress/regress_tables.sql b/regress/regress_tables.sql
new file mode 100644 (file)
index 0000000..7f5ab45
--- /dev/null
@@ -0,0 +1,46 @@
+------------
+-- regress_tables.sql
+--
+--      Table creation script for Veil regression tests.
+--
+--      Copyright (c) 2005 - 2011 Marc Munro
+--      Author:  Marc Munro
+-- License: BSD
+--
+------------
+-- Tables for regression tests
+
+\echo - Creating tables...
+\echo - ...privileges...
+create table privileges (
+    privilege_id   integer not null,
+    privilege_name varchar(80) not null
+);
+alter table privileges add constraint privilege__pk
+    primary key(privilege_id);
+
+\echo - ...role...
+create table roles (
+    role_id    integer not null,
+    role_name  varchar(80) not null
+);
+alter table roles add constraint role__pk
+    primary key(role_id);
+
+\echo - ...role_privileges...
+create table role_privileges (
+    role_id        integer not null,
+    privilege_id   integer not null
+);
+
+alter table role_privileges add constraint role_privilege__pk
+    primary key(role_id, privilege_id);
+
+alter table role_privileges add constraint role_privilege__role_fk
+    foreign key(role_id)
+    references roles(role_id);
+
+alter table role_privileges add constraint role_privilege__priv_fk
+    foreign key(privilege_id)
+    references privileges(privilege_id);
+
diff --git a/src/GNUmakefile b/src/GNUmakefile
new file mode 100644 (file)
index 0000000..bd5dd5c
--- /dev/null
@@ -0,0 +1,14 @@
+# ----------
+# GNUmakefile
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# ----------
+#
+
+all:
+
+%::
+   cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
\ No newline at end of file
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..6865a95
--- /dev/null
@@ -0,0 +1,43 @@
+# Makefile
+#
+#      Makefile for src directory of Veil
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# Do not attempt to use this makefile explicitly: its targets are available
+# and should be built from the main GNUmakefile in the parent directory.
+# The GNUmakefile in this directory will build using the parent GNUmakefile
+# so using make <target> in this directory will work as long as you don't
+# try to specify this makefile.  It even works with emacs compile and 
+# next-error functions though the number of makefiles involved seems a 
+# little alarming at first.
+# The whole strangeness of this makefile hierarchy derives from a, 
+# possibly misguided, attempt to avoid recursive make (see the article
+# "Recursive make considered harmful" for a rationale).
+
+
+SOURCES = src/veil_bitmap.c src/veil_config.c src/veil_datatypes.c \
+       src/veil_interface.c src/veil_mainpage.c src/veil_query.c \
+       src/veil_serialise.c src/veil_shmem.c src/veil_utils.c \
+       src/veil_variables.c
+
+ifdef EXTENSION
+   LIBDIR=$(DESTDIR)$(datadir)/extension
+else
+   LIBDIR=$(DESTDIR)$(pkglibdir)
+endif
+
+INSTALLED_LIB = $(LIBDIR)/$(addsuffix $(DLSUFFIX), veil)
+TRIAL_LIB = $(BUILD_DIR)/$(addsuffix $(DLSUFFIX), veil_trial)
+VEIL_LIB = $(BUILD_DIR)/$(addsuffix $(DLSUFFIX), veil)
+HEADERS = $(wildcard src/*.h)
+SRC_CLEAN = $(VEIL_LIB)
+
+all: $(VEIL_CONTROL)
+
+$(VEIL_CONTROL): src/veil_interface.sqs
+   @echo Creating $(VEIL_CONTROL)
+   @sed -e 's!@LIBPATH@!$$libdir/veil!g' <$< >$(VEIL_CONTROL)
+
diff --git a/src/veil_bitmap.c b/src/veil_bitmap.c
new file mode 100644 (file)
index 0000000..483122e
--- /dev/null
@@ -0,0 +1,605 @@
+/**
+ * @file   veil_bitmap.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Functions for manipulating Bitmaps, BitmapHashes and BitmapArrays
+ * 
+ */
+
+#include <stdio.h>
+#include "postgres.h"
+#include "veil_datatypes.h"
+#include "veil_funcs.h"
+
+
+
+/**
+ * Array of bit positions for int32, indexed by bitno.
+ */
+static
+uint32 bitmasks[] = {0x00000001, 0x00000002, 0x00000004, 0x00000008, 
+                    0x00000010, 0x00000020, 0x00000040, 0x00000080, 
+                    0x00000100, 0x00000200, 0x00000400, 0x00000800, 
+                    0x00001000, 0x00002000, 0x00004000, 0x00008000,
+                    0x00010000, 0x00020000, 0x00040000, 0x00080000,
+                    0x00100000, 0x00200000, 0x00400000, 0x00800000,
+                    0x01000000, 0x02000000, 0x04000000, 0x08000000,
+                    0x10000000, 0x20000000, 0x40000000, 0x80000000};
+
+
+/** 
+ * Clear all bits in a ::Bitmap.
+ * 
+ * @param bitmap The ::Bitmap in which all bits are to be cleared
+ */
+void
+vl_ClearBitmap(Bitmap *bitmap)
+{
+   int elems = ARRAYELEMS(bitmap->bitzero, bitmap->bitmax);
+   int i;
+   
+   for (i = 0; i < elems; i++) {
+       bitmap->bitset[i] = 0;
+   }
+}
+
+/** 
+ * Return a newly initialised (empty) ::Bitmap.  The bitmap may already
+ * exist in which case it will be re-used if possible.  The bitmap may
+ * be created in either session or shared memory depending on the value
+ * of shared.
+ * 
+ * @param p_bitmap Pointer to an existing bitmap if one exists
+ * @param shared Whether to create the bitmap in shared memory
+ * @param min The smallest bit to be stored in the bitmap
+ * @param max The largest bit to be stored in the bitmap
+ */
+void
+vl_NewBitmap(Bitmap **p_bitmap, bool shared,
+            int32 min, int32 max)
+{
+   Bitmap *bitmap = *p_bitmap;
+   int     elems = ARRAYELEMS(min, max);
+
+   if (bitmap) {
+       int cur_elems = ARRAYELEMS(bitmap->bitzero, bitmap->bitmax);
+       /* OK, there is an old bitmap in place.  If it is the same size
+        * or larger than we need we will re-use it, otherwise we will
+        * dispose of it and get a new one. */
+
+       if (elems <= cur_elems) {
+           vl_ClearBitmap(bitmap);
+       }
+       else {
+           if (shared) {
+               vl_free(bitmap);
+           }
+           else {
+               pfree(bitmap);
+           }
+           bitmap = NULL;
+       }
+   }
+   if (!bitmap) {
+       /* We need a new bitmap */
+       if (shared) {
+           bitmap = vl_shmalloc(sizeof(Bitmap) + (sizeof(int32) * elems));
+       }
+       else {
+           bitmap = vl_malloc(sizeof(Bitmap) + (sizeof(int32) * elems));
+       }
+   }
+
+   DBG_SET_CANARY(*bitmap);
+   DBG_SET_ELEMS(*bitmap, elems);
+   DBG_SET_TRAILER(*bitmap, bitset);
+   bitmap->type = OBJ_BITMAP;
+   bitmap->bitzero = min;
+   bitmap->bitmax = max;
+   vl_ClearBitmap(bitmap);
+
+   DBG_TEST_CANARY(*bitmap);
+   DBG_TEST_TRAILER(*bitmap, bitset);
+   *p_bitmap = bitmap;
+}
+
+/** 
+ * Set a bit within a ::Bitmap.  If the bit is outside of the acceptable
+ * range, raise an error.
+ * 
+ * @param bitmap The ::Bitmap within which the bit is to be set. 
+ * @param bit The bit to be set.
+ */
+void
+vl_BitmapSetbit(Bitmap *bitmap,
+               int32 bit)
+{
+    int relative_bit = bit - BITZERO(bitmap->bitzero);
+    int element = BITSET_ELEM(relative_bit);
+
+   if ((bit > bitmap->bitmax) ||
+       (bit < bitmap->bitzero)) 
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap range error"),
+                errdetail("Bit (%d) not in range %d..%d.  ", bit,
+                          bitmap->bitzero, bitmap->bitmax)));
+   }
+
+   DBG_CHECK_INDEX(*bitmap, element);
+    bitmap->bitset[element] |= bitmasks[BITSET_BIT(relative_bit)];
+   DBG_TEST_CANARY(*bitmap);
+   DBG_TEST_TRAILER(*bitmap, bitset);
+}
+
+/** 
+ * Clear a bit within a ::Bitmap.  If the bit is outside of the acceptable
+ * range, raise an error.
+ * 
+ * @param bitmap The ::Bitmap within which the bit is to be cleared. 
+ * @param bit The bit to be cleared.
+ */
+void
+vl_BitmapClearbit(Bitmap *bitmap,
+                 int32 bit)
+{
+    int relative_bit = bit - BITZERO(bitmap->bitzero);
+    int element = BITSET_ELEM(relative_bit);
+
+   if ((bit > bitmap->bitmax) ||
+       (bit < bitmap->bitzero)) 
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap range error"),
+                errdetail("Bit (%d) not in range %d..%d.  ", bit,
+                          bitmap->bitzero, bitmap->bitmax)));
+   }
+
+    bitmap->bitset[element] &= ~(bitmasks[BITSET_BIT(relative_bit)]);
+}
+
+/** 
+ * Test a bit within a ::Bitmap.  If the bit is outside of the acceptable
+ * range return false.
+ * 
+ * @param bitmap The ::Bitmap within which the bit is to be set. 
+ * @param bit The bit to be tested.
+ * 
+ * @return True if the bit is set, false otherwise.
+ */
+bool
+vl_BitmapTestbit(Bitmap *bitmap,
+                int32 bit)
+{
+    int relative_bit = bit - BITZERO(bitmap->bitzero);
+    int element = BITSET_ELEM(relative_bit);
+
+   if ((bit > bitmap->bitmax) ||
+       (bit < bitmap->bitzero)) 
+   {
+       return false;
+   }
+
+    return (bitmap->bitset[element] & bitmasks[BITSET_BIT(relative_bit)]) != 0;
+}
+
+/** 
+ * Create the union of two bitmaps, updating the first with the result.
+ * 
+ * @param target The ::Bitmap into which the result will be placed.
+ * @param source The ::Bitmap to be unioned into target.
+ */
+void
+vl_BitmapUnion(Bitmap *target,
+              Bitmap *source)
+{
+   int i;
+   int elems = ARRAYELEMS(target->bitzero, target->bitmax);
+
+   /* TODO: Make this tolerant of range mismatches. */
+   if ((target->bitzero != source->bitzero) ||
+       (target->bitmax != source->bitmax))
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Incompatible bitmaps"),
+                errdetail("Target range is %d..%d.  Source range %d..%d.",
+                          target->bitzero, target->bitmax,
+                          source->bitzero, source->bitmax)));
+   }
+   for (i = 0; i < elems; i++) {
+       target->bitset[i] |= source->bitset[i];
+   }
+}
+
+/** 
+ * Create the intersection of two bitmaps, updating the first with the
+ * result.
+ * 
+ * @param target The ::Bitmap into which the result will be placed.
+ * @param source The ::Bitmap to be intersected into target.
+ */
+void
+vl_BitmapIntersect(Bitmap *target,
+                  Bitmap *source)
+{
+   int i;
+   int elems = ARRAYELEMS(target->bitzero, target->bitmax);
+
+   /* TODO: Make this tolerant of range mismatches. */
+   if ((target->bitzero != source->bitzero) ||
+       (target->bitmax != source->bitmax))
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Incompatible bitmaps"),
+                errdetail("Target range is %d..%d.  Source range %d..%d.",
+                          target->bitzero, target->bitmax,
+                          source->bitzero, source->bitmax)));
+   }
+   for (i = 0; i < elems; i++) {
+       target->bitset[i] &= source->bitset[i];
+   }
+}
+
+/** 
+ * Return the next set bit in the ::Bitmap.
+ * 
+ * @param bitmap The ::Bitmap being scanned.
+ * @param bit The starting bit from which to scan the bitmap
+ * @param found Boolean that will be set to true when a set bit has been
+ * found.
+ * 
+ * @return The bit id of the found bit, or zero if no set bits were found. 
+ */
+int32
+vl_BitmapNextBit(Bitmap *bitmap,
+                int32 bit,
+                bool *found)
+{
+   while (bit <= bitmap->bitmax) {
+       if (vl_BitmapTestbit(bitmap, bit)) {
+           *found = true;
+           return bit;
+       }
+       bit++;
+   }
+   *found = false;
+   return 0;
+}
+
+/** 
+ * Return a specified ::Bitmap from a ::BitmapArray.
+ * 
+ * @param bmarray The ::BitmapArray from which the result is to be
+ * returned.
+ * @param elem The index of the ::Bitmap within the array. 
+ * 
+ * @return The bitmap corresponding to the parameters, or NULL if no
+ *such entry exists within the array.
+ */
+Bitmap *
+vl_BitmapFromArray(BitmapArray *bmarray,
+                  int32 elem)
+{
+   DBG_TEST_CANARY(*bmarray);
+   DBG_TEST_TRAILER(*bmarray, bitmap);
+   if ((elem < bmarray->arrayzero) || (elem > bmarray->arraymax)) {
+       return NULL;
+   }
+   else {
+       DBG_CHECK_INDEX(*bmarray, elem - bmarray->arrayzero);
+       return bmarray->bitmap[elem - bmarray->arrayzero];
+   }
+}
+
+/** 
+ * Clear all bitmaps in the given ::BitmapArray
+ * 
+ * @param bmarray The ::BitmapArray to be cleared
+ */
+void
+vl_ClearBitmapArray(BitmapArray *bmarray)
+{
+   int bitmaps = bmarray->arraymax + 1 - bmarray->arrayzero;
+   int i;
+
+   DBG_TEST_CANARY(*bmarray);
+   DBG_TEST_TRAILER(*bmarray, bitmap);
+   for (i = 0; i < bitmaps; i++) {
+       DBG_CHECK_INDEX(*bmarray, i);
+       vl_ClearBitmap(bmarray->bitmap[i]);
+   }
+}
+
+/** 
+ * Return a newly initialised (empty) ::BitmapArray.  It may already
+ * exist in which case it will be re-used if possible.  It may
+ * be created in either session or shared memory depending on the value
+ * of shared.
+ * 
+ * @param p_bmarray Pointer to an existing bitmap if one exists.
+ * @param shared Whether to create the bitmap in shared memory
+ * @param arrayzero The lowest array index
+ * @param arraymax The highest array index
+ * @param bitzero The smallest bit to be stored in the bitmap
+ * @param bitmax The largest bit to be stored in the bitmap
+ */
+void
+vl_NewBitmapArray(BitmapArray **p_bmarray, bool shared,
+                 int32 arrayzero, int32 arraymax,
+                 int32 bitzero, int32 bitmax)
+{
+   BitmapArray *bmarray = *p_bmarray;
+   int bitsetelems = ARRAYELEMS(bitzero, bitmax);
+   int bitmaps = arraymax + 1 - arrayzero;
+   int i;
+
+   if (bmarray) {
+       /* We already have a bitmap array.  If possible, we re-use it. */
+       int cur_elems = ARRAYELEMS(bmarray->bitzero, bmarray->bitmax);
+       int cur_maps = bmarray->arraymax + 1 - bmarray->arrayzero;
+
+       DBG_TEST_CANARY(*bmarray);
+       DBG_TEST_TRAILER(*bmarray, bitmap);
+       if ((cur_elems >= bitsetelems) && (cur_maps >= bitmaps)) {
+           vl_ClearBitmapArray(bmarray);
+       }
+       else {
+           if (shared) {
+               for (i = 0; i < cur_maps; i++) {
+                   vl_free(bmarray->bitmap[i]);
+               }
+               vl_free(bmarray);
+           }
+           else {
+               for (i = 0; i < cur_maps; i++) {
+                   pfree(bmarray->bitmap[i]);
+               }
+               pfree(bmarray);
+           }
+           bmarray = NULL;
+       }
+   }
+
+   if (!bmarray) {
+       if (shared) {
+           bmarray = vl_shmalloc(sizeof(BitmapArray) + 
+                                (sizeof(Bitmap *) * bitmaps));
+
+       }
+       else {
+           bmarray = vl_malloc(sizeof(BitmapArray) + 
+                               (sizeof(Bitmap *) * bitmaps));
+       }
+
+       for (i = 0; i < bitmaps; i++) {
+           bmarray->bitmap[i] = NULL;
+           vl_NewBitmap(&(bmarray->bitmap[i]), shared, bitzero, bitmax);
+       }
+
+       bmarray->type = OBJ_BITMAP_ARRAY;
+       DBG_SET_CANARY(*bmarray);
+       DBG_SET_ELEMS(*bmarray, bitmaps);
+       DBG_SET_TRAILERP(*bmarray, bitmap);
+   }
+   bmarray->bitzero = bitzero;
+   bmarray->bitmax = bitmax;
+   bmarray->arrayzero = arrayzero;
+   bmarray->arraymax = arraymax;
+
+   for (i = 0; i < bitmaps; i++) {
+       bmarray->bitmap[i]->type = OBJ_BITMAP;
+       bmarray->bitmap[i]->bitzero = bitzero;
+       bmarray->bitmap[i]->bitmax = bitmax;
+   }
+
+   *p_bmarray = bmarray;
+}
+
+/** 
+ * Create a new hash table.  This is allocated from session memory as
+ * BitmapHashes may not be declared as shared variables.
+ * 
+ * @param name The name of the hash to be created.  Note that we prefix
+ * this with "vl_" to prevent name collisions from other subsystems.
+ * 
+ * @return Pointer to the newly created hash table.
+ */
+static HTAB *
+new_hash(char *name)
+{
+   char     vl_name[HASH_KEYLEN];
+   HTAB    *hash;
+   HASHCTL  hashctl;
+
+   (void) snprintf(vl_name, HASH_KEYLEN - 1, "vl_%s", name);
+
+   hashctl.keysize = HASH_KEYLEN;
+   hashctl.entrysize = sizeof(VarEntry);
+
+   hash = hash_create(vl_name, 200, &hashctl, HASH_ELEM);
+    return hash;
+}
+
+/** 
+ * Utility function for scanning the hash table of a BitmapHash.
+ * 
+ * @param hash The hash table being scanned
+ * @param prev The entry from which to scan, starting with NULL.
+ * 
+ * @return The next element in the hash table (a VarEntry) or NULL when
+ * the last element has already been scanned.
+ */
+VarEntry *
+vl_NextHashEntry(HTAB *hash,
+                VarEntry *prev)
+{
+   VarEntry *next;
+   static HASH_SEQ_STATUS status;
+   if (!prev) {
+       /* Initialise the hash search */
+       
+       hash_seq_init(&status, hash);
+   }
+   next = (VarEntry *) hash_seq_search(&status);
+   return (next);
+}
+
+/** 
+ * Return a newly initialised (empty) ::BitmapHash.  It may already
+ * exist in which case it will be re-used if possible.  BitmapHash
+ * variables may only be created as session (not shared) variables.
+ * 
+ * @param p_bmhash Pointer to an existing bitmap if one exists.
+ * @param name The name to be used for the hash table
+ * @param bitzero The smallest bit to be stored in the bitmap
+ * @param bitmax The largest bit to be stored in the bitmap
+ */
+void
+vl_NewBitmapHash(BitmapHash **p_bmhash, char *name,
+                int32 bitzero, int32 bitmax)
+{
+   BitmapHash *bmhash = *p_bmhash;
+
+   if (bmhash) {
+       VarEntry *entry = NULL;
+       HTAB *hash = bmhash->hash;
+       bool found;
+
+       while ((entry = vl_NextHashEntry(hash, entry))) {
+           if (entry->obj) {
+               if (entry->obj->type != OBJ_BITMAP) {
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INTERNAL_ERROR),
+                            errmsg("Bitmap hash contains invalid object %d",
+                                   entry->obj->type),
+                            errdetail("Object type is %d, expected is %d.",
+                                      entry->obj->type, OBJ_BITMAP)));
+               }
+               pfree(entry->obj);  /* Free the bitmap */
+           }
+           /* Now remove the entry from the hash */
+           (void) hash_search(hash, entry->key, HASH_REMOVE, &found);
+       }
+   }
+   else {
+       bmhash = vl_malloc(sizeof(BitmapHash));
+       bmhash->type = OBJ_BITMAP_HASH;
+       bmhash->hash = new_hash(name);
+   }
+   bmhash->bitzero = bitzero;
+   bmhash->bitmax = bitmax;
+
+   *p_bmhash = bmhash;
+}
+
+/** 
+ * Return a specified ::Bitmap from a ::BitmapHash.  Raise an error if
+ * the returned object from the hash search is not a bitmap.
+ * 
+ * @param bmhash The ::BitmapHash from which the result is to be
+ * returned.
+ * @param hashelem The key of the ::Bitmap within the hash. 
+ * 
+ * @return The bitmap corresponding to the parameters, or NULL if no
+ *such entry exists within the hash.
+ */
+Bitmap *
+vl_BitmapFromHash(BitmapHash *bmhash,
+                 char *hashelem)
+{
+   VarEntry *var;
+   Bitmap   *bitmap;
+   bool      found;
+
+   var = hash_search(bmhash->hash, hashelem, HASH_FIND, &found);
+   if (!found) {
+       return NULL;
+   }
+   
+   if (!var->obj) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("BitmapFromHash - empty VarEntry")));
+   }
+
+   if (var->obj->type != OBJ_BITMAP) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap hash contains invalid object %d",
+                       var->obj->type),
+                errdetail("Object type is %d, expected is %d.",
+                          var->obj->type, OBJ_BITMAP)));
+   }
+
+   bitmap = (Bitmap *) var->obj;
+   return bitmap;
+}
+
+/** 
+ * Create a newly allocated empty ::Bitmap to a ::BitmapHash
+ * 
+ * @param bmhash The ::BitmapHash to which to add the new ::Bitmap.
+ * @param hashelem The key for the new entry
+ * 
+ * @return Pointer to the newly allocated empty ::Bitmap.
+ */
+Bitmap *
+vl_AddBitmapToHash(BitmapHash *bmhash,
+                  char *hashelem)
+{
+   VarEntry *var;
+   Bitmap   *bitmap = NULL;
+   bool      found;
+
+   var = hash_search(bmhash->hash, hashelem, HASH_ENTER, &found);
+   if (found) {
+       if (!var->obj) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("AddBitmapToHash - empty VarEntry")));
+       }
+       if (var->obj->type != OBJ_BITMAP) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("AddBitmapToHash - type mismatch %d",
+                           var->obj->type),
+                    errdetail("Object type is %d, expected is %d.",
+                              var->obj->type, OBJ_BITMAP)));
+       }
+       return (Bitmap *) var->obj;
+   }
+
+   /* We've created a new entry.  Now create the bitmap for it. */
+
+   vl_NewBitmap(&bitmap, FALSE, bmhash->bitzero, bmhash->bitmax);
+   var->obj = (Object *) bitmap;
+   return bitmap;
+}
+
+/** 
+ * Determine whether the supplied key exists in the ::BitmapHash.
+ * 
+ * @param bmhash The ::BitmapHash to be tested
+ * @param hashelem The key to be tested
+ * 
+ * @return True if the key already exists in the hash.
+ */
+bool
+vl_BitmapHashHasKey(BitmapHash *bmhash,
+                   char *hashelem)
+{
+   VarEntry *var;
+   bool      found;
+
+   var = hash_search(bmhash->hash, hashelem, HASH_FIND, &found);
+   return found;
+}
diff --git a/src/veil_bitmap.d b/src/veil_bitmap.d
new file mode 100644 (file)
index 0000000..870b553
--- /dev/null
@@ -0,0 +1,4 @@
+src/veil_bitmap.o src/veil_bitmap.d: \
+  src/veil_bitmap.c \
+  src/veil_datatypes.h \
+  src/veil_funcs.h
diff --git a/src/veil_config.c b/src/veil_config.c
new file mode 100644 (file)
index 0000000..d8ac770
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * @file   veil_config.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2006-2011 Marc Munro
+ *     License:      BSD
+ *
+ * \endcode
+ * @brief  
+ * Code for setting and reading configuration data.
+ * 
+ */
+
+#include "postgres.h"
+#include "storage/fd.h"
+#include "utils/guc.h"
+#include "veil_version.h"
+#include "veil_funcs.h"
+
+
+/** 
+ * The number of buckets to create in the hash for shared variables.
+ * This defaults to 32 and may be defined in postgresql.conf using eg:
+ * "veil.shared_hash_elems = 64"
+ */
+static int shared_hash_elems = 32;
+
+/** 
+ * The number of databases within the db cluster that will use veil.
+ * Every veil-using database within the cluster will get the same
+ * allocation of shared memory.
+ */
+static int dbs_in_cluster = 2;
+
+/** 
+ * The size in KBytes, of each of Veil's shared memory contexts.  Veil
+ * will pre-allocate (from Postgres 8.2 onwards) twice this amount of
+ * shared memory, one for each context area.
+ * The default is 16384 Bytes and may be defined in postgresql.conf
+ * using eg: "veil.shmem_context_size = 8192"
+ */
+static int shmem_context_size = 16384;
+
+/** 
+ * Return the number of databases, within the database cluster, that
+ * will use Veil.  Each such database will be allocated 2 chunks of
+ * shared memory (of shmem_context_size), and a single LWLock.
+ * It defaults to 1 and may be defined in postgresql.conf using eg:
+ * "veil.dbs_in_cluster = 2"
+ */
+int
+veil_dbs_in_cluster()
+{
+   veil_load_config();
+   return dbs_in_cluster;
+}
+
+/** 
+ * Return the number of entries that should be allocated for shared
+ * variables in our shared hashes.  This defaults to 32 and may be
+ * defined in postgresql.conf using eg:
+ * "veil.shared_hash_elems = 64"
+ */
+int
+veil_shared_hash_elems()
+{
+   veil_load_config();
+   return shared_hash_elems;
+}
+
+/** 
+ * Return the amount of shared memory to be requested for each of the
+ * two shared memory contexts.  This variable has no effect unless
+ * shared_preload_libraries has been defined in postgresql.conf to load
+ * the Veil shared library
+ * Note that this must be large enough to allocate at least one chunk of
+ * memory for each veil-using database in the Postgres cluster.  It
+ * defaults to 16K and may be defined in postgresql.conf using eg: 
+ * "veil.shmem_context_size = 16384"
+ */
+int
+veil_shmem_context_size()
+{
+   veil_load_config();
+   return shmem_context_size;
+}
+
+/** 
+ * Retrieve Veil's GUC variables for this session.
+ */
+void
+veil_load_config()
+{
+   static bool first_time = true;
+   if (!first_time) {
+       return;
+   }
+
+   dbs_in_cluster = atoi(GetConfigOption("veil.dbs_in_cluster", FALSE));
+   shared_hash_elems = atoi(GetConfigOption("veil.shared_hash_elems", 
+                                            FALSE));
+   shmem_context_size = atoi(GetConfigOption("veil.shmem_context_size", 
+                                             FALSE));
+   first_time = false;
+}
+
+
diff --git a/src/veil_config.d b/src/veil_config.d
new file mode 100644 (file)
index 0000000..ce47906
--- /dev/null
@@ -0,0 +1,5 @@
+src/veil_config.o src/veil_config.d: \
+  src/veil_config.c \
+  src/veil_version.h \
+  src/veil_funcs.h \
+  src/veil_datatypes.h
diff --git a/src/veil_datatypes.c b/src/veil_datatypes.c
new file mode 100644 (file)
index 0000000..e37d94a
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * @file   veil_datatypes.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Code for non-bitmap datatypes.
+ * 
+ */
+
+#include "postgres.h"
+#include "veil_datatypes.h"
+#include "veil_funcs.h"
+
+
+/** 
+ * Create a new session or shared ::Range object.
+ * 
+ * @param shared Whether the object is to be created in shared (true) or
+ * session (false) memory.
+ * 
+ * @return Pointer to newly created object.
+ */
+Range *
+vl_NewRange(bool shared)
+{
+    Range *range;
+
+   if (shared) {
+       range = vl_shmalloc(sizeof(Range));
+   }
+   else {
+       range = vl_malloc(sizeof(Range));
+   }
+
+   range->type = OBJ_RANGE;
+   return range;
+}
+
+/** 
+ * Create a new session or shared ::Int4Var object.
+ * 
+ * @param shared Whether the object is to be created in shared (true) or
+ * session (false) memory.
+ * 
+ * @return Pointer to newly created object.
+ */
+Int4Var *
+vl_NewInt4(bool shared)
+{
+   Int4Var *i4v;
+
+   if (shared) {
+       i4v = vl_shmalloc(sizeof(Int4Var));
+   }
+   else {
+       i4v = vl_malloc(sizeof(Int4Var));
+   }
+   i4v->type = OBJ_INT4;
+   i4v->isnull = true;
+   return i4v;
+}
+
diff --git a/src/veil_datatypes.d b/src/veil_datatypes.d
new file mode 100644 (file)
index 0000000..2090d11
--- /dev/null
@@ -0,0 +1,4 @@
+src/veil_datatypes.o src/veil_datatypes.d: \
+  src/veil_datatypes.c \
+  src/veil_datatypes.h \
+  src/veil_funcs.h
diff --git a/src/veil_datatypes.h b/src/veil_datatypes.h
new file mode 100644 (file)
index 0000000..a37031e
--- /dev/null
@@ -0,0 +1,387 @@
+/**
+ * @file   veil_datatypes.h
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Define all Veil public datatypes 
+ * 
+ */
+
+#ifndef VEIL_DATATYPES
+/** 
+ * Prevent this header from being included multiple times.
+ */
+#define VEIL_DATATYPES 1
+
+#ifndef VEIL_DEBUG
+/**
+ * Enables various debugging constructs, such as canaries, in code and
+ * data structures.  If such debugging is required, define VEIL_DEBUG in
+ * the make invocation, eg: "make VEIL_DEBUG=1".
+ */
+// TODO: REMOVE THE FOLLOWING DEFINITION
+#define VEIL_DEBUG 1
+#endif 
+
+#if VEIL_DEBUG == 1
+/** 
+ * Value to be set in sacrificial "canary" fields.  If this value is not
+ * as expected, the canary has been killed by something inappropriately 
+ * stomping on memory.
+ */
+#define DBG_CANARY 0xca96ca96
+
+/** 
+ * Defines a canary element in a data structure.
+ */
+#define DBG_CANARY_ENTRY int32 canary;
+
+/** 
+ * Field to record the size of an array so that its canary element can
+ * be found.
+ */
+#define DBG_ELEMS_ENTRY  int32 dbgelems;
+
+/** 
+ * Code to records the size of an array so that its canary element can
+ * be found.
+ */
+#define DBG_SET_ELEMS(x,y) (x).dbgelems = y
+
+/** 
+ * Code to initialise a canary.
+ */
+#define DBG_SET_CANARY(x) (x).canary = DBG_CANARY
+
+/** 
+ * Code to test for a canary having been overwritten.
+ */
+#define DBG_TEST_CANARY(x) if ((x).canary != DBG_CANARY) {\
+       elog(ERROR, "canary fault"); }
+
+/** 
+ * Base size for an array containing a canary.  This is zero if
+ * VEIL_DEBUG is not defined, when this is defined arrays will be one
+ * element longer to allow a canary to be placed at the end of the array.
+ */
+#define EMPTY 1
+
+/**
+ * Set a trailing canary in an array.
+ */
+#define DBG_SET_TRAILER(x, y) (x).y[(x).dbgelems] = DBG_CANARY;
+/**
+ * Set a trailing canary in an array (using a void pointer).
+ */
+#define DBG_SET_TRAILERP(x, y) (x).y[(x).dbgelems] = (void *) DBG_CANARY;
+
+/**
+ * Test the trailing canary in an array.
+ */
+#define DBG_TEST_TRAILER(x, y)                         \
+   if ((int) (x).y[(x).dbgelems] != DBG_CANARY) {      \
+       elog(ERROR, "trailing canary fault"); }
+
+/**
+ * Check whether an array index is in bounds.
+ */
+#define DBG_CHECK_INDEX(x,i) if (i >= (x).dbgelems) {\
+       elog(ERROR, "Element index out of range %d", i); }
+
+#else
+#define DBG_CANARY_ENTRY 
+#define DBG_ELEMS_ENTRY 
+#define DBG_SET_ELEMS(x,y)
+#define DBG_SET_CANARY(x) 
+#define DBG_TEST_CANARY(x)
+#define EMPTY 0
+#define DBG_SET_TRAILER(x,Y)
+#define DBG_TEST_TRAILER(x,Y)
+#define DBG_CHECK_INDEX(x,i)
+#endif
+
+#include "utils/hsearch.h"
+#include "storage/lwlock.h"
+
+/** 
+ * Chunks provide a linked list of dynamically allocated shared memory
+ * segments, with the most recently allocated chunk at the tail.
+ * Shmalloc allocates space from this list of chunks, creating new
+ * chunks as needed    up to MAX_ALLOWED_SHMEM.
+ */
+typedef struct MemChunk {
+    struct MemChunk *next_chunk;  /**< Pointer to next allocated chunk */
+   size_t  next;                 /**< Offset, within this chunk, of 1st
+                                  * free byte */
+   size_t  limit;                /**< Offset, of 1st byte beyond chunk */
+    void   *memory[0];            /**< The rest of the chunk, from which
+                                  * memory is allocated */
+} MemChunk;
+
+
+
+/**
+ * The key length for veil hash types.
+ */
+#define HASH_KEYLEN           60
+
+
+/**
+ * Describes the type of an Object record or one of its subtypes.
+ */
+typedef enum { 
+   OBJ_UNDEFINED = 0,
+    OBJ_SHMEMCTL,
+   OBJ_INT4,
+   OBJ_RANGE,
+   OBJ_BITMAP,
+   OBJ_BITMAP_ARRAY,
+   OBJ_BITMAP_HASH,
+   OBJ_BITMAP_REF,
+   OBJ_INT4_ARRAY
+} ObjType;
+
+/** 
+ * General purpose object-type.  All veil variables are effectively
+ * sub-types of this.
+ */
+typedef struct Object {
+    ObjType type;                /**< Identifies the type of the object. */
+} Object;
+
+/** 
+ * The ShmemCtl structure is the first object allocated from the first
+ * chunk of shared memory in context 0.  This object describes and
+ * manages shared memory allocated by shmalloc()
+ */
+typedef struct ShmemCtl {
+    ObjType type;                /**< This must have the value OBJ_SHMEMCTL */
+    bool      initialised;        /**< Set to true once struct is setup */
+    LWLockId  veil_lwlock;        /**< dynamically allocated LWLock */
+   int       current_context;    /**< Index of the current context (0
+                                  * or 1) */
+    size_t    total_allocated[2]; /**< Total shared memory allocated in
+                                  * chunks in each context */ 
+    bool      switching;          /**< Whether a context-switch is in
+                                  * progress */
+   MemChunk *context[2];         /**< The first chunks of each context */
+   TransactionId xid[2];         /**< The transaction id of the
+                                  * transaction that initialised each
+                                  * context: this is used to determine
+                                  * whether there are transactions
+                                  * still runnning that may be using an
+                                  * earlier context. */
+} ShmemCtl;
+
+/**
+ * Subtype of Object for storing simple int4 values.  These values are
+ * allowed to be null.
+ */
+typedef struct Int4Var {
+    ObjType type;      /**< This must have the value OBJ_INT4 */
+   bool    isnull;     /**< if true, the value is null */
+    int32   value;      /**< the integer value of the variable */
+} Int4Var;
+
+/**
+ * Subtype of Object for storing range values.  A range has an upper and
+ * lower bound, both stored as int4s.  Nulls are not allowed.
+ */
+typedef struct Range {
+    ObjType type;      /**< This must have the value OBJ_RANGE */
+    int32   min;        /**< Lower limit for range. */
+    int32   max;        /**< Upper limit for range. */
+} Range;
+
+
+
+/**
+ * Gives the bitmask index for the bitzero value of a Bitmap.  This is
+ * part of the "normalisation" process for bitmap ranges.  This process
+ * allows unlike bitmaps to be more easily compared by forcing bitmap
+ * indexes to be normalised around 32-bit word boundaries.  Eg, 2 bitmaps
+ * with domains 1 to 50 and 3 to 55, will have identical bit patterns
+ * for bits 3 to 50.
+ * 
+ * @param x The bitzero value of a bitmap
+ * 
+ * @return The bitmask index representing x.
+ */
+#define BITZERO(x) (x & 0xffffffe0)
+
+/**
+ * Gives the bitmask index for the bitmax value of a bitmap.  See
+ * BITZERO() for more information.
+ *
+ * @param x The bitmax value of a bitmap
+ *
+ * @return The bitmask index representing x.
+ */
+#define BITMAX(x) (x | 0x1f)
+
+/**
+ * Gives the index of a bit within the array of 32-bit words that
+ * comprise the bitmap.
+ *
+ * @param x The bit in question
+ *
+ * @return The array index of the bit.
+ */
+#define BITSET_ELEM(x) (x >> 5)
+
+/**
+ * Gives the index into ::bitmasks for the bit specified in x.
+ *
+ * @param x The bit in question
+ *
+ * @return The bitmask index
+ */
+#define BITSET_BIT(x) (x & 0x1f)
+
+/**
+ * Gives the number of array elements in a ::Bitmap that runs from
+ * element min to element max.
+ *
+ * @param min
+ * @param max
+ *
+ * @return The number of elements in the bitmap.
+ */
+#define ARRAYELEMS(min,max) (((max - BITZERO(min)) >> 5) + 1)
+
+
+/**
+ * Return the smaller of a or b.  Note that expressions a and b may be
+ * evaluated more than once.
+ *
+ * @param a
+ * @param b
+ *
+ * @return The smaller value of a or b.
+ */
+#define MIN(a,b) ((a < b)? a: b)
+
+
+/**
+ * Subtype of Object for storing bitmaps.  A bitmap is stored as an
+ * array of int4 values.  See veil_bitmap.c for more information.  Note
+ * that the size of a Bitmap structure is determined dynamically at run
+ * time as the size of the array is only known then.
+ */
+typedef struct Bitmap {
+    ObjType type;      /**< This must have the value OBJ_BITMAP */
+    DBG_CANARY_ENTRY   /**< Debugging entry */
+   DBG_ELEMS_ENTRY     /**< Debugging entry */
+    int32   bitzero;   /**< The index of the lowest bit the bitmap can
+                        * store */
+    int32   bitmax;        /**< The index of the highest bit the bitmap can
+                        * store */
+   uint32   bitset[EMPTY]; /**< Element zero of the array of int4 values
+                        * comprising the bitmap. */
+} Bitmap;
+
+/**
+ * Subtype of Object for storing bitmap refs.  A bitmapref is like a
+ * bitmap but instead of containing a bitmap it contains a reference to
+ * one.  This reference may be set during a transaction and then
+ * referenced only from within the setting transaction.
+ */
+typedef struct BitmapRef {
+    ObjType        type;   /**< This must have the value OBJ_BITMAP_REF */
+    TransactionId  xid;        /**< The xid for which this variable is
+                            * valid */
+   Bitmap        *bitmap;  /**< Pointer to the referenced bitmap */
+} BitmapRef;
+
+/**
+ * Subtype of Object for storing bitmap arrays.  A bitmap array is
+ * simply an array of pointers to dynamically allocated Bitmaps.  Note
+ * that the size of a Bitmap structure is determined dynamically at run
+ * time as the size of the array is only known then.
+ */
+typedef struct BitmapArray {   // subtype of Object
+    ObjType type;      /**< This must have the value OBJ_BITMAP_ARRAY */
+    DBG_CANARY_ENTRY    /**< Debugging entry */
+   DBG_ELEMS_ENTRY     /**< Debugging entry */
+    int32   bitzero;   /**< The index of the lowest bit each bitmap can
+                        * store */
+    int32   bitmax;        /**< The index of the highest bit each bitmap can
+                        * store */
+   int32   arrayzero;  /**< The index of array element zero: the
+                        * index of the lowest numbered bitmap in the
+                        * array */
+   int32   arraymax;   /**< The index of the lowest numbered bitmap in
+                        * the array */
+   Bitmap *bitmap[EMPTY];  /**< Element zero of the array of Bitmap pointers
+                        * comprising the array. */
+} BitmapArray;
+
+/** 
+ * Subtype of Object for storing bitmap hashes.  A bitmap hash is a hash
+ * of dynamically allocated bitmaps, keyed by strings.  Note that these
+ * cannot be created as shared variables.
+ */
+typedef struct BitmapHash {
+    ObjType type;      /**< This must have the value OBJ_BITMAP_HASH */
+    int32   bitzero;    /**< The index of the lowest bit each bitmap can
+                        * store */
+    int32   bitmax;     /**< The index of the highest bit each bitmap can
+                        * store */
+   HTAB   *hash;       /**< Pointer to the (Postgresql dynahash) hash
+                        * table */ 
+} BitmapHash;
+
+
+/** 
+ * Subtype of Object for storing arrays of integers.
+ */
+typedef struct Int4Array {
+    ObjType type;      /**< This must have the value OBJ_INT4_ARRAY */
+    int32   arrayzero;  /**< The index of array element zero: the
+                        * index of the lowest numbered bitmap in the
+                        * array */
+    int32   arraymax;   /**< The index of the lowest numbered bitmap in
+                        * the array */
+   int32   array[0];   /**< Element zero of the array of integers */
+} Int4Array;
+
+
+/**
+ * A Veil variable.  These may be session or shared variables, and may
+ * contain any Veil variable type.  They are created and accessed by
+ * vl_lookup_shared_variable() and vl_lookup_variable(), and are stored
+ * in either the shared hash or one of the session hashes.  See
+ * veil_shmem.c and veil_variables.c for more details.
+ */
+typedef struct VarEntry {
+    char  key[HASH_KEYLEN];    /**< String containing variable name */
+   bool  shared;           /**< Whether this is a shared variable */
+    Object *obj;            /**< Pointer to the contents of the variable */
+} VarEntry;
+
+
+/**
+ * Describes a veil shared or session variable.  This matches the SQL
+ * veil_variable_t which is defined as:
+\verbatim
+create type veil_variable_t as (
+    name    text,
+    type    text,
+    shared  bool,
+);
+\endverbatim
+ */
+typedef struct veil_variable_t {
+    char *name;            /**< The name of the variable */
+    char *type;            /**< The type of the variable (eg "Bitmap") */
+    bool shared;       /**< Whether this is a shared variable (as
+                          opposed to a session variable) */
+} veil_variable_t;
+
+
+#endif
+
diff --git a/src/veil_demo.sql b/src/veil_demo.sql
new file mode 100644 (file)
index 0000000..313f076
--- /dev/null
@@ -0,0 +1,423 @@
+
+\echo Creating tables...
+
+\echo ...privileges...
+create table privileges (
+    privilege_id   integer not null,
+    privilege_name varchar(80) not null
+);
+alter table privileges add constraint privilege__pk
+    primary key(privilege_id);
+
+
+\echo ...role...
+create table roles (
+    role_id    integer not null,
+    role_name  varchar(80) not null
+);
+alter table roles add constraint role__pk
+    primary key(role_id);
+
+
+\echo ...role_privileges...
+create table role_privileges (
+    role_id        integer not null,
+    privilege_id   integer not null
+);
+
+alter table role_privileges add constraint role_privilege__pk
+    primary key(role_id, privilege_id);
+
+alter table role_privileges add constraint role_privilege__role_fk
+    foreign key(role_id)
+    references roles(role_id);
+
+alter table role_privileges add constraint role_privilege__priv_fk
+    foreign key(privilege_id)
+    references privileges(privilege_id);
+
+\echo ...role_roles...
+create table role_roles (
+    role_id        integer not null,
+    has_role_id        integer not null
+);
+
+alter table role_roles add constraint role_role__pk
+    primary key(role_id, has_role_id);
+
+alter table role_roles add constraint role_role__role_fk
+    foreign key(role_id)
+    references roles(role_id);
+
+alter table role_roles add constraint role_role__has_role_fk
+    foreign key(has_role_id)
+    references roles(role_id);
+
+
+\echo ...persons...
+create sequence person_id_seq;
+create table persons (
+    person_id      integer not null,
+    person_name        varchar(80) not null
+);
+alter table persons add constraint person__pk
+    primary key(person_id);
+
+
+\echo ...projects...
+create sequence project_id_seq;
+create table projects (
+    project_id     integer not null,
+    project_name   varchar(80) not null
+);
+alter table projects add constraint project__pk
+    primary key(project_id);
+
+
+\echo ...detail_types...
+create table detail_types (
+    detail_type_id   integer not null,
+    required_privilege_id integer not null,
+    detail_type_name     varchar(80) not null
+);
+alter table detail_types add constraint detail_type__pk
+    primary key(detail_type_id);
+
+alter table detail_types add constraint detail_type__priv_fk
+    foreign key(required_privilege_id)
+    references privileges(privilege_id);
+
+
+\echo ...assignments...
+create table assignments (
+    project_id     integer not null,
+    person_id      integer not null,
+    role_id        integer not null
+);
+alter table assignments add constraint assignment__pk
+    primary key(project_id, person_id);
+
+alter table assignments add constraint assignment__project_fk
+    foreign key(project_id)
+    references projects(project_id);
+
+alter table assignments add constraint assignment__person_fk
+    foreign key(person_id)
+    references persons(person_id);
+
+alter table assignments add constraint assignment__role_fk
+    foreign key(role_id)
+    references roles(role_id);
+
+
+\echo ...person_roles...
+create table person_roles (
+    person_id      integer not null,
+    role_id        integer not null
+);
+alter table person_roles add constraint person_role__pk
+    primary key(person_id, role_id);
+
+alter table person_roles add constraint person_role__person_fk
+    foreign key(person_id)
+    references persons(person_id);
+
+alter table person_roles add constraint person_role__role_fk
+    foreign key(role_id)
+    references roles(role_id);
+
+
+\echo ...project_details...
+create table project_details (
+    project_id     integer not null,
+    detail_type_id integer not null,
+    value      text not null
+);
+alter table project_details add constraint project_detail__pk
+    primary key(project_id, detail_type_id);
+
+alter table project_details add constraint project_detail__project_fk
+    foreign key(project_id)
+    references projects(project_id);
+
+alter table project_details add constraint project_detail__detail_fk
+    foreign key(detail_type_id)
+    references detail_types(detail_type_id);
+
+
+\echo ...person_details...
+create table person_details (
+    person_id      integer not null,
+    detail_type_id integer not null,
+    value      text not null
+);
+alter table person_details add constraint person_detail__pk
+    primary key(person_id, detail_type_id);
+
+alter table person_details add constraint person_detail__person_fk
+    foreign key(person_id)
+    references persons(person_id);
+
+alter table person_details add constraint person_detail__detail_fk
+    foreign key(detail_type_id)
+    references detail_types(detail_type_id);
+
+\echo Setting up base data...
+
+\echo ...privileges...
+copy privileges (privilege_id, privilege_name) from stdin;
+10001  select_privileges
+10002  insert_privileges
+10003  update_privileges
+10004  delete_privileges
+10005  select_roles
+10006  insert_roles
+10007  update_roles
+10008  delete_roles
+10009  select_role_privileges
+10010  insert_role_privileges
+10011  update_role_privileges
+10012  delete_role_privileges
+10013  select_persons
+10014  insert_persons
+10015  update_persons
+10016  delete_persons
+10017  select_projects
+10018  insert_projects
+10019  update_projects
+10020  delete_projects
+10021  select_detail_types
+10022  insert_detail_types
+10023  update_detail_types
+10024  delete_detail_types
+10025  select_assignments
+10026  insert_assignments
+10027  update_assignments
+10028  delete_assignments
+10029  select_person_roles
+10030  insert_person_roles
+10031  update_person_roles
+10032  delete_person_roles
+10033  select_project_details
+10034  insert_project_details
+10035  update_project_details
+10036  delete_project_details
+10037  select_person_details
+10038  insert_person_details
+10039  update_person_details
+10040  delete_person_details
+10041  select_role_roles
+10042  insert_role_roles
+10043  update_role_roles
+10044  delete_role_roles
+10100  can_connect
+10150  view_basic
+10151  view_personal
+10152  view_personal_secure
+10153  view_project_confidential
+\.
+
+\echo ...roles...
+copy roles (role_id, role_name) from stdin;
+11001  DBA
+11002  Personal Context
+11003  Employee
+11004  Worker
+11005  Project Manager
+11006  Director
+11007  Manager
+\.
+
+\echo ...role_privileges...
+-- DBA can do anything (but is not automatically an employee)
+insert into role_privileges (role_id, privilege_id)
+select 11001, privilege_id
+from   privileges
+where  privilege_id != 10100;
+
+-- Personal Context allows update of personal details
+copy role_privileges (role_id, privilege_id) from stdin;
+11002  10013
+11002  10015
+11002  10025
+11002  10029
+11002  10037
+11002  10038
+11002  10039
+11002  10040
+11002  10150
+11002  10151
+11002  10152
+\.
+
+-- Basic Access can see lookup data
+insert into role_privileges (role_id, privilege_id)
+select 11003, privilege_id
+from   privileges
+where  privilege_name in ('select_privileges', 'select_roles',
+             'select_role_privileges', 'select_detail_types');
+
+insert into role_privileges (role_id, privilege_id)
+select 11003, 10100;
+
+-- Workers can modify project info
+insert into role_privileges (role_id, privilege_id)
+select 11004, privilege_id
+from   privileges
+where  privilege_name like '%project%'
+and    privilege_name not like 'delete%'
+and    privilege_name not like '%confidential';
+
+insert into role_privileges (role_id, privilege_id)
+select 11004, 10025;
+insert into role_privileges (role_id, privilege_id)
+select 11004, 10150;
+
+-- Project Manager can do anything to project info and can see personal info
+insert into role_privileges (role_id, privilege_id)
+select 11005, privilege_id
+from   privileges
+where  privilege_name like '%project%'
+or     privilege_name like '%assignment%';
+
+insert into role_privileges (role_id, privilege_id)
+select 11005, privilege_id
+from   privileges
+where  privilege_name like 'select_person%';
+
+insert into role_privileges (role_id, privilege_id)
+select 11005, 10150;
+insert into role_privileges (role_id, privilege_id)
+select 11005, 10151;
+
+-- Director can do anything except modify personal details
+insert into role_privileges (role_id, privilege_id)
+select 11006, privilege_id
+from   privileges
+where  privilege_name not like '%person%';
+
+insert into role_privileges (role_id, privilege_id)
+select 11006, privilege_id
+from   privileges
+where  privilege_name like 'select_person%';
+
+insert into role_privileges (role_id, privilege_id)
+select 11006, 10014;
+
+insert into role_privileges (role_id, privilege_id)
+select 11006, 10151;
+
+insert into role_privileges (role_id, privilege_id)
+select 11006, 10152;
+
+-- Manager can see personal info
+insert into role_privileges (role_id, privilege_id)
+select 11007, privilege_id
+from   privileges
+where  privilege_name like 'select_person%';
+
+insert into role_privileges (role_id, privilege_id)
+select 11007, 10150;
+insert into role_privileges (role_id, privilege_id)
+select 11007, 10151;
+
+
+\echo ...persons...
+copy persons (person_id, person_name) from stdin;
+1  Deb (the DBA)
+2  Pat (the PM)
+3  Derick (the director)
+4  Will (the worker)
+5  Wilma (the worker)
+6  Fred (the fired DBA)
+\.
+
+\echo ...person_roles...
+copy person_roles (person_id, role_id) from stdin;
+1  11001
+1  11003
+2  11003
+2  11007
+3  11003
+3  11006
+4  11003
+5  11003
+6  11001
+\.
+
+\echo ...projects...
+copy projects (project_id, project_name) from stdin;
+101    Secret Project
+102    Public project
+\.
+
+\echo ...assignments...
+copy assignments (project_id, person_id, role_id) from stdin;
+101    3   11005
+101    5   11004
+102    2   11005
+102    4   11004
+102    5   11004
+\.
+
+\echo ...detail_types...
+copy detail_types (detail_type_id, required_privilege_id, 
+                   detail_type_name) from stdin;
+1001   10150   start_date
+1002   10150   status
+1003   10150   join_date
+1004   10152   salary
+1005   10151   date of birth
+1006   10152   sin
+1007   10150   skills
+1008   10153   contract value
+\.
+
+\echo ...person_details...
+copy person_details (person_id, detail_type_id, value) from stdin;
+1  1003    20050102
+2  1003    20050103
+3  1003    20050104
+4  1003    20050105
+5  1003    20050106
+6  1003    20050107
+1  1002    Employee
+2  1002    Employee
+3  1002    Employee
+4  1002    Employee
+5  1002    Employee
+6  1002    Terminated
+1  1004    50,000
+2  1004    50,000
+3  1004    80,000
+4  1004    30,000
+5  1004    30,000
+6  1004    40,000
+1  1005    19610102
+2  1005    19600102
+3  1005    19650102
+4  1005    19660102
+5  1005    19670102
+1  1006    123456789
+2  1006    123456789
+3  1006    123456789
+4  1006    123456789
+5  1006    123456789
+1  1007    Oracle, C, SQL
+2  1007    Soft peoply-stuff
+3  1007    None at all
+4  1007    Subservience
+5  1007    Subservience
+\.
+
+
+\echo ...project_details
+copy project_details (project_id, detail_type_id, value) from stdin;
+101    1001    20050101
+101    1002    Secretly ongoing
+101    1008    $800,000
+102    1001    20050101
+102    1002    Ongoing
+102    1008    $100,000
+\.
+
diff --git a/src/veil_funcs.h b/src/veil_funcs.h
new file mode 100644 (file)
index 0000000..a0dd76e
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * @file   veil_funcs.h
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * \endcode
+ * @brief  
+ * Provide definitions for all non-local C-callable Veil functions.
+ * 
+ */
+
+#include "veil_datatypes.h"
+#include "fmgr.h"
+
+/* veil_utils */
+extern void *vl_malloc(size_t size);
+char *vl_ObjTypeName(ObjType obj);
+
+
+/* veil_variables */
+extern VarEntry *vl_lookup_shared_variable(char *name);
+extern VarEntry *vl_lookup_variable(char *name);
+extern veil_variable_t *vl_next_variable(veil_variable_t *prev);
+extern void vl_ClearInt4Array(Int4Array *array);
+extern Int4Array *vl_NewInt4Array(Int4Array *current, bool shared,
+                                 int32 min, int32 max);
+extern void vl_Int4ArraySet(Int4Array *array, int32 idx, int32 value);
+extern int32 vl_Int4ArrayGet(Int4Array *array, int32 idx);
+
+
+/* veil_datatypes */
+extern Range *vl_NewRange(bool shared);
+extern Int4Var *vl_NewInt4(bool shared);
+
+/* veil_bitmap */
+extern void vl_ClearBitmap(Bitmap *bitmap);
+extern void vl_NewBitmap(Bitmap **p_bitmap, bool shared, int32 min, int32 max);
+extern void vl_BitmapSetbit(Bitmap *bitmap, int32 bit);
+extern void vl_BitmapClearbit(Bitmap *bitmap, int32 bit);
+extern bool vl_BitmapTestbit(Bitmap *bitmap, int32 bit);
+extern void vl_BitmapUnion(Bitmap *target, Bitmap *source);
+extern void vl_BitmapIntersect(Bitmap *target, Bitmap *source);
+extern int32 vl_BitmapNextBit(Bitmap *bitmap, int32 bit, bool *found);
+extern Bitmap *vl_BitmapFromArray(BitmapArray *bmarray, int32 elem);
+extern void vl_ClearBitmapArray(BitmapArray *bmarray);
+extern void vl_NewBitmapArray(BitmapArray **p_bmarray, bool shared,
+                             int32 arrayzero, int32 arraymax,
+                             int32 bitzero, int32 bitmax);
+extern VarEntry *vl_NextHashEntry(HTAB *hash, VarEntry *prev);
+extern void vl_NewBitmapHash(BitmapHash **p_bmhash, char *name,
+                            int32 bitzero, int32 bitmax);
+extern Bitmap *vl_BitmapFromHash(BitmapHash *bmhash, char *hashelem);
+extern Bitmap *vl_AddBitmapToHash(BitmapHash *bmhash, char *hashelem);
+extern bool vl_BitmapHashHasKey(BitmapHash *bmhash, char *hashelem);
+
+/* veil_shmem */
+extern HTAB *vl_get_shared_hash(void);
+extern bool vl_prepare_context_switch(void);
+extern bool vl_complete_context_switch(void);
+extern void vl_force_context_switch(void);
+extern void *vl_shmalloc(size_t size);
+extern void vl_free(void *mem);
+extern void _PG_init(void);
+
+/* veil_query */
+extern int vl_spi_connect(void);
+extern int vl_spi_finish(void);
+extern bool vl_bool_from_query(const char *qry, bool *result);
+extern bool vl_db_exists(Oid db_id);
+
+/* veil_config */
+extern void veil_config_init(void);
+extern void veil_load_config(void);
+extern int veil_shared_hash_elems(void);
+extern int veil_dbs_in_cluster(void);
+extern int veil_shmem_context_size(void);
+
+
+/* veil_interface */
+extern void vl_type_mismatch(char *name,  ObjType expected, ObjType got);
+extern Datum veil_variables(PG_FUNCTION_ARGS);
+extern Datum veil_share(PG_FUNCTION_ARGS);
+extern Datum veil_init_range(PG_FUNCTION_ARGS);
+extern Datum veil_range(PG_FUNCTION_ARGS);
+extern Datum veil_init_bitmap(PG_FUNCTION_ARGS);
+extern Datum veil_clear_bitmap(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_setbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_clearbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_testbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_union(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_intersect(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_bits(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_range(PG_FUNCTION_ARGS);
+extern Datum veil_init_bitmap_array(PG_FUNCTION_ARGS);
+extern Datum veil_clear_bitmap_array(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_from_array(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_array_testbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_array_setbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_array_clearbit(PG_FUNCTION_ARGS);
+extern Datum veil_union_from_bitmap_array(PG_FUNCTION_ARGS);
+extern Datum veil_intersect_from_bitmap_array(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_array_bits(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_array_arange(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_array_brange(PG_FUNCTION_ARGS);
+extern Datum veil_init_bitmap_hash(PG_FUNCTION_ARGS);
+extern Datum veil_clear_bitmap_hash(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_key_exists(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_from_hash(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_testbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_setbit(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_clearbit(PG_FUNCTION_ARGS);
+extern Datum veil_union_into_bitmap_hash(PG_FUNCTION_ARGS);
+extern Datum veil_union_from_bitmap_hash(PG_FUNCTION_ARGS);
+extern Datum veil_intersect_from_bitmap_hash(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_bits(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_entries(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_from_hash(PG_FUNCTION_ARGS);
+extern Datum veil_bitmap_hash_range(PG_FUNCTION_ARGS);
+extern Datum veil_int4_set(PG_FUNCTION_ARGS);
+extern Datum veil_int4_get(PG_FUNCTION_ARGS);
+extern Datum veil_init_int4array(PG_FUNCTION_ARGS);
+extern Datum veil_clear_int4array(PG_FUNCTION_ARGS);
+extern Datum veil_int4array_set(PG_FUNCTION_ARGS);
+extern Datum veil_int4array_get(PG_FUNCTION_ARGS);
+extern Datum veil_init(PG_FUNCTION_ARGS);
+extern Datum veil_perform_reset(PG_FUNCTION_ARGS);
+extern Datum veil_force_reset(PG_FUNCTION_ARGS);
+extern Datum veil_version(PG_FUNCTION_ARGS);
+extern Datum veil_serialise(PG_FUNCTION_ARGS);
+extern Datum veil_deserialise(PG_FUNCTION_ARGS);
+
+
+/* veil_serialise */
+extern char *vl_serialise_var(char *name);
+extern int4 vl_deserialise(char **p_stream);
+extern VarEntry *vl_deserialise_next(char **p_stream);
diff --git a/src/veil_interface.c b/src/veil_interface.c
new file mode 100644 (file)
index 0000000..f5cc6bf
--- /dev/null
@@ -0,0 +1,2506 @@
+/**
+ * @file   veil_interface.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ *
+ * \endcode
+ * @brief  
+ * Functions providing the SQL interface to veil, and utility functions
+ * to support them.
+ */
+
+#include "postgres.h"
+#include "access/xact.h"
+#include "executor/spi.h"
+#include "funcapi.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+
+#include "veil_version.h"
+#include "veil_funcs.h"
+#include "veil_datatypes.h"
+
+
+PG_MODULE_MAGIC;
+
+
+/** 
+ * Create a dynamically allocated C string as a copy of a text value.
+ * 
+ * @param in text value from which the copy is made.
+ * @return Dynamically allocated (by palloc()) copy of in.
+ */
+static char *
+strfromtext(text *in)
+{
+    char *out = palloc(VARSIZE(in) - VARHDRSZ + 1);
+    memcpy(out, VARDATA(in), VARSIZE(in) - VARHDRSZ);
+    out[VARSIZE(in) - VARHDRSZ] = '\0';
+
+    return out;
+}
+
+/** 
+ * Create a dynamically allocated text value as a copy of a C string.
+ * 
+ * @param in String to be copied
+ * @return Dynamically allocated (by palloc()) copy of in.
+ */
+static text *
+textfromstr(char *in)
+{
+    int   len = strlen(in);
+    text *out = palloc(len + VARHDRSZ);
+    memcpy(VARDATA(out), in, len);
+   SET_VARSIZE(out, (len + VARHDRSZ));
+
+    return out;
+}
+
+/** 
+ * Create a dynamically allocated text value as a copy of a C string,
+ * applying a limit to the length.
+ * 
+ * @param in String to be copied
+ * @param limit Maximum length of string to be copied.
+ * @return Dynamically allocated (by palloc()) copy of in.
+ */
+static text *
+textfromstrn(char *in, int limit)
+{
+    int   len = strlen(in);
+    text *out;
+
+    if (limit < len) {
+        len = limit;
+    }
+
+    out = palloc(len + VARHDRSZ);
+    memcpy(VARDATA(out), in, len);
+   SET_VARSIZE(out, (len + VARHDRSZ));
+
+    return out;
+}
+
+/** 
+ * Create a dynamically allocated text value as a copy of a C string,
+ * applying a limit to the length.
+ * 
+ * @param str String to be copied
+ * @return Dynamically allocated (by palloc()) copy of str.
+ */
+static char *
+copystr(char *str)
+{
+    char *new = palloc((sizeof(char) * strlen(str) + 1));
+    strcpy(new, str);
+    return new;
+}
+
+/** 
+ * Create a dynamically allocated C string as a copy of an integer value.
+ * 
+ * @param val value to be stringified
+ * @return Dynamically allocated string.
+ */
+static char *
+strfromint(int4 val)
+{
+    char *new = palloc((sizeof(char) * 17)); /* Large enough for any 32 
+                                             * bit number */
+    sprintf(new, "%d", val);
+    return new;
+}
+
+/** 
+ * Create a dynamically allocated C string as a copy of a boolean value.
+ * 
+ * @param val value to be stringified
+ * @return Dynamically allocated string.
+ */
+static char *
+strfrombool(bool val)
+{
+    char *new = palloc((sizeof(char) * 2));
+    if (val) {
+        strcpy(new, "t");
+    }
+    else {
+        strcpy(new, "f");
+    }
+    return new;
+}
+
+/** 
+ * Perform session initialisation once for the session.  This calls the
+ * user-defined function veil_init which should create and possibly
+ * initialise all session and, maybe, shared variables.  This function
+ * may be safely called any number of times - it will only perform the
+ * initialisation on the first call.
+ * 
+ */
+static void
+ensure_init()
+{
+    bool  success = false;
+   TransactionId this_xid;
+    int   ok;
+   HTAB *hash;
+    static bool done = false;
+   static TransactionId xid = 0;
+
+    if (!done) {
+       this_xid =  GetCurrentTransactionId();
+       if (xid == this_xid) {
+           /* We must have been called recursively, so just return */
+           return;
+       }
+       xid = this_xid;       /* Record our xid in case we recurse */
+        ok = vl_spi_connect();
+        if (ok != SPI_OK_CONNECT) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("failed to initialise session (1)"),
+                    errdetail("SPI_connect() failed, returning %d.", ok)));
+        }
+
+       hash = vl_get_shared_hash();  /* Init all shared memory
+                                        constructs */
+        (void) vl_bool_from_query("select veil_init(FALSE)", &success);
+
+        if (!success) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("failed to initialise session (2)"),
+                    errdetail("veil_init() did not return true.")));
+        }
+        
+        ok = vl_spi_finish();
+        if (ok != SPI_OK_FINISH) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("failed to initialise session (3)"),
+                    errdetail("SPI_finish() failed, returning %d.", ok)));
+        }
+        done = true;   /* init is done, we don't need to do it again. */
+    }
+}
+
+/** 
+ * Report, by raising an error, a type mismatch between the expected and
+ * actual type of a VarEntry variable.
+ * 
+ * @param name The name of the variable
+ * @param expected The expected type.
+ * @param got The actual type
+ */
+extern void
+vl_type_mismatch(char *name,
+                 ObjType expected,
+                 ObjType got)
+{
+   ereport(ERROR,
+           (errcode(ERRCODE_INTERNAL_ERROR),
+            errmsg("type mismatch in %s: expected %s, got %s",
+                   name, vl_ObjTypeName(expected), vl_ObjTypeName(got)),
+            errdetail("Variable %s is not of the expected type.", name)));
+}
+
+/** 
+ * Return the Int4Var variable matching the name parameter, possibly
+ * creating the variable.  Raise an error if the named variable already
+ * exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @param create Whether to create the variable if it does not exist.
+ * @return Pointer to the variable or null if the variable does not
+ * exist and create was false.
+ */
+static Int4Var *
+GetInt4Var(char *name,
+           bool  create)
+{
+    VarEntry *var;
+    Int4Var  *i4v;
+    
+    var = vl_lookup_variable(name);
+    i4v = (Int4Var *) var->obj;
+
+    if (i4v) {
+        if (i4v->type != OBJ_INT4) {
+            vl_type_mismatch(name, OBJ_INT4, i4v->type);
+        }
+    }
+    else {
+        if (create) {
+            var->obj = (Object *) vl_NewInt4(var->shared);
+            i4v = (Int4Var *) var->obj;
+        }
+        else {
+            vl_type_mismatch(name, OBJ_INT4, OBJ_UNDEFINED);
+        }
+    }
+    return i4v;
+}
+
+/** 
+ * Return the Range variable matching the name parameter, possibly
+ * creating the variable.  Raise an error if the named variable already
+ * exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @param create Whether to create the variable if it does not exist.
+ * @return Pointer to the variable or null if the variable does not
+ * exist and create was false.
+ */
+static Range *
+GetRange(char *name,
+         bool  create)
+{
+    VarEntry *var;
+    Range    *range;
+    
+    var = vl_lookup_variable(name);
+    range = (Range *) var->obj;
+
+    if (range) {
+        if (range->type != OBJ_RANGE) {
+            vl_type_mismatch(name, OBJ_RANGE, range->type);
+        }
+    }
+    else {
+        if (create) {
+            var->obj = (Object *) vl_NewRange(var->shared);
+            range = (Range *) var->obj;
+        }
+        else {
+            vl_type_mismatch(name, OBJ_RANGE, OBJ_UNDEFINED);
+        }
+    }
+    return range;
+}
+
+/** 
+ * Return the Bitmap from a bitmap variable.  This function exists
+ * primarily to perform type checking, and to raise an error if the
+ * variable is not a bitmap.
+ * 
+ * @param var The VarEntry that should contain a bitmap.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * yet been initialised.
+ * @param allow_ref Whether to (not) raise an error if the variable is a
+ * bitmap_ref rather than a bitmap.
+ * @return Pointer to the variable or null if the variable is undefined 
+ * and allow_empty was true.
+ */
+static Bitmap *
+GetBitmapFromVar(VarEntry *var,
+                 bool allow_empty,
+                bool allow_ref)
+{
+    Bitmap *bitmap = (Bitmap *) var->obj;
+
+    if (bitmap) {
+        if (bitmap->type != OBJ_BITMAP) {
+           if (allow_ref && (bitmap->type == OBJ_BITMAP_REF)) {
+               BitmapRef *bmref = (BitmapRef *) bitmap;
+               if (bmref->xid == GetCurrentTransactionId()) {
+                   bitmap = bmref->bitmap;
+               }
+               else {
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INTERNAL_ERROR),
+                            errmsg("BitmapRef %s is not defined",
+                                   var->key),
+                            errhint("Perhaps the name is mis-spelled, or its "
+                                    "definition is missing from "
+                                    "veil_init().")));
+               }
+           }
+           else {
+               vl_type_mismatch(var->key, OBJ_BITMAP, bitmap->type);
+           }
+        }
+    }
+   if (!bitmap) {
+        if (!allow_empty) {
+            vl_type_mismatch(var->key, OBJ_BITMAP, OBJ_UNDEFINED);
+        }
+    }
+    return bitmap;
+}
+
+/** 
+ * Return the Bitmap matching the name parameter, possibly creating the
+ * VarEntry (variable) for it.  Raise an error if the named variable
+ * already exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * been defined.
+ * @param allow_ref Whether to (not) raise an error if the variable is a
+ * bitmap_ref rather than a bitmap.
+ * @return Pointer to the variable or null if the variable does not
+ * exist and allow_empty was false.
+ */
+static Bitmap *
+GetBitmap(char *name,
+          bool allow_empty,
+         bool allow_ref)
+{
+    VarEntry *var;
+    Bitmap   *bitmap;
+
+    var = vl_lookup_variable(name);
+    bitmap = GetBitmapFromVar(var, allow_empty, allow_ref);
+
+   return bitmap;
+}
+
+/** 
+ * Return the BitmapRef from a bitmap ref variable.  This function exists
+ * primarily to perform type checking, and to raise an error if the
+ * variable is not a bitmap ref.  Note that BitmapRef variables may not
+ * be shared as they can contain references to non-shared objects.
+ * 
+ * @param var The VarEntry that should contain a bitmap ref.
+ * @return Pointer to the variable.
+ */
+static BitmapRef *
+GetBitmapRefFromVar(VarEntry *var)
+{
+    BitmapRef *bmref = (BitmapRef *) var->obj;
+
+    if (bmref) {
+        if (bmref->type != OBJ_BITMAP_REF) {
+           vl_type_mismatch(var->key, OBJ_BITMAP_REF, bmref->type);
+       }
+    }
+   else {
+       if (var->shared) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("illegal attempt to define shared BitmapRef %s",
+                           var->key),
+                    errhint("BitmapRefs may only be defined as session, "
+                            "not shared, variables.")));
+       }
+       /* Create a new bmref (these are always session variables. */
+       bmref = vl_malloc(sizeof(BitmapRef));
+       bmref->type = OBJ_BITMAP_REF;
+       bmref->bitmap = NULL;
+       var->obj = (Object *) bmref;
+   }
+
+    return bmref;
+}
+
+/** 
+ * Return the BitmapRef matching the name parameter, possibly creating the
+ * VarEntry (variable) for it.  Raise an error if the named variable
+ * already exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @return Pointer to the variable
+ */
+static BitmapRef *
+GetBitmapRef(char *name)
+{
+    VarEntry  *var;
+    BitmapRef *bmref;
+
+    var = vl_lookup_variable(name);
+    bmref = GetBitmapRefFromVar(var);
+
+   return bmref;
+}
+
+/** 
+ * Return the BitmapArray from a bitmap array variable.  This function
+ * exists primarily to perform type checking, and to raise an error if
+ * the variable is not a bitmap array.
+ * 
+ * @param var The VarEntry that should contain a bitmap array.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * yet been initialised.
+ * @return Pointer to the variable or null if the variable is undefined 
+ * and allow_empty was true.
+ */
+static BitmapArray *
+GetBitmapArrayFromVar(VarEntry *var,
+                      bool allow_empty)
+{
+    BitmapArray *bmarray;
+    bmarray = (BitmapArray *) var->obj;
+
+    if (bmarray) {
+        if (bmarray->type != OBJ_BITMAP_ARRAY) {
+            vl_type_mismatch(var->key, OBJ_BITMAP_ARRAY, bmarray->type);
+        }
+    }
+    else {
+        if (!allow_empty) {
+            vl_type_mismatch(var->key, OBJ_BITMAP_ARRAY, OBJ_UNDEFINED);
+        }
+    }
+
+    return bmarray;
+}
+
+/** 
+ * Return the BitmapArray matching the name parameter, possibly creating
+ * the (VarEntry) variable.  Raise an error if the named variable
+ * already exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * been defined
+ * @return Pointer to the variable or null if the variable does not
+ * exist and create was false.
+ */
+static BitmapArray *
+GetBitmapArray(char *name,
+               bool allow_empty)
+{
+    VarEntry    *var;
+    BitmapArray *bmarray;
+
+    var = vl_lookup_variable(name);
+    bmarray = GetBitmapArrayFromVar(var, allow_empty);
+
+   return bmarray;
+}
+
+/** 
+ * Return the BitmapHash from a bitmap hash variable.  This function
+ * exists primarily to perform type checking, and to raise an error if
+ * the variable is not a bitmap hash.
+ * 
+ * @param var The VarEntry that should contain a bitmap hash.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * yet been initialised.
+ * @return Pointer to the variable or null if the variable is undefined 
+ * and allow_empty was true.
+ */
+static BitmapHash *
+GetBitmapHashFromVar(VarEntry *var,
+                      bool allow_empty)
+{
+    BitmapHash *bmhash;
+    bmhash = (BitmapHash *) var->obj;
+
+    if (bmhash) {
+        if (bmhash->type != OBJ_BITMAP_HASH) {
+            vl_type_mismatch(var->key, OBJ_BITMAP_HASH, bmhash->type);
+        }
+    }
+    else {
+        if (!allow_empty) {
+            vl_type_mismatch(var->key, OBJ_BITMAP_HASH, OBJ_UNDEFINED);
+        }
+    }
+
+    return bmhash;
+}
+
+/** 
+ * Return the BitmapHash matching the name parameter, possibly creating
+ * the VarEntry (variable) for it.  Raise an error if the named variable
+ * already exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * been defined.
+ * @return Pointer to the variable or null if the variable does not
+ * exist and create was false.
+ */
+static BitmapHash *
+GetBitmapHash(char *name,
+              bool allow_empty)
+{
+    VarEntry   *var;
+    BitmapHash *bmhash;
+    
+    var = vl_lookup_variable(name);
+    bmhash = GetBitmapHashFromVar(var, allow_empty);
+
+    return bmhash;
+}
+
+/** 
+ * Return the Int4Array from an Int4Array variable.  This function
+ * exists primarily to perform type checking, and to raise an error if
+ * the variable is not an Int4Array.
+ * 
+ * @param var The VarEntry that should contain an Int4Array.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * yet been initialised.
+ * @return Pointer to the variable or null if the variable is undefined 
+ * and allow_empty was true.
+ */
+static Int4Array *
+GetInt4ArrayFromVar(VarEntry *var,
+                   bool allow_empty)
+{
+    Int4Array *array;
+    array = (Int4Array *) var->obj;
+
+    if (array) {
+        if (array->type != OBJ_INT4_ARRAY) {
+            vl_type_mismatch(var->key, OBJ_INT4_ARRAY, array->type);
+        }
+    }
+    else {
+        if (!allow_empty) {
+            vl_type_mismatch(var->key, OBJ_INT4_ARRAY, OBJ_UNDEFINED);
+        }
+    }
+
+    return array;
+}
+
+/** 
+ * Return the Int4Array matching the name parameter, possibly creating
+ * the VarEntry (variable) for it.  Raise an error if the named variable
+ * already exists and is of the wrong type.
+ * 
+ * @param name The name of the variable.
+ * @param allow_empty Whether to raise an error if the variable has not
+ * been defined.
+ * @return Pointer to the variable or null if the variable does not
+ * exist and create was false.
+ */
+static Int4Array *
+GetInt4Array(char *name,
+            bool allow_empty)
+{
+    VarEntry  *var;
+    Int4Array *array;
+    
+    var = vl_lookup_variable(name);
+    array = GetInt4ArrayFromVar(var, allow_empty);
+
+    return array;
+}
+
+PG_FUNCTION_INFO_V1(veil_variables);
+/** 
+ * <code>veil_variables() returns setof veil_variable_t</code>
+ * Return a <code>veil_variable_t</code> record for each defined
+ * variable.  This includes both session and shared variables.
+ *
+ * @param fcinfo None
+ * @return <code>setof veil_variable_t</code>
+ */
+Datum
+veil_variables(PG_FUNCTION_ARGS)
+{
+    TupleDesc tupdesc;
+    TupleTableSlot *slot;
+    AttInMetadata *attinmeta;
+    FuncCallContext *funcctx;
+
+    veil_variable_t *var;
+    char **values;
+    HeapTuple tuple;
+    Datum datum;
+
+    if (SRF_IS_FIRSTCALL())
+    {
+        /* Only do this on first call for this result set */
+        MemoryContext   oldcontext;
+
+        ensure_init();
+        funcctx = SRF_FIRSTCALL_INIT();
+        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+        tupdesc = RelationNameGetTupleDesc("veil_variable_t");
+        slot = TupleDescGetSlot(tupdesc);
+        funcctx->slot = slot;
+        attinmeta = TupleDescGetAttInMetadata(tupdesc);
+        funcctx->attinmeta = attinmeta;
+
+        MemoryContextSwitchTo(oldcontext);
+       funcctx->user_fctx = NULL;
+    }
+    
+    funcctx = SRF_PERCALL_SETUP();
+    var = vl_next_variable(funcctx->user_fctx);
+    funcctx->user_fctx = var;
+
+    if (var) {
+        values = (char **) palloc(3 * sizeof(char *));
+        values[0] = copystr(var->name);
+        values[1] = copystr(var->type);
+        values[2] = strfrombool(var->shared);
+
+        slot = funcctx->slot;
+        attinmeta = funcctx->attinmeta;
+        
+        tuple = BuildTupleFromCStrings(attinmeta, values);
+        datum = TupleGetDatum(slot, tuple);
+        SRF_RETURN_NEXT(funcctx, datum);
+
+    }
+    else {
+        SRF_RETURN_DONE(funcctx);
+    }
+}
+
+PG_FUNCTION_INFO_V1(veil_share);
+/** 
+ * <code>veil_share(name text) returns bool</code>
+ * Define a shared variable called NAME, returning true.  If the
+ * variable is already defined as a session variable an ERROR will be
+ * raised.
+ *
+ * Session variables are simply defined by their first usage.  Shared
+ * variables must be defined using this function.  They may then be used
+ * in exactly the same way as session variables.  Shared variables are
+ * shared by all backends and so need only be initialised once.  The
+ * result of this function tells the caller whether the variable needs
+ * to be initialised.  The caller that first defines a shared variable
+ * will get a false result and from this will know that the variable
+ * must be initialised.  All subsequent callers will get a true result
+ * and so will know that the variable is already initialised.
+ * 
+ * @param fcinfo <code>name text</code> name of variable.
+ * @return <code>bool</code> true if the variable already exists
+ */
+Datum
+veil_share(PG_FUNCTION_ARGS)
+{
+    char     *name;
+    VarEntry *var;
+
+    ensure_init();
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+
+    var = vl_lookup_shared_variable(name);
+
+    PG_RETURN_BOOL((var->obj != NULL));
+}
+
+
+PG_FUNCTION_INFO_V1(veil_init_range);
+/** 
+ * veil_init_range(name text, min int4, max int4) returns int4
+ * Initialise a Range variable called NAME constrained by MIN and MAX,
+ * returning the number of  elements in the range.  Ranges may be
+ * examined using the veil_range() function.
+ *
+ * @param fcinfo <code>name text</code> The name of the variable to
+ * initialise.
+ * <br><code>min int4</code> The min value of the range.
+ * <br><code>max int4</code> The max value of the range.
+ * @return <code>int4</code> The size of the range ((max - min) + 1).
+ */
+Datum
+veil_init_range(PG_FUNCTION_ARGS)
+{
+    int32     min;
+    int32     max;
+    Range    *range;
+    char     *name;
+
+   ensure_init();
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    min = PG_GETARG_INT32(1);
+    max = PG_GETARG_INT32(2);
+
+   range = GetRange(name, true);
+
+    range->min = min;
+    range->max = max;
+    PG_RETURN_INT32(max + 1 - min);
+}
+
+
+/** 
+ * Create a datum containing the values of a veil_range_t composite
+ * type.
+ * 
+ * @param min Min value of range
+ * @param max Max value of range
+ * @return Composite (row) type datum containing the range elements.
+ */
+static Datum 
+datum_from_range(int32 min, int32 max)
+{
+    static bool init_done = false;
+    static TupleDesc tupdesc;
+    static AttInMetadata *attinmeta;
+    TupleTableSlot *slot;
+    HeapTuple tuple;
+    char **values;
+
+    if (!init_done) {
+        /* Keep all static data in top memory context where it will
+         * safely remain during the session. */
+
+        MemoryContext oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+        
+        init_done = true;
+        tupdesc = RelationNameGetTupleDesc("veil_range_t");
+        slot = TupleDescGetSlot(tupdesc);
+        attinmeta = TupleDescGetAttInMetadata(tupdesc);
+        
+        MemoryContextSwitchTo(oldcontext);
+    }
+
+    /* Create value strings to be returned to caller. */
+    values = (char **) palloc(2 * sizeof(char *));
+    values[0] = strfromint(min);
+    values[1] = strfromint(max);
+    
+    tuple = BuildTupleFromCStrings(attinmeta, values);
+   slot = TupleDescGetSlot(tupdesc);
+
+    /* make the tuple into a datum */
+    return TupleGetDatum(slot, tuple);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_range);
+/** 
+ * <code>veil_range(name text) returns veil_range_t</code>
+ * Return the range (as a SQL veil_range_t composite type) from the
+ * named variable.
+ * An Error will be raised if the variable is not defined or is of the
+ * wrong type.
+ *
+ * @param fcinfo <code>name text</code> The name of the range variable.
+ * @return <code>veil_range_t</code>  Composite type containing the min
+ * and max values from the named variable.
+ */
+Datum
+veil_range(PG_FUNCTION_ARGS)
+{
+    char  *name;
+    Range *range;
+   Datum  datum;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    range = GetRange(name, false);
+    
+    datum = (datum_from_range(range->min, range->max));
+
+   PG_RETURN_DATUM(datum);
+}
+
+PG_FUNCTION_INFO_V1(veil_init_bitmap);
+/** 
+ * <code>veil_init_bitmap(bitmap_name text, range_nametext) returns bool</code>
+ * Create or re-initialise a Bitmap, for dealing with a named range of
+ * values.
+ * An error will be raised if the variable already exists and is not a
+ * Bitmap.
+ *
+ * @param fcinfo <code>bitmap_name text</code> The name of the bitmap to
+ * create or reset
+ * <br><code>range_name text</code> The name of a Range variable that
+ * defines the range of the new bitmap.
+ * @return <code>bool</code> true
+ */
+Datum
+veil_init_bitmap(PG_FUNCTION_ARGS)
+{
+    char     *bitmap_name;
+    char     *range_name;
+    Bitmap   *bitmap;
+    VarEntry *bitmap_var;
+    Range    *range;
+
+    ensure_init();
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bitmap_var = vl_lookup_variable(bitmap_name);
+    bitmap = GetBitmapFromVar(bitmap_var, true, false);
+    range_name = strfromtext(PG_GETARG_TEXT_P(1));
+    range = GetRange(range_name, false);
+
+    vl_NewBitmap(&bitmap, bitmap_var->shared, range->min, range->max);
+
+    bitmap_var->obj = (Object *) bitmap;
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_clear_bitmap);
+/** 
+ * <code>veil_clear_bitmap(name text) returns bool</code>
+ * Clear all bits in the specified Bitmap.
+ * An error will be raised if the variable is not a Bitmap or BitmapRef.
+ *
+ * @param fcinfo <code>name text</code> The name of the bitmap to
+ * be cleared.
+ * @return <code>bool</code> true
+ */
+Datum
+veil_clear_bitmap(PG_FUNCTION_ARGS)
+{
+    char     *bitmap_name;
+    VarEntry *bitmap_var;
+    Bitmap   *bitmap;
+
+    ensure_init();
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bitmap_var = vl_lookup_variable(bitmap_name);
+    bitmap = GetBitmapFromVar(bitmap_var, false, true);
+
+    vl_ClearBitmap(bitmap);
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_setbit);
+/** 
+ * <code>veil_bitmap_setbit(name text, bit_number int4) returns bool</code>
+ * Set the specified bit in the specified Bitmap.
+ *
+ * An error will be raised if the variable is not a Bitmap or BitmapRef.
+ *
+ * @param fcinfo <code>name text</code> The name of the bitmap variable.
+ * <br><code>bit_number int4</code> The bit to be set.
+ * @return <code>bool</code> true
+ */
+Datum
+veil_bitmap_setbit(PG_FUNCTION_ARGS)
+{
+    char   *name;
+    Bitmap *bitmap;
+    int32   bit;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bit = PG_GETARG_INT32(1);
+    bitmap = GetBitmap(name, false, true);
+    vl_BitmapSetbit(bitmap, bit);
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_clearbit);
+/** 
+ * <code>veil_bitmap_clearbit(name int4, bit_number text) returns bool</code>
+ * Clear the specified bit in the specified Bitmap.
+ *
+ * An error will be raised if the variable is not a Bitmap or BitmapRef.
+ *
+ * @param fcinfo <code>name text</code> The name of the bitmap variable.
+ * <br><code>bit_number int4</code> The bit to be cleared.
+ * @return <code>bool</code> true
+ */
+Datum
+veil_bitmap_clearbit(PG_FUNCTION_ARGS)
+{
+    char   *name;
+    Bitmap *bitmap;
+    int32   bit;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bit = PG_GETARG_INT32(1);
+    bitmap = GetBitmap(name, false, true);
+    vl_BitmapClearbit(bitmap, bit);
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_testbit);
+/** 
+ * <code>veil_bitmap_testbit(name text, bit_number int4) returns bool</code>
+ * Test the specified bit in the specified Bitmap, returning true if it
+ * is set.
+ *
+ * An error will be raised if the variable is not a Bitmap or BitmapRef.
+ *
+ * @param fcinfo <code>name text</code> The name of the bitmap variable.
+ * <br><code>bit_number int4</code> The bit to be tested.
+ * @return <code>bool</code> true if the bit was set
+ */
+Datum
+veil_bitmap_testbit(PG_FUNCTION_ARGS)
+{
+    char   *name;
+    Bitmap *bitmap;
+    int32   bit;
+    bool    result;
+
+    ensure_init();
+
+    bit = PG_GETARG_INT32(1);
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bitmap = GetBitmap(name, false, true);
+
+    result = vl_BitmapTestbit(bitmap, bit);
+    PG_RETURN_BOOL(result);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_union);
+/** 
+ * <code>veil_bitmap_union(result_name text, name2 text) returns bool</code>
+ * Union the bitmap specified in parameter 1 with that in parameter 2,
+ * with the result in parameter 1.
+ *
+ * An error will be raised if the variables are not of type Bitmap or
+ * BitmapRef.
+ *
+ * @param fcinfo <code>result_name text</code> The target bitmap
+ * <br><code>name2 text</code> The bitmap with which to union the target
+ * @return <code>bool</code> true 
+ */
+Datum
+veil_bitmap_union(PG_FUNCTION_ARGS)
+{
+    char        *bitmap1_name;
+    char        *bitmap2_name;
+    Bitmap      *target;
+    Bitmap      *source;
+
+    ensure_init();
+
+    bitmap1_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bitmap2_name = strfromtext(PG_GETARG_TEXT_P(1));
+    target = GetBitmap(bitmap1_name, false, true);
+    source = GetBitmap(bitmap2_name, false, true);
+
+   if (target && source) {
+       vl_BitmapUnion(target, source);
+   }
+
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_intersect);
+/** 
+ * <code>veil_bitmap_intersect(result_name text, name2 text) returns bool</code>
+ * Intersect the bitmap specified in parameter 1 with that in parameter 2,
+ * with the result in parameter 1.
+ *
+ * An error will be raised if the variables are not of type Bitmap or
+ * BitmapRef.
+ *
+ * @param fcinfo <code>result_name text</code> The target bitmap
+ * <br><code>name2 text</code> The bitmap with which to intersect the target
+ * @return <code>bool</code> true 
+ */
+Datum
+veil_bitmap_intersect(PG_FUNCTION_ARGS)
+{
+    char        *bitmap1_name;
+    char        *bitmap2_name;
+    Bitmap      *target;
+    Bitmap      *source;
+
+    ensure_init();
+
+    bitmap1_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bitmap2_name = strfromtext(PG_GETARG_TEXT_P(1));
+    target = GetBitmap(bitmap1_name, false, true);
+    source = GetBitmap(bitmap2_name, false, true);
+
+   vl_BitmapIntersect(target, source);
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_bits);
+/** 
+ * <code>veil_bitmap_bits(name text)</code> returns setof int4
+ * Return the set of all bits set in the specified Bitmap or BitmapRef.
+ *
+ * @param fcinfo <code>name text</code> The name of the bitmap.
+ * @return <code>setof int4</code>The set of bits that are set in the
+ * bitmap.
+ */
+Datum
+veil_bitmap_bits(PG_FUNCTION_ARGS)
+{
+   struct bitmap_bits_state {
+       Bitmap *bitmap;
+       int32   bit;
+   } *state;
+    FuncCallContext *funcctx;
+   MemoryContext    oldcontext;
+    char  *name;
+    bool   found;
+    Datum  datum;
+    
+    if (SRF_IS_FIRSTCALL())
+    {
+        /* Only do this on first call for this result set */
+        ensure_init();
+
+        funcctx = SRF_FIRSTCALL_INIT();
+       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+       state = palloc(sizeof(struct bitmap_bits_state));
+        MemoryContextSwitchTo(oldcontext);
+
+        name = strfromtext(PG_GETARG_TEXT_P(0));
+        state->bitmap = GetBitmap(name, false, true);
+
+        if (!state->bitmap) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("Bitmap %s is not defined",
+                           name),
+                    errhint("Perhaps the name is mis-spelled, or its "
+                            "definition is missing from veil_init().")));
+        }
+
+        state->bit = state->bitmap->bitzero;
+       funcctx->user_fctx = state;
+    }
+    
+    funcctx = SRF_PERCALL_SETUP();
+   state = funcctx->user_fctx;
+    
+    state->bit = vl_BitmapNextBit(state->bitmap, state->bit, &found);
+    
+    if (found) {
+        datum = Int32GetDatum(state->bit);
+        state->bit++;
+        SRF_RETURN_NEXT(funcctx, datum);
+    }
+    else {
+        SRF_RETURN_DONE(funcctx);
+    }
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_range);
+/** 
+ * <code>veil_bitmap_range(name text) returns veil_range_t</code>
+ * Return composite type giving the range of the specified Bitmap or
+ * BitmapRef.
+ *
+ * @param fcinfo <code>name text</code> The name of the bitmap.
+ * @return <code>veil_range_t</code>  Composite type containing the min
+ * and max values of the bitmap's range
+ */
+Datum
+veil_bitmap_range(PG_FUNCTION_ARGS)
+{
+    char   *name;
+    Bitmap *bitmap;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bitmap = GetBitmap(name, false, true);
+
+    if (!bitmap) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap %s is not defined", name),
+                errhint("Perhaps the name is mis-spelled, or its "
+                        "definition is missing from veil_init().")));
+    }
+
+    PG_RETURN_DATUM(datum_from_range(bitmap->bitzero, bitmap->bitmax));
+}
+
+
+PG_FUNCTION_INFO_V1(veil_init_bitmap_array);
+/** 
+ * <code>veil_init_bitmap_array(text, text, text) returns bool</code>
+ * Create or reset a BitmapArray.
+ * An error will be raised if any parameter is not of the correct type.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the bitmap array.
+ * <br><code>array_range text</code> Name of the Range variable that
+ * provides the range of the array part of the bitmap array.
+ * <br><code>bitmap_range text</code> Name of the Range variable that
+ * provides the range of each bitmap in the array.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_init_bitmap_array(PG_FUNCTION_ARGS)
+{
+    char        *bmarray_name;
+    char        *arrayrange_name;
+    char        *maprange_name;
+    VarEntry    *bmarray_var;
+    BitmapArray *bmarray;
+    Range       *arrayrange;
+    Range       *maprange;
+
+    ensure_init();
+
+    bmarray_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray_var = vl_lookup_variable(bmarray_name);
+    bmarray = GetBitmapArrayFromVar(bmarray_var, true);
+
+    arrayrange_name = strfromtext(PG_GETARG_TEXT_P(1));
+    arrayrange = GetRange(arrayrange_name, false);
+    maprange_name = strfromtext(PG_GETARG_TEXT_P(2));
+    maprange = GetRange(maprange_name, false);
+
+    vl_NewBitmapArray(&bmarray, bmarray_var->shared, 
+                     arrayrange->min, arrayrange->max,
+                     maprange->min, maprange->max);
+
+    bmarray_var->obj = (Object *) bmarray;
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_clear_bitmap_array);
+/** 
+ * <code>veil_clear_bitmap_array(bmarray text) returns bool</code>
+ * Clear the bits in an existing BitmapArray.
+ * An error will be raised if the parameter is not of the correct type.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the BitmapArray.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_clear_bitmap_array(PG_FUNCTION_ARGS)
+{
+    char        *bmarray_name;
+    VarEntry    *bmarray_var;
+    BitmapArray *bmarray;
+
+    ensure_init();
+
+    bmarray_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray_var = vl_lookup_variable(bmarray_name);
+    bmarray = GetBitmapArrayFromVar(bmarray_var, false);
+
+    vl_ClearBitmapArray(bmarray);
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_from_array);
+/** 
+ * <code>veil_bitmap_from_array(bmref text, bmarray text, index int4) returns text</code>
+ * Place a reference to the specified Bitmap from a BitmapArray into
+ * the specified BitmapRef
+ * An error will be raised if any parameter is not of the correct type.
+ *
+ * @param fcinfo <code>bmref text</code> The name of the BitmapRef into which
+ * a reference to the relevant Bitmap will be placed.
+ * <br><code>bmarray text</code> Name of the BitmapArray containing the Bitmap
+ * in which we are interested.
+ * <br><code>index int4</code> Index into the array of the bitmap in question.
+ * @return <code>text</code>  The name of the BitmapRef
+ */
+Datum
+veil_bitmap_from_array(PG_FUNCTION_ARGS)
+{
+   text        *bmref_text;
+   char        *bmref_name;
+   BitmapRef   *bmref;
+   char        *bmarray_name;
+    BitmapArray *bmarray;
+    int32        arrayelem;
+   Bitmap      *bitmap;
+
+   bmref_text = PG_GETARG_TEXT_P(0);
+    bmref_name = strfromtext(bmref_text);
+    bmref = GetBitmapRef(bmref_name);
+
+    bmarray_name = strfromtext(PG_GETARG_TEXT_P(1));
+    bmarray = GetBitmapArray(bmarray_name, false);
+
+   arrayelem = PG_GETARG_INT32(2);
+    bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+   if (!bitmap) {
+       ereport(ERROR,
+           (errcode(ERRCODE_INTERNAL_ERROR),
+            errmsg("Range error for BitmapArray %s, %d not in %d - %d",
+                   bmarray_name, arrayelem,
+                   bmarray->arrayzero, bmarray->arraymax)));
+   }
+
+   bmref->bitmap = bitmap;
+   bmref->xid = GetCurrentTransactionId();
+    PG_RETURN_TEXT_P(bmref_text);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_array_testbit);
+/** 
+ * <code>veil_bitmap_array_testbit(bmarray text, arr_idx int4, bitno int4) returns bool</code>
+ * Test a specified bit within a BitmapArray
+ *
+ * An error will be raised if the first parameter is not a BitmapArray.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the BitmapArray
+ * <br><code>arr_idx int4</code> Index of the Bitmap within the array.
+ * <br><code>bitno int4</code> Bit id of the bit within the Bitmap.
+ * @return <code>bool</code>  True if the bit was set, false otherwise.
+ */
+Datum
+veil_bitmap_array_testbit(PG_FUNCTION_ARGS)
+{
+    char        *name;
+    BitmapArray *bmarray;
+    Bitmap      *bitmap;
+    int32        arrayelem;
+    int32        bit;
+
+    ensure_init();
+
+    arrayelem = PG_GETARG_INT32(1);
+    bit = PG_GETARG_INT32(2);
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray = GetBitmapArray(name, false);
+    
+    bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+    if (bitmap) {
+        PG_RETURN_BOOL(vl_BitmapTestbit(bitmap, bit));
+    }
+    else {
+        PG_RETURN_BOOL(false);
+    }
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_array_setbit);
+/** 
+ * <code>veil_bitmap_array_setbit(bmarray text, arr_idx int4, bitno int4) returns bool</code>
+ * Set a specified bit within a BitmapArray
+ *
+ * An error will be raised if the first parameter is not a BitmapArray.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the BitmapArray
+ * <br><code>arr_idx int4</code> Index of the Bitmap within the array.
+ * <br><code>ibitno nt4</code> Bit id of the bit within the Bitmap.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_bitmap_array_setbit(PG_FUNCTION_ARGS)
+{
+    char        *name;
+    BitmapArray *bmarray;
+    Bitmap      *bitmap;
+    int32        arrayelem;
+    int32        bit;
+
+    ensure_init();
+
+    arrayelem = PG_GETARG_INT32(1);
+    bit = PG_GETARG_INT32(2);
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray = GetBitmapArray(name, false);
+
+    bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+    if (bitmap) {
+       vl_BitmapSetbit(bitmap, bit);
+        PG_RETURN_BOOL(true);
+    }
+    else {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap Array range error (%d not in %d..%d)", 
+                       arrayelem, bmarray->arrayzero, bmarray->arraymax),
+                errdetail("Attempt to reference BitmapArray element "
+                          "outside of the BitmapArray's defined range")));
+    }
+   PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_array_clearbit);
+/** 
+ * <code>veil_bitmap_array_clearbit(bmarray text, arr_idx int4, bitno int4) returns bool</code>
+ * Clear a specified bit within a BitmapArray
+ *
+ * An error will be raised if the first parameter is not a BitmapArray.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the BitmapArray
+ * <br><code>arr_idx int4</code> Index of the Bitmap within the array.
+ * <br><code>bitno int4</code> Bit id of the bit within the Bitmap.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_bitmap_array_clearbit(PG_FUNCTION_ARGS)
+{
+    char        *name;
+    BitmapArray *bmarray;
+    Bitmap      *bitmap;
+    int32        arrayelem;
+    int32        bit;
+
+    ensure_init();
+
+    arrayelem = PG_GETARG_INT32(1);
+    bit = PG_GETARG_INT32(2);
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray = GetBitmapArray(name, false);
+
+    bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+    if (bitmap) {
+       vl_BitmapClearbit(bitmap, bit);
+        PG_RETURN_BOOL(true);
+    }
+    else {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap Array range error (%d not in %d..%d)", 
+                       arrayelem, bmarray->arrayzero, bmarray->arraymax),
+                errdetail("Attempt to reference BitmapArray element "
+                          "outside of the BitmapArray's defined range")));
+    }
+   PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_union_from_bitmap_array);
+/** 
+ * <code>veil_union_from_bitmap_array(bitmap text, bmarray text, arr_idx int4) returns bool</code>
+ * Union a Bitmap with the specified Bitmap from a BitmapArray with the
+ * result placed into the first parameter.
+ *
+ * An error will be raised if the parameters are not of the correct types.
+ *
+ * @param fcinfo <code>bitmap text</code> The name of the Bitmap into which the
+ * resulting union will be placed.
+ * <br><code>bmarray text</code> Name of the BitmapArray
+ * <br><code>arr_idx int4</code> Index of the required bitmap in the array
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_union_from_bitmap_array(PG_FUNCTION_ARGS)
+{
+    char        *bitmap_name;
+    char        *bmarray_name;
+    Bitmap      *target;
+    BitmapArray *bmarray;
+    Bitmap      *bitmap;
+    int32        arrayelem;
+
+    ensure_init();
+
+    arrayelem = PG_GETARG_INT32(2);
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray_name = strfromtext(PG_GETARG_TEXT_P(1));
+    target = GetBitmap(bitmap_name, false, true);
+    bmarray = GetBitmapArray(bmarray_name, false);
+
+    bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+    if (bitmap) {
+        vl_BitmapUnion(target, bitmap);
+    }
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_intersect_from_bitmap_array);
+/** 
+ * <code>veil_intersect_from_bitmap_array(bitmap text, bmarray text, arr_idx int4) returns bool</code>
+ * Intersect a Bitmap with the specified Bitmap from a BitmapArray with the
+ * result placed into the first parameter.
+ *
+ * An error will be raised if the parameters are not of the correct types.
+ *
+ * @param fcinfo <code>bitmap text</code> The name of the Bitmap into which the
+ * resulting intersection will be placed.
+ * <br><code>bmarray text</code> Name of the BitmapArray
+ * <br><code>arr_idx int4</code> Index of the required bitmap in the array
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_intersect_from_bitmap_array(PG_FUNCTION_ARGS)
+{
+    char        *bitmap_name;
+    char        *bmarray_name;
+    Bitmap      *target;
+    BitmapArray *bmarray;
+    Bitmap      *bitmap;
+    int32        arrayelem;
+
+    ensure_init();
+
+    arrayelem = PG_GETARG_INT32(2);
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray_name = strfromtext(PG_GETARG_TEXT_P(1));
+    target = GetBitmap(bitmap_name, false, true);
+    bmarray = GetBitmapArray(bmarray_name, false);
+
+    bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+    if (bitmap) {
+        vl_BitmapIntersect(target, bitmap);
+    }
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_array_bits);
+/** 
+ * <code>veil_bitmap_array_bits(bmarray text, arr_idx int4)</code> returns setof int4
+ * Return the set of all bits set in the specified Bitmap from the
+ * BitmapArray.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the bitmap array.
+ * <br><code>arr_idx int4</code> Index of the required bitmap in the array
+ * @return <code>setof int4</code>The set of bits that are set in the
+ * bitmap.
+ */
+Datum
+veil_bitmap_array_bits(PG_FUNCTION_ARGS)
+{
+   struct bitmap_array_bits_state {
+       Bitmap *bitmap;
+       int32   bit;
+   } *state;
+    FuncCallContext *funcctx;
+   MemoryContext    oldcontext;
+    char   *name;
+    bool    found;
+    BitmapArray *bmarray;
+    int     arrayelem;
+    Datum   datum;
+    
+    if (SRF_IS_FIRSTCALL())
+    {
+        /* Only do this on first call for this result set */
+        ensure_init();
+        
+        funcctx = SRF_FIRSTCALL_INIT();
+       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+       state = palloc(sizeof(struct bitmap_array_bits_state));
+        MemoryContextSwitchTo(oldcontext);
+
+        name = strfromtext(PG_GETARG_TEXT_P(0));
+        arrayelem = PG_GETARG_INT32(1);
+        bmarray = GetBitmapArray(name, false);
+
+        if (!bmarray) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("BitmapArray %s is not defined",
+                           name),
+                    errhint("Perhaps the name is mis-spelled, or its "
+                            "definition is missing from "
+                            "veil_init().")));
+        }
+
+        state->bitmap = vl_BitmapFromArray(bmarray, arrayelem);
+        if (!state->bitmap) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("Bitmap Array range error (%d not in %d..%d)", 
+                           arrayelem, bmarray->arrayzero, bmarray->arraymax),
+                    errdetail("Attempt to reference BitmapArray element "
+                              "outside of the BitmapArray's defined range")));
+        }
+
+        state->bit = state->bitmap->bitzero;
+       funcctx->user_fctx = state;
+    }
+    
+    funcctx = SRF_PERCALL_SETUP();
+   state = funcctx->user_fctx;
+
+    state->bit = vl_BitmapNextBit(state->bitmap, state->bit, &found);
+    
+    if (found) {
+        datum = Int32GetDatum(state->bit);
+        state->bit++;
+        SRF_RETURN_NEXT(funcctx, datum);
+    }
+    else {
+        SRF_RETURN_DONE(funcctx);
+    }
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_array_arange);
+/** 
+ * <code>veil_bitmap_array_arange(bmarray text) returns veil_range_t</code>
+ * Return composite type giving the range of the array part of the
+ * specified BitmapArray
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the bitmap array.
+ * @return <code>veil_range_t</code>  Composite type containing the min
+ * and max indices of the array
+ */
+Datum
+veil_bitmap_array_arange(PG_FUNCTION_ARGS)
+{
+    char        *name;
+    BitmapArray *bmarray;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray = GetBitmapArray(name, false);
+
+    if (!bmarray) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("BitmapArray %s is not defined",
+                       name),
+                errhint("Perhaps the name is mis-spelled, or its "
+                        "definition is missing from veil_init().")));
+    }
+
+    PG_RETURN_DATUM(datum_from_range(bmarray->arrayzero, bmarray->arraymax));
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_array_brange);
+/** 
+ * <code>veil_bitmap_array_brange(bmarray text) returns veil_range_t</code>
+ * Return composite type giving the range of every Bitmap within
+ * the BitmapArray.
+ *
+ * @param fcinfo <code>bmarray text</code> The name of the bitmap array.
+ * @return <code>veil_range_t</code>  Composite type containing the min
+ * and max values of the bitmap array's range
+ */
+Datum
+veil_bitmap_array_brange(PG_FUNCTION_ARGS)
+{
+    char        *name;
+    BitmapArray *bmarray;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmarray = GetBitmapArray(name, false);
+
+    if (!bmarray) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("BitmapArray %s is not defined",
+                       name),
+                errhint("Perhaps the name is mis-spelled, or its "
+                        "definition is missing from veil_init().")));
+    }
+
+    PG_RETURN_DATUM(datum_from_range(bmarray->bitzero, bmarray->bitmax));
+}
+
+
+
+PG_FUNCTION_INFO_V1(veil_init_bitmap_hash);
+/** 
+ * <code>veil_init_bitmap_hash(bmhash text, range text) returns bool</code>
+ * Create or reset a BitmapHash.
+ * An error will be raised if any parameter is not of the correct type.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the bitmap hash.
+ * <br><code>range text</code> Name of the Range variable that provides the
+ * range of each bitmap in the hash.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_init_bitmap_hash(PG_FUNCTION_ARGS)
+{
+    char       *bmhash_name;
+    char       *range_name;
+    VarEntry   *bmhash_var;
+    BitmapHash *bmhash;
+    Range      *range;
+
+    ensure_init();
+
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash_var = vl_lookup_variable(bmhash_name);
+    bmhash = GetBitmapHashFromVar(bmhash_var, true);
+
+    range_name = strfromtext(PG_GETARG_TEXT_P(1));
+    range = GetRange(range_name, false);
+
+   if (bmhash_var->shared) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("illegal attempt to define shared BitmapHash %s",
+                       bmhash_name),
+                errhint("BitmapHashes may only be defined as session, "
+                        "not shared, variables.")));
+   }
+    vl_NewBitmapHash(&bmhash, bmhash_name,
+                    range->min, range->max);
+
+    bmhash_var->obj = (Object *) bmhash;
+
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_clear_bitmap_hash);
+/** 
+ * <code>veil_clear_bitmap_hash(bmhash text) returns bool</code>
+ * Clear the bits in an existing BitmapHash.
+ * An error will be raised if the parameter is not of the correct type.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the BitmapHash.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_clear_bitmap_hash(PG_FUNCTION_ARGS)
+{
+    char       *bmhash_name;
+    VarEntry   *bmhash_var;
+    BitmapHash *bmhash;
+
+    ensure_init();
+
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash_var = vl_lookup_variable(bmhash_name);
+    bmhash = GetBitmapHashFromVar(bmhash_var, true);
+
+    vl_NewBitmapHash(&bmhash, bmhash_name,
+                    bmhash->bitzero, bmhash->bitmax);
+
+    bmhash_var->obj = (Object *) bmhash;
+
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_key_exists);
+/** 
+ * <code>veil_bitmap_hashkey_exists(bmhash text, key text) returns bool</code>
+ * Return true if the key exists in the bitmap hash.
+ *
+ * @param fcinfo <code>bmhash text</code> Name of the BitmapHashin which we are
+ * interested.
+ * <br><code>key text</code> Key, into the hash, of the bitmap in question.
+ * @return <code>boolean</code>  Whether the key is present in the BitmapHash
+ */
+Datum
+veil_bitmap_hash_key_exists(PG_FUNCTION_ARGS)
+{
+   char       *bmhash_name;
+    BitmapHash *bmhash;
+    char       *hashelem;
+   bool        found;
+
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash = GetBitmapHash(bmhash_name, false);
+
+   hashelem = strfromtext(PG_GETARG_TEXT_P(1));
+   
+    found = vl_BitmapHashHasKey(bmhash, hashelem);
+   
+    PG_RETURN_BOOL(found);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_from_hash);
+/** 
+ * <code>veil_bitmap_from_hash(bmref text, bmhash text, key text) returns text</code>
+ * Place a reference to the specified Bitmap from a BitmapHash into
+ * the specified BitmapRef
+ * An error will be raised if any parameter is not of the correct type.
+ *
+ * @param fcinfo <code>bmref text</code> The name of the BitmapRef into which
+ * a reference to the relevant Bitmap will be placed.
+ * <br><code>bmhash text</code> Name of the BitmapHash containing the Bitmap
+ * in which we are interested.
+ * <br><code>key text</code> Key, into the hash, of the bitmap in question.
+ * @return <code>text</code>  The name of the BitmapRef
+ */
+Datum
+veil_bitmap_from_hash(PG_FUNCTION_ARGS)
+{
+   text       *bmref_text;
+   char       *bmref_name;
+   BitmapRef  *bmref;
+   char       *bmhash_name;
+    BitmapHash *bmhash;
+    char       *hashelem;
+   Bitmap     *bitmap;
+
+   bmref_text = PG_GETARG_TEXT_P(0);
+    bmref_name = strfromtext(bmref_text);
+    bmref = GetBitmapRef(bmref_name);
+
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(1));
+    bmhash = GetBitmapHash(bmhash_name, false);
+
+   hashelem = strfromtext(PG_GETARG_TEXT_P(2));
+    bitmap = vl_AddBitmapToHash(bmhash, hashelem);
+   
+   bmref->bitmap = bitmap;
+   bmref->xid = GetCurrentTransactionId();
+    PG_RETURN_TEXT_P(bmref_text);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_testbit);
+/** 
+ * <code>veil_bitmap_hash_testbit(bmhash text, key text, bitno int4) returns bool</code>
+ * Test a specified bit within a BitmapHash
+ *
+ * An error will be raised if the first parameter is not a BitmapHash.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the BitmapHash
+ * <br><code>key text</code> Key of the Bitmap within the hash.
+ * <br><code>bitno int4</code> Bit id of the bit within the Bitmap.
+ * @return <code>bool</code>  True if the bit was set, false otherwise.
+ */
+Datum
+veil_bitmap_hash_testbit(PG_FUNCTION_ARGS)
+{
+    char       *name;
+    BitmapHash *bmhash;
+    char       *hashelem;
+    Bitmap     *bitmap;
+    int32       bit;
+
+    ensure_init();
+
+    hashelem = strfromtext(PG_GETARG_TEXT_P(1));
+    bit = PG_GETARG_INT32(2);
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash = GetBitmapHash(name, false);
+    
+    bitmap = vl_BitmapFromHash(bmhash, hashelem);
+    if (bitmap) {
+        PG_RETURN_BOOL(vl_BitmapTestbit(bitmap, bit));
+    }
+    else {
+        PG_RETURN_BOOL(false);
+    }
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_setbit);
+/** 
+ * <code>veil_bitmap_hash_setbit(bmhash text, key text, bitno int4) returns bool</code>
+ * Set a specified bit within a BitmapHash
+ *
+ * An error will be raised if the first parameter is not a BitmapHash.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the BitmapHash
+ * <br><code>key text</code> Key of the Bitmap within the hash.
+ * <br><code>bitno int4</code> Bit id of the bit within the Bitmap.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_bitmap_hash_setbit(PG_FUNCTION_ARGS)
+{
+    char       *name;
+    BitmapHash *bmhash;
+    Bitmap     *bitmap;
+    char       *hashelem;
+    int32       bit;
+
+    ensure_init();
+
+    hashelem = strfromtext(PG_GETARG_TEXT_P(1));
+    bit = PG_GETARG_INT32(2);
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash = GetBitmapHash(name, false);
+
+    bitmap = vl_AddBitmapToHash(bmhash, hashelem);
+
+   vl_BitmapSetbit(bitmap, bit);
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_clearbit);
+/** 
+ * <code>veil_bitmap_hash_clearbit(bmhash text, key text, bitno int4) returns bool</code>
+ * Clear a specified bit within a BitmapHash
+ *
+ * An error will be raised if the first parameter is not a BitmapHash.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the BitmapHash
+ * <br><code>key text</code> Key of the Bitmap within the hash.
+ * <br><code>bitno int4</code> Bit id of the bit within the Bitmap.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_bitmap_hash_clearbit(PG_FUNCTION_ARGS)
+{
+    char       *name;
+    BitmapHash *bmhash;
+    Bitmap     *bitmap;
+    char       *hashelem;
+    int32       bit;
+
+    ensure_init();
+
+    hashelem = strfromtext(PG_GETARG_TEXT_P(1));
+    bit = PG_GETARG_INT32(2);
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash = GetBitmapHash(name, false);
+
+    bitmap = vl_AddBitmapToHash(bmhash, hashelem);
+
+   vl_BitmapClearbit(bitmap, bit);
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_union_into_bitmap_hash);
+/** 
+ * <code>veil_union_into_bitmap_hash(bmhash text, key text, bitmap text) returns bool</code>
+ * Union a Bitmap with the specified Bitmap from a BitmapHash with the
+ * result placed into the bitmap hash.
+ *
+ * An error will be raised if the parameters are not of the correct types.
+ *
+ * @param fcinfo <code>bmhash text</code> Name of the BitmapHash
+ * <br><code>key text</code> Key of the required bitmap in the hash
+ * <br><code>bitmap text</code> The name of the Bitmap into which the
+ * resulting union will be placed.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_union_into_bitmap_hash(PG_FUNCTION_ARGS)
+{
+    char       *bitmap_name;
+    char       *bmhash_name;
+    Bitmap     *target;
+    BitmapHash *bmhash;
+    Bitmap     *bitmap;
+    char       *hashelem;
+
+    ensure_init();
+
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(0));
+    hashelem = strfromtext(PG_GETARG_TEXT_P(1));
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(2));
+    bitmap = GetBitmap(bitmap_name, false, true);
+    bmhash = GetBitmapHash(bmhash_name, false);
+
+    target = vl_AddBitmapToHash(bmhash, hashelem);
+    if (target && bitmap) {
+        vl_BitmapUnion(target, bitmap);
+    }
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_union_from_bitmap_hash);
+/** 
+ * <code>veil_union_from_bitmap_hash(bmhash text, key text, bitmap text) returns bool</code>
+ * Union a Bitmap with the specified Bitmap from a BitmapHash with the
+ * result placed into the bitmap parameter.
+ *
+ * An error will be raised if the parameters are not of the correct types.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the Bitmap into which the
+ * resulting union will be placed.
+ * <br><code>key text</code> Name of the BitmapHash
+ * <br><code>bitmap text</code> Key of the required bitmap in the hash
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_union_from_bitmap_hash(PG_FUNCTION_ARGS)
+{
+    char       *bitmap_name;
+    char       *bmhash_name;
+    Bitmap     *target;
+    BitmapHash *bmhash;
+    Bitmap     *bitmap;
+    char       *hashelem;
+
+    ensure_init();
+
+    hashelem = strfromtext(PG_GETARG_TEXT_P(2));
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(1));
+    target = GetBitmap(bitmap_name, false, true);
+    bmhash = GetBitmapHash(bmhash_name, false);
+
+    bitmap = vl_BitmapFromHash(bmhash, hashelem);
+    if (bitmap) {
+        vl_BitmapUnion(target, bitmap);
+    }
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_intersect_from_bitmap_hash);
+/** 
+ * <code>veil_intersect_from_bitmap_hash(bitmap text, bmhash text, key text) returns bool</code>
+ * Intersect a Bitmap with the specified Bitmap from a BitmapArray with the
+ * result placed into the bitmap parameter.
+ *
+ * An error will be raised if the parameters are not of the correct types.
+ *
+ * @param fcinfo <code>bitmap text</code> The name of the Bitmap into which the
+ * resulting intersection will be placed.
+ * <br><code>bmhash text</code> Name of the BitmapHash
+ * <br><code>key text</code> Key of the required bitmap in the hash
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_intersect_from_bitmap_hash(PG_FUNCTION_ARGS)
+{
+    char       *bitmap_name;
+    char       *bmhash_name;
+    Bitmap     *target;
+    BitmapHash *bmhash;
+    Bitmap     *bitmap;
+    char       *hashelem;
+
+    ensure_init();
+
+    hashelem = strfromtext(PG_GETARG_TEXT_P(2));
+
+    bitmap_name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash_name = strfromtext(PG_GETARG_TEXT_P(1));
+    target = GetBitmap(bitmap_name, false, true);
+    bmhash = GetBitmapHash(bmhash_name, false);
+
+    bitmap = vl_BitmapFromHash(bmhash, hashelem);
+    if (bitmap) {
+        vl_BitmapIntersect(target, bitmap);
+    }
+   else {
+       /* The bitmap from the hash does not exist, so it is logically
+        * empty.  Intersection with an empty set yields an empty set. */
+       vl_ClearBitmap(target);
+   }
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_bits);
+/** 
+ * <code>veil_bitmap_hash_bits(bmhash text, key text)</code> returns setof int4
+ * Return the set of all bits set in the specified Bitmap from the
+ * BitmapHash.
+ *
+ * @param fcinfo <code>bmhashtext</code> The name of the bitmap hash.
+ * <br><code>key text</code> Key of the required bitmap in the hash
+ * @return <code>setof int4</code>The set of bits that are set in the
+ * bitmap.
+ */
+Datum
+veil_bitmap_hash_bits(PG_FUNCTION_ARGS)
+{
+   struct bitmap_hash_bits_state {
+       Bitmap *bitmap;
+       int32   bit;
+   } *state;
+    FuncCallContext *funcctx;
+   MemoryContext    oldcontext;
+    char   *name;
+    bool    found;
+    BitmapHash *bmhash;
+    char   *hashelem;
+    Datum   datum;
+    
+    if (SRF_IS_FIRSTCALL())
+    {
+        /* Only do this on first call for this result set */
+        ensure_init();
+        
+        funcctx = SRF_FIRSTCALL_INIT();
+       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+       state = palloc(sizeof(struct bitmap_hash_bits_state));
+        MemoryContextSwitchTo(oldcontext);
+
+        name = strfromtext(PG_GETARG_TEXT_P(0));
+        hashelem = strfromtext(PG_GETARG_TEXT_P(1));
+        bmhash = GetBitmapHash(name, false);
+
+        if (!bmhash) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("Bitmap Hash %s not defined", name)));
+        }
+
+        state->bitmap = vl_BitmapFromHash(bmhash, hashelem);
+        if (!state->bitmap) {
+           SRF_RETURN_DONE(funcctx);
+        }
+
+        state->bit = state->bitmap->bitzero;
+       funcctx->user_fctx = state;
+    }
+    
+    funcctx = SRF_PERCALL_SETUP();
+   state = funcctx->user_fctx;
+
+    state->bit = vl_BitmapNextBit(state->bitmap, state->bit, &found);
+    
+    if (found) {
+        datum = Int32GetDatum(state->bit);
+        state->bit++;
+        SRF_RETURN_NEXT(funcctx, datum);
+    }
+    else {
+        SRF_RETURN_DONE(funcctx);
+    }
+}
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_range);
+/** 
+ * <code>veil_bitmap_hash_range(bmhash text) returns veil_range_t</code>
+ * Return composite type giving the range of every Bitmap within
+ * the BitmapHash.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the bitmap array.
+ * @return <code>veil_range_t</code>  Composite type containing the min
+ * and max values of the bitmap hash's range
+ */
+Datum
+veil_bitmap_hash_range(PG_FUNCTION_ARGS)
+{
+    char       *name;
+    BitmapHash *bmhash;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    bmhash = GetBitmapHash(name, false);
+
+    if (!bmhash) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Bitmap Hash %s not defined", name)));
+    }
+
+    PG_RETURN_DATUM(datum_from_range(bmhash->bitzero, bmhash->bitmax));
+}
+
+
+PG_FUNCTION_INFO_V1(veil_bitmap_hash_entries);
+/** 
+ * <code>veil_bitmap_hash_entries(bmhash text) returns setof text</code>
+ * Return  the key of every Bitmap within the BitmapHash.
+ *
+ * @param fcinfo <code>bmhash text</code> The name of the bitmap hash.
+ * @return <code>setof text</code>  Every key in the hash.
+ */
+Datum
+veil_bitmap_hash_entries(PG_FUNCTION_ARGS)
+{
+   struct bitmap_hash_entries_state {
+       BitmapHash *bmhash;
+       VarEntry   *var;
+   } *state;
+    FuncCallContext *funcctx;
+   MemoryContext    oldcontext;
+    char  *name;
+    Datum  datum;
+    text  *result;
+
+    if (SRF_IS_FIRSTCALL())
+    {
+        /* Only do this on first call for this result set */
+        ensure_init();
+        
+        funcctx = SRF_FIRSTCALL_INIT();
+       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+       state = palloc(sizeof(struct bitmap_hash_entries_state));
+        MemoryContextSwitchTo(oldcontext);
+
+        name = strfromtext(PG_GETARG_TEXT_P(0));
+        state->bmhash = GetBitmapHash(name, false);
+
+        if (!state->bmhash) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("Bitmap Hash %s not defined", name)));
+        }
+
+        state->var = NULL;
+       funcctx->user_fctx = state;
+    }
+
+    funcctx = SRF_PERCALL_SETUP();
+   state = funcctx->user_fctx;
+
+    state->var = vl_NextHashEntry(state->bmhash->hash, state->var);
+
+    if (state->var) {
+        result = textfromstrn(state->var->key, HASH_KEYLEN);
+        datum = PointerGetDatum(result);
+        SRF_RETURN_NEXT(funcctx, datum);
+    }
+    else {
+        SRF_RETURN_DONE(funcctx);
+    }
+}
+
+
+PG_FUNCTION_INFO_V1(veil_int4_set);
+/** 
+ * <code>veil_int4_set(name text,value int4) returns int4</code>
+ * Set an Int4Var variable type to a specified value.
+ * An Error will be raised if the variable is not defined or is of the
+ * wrong type.
+ *
+ * @param fcinfo <code>name text</code> The name of the int4 variable.
+ * @param fcinfo <code>value int4</code> The value to be set (may be null).
+ * @return <code>int4</code>  The new value of the variable.
+ */
+Datum
+veil_int4_set(PG_FUNCTION_ARGS)
+{
+    char     *name;
+    Int4Var  *var;
+    int32     value;
+
+    ensure_init();
+
+    if (PG_ARGISNULL(0)) {
+        PG_RETURN_NULL();
+   }
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    var = GetInt4Var(name, true);
+
+    if (PG_ARGISNULL(1)) {
+        var->isnull = true;
+        PG_RETURN_NULL();
+    }
+    else {
+        value = PG_GETARG_INT32(1);
+        var->isnull = false;
+        var->value = value;
+        PG_RETURN_INT32(value);
+    }
+}
+
+PG_FUNCTION_INFO_V1(veil_int4_get);
+/** 
+ * <code>veil_int4_get(name text) returns int4</code>
+ * Return the value of an Int4Var variable.
+ * An Error will be raised if the variable is not defined or is of the
+ * wrong type.
+ *
+ * @param fcinfo <code>name text</code> The name of the int4 variable.
+ * @return <code>int4</code>  The value of the variable.
+ */
+Datum
+veil_int4_get(PG_FUNCTION_ARGS)
+{
+    char        *name;
+    Int4Var     *var;
+
+    ensure_init();
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+    var = GetInt4Var(name, true);
+
+    if (var->isnull) {
+        PG_RETURN_NULL();
+    }
+    else {
+        PG_RETURN_INT32(var->value);
+    }
+}
+
+PG_FUNCTION_INFO_V1(veil_init_int4array);
+/** 
+ * <code>veil_init_int4array(arrayname text, range text) returns bool</code>
+ * Initialise an Int4Array variable.  Each entry in the array will be
+ * zeroed.
+ *
+ * @param fcinfo <code>arrayname text</code> The name of the Int4Array variable.
+ * <br><code>range text</code> Name of the range variable defining the size of
+ * the array.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_init_int4array(PG_FUNCTION_ARGS)
+{
+    char      *array_name;
+    char      *range_name;
+    VarEntry  *array_var;
+    Int4Array *array;
+   Range     *range;
+
+    ensure_init();
+
+    array_name = strfromtext(PG_GETARG_TEXT_P(0));
+    array_var = vl_lookup_variable(array_name);
+   array = GetInt4ArrayFromVar(array_var, true);
+
+    range_name = strfromtext(PG_GETARG_TEXT_P(1));
+    range = GetRange(range_name, false);
+
+    array = vl_NewInt4Array(array, array_var->shared, range->min, range->max);
+   array_var->obj = (Object *) array;
+
+   PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_clear_int4array);
+/** 
+ * <code>veil_clear_int4array(name text) returns bool</code>
+ * Clear an Int4Array variable.  Each entry in the array will be
+ * zeroed.
+ *
+ * @param fcinfo <code>name text</code> The name of the Int4Array variable.
+ * @return <code>bool</code>  True
+ */
+Datum
+veil_clear_int4array(PG_FUNCTION_ARGS)
+{
+    char      *array_name;
+    Int4Array *array;
+
+    ensure_init();
+
+    array_name = strfromtext(PG_GETARG_TEXT_P(0));
+   array = GetInt4Array(array_name, false);
+
+    vl_ClearInt4Array(array);
+
+   PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_int4array_set);
+/** 
+ * <code>veil_int4array_set(array text, idx int4, value int4) returns int4</code>
+ * Set an Int4Array entry. 
+ *
+ * @param fcinfo <code>array text</code> The name of the Int4Array variable.
+ * <br><code>idx int4</code> Index of the entry to be set
+ * <br><code>value int4</code> Value to which the entry will be set
+ * @return <code>int4</code> The new value of the array entry
+ */
+Datum
+veil_int4array_set(PG_FUNCTION_ARGS)
+{
+    char      *array_name;
+    Int4Array *array;
+    int32      idx;
+    int32      value;
+
+    ensure_init();
+
+    array_name = strfromtext(PG_GETARG_TEXT_P(0));
+   array = GetInt4Array(array_name, false);
+   idx = PG_GETARG_INT32(1);
+   value = PG_GETARG_INT32(2);
+    vl_Int4ArraySet(array, idx, value);
+
+   PG_RETURN_INT32(value);
+}
+
+PG_FUNCTION_INFO_V1(veil_int4array_get);
+/** 
+ * <code>veil_int4array_get(array text, idx int4) returns int4</code>
+ * Get an Int4Array entry. 
+ *
+ * @param fcinfo <code>array text</code> The name of the Int4Array variable.
+ * <br><code>idx int4</code> Index of the entry to be retrieved
+ * @return <code>int4</code> The value of the array entry
+ */
+Datum
+veil_int4array_get(PG_FUNCTION_ARGS)
+{
+    char      *array_name;
+    Int4Array *array;
+    int32      idx;
+    int32      value;
+
+    ensure_init();
+
+    array_name = strfromtext(PG_GETARG_TEXT_P(0));
+   array = GetInt4Array(array_name, false);
+   idx = PG_GETARG_INT32(1);
+    value = vl_Int4ArrayGet(array, idx);
+
+   PG_RETURN_INT32(value);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_init);
+/** 
+ * <code>veil_init(doing_reset bool) returns bool</code>
+ * Initialise or reset a veil session.
+ * The boolean parameter will be false when called for initialisation,
+ * and true when performing a reset.
+ *
+ * This function must be redefined as a custom function in your
+ * implementation. 
+ *
+ * @param fcinfo <code>doing_reset bool</code> Whether we are being
+ * called in order to reset (true) the session or (false) simply to
+ * initialise it. 
+ * @return <code>bool</code> True
+ */
+Datum
+veil_init(PG_FUNCTION_ARGS)
+{
+   ereport(ERROR,
+           (errcode(ERRCODE_INTERNAL_ERROR),
+            errmsg("default veil version of veil_init() has been called"),
+            errhint("You must define your own version of this function.")));
+
+    PG_RETURN_BOOL(true);
+}
+
+PG_FUNCTION_INFO_V1(veil_perform_reset);
+/** 
+ * <code>veil_perform_reset() returns bool</code>
+ * Reset veil shared memory for this database.  This creates a new
+ * shared memory context with none of the existing shared variables.
+ * All current transactions will be able to continue using the current
+ * variables, all new transactions will see the new set, once this
+ * function has completed.
+ *
+ * @param fcinfo 
+ * @return <code>bool</code> True if the function is able to complete
+ * successfully.  If it is unable, no harm will have been done but
+ * neither will a memory reset have been performed.
+ */
+Datum
+veil_perform_reset(PG_FUNCTION_ARGS)
+{
+    bool success = true;
+    bool result;
+    int  ok;
+
+   ensure_init();
+    ok = vl_spi_connect();
+    if (ok != SPI_OK_CONNECT) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("failed to perform reset"),
+                errdetail("SPI_connect() failed, returning %d.", ok)));
+    }
+
+    success = vl_prepare_context_switch();  
+    if (success) {
+        result = vl_bool_from_query("select veil_init(TRUE)", &success);
+       elog(NOTICE, "veil_init returns %s to veil_perform_reset", 
+            result? "true": "false");
+        success = vl_complete_context_switch();
+       elog(NOTICE, 
+            "vl_complete_context_switch returns %s to veil_perform_reset", 
+            success? "true": "false");
+       success &= result;
+    }
+    else {
+       ereport(WARNING,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("failed to perform reset"),
+                errdetail("Unable to prepare for memory reset.  "
+                          "Maybe another process is performing a reset, "
+                          "or maybe there is a long-running transaction that "
+                          "is still using the previous memory context.")));
+    }
+
+    ok = vl_spi_finish();
+    PG_RETURN_BOOL(success);
+}
+
+PG_FUNCTION_INFO_V1(veil_force_reset);
+/** 
+ * <code>veil_force_reset() returns bool</code>
+ * Reset veil shared memory for this database, ignoring existing
+ * transactions.  This function will always reset the shared memory
+ * context, even for sessions that are still using it.  Having taken
+ * this drastic action, it will then cause a panic to reset the server.
+ *
+ * Question - won't a panic reset the shared memory in any case?  Is the
+ * panic superfluous, or maybe is this entire function superfluous?
+ *
+ * @param fcinfo 
+ * @return <code>bool</code> True.
+ */
+Datum
+veil_force_reset(PG_FUNCTION_ARGS)
+{
+   ensure_init();
+    vl_force_context_switch();
+    PG_RETURN_BOOL(true);
+}
+
+
+PG_FUNCTION_INFO_V1(veil_version);
+/** 
+ * <code>veil_version() returns text</code>
+ * Return a string describing this version of veil.
+ *
+ * @param fcinfo 
+ * @return <code>text</code> String describing the version.
+ */
+Datum
+veil_version(PG_FUNCTION_ARGS)
+{
+    char *version_str;
+   text *version_text;
+   
+    version_str = palloc(sizeof(char) * strlen(VEIL_VERSION) + 
+                        strlen(VEIL_VERSION_INFO) + 4);
+   sprintf(version_str, "%s (%s)", VEIL_VERSION, VEIL_VERSION_INFO);
+
+   version_text = textfromstr(version_str);
+   PG_RETURN_TEXT_P(version_text);
+}
+
+PG_FUNCTION_INFO_V1(veil_serialise);
+/** 
+ * <code>veil_serialise(varname text) returns text</code>
+ * Return a string representing the contents of our variable.
+ *
+ * @param fcinfo 
+ * <br><code>varname text</code> Name of the variable to be serialised.
+ * @return <code>text</code> String containing the serialised variable.
+ */
+Datum
+veil_serialise(PG_FUNCTION_ARGS)
+{
+    char  *name;
+   char  *result;
+
+    ensure_init();
+
+    if (PG_ARGISNULL(0)) {
+        PG_RETURN_NULL();
+   }
+
+    name = strfromtext(PG_GETARG_TEXT_P(0));
+   result = vl_serialise_var(name);
+
+   if (result) {
+       PG_RETURN_TEXT_P(textfromstr(result));
+   }
+   else {
+       PG_RETURN_NULL();
+   }
+}
+
+
+PG_FUNCTION_INFO_V1(veil_deserialise);
+/** 
+ * <code>veil_deserialise(stream text) returns text</code>
+ * Create or reset variables based on the output of previous
+ * veil_serialise calls.
+ *
+ * @param fcinfo 
+ * <br><code>stream text</code> Serialised variables string
+ * @return <code>int4</code> Count of the items de-serialised from
+ * the string.
+ */
+Datum
+veil_deserialise(PG_FUNCTION_ARGS)
+{
+    char  *stream;
+   int4   result;
+   text  *txt;
+
+    ensure_init();
+
+    if (PG_ARGISNULL(0)) {
+        PG_RETURN_NULL();
+   }
+
+   txt = PG_GETARG_TEXT_P(0);
+   stream = strfromtext(txt);
+   result = vl_deserialise(&stream);
+
+   PG_RETURN_INT32(result);
+}
+
+
diff --git a/src/veil_interface.d b/src/veil_interface.d
new file mode 100644 (file)
index 0000000..a242884
--- /dev/null
@@ -0,0 +1,5 @@
+src/veil_interface.o src/veil_interface.d: \
+  src/veil_interface.c \
+  src/veil_version.h \
+  src/veil_funcs.h \
+  src/veil_datatypes.h
diff --git a/src/veil_interface.sqs b/src/veil_interface.sqs
new file mode 100644 (file)
index 0000000..25b0036
--- /dev/null
@@ -0,0 +1,743 @@
+/* ----------
+ * veil_interface.sqs (or a file derived from it)
+ *
+ *      Source file from which veil_interface_trial.sql and
+ *      veil--<version>.sql is generated using sed.
+ *
+ *      Copyright (c) 2005 - 2011 Marc Munro
+ *      Author:  Marc Munro
+ * License: BSD
+ *
+ * ----------
+ */
+
+create type veil_range_t as (
+    min  int4,
+    max  int4
+);
+comment on type veil_range_t is
+'Veil type used to record ranges.  A range is a pair of integers identifying
+the minimum and maximum values of the range.  Ranges are used to
+constrain the size of a bitmap or bitmap array.';
+
+create type veil_variable_t as (
+    name    text,
+    type    text,
+    shared  bool
+);
+comment on type veil_variable_t is
+'Veil type used as the result type of veil_variables(), to describe each
+variable known to a veil instance.';
+
+
+create or replace
+function veil_share(name text) returns bool
+     as '@LIBPATH@', 'veil_share'
+     language C stable strict;
+
+comment on function veil_share(name text) is
+'Create a shared variable named NAME.
+
+Return TRUE if successful, else raise an error.
+
+Create a veil variable as a shared variable.  Variables are named
+containers for various types of values.  They are referenced by name,
+passing the name as a string to the various veil functions.
+
+Variables may be shared variables in which case they are available, with
+the same value, to all sessions, or session variables in which case each
+session will have its own instance.  Variables are by default created as
+session variables, and are created by assignment, or initialisation.  To
+create a shared variable, call veil_share() with the name of the
+variable you wish to create, then create and use the variable as you
+would a session variable.  Shared variables should only be created and
+initialised from veil_init() in order to prevent race conditions.  If a
+variable has already been created as a session variable, it cannot be
+repurposed as a shared variable.';
+
+
+create or replace
+function veil_variables() returns setof veil_variable_t
+     as '@LIBPATH@', 'veil_variables'
+     language C stable;
+
+comment on function veil_variables() is
+'List all current veil_variables.
+Return a set of veil_variable_t results, detailing each existant
+variable
+
+This is intended for interactive use for debugging purposes.';
+
+
+create or replace
+function veil_init_range(name text, min int4, max int4) returns int4
+     as '@LIBPATH@', 'veil_init_range'
+     language C stable strict;
+
+comment on function veil_init_range(text, int4, int4) is
+'Initialise a Range variable called NAME constrained by MIN and MAX.
+
+Return the number of  elements in the range.
+
+Ranges may be examined using the veil_range() function.';
+
+
+create or replace
+function veil_range(name text) returns veil_range_t
+     as '@LIBPATH@', 'veil_range'
+     language C stable strict;
+
+comment on function veil_range(name text) is
+'Return the range for range variable NAME.';
+
+
+create or replace
+function veil_init_bitmap(bitmap_name text, range_name text) returns bool
+     as '@LIBPATH@', 'veil_init_bitmap'
+     language C stable strict;
+
+comment on function veil_init_bitmap(text, text) is
+'Create or re-initialise the Bitmap named BITMAP_NAME, for the range of
+bits given by RANGE_NAME.
+
+Return TRUE on success, raise an error otherwise.
+
+All bits in the bitmap will be zero (cleared).';
+
+
+create or replace
+function veil_clear_bitmap(name text) returns bool
+     as '@LIBPATH@', 'veil_clear_bitmap'
+     language C stable strict;
+
+comment on function veil_clear_bitmap(text) is
+'Clear the Bitmap or BitmapRef identified by NAME.
+
+Return TRUE, or raise an error.
+
+Clear (set to zero) all bits in the named bitmap.';
+
+
+create or replace
+function veil_bitmap_setbit(name text, bit_number int4) returns bool
+     as '@LIBPATH@', 'veil_bitmap_setbit'
+     language C stable strict;
+
+comment on function veil_bitmap_setbit(text, int4) is
+'In the Bitmap or BitmapRef identified by NAME, set the bit given by
+BIT_NUMBER.
+
+Return TRUE or raise an error.
+
+Set to 1, the identified bit.';
+
+
+create or replace
+function veil_bitmap_clearbit(name text, bit_number int4) returns bool
+     as '@LIBPATH@', 'veil_bitmap_clearbit'
+     language C stable strict;
+
+comment on function veil_bitmap_clearbit(text, int4) is
+'In the Bitmap or BitmapRef identified by NAME, clear the bit given by
+BIT_NUMBER.
+
+Return TRUE or raise an error.
+
+Set to 0, the identified bit.';
+
+
+create or replace
+function veil_bitmap_testbit(name text, bit_number int4) returns bool
+     as '@LIBPATH@', 'veil_bitmap_testbit'
+     language C stable strict;
+
+comment on function veil_bitmap_testbit(text, int4) is
+'In the Bitmap or BitmapRef identified by NAME, test the bit given by
+BIT_NUMBER.
+
+Return TRUE if the bit is set, FALSE if it is zero.';
+
+
+create or replace
+function veil_bitmap_union(result_name text, name2 text) returns bool
+     as '@LIBPATH@', 'veil_bitmap_union'
+     language C stable strict;
+
+comment on function veil_bitmap_union(text, text) is
+'Union two Bitmaps, RESULT_NAME and NAME2, with the result going into
+the first.
+
+Return TRUE, or raise an error.';
+
+
+create or replace
+function veil_bitmap_intersect(result_name text, name2 text) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_intersect'
+     language C stable strict;
+
+comment on function veil_bitmap_intersect(text, text) is
+'Intersect two Bitmaps, RESULT_NAME and NAME2, with the result going into
+the first.
+
+Return TRUE, or raise an error.';
+
+
+create or replace
+function veil_bitmap_bits(name text) returns setof int4
+     as '@LIBPATH@', 'veil_bitmap_bits'
+     language C stable strict;
+
+comment on function veil_bitmap_bits(text) is
+'Return each bit in the bitmap NAME.
+
+This is primarily intended for interactive use for debugging, etc.';
+
+
+create or replace
+function veil_bitmap_range(name text) returns veil_range_t
+     as '@LIBPATH@', 'veil_bitmap_range'
+     language C stable strict;
+
+comment on function veil_bitmap_range(text) is
+'Return the range of bitmap NAME.  
+
+It is primarily intended for interactive use.';
+
+
+
+create or replace
+function veil_init_bitmap_array(bmarray text, array_range text, 
+               bitmap_range text) returns bool
+     as '@LIBPATH@', 'veil_init_bitmap_array'
+     language C stable strict;
+
+comment on function veil_init_bitmap_array(text, text, text) is
+'Creates or resets (clears) BMARRAY, to have ARRAY_RANGE bitmaps of
+BITMAP_RANGE bits.
+
+Returns TRUE or raises an error';
+
+
+create or replace
+function veil_clear_bitmap_array(bmarray text) returns bool
+     as '@LIBPATH@', 'veil_clear_bitmap_array'
+     language C stable strict;
+
+comment on function veil_clear_bitmap_array(text) is
+'Clear all bits in all bitmaps of bitmap array BMARRAY.
+
+Return TRUE or raise an error';
+
+
+create or replace
+function veil_bitmap_from_array(bmref text, bmarray text, 
+               index int4) returns text
+     as '@LIBPATH@', 
+   'veil_bitmap_from_array'
+     language C stable strict;
+
+comment on function veil_bitmap_from_array(text, text, int4) is
+'Set BitmapRef BMREF to the bitmap from BMARRAY indexed by INDEX.
+
+Return the name of the BitmapRef.
+
+This is used to isolate a single bitmap from a bitmap array, recording
+it in a BitmapRef.  That bitmap can then be manipulated by ordinary veil
+bitmap functions.  Note that BitMapRefs can only be referenced within
+the transaction they are defined.';
+
+
+create or replace
+function veil_bitmap_array_testbit(
+    bmarray text, arr_idx int4, bitno int4) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_array_testbit'
+     language C stable strict;
+
+comment on function veil_bitmap_array_testbit(text, int4, int4) is
+'Test a bit in BMARRAY, from the bitmap indexed by ARR_IDX, checking the
+bit identified by BITNO.
+
+Return TRUE if the bit is set, else FALSE';
+
+
+create or replace
+function veil_bitmap_array_setbit(
+    bmarray text, arr_idx int4, bitno int4) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_array_setbit'
+     language C stable strict;
+
+comment on function veil_bitmap_array_setbit(text, int4, int4) is
+'Set a bit in BMARRAY, from the bitmap indexed by ARR_IDX, setting the
+bit identified by BITNO.
+
+Return TRUE';
+
+
+create or replace
+function veil_bitmap_array_clearbit(
+    bmarray text, arr_idx int4, bitno int4) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_array_clearbit'
+     language C stable strict;
+
+comment on function veil_bitmap_array_clearbit(text, int4, int4) is
+'Clear a bit in BMARRAY, from the bitmap indexed by ARR_IDX, clearing the
+bit identified by BITNO.
+
+Return TRUE';
+
+
+create or replace
+function veil_union_from_bitmap_array(
+    bitmap text, bmarray text, arr_idx int4) returns bool
+     as '@LIBPATH@', 
+   'veil_union_from_bitmap_array'
+     language C stable strict;
+
+comment on function veil_union_from_bitmap_array(text, text, int4) is
+'Union BITMAP with BMARRAY[ARR_IDX], with the result going into bitmap.
+
+Return TRUE';
+
+
+create or replace
+function veil_intersect_from_bitmap_array(
+    bitmap text, bmarray text, arr_idx int4) returns bool
+     as '@LIBPATH@', 
+   'veil_intersect_from_bitmap_array'
+     language C stable strict;
+
+comment on function veil_intersect_from_bitmap_array(text, text, int4) is
+'Intersect BITMAP with BMARRAY[ARR_IDX], with the result going into bitmap.
+
+Return TRUE';
+
+
+create or replace
+function veil_bitmap_array_bits(bmarray text, arr_idx int4) returns setof int4
+     as '@LIBPATH@', 
+   'veil_bitmap_array_bits'
+     language C stable strict;
+
+comment on function veil_bitmap_array_bits(text, int4) is
+'Return all bits in the bitmap given by BMARRAY[ARR_IDX].
+
+This is primarily intended for interactive use: for debugging, etc.';
+
+
+create or replace
+function veil_bitmap_array_arange(bmarray text) returns veil_range_t
+     as '@LIBPATH@', 
+   'veil_bitmap_array_arange'
+     language C stable strict;
+
+comment on function veil_bitmap_array_arange(text) is
+'Return the array bounds for BMARRAY.';
+
+
+create or replace
+function veil_bitmap_array_brange(bmarray text) returns veil_range_t
+     as '@LIBPATH@', 
+   'veil_bitmap_array_brange'
+     language C stable strict;
+
+comment on function veil_bitmap_array_brange(text) is
+'Return the range of the bitmaps in BMARRAY.';
+
+
+
+create or replace
+function veil_init_bitmap_hash(bmhash text, range text) returns bool
+     as '@LIBPATH@', 'veil_init_bitmap_hash'
+     language C stable strict;
+
+comment on function veil_init_bitmap_hash(text, text) is
+'Initialise a bitmap hash variable called BMHASH to contain bitmaps of
+size RANGE.
+
+Return TRUE.';
+
+
+create or replace
+function veil_clear_bitmap_hash(bmhash text) returns bool
+     as '@LIBPATH@', 'veil_clear_bitmap_hash'
+     language C stable strict;
+
+comment on function veil_clear_bitmap_hash(text) is
+'Clear all bits in an existing bitmap hash named BMHASH.
+
+Return TRUE.';
+
+
+create or replace
+function veil_bitmap_hash_key_exists(bmhash text, key text) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_key_exists'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_key_exists(text, text) is
+'Determine whether in BMHASH the given KEY already exists.
+
+Return TRUE if the key exists, else FALSE.';
+
+
+create or replace
+function veil_bitmap_from_hash(text, text, text) returns text
+     as '@LIBPATH@', 
+   'veil_bitmap_from_hash'
+     language C stable strict;
+
+comment on function veil_bitmap_from_hash(text, text, text) is
+'Set BitmapRef BMREF to the bitmap from BMHASH identfied by KEY.
+
+Return the name of BMREF.';
+
+
+create or replace
+function veil_bitmap_hash_testbit(text, text, int4) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_testbit'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_testbit(text, text, int4) is
+'Test the bit, in the bitmap from BMHASH identified by KEY, given by
+BITNO.
+
+Return TRUE if the bit is set, else FALSE.';
+
+
+create or replace
+function veil_bitmap_hash_setbit(text, text, int4) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_setbit'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_setbit(text, text, int4) is
+'Set the bit, in the bitmap from BMHASH identified by KEY, given by
+BITNO to TRUE.
+
+Return TRUE.';
+
+
+create or replace
+function veil_bitmap_hash_clearbit(text, text, int4) returns bool
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_clearbit'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_clearbit(text, text, int4) is
+'Set the bit, in the bitmap from BMHASH identified by KEY, given by
+BITNO to FALSE.
+
+Return TRUE.';
+
+
+create or replace
+function veil_union_into_bitmap_hash(text, text, text) returns bool
+     as '@LIBPATH@', 
+   'veil_union_into_bitmap_hash'
+     language C stable strict;
+
+comment on function veil_union_into_bitmap_hash(text, text, text) is
+'Into the bitmap from BMHASH, identified by KEY, and union the bits from
+BITMAP (which may be a bitmap or bitmap_ref).
+
+Return TRUE.';
+
+
+create or replace
+function veil_union_from_bitmap_hash(text, text, text) returns bool
+     as '@LIBPATH@', 
+   'veil_union_from_bitmap_hash'
+     language C stable strict;
+comment on function veil_union_from_bitmap_hash(text, text, text) is
+'Retrieve the bitmap from BMHASH, identified by KEY, and union it into
+BITMAP (which may be a bitmap or bitmap_ref).
+
+Return TRUE.';
+
+
+create or replace
+function veil_intersect_from_bitmap_hash(text, text, text) returns bool
+     as '@LIBPATH@', 
+   'veil_intersect_from_bitmap_hash'
+     language C stable strict;
+comment on function veil_intersect_from_bitmap_hash(text, text, text) is
+'Into BITMAP, intersect the bits from the bitmap in BMHASH identified by
+KEY.
+
+Return TRUE.';
+
+
+create or replace
+function veil_bitmap_hash_bits(text, text) returns setof int4
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_bits'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_bits(text, text) is
+'Return the set of bits in the bitset from BMHASH identfied by KEY.';
+
+
+create or replace
+function veil_bitmap_hash_range(text) returns veil_range_t
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_range'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_range(text) is
+'Return the range of all bitmaps in BMHASH.';
+
+
+create or replace
+function veil_bitmap_hash_entries(text) returns setof text
+     as '@LIBPATH@', 
+   'veil_bitmap_hash_entries'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_entries(text) is
+'Return the keys of all bitmaps in BMHASH.';
+
+
+create or replace
+function veil_int4_set(text, int4) returns int4
+     as '@LIBPATH@', 
+   'veil_int4_set'
+     language C stable;
+
+comment on function veil_int4_set(text, int4) is
+'Set the int4 variable NAME to VALUE.
+
+Return the new value';
+
+
+create or replace
+function veil_int4_get(text) returns int4
+     as '@LIBPATH@', 
+   'veil_int4_get'
+     language C stable strict;
+
+comment on function veil_int4_get(text) is
+'Return the value of int4 variable NAME.';
+
+
+create or replace
+function veil_init_int4array(text, text) returns bool
+     as '@LIBPATH@', 'veil_init_int4array'
+     language C stable strict;
+
+comment on function veil_init_int4array(text, text) is
+'Initialise the int4 array ARRAYNAME, with an index range of RANGE.
+
+Each entry in the array is set to zero.
+
+Return TRUE.';
+
+
+create or replace
+function veil_clear_int4array(text) returns bool
+     as '@LIBPATH@', 
+   'veil_clear_int4array'
+     language C stable strict;
+comment on function veil_clear_int4array(text) is
+'Reset each entry in the int4 array ARRAYNAME to zero.
+
+Return TRUE.';
+
+
+
+create or replace
+function veil_int4array_set(text, int4, int4) returns int4
+     as '@LIBPATH@', 
+   'veil_int4array_set'
+     language C stable;
+
+comment on function veil_int4array_set(text, int4, int4) is
+'Set the ARRAY element IDX to VALUE.
+
+Return the new value.';
+
+
+create or replace
+function veil_int4array_get(text, int4) returns int4
+     as '@LIBPATH@', 
+   'veil_int4array_get'
+     language C stable strict;
+
+comment on function veil_int4array_get(text, int4) is
+'Return the value of ARRAY element IDX.';
+
+
+create or replace
+function veil_init(bool) returns bool 
+     as '@LIBPATH@', 
+   'veil_init'
+     language C stable strict;
+
+comment on function veil_init(bool) is
+'This is the default version of veil_init, which does nothing except
+raise an error.';
+
+
+create or replace
+function veil_perform_reset() returns bool
+     as '@LIBPATH@', 'veil_perform_reset'
+     language C stable;
+
+comment on function veil_perform_reset() is
+'Allow userspace to force call of veil_init.
+
+This function may fail due to outstanding transactions possibly holding
+shared memory that we wish to re-use.  In this case a warning will be
+issued.  Once any long running transactions have completed, a retry
+should succeed.
+
+Return TRUE if successful, FALSE otherwise.';
+
+
+create or replace
+function veil_force_reset() returns bool
+     as '@LIBPATH@', 'veil_force_reset'
+     language C stable;
+
+comment on function veil_force_reset() is
+'Allow userspace to force an unconditional reset of veil shared memory.
+
+This always causes a PANIC, causing the database to fully reset.';
+
+
+create or replace
+function veil_version() returns text
+     as '@LIBPATH@', 'veil_version'
+     language C stable;
+
+comment on function veil_version() is
+'Return a text string identifying the current veil version.';
+
+
+
+create or replace
+function veil_serialise(varname text) returns text
+     as '@LIBPATH@', 
+   'veil_serialise'
+     language C stable strict;
+
+comment on function veil_serialise(varname text) is
+'Return a serialised copy of a variable VARNAME in text form.
+
+This is intended to be used with pgmemcache so that session variables
+can be efficiently cached.  Serialised values can be concatenated
+together as a single string and then deserialised in a single operation.
+
+The value can be restored by de-serialising it.';
+
+
+
+create or replace
+function veil_serialize(text) returns text
+     as '@LIBPATH@', 
+   'veil_serialise'
+     language C stable strict;
+
+comment on function veil_serialize(varname text) is
+'Return a serialised copy of a variable VARNAME in text form.
+
+This is intended to be used with pgmemcache so that session variables
+can be efficiently cached.  Serialized values can be concatenated
+together as a single string and then deserialized in a single operation.
+
+The value can be restored by de-serializing it.';
+
+
+
+create or replace
+function veil_deserialise(text) returns int4
+     as '@LIBPATH@', 
+   'veil_deserialise'
+     language C stable strict;
+
+comment on function veil_deserialise(text) is
+'Reset the contents of a set of serialised variable from STREAM.
+
+Return the number of items de-serialised.';
+
+
+-- Ditto for victims of webster.
+create or replace
+function veil_deserialize(text) returns int4
+     as '@LIBPATH@', 
+   'veil_deserialise'
+     language C stable strict;
+
+comment on function veil_deserialize(text) is
+'Reset the contents of a set of serialized variable from STREAM.
+
+Return the number of items de-serialized.';
+
+
+revoke execute on function veil_share(text) from public;
+revoke execute on function veil_variables() from public;
+revoke execute on function veil_init_range(text, int4,int4) from public;
+revoke execute on function veil_range(text) from public;
+
+revoke execute on function veil_init_bitmap(text, text) from public;
+revoke execute on function veil_clear_bitmap(text) from public;
+revoke execute on function veil_bitmap_setbit(text, int4) from public;
+revoke execute on function veil_bitmap_testbit(text, int4) from public;
+revoke execute on function veil_bitmap_bits(text) from public;
+revoke execute on function veil_bitmap_range(text) from public;
+
+revoke execute on function veil_init_bitmap_array(text, text, text)
+  from public;
+revoke execute on function veil_clear_bitmap_array(text) from public;
+revoke execute on function veil_bitmap_from_array(text, text, int4)
+  from public;
+revoke execute on function veil_bitmap_array_setbit(text, int4, int4)
+  from public;
+revoke execute on function veil_bitmap_array_testbit(text, int4, int4)
+  from public;
+revoke execute on function veil_union_from_bitmap_array(text, text, int4)
+  from public;
+revoke execute on function veil_intersect_from_bitmap_array(text, text, int4)
+  from public;
+revoke execute on function veil_bitmap_array_bits(text, int4) from public;
+revoke execute on function veil_bitmap_array_arange(text) from public;
+revoke execute on function veil_bitmap_array_brange(text) from public;
+
+
+revoke execute on function veil_init_bitmap_hash(text, text) from public;
+revoke execute on function veil_clear_bitmap_hash(text) from public;
+revoke execute on function veil_bitmap_hash_key_exists(text, text)
+  from public;
+revoke execute on function veil_bitmap_from_hash(text, text, text)
+  from public;
+revoke execute on function veil_bitmap_hash_setbit(text, text, int4)
+  from public;
+revoke execute on function veil_bitmap_hash_testbit(text, text, int4)
+  from public;
+revoke execute on function veil_union_into_bitmap_hash(text, text, text)
+  from public;
+revoke execute on function veil_union_from_bitmap_hash(text, text, text)
+  from public;
+revoke execute on function veil_intersect_from_bitmap_hash(text, text, text)
+  from public;
+revoke execute on function veil_bitmap_hash_bits(text, text) from public;
+revoke execute on function veil_bitmap_hash_range(text) from public;
+revoke execute on function veil_bitmap_hash_entries(text) from public;
+
+revoke execute on function veil_init_int4array(text, text) from public;
+revoke execute on function veil_clear_int4array(text) from public;
+revoke execute on function veil_int4array_set(text, int4, int4) from public;
+revoke execute on function veil_int4array_get(text, int4) from public;
+
+revoke execute on function veil_init(bool) from public;
+
+revoke execute on function veil_serialise(text) from public;
+revoke execute on function veil_serialize(text) from public;
+revoke execute on function veil_deserialise(text) from public;
+revoke execute on function veil_deserialize(text) from public;
+
+
diff --git a/src/veil_mainpage.c b/src/veil_mainpage.c
new file mode 100644 (file)
index 0000000..0f2f715
--- /dev/null
@@ -0,0 +1,2229 @@
+/* ----------
+ * veil_mainpage.c
+ *
+ *      Doxygen documentation root for Veil
+ *
+ *      Copyright (c) 2005 - 2011 Marc Munro
+ *      Author:  Marc Munro
+ * License: BSD
+ *
+ */
+
+
+/*! \mainpage Veil
+\version 1.0.0 (Stable))
+\section license License
+BSD
+\section intro_sec Introduction
+
+Veil is a data security add-on for Postgres.  It provides an API
+allowing you to control access to data at the row, or even column,
+level.  Different users will be able to run the same query and see
+different results.  Other database vendors describe this as a Virtual
+Private Database.
+
+\section Why Why do I need this?
+If you have a database-backed application that stores sensitive data,
+you will be taking at least some steps to protect that data.  Veil
+provides a way of protecting your data with a security mechanism
+within the database itself.  No matter how you access the database,
+whether you are a legitimate user or not, you cannot by-pass Veil
+without superuser privileges. 
+
+\subsection Advantages Veil Advantages
+By placing security mechanisms within the database itself we get a
+number of advantages:
+- Ubiquity.  Security is always present, no matter what application or
+tool is used to connect to the database.  If your application is
+compromised, your data is still protected by Veil.  If an intruder gets
+past your outer defences and gains access to psql, your data is still
+protected.
+- Single Security Policy and Implementation.  If you have N applications
+to secure, you have to implement your security policy N times.  With
+Veil, all applications may be protected by a single implementation.
+- Strength in Depth.  For the truly security conscious, Veil provides
+yet another level of security.  If you want strength in depth, with
+layers and layers of security like an onion, Veil gives you that extra
+layer.
+- Performance.  Veil is designed to be both flexible and efficient.
+With a good implementation it is possible to build access controls with
+a very low overhead, typically much lower than building the equivalent
+security in each application.
+- Cooperation.  The Veil security model is designed to cooperate with your
+applications.  Although Veil is primarily concerned with data access
+controls, it can also be used to provide function-level privileges.  If
+your application has a sensitive function X, it can query the database,
+through Veil functions, to ask the question, "Does the current user have
+execute_X privilege?".  Also, that privilege can be managed in exactly
+the same way as any other privilege.
+- Flexibility.  Veil is a set of tools rather than a product.  How you
+use it is up to you.
+
+\subsection Limitations Veil Limitations
+Veil can restrict the data returned by queries but cannot prevent the
+query engine itself from seeing restricted data.  In particular,
+functions executed during evaluation of the where clause of a query may
+be able to see data that Veil is supposed to restrict access to.
+
+As an example let's assume that we have a secured view, users, that
+allows a user to see only their own record.  When Alice queries the
+view, she will see this:
+
+\code
+select * from users;
+
+  userid  |   username
+----------+-----------
+   12345  |   Alice
+\endcode
+
+Alice should not be able to see any details for Bob or even, strictly
+speaking, tell whether there is an entry for Bob.  This query though:
+
+\verbatim
+select * from users 
+where 0 = 9 / (case username when 'Bob' then 0 else 1 end);
+\endverbatim
+
+will raise an exception if the where clause encounters the username
+'Bob'.  So Alice can potentially craft queries that will enable her to
+discover whether specific names appear in the database.
+
+This is not something that Veil is intended to, or is able to, prevent.
+Changes to the underlying query engine to attempt to plug such holes
+have been proposed, but they all have their limitations and are likely
+to lead to degraded performance.
+
+A more serious problem occurs if a user is able to create user defined
+functions as these can easily provide covert channels for leaking data.
+Consider this query:
+
+\verbatim
+select * from users where leak_this(username);
+\endverbatim
+
+Although the query will only return what the secured view allows it to,
+the where clause, if the function is deemed inexpensive enough, will see
+every row in the table and so will be able to leak supposedly protected
+data.  This type of exploit can be protected against easily enough by
+preventing users from defining their own functions, however there are
+postgres' builtins that can be potentially be exploited in the same way.
+
+\subsection GoodNews The Good News
+
+The news is not all bad.  Although Veil can be circumvented, as shown
+above, a database protected by Veil is a lot more secure than one which
+is not.  Veil can provide extra security over and above that provided by
+your application, and in combination with a well designed application
+can provide security that is more than adequate for most commercial
+purposes.
+
+\section the-rest Veil Documentation
+- \subpage overview-page
+- \subpage API-page
+- \subpage Building
+- \subpage Demo 
+- \subpage Management
+- \subpage Esoteria
+- \subpage install
+- \subpage History
+- \subpage Feedback
+- \subpage Performance
+- \subpage Credits
+
+Next: \ref overview-page
+
+*/
+/*! \page overview-page Overview: a quick introduction to Veil
+
+\section Overview-section Introduction
+The section introduces a number of key concepts, and shows the basic
+components of a Veil-protected system:
+- \ref over-views
+- \ref over-connections
+- \ref over-privs
+- \ref over-contexts
+- \ref over-funcs2
+- \ref over-roles
+
+\subsection over-views Secured Views and Access Functions
+Access controls are implemented using secured views and instead-of triggers.
+Users connect to an account that has access only to the secured views.
+For a table defined thus:
+\verbatim
+create table persons (
+    person_id      integer not null,
+    person_name        varchar(80) not null
+);
+\endverbatim
+The secured view would be defined something like this:
+\verbatim
+create view persons(
+       person_id,
+       person_name) as
+select person_id,
+       person_name
+from   persons
+where  i_have_personal_priv(10013, person_id);
+\endverbatim
+
+A query performed on the view will return rows only for those persons
+where the current user has privilege 10013
+(<code>SELECT_PERSONS</code>).  We call the function
+<code>i_have_personal_priv()</code>, an access function.  Such
+functions are user-defined, and are used to determine whether the
+connected user has a specific privilege in any of a number of security
+contexts (see \ref over-contexts).  The example above is
+taken from the Veil demo application (\ref demo-sec) and 
+checks for privilege in the global and personal contexts.
+
+\subsection over-connections The Connected User and Connection Functions
+To determine a user's privileges, we have to know who that user is.
+At the start of each database session the user must be identified, and
+their privileges must be determined.  This is done by calling a
+connection function, eg:
+\verbatim
+select connect_person('Wilma', 'AuthenticationTokenForWilma');
+\endverbatim
+The connection function performs authentication, and stores the user's
+access privileges in Veil state variables.  These variables are then
+interrogated by the access functions used in the secured views.
+
+Prior to connection, or in the event of the connection failing, the
+session will have no privileges and will probably be unable to see any
+data.  Like access functions, connection functions are user-defined and 
+may be written in any language supported by PostgreSQL.
+
+\subsection over-privs Privileges
+Veil-based systems define access rights in terms of privileges.  A
+privilege is a named thing with a numerical value (actually, the name
+is kind of optional).
+
+An example will probably help.  Here is a definition of a privileges
+table and a subset of its data:
+\verbatim
+create table privileges (
+    privilege_id   integer not null,
+    privilege_name varchar(80) not null
+);
+
+copy privileges (privilege_id, privilege_name) from stdin;
+10001  select_privileges
+10002  insert_privileges
+10003  update_privileges
+10004  delete_privileges
+. . .
+10013  select_persons
+10014  insert_persons
+10015  update_persons
+10016  delete_persons
+10017  select_projects
+10018  insert_projects
+10019  update_projects
+10020  delete_projects
+. . .
+10100  can_connect
+\.
+
+\endverbatim
+Each privilege describes something that a user can do.  It is up to the
+access and connection functions to make use of these privileges; the
+name of the privilege is only a clue to its intended usage.  In the
+example we might expect that a user that has not been given the
+<code>can_connect</code> privilege would not be able to authenticate
+using a connection function but this is entirely dependent on the
+implementation.
+
+\subsection over-contexts Security Contexts 
+
+Users may be assigned privileges in a number of different ways.  They
+may be assigned directly, indirectly through various relationships, or
+may be inferred by some means.  To aid in the discussion and design of a
+Veil-based security model we introduce the concept of security
+contexts, and we say that a user has a given set of privileges in a
+given context.  There are three types of security context:
+
+ - Global Context.  This refers to privileges that a user has been given
+   globally.  If a user has <code>select_persons</code> privilege in the
+   global context, they will be able to select every record in the
+   persons table.  Privileges in global context are exactly like
+   database-level privileges: there is no row-level element to them.
+
+ - Personal Context.  This context provides privileges on data that you
+   may be said to own.  If you have <code>select_persons</code>
+   privilege in only the personal context, you will only be able to
+   select your own persons record.  Assignment of privileges in the
+   personal context is often defined implicitly or globally, for all
+   users, rather than granted explicitly to each user.  It is likely
+   that everyone should have the same level of access to their own data
+   so it makes little sense to have to explicitly assign the privileges
+   for each individual user.
+
+ - Relational Contexts.  These are the key to most row-level access
+   controls.  Privileges assigned in a relational context are assigned
+   through relationships between the connected user and the data to be
+   accessed.  Examples of relational contexts include: assignments to
+   projects, in which a user will gain access to project data only if
+   they have been assigned to the project; and the management hierarchy
+   within a business, in which a manager may have specific access to
+   data about a staff member.  Note that determining a user's access
+   rights in a relational context may require extra queries to be
+   performed for each function call.  Your design should aim to minimise
+   this.  Some applications may require a number of distinct relational 
+   contexts.
+
+\subsection over-funcs2 Access Functions and Security Contexts
+Each access function will operate on privileges for a specific set of
+contexts.  For some tables, access will only be through global context.
+For others, it may be through global and personal as well as a number of
+different relational contexts.  Here, from the demo application, are a
+number of view definitions, each using a different access function that
+checks different contexts.
+\verbatim
+create view privileges(
+       privilege_id,
+       privilege_name) as
+select privilege_id,
+       privilege_name
+from   privileges
+where  i_have_global_priv(10001);
+
+. . .
+
+create view persons(
+       person_id,
+       person_name) as
+select person_id,
+       person_name
+from   persons
+where  i_have_personal_priv(10013, person_id);
+
+. . .
+
+create view projects(
+       project_id,
+       project_name) as
+select project_id,
+       project_name
+from   projects
+where  i_have_project_priv(10017, project_id);
+
+. . .
+
+create view assignments (
+       project_id,
+       person_id,
+       role_id) as
+select project_id,
+       person_id,
+       role_id
+from   assignments
+where  i_have_proj_or_pers_priv(10025, project_id, person_id);
+\endverbatim
+
+In the <code>privileges</code> view, we only check for privilege in the
+global context.  This is a look-up view, and should be visible to all
+authenticated users.
+
+The <code>persons</code> view checks for privilege in both the global
+and personal contexts.  It takes an extra parameter identifying the
+person who owns the record.  If that person is the same as the connected
+user, then privileges in the personal context may be checked.  If not,
+only the global context applies.
+
+The <code>projects</code> view checks global and project contexts.  The
+project context is a relational context.  In the demo application, a
+user gains privileges in the project context through assignments.  An
+assignment is a relationship between a person and a project.  Each 
+assignment record has a role.  This role describes the set of privileges
+the assignee (person) has within the project context.
+
+The <code>assignments</code> view checks all three contexts (global,
+personal and project).  An assignment contains data about a person and a
+project so privileges may be acquired in either of the relational
+contexts, or globally.
+
+\subsection over-roles Grouping Privileges by Roles
+Privileges operate at a very low-level.  In a database of 100 tables,
+there are likely to be 500 to 1,000 privileges in use.  Managing
+users access at the privilege level is, at best, tedious.  Instead, we
+tend to group privileges into roles, and assign only roles to individual
+users.  Roles act as function-level collections of privileges.  For
+example, the role <code>project-readonly</code> might contain all of the
+<code>select_xxx</code> privileges required to read all project data.
+
+A further refinement allows roles to be collections of sub-roles.
+Defining suitable roles for a system is left as an exercise for the
+reader.
+
+Next: \ref API-page
+
+*/
+/*! \page API-page The Veil API
+\section API-sec The Veil API
+This section describes the Veil API.  It consists of the following
+sections
+
+- \ref API-intro
+- \subpage API-variables
+- \subpage API-simple
+- \subpage API-bitmaps
+- \subpage API-bitmap-arrays
+- \subpage API-bitmap-hashes
+- \subpage API-int-arrays
+- \subpage API-serialisation
+- \subpage API-control
+
+\section API-intro Veil API Overview
+Veil is an API that simply provides a set of state variable types, and 
+operations on those variable types, which are optimised for privilege
+examination and manipulation.
+
+The fundamental data type is the bitmap.  Bitmaps are used to
+efficiently record and test sets of privileges.  Bitmaps may be combined
+into bitmap arrays, which are contiguous groups of bitmaps indexed by
+integer, and bitmap hashes which are non-contiguous and may be indexed
+by text strings.
+
+In addition to the bitmap-based types, there are a small number of
+support types that just help things along.  If you think you have a case
+for defining a new type, please 
+\ref Feedback "contact" 
+the author.
+
+Next: \ref API-variables
+*/
+/*! \page API-variables Variables
+Veil variables exist to record session and system state.  They retain
+their values across transactions.  Variables may be defined as either
+session variables or shared variables.
+
+All variables are referenced by name; the name of the variable is
+passed as a text string to Veil functions.
+
+Session variables are private to the connected session.  They are
+created when first referenced and, once defined, their type is set for
+the life of the session.
+
+Shared variables are global across all sessions.  Once a shared variable
+is defined, all sessions will have access to it.  Shared variables are
+defined in two steps.  First, the variable is defined as shared, and
+then it is initialised and accessed in the same way as for session
+variables.  Note that shared variables should only be modified within
+the function veil_init().
+
+Note that bitmap refs and bitmap hashes may not be stored in shared
+variables.
+
+The following types of variable are supported by Veil, and are described
+in subsequent sections:
+- integers 
+- ranges
+- bitmaps
+- bitmap refs
+- bitmap arrays
+- bitmap hashes
+- integer arrays
+
+The following functions comprise the Veil variables API:
+
+- <code>\ref API-variables-share</code>
+- <code>\ref API-variables-var</code>
+
+Note again that session variables are created on usage.  Their is no
+specific function for creating a variable in the variables API.  For an
+example of a function to create a variable see \ref API-bitmap-init.
+
+\section API-variables-share veil_share(name text)
+\code
+function veil_share(name text) returns bool
+\endcode
+
+This is used to define a specific variable as being shared.  A shared
+variable is accessible to all sessions and exists to reduce the need for
+multiple copies of identical data.  For instance in the Veil demo,
+role_privileges are recorded in a shared variable as they will be
+identical for all sessions, and to create a copy for each session  would
+be an unnecessary overhead.  This function should only be called from
+veil_init().
+
+\section API-variables-var veil_variables()
+\code
+function veil_variables() returns setof veil_variable_t
+\endcode
+
+This function returns a description for each variable known to the
+session.  It provides the name, the type, and whether the variable is
+shared.  It is primarily intended for interactive use when developing
+and debugging Veil-based systems.
+
+Next: \ref API-simple
+*/
+/*! \page API-simple Basic Types: Integers and Ranges
+
+Veil's basic types are those that do not contain repeating groups
+(arrays, hashes, etc).  
+
+Ranges consist of a pair of values and are generally used to initialise
+the bounds of array and bitmap types.  Ranges may not contain nulls.
+
+The int4 type is used to record a simple nullable integer.  This is
+typically used to record the id of the connected user in a session.
+
+The following functions comprise the Veil basic types API:
+
+- <code>\ref API-basic-init-range</code>
+- <code>\ref API-basic-range</code>
+- <code>\ref API-basic-int4-set</code>
+- <code>\ref API-basic-int4-get</code>
+
+\section API-basic-init-range veil_init_range(name text, min int4, max int4)
+\code
+function veil_init_range(name text, min int4, max int4) returns int4
+\endcode
+
+This defines a range, and returns the extent of that range.
+
+\section API-basic-range veil_range(name text)
+\code
+function veil_range(name text) returns veil_range_t
+\endcode
+
+This returns the contents of a range.  It is intended primarily for
+interactive use.
+
+\section API-basic-int4-set veil_int4_set(text, int4)
+\code
+function veil_int4_set(text, int4) returns int4
+\endcode
+
+Sets an int4 variable to a value, returning that same value.
+
+\section API-basic-int4-get veil_int4_get(text)
+\code
+function veil_int4_get(text) returns int4
+\endcode
+
+Returns the value of an int4 variable.
+
+
+Next: \ref API-bitmaps
+*/
+/*! \page API-bitmaps Bitmaps and Bitmap Refs
+Bitmaps are used to implement bounded sets.  Each bit in the bitmap may
+be on or off representing presence or absence of a value in the set.
+Typically bitmaps are used to record sets of privileges.
+
+A bitmap ref is a variable that may temporarily reference another
+bitmap.  These are useful for manipulating specific bitmaps within
+bitmap arrays or bitmap hashes.  All bitmap operations except for \ref
+API-bitmap-init may take the name of a bitmap ref instead of a bitmap.
+
+Bitmap refs may not be shared, and the reference is only accessible
+within the transaction that created it.  These restrictions exist to
+eliminate the possibility of references to deleted objects or to objects
+from other sessions.
+
+The following functions comprise the Veil bitmaps API:
+
+- <code>\ref API-bitmap-init</code>
+- <code>\ref API-bitmap-clear</code>
+- <code>\ref API-bitmap-setbit</code>
+- <code>\ref API-bitmap-clearbit</code>
+- <code>\ref API-bitmap-testbit</code>
+- <code>\ref API-bitmap-union</code>
+- <code>\ref API-bitmap-intersect</code>
+- <code>\ref API-bitmap-bits</code>
+- <code>\ref API-bitmap-range</code>
+
+\section API-bitmap-init veil_init_bitmap(bitmap_name text, range_name text)
+\code
+function veil_init_bitmap(bitmap_name text, range_name text) returns bool
+\endcode
+This is used to create or resize a bitmap.  The first parameter provides
+the name of the bitmap, the second is the name of a range variable that
+will govern the size of the bitmap.
+
+\section API-bitmap-clear veil_clear_bitmap(name text)
+\code
+function veil_clear_bitmap(name text) returns bool
+\endcode
+This is used to clear (set to zero) all bits in the bitmap.
+
+\section API-bitmap-setbit veil_bitmap_setbit(name text, bit_number int4)
+\code
+function veil_bitmap_setbit(text, int4) returns bool
+\endcode
+This is used to set a specified bit in a bitmap.
+
+\section API-bitmap-clearbit veil_bitmap_clearbit(name text, bit_number int4)
+\code
+function veil_bitmap_clearbit(name text, bit_number int4) returns bool
+\endcode
+This is used to clear (set to zero) a specified bit in a bitmap.
+
+\section API-bitmap-testbit veil_bitmap_testbit(name text, bit_number int4)
+\code
+function veil_bitmap_testbit(name text, bit_number int4) returns bool
+\endcode
+This is used to test a specified bit in a bitmap.  It returns true if
+the bit is set, false otherwise.
+
+\section API-bitmap-union veil_bitmap_union(result_name text, name2 text)
+\code
+function veil_bitmap_union(result_name text, name2 text) returns bool
+\endcode
+Form the union of two bitmaps with the result going into the first.
+
+\section API-bitmap-intersect veil_bitmap_intersect(result_name text, name2 text)
+\code
+function veil_bitmap_intersect(result_name text, name2 text) returns bool
+\endcode
+Form the intersection of two bitmaps with the result going into the first.
+
+\section API-bitmap-bits veil_bitmap_bits(name text)
+\code
+function veil_bitmap_bits(name text) returns setof int4
+\endcode
+This is used to list all bits set within a bitmap.  It is primarily for
+interactive use during development and debugging of Veil-based systems.
+
+\section API-bitmap-range veil_bitmap_range(name text)
+\code
+function veil_bitmap_range(name text) returns veil_range_t
+\endcode
+This returns the range of a bitmap.  It is primarily intended for
+interactive use.
+
+Next: \ref API-bitmap-arrays
+*/
+/*! \page API-bitmap-arrays Bitmap Arrays
+A bitmap array is an array of identically-ranged bitmaps, indexed
+by an integer value.  They are initialised using two ranges, one for the
+range of each bitmap, and one providing the range of indices for the
+array.
+
+Typically bitmap arrays are used for collections of privileges, where
+each element of the collection is indexed by something like a role_id.
+
+The following functions comprise the Veil bitmap arrays API:
+
+- <code>\ref API-bmarray-init</code>
+- <code>\ref API-bmarray-clear</code>
+- <code>\ref API-bmarray-bmap</code>
+- <code>\ref API-bmarray-testbit</code>
+- <code>\ref API-bmarray-setbit</code>
+- <code>\ref API-bmarray-clearbit</code>
+- <code>\ref API-bmarray-union</code>
+- <code>\ref API-bmarray-intersect</code>
+- <code>\ref API-bmarray-bits</code>
+- <code>\ref API-bmarray-arange</code>
+- <code>\ref API-bmarray-brange</code>
+
+\section API-bmarray-init veil_init_bitmap_array(bmarray text, array_range text, bitmap_range text)
+\code
+function veil_init_bitmap_array(bmarray text, array_range text, bitmap_range text) returns bool
+\endcode
+Creates or resets (clears) a bitmap array.
+
+\section API-bmarray-clear veil_clear_bitmap_array(bmarray text)
+\code
+function veil_clear_bitmap_array(bmarray text) returns bool
+\endcode
+Clear all bits in all bitmaps of a bitmap array
+
+\section API-bmarray-bmap veil_bitmap_from_array(bmref text, bmarray text, index int4)
+\code
+function veil_bitmap_from_array(bmref text, bmarray text, index int4) returns text
+\endcode
+Generate a reference to a specific bitmap in a bitmap array
+
+\section API-bmarray-testbit veil_bitmap_array_testbit(bmarray text, arr_idx int4, bitno int4)
+\code
+function veil_bitmap_array_testbit(bmarray text, arr_idx int4, bitno int4) returns bool
+\endcode
+Test a specific bit in a bitmap array.
+
+\section API-bmarray-setbit veil_bitmap_array_setbit(bmarray text, arr_idx int4, bitno int4)
+\code
+function veil_bitmap_array_setbit(bmarray text, arr_idx int4, bitno int4) returns bool
+\endcode
+Set a specific bit in a bitmap array.
+
+\section API-bmarray-clearbit veil_bitmap_array_clearbit(bmarray text, arr_idx int4, bitno int4)
+\code
+function veil_bitmap_array_clearbit(bmarray text, arr_idx int4, bitno int4) returns bool
+\endcode
+Clear a specific bit in a bitmap array.
+
+\section API-bmarray-union veil_union_from_bitmap_array(bitmap text, bmarray text, arr_idx int4)
+\code
+function veil_union_from_bitmap_array(bitmap text, bmarray text, arr_idx int4) returns bool
+\endcode
+Union a bitmap with a specified bitmap from an array, with the result in
+the bitmap.  This is a faster shortcut for:
+
+<code>
+veil_bitmap_union(&lt;bitmap>, veil_bitmap_from_array(&lt;bitmap_array>, &lt;index>))
+</code>.
+
+\section API-bmarray-intersect veil_intersect_from_bitmap_array(bitmap text, bmarray text, arr_idx int4)
+\code
+function veil_intersect_from_bitmap_array(bitmap text, bmarray text, arr_idx int4) returns bool
+\endcode
+Intersect a bitmap with a specified bitmap from an array, with the result in
+the bitmap.  This is a faster shortcut for:
+
+<code>
+veil_bitmap_intersect(&lt;bitmap>, veil_bitmap_from_array(&lt;bitmap_array>, &lt;index>))
+</code>.
+
+\section API-bmarray-bits veil_bitmap_array_bits(bmarray text, arr_idx int4)
+\code
+function veil_bitmap_array_bits(bmarray text, arr_idx int4) returns setof int4
+\endcode
+Show all bits in the specific bitmap within an array.  This is primarily
+intended for interactive use when developing and debugging Veil-based
+systems.
+
+\section API-bmarray-arange veil_bitmap_array_arange(bmarray text)
+\code
+function veil_bitmap_array_arange(bmarray text) returns veil_range_t
+\endcode
+Show the range of array indices for the specified bitmap array.
+Primarily for interactive use.
+
+\section API-bmarray-brange veil_bitmap_array_brange(bmarray text)
+\code
+function veil_bitmap_array_brange(bmarray text) returns veil_range_t
+\endcode
+Show the range of all bitmaps in the specified bitmap array.
+Primarily for interactive use.
+
+
+Next: \ref API-bitmap-hashes
+*/
+/*! \page API-bitmap-hashes Bitmap Hashes
+A bitmap hashes is a hash table of identically-ranged bitmaps, indexed
+by a text key.
+
+Typically bitmap hashes are used for sparse collections of privileges.
+
+Note that bitmap hashes may not be stored in shared variables as hashes
+in shared memory are insufficiently dynamic.
+
+The following functions comprise the Veil bitmap hashes API:
+
+- <code>\ref API-bmhash-init</code>
+- <code>\ref API-bmhash-clear</code>
+- <code>\ref API-bmhash-key-exists</code>
+- <code>\ref API-bmhash-from</code>
+- <code>\ref API-bmhash-testbit</code>
+- <code>\ref API-bmhash-setbit</code>
+- <code>\ref API-bmhash-clearbit</code>
+- <code>\ref API-bmhash-union-into</code>
+- <code>\ref API-bmhash-union-from</code>
+- <code>\ref API-bmhash-intersect-from</code>
+- <code>\ref API-bmhash-bits</code>
+- <code>\ref API-bmhash-range</code>
+- <code>\ref API-bmhash-entries</code>
+
+\section API-bmhash-init veil_init_bitmap_hash(bmhash text, range text)
+\code
+function veil_init_bitmap_hash(bmhash text, range text) returns bool
+\endcode
+Creates, or resets, a bitmap hash.
+
+\section API-bmhash-clear veil_clear_bitmap_hash(bmhash text)
+\code
+function veil_clear_bitmap_hash(bmhash text) returns bool
+\endcode
+Clear all bits in a bitmap hash.
+
+\section API-bmhash-key-exists veil_bitmap_hash_key_exists(bmhash text, key text)
+\code
+function veil_bitmap_hash_key_exists(bmhash text, key text) returns bool
+\endcode
+Determine whether a given key exists in the hash (contains a bitmap).
+
+\section API-bmhash-from veil_bitmap_from_hash(text, text, text)
+\code
+function veil_bitmap_from_hash(text, text, text) returns text
+\endcode
+Generate a reference to a specific bitmap in a bitmap hash.
+
+\section API-bmhash-testbit veil_bitmap_hash_testbit(text, text, int4)
+\code
+function veil_bitmap_hash_testbit(text, text, int4) returns bool
+\endcode
+Test a specific bit in a bitmap hash.
+
+\section API-bmhash-setbit veil_bitmap_hash_setbit(text, text, int4)
+\code
+function veil_bitmap_hash_setbit(text, text, int4) returns bool
+\endcode
+Set a specific bit in a bitmap hash.
+
+\section API-bmhash-clearbit veil_bitmap_hash_clearbit(text, text, int4)
+\code
+function veil_bitmap_hash_clearbit(text, text, int4) returns bool
+\endcode
+Clear a specific bit in a bitmap hash.
+
+\section API-bmhash-union-into veil_union_into_bitmap_hash(text, text, text)
+\code
+function veil_union_into_bitmap_hash(text, text, text) returns bool
+\endcode
+Union a specified bitmap from a hash with a bitmap, with the result in
+the bitmap hash.  This is a faster shortcut for:
+
+<code>
+veil_bitmap_union(veil_bitmap_from_hash(&lt;bitmap_hash>, &lt;key>), &lt;bitmap>)
+</code>.
+
+\section API-bmhash-union-from veil_union_from_bitmap_hash(text, text, text)
+\code
+function veil_union_from_bitmap_hash(text, text, text) returns bool
+\endcode
+Union a bitmap with a specified bitmap from a hash, with the result in
+the bitmap.  This is a faster shortcut for:
+
+<code>
+veil_bitmap_union(&lt;bitmap>, veil_bitmap_from_hash(&lt;bitmap_array>, &lt;key>))
+</code>.
+
+\section API-bmhash-intersect-from veil_intersect_from_bitmap_hash(text, text, text)
+\code
+function veil_intersect_from_bitmap_hash(text, text, text) returns bool
+\endcode
+Intersect a bitmap with a specified bitmap from a hash, with the result in
+the bitmap.  This is a faster shortcut for:
+
+<code>
+veil_bitmap_intersect(&lt;bitmap>, veil_bitmap_from_hash(&lt;bitmap_array>, &lt;key>))
+</code>.
+
+\section API-bmhash-bits veil_bitmap_hash_bits(text, text)
+\code
+function veil_bitmap_hash_bits(text, text) returns setof int4
+\endcode
+Show all bits in the specific bitmap within a hash.  This is primarily
+intended for interactive use when developing and debugging Veil-based
+systems.
+
+\section API-bmhash-range veil_bitmap_hash_range(text)
+\code
+function veil_bitmap_hash_range(text) returns veil_range_t
+\endcode
+Show the range of all bitmaps in the hash.  Primarily intended for
+interactive use. 
+
+\section API-bmhash-entries veil_bitmap_hash_entries(text)
+\code
+function veil_bitmap_hash_entries(text) returns setof text
+\endcode
+Show every key in the hash.  Primarily intended for interactive use.
+
+Next: \ref API-int-arrays
+*/
+/*! \page API-int-arrays Integer Arrays
+Integer arrays are used to store simple mappings of keys to values.  In
+the Veil demo (\ref demo-sec) they are used to record the extra privilege
+required to access person_details and project_details of each
+detail_type: the integer array being used to map the detail_type_id to
+the privilege_id.
+
+Note that integer array elements cannot be null.
+
+The following functions comprise the Veil int arrays API:
+
+- <code>\ref API-intarray-init</code>
+- <code>\ref API-intarray-clear</code>
+- <code>\ref API-intarray-set</code>
+- <code>\ref API-intarray-get</code>
+
+\section API-intarray-init veil_init_int4array(text, text)
+\code
+function veil_init_int4array(text, text) returns bool
+\endcode
+Creates, or resets the ranges of, an int array.
+
+\section API-intarray-clear veil_clear_int4array(text)
+\code
+function veil_clear_int4array(text) returns bool
+\endcode
+Clears (zeroes) an int array.
+
+\section API-intarray-set veil_int4array_set(text, int4, int4)
+\code
+function veil_int4array_set(text, int4, int4) returns int4
+\endcode
+Set the value of an element in an int array.
+
+\section API-intarray-get veil_int4array_get(text, int4)
+\code
+function veil_int4array_get(text, int4) returns int4
+\endcode
+Get the value of an element from an int array.
+
+Next: \ref API-serialisation
+*/
+/*! \page API-serialisation Veil Serialisation Functions
+With modern web-based applications, database connections are often
+pooled, with each connection representing many different users.  In
+order to reduce the overhead of connection functions for such
+applications, Veil provides a serialisation API.  This allows session
+variables for a connected user to be saved for subsequent re-use.  This
+is particularly effective in combination with pgmemcache  
+http://pgfoundry.org/projects/pgmemcache/
+
+Only session variables may be serialised.
+
+The following functions comprise the Veil serialisatation API:
+
+- <code>\ref API-serialise</code>
+- <code>\ref API-deserialise</code>
+- <code>\ref API-serialize</code>
+- <code>\ref API-deserialize</code>
+
+\section API-serialise veil_serialise(text)
+\code
+function veil_serialise(text) returns text
+\endcode
+This creates a serialised textual representation of the named session
+variable.  The results of this function may be concatenated into a
+single string, which can be deserialised in a single call to
+veil_deserialise()
+
+\section API-deserialise veil_deserialise(text)
+\code
+function veil_deserialise(text) returns text
+\endcode
+This takes a serialised representation of one or more variables as
+created by concatenating the results of veil_serialise(), and
+de-serialises them, creating new variables as needed and resetting their
+values to those they had when they were serialised.
+
+\section API-serialize veil_serialize(text)
+\code
+function veil_serialize(text) returns text
+\endcode
+Synonym for veil_serialise()
+
+\section API-deserialize veil_deserialize(text)
+\code
+function veil_deserialize(text) returns text
+\endcode
+Synonym for veil_deserialise()
+
+Next: \ref API-control
+*/
+/*! \page API-control Veil Control Functions
+Veil generally requires no management.  The exception to this is when
+you wish to reset shared variables.  You may wish to do this because
+your underlying security definitions have changed, or because you have
+added new features.  In this case, you may use veil_perform_reset() to
+re-initialise your shared variables.  This function replaces the current
+set of shared variables with a new set in a transaction-safe manner.
+All current transactions will complete with the old set of variables in
+place.  All subsequent transactions will see the new set.
+
+The following functions comprise the Veil control functions API:
+
+- <code>\ref API-control-init</code>
+- <code>\ref API-control-reset</code>
+- <code>\ref API-control-force</code>
+- <code>\ref API-version</code>
+
+\section API-control-init veil_init(bool)
+\code
+function veil_init(bool) returns bool
+\endcode
+This function must be redefined by the application.  The default
+installed version simply raises an error telling you to redefine it.
+See \ref Implementation for a more detailed description of this function.
+
+\section API-control-reset veil_perform_reset()
+\code
+function veil_perform_reset() returns bool
+\endcode
+This is used to reset Veil's shared variables.  It causes veil_init() to
+be called.
+
+\section API-control-force veil_force_reset(bool)
+\code
+function veil_force_reset() returns bool
+\endcode
+In the event of veil_perform_reset() failing to complete and leaving
+shared variables in a state of limbo, this function may be called to
+force the reset.  After forcing the reset, this function raises a panic
+which will reset the database server.  Use this at your peril.
+
+\section API-version veil_version()
+\code
+function veil_version() returns text
+\endcode
+This function returns a string describing the installed version of
+veil.
+
+Next: \ref Building
+
+*/
+/*! \page Building Building a Veil-based secure database
+\section Build-sec Building a Veil-based secure database
+
+This section describes the steps necessary to secure a database using
+Veil.  The steps are:
+- \ref Policy
+- \ref Schemas
+- \ref Design
+- \ref Implementation
+- \ref Implementation2
+- \ref Implementation3
+- \ref Implementation4
+- \ref Testing
+
+\subsection Policy Determine your Policies
+
+You must identify which security contexts exist for your application,
+and how privileges should be assigned to users in those contexts.  You
+must also figure out how privileges, roles, and the assignment of roles
+to users are to be managed.  You must identify each object that is to be
+protected by Veil, identify the security contexts applicable for that
+object, and determine the privileges that will apply to each object in
+each possible mode of use.  Use the Veil demo application (\ref
+demo-sec) as a guide.
+
+For data access controls, typically you will want specific privileges
+for select, insert, update and delete on each table.  You may also want
+separate admin privileges that allow you to grant those rights.
+
+At the functional level, you will probably have an execute privilege for
+each callable function, and you will probably want similar privileges
+for individual applications and components of applications.  Eg, to
+allow the user to execute the role_manager component of admintool, you
+would probably create a privilege called
+<code>exec_admintool_roleman</code>.
+
+The hardest part of this is figuring out how you will securely manage
+these privileges.  A useful, minimal policy is to not allow anyone to
+assign a role that they themselves have not been assigned.
+
+\subsection Schemas Design Your Database-Level Security
+
+Veil operates within the security provided by PostgreSQL.  If you wish
+to use Veil to protect underlying tables, then those tables must not be
+directly accessible to the user.  Also, the Veil functions themselves,
+as they provide privileged operations, must not be accessible to user
+accounts. 
+
+A sensible basic division of schema responsibilities would be as follows:
+
+- An "owner" user will own the underlying objects (tables, views,
+  functions, etc) that are to be secured.  Access to these objects will
+  be granted only to "Veil".  The "owner" user will connect only when
+  the underlying objects are to be modified.  No-one but a DBA will ever
+  connect to this account, and generally, the password for this account
+  should be disabled.
+
+- A "Veil" user will own all secured views and access functions (see
+  \ref over-views).  Access to these objects will be granted to the
+  "Accessor" user.  Like the "owner" user, this user should not be
+  directly used except by DBAs performing maintenance.  It will also own
+  the Veil API, ie this is the account where Veil itself will be
+  installed.  Direct access to Veil API functions should not be granted
+  to other users.  If access to a specific function is needed, it should
+  be wrapped in a local function to which access may then be granted.
+- "Accessor" users are the primary point of contact.  These must have no
+  direct access to the underlying objects owned by owner.  They will have
+  access only to the secured views and access functions.  All
+  applications may connect to these user accounts.
+
+\subsection Design Design Your Access Functions
+
+Provide a high-level view of the workings of each access function.  You
+will need this in order to figure out what session and shared variables
+you will need.  The following is part of the design from the Veil demo
+application:
+\verbatim
+Access Functions are required for:
+- Global context only (lookup-data, eg privileges, roles, etc)
+- Personal and Global Context (personal data, persons, assignments, etc)
+- Project and Global (projects, project_details)
+- All 3 (assignments)
+
+Determining privilege in Global Context:
+
+User has priv X, if X is in role_privileges for any role R, that has
+been assigned to the user.
+
+Role privileges are essentially static so may be loaded into memory as a
+shared variable.  When the user connects, the privileges associated with
+their roles may be loaded into a session variable.
+
+Shared initialisation code:
+  role_privs ::= shared array of privilege bitmaps indexed by role.
+  Populate role_privs with:
+    select bitmap_array_setbit(role_privs, role_id, privilege_id)
+    from   role_privileges;
+
+Connection initialisation code:
+  global_privs ::= session privileges bitmap
+  Clear global_privs and then initialise with:
+    select bitmap_union(global_privs, role_privs[role_id])
+    from   person_roles
+    where  person_id = connected_user;
+
+i_have_global_priv(x):
+  return bitmap_testbit(global_privs, x);
+
+\endverbatim 
+
+This gives us the basic structure of each function, and identifies what
+must be provided by session and system initialisation to support those
+functions.  It also allows us to identify the overhead that Veil imposes.
+
+In the case above, there is a connect-time overhead of one extra query
+to load the global_privs bitmap.  This is probably a quite acceptable
+overhead as typically each user will have relatively few roles.
+
+If the overhead of any of this seems too significant there are
+essentially 4 options:
+- Simplify the design.
+- Defer the overhead until it is absolutely necessary.  This can be done
+  with connection functions where we may be able to defer the overhead
+  of loading relational context data until the time that we first need
+  it.
+- Implement a caching solution (check out pgmemcache).  Using an
+  in-memory cache will save data set-up queries from having to be
+  repeated.  This is pretty complex though and may require you to write
+  code in C.
+- Suffer the performance hit.
+
+\subsection Implementation Implement the Initialisation Function
+
+The initialisation function \ref API-control-init is a critical
+function and must be defined.  It will be called by Veil, when the first
+in-built Veil function is invoked.  It is responsible for three distinct
+tasks:
+
+- Initialisation of session variables
+- Initialisation of shared variables
+- Re-initialisation of variables during reset
+
+The boolean parameter to veil_init will be false on initial session
+startup, and true when performing a reset (\ref API-control-reset).
+
+Shared variables are created using \ref API-variables-share.  This
+returns a boolean result describing whether the variable already
+existed.  If so, and we are not performing a reset, the current session
+need not initialise it.
+
+Session variables are simply created by referring to them.  It is worth
+creating and initialising all session variables to "fix" their data
+types.  This will prevent other functions from misusing them.
+
+If the boolean parameter to veil_init is true, then we are performing a
+memory reset, and all shared variables should be re-initialised.  A
+memory reset will be performed whenever underlying, essentially static,
+data has been modified.  For example, when new privileges have been
+added, we must rebuild all privilege bitmaps to accommodate the new
+values.
+
+\subsection Implementation2 Implement the Connection Functions
+
+The connection functions have to authenticate the connecting user, and
+then initialise the user's session.  
+
+Authentication should use a secure process in which no plaintext
+passwords are ever sent across the wire.  Veil does not provide
+authentication services.  For your security needs you should probably
+check out pgcrypto.
+
+Initialising a user session is generally a matter of initialising
+bitmaps that describe the user's base privileges, and may also involve
+setting up bitmap hashes of their relational privileges.  Take a look at
+the demo (\ref demo-sec) for a working example of this.
+
+\subsection Implementation3 Implement the Access Functions
+
+Access functions provide the low-level access controls to individual
+records.  As such their performance is critical.  It is generally better
+to make the connection functions to more work, and the access functions
+less.  Bear in mind that if you perform a query that returns 10,000 rows
+from a table, your access function for that view is going to be called
+10,000 times.  It must be as fast as possible.
+
+When dealing with relational contexts, it is not always possible to keep
+all privileges for every conceivable relationship in memory.  When this
+happens, your access function will have to perform a query itself to
+load the specific data into memory.  If your application requires this,
+you should: 
+
+- Ensure that each such query is as simple and efficient as possible 
+- Cache your results in some way
+
+You may be able to trade-off between the overhead of connection
+functions and that of access functions.  For instance if you have a
+relational security context based upon a tree of relationships, you may
+be able to load all but the lowest level branches of the tree at connect
+time.  The access function then has only to load the lowest level branch
+of data at access time, rather than having to perform a full tree-walk.
+
+Caching can be very effective, particularly for nested loop joins.  If
+you are joining A with B, and they both have the same access rules, once
+the necessary privilege to access a record in A has been determined and
+cached, we will be able to use the cached privileges when checking for
+matching records in B (ie we can avoid repeating the fetch).
+
+\subsection Implementation4 Implement the views and instead-of triggers
+
+This is the final stage of implementation.  For every base table you
+must create a secured view and a set of instead-of triggers for insert,
+update and delete.  Refer to the demo (\ref demo-sec) for details of
+this.
+
+\subsection Testing Testing
+
+Be sure to test it all.  Specifically, test to ensure that failed
+connections do not provide any privileges, and to ensure that all
+privileges assigned to highly privileged users are cleared when a more
+lowly privileged user takes over a connection.  Also ensure that
+the underlying tables and raw veil functions are not accessible from
+user accounts.
+
+\section Automation Automatic code generation 
+
+Note that the bulk of the code in a Veil application is in the
+definition of secured views and instead-of triggers, and that this code
+is all very similar.  Consider using a code-generation tool to implement
+this.  A suitable code-generator for Veil may be provided in subsequent
+releases.
+
+Next: \ref Demo
+
+*/
+/*! \page Demo A Full Example Application: The Veil Demo
+\section demo-sec The Veil Demo Application
+
+The Veil demo application serves two purposes:
+- it provides a demonstration of Veil-based access controls;
+- it provides a working example of how to build a secured system using Veil.
+
+This section covers the following topics:
+
+- \ref demo-install
+- \subpage demo-model
+- \subpage demo-security
+- \subpage demo-explore
+- \subpage demo-code
+- \subpage demo-uninstall
+
+\subsection demo-install Installing the Veil demo
+
+The veil_demo application, is packaged as an extension just like Veil
+itself, and is installed as part of the installation of Veil.  To create
+a database containing the veil_demo application:
+- create a new database
+- connect to that database
+- execute <code>create extension veil;</code>
+- execute <code>create extension veil_demo;</code
+
+Next: \ref demo-model
+
+*/
+/*! \page demo-model The Demo Database ERD, Tables and Views
+\section demo-erd The Demo Database ERD
+
+\image html veil_demo.png "The Veil Demo Database" width=10cm
+
+\section demo-tables Table Descriptions
+
+\subsection demo-privs Privileges
+
+This table describes each privilege.  A privilege is a right to do
+something.  Most privileges are concerned with providing access to
+data.  For each table, "X" there are 4 data privileges, SELECT_X, UPDATE_X,
+INSERT_X and DELETE_X.  There are separate privileges to provide access
+to project and person details, and there is a single function privilege,
+<code>can_connect</code>.
+
+\subsection demo-roles Roles
+
+A role is a named collection of privileges.  Privileges are assigned to
+roles through role_privileges.  Roles exist to reduce the number of
+individual privileges that have to be assigned to users, etc.  Instead
+of assigning twenty or more privileges, we assign a single role that
+contains those privileges.
+
+In this application there is a special role, <code>Personal
+Context</code> that contains the set of privileges that apply to all
+users in their personal context.  This role does not need to be
+explicitly assigned to users, and should probably never be explicitly
+assigned.
+
+Assignments of roles in the global context are made through
+person_roles, and in the project (relational) context through
+assignments.
+
+\subsection demo-role-privs Role_Privileges
+
+Role privileges describe the set of privileges for each role.
+
+\subsection demo-role-roles Role_Roles
+
+This is currently unused in the Veil demo application.  Role roles
+provides the means to assign roles to other roles.  This allows new
+roles to be created as combinations of existing roles.  The use of this
+table is currently left as an exercise for the reader.
+
+\subsection demo-persons Persons
+
+This describes each person.  A person is someone who owns data and who
+may connect to the database.  This table should contain authentication
+information etc.  In actuality it just maps a name to a person_id.
+
+\subsection demo-projects Projects
+
+A project represents a real-world project, to which many persons may be
+assigned to work.
+
+\subsection demo-person-roles Person_Roles
+
+This table describes the which roles have been assigned to users in the
+global context.
+
+\subsection demo-assignments Assignments
+
+This describes the roles that have been assigned to a person on a
+specific project.  Assignments provide privilege to a user in the
+project context.
+
+\subsection demo-detail_types Detail_Types
+
+This is a lookup-table that describes general-purpose attributes that
+may be assigned to persons or project.  An example of an attribute for a
+person might be birth-date.  For a project it might be inception date.
+This allows new attributes to be recorded for persons, projects, etc
+without having to add columns to the table.
+
+Each detail_type has a required_privilege field.  This identifies the
+privilege that a user must have in order to be able to see attributes of
+the specific type.
+
+\subsection demo-person_details Person_Details
+
+These are instances of specific attributes for specific persons.
+
+\subsection demo-project-details Project_Details
+
+These are instances of specific attributes for specific projects.
+
+\section Demo-Views The Demo Application's Helper Views
+
+Getting security right is difficult.  The Veil demo provides a number of
+views that help you view the privileges you have in each context.
+
+- my_global_privs shows you the privileges you have in the global
+  context
+- my_personal_privs shows you the privileges you have in the
+  personal context
+- my_project_privs shows you the privileges you have for each project
+  in the project context
+- my_privs shows you all your privileges in all contexts
+- my_projects shows you all the projects to which you have been assigned
+
+Using these views, access control mysteries may be more easily tracked
+down.
+
+Next: \ref demo-security
+
+*/
+/*! \page demo-security The Demo Database Security Model
+\section demo-secmodel The Demo Database Security Model
+
+The Veil demo has three security contexts.
+
+- Personal Context applies to personal data that is owned by the
+  connected user.  All users have the same privileges in personal
+  context, as defined by the role <code>Personal Context</code>.
+- Global Context applies equally to every record in a table.  If a user
+  has <code>SELECT_X</code> privilege in the global context, they will
+  be able to select every record in <code>X</code>, regardless of
+  ownership.  Privileges in global context are assigned through
+  <code>person_roles</code>.
+- Project Context is a relational context and applies to project data.
+  If you are assigned a role on a project, you will be given specific
+  access to certain project tables.  The roles you have been assigned
+  will define your access rights.
+
+The following sections identify which tables may be accessed in which
+contexts.
+
+\subsection demo-global-context The Global Context
+The global context applies to all tables.  All privilege checking
+functions will always look for privileges in the global context.
+
+\subsection demo-personal-context Personal Context
+The following tables may be accessed using rights assigned in the
+personal context:
+- persons
+- assignments
+- person_details
+
+\subsubsection demo-project-context Project Context
+The following tables may be accessed using rights assigned in the
+project context:
+- projects
+- assignments
+- project_details
+
+Next: \ref demo-explore
+
+*/
+/*! \page demo-explore Exploring the Demo
+\section demo-use Exploring the Demo
+\subsection demo-connect Accessing the Demo Database
+Using your favourite tool connect to your veil_demo database.
+
+You will be able to see all of the demo views, both the secured views and
+the helpers.  But you will not initially be able to see any records:
+each view will appear to contain no data.  To gain some privileges you
+must identify yourself using the <code>connect_person()</code> function.
+
+There are 6 persons in the demo.  You may connect as any of them and see
+different subsets of data.  The persons are
+
+- 1 Deb (the DBA).  Deb has global privileges on everything.  She needs
+  them as she is the DBA.
+- 2 Pat (the PM).  Pat has the manager role globally, and is the project
+  manager of project 102.  Pat can see all but the most confidential
+  personal data, and all data about her project.
+- 3 Derick (the director).  Derick can see all personal and project
+  data.  He is also the project manager for project 101, the secret
+  project.
+- 4 Will (the worker).  Will has been assigned to both projects.  He has
+  minimal privileges and cannot access project confidential data.
+- 5 Wilma (the worker).  Willma has been assigned to project 101.  She has
+  minimal privileges and cannot access project confidential data.
+- 6 Fred (the fired DBA).  Fred has all of the privileges of Deb, except
+  for can_connect privilege.  This prevents Fred from being able to do
+  anything.
+
+Here is a sample session, showing the different access enjoyed by
+different users.
+
+\verbatim
+veildemo=> select connect_person(4);
+ connect_person 
+----------------
+ t
+(1 row)
+
+veildemo=> select * from persons;
+ person_id |    person_name    
+-----------+-------------------
+         4 | Will (the worker)
+(1 row)
+
+veildemo=> select * from person_details;
+ person_id | detail_type_id |    value     
+-----------+----------------+--------------
+         4 |           1003 | 20050105
+         4 |           1002 | Employee
+         4 |           1004 | 30,000
+         4 |           1005 | 19660102
+         4 |           1006 | 123456789
+         4 |           1007 | Subservience
+(6 rows)
+
+veildemo=> select * from project_details;
+ project_id | detail_type_id |  value   
+------------+----------------+----------
+        102 |           1001 | 20050101
+        102 |           1002 | Ongoing
+(2 rows)
+
+veildemo=> select connect_person(2);
+ connect_person 
+----------------
+ t
+(1 row)
+
+veildemo=> select * from person_details;
+ person_id | detail_type_id |       value       
+-----------+----------------+-------------------
+         1 |           1003 | 20050102
+         2 |           1003 | 20050103
+         3 |           1003 | 20050104
+         4 |           1003 | 20050105
+         5 |           1003 | 20050106
+         6 |           1003 | 20050107
+         1 |           1002 | Employee
+         2 |           1002 | Employee
+         3 |           1002 | Employee
+         4 |           1002 | Employee
+         5 |           1002 | Employee
+         6 |           1002 | Terminated
+         2 |           1004 | 50,000
+         1 |           1005 | 19610102
+         2 |           1005 | 19600102
+         3 |           1005 | 19650102
+         4 |           1005 | 19660102
+         5 |           1005 | 19670102
+         2 |           1006 | 123456789
+         1 |           1007 | Oracle, C, SQL
+         2 |           1007 | Soft peoply-stuff
+         3 |           1007 | None at all
+         4 |           1007 | Subservience
+         5 |           1007 | Subservience
+(24 rows)
+
+veildemo=> select * from project_details;
+ project_id | detail_type_id |  value   
+------------+----------------+----------
+        102 |           1001 | 20050101
+        102 |           1002 | Ongoing
+        102 |           1008 | $100,000
+(3 rows)
+
+veildemo=>
+
+\endverbatim
+
+Next: \ref demo-code
+
+*/
+/*! \page demo-code The Demo Code
+\dontinclude funcs.sql
+\section demo-codesec The Code
+\subsection demo-code-veil-init veil_init(bool)
+
+This function is called at the start of each session, and whenever
+\ref API-control-reset is called.  The parameter, doing_reset, is
+false when called to initialise a session and true when called from
+veil_perform_reset().
+
+This definition replaces the standard default, do-nothing,
+implementation that is shipped with Veil (see \ref API-control-init).
+
+\skip veil_init(bool)
+\until veil_share(''det_types_privs'')
+
+The first task of veil_init() is to declare a set of Veil shared
+variables.  This is done by calling \ref API-variables-share.  This function
+returns true if the variable already exists, and creates the variable
+and returns false, if not.
+
+These variables are defined as shared because they will be identical for
+each session.  Making them shared means that only one session has to
+deal with the overhead of their initialisation.
+
+\until end if;
+
+We then check whether the shared variables must be initialised.  We will
+initialise them if they have not already been initialised by another
+session, or if we are performing a reset (see \ref API-control-reset).
+
+Each variable is initialised in its own way.  
+
+Ranges are set by a single call to \ref API-basic-init-range.  Ranges are
+used to create bitmap and array types of a suitable size.
+
+Int4Arrays are used to record mappings of one integer to another.  In
+the demo, they are used to record the mapping of detail_type_id to
+required_privilege_id.  We use this variable so that we can look-up the
+privilege required to access a given project_detail or person_detail
+without having to explicitly fetch from attribute_detail_types.
+
+Int4Arrays are initialised by a call to \ref API-intarray-init, and
+are populated by calling \ref API-intarray-set for each value to
+be recorded.  Note that rather than using a cursor to loop through each
+detail_type record, we use select count().  This requires less code and
+has the same effect.
+
+We use a BitmapArray to record the set of privileges for each role.  Its
+initialisation and population is handled in much the same way as
+described above for Int4Arrays, using the functions \ref
+API-bmarray-init and \ref API-bmarray-setbit.
+
+\until end;
+
+The final section of code defines and initialises a set of session
+variables.  These are defined here to avoid getting undefined variable
+errors from any access function that may be called before an
+authenticated connection has been established.
+
+Note that this and all Veil related functions are defined with
+<code>security definer</code> attributes.  This means that the function
+will be executed with the privileges of the function's owner, rather
+than those of the invoker.  This is absolutely critical as the invoker
+must have no privileges on the base objects, or on the raw Veil
+functions themselves.  The only access to objects protected by Veil must
+be through user-defined functions and views.
+
+\subsection demo-code-connect-person connect_person(int4)
+
+This function is used to establish a connection from a specific person.
+In a real application this function would be provided with some form of
+authentication token for the user.  For the sake of simplicity the demo
+allows unauthenticated connection requests.
+
+\skip connect_person(int4)
+\until end;
+
+This function identifies the user, ensures that they have can_connect
+privilege.  It initialises the global_context bitmap to contain the
+union of all privileges for each role the person is assigned through
+person_roles.  It also sets up a bitmap hash containing a bitmap of
+privileges for each project to which the person is assigned.
+
+\subsection demo-code-global-priv i_have_global_priv(int4)
+
+This function is used to determine whether a user has a specified
+privilege in the global context.  It tests that the user is connected
+using <code>veil_int4_get()</code>, and then checks whether the
+specified privilege is present in the <code>global_context</code>
+bitmap.
+
+\skip function i_have_global_priv(int4)
+\until security definer;
+
+The following example shows this function in use by the secured view,
+<code>privileges</code>:
+
+\dontinclude views.sql
+\skip create view privileges
+\until i_have_global_priv(10004);
+
+The privileges used above are <code>select_privileges</code> (10001),
+<code>insert_privileges</code> (10002), <code>update_privileges</code>
+(10003), and <code>delete_privileges</code> (10004).
+
+\subsection demo-code-personal-priv i_have_personal_priv(int4, int4)
+
+This function determines whether a user has a specified privilege to a
+specified user's data, in the global or personal contexts.  It performs
+the same tests as for <code>i_have_global_context()</code>.  If the user
+does not have access in the global context, and the connected user is
+the same user as the owner of the data we are looking at, then we test
+whether the specified privilege exists in the <code>role_privs</code>
+bitmap array for the <code>Personal Context</code> role.
+
+\dontinclude funcs.sql
+\skip function i_have_personal_priv(int4, int4)
+\until end;
+
+Here is an example of this function in use from the persons secured view:
+
+\dontinclude views.sql
+\skip create view persons
+\until i_have_personal_priv(10013, person_id);
+
+\subsection demo-code-project-priv i_have_project_priv(int4, int4)
+This function determines whether a user has a specified privilege in the
+global or project contexts.  If the user does not have the global
+privilege, we check whether they have the privilege defined in the
+project_context BitmapHash.
+
+\dontinclude funcs.sql
+\skip function i_have_project_priv(int4, int4)
+\until security definer;
+
+Here is an example of this function in use from the instead-of insert
+trigger for the projects secured view:
+
+\dontinclude views.sql
+\skip create rule ii_projects 
+\until i_have_project_priv(10018, new.project_id);
+
+\subsection demo-code-proj-pers-priv i_have_proj_or_pers_priv(int4, int4, int4)
+This function checks all privileges.  It starts with the cheapest check
+first, and short-circuits as soon as a privilege is found.
+
+\dontinclude funcs.sql
+\skip function i_have_proj_or_pers_priv(int4, int4, int4)
+\until security definer;
+
+Here is an example of this function in use from the instead-of update
+trigger for the assignments secured view:
+
+\dontinclude views.sql
+\skip create rule ii_assignments 
+\until i_have_proj_or_pers_priv(10027, old.project_id, old.person_id);
+
+\subsection demo-code-pers-detail-priv i_have_person_detail_priv(int4, int4)
+This function is used to determine which types of person details are
+accessible to each user.  This provides distinct access controls to each
+attribute that may be recorded for a person.
+
+\dontinclude funcs.sql
+\skip function i_have_person_detail_priv(int4, int4)
+\until security definer;
+
+The function is shown in use, below, in the instead-of delete trigger
+for person_details.  Note that two distinct access functions are being
+used here.
+
+\dontinclude views.sql
+\skip create rule id_person_details
+\until i_have_person_detail_priv(old.detail_type_id, old.person_id);
+
+Next: \ref demo-uninstall
+
+*/
+/*! \page demo-uninstall Removing The Demo Database
+\section demo-clean-up Removing The Demo Database
+In your veil_demo database execute:
+- <code>drop extension veil_demo;</code> 
+
+Next: \ref Management
+
+*/
+/*! \page Management Managing Privileges, etc
+\section Management-sec Managing Privileges, etc
+The management of privileges and their assignments to roles, persons,
+etc are the key to securing a veil-based application.  It is therefore
+vital that privilege assignment is itself a privileged operation.
+
+The veil demo does not provide an example of how to do this, and this
+section does little more than raise the issue.
+
+IT IS VITAL THAT YOU CAREFULLY LIMIT HOW PRIVILEGES ARE MANIPULATED AND
+ASSIGNED!
+
+Here are some possible rules of thumb that you may wish to apply:
+
+- give only the most senior and trusted users the ability to assign
+  privileges;
+- allow only the DBAs to create privileges;
+- allow only 1 or 2 security administrators to manage roles;
+- allow roles or privileges to be assigned only by users that have both
+  the "assign_privileges"/"assign_roles" privileges, and that themselves
+  have the privilege or role they are assigning;
+- consider having an admin privilege for each table and only allow users
+  to assign privileges on X if they have "admin_x" privilege;
+- limit the users who have access to the role/privilege management
+  functions, and use function-level privileges to enforce this;
+- audit/log all assignments of privileges and roles;
+- send email to the security administrator whenever role_privileges are
+  manipulated and when roles granting high-level privileges are granted.
+
+Next: \ref Esoteria
+
+*/
+/*! \page Esoteria Exotic and Esoteric uses of Veil
+
+\section Esoteria-sec Exotic and Esoteric uses of Veil
+Actually this is neither exotic nor particularly esoteric.  The title is
+simply wishful thinking on the author's part.
+\subsection layered-sessions Multi-Layered Connections
+So far we have considered access controls based only on the user.  If we
+wish to be more paranoid, and perhaps we should, we may also consider
+limiting the access rights of each application.
+
+This might mean that reporting applications would have no ability to
+update data, that financial applications would have no access to
+personnel data, and that personnel apps would have no access to business
+data.
+
+This can be done in addition to the user-level checks, so that even if I
+have DBA privilege, I can not subvert the personnel reporting tools to
+modify financial data.
+
+All access functions would check the service's privileges in addition to
+the user's before allowing any operation.
+
+This could be implemented with a connect_service() function that would
+be called only once per session and that *must* be called prior to
+connecting any users.  Alternatively, the connected service could be
+inferred from the account to which the service is connected.
+
+\subsection columns Column-Level Access Controls
+
+Although veil is primarily intended for row-based access controls,
+column-based is also possible.  If this is required it may be better to
+use a highly normalised data model where columns are converted instead
+into attributes, much like the person_details and project_details tables
+from the demo application (\ref demo-sec).
+
+If this is not possible then defining access_views that only show
+certain columns can be done something like this:
+
+\verbatim
+create view wibble(key, col1, col2, col3) as
+select key, 
+       case when have_col_priv(100001) then col1 else null end,
+       case when have_col_priv(100002) then col2 else null end,
+       case when have_col_priv(100003) then col3 else null end
+where  have_row_priv(1000);
+\endverbatim
+
+The instead-of triggers for this are left as an exercise.
+
+Next: \ref install
+
+*/
+/*! \page install Installation and Configuration
+\section install_sec Installation
+\subsection Get Getting Veil
+Veil can be downloaded as a gzipped tarball from
+http://pgfoundry.org/projects/veil/
+
+The latest development version can also be retrieved from the cvs
+repository using the following commands.  When prompted for a password
+for anonymous, simply press the Enter key. 
+
+\verbatim
+cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/veil login
+cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/veil checkout veil
+\endverbatim
+
+\subsection Pre-requisites Pre-requisites
+You must have a copy of the Postgresql header files available in order
+to build Veil.  For this, you may need to install the postgres developer
+packages for your OS, or even build postgres from source.
+\subsection build-sub Building Veil
+Veil is built using the postgresql extension building infastructure,
+PGXS.  In order to be able to build using PGXS, the following command
+must return the location of the PGXS makefile:
+\verbatim
+$ pg_config --pgxs
+\endverbatim
+You may need to modify your PATH variable to make pg_config usable, or
+ensure it is for the correct postgresql version.
+
+To build the postgres extensions, simply run make with no target:
+\verbatim
+$ make
+\endverbatim
+
+To build the veil documentation (the documentation you are now reading)
+use make docs.
+
+Note that the build system deliberately avoids using make recursively.
+Search the Web for "Recursive Make Considered Harmful" for the reasons
+why.  This makes the construction of the build system a little different
+from what you may be used to.  This may or may not turn out to be a good
+thing.  \ref Feedback "Feedback" is welcomed.
+
+\subsection Install Installing Veil
+As the postgres, pgsql, or root user, run <code>make install</code>.
+\verbatim
+make install
+\endverbatim
+
+\section configuration Configuration
+To configure Veil, the following lines should be added to your
+postgresql.conf:
+\code
+shared_preload_libraries = '<path to shared library>/veil.so' 
+
+custom_variable_classes = 'veil'
+
+#veil.dbs_in_cluster = 1
+#veil.shared_hash_elems = 32
+#veil.shmem_context_size = 16384
+\endcode
+
+The three configuration options, commented out above, are:
+- dbs_in_cluster
+  The number of databases, within the database cluster, that
+  will use Veil.  Each such database will be allocated 2 chunks of
+  shared memory (of shmem_context_size), and a single LWLock.
+  It defaults to 1.
+
+- shared_hash_elems
+  This describes how large a hash table should be created for veil
+  shared variables.  It defaults to 32.  If you have more than about 20
+  shared variables you may want to increase this to improve
+  performance.  This setting does not limit the number of variables that
+  may be defined, it just limits how efficiently they may be accessed.
+
+- shmem_context_size
+  This sets an upper limit on the amount of shared memory for a single
+  Veil shared memory context (there will be two of these).  It defaults 
+  to 16K.  Increase this if you have many shared memory structures.
+
+\subsection Regression Regression Tests
+Veil comes with a built-in regression test suite.  Use <code>make
+regress</code> or <code>make check</code> (after installing and
+configuring Veil) to run this.  You will need superuser access to
+Postgres in order to create the regression test database.  The
+regression test assumes you will have a postgres superuser account named
+the same as your OS account.  If pg_hba.conf disallows "trust"ed access
+locally, then you will need to provide a password for this account in
+your .pgpass file (see postgres documentation for details).
+
+The regression tests are all contained within the regress directory and
+are run by the regress.sh shell script.  Use the -h option to get
+fairly detailed help.
+
+\subsection Demodb_install Demo Database
+As with the regression tests, you will need to be a privileged database
+user to be able to create the demo database.  For more on installing the
+demo database see \ref demo-install.
+
+\subsection Debugging Debugging
+If you encounter problems with Veil, you may want to try building with
+debug enabled.  Define the variable VEIL_DEBUG on the make command line
+to add extra debug code to the executable:
+\verbatim
+$ make clean; make VEIL_DEBUG=1 all
+\endverbatim
+
+This is a new feature and not yet fully formed but is worth trying if
+Veil appears to be misbehaving.  If any of the debug code encounters a
+problem, ERRORs will be raised.
+
+Next: \ref History
+
+*/
+/*! \page History History and Compatibility
+\section past Changes History
+\subsection v1_0 Version 1.0.0 (Stable) (2011-07-22)
+This is the first version of Veil to be considered production ready, and
+completely stable.  It is for use only with PostgreSQL 9.1.  Support for
+older versions of PostgreSQL has been removed in this version.
+
+Major changes include:
+- revamp of the build system to use PGXS and the new PostgreSQL 9.1
+  extensions mechanism.  Veil is now built as an extension.
+- removal of the old veil_trial mechanism, which allowed Veil to be
+  tried out without fully installing it.  This has removed much
+  unnecessary complexity.
+- much general code cleanup, including the removal of conditional code
+  for older PostgreSQL versions.
+- documentation changes, including improved comments for Veil
+  functions.
+
+\subsection v9_12 Version 0.9.12 (2010-11-19)
+Release for compatibility with PostgreSQL V9.0.  Minor bugfixes and
+improvements to the build system.  Also added documentation about Veil's
+limitations.
+
+\subsection v9_11 Version 0.9.11 (2010-03-12)
+Bugfix release, fixing a serious memory corruption bug that has existed
+in all previous versions.  Users are strongly encouraged to avoid using
+older versions of Veil.
+
+The version number has been deliberatley bumped past 0.9.10 to emphasize
+that the last part of the version is a two digit number.
+
+\subsection v9_9 Version 0.9.9 (2009-07-06)
+New release to coincide with PostgreSQL V8.4.
+
+\subsection v9_8 Version 0.9.8 (2008-02-06)
+This is the first Beta release.  It incorporates a few bug fixes, a new
+serialisation API, improvements to the autoconf setup and makefiles, and
+some documentation improvements.  The status of Veil has been raised to
+Beta in recognition of its relative stability.
+
+\subsection v9_6 Version 0.9.6 (2008-02-06)
+This release has minor changes to support PostgreSQL 8.3.
+
+\subsection v9_5 Version 0.9.5 (2007-07-31)
+This is a bugifx release, fixing a memory allocation bug in the use of 
+bitmap_refs.  There are also fixes for minor typos, etc.
+
+\subsection v9_4 Version 0.9.4 (2007-02-21)
+This is a bugifx release, providing:
+ - fix for major bug with recursive handling of spi connect, etc;
+ - improvement to session initialisation code to do more up-front work
+   in ensure_init();
+ - safer initialisation of malloc'd data structures;
+ - improved error messages for shared memory exhaustion cases;
+ - addition of debug code including canaries in data structures;
+ - improvement to autoconf to better support Debian GNU/Linux, and OSX;
+ - improvement to autoconf/make for handling paths containing spaces;
+ - improvement to regression tests to better support OSX;
+ - removal of spurious debug warning messages.
+
+\subsection v9_3 Version 0.9.3 (2006-10-31) 
+This version uses the new Postgres API for reserving shared memory for
+add-ins.  It also allows the number of Veil-enabled databases for a
+cluster to be configured, and refactors much of the shared memory code.
+A small fix for the Darwin makefile was also made.
+
+\subsection v9_2 Version 0.9.2 (2006-10-01) 
+This version was released to coincide with Postgres 8.2beta1 and first
+made use of new Postgres APIs to allow Veil to be a good Postgres
+citizen.  
+
+With prior versions of Veil, or prior versions of Postgres, Veil steals
+from Postgres the shared memory that it requires.  This can lead to the
+exhaustion of Postgres shared memory.
+
+Unfortunately, the Postgres API for shared memory reservation had to
+change follwing 8.2.beta1, and this version of Veil is therefore deprecated.
+
+\subsection v9_1 Version 0.9.1 (2006-07-04)
+This release fixed a small number of bugs and deficiencies:
+- major error in veil_perform_reset that prevented proper use of the two
+interdependant shared memory contexts
+- minor improvements in the build process to "configure" and friends
+- minor documentation improvements
+
+\subsection v9_0 Version 0.9.0  (2005-10-04) 
+This was the first public alpha release of Veil.
+
+\section forecast Change Forecast
+New versions will be released with each new major version of
+PostgreSQL.  Once there are three PostgreSQL versions for which Veil has
+been at production status, the change history and support matrix for for
+pre-production versions will be removed from this documentation.
+
+\section compatibility Supported versions of Postgres
+<TABLE>
+  <TR>
+    <TD rowspan=2>Veil version</TD>
+    <TD colspan=9>Postgres Version</TD>
+  </TR>
+  <TR>
+    <TD>7.4</TD>
+    <TD>8.0</TD>
+    <TD>8.1</TD>
+    <TD>8.2beta1</TD>
+    <TD>8.2</TD>
+    <TD>8.3</TD>
+    <TD>8.4</TD>
+    <TD>9.0</TD>
+    <TD>9.1</TD>
+  </TR>
+  <TR>
+    <TD>0.9.0 Alpha</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.1 Alpha</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.2 Alpha</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>2</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.3 Alpha</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.4 Alpha</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.5 Alpha</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.6 Alpha</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.8 Beta</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.9 Beta</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.11 Beta</TD>
+    <TD>-</TD>
+    <TD>1</TD>
+    <TD>1</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>0.9.12 Beta</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>3</TD>
+    <TD>-</TD>
+  </TR>
+  <TR>
+    <TD>1.0.0 (Stable)</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>-</TD>
+    <TD>Yes</TD>
+  </TR>
+</TABLE>
+Notes:
+
+1) These combinations of Veil and Postgres provide no configuration
+   options for shared memory.  Veil's shared memory may be exhausted by
+   too many requests for large shared objects.  Furthermore, Postgres'
+   own shared memory may be easily exhausted by creating too many
+   Veil-using databases within a cluster.
+
+2) This version is deprecated
+
+3) These combinations of Veil and Postgres provide full configuration
+   options for shared memory usage, and Veil cooperates with Postgres
+   for the allocation of such memory meaning that it is not possible to
+   use Veil to exhaust Postgres' shared memory.  This is the minimum
+   Veil configuration recommended for production use.
+
+\section platforms Supported Platforms
+Veil should be buildable on any platform supported by PostgreSQL and
+PGXS. 
+
+Next: \ref Feedback
+
+*/
+/*! \page Feedback Bugs and Feedback
+\section Feedback Bugs and Feedback
+For general feedback, to start and follow discussions, etc please join
+the veil-general@pgfoundry.org mailing list.
+
+If you wish to report a bug or request a feature, please send mail to
+veil-general@pgfoundry.org
+
+If you encounter a reproducible veil bug that causes a database server
+crash, a gdb backtrace would be much appreciated.  To generate a
+backtrace, you will need to login to the postgres owner account on the
+database server.  Then identify the postgres backend process associated
+with the database session that is going to crash.  The following command
+identifies the backend pid for a database session started by marc:
+
+\verbatim
+$ ps auwwx | grep ^postgres.*ma[r]c | awk '{print $2}'
+\endverbatim
+
+Now you invoke gdb with the path to the postgres binary and the pid for
+the backend, eg:
+
+\verbatim
+$ gdb /usr/lib/postgresql/9.1/bin/postgres 5444
+\endverbatim
+
+Hit c and Enter to get gdb to allow the session to continue.   Now,
+reproduce the crash from your database session.  When the crash occurs,
+your gdb session will return to you.  Now type bt and Enter to get a
+backtrace. 
+
+If you wish to contact the author offlist, you can find him at his website
+http://bloodnok.com/Marc.Munro
+
+Next: \ref Performance
+
+*/
+/*! \page Performance Performance
+\section perf Performance
+Attempts to benchmark veil using pgbench have not been very successful.
+It seems that the overhead of veil is small enough that it is
+overshadowed by the level of "noise" within pgbench.
+
+Based on this inability to properly benchmark veil, the author is going
+to claim that "it performs well".
+
+To put this into perspective, if your access functions do not require
+extra fetches to be performed in order to establish your access rights,
+you are unlikely to notice or be able to measure any performance hit
+from veil.
+
+If anyone can provide good statistical evidence of a performance hit,
+the author would be most pleased to hear from you.
+
+Next: \ref Credits
+
+*/
+/*! \page Credits Credits
+\section Credits
+The Entity Relationship Diagram in section \ref demo-erd was produced
+automatically from an XML definition of the demo tables, using Autograph
+from CF Consulting.  Thanks to Colin Fox for allowing its use.
+
+Thanks to the PostgreSQL core team for providing PostgreSQL.
+
+Thanks to pgfoundry for providing a home for this project.
+*/
+
diff --git a/src/veil_mainpage.d b/src/veil_mainpage.d
new file mode 100644 (file)
index 0000000..f9995de
--- /dev/null
@@ -0,0 +1,2 @@
+src/veil_mainpage.o src/veil_mainpage.d: \
+  src/veil_mainpage.c
diff --git a/src/veil_query.c b/src/veil_query.c
new file mode 100644 (file)
index 0000000..af9234f
--- /dev/null
@@ -0,0 +1,302 @@
+/**
+ * @file   veil_query.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Functions to simplify SPI-based queries.  These are way more
+ * sophisticated than veil really needs but are nice and generic.
+ * 
+ */
+
+
+#include <stdio.h>
+#include "postgres.h"
+#include "catalog/pg_type.h"
+#include "executor/spi.h"
+#include "veil_version.h"
+#include "access/xact.h"
+#include "veil_funcs.h"
+
+/**
+ * A Fetch_fn is a function that processes records, one at a time,
+ * returned from a query.
+ */
+typedef bool (Fetch_fn)(HeapTuple, TupleDesc, void *);
+
+
+
+/**
+ * The number of records to fetch in one go from the query executor.
+ */
+#define FETCH_SIZE    20
+
+
+/**
+ * Counter to assess depth of recursive spi calls, so that we can
+ * sensibly and safely use spi_push and spi_pop when appropriate.
+ */
+static int4 query_depth = 0;
+
+/**
+ * State variable used to assess whther query_depth may have been left
+ * in an invalid state following an error being raised.
+ */
+static TransactionId connection_xid = 0;
+
+/**
+ * If already connected in this session, push the current connection,
+ * and get a new one.
+ * We are already connected, if:
+ * - are within a query
+ * - and the current transaction id matches the saved transaction id
+ */
+int
+vl_spi_connect(void)
+{
+   TransactionId xid = GetCurrentTransactionId();
+
+   if (query_depth > 0) {
+       if (xid == connection_xid) {
+           SPI_push();
+       }
+       else {
+           /* The previous transaction must have aborted without
+            * resetting query_depth */
+           query_depth = 0;
+       }
+   }
+
+   connection_xid = xid;
+   return SPI_connect();
+}
+
+/**
+ * Reciprocal function for vl_spi_connect()
+ */
+int
+vl_spi_finish(void)
+{
+   int spi_result = SPI_finish();
+   if (query_depth > 0) {
+       SPI_pop();
+   }
+
+   return spi_result;
+}
+
+/** 
+ * Prepare a query for query().  This creates and executes a plan.  The
+ * caller must have established SPI_connect.  It is assumed that no
+ * parameters to the query will be null.
+ * \param qry The text of the SQL query to be performed.
+ * \param nargs The number of input parameters ($1, $2, etc) to the query
+ * \param argtypes Pointer to an array containing the OIDs of the data
+ * \param args Actual parameters
+ * types of the parameters 
+ * \param read_only Whether the query should be read-only or not
+ * \param saved_plan Adress of void pointer into which the query plan
+ * will be saved.  Passing the same void pointer on a subsequent call
+ * will cause the saved query plan to be re-used.
+ */
+static void
+prepare_query(const char *qry,
+             int nargs,
+             Oid *argtypes,
+             Datum *args,
+             bool read_only,
+             void **saved_plan)
+{
+    void   *plan;
+    int     exec_result;
+   
+    if (saved_plan && *saved_plan) {
+       /* A previously prepared plan is available, so use it */
+       plan = *saved_plan;
+    }
+    else {
+       if (!(plan = SPI_prepare(qry, nargs, argtypes))) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("prepare_query fails"),
+                    errdetail("SPI_prepare('%s') returns NULL "
+                              "(SPI_result = %d)", 
+                              qry, SPI_result)));
+       }
+
+       if (saved_plan) {
+           /* We have somewhere to put the saved plan, so save  it. */
+           *saved_plan = SPI_saveplan(plan);
+       }
+    }
+   
+   exec_result = SPI_execute_plan(plan, args, NULL, read_only, 0);
+   if (exec_result < 0) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("prepare_query fails"),
+                errdetail("SPI_execute_plan('%s') returns error %d",
+                          qry, exec_result)));
+    }
+}
+
+/** 
+ * Prepare and execute a query.  Query execution consists of a call to
+ * process_row for each returned record.  Process_row can return a
+ * single value to the caller of this function through the fn_param
+ * parameter.  It is the caller's responsibility to establish an SPI
+ * connection with SPI_connect.  It is assumed that no parameters to
+ * the query, and no results will be null.
+ * \param qry The text of the SQL query to be performed.
+ * \param nargs The number of input parameters ($1, $2, etc) to the query
+ * \param argtypes Pointer to an array containing the OIDs of the data
+ * \param args Actual parameters
+ * types of the parameters 
+ * \param read_only Whether the query should be read-only or not
+ * \param saved_plan Adress of void pointer into which the query plan
+ * will be saved.  Passing the same void pointer on a subsequent call
+ * will cause the saved query plan to be re-used.
+ * \param process_row  The ::Fetch_fn function to be called for each
+ * fetched row to process it.  If this is null, we simply count the row,
+ * doing no processing on the tuples returned.
+ * \param fn_param  An optional parameter to the process_row function.
+ * This may be used to return a value to the caller.
+ * \return The total number of records fetched and processed by
+ * process_row.
+ */
+static int
+query(const char *qry,
+      int nargs,
+      Oid *argtypes,
+      Datum *args,
+     bool  read_only,
+      void **saved_plan,
+      Fetch_fn process_row,
+      void *fn_param)
+{
+    int    row;
+   int    fetched = 0;
+
+   query_depth++;
+    prepare_query(qry, nargs, argtypes, args, read_only, saved_plan);
+   
+   for(row = 0; row < SPI_processed; row++) {
+       fetched++;
+       /* Process a row using the processor function */
+       if (!process_row(SPI_tuptable->vals[row], 
+                        SPI_tuptable->tupdesc,
+                        fn_param)) 
+       {
+           break;
+       }
+   }
+   query_depth--;
+    return fetched;
+}
+
+
+/** 
+ * ::Fetch_fn function for processing a single row of a single integer for 
+ * ::query.
+ * \param tuple The row to be processed
+ * \param tupdesc Descriptor for the types of the fields in the tuple.
+ * \param p_result Pointer to an int4 variable into which the value
+ * returned from the query will be placed.
+ * \return false.  This causes ::query to terminate after processing a
+ * single row.
+ */
+static bool
+fetch_one_bool(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
+{
+   static bool ignore_this = false;
+    bool col = DatumGetBool(SPI_getbinval(tuple, tupdesc, 1, &ignore_this));
+    *((bool *) p_result) = col;
+   
+    return false;
+}
+
+/** 
+ * ::Fetch_fn function for processing a single row of a single integer for 
+ * ::query.
+ * \param tuple The row to be processed
+ * \param tupdesc Descriptor for the types of the fields in the tuple.
+ * \param p_result Pointer to an int4 variable into which the value
+ * returned from the query will be placed.
+ * \return false.  This causes ::query to terminate after processing a
+ * single row.
+ */
+static bool
+fetch_one_str(HeapTuple tuple, TupleDesc tupdesc, void *p_result)
+{
+    char *col = SPI_getvalue(tuple, tupdesc, 1);
+   char **p_str = (char **) p_result;
+   *p_str = col;
+   
+    return false;
+}
+
+/** 
+ * Executes a query that returns a single bool value.
+ * 
+ * @param qry The text of the query to be performed.
+ * @param result Variable into which the result of the query will be placed.
+ * 
+ * @return true if the query returned a record, false otherwise.
+ */
+bool
+vl_bool_from_query(const char *qry,
+                  bool *result)
+{
+   int     rows;
+    Oid     argtypes[0];
+    Datum   args[0];
+   rows = query(qry, 0, argtypes, args, false, NULL, 
+                fetch_one_bool, (void *)result);
+   return (rows > 0);
+}
+
+/** 
+ * Executes a query by oid, that returns a single string value.
+ * 
+ * @param qry The text of the query to be performed.
+ * @param param The oid of the row to be fetched.
+ * @param result Variable into which the result of the query will be placed.
+ * 
+ * @return true if the query returned a record, false otherwise.
+ */
+static bool
+str_from_oid_query(const char *qry,
+                  const Oid param,
+                  char *result)
+{
+   int     rows;
+    Oid     argtypes[1] = {OIDOID};
+    Datum   args[1];
+   
+   args[0] = ObjectIdGetDatum(param);
+   rows = query(qry, 1, argtypes, args, false, NULL, 
+                fetch_one_str, (void *)result);
+   return (rows > 0);
+}
+
+/** 
+ * Determine whether the given oid represents an existing database or not.
+ * 
+ * @param db_id Oid of the database in which we are interested.
+ * 
+ * @result True if the database exists.
+ */
+
+extern bool
+vl_db_exists(Oid db_id)
+{
+   char dbname[NAMEDATALEN + 1];
+
+   return str_from_oid_query("select datname from pg_database where oid = $1",
+                             db_id, dbname);
+}
+
+
diff --git a/src/veil_query.d b/src/veil_query.d
new file mode 100644 (file)
index 0000000..dcda96c
--- /dev/null
@@ -0,0 +1,5 @@
+src/veil_query.o src/veil_query.d: \
+  src/veil_query.c \
+  src/veil_version.h \
+  src/veil_funcs.h \
+  src/veil_datatypes.h
diff --git a/src/veil_serialise.c b/src/veil_serialise.c
new file mode 100644 (file)
index 0000000..1da479c
--- /dev/null
@@ -0,0 +1,958 @@
+/**
+ * @file   veil_serialise.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2009 - 2011 Marc Munro
+ *     License:      BSD
+ *
+ * \endcode
+ * @brief  
+ * Functions serialising and de-serialising session variables.  The
+ * purpose of this is to allow the contents of session variables to be
+ * saved for later re-use.  They may be saved in files, temporary tables
+ * or using some smart cache such as memcached (thru the pgmemcache
+ * add-in).
+ * 
+ */
+
+/*TODO: Provide a patch to postgres to make b64_encode and
+ *      b64_decode extern functions and to elimiate the 
+ *      redundant copies from pgcrypto 
+ */
+
+#include "postgres.h"
+#include "veil_version.h"
+#include "veil_funcs.h"
+#include "veil_datatypes.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
+#define INT4VAR_HDR       'V'
+#define RANGE_HDR         'R'
+#define BITMAP_HDR        'B'
+#define BITMAP_ARRAY_HDR  'A'
+#define BITMAP_HASH_HDR   'H'
+#define INT4_ARRAY_HDR    'I'
+#define BITMAP_HASH_MORE  '>'
+#define BITMAP_HASH_DONE  '.'
+
+
+#define HDRLEN                 8   /* HDR field plus int32 for length of
+                                   * item */
+#define INT32SIZE_B64          7
+#define BOOLSIZE               1
+
+
+/* BEGIN SECTION OF CODE COPIED FROM pgcrypto.c */
+
+static const char _base64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const int8 b64lookup[128] = {
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+   -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+};
+
+static unsigned
+b64_encode(const char *src, unsigned len, char *dst)
+{
+   char       *p,
+              *lend = dst + 76;
+   const char *s,
+              *end = src + len;
+   int         pos = 2;
+   uint32      buf = 0;
+
+   s = src;
+   p = dst;
+
+   while (s < end)
+   {
+       buf |= (unsigned char) *s << (pos << 3);
+       pos--;
+       s++;
+
+       /* write it out */
+       if (pos < 0)
+       {
+           *p++ = _base64[(buf >> 18) & 0x3f];
+           *p++ = _base64[(buf >> 12) & 0x3f];
+           *p++ = _base64[(buf >> 6) & 0x3f];
+           *p++ = _base64[buf & 0x3f];
+
+           pos = 2;
+           buf = 0;
+       }
+       if (p >= lend)
+       {
+           *p++ = '\n';
+           lend = p + 76;
+       }
+   }
+   if (pos != 2)
+   {
+       *p++ = _base64[(buf >> 18) & 0x3f];
+       *p++ = _base64[(buf >> 12) & 0x3f];
+       *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
+       *p++ = '=';
+   }
+
+   return p - dst;
+}
+
+static unsigned
+b64_decode(const char *src, unsigned len, char *dst)
+{
+   const char *srcend = src + len,
+              *s = src;
+   char       *p = dst;
+   char        c;
+   int         b = 0;
+   uint32      buf = 0;
+   int         pos = 0,
+               end = 0;
+
+   while (s < srcend)
+   {
+       c = *s++;
+
+       if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
+           continue;
+
+       if (c == '=')
+       {
+           /* end sequence */
+           if (!end)
+           {
+               if (pos == 2)
+                   end = 1;
+               else if (pos == 3)
+                   end = 2;
+               else
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("unexpected \"=\"")));
+           }
+           b = 0;
+       }
+       else
+       {
+           b = -1;
+           if (c > 0 && c < 127)
+               b = b64lookup[(unsigned char) c];
+           if (b < 0)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("invalid symbol")));
+       }
+       /* add it to buffer */
+       buf = (buf << 6) + b;
+       pos++;
+       if (pos == 4)
+       {
+           *p++ = (buf >> 16) & 255;
+           if (end == 0 || end > 1)
+               *p++ = (buf >> 8) & 255;
+           if (end == 0 || end > 2)
+               *p++ = buf & 255;
+           buf = 0;
+           pos = 0;
+       }
+   }
+
+   if (pos != 0)
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                errmsg("invalid end sequence")));
+
+   return p - dst;
+}
+
+/* END SECTION OF CODE COPIED FROM pgcrypto.c */
+
+#endif
+
+/** 
+ * Return the length of a base64 encoded stream for a binary stream of
+ * ::bytes length.
+ * 
+ * @param bytes The length of the input binary stream in bytes
+ * @return The length of the base64 character stream required to
+ * represent the input stream.
+ */
+static int
+streamlen(int bytes)
+{
+   return (4 * ((bytes + 2) / 3));
+}
+
+/** 
+ * Return the length of the header part of a serialised data stream for
+ * the given named variable.  Note that the header contains the name and
+ * a base64 encode length indicator for the name.
+ * 
+ * @param name The variable name to be recorded in the header
+ * @return The length of the base64 character stream required to
+ * hold the serialised header for the named variable.
+ */
+static int
+hdrlen(char *name)
+{
+   return HDRLEN + INT32SIZE_B64 + strlen(name);
+}
+
+
+/** 
+ * Serialise an int4 value as a base64 stream (truncated to save a
+ * byte) into *p_stream.
+ *
+ * @param p_stream Pointer into stream currently being written.  This
+ * must be large enought to take the contents to be written.  This
+ * pointer is updated to point to the next free slot in the stream after
+ * writing the int4 value, and is null terminated at that position.
+ * @param value The value to be written to the stream.
+ */
+static void
+serialise_int4(char **p_stream, int4 value)
+{
+   int len = b64_encode((char *) &value, sizeof(int32), *p_stream);
+   (*p_stream) += (len - 1);  /* X: dumb optimisation saves a byte */
+   (**p_stream) = '\0';
+}
+
+
+/** 
+ * De-serialise an int4 value from a base64 character stream.
+ *
+ * @param p_stream Pointer into the stream currently being read.
+ * must be large enought to take the contents to be written.  This
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the int4 value.
+ * @return the int4 value read from the stream
+ */
+static int4
+deserialise_int4(char **p_stream)
+{
+   int4 value;
+   char *endpos = (*p_stream) + INT32SIZE_B64;
+   char endchar = *endpos;
+   *endpos = '=';  /* deal with dumb optimisation (X) above */
+   b64_decode(*p_stream, INT32SIZE_B64 + 1, (char *) &value);
+   *endpos = endchar;
+   (*p_stream) += INT32SIZE_B64;
+   return value;
+}
+
+/** 
+ * Serialise a binary stream as a base64 stream into *p_stream.
+ *
+ * @param p_stream Pointer into stream currently being written.  This
+ * pointer is updated to point to the next free slot in the stream after
+ * writing the contents of instream and is null terminated at that
+ * position.
+ * @param bytes The number of bytes to be written.
+ * @param instream The binary stream to be written.
+ */
+static void
+serialise_stream(char **p_stream, int4 bytes, char *instream)
+{
+   int len = b64_encode(instream, bytes, *p_stream);
+   (*p_stream)[len] = '\0';
+   (*p_stream) += len;
+}
+
+/** 
+ * De-serialise a binary stream.
+ *
+ * @param p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream.
+ * @param bytes The number of bytes to be read
+ * @param outstream Pointer into the pre-allocated memory are into which
+ * the binary from p_stream is to be written.
+ */
+static void
+deserialise_stream(char **p_stream, int4 bytes, char *outstream)
+{
+   int4 len = streamlen(bytes);
+   b64_decode(*p_stream, len, outstream);
+   (*p_stream) += len;
+}
+
+
+/** 
+ * Serialise a boolean value into *p_stream.
+ *
+ * @param p_stream Pointer into stream currently being written.  This
+ * pointer is updated to point to the next free slot in the stream after
+ * writing the contents of value and is null terminated at that
+ * position.  A true value is written as 'T', and false as 'F'.
+ * @param value The boolean value to be written.
+ */
+static void
+serialise_bool(char **p_stream, bool value)
+{
+   (**p_stream) = value? 'T': 'F';
+   (*p_stream)++;
+   (**p_stream) = '\0';
+}
+
+/** 
+ * De-serialise a boolean value.
+ *
+ * @param p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream.
+ * @return True or false depending on the contents of p_stream.
+ */
+static bool
+deserialise_bool(char **p_stream)
+{
+   bool result = (**p_stream) == 'T';
+   (*p_stream)++;
+
+   return result;
+}
+
+/** 
+ * Serialise a character value into *p_stream.
+ *
+ * @param p_stream Pointer into stream currently being written.  This
+ * pointer is updated to point to the next free slot in the stream after
+ * writing the contents of value and is null terminated at that
+ * position.  The character is written as a single byte character.
+ * @param value The character value to be written.
+ */
+static void
+serialise_char(char **p_stream, char value)
+{
+   (**p_stream) = value;
+   (*p_stream)++;
+   (**p_stream) = '\0';
+}
+
+/** 
+ * De-serialise a character value.
+ *
+ * @param p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream.
+ * @return The value of the character read from p_stream.
+ */
+static char
+deserialise_char (char **p_stream)
+{
+   char result = **p_stream;
+   (*p_stream)++;
+
+   return result;
+}
+
+/** 
+ * Serialise a string (containing a name) into *p_stream.
+ *
+ * @param p_stream Pointer into stream currently being written.  This
+ * pointer is updated to point to the next free slot in the stream after
+ * writing the contents of value and is null terminated at that
+ * position.  The character is written as a single byte character.
+ * @param name The string to be written.
+ */
+static void
+serialise_name(char **p_stream, char *name)
+{
+   static char *blank_name = "";
+   char *safe_name;
+   if (name) {
+       safe_name = name;
+   }
+   else {
+       safe_name = blank_name;
+   }
+       
+   serialise_int4(p_stream, strlen(safe_name));
+   strcpy((*p_stream), safe_name);
+   (*p_stream) += strlen(safe_name);
+   (**p_stream) = '\0';
+}
+
+/** 
+ * De-serialise a string returning a dynamically allocated string.
+ *
+ * @param p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream.
+ * @return Dynamically allocated copy of string read from *p_stream
+ */
+static char *
+deserialise_name(char **p_stream)
+{
+   int32 name_len = deserialise_int4(p_stream);
+   char *result = palloc((name_len + 1) * sizeof(char));
+   strncpy(result, *p_stream, name_len);
+   result[name_len] = '\0';
+   (*p_stream) += name_len;
+   return result;
+}
+
+/** 
+ * Serialise a veil integer variable into a dynamically allocated string.
+ *
+ * @param var Pointer to the variable to be serialised
+ * @param name The name of the variable
+ * @return Dynamically allocated string containing the serialised
+ * variable
+ */
+static char *
+serialise_int4var(Int4Var *var, char *name)
+{
+    int stream_len = hdrlen(name) + BOOLSIZE + INT32SIZE_B64 + 1;
+   char *stream = palloc(stream_len * sizeof(char));
+   char *streamstart = stream;
+   
+   serialise_char(&stream, INT4VAR_HDR);
+   serialise_name(&stream, name);
+   serialise_bool(&stream, var->isnull);
+   serialise_int4(&stream, var->value);
+   return streamstart;
+}
+
+/** 
+ * De-serialise a veil integer variable.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return Pointer to the variable created or updated from the stream.
+ */
+static VarEntry *
+deserialise_int4var(char **p_stream)
+{
+   char *name = deserialise_name(p_stream);
+   VarEntry *var = vl_lookup_variable(name);
+    Int4Var *i4v = (Int4Var *) var->obj;
+
+    if (i4v) {
+        if (i4v->type != OBJ_INT4) {
+            vl_type_mismatch(name, OBJ_INT4, i4v->type);
+        }
+    }
+   else {
+        var->obj = (Object *) vl_NewInt4(var->shared);
+        i4v = (Int4Var *) var->obj;
+   }
+   i4v->isnull = deserialise_bool(p_stream);
+   i4v->value = deserialise_int4(p_stream);
+   return var;
+}
+
+/** 
+ * Serialise a veil integer array variable into a dynamically allocated
+ * string.
+ *
+ * @param array Pointer to the variable to be serialised
+ * @param name The name of the variable
+ * @return Dynamically allocated string containing the serialised
+ * variable
+ */
+static char *
+serialise_int4array(Int4Array *array, char *name)
+{
+    int elems = 1 + array->arraymax - array->arrayzero;
+    int stream_len = hdrlen(name) + (2 * INT32SIZE_B64) +
+       streamlen(elems * sizeof(int32)) + 1;
+   char *stream = palloc(stream_len * sizeof(char));
+   char *streamstart = stream;
+
+   serialise_char(&stream, INT4_ARRAY_HDR);
+   serialise_name(&stream, name);
+   serialise_int4(&stream, array->arrayzero);
+   serialise_int4(&stream, array->arraymax);
+   serialise_stream(&stream, elems * sizeof(int32), 
+                    (char *) &(array->array[0]));
+
+   return streamstart;
+}
+
+/** 
+ * De-serialise a veil integer array variable.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return Pointer to the variable created or updated from the stream.
+ */
+static VarEntry *
+deserialise_int4array(char **p_stream)
+{
+   char *name = deserialise_name(p_stream);
+    int4 arrayzero;
+   int4 arraymax;
+   int4 elems;
+   VarEntry *var = vl_lookup_variable(name);
+   Int4Array *array = (Int4Array *) var->obj;
+
+   arrayzero = deserialise_int4(p_stream);
+   arraymax = deserialise_int4(p_stream);
+   elems = 1 + arraymax - arrayzero;
+
+    if (array) {
+        if (array->type != OBJ_INT4_ARRAY) {
+            vl_type_mismatch(name, OBJ_INT4_ARRAY, array->type);
+        }
+    }
+   array = vl_NewInt4Array(array, var->shared, arrayzero, arraymax);
+   var->obj = (Object *) array;
+
+   deserialise_stream(p_stream, elems * sizeof(int4), 
+                      (char *) &(array->array[0]));
+   return var;
+}
+
+/** 
+ * Serialise a veil range variable into a dynamically allocated
+ * string.
+ *
+ * @param range Pointer to the variable to be serialised
+ * @param name The name of the variable
+ * @return Dynamically allocated string containing the serialised
+ * variable
+ */
+static char *
+serialise_range(Range *range, char *name)
+{
+    int stream_len = hdrlen(name) + (INT32SIZE_B64 * 2) + 1;
+   char *stream = palloc(stream_len * sizeof(char));
+   char *streamstart = stream;
+
+   serialise_char(&stream, RANGE_HDR);
+   serialise_name(&stream, name);
+   serialise_int4(&stream, range->min);
+   serialise_int4(&stream, range->max);
+   return streamstart;
+}
+
+/** 
+ * De-serialise a veil range variable.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return Pointer to the variable created or updated from the stream.
+ */
+static VarEntry *
+deserialise_range(char **p_stream)
+{
+   char *name = deserialise_name(p_stream);
+   VarEntry *var = vl_lookup_variable(name);
+    Range *range = (Range *) var->obj;
+
+    if (range) {
+        if (range->type != OBJ_RANGE) {
+            vl_type_mismatch(name, OBJ_RANGE, range->type);
+        }
+    }
+   else {
+        var->obj = (Object *) vl_NewRange(var->shared);
+        range = (Range *) var->obj;
+   }
+
+   range->min = deserialise_int4(p_stream);
+   range->max = deserialise_int4(p_stream);
+   return var;
+}
+
+/** 
+ * Serialise a single bitmap from a veil bitmap array or bitmap hash.
+ *
+ * @param p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * writing the stream.
+ * @param bitmap The bitmap to be serialised.
+ */
+static void
+serialise_one_bitmap(char **p_stream, Bitmap *bitmap)
+{
+    int elems = ARRAYELEMS(bitmap->bitzero, bitmap->bitmax);
+   serialise_int4(p_stream, bitmap->bitzero);
+   serialise_int4(p_stream, bitmap->bitmax);
+   serialise_stream(p_stream, elems * sizeof(int32), 
+                    (char *) &(bitmap->bitset));
+}
+
+/** 
+ * Serialise a veil bitmap variable into a dynamically allocated
+ * string.
+ *
+ * @param bitmap Pointer to the variable to be serialised
+ * @param name The name of the variable
+ * @return Dynamically allocated string containing the serialised
+ * variable
+ */
+static char *
+serialise_bitmap(Bitmap *bitmap, char *name)
+{
+    int elems = ARRAYELEMS(bitmap->bitzero, bitmap->bitmax);
+    int stream_len = hdrlen(name) + (INT32SIZE_B64 * 2) + 
+                    streamlen(sizeof(int32) * elems) + 1;
+   char *stream = palloc(stream_len * sizeof(char));
+   char *streamstart = stream;
+
+   serialise_char(&stream, BITMAP_HDR);
+   serialise_name(&stream, name);
+   serialise_one_bitmap(&stream, bitmap);
+   return streamstart;
+}
+
+/** 
+ * De-serialise a single bitmap into a veil bitmap array or bitmap hash.
+ *
+ * @param p_bitmap Pointer to bitmap pointer.  This may be updated to
+ * contain a dynamically allocated bitmap if none is already present.
+ * @param name  The name of the variable, for error reporting purposes.
+ * @param shared Whether the bitmap is part of a shared rather than
+ * session variable.
+ * @param p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream.
+ */
+static void
+deserialise_one_bitmap(Bitmap **p_bitmap, char *name, 
+                      bool shared, char **p_stream)
+{
+   Bitmap *bitmap = *p_bitmap;
+    int4 bitzero;
+   int4 bitmax;
+   int4 elems;
+
+   bitzero = deserialise_int4(p_stream);
+   bitmax = deserialise_int4(p_stream);
+   elems = ARRAYELEMS(bitzero, bitmax);
+
+    if (bitmap) {
+        if (bitmap->type != OBJ_BITMAP) {
+            vl_type_mismatch(name, OBJ_BITMAP, bitmap->type);
+        }
+    }
+   /* Check size and re-allocate memory if needed */
+   vl_NewBitmap(p_bitmap, shared, bitzero, bitmax);
+   bitmap = *p_bitmap;
+
+   deserialise_stream(p_stream, elems * sizeof(bitmap->bitset[0]), 
+                      (char *) &(bitmap->bitset[0]));
+
+}
+
+/** 
+ * De-serialise a veil bitmap variable.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return Pointer to the variable created or updated from the stream.
+ */
+static VarEntry *
+deserialise_bitmap(char **p_stream)
+{
+   char *name = deserialise_name(p_stream);
+   VarEntry *var = vl_lookup_variable(name);
+   Bitmap *bitmap = (Bitmap *) var->obj;
+
+   deserialise_one_bitmap(&bitmap, name, var->shared, p_stream);
+   var->obj = (Object *) bitmap;
+   return var;
+}
+
+/** 
+ * Serialise a veil bitmap array variable into a dynamically allocated
+ * string.
+ *
+ * @param bmarray Pointer to the variable to be serialised
+ * @param name The name of the variable
+ * @return Dynamically allocated string containing the serialised
+ * variable
+ */
+static char *
+serialise_bitmap_array(BitmapArray *bmarray, char *name)
+{
+    int bitset_elems = ARRAYELEMS(bmarray->bitzero, bmarray->bitmax);
+    int array_elems = 1 + bmarray->arraymax - bmarray->arrayzero;
+   int bitmap_len = (INT32SIZE_B64 * 2) + 
+                    streamlen(sizeof(int32) * bitset_elems);
+    int stream_len = hdrlen(name) + (INT32SIZE_B64 * 4) + 
+                    (bitmap_len * array_elems) + 1;
+   int idx;
+   char *stream = palloc(stream_len * sizeof(char));
+   char *streamstart = stream;
+
+   serialise_char(&stream, BITMAP_ARRAY_HDR);
+   serialise_name(&stream, name);
+   serialise_int4(&stream, bmarray->bitzero);
+   serialise_int4(&stream, bmarray->bitmax);
+   serialise_int4(&stream, bmarray->arrayzero);
+   serialise_int4(&stream, bmarray->arraymax);
+   for (idx = 0; idx < array_elems; idx++) {
+       serialise_one_bitmap(&stream, bmarray->bitmap[idx]);
+   }
+   return streamstart;
+}
+
+/** 
+ * De-serialise a veil bitmap array variable.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return Pointer to the variable created or updated from the stream.
+ */
+static VarEntry *
+deserialise_bitmap_array(char **p_stream)
+{
+   char *name = deserialise_name(p_stream);
+    int4 bitzero;
+   int4 bitmax;
+    int4 arrayzero;
+   int4 arraymax;
+    int4 array_elems;
+    int4 idx;
+   VarEntry *var = vl_lookup_variable(name);
+   BitmapArray *bmarray = (BitmapArray *) var->obj;
+
+   bitzero = deserialise_int4(p_stream);
+   bitmax = deserialise_int4(p_stream);
+   arrayzero = deserialise_int4(p_stream);
+   arraymax = deserialise_int4(p_stream);
+
+    if (bmarray) {
+        if (bmarray->type != OBJ_BITMAP_ARRAY) {
+            vl_type_mismatch(name, OBJ_BITMAP_ARRAY, bmarray->type);
+        }
+    }
+   /* Check size and re-allocate memory if needed */
+   vl_NewBitmapArray(&bmarray, var->shared, arrayzero, 
+                     arraymax, bitzero, bitmax);
+   var->obj = (Object *) bmarray;
+
+    array_elems = 1 + arraymax - arrayzero;
+   for (idx = 0; idx < array_elems; idx++) {
+       deserialise_one_bitmap(&(bmarray->bitmap[idx]), "", 
+                              var->shared, p_stream);
+       
+   }
+   return var;
+}
+
+
+/** 
+ * Calculate the size needed for a base64 stream to contain all of the
+ * bitmaps in a bitmap hash including their keys.
+ *
+ * @param bmhash Pointer to the variable to be serialised
+ * @param bitset_size The size, in bytes, of each bitset in the bitmap
+ * hash.
+ * @return Number of bytes required to contain all of the bitmaps and
+ * keys in the bitmap_hash
+ */
+static int
+sizeof_bitmaps_in_hash(BitmapHash *bmhash, int bitset_size)
+{
+   VarEntry *var = NULL;
+   int size = 1;  /* Allow for final end of stream indicator */
+   while ((var = vl_NextHashEntry(bmhash->hash, var))) {
+       /* 1 byte below for record/end flag to precede each bitmap in
+        * the hash */
+       size += 1 + bitset_size + hdrlen(var->key); 
+   }
+   return size;
+}
+
+
+/** 
+ * Serialise a veil bitmap hash variable into a dynamically allocated
+ * string.
+ *
+ * @param bmhash Pointer to the variable to be serialised
+ * @param name The name of the variable
+ * @return Dynamically allocated string containing the serialised
+ * variable
+ */
+static char *
+serialise_bitmap_hash(BitmapHash *bmhash, char *name)
+{
+    int bitset_elems = ARRAYELEMS(bmhash->bitzero, bmhash->bitmax);
+    int bitset_size = (INT32SIZE_B64 * 2) + 
+                     streamlen(sizeof(int32) * bitset_elems);
+   int all_bitmaps_size = sizeof_bitmaps_in_hash(bmhash, bitset_size);
+    int stream_len = hdrlen(name) + (INT32SIZE_B64 * 2) + 
+                    all_bitmaps_size + 1;
+   char *stream = palloc(stream_len * sizeof(char));
+   char *streamstart = stream;
+   VarEntry *var = NULL;
+
+   serialise_char(&stream, BITMAP_HASH_HDR);
+   serialise_name(&stream, name);
+   serialise_int4(&stream, bmhash->bitzero);
+   serialise_int4(&stream, bmhash->bitmax);
+   while ((var = vl_NextHashEntry(bmhash->hash, var))) {
+       serialise_char(&stream, BITMAP_HASH_MORE);
+       serialise_name(&stream, var->key);
+       serialise_one_bitmap(&stream, (Bitmap *) var->obj);
+   }
+   serialise_char(&stream, BITMAP_HASH_DONE);
+
+   return streamstart;
+}
+
+/** 
+ * De-serialise a veil bitmap hash variable.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return Pointer to the variable created or updated from the stream.
+ */
+static VarEntry *
+deserialise_bitmap_hash(char **p_stream)
+{
+   char *name = deserialise_name(p_stream);
+   char *hashkey;
+    int4 bitzero;
+   int4 bitmax;
+   VarEntry *var = vl_lookup_variable(name);
+   BitmapHash *bmhash = (BitmapHash *) var->obj;
+   Bitmap *tmp_bitmap = NULL;
+   Bitmap *bitmap;
+
+   bitzero = deserialise_int4(p_stream);
+   bitmax = deserialise_int4(p_stream);
+
+    if (bmhash) {
+        if (bmhash->type != OBJ_BITMAP_HASH) {
+            vl_type_mismatch(name, OBJ_BITMAP_HASH, bmhash->type);
+        }
+    }
+   /* Check size and re-allocate memory if needed */
+   vl_NewBitmapHash(&bmhash, name, bitzero, bitmax);
+   var->obj = (Object *) bmhash;
+
+   while (deserialise_char(p_stream) == BITMAP_HASH_MORE) {
+       hashkey = deserialise_name(p_stream);
+       deserialise_one_bitmap(&tmp_bitmap, "", var->shared, p_stream);
+       /* tmp_bitmap now contains a (dynamically allocated) bitmap
+        * Now we want to copy that into the bmhash.  We don't worry
+        * about memory leaks here since this is allocated only once
+        * per call of this function, and the memory context will
+        * eventually be freed anyway.
+        */
+       bitmap = vl_AddBitmapToHash(bmhash, hashkey);
+       vl_BitmapUnion(bitmap, tmp_bitmap);
+   }
+   return var;
+}
+
+/** 
+ * Serialise a veil variable
+ *
+ * @param name  The name of the variable to be serialised.
+ * @return Dynamically allocated string containing the serialised value.
+ */
+extern char *
+vl_serialise_var(char *name)
+{
+   VarEntry *var;
+   char *result = NULL;
+
+   var = vl_lookup_variable(name);
+   if (var && var->obj) {
+       switch (var->obj->type) {
+           case OBJ_INT4:
+               result = serialise_int4var((Int4Var *)var->obj, name);
+               break;
+           case OBJ_INT4_ARRAY:
+               result = serialise_int4array((Int4Array *)var->obj, name);
+               break;
+           case OBJ_RANGE:
+               result = serialise_range((Range *)var->obj, name);
+               break;
+           case OBJ_BITMAP:
+               result = serialise_bitmap((Bitmap *)var->obj, name);
+               break;
+           case OBJ_BITMAP_ARRAY:
+               result = serialise_bitmap_array((BitmapArray *)var->obj, name);
+               break;
+           case OBJ_BITMAP_HASH:
+               result = serialise_bitmap_hash((BitmapHash *)var->obj, name);
+               break;
+           default:
+               ereport(ERROR,
+                       (errcode(ERRCODE_INTERNAL_ERROR),
+                        errmsg("Unsupported type for variable serialisation"),
+                        errdetail("Cannot serialise objects of type %d.", 
+                                  (int4) var->obj->type)));
+               
+       }
+   }
+   return result;
+}
+
+/** 
+ * De-serialise the next veil variable from *p_stream
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * pointer is updated to point to the next free slot in the stream after
+ * reading the stream
+ * @return The deserialised variable.
+ */
+extern VarEntry *
+vl_deserialise_next(char **p_stream)
+{
+   VarEntry *var = NULL;
+   if ((**p_stream) != '\0') {
+       char type = deserialise_char(p_stream);
+       switch (type){
+           case INT4VAR_HDR: var = deserialise_int4var(p_stream);
+               break;
+           case INT4_ARRAY_HDR: var = deserialise_int4array(p_stream);
+               break;
+           case RANGE_HDR: var = deserialise_range(p_stream);
+               break;
+           case BITMAP_HDR: var = deserialise_bitmap(p_stream);
+               break;
+           case BITMAP_ARRAY_HDR: var = deserialise_bitmap_array(p_stream);
+               break;
+           case BITMAP_HASH_HDR: var = deserialise_bitmap_hash(p_stream);
+               break;
+           default:
+               ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("Unsupported type for variable deserialisation"),
+                    errdetail("Cannot deserialise objects of type %c.", 
+                              type)));
+               
+       }
+   }
+   return var;
+}
+
+/** 
+ * De-serialise a base64 string containing, possibly many, derialised
+ * veil variables.
+ *
+ * @param **p_stream Pointer into the stream currently being read.
+ * @return A count of the number of variables that have been de-serialised.
+ */
+extern int4
+vl_deserialise(char **p_stream)
+{
+   int count = 0;
+   while ((**p_stream) != '\0') {
+       (void) vl_deserialise_next(p_stream);
+       count++;
+   }
+   return count;
+}
diff --git a/src/veil_serialise.d b/src/veil_serialise.d
new file mode 100644 (file)
index 0000000..232592e
--- /dev/null
@@ -0,0 +1,5 @@
+src/veil_serialise.o src/veil_serialise.d: \
+  src/veil_serialise.c \
+  src/veil_version.h \
+  src/veil_funcs.h \
+  src/veil_datatypes.h
diff --git a/src/veil_shmem.c b/src/veil_shmem.c
new file mode 100644 (file)
index 0000000..89ed412
--- /dev/null
@@ -0,0 +1,710 @@
+/**
+ * @file   veil_shmem.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ *
+ * \endcode
+ * @brief  
+ * Functions for dealing with veil shared memory.
+ *
+ * This provides dynamic memory allocation, like malloc, from chunks of
+ * shared memory allocated from the Postgres shared memory pool.  In
+ * order to be able to reset and reload shared memory structures while
+ * other backends continue to use the existing structures, a shared
+ * memory reset creates a new context, or switches to an existing one
+ * that is no longer in use.  No more than two separate contexts will be
+ * created.
+ *
+ * Each context of veil shared memory is associated with a shared hash,
+ * which is used to store veil's shared variables.  A specially named
+ * variable, VEIL_SHMEMCTL appears only in context0 and contains a
+ * reference to chunk0, and the ShmemCtl structure.  From this structure
+ * we can identify the current context, the initial chunks for each
+ * active context, and whether a context switch is in progress. 
+ * 
+ * A context switch takes place in 3 steps:
+ * -  preparation, in which we determine if a context switch is allowed,
+ *    initialise the new context and record the fact that we are in the
+ *    process of switching.  All subsequent operations in the current
+ *    backend will work in the new context, while other backends will
+ *    continue to use the original context
+ * -  initialisation of the new context, variables, etc.  This is done
+ *    by the user-space function veil_init().
+ * -  switchover, when all other processes gain access to the newly
+ *    initialised context.  They may continue to use the previous
+ *    context for the duration of their current transactions.
+ *
+ * To access shared variable "x" in a new session, the following steps
+ * are taken:
+ *  - We access the hash "VEIL_SHARED1_nnn" (where nnn is the oid of our
+ *    database).  This gives us a reference to the ShmemCtl structure.
+ *    We record hash0 and shared_meminfo on the way.
+ *  - We access ShemCtl to identify the current hash and current
+ *    context. 
+ *  - We look up variable "x" in the current hash, and if we have to
+ *    allocate space for it, allocate it from the current context.
+ *
+ * Note that We use a dynamicall allocated LWLock, VeilLWLock to protect
+ * our shared control structures.
+ * 
+ */
+
+#include "postgres.h"
+#include "utils/hsearch.h"
+#include "storage/pg_shmem.h"
+#include "storage/shmem.h"
+#include "storage/lwlock.h"
+#include "storage/procarray.h"
+#include "access/xact.h"
+#include "access/transam.h"
+#include "miscadmin.h"
+#include "veil_version.h"
+#include "veil_shmem.h"
+#include "veil_funcs.h"
+
+/**
+ * shared_meminfo provides access to the ShmemCtl structure allocated in
+ * context 0.
+ */
+static ShmemCtl *shared_meminfo = NULL;
+
+/**
+ * Whether the current backend is in the process of switching contexts.
+ * If so, it will be setting up the non-current context in readiness for
+ * making it available to all other backends.
+ */
+static bool      prepared_for_switch = false;
+
+/**
+ * The LWLock that Veil will use for managing concurrent access to
+ * shared memory.  It is initialised to a lock id that is distinct
+ * from any tha twill be dynamically allocated.
+ */
+static LWLockId  VeilLWLock = AddinShmemInitLock;
+
+/**
+ * The LWLock to be used while initially setting up shared memory and 
+ * allocating a veil database-specific LWLock.
+ */
+static LWLockId  InitialLWLock = AddinShmemInitLock;
+
+/** 
+ * Return the index of the other context from the one supplied.
+ * 
+ * @param x the context for which we want the other one.
+ * 
+ * @return the opposite context to that of x.
+ */
+#define OTHER_CONTEXT(x)   (x ? 0: 1)
+
+/** 
+ * Veil's startup function.  This should be run when the Veil shared
+ * library is loaded by postgres.
+ * 
+ * If shared_preload_libraries is not defined, Veil may still be run but
+ * it will steal shared memory from postgres, potentially exhausting it.
+ * 
+ */
+void
+_PG_init()
+{
+   int veil_dbs;
+
+   /* Define GUCs for veil */
+   veil_config_init(); 
+   veil_dbs = veil_dbs_in_cluster();
+
+   /* Request a Veil-specific shared memory context */
+   RequestAddinShmemSpace(2 * veil_shmem_context_size() * veil_dbs);
+
+   /* Request a LWLock for later use by all backends */
+   RequestAddinLWLocks(veil_dbs);
+}
+
+/** 
+ * Create/attach to the shared hash identified by hashname.  Return a
+ * pointer to an HTAB that references the shared hash.  All locking is
+ * handled by the caller.
+ * 
+ * @param hashname 
+ * 
+ * @return Pointer to HTAB referencing the shared hash.
+ */
+static HTAB *
+create_shared_hash(const char *hashname)
+{
+   HASHCTL  hashctl;
+   HTAB    *result;
+   char    *db_hashname;
+   int      hash_elems = veil_shared_hash_elems();
+
+   /* Add the current database oid into the hashname so that it is
+    * distinct from the shared hash for other databases in the
+    * cluster. */
+   db_hashname = (char *) vl_malloc(HASH_KEYLEN);
+   (void) snprintf(db_hashname, HASH_KEYLEN - 1, "%s_%u", 
+                   hashname, MyDatabaseId);
+   hashctl.keysize = HASH_KEYLEN;
+   hashctl.entrysize = sizeof(VarEntry);
+
+   result = ShmemInitHash(db_hashname, hash_elems,
+                          hash_elems, &hashctl, HASH_ELEM);
+   pfree(db_hashname);
+   return result;
+}
+
+/** 
+ * Return reference to the HTAB for the shared hash associated with
+ * context 0.
+ * 
+ * @return Pointer to HTAB referencing shared hash for context 0.
+ */
+static HTAB *
+get_hash0()
+{
+   static HTAB *hash0 = NULL;
+
+    if (!hash0) {
+       hash0 = create_shared_hash("VEIL_SHARED1");
+   }
+   return hash0;
+}
+
+/** 
+ * Return reference to the HTAB for the shared hash associated with
+ * context 1.
+ * 
+ * @return Pointer to HTAB referencing shared hash for context 1.
+ */
+static HTAB *
+get_hash1()
+{
+   static HTAB *hash1 = NULL;
+
+    if (!hash1) {
+       hash1 = create_shared_hash("VEIL_SHARED2");
+   }
+
+   return hash1;
+}
+
+
+/** 
+ * Allocate or attach to, a new chunk of shared memory for a named
+ * memory context.
+ * 
+ * @param name The name
+ * @param size The size of the shared memory chunk to be allocated.
+ * @param p_found Pointer to boolean that will identify whether this
+ * chunk has already been initialised.
+ * 
+ * @return Pointer to chunk of shared memory.
+ */
+static MemContext *
+get_shmem_context(char   *name,
+                 size_t  size,
+                 bool   *p_found)
+{
+   int         i;
+   MemContext *context;
+   char       *uniqname  = (char *) vl_malloc(strlen(name) + 16);
+   int         max_dbs = veil_dbs_in_cluster();
+
+   for (i = 0; i < max_dbs; i++) {
+       (void) sprintf(uniqname, "%s_%d", name, i);
+       context = ShmemInitStruct(uniqname, size, p_found);;
+       if (!context) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("veil: cannot allocate shared memory(1)")));
+       }
+
+       if (*p_found) {
+           /* Already exists.  Check database id. */
+           if (context->db_id == MyDatabaseId) {
+               /* This context is the one for the current database, 
+                * nothing else to do. */
+               return context;
+           }
+       }
+       else {
+           /* We Just allocated our first context */
+           context->db_id = MyDatabaseId;
+           context->next = sizeof(MemContext);
+           context->limit = size;
+           context->lwlock = VeilLWLock;
+           return context;
+       }
+   }
+
+   /* We reach this point if no existing contexts are allocated to our
+    * database.  Now we check those existing contexts to see whether
+    * they are still in use.  If not, we will redeploy them. */
+
+   for (i = 0; i < max_dbs; i++) {
+       (void) sprintf(uniqname, "%s_%d", name, i);
+       context = ShmemInitStruct(uniqname, size, p_found);;
+
+       if (!context) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("veil: cannot allocate shared memory(2)")));
+       }
+
+       if (*p_found) {
+           /* Is this context for a still existant database? */
+           if (!vl_db_exists(context->db_id)) {
+               /* We can re-use this context. */
+               context->db_id = MyDatabaseId;
+               context->next = sizeof(MemContext);
+               context->limit = size;
+
+               *p_found = false;  /* Tell the caller that init is
+                                   * required */
+               return context;
+           }
+       }
+       else {
+           /* We didn't find an unused context, so now we have created 
+            * a new one. */
+
+           context->db_id = MyDatabaseId;
+           context->next = sizeof(MemContext);
+           context->limit = size;
+           return context;
+       }
+   }
+   ereport(ERROR,
+           (errcode(ERRCODE_INTERNAL_ERROR),
+            errmsg("veil: no more shared memory contexts allowed")));
+   return NULL;
+}
+
+/* Forward ref, required by next function. */
+static void shmalloc_init(void);
+
+/** 
+ * Return the id (index) of the current context for this session 
+ * 
+ * @return The current context id
+ */
+static int
+get_cur_context_id()
+{
+   static bool initialised = false;
+   int context;
+
+   if (!initialised) {
+       shmalloc_init();
+       initialised = true;
+   }
+       
+   context = shared_meminfo->current_context;
+   if (prepared_for_switch) {
+       context = OTHER_CONTEXT(context);
+   }
+   else {
+       /* Check for the default context being for a later transaction
+        * than current and, if so, use the other one. */
+       if (TransactionIdPrecedes(GetCurrentTransactionId(), 
+                                 shared_meminfo->xid[context]))
+       {
+           context = OTHER_CONTEXT(context);
+       }
+   }
+
+    return context;
+}
+
+/** 
+ * Return pointer to shared memory allocated for the current context.
+ * 
+ * @return The current context. 
+ */
+static MemContext *
+get_cur_context()
+{
+    int context;
+   context = get_cur_context_id();
+    return shared_meminfo->context[context];
+}
+
+/** 
+ * Dynamically allocate a piece of shared memory from the current
+ * context, doing no locking.
+ * 
+ * @param context The context in which we are operating
+ * @param size The size of the requested piece of memory.
+ * 
+ * @return Pointer to dynamically allocated memory.
+ */
+static void *
+do_vl_shmalloc(MemContext *context,
+              size_t size)
+{
+   void *result = NULL;
+   size_t amount = (size_t) MAXALIGN(size);
+
+   if ((amount + context->next) <= context->limit) {
+       result = (void *) ((char *) context + context->next);
+       context->next += amount;
+   }
+   else {
+       ereport(ERROR,
+               (ERROR,
+                (errcode(ERRCODE_INTERNAL_ERROR),
+                 errmsg("veil: out of shared memory"))));
+   }
+   return result;
+}
+
+/** 
+ * Dynamically allocate a piece of shared memory from the current context. 
+ * 
+ * @param size The size of the requested piece of memory.
+ * 
+ * @return Pointer to dynamically allocated memory.
+ */
+void *
+vl_shmalloc(size_t size)
+{
+   MemContext *context;
+   void       *result;
+
+   context = get_cur_context();
+
+   LWLockAcquire(VeilLWLock, LW_EXCLUSIVE);
+   result = do_vl_shmalloc(context, size);
+   LWLockRelease(VeilLWLock);
+
+   return result;
+}
+
+/** 
+ * Free a piece of shared memory within the current context.  Currently
+ * this does nothing as implementation of freeing of shared memory has
+ * been deferred.
+ * 
+ * @param mem Pointer to the memory to be freed.
+ * 
+ */
+void
+vl_free(void *mem)
+{
+   return;
+}
+
+
+/** 
+ * Attach to, creating and initialising as necessary, the shared memory
+ * control structure.  Record this for the session in shared_meminfo.
+ */
+static void
+shmalloc_init(void)
+{
+   if (!shared_meminfo) {
+       VarEntry   *var;
+       MemContext *context0;
+       MemContext *context1;
+       bool        found = false;
+       HTAB       *hash0;
+       HTAB       *hash1;
+       size_t      size;
+
+       size = veil_shmem_context_size();
+
+       LWLockAcquire(InitialLWLock, LW_EXCLUSIVE);
+       context0 = get_shmem_context("VEIL_SHMEM0", size, &found);
+
+       if (found && context0->memctl) {
+           shared_meminfo = context0->memctl;
+           VeilLWLock = shared_meminfo->veil_lwlock;
+           /* By aquiring and releasing this lock, we ensure that Veil
+            * shared memory has been fully initialised, by a process
+            * following the else clause of this code path. */
+           LWLockAcquire(VeilLWLock, LW_EXCLUSIVE);
+           LWLockRelease(InitialLWLock);
+           LWLockRelease(VeilLWLock);
+       }
+       else {
+           /* Do minimum amount of initialisation while holding
+            * the initial lock.  We don't want to do anything that
+            * may cause other locks to be aquired as this could lead
+            * to deadlock with other add-ins.  Instead, we aquire the
+            * Veil-specific lock before finishing the initialisation. */
+
+           shared_meminfo = do_vl_shmalloc(context0, sizeof(ShmemCtl));
+
+           if (context0->lwlock != InitialLWLock) {
+               /* Re-use the LWLock previously allocated to this memory 
+                * context */
+               VeilLWLock = context0->lwlock;
+           }
+           else {
+               /* Allocate new LWLock for this new shared memory
+                * context */
+               VeilLWLock = LWLockAssign(); 
+           }
+           /* Record the lock id in context0 (for possible re-use if
+            * the current database is dropped and a new veil-using
+            * database created), and in the shared_meminfo struct */
+           context0->lwlock = VeilLWLock;
+           shared_meminfo->veil_lwlock = VeilLWLock;
+           
+           /* Exchange the initial lock for our Veil-specific one. */
+           LWLockAcquire(VeilLWLock, LW_EXCLUSIVE);
+           LWLockRelease(InitialLWLock);
+   
+           /* Now do the rest of the Veil shared memory initialisation */
+
+           /* Set up the other memory context */
+           context1 = get_shmem_context("VEIL_SHMEM1", size, &found);
+           
+           /* Record location of shmemctl structure in each context */
+           context0->memctl = shared_meminfo;
+           context1->memctl = shared_meminfo;
+
+           /* Finish initialising the shmemctl structure */
+           shared_meminfo->type = OBJ_SHMEMCTL;
+           shared_meminfo->current_context = 0;
+           shared_meminfo->total_allocated[0] = size;
+           shared_meminfo->total_allocated[1] = size;
+           shared_meminfo->switching = false;
+           shared_meminfo->context[0] = context0;
+           shared_meminfo->context[1] = context1;
+           shared_meminfo->xid[0] = GetCurrentTransactionId();
+           shared_meminfo->xid[1] = shared_meminfo->xid[0];
+           shared_meminfo->initialised = true;
+
+           /* Set up both shared hashes */
+           hash0 = get_hash0();
+           hash1 = get_hash1();
+
+           /* Record the shmemctl structure in hash0 */
+           var = (VarEntry *) hash_search(hash0, (void *) "VEIL_SHMEMCTL",
+                                          HASH_ENTER, &found);
+
+           var->obj = (Object *) shared_meminfo;
+           var->shared = true;
+
+           var = (VarEntry *) hash_search(hash0, (void *) "VEIL_SHMEMCTL",
+                                          HASH_ENTER, &found);
+
+           LWLockRelease(VeilLWLock);
+       }
+   }
+}
+
+/** 
+ * Return the shared hash for the current context.
+ * 
+ * @return Pointer to the HTAB for the current context's shared hash.
+ */
+HTAB *
+vl_get_shared_hash()
+{
+   int context;
+   HTAB *hash;
+   static bool initialised = false;
+
+   if (!initialised) {
+       (void) get_cur_context();  /* Ensure shared memory is set up. */
+       initialised = true;
+   }
+
+   context = get_cur_context_id();
+
+   if (context == 0) {
+       hash = get_hash0();
+   }
+   else {
+       hash = get_hash1();
+   }
+   
+   return hash;
+}
+
+/** 
+ * Reset one of the shared hashes.  This is one of the final steps in a
+ * context switch.
+ * 
+ * @return hash The shared hash that is to be reset.
+ */
+static void
+clear_hash(HTAB *hash)
+{
+   static HASH_SEQ_STATUS status;
+   VarEntry *var;
+
+   hash_seq_init(&status, hash);
+   while ((var = hash_seq_search(&status))) {
+       if (strncmp("VEIL_SHMEMCTL", var->key, strlen("VEIL_SHMEMCTL")) != 0) {
+           (void) hash_search(hash, var->key, HASH_REMOVE, NULL);
+       }
+   }
+}
+
+/** 
+ * Prepare for a switch to the alternate context.  Switching will
+ * only be allowed if there are no transactions that may still be using
+ * the context to which we are switching, and there is no other
+ * process attempting the switch.
+ * 
+ * @return true if the switch preparation was successful.
+ */
+bool
+vl_prepare_context_switch()
+{
+   int   context_curidx;
+   int   context_newidx;
+   HTAB *hash0 = get_hash0(); /* We must not attempt to create hashes
+                               * on the fly below as they also acquire
+                               * the lock */
+   HTAB *hash1 = get_hash1(); 
+   TransactionId oldest_xid;
+   MemContext *context;
+
+   (void) get_cur_context();  /* Ensure shared memory is set up */
+
+   LWLockAcquire(VeilLWLock, LW_EXCLUSIVE);
+
+   if (shared_meminfo->switching) {
+       /* Another process is performing the switch */
+       LWLockRelease(VeilLWLock);
+       return false;
+   }
+
+   shared_meminfo->switching = true;
+
+   /* We have claimed the switch.  If we decide that we cannot proceed,
+    * we will return it to its previous state. */
+
+   context_curidx = shared_meminfo->current_context;
+   context_newidx = OTHER_CONTEXT(context_curidx);
+
+   /* In case the alternate context has been used before, we must
+    * clear it. */
+
+   oldest_xid = GetOldestXmin(false, true);
+   if (TransactionIdPrecedes(oldest_xid, 
+                             shared_meminfo->xid[context_curidx])) 
+   {
+       /* There is a transaction running that precedes the time of
+        * the last context switch.  That transaction may still be
+        * using the chunk to which we wish to switch.  We cannot
+        * allow the switch. */
+       shared_meminfo->switching = false;
+       LWLockRelease(VeilLWLock);
+       return false;
+   }
+   else {
+       /* It looks like we can safely make the switch.  Reset the
+        * new context, and make it the current context for this
+        * session only. */
+       context = shared_meminfo->context[context_newidx];
+       context->next = sizeof(MemContext);
+
+       /* If we are switching to context 0, reset the next field of
+        * the first chunk to leave space for the ShmemCtl struct. */
+       if (context_newidx == 0) {
+           context->next += sizeof(ShmemCtl);
+           clear_hash(hash0);
+       }
+       else {
+           clear_hash(hash1);
+       }
+   }
+
+   LWLockRelease(VeilLWLock);
+   prepared_for_switch = true;
+   return true;
+}
+
+/** 
+ * Complete the context switch started by vl_prepare_context_switch().
+ * Raise an ERROR if the context switch cannot be completed.
+ * 
+ * @return true if the context switch is successfully completed.
+ */
+bool
+vl_complete_context_switch()
+{
+   int  context_curidx;
+   int  context_newidx;
+
+    if (!prepared_for_switch) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("failed to complete context switch"),
+                errdetail("Not prepared for switch - "
+                          "invalid state for operation")));
+   }
+
+   LWLockAcquire(VeilLWLock, LW_EXCLUSIVE);
+   context_curidx = shared_meminfo->current_context;
+   context_newidx = OTHER_CONTEXT(context_curidx);
+
+   if (!shared_meminfo->switching) {
+       /* We do not claim to be switching.  We should. */
+       LWLockRelease(VeilLWLock);
+
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("failed to complete context switch"),
+                errdetail("Session does not have switching set to true- "
+                          "invalid state for operation")));
+   }
+
+   shared_meminfo->switching = false;
+   shared_meminfo->current_context = context_newidx;
+   shared_meminfo->xid[context_newidx] = GetCurrentTransactionId();
+   LWLockRelease(VeilLWLock);
+   prepared_for_switch = false;
+   return true;
+}
+
+/** 
+ * In desparation, if we are unable to complete a context switch, we
+ * should use this function.
+ */
+void
+vl_force_context_switch()
+{
+   int  context_curidx;
+   int  context_newidx;
+   MemContext *context;
+   HTAB *hash0 = get_hash0();
+   HTAB *hash1 = get_hash1();
+
+   (void) get_cur_context();
+
+   LWLockAcquire(VeilLWLock, LW_EXCLUSIVE);
+
+   context_curidx = shared_meminfo->current_context;
+   context_newidx = OTHER_CONTEXT(context_curidx);
+
+   /* Clear the alternate context. */
+
+   context = shared_meminfo->context[context_newidx];
+   context->next = sizeof(MemContext);
+   
+   /* If we are switching to context 0, reset the next field of
+    * the first chunk to leave space for the ShmemCtl struct. */
+   if (context_newidx == 0) {
+       context->next += sizeof(ShmemCtl);
+       clear_hash(hash0);
+   }
+   else {
+       clear_hash(hash1);
+   }
+   
+   shared_meminfo->switching = false;
+   shared_meminfo->current_context = context_newidx;
+   shared_meminfo->xid[context_newidx] = GetCurrentTransactionId();
+   shared_meminfo->xid[0] = GetCurrentTransactionId();
+   LWLockRelease(VeilLWLock);
+   prepared_for_switch = false;
+}
+
diff --git a/src/veil_shmem.d b/src/veil_shmem.d
new file mode 100644 (file)
index 0000000..5d3ab9f
--- /dev/null
@@ -0,0 +1,6 @@
+src/veil_shmem.o src/veil_shmem.d: \
+  src/veil_shmem.c \
+  src/veil_version.h \
+  src/veil_shmem.h \
+  src/veil_funcs.h \
+  src/veil_datatypes.h
diff --git a/src/veil_shmem.h b/src/veil_shmem.h
new file mode 100644 (file)
index 0000000..5ccd2df
--- /dev/null
@@ -0,0 +1,260 @@
+/**
+ * @file   veil_shmem.h
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Define the basic veil shared memory structures
+ * 
+ */
+
+#ifndef VEIL_DATATYPES
+/** Prevent multiple definitions of the contents of this file.
+ */
+#define VEIL_DATATYPES 1
+
+#include "utils/hsearch.h"
+#include "storage/lwlock.h"
+
+/**
+ * Chunks od shared memory are allocated in multiples of this size.
+ */
+#define CHUNK_SIZE 8192
+
+/**
+ * Limits the total amount of memory available for veil shared
+ * variables.
+ */
+#define MAX_ALLOWED_SHMEM CHUNK_SIZE * 100
+
+
+/** 
+ * Chunks provide a linked list of dynamically allocated shared memory
+ * segments, with the most recently allocated chunk at the tail.
+ * Shmalloc allocates space from this list of chunks, creating new
+ * chunks as needed    up to MAX_ALLOWED_SHMEM.
+ */
+typedef struct MemChunk {
+    struct MemChunk *next_chunk;  /**< Pointer to next allocated chunk */
+   size_t  next;                 /**< Offset, within this chunk, of 1st
+                                  * free byte */
+   size_t  limit;                /**< Offset, of 1st byte beyond chunk */
+    void   *memory[0];            /**< The rest of the chunk, from which
+                                  * memory is allocated */
+} MemChunk;
+
+
+/** 
+ * MemContexts are large single chunks of shared memory from which 
+ * smaller allocations may be made
+ */
+typedef struct MemContext {
+   Oid       db_id;              /**< Identifier for the database for
+                                  * which this context was created,
+                                  * or by which it has been taken
+                                  * over. */
+   LWLockId  lwlock;             /**< The LWLock associated with this
+                                  *  memory context */
+   size_t    next;               /**< Offset of 1st free byte */
+   size_t    limit;              /**< Offset, of 1st byte beyond this 
+                                  * struct */
+   
+   struct ShmemCtl *memctl;      /**< Pointer to shared memory control
+                                  * structure. */
+    void     *memory[0];          /**< The rest of the chunk, from which
+                                  * memory is allocated */
+} MemContext;
+
+
+
+/**
+ * The key length for veil hash types.
+ */
+#define HASH_KEYLEN           60
+
+
+/**
+ * Describes the type of an Object record or one of its subtypes.
+ */
+typedef enum { 
+   OBJ_UNDEFINED = 0,
+    OBJ_SHMEMCTL,
+   OBJ_INT4,
+   OBJ_RANGE,
+   OBJ_BITMAP,
+   OBJ_BITMAP_ARRAY,
+   OBJ_BITMAP_HASH,
+   OBJ_BITMAP_REF,
+   OBJ_INT4_ARRAY
+} ObjType;
+
+/** 
+ * General purpose object-type.  All veil variables are effectively
+ * sub-types of this.
+ */
+typedef struct Object {
+    ObjType type;
+} Object;
+
+/** 
+ * The ShmemCtl structure is the first object allocated from the first
+ * chunk of shared memory in context 0.  This object describes and
+ * manages shared memory allocated by shmalloc()
+ */
+typedef struct ShmemCtl {
+    ObjType type;                /**< This must have the value OBJ_SHMEMCTL */
+    bool      initialised;        /**< Set to true once struct is setup */
+    LWLockId  veil_lwlock;        /** dynamically allocated LWLock */
+   int       current_context;    /**< Index of the current context (0
+                                  * or 1) */
+    size_t    total_allocated[2]; /**< Total shared memory allocated in
+                                  * chunks in each context */ 
+    bool      switching;          /**< Whether a context-switch is in
+                                  * progress */
+   MemContext *context[2];       /**< Array (pair) of contexts */
+   TransactionId xid[2];         /**< The transaction id of the
+                                  * transaction that initialised each
+                                  * context: this is used to determine
+                                  * whether there are transactions
+                                  * still runnning that may be using an
+                                  * earlier context. */
+} ShmemCtl;
+
+/**
+ * Subtype of Object for storing simple int4 values.  These values are
+ * allowed to be null.
+ */
+typedef struct Int4Var {
+    ObjType type;      /**< This must have the value OBJ_INT4 */
+   bool    isnull;     /**< if true, the value is null */
+    int32   value;      /**< the integer value of the variable */
+} Int4Var;
+
+/**
+ * Subtype of Object for storing range values.  A range has an upper and
+ * lower bound, both stored as int4s.  Nulls are not allowed.
+ */
+typedef struct Range {
+    ObjType type;      /**< This must have the value OBJ_RANGE */
+    int32   min;
+    int32   max;
+} Range;
+
+/**
+ * Subtype of Object for storing bitmaps.  A bitmap is stored as an
+ * array of int4 values.  See veil_bitmap.c for more information.  Note
+ * that the size of a Bitmap structure is determined dynamically at run
+ * time as the size of the array is only known then.
+ */
+typedef struct Bitmap {
+    ObjType type;      /**< This must have the value OBJ_BITMAP */
+    int32   bitzero;   /**< The index of the lowest bit the bitmap can
+                        * store */
+    int32   bitmax;        /**< The index of the highest bit the bitmap can
+                        * store */
+   uint32   bitset[0]; /**< Element zero of the array of int4 values
+                        * comprising the bitmap. */
+} Bitmap;
+
+/**
+ * Subtype of Object for storing bitmap refs.  A bitmapref is like a
+ * bitmap but instead of containing a bitmap it contains a reference to
+ * one.  This reference may be set during a transaction and then
+ * referenced only from within the setting transaction.
+ */
+typedef struct BitmapRef {
+    ObjType        type;   /**< This must have the value OBJ_BITMAP_REF */
+    TransactionId  xid;        /**< The xid for which this variable is
+                            * valid */
+   Bitmap        *bitmap;
+} BitmapRef;
+
+/**
+ * Subtype of Object for storing bitmap arrays.  A bitmap array is
+ * simply an array of pointers to dynamically allocated Bitmaps.  Note
+ * that the size of a Bitmap structure is determined dynamically at run
+ * time as the size of the array is only known then.
+ */
+typedef struct BitmapArray {   // subtype of Object
+    ObjType type;      /**< This must have the value OBJ_BITMAP_ARRAY */
+    int32   bitzero;   /**< The index of the lowest bit each bitmap can
+                        * store */
+    int32   bitmax;        /**< The index of the highest bit each bitmap can
+                        * store */
+   int32   arrayzero;  /**< The index of array element zero: the
+                        * index of the lowest numbered bitmap in the
+                        * array */
+   int32   arraymax;   /**< The index of the lowest numbered bitmap in
+                        * the array */
+   Bitmap *bitmap[0];  /** Element zero of the array of Bitmap pointers
+                        * comprising the array. */
+} BitmapArray;
+
+/** 
+ * Subtype of Object for storing bitmap hashes.  A bitmap hash is a hash
+ * of dynamically allocated bitmaps, keyed by strings.  Note that these
+ * cannot be created as shared variables.
+ */
+typedef struct BitmapHash {
+    ObjType type;      /**< This must have the value OBJ_BITMAP_HASH */
+    int32   bitzero;    /**< The index of the lowest bit each bitmap can
+                        * store */
+    int32   bitmax;     /**< The index of the highest bit each bitmap can
+                        * store */
+   HTAB   *hash;       /**< Pointer to the (Postgresql dynahash) hash
+                        * table */ 
+} BitmapHash;
+
+
+/** 
+ * Subtype of Object for storing arrays of integers.
+ */
+typedef struct Int4Array {
+    ObjType type;      /**< This must have the value OBJ_INT4_ARRAY */
+    int32   arrayzero;  /**< The index of array element zero: the
+                        * index of the lowest numbered bitmap in the
+                        * array */
+    int32   arraymax;   /**< The index of the lowest numbered bitmap in
+                        * the array */
+   int32   array[0];   /** Element zero of the array of integers */
+} Int4Array;
+
+
+/**
+ * A Veil variable.  These may be session or shared variables, and may
+ * contain any Veil variable type.  They are created and accessed by
+ * vl_lookup_shared_variable() and vl_lookup_variable(), and are stored
+ * in either the shared hash or one of the session hashes.  See
+ * veil_shmem.c and veil_variables.c for more details.
+ */
+typedef struct VarEntry {
+    char  key[HASH_KEYLEN];    /**< String containing variable name */
+   bool  shared;           /**< Whether this is a shared variable */
+    Object *obj;            /**< Pointer to the contents of the variable */
+} VarEntry;
+
+
+/**
+ * Describes a veil shared or session variable.  This matches the SQL
+ * veil_variable_t which is defined as:
+\verbatim
+create type veil_variable_t as (
+    name    text,
+    type    text,
+    shared  bool,
+);
+\endverbatim
+ */
+typedef struct veil_variable_t {
+    char *name;            /**< The name of the variable */
+    char *type;            /**< The type of the variable (eg "Bitmap") */
+    bool shared;       /**< Whether this is a shared variable (as
+                          opposed to a session variable) */
+} veil_variable_t;
+
+
+#endif
+
diff --git a/src/veil_utils.c b/src/veil_utils.c
new file mode 100644 (file)
index 0000000..a1c9c07
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * @file   veil_utils.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Miscelaneous functions for veil
+ * 
+ */
+
+
+#include "postgres.h"
+#include "utils/memutils.h"
+#include "veil_funcs.h"
+#include "veil_datatypes.h"
+
+/** 
+ * Dynamically allocate memory using palloc in TopMemoryContext.
+ * 
+ * @param size The size of the chunk of memory being requested.
+ * 
+ * @return Pointer to the newly allocated chunk of memory
+ */
+void *
+vl_malloc(size_t size)
+{
+   void *result;
+    MemoryContext oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+   result = palloc(size);
+   (void) MemoryContextSwitchTo(oldcontext);
+   return result;
+}
+
+/** 
+ * Return a static string describing an ObjType object.
+ * 
+ * @param obj The ObjType for which we want a description.
+ * 
+ * @return Pointer to a static string describing obj.
+ */
+char *
+vl_ObjTypeName(ObjType obj)
+{
+    static char *names[] = {
+       "Undefined", "ShmemCtl", "Int4", 
+       "Range", "Bitmap", "BitmapArray", 
+       "BitmapHash", "BitmapRef", "Int4Array"
+   };
+
+   if ((obj < OBJ_UNDEFINED) ||
+       (obj > OBJ_INT4_ARRAY)) 
+   {
+       return "Unknown";
+   }
+   else {
+       return names[obj];
+   }
+}
+
diff --git a/src/veil_utils.d b/src/veil_utils.d
new file mode 100644 (file)
index 0000000..074fe42
--- /dev/null
@@ -0,0 +1,4 @@
+src/veil_utils.o src/veil_utils.d: \
+  src/veil_utils.c \
+  src/veil_funcs.h \
+  src/veil_datatypes.h
diff --git a/src/veil_variables.c b/src/veil_variables.c
new file mode 100644 (file)
index 0000000..a0dc4b1
--- /dev/null
@@ -0,0 +1,327 @@
+/**
+ * @file   veil_variables.c
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * \endcode
+ * @brief  
+ * Functions for dealing with Veil variables.
+ *
+ * Variables may be either session or shared, and are used to retain
+ * state between function calls.  Shared variables are available to all
+ * suitably privileged sessions within a database.  Session variables
+ * hold values that are private to a single session.
+ * 
+ */
+
+#include "postgres.h"
+#include "veil_datatypes.h"
+#include "utils/hsearch.h"
+#include "storage/shmem.h"
+
+#include "veil_funcs.h"
+
+/**
+ * Baselines the number of session variables that can be created in each
+ * context.
+ */
+#define SESSION_HASH_ELEMS    32
+
+/**
+ * This identifies the hash table for all session variables.  The shared
+ * variable hash tables are managed in veil_shmem.c.
+ */
+
+static HTAB *session_hash = NULL;
+
+
+/** 
+ * Create, or attach to, a hash for session variables.
+ * 
+ */
+static HTAB *
+create_session_hash()
+{
+   HASHCTL hashctl;
+
+   /* TODO: Think about creating a specific memory context for this. */
+
+   hashctl.keysize = HASH_KEYLEN;
+   hashctl.entrysize = sizeof(VarEntry);
+
+   return hash_create("VEIL_SESSION", 
+                      SESSION_HASH_ELEMS, &hashctl, HASH_ELEM);
+}
+
+/** 
+ * Define a new, or attach to an existing, shared variable.  Raise an
+ * ERROR if the variable already exists as a session variable or if we
+ * cannot create the variable due to resource limitations (out of
+ * memory, or out of space in the shared hash).
+ * 
+ * @param name The name of the variable.
+ * 
+ * @return Pointer to the shared variable.  If the variable is newly
+ * created by this call then it will be unitialised (ie it will have a
+ * NULL obj reference).
+ */
+VarEntry *
+vl_lookup_shared_variable(char *name)
+{
+   VarEntry *var;
+   HTAB     *shared_hash = vl_get_shared_hash();
+   bool      found;
+
+   if (!session_hash) {
+       session_hash = create_session_hash();
+   }
+
+   var = (VarEntry *) hash_search(session_hash, (void *) name,
+                                  HASH_FIND, &found);
+   if (found) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("attempt to redefine session variable %s", name),
+                errdetail("You are trying to create shared variable %s "
+                          "but it already exists as a session variable.",
+                          name)));
+   }
+
+   var = (VarEntry *) hash_search(shared_hash, (void *) name,
+                                  HASH_ENTER, &found);
+
+   if (!var) {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Out of memory for shared variables")));
+   }
+
+   if (!found) {
+       /* Shared variable did not already exist so we must initialise
+        * it. */
+
+       var->obj = NULL;
+       var->shared = true;
+   }
+
+   return var;
+}
+
+/** 
+ * Lookup a variable by name, creating it as as a session variable if it
+ * does not already exist.
+ * 
+ * @param name The name of the variable
+ * 
+ * @return Pointer to the shared or session variable.  If the variable
+ * is newly created by this call then it will be unitialised (ie it will
+ * have a NULL obj reference).
+ */
+VarEntry *
+vl_lookup_variable(char *name)
+{
+   VarEntry *var;
+   HTAB     *shared_hash = vl_get_shared_hash();
+   bool found;
+
+   if (!session_hash) {
+       session_hash = create_session_hash();
+   }
+
+   var = (VarEntry *)hash_search(session_hash, (void *) name,
+                                 HASH_FIND, &found);
+   if (!var) {
+       /* See whether this is a shared variable. */
+       var = (VarEntry *)hash_search(shared_hash, (void *) name,
+                                     HASH_FIND, NULL);
+   }
+
+
+   if (!var) {
+       /* Create new session variable */
+       var = (VarEntry *) hash_search(session_hash, (void *) name,
+                                      HASH_ENTER, &found);
+       if (!var) {
+           ereport(ERROR,
+                   (errcode(ERRCODE_INTERNAL_ERROR),
+                    errmsg("Out of memory for shared variables")));
+       }
+       var->obj = NULL;
+       var->shared = false;
+   }
+   return var;
+}
+
+/** 
+ * Return the next variable from a scan of the hash of variables.  Note
+ * that this function is not re-entrant.
+ * 
+ * @param prev The last variable retrieved by a scan, or NULL if
+ * starting a new scan.
+ * 
+ * @return The next variable encountered in the scan.  NULL if we have
+ * finished.
+ */
+veil_variable_t *
+vl_next_variable(veil_variable_t *prev)
+{
+   static bool doing_shared;
+   static HTAB *hash;
+   static HASH_SEQ_STATUS status;
+   static veil_variable_t result;
+   VarEntry *var;
+
+   if (!session_hash) {
+       session_hash = create_session_hash();
+   }
+
+   if (!prev) {
+       doing_shared = true;
+       /* Initialise a scan of the shared hash. */
+       hash = vl_get_shared_hash();
+
+       hash_seq_init(&status, hash);
+   }
+
+   var = hash_seq_search(&status);
+
+   if (!var) {
+       /* No more entries from that hash. */
+       if (doing_shared) {
+           /* Switch to, and get var from, the session hash. */
+           doing_shared = false;
+           hash = session_hash;
+           hash_seq_init(&status, hash);
+           var = hash_seq_search(&status);
+       }
+   }
+
+   if (var) {
+       /* Yay, we have an entry. */
+       result.name = var->key;
+       result.shared = var->shared;
+       if (var->obj) {
+           result.type = vl_ObjTypeName(var->obj->type);
+       }
+       else {
+           result.type = vl_ObjTypeName(OBJ_UNDEFINED);;
+       }
+       return &result;
+   }
+   else {
+       /* Thats all.  There are no more entries */
+       return NULL;
+   }
+}
+
+/** 
+ * Reset all Int4 entries in an ::Int4Array (to zero).
+ * 
+ * @param array The array to be reset.
+ */
+void
+vl_ClearInt4Array(Int4Array *array)
+{
+   int elems = 1 + array->arraymax - array->arrayzero;
+   int i;
+   for (i = 0; i < elems; i++) {
+       array->array[i] = 0;
+   }
+}
+
+/** 
+ * Return a newly initialised (zeroed) ::Int4Array.  It may already
+ * exist in which case it will be re-used if possible.  It may
+ * be created in either session or shared memory depending on the value
+ * of shared.
+ * 
+ * @param current Pointer to an existing Int4Array if one exists.
+ * @param shared Whether to create the variable in shared or session
+ * memory.
+ * @param min Index of the first entry in the array.
+ * @param max Index of the last entry in the array.
+ */
+Int4Array *
+vl_NewInt4Array(Int4Array *current, bool shared,
+               int32 min, int32 max)
+{
+   Int4Array *result = NULL;
+   int        elems = 1 + max - min;
+
+    if (current) {
+       int cur_elems = 1 + current->arraymax - current->arrayzero;
+       if (elems <= cur_elems) {
+           vl_ClearInt4Array(current);
+           result = current;
+       }
+       else {
+           if (!shared) {
+               /* Note that we can't free shared memory - no api to do
+                * so. */
+               pfree(current);
+           }
+       }
+   }
+   if (!result) {
+       if (shared) {
+           result = vl_shmalloc(sizeof(Int4Array) + (sizeof(int32) * elems));
+       }
+       else {
+           result = vl_malloc(sizeof(Int4Array) + (sizeof(int32) * elems));
+       }
+   }
+   result->type = OBJ_INT4_ARRAY;
+   result->arrayzero = min;
+   result->arraymax = max;
+
+   return result;
+}
+
+/** 
+ * Set an entry within an ::Int4Array.  If idx is outside of the
+ * acceptable range, raise an error.
+ * 
+ * @param array The ::Int4Array within which the entry is to be set. 
+ * @param idx The index of the entry to be set.
+ * @param value The value to which the entry will be set.
+ */
+void
+vl_Int4ArraySet(Int4Array *array, int32 idx, int32 value)
+{
+   if ((idx < array->arrayzero) ||
+       (idx > array->arraymax))
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                 errmsg("Int4ArraySet range error"),
+                errdetail("Index (%d) not in range %d..%d.  ", idx,
+                          array->arrayzero, array->arraymax)));
+   }
+   array->array[idx - array->arrayzero] = value;
+}
+
+/** 
+ * Get an entry from an ::Int4Array.  If idx is outside of the
+ * acceptable range, raise an error.
+ * 
+ * @param array The ::Int4Array within from the entry is to be read. 
+ * @param idx The index of the entry to be retrieved.
+
+ * @return The value of the entry
+ */
+int32
+vl_Int4ArrayGet(Int4Array *array, int32 idx)
+{
+   if ((idx < array->arrayzero) ||
+       (idx > array->arraymax))
+   {
+       ereport(ERROR,
+               (errcode(ERRCODE_INTERNAL_ERROR),
+                errmsg("Int4ArrayGet range error"),
+                errdetail("Index (%d) not in range %d..%d.  ", idx,
+                          array->arrayzero, array->arraymax)));
+   }
+   return array->array[idx - array->arrayzero];
+}
diff --git a/src/veil_variables.d b/src/veil_variables.d
new file mode 100644 (file)
index 0000000..09fb2d8
--- /dev/null
@@ -0,0 +1,4 @@
+src/veil_variables.o src/veil_variables.d: \
+  src/veil_variables.c \
+  src/veil_datatypes.h \
+  src/veil_funcs.h
diff --git a/src/veil_version.h b/src/veil_version.h
new file mode 100644 (file)
index 0000000..8bad207
--- /dev/null
@@ -0,0 +1,20 @@
+/**
+ * @file   veil_version.h
+ * \code
+ *     Author:       Marc Munro
+ *     Copyright (c) 2005 - 2011 Marc Munro
+ *     License:      BSD
+ * 
+ * \endcode
+ * @brief  
+ * Provides version information for veil
+ * 
+ */
+
+/** The version number for this version of veil
+ */
+#define VEIL_VERSION "1.0.0"
+/** The version number suffix, indicating stability.
+ */
+#define VEIL_VERSION_INFO "Stable"
+
diff --git a/tools/psql_funcs.sh b/tools/psql_funcs.sh
new file mode 100644 (file)
index 0000000..3aca58d
--- /dev/null
@@ -0,0 +1,216 @@
+#! /bin/sh
+# psql_funcs.sh
+#
+#      Functions for use by psql shell scripts.
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+
+# psql_collate
+# Filter test output from psql, cleaning and collating as we go.  Unfiltered
+# output, and summary output goes to logfile $1.  Result is non-zero if
+# the tests fail
+#
+psql_collate()
+{
+    tee -a $1 | awk '
+        function finish_test()
+        {
+            if (!testing) return
+
+            if ((condition == "=") ||
+                (condition == "~"))
+            {
+                ok = ok && found
+            }
+            else {
+                ok = ok && !found
+            }
+
+       if (prepfail) {
+           failed++
+       ok = 0
+       res = "PREP FAIL\n  [" preplines "\n  ]"
+       }
+            else {
+                if (ok && lines != "") # If lines is empty, then no result
+                {                      # from test, so consider it failed
+                    passed++
+                }
+                else {
+                    failed++
+                }
+       res = ok ? "PASS": "FAIL\n  [" lines "\n  ]"
+            }
+
+            printf("      %s\n", res)
+            print res >>LOGFILE
+            printf "END TEST %s\n\n", testno >>LOGFILE
+
+            found = 0
+            testing = 0
+       prepfail = 0
+        }
+        BEGIN {
+            prepping = 0  # boolean
+       prepfail = 0  # boolean
+            testing = 0   # boolean
+            complete = 0  # boolean
+            passed = 0    # counter
+            failed = 0    # counter
+            tests = 0     # counter
+            printf("\n")
+        }
+        /^-/ {
+            # Print lines starting with hyphen.  Such lines terminate any
+            # open test
+            finish_test()
+            line = $0
+            gsub(/^. ?/, "", line)
+            print line
+            next
+        }
+        /^COMPLETE/ {
+            # The line identifies completion of all tests.  If this
+            # line is not reached it may be because the pipe from psql
+            # was broken.
+            complete = 1
+        }
+        /^PREP/ {
+       # The line identifies the start of the preparation for a 
+       # test.  Lines between here and the TEST statement are
+            # ignored unless an ERROR is detected which will cause the
+       # subsequent test to fail
+            finish_test()
+       prepping = 1
+       prepfail = 0
+            ignore = $2 == "IGNORE"
+       preplines = ""
+       next
+   }
+        /^TEST/ {
+            # The line defines a test.  Format is:
+            # TEST <testno> <condition> #<pattern to test using condition>
+       prepping = 0
+            finish_test()
+            testno = $2
+            condition = $3
+            nf = split($0, fields, /#/)
+            pattern = fields[2]
+            if (nf > 2) {
+                summary = fields[3]
+            }
+            else {
+                summary = "result " condition " " pattern
+            }
+            line = sprintf("%8s: %s", testno, summary)
+            printf("%-65s", line)
+            found = 0
+            lines=""
+            testing = 1
+            ok = 1
+            tests++
+            print >>LOGFILE # All input lines go to logfile
+                            # This is done explicitly so that the output
+                            # from finish_test is logged first
+            next            # No more to do for this line
+        }
+        { print >>LOGFILE }  # All input lines go to logfile
+   (prepping) {
+       if ($0 ~ /ERROR/) {
+                if (!ignore) {
+               prepfail = 1
+                }
+       }
+       if (prepfail) {
+                if (preplines == "") {
+                    preplines = "   " $0
+                }
+                else {
+               preplines = preplines "\n   " $0
+                }
+       }
+       next
+   }
+        {   # may be a result line.  Test against condition and pattern,
+            # and accumlate the line for later output
+            sub(/^[    ]*/, "")  # Remove leading space
+            sub(/[     ]*$/, "") # Remove trailing space
+#printf("\nComparing :%s:%s:%s:\n", $0, condition, pattern)
+            if ((condition == "=") ||
+                (condition == "!="))
+            {
+                if ($0 == pattern) {
+#print "Found"
+                    found = 1
+           printf "MATCH = \"%s\"\n", $0 >>LOGFILE
+                }
+            }
+            else if ((condition == "~") ||
+                     (condition == "!~")) 
+            {
+                if ($0 ~ pattern) {
+                    found = 1
+           printf "MATCH ~ \"%s\"\n", $0 >>LOGFILE
+                }
+            }
+            if (lines == "") {
+                lines = "   " $0
+            }
+            else {
+                lines = lines "\n   " $0
+            }
+        }
+        END {
+            finish_test()
+            printf("\ntests:     %d\npassed:    %d\nfailed:    %d\n\n",
+                   tests, passed, failed)
+            printf("\ntests:     %d\npassed:    %d\nfailed:    %d\n\n",
+                   tests, passed, failed) >>LOGFILE
+            if (!complete) {
+                print "NOT ALL TESTS COMPLETED!  (Check logfile for details)"
+                print "NOT ALL TESTS COMPLETED!" >>LOGFILE
+                exit(2)
+            }
+            else {
+                if (tests != passed) {
+                    exit(2)
+                }
+            }
+        }
+    ' LOGFILE=$1
+}
+
+# psql_clean
+# Filter output from psql to clean it.  Send raw output to logfile $1
+#
+psql_clean()
+{
+    tee -a $1 | awk '
+        function do_print(str) {
+       if (continuing) {
+                printf("\n")
+            }
+            printf("%s", str)
+            continuing = 1
+        }
+        /^-/ {
+            sub(/^- ?/, "")
+            do_print($0)
+        }
+   /^INSERT/ {printf(".")}
+   /^CREATE/ {printf(".")}
+   /^DROP/   {printf(".")}
+   /^DELETE/ {printf(".")}
+   /^COMMIT/ {printf(".")}
+   /[: ]*ERROR[:   ]/ {do_print($0); errors=1}
+   /[: ]*HINT[:    ]/ {do_print($0)}
+   /[: ]*WARN[:    ]/ {do_print($0)}
+       END {
+       do_print("")
+            if (errors) exit(2)
+   }   
+    '
+}
diff --git a/veil--1.0.sql b/veil--1.0.sql
new file mode 100644 (file)
index 0000000..e1a912d
--- /dev/null
@@ -0,0 +1,743 @@
+/* ----------
+ * veil_interface.sqs (or a file derived from it)
+ *
+ *      Source file from which veil_interface_trial.sql and
+ *      veil--<version>.sql is generated using sed.
+ *
+ *      Copyright (c) 2005 - 2011 Marc Munro
+ *      Author:  Marc Munro
+ * License: BSD
+ *
+ * ----------
+ */
+
+create type veil_range_t as (
+    min  int4,
+    max  int4
+);
+comment on type veil_range_t is
+'Veil type used to record ranges.  A range is a pair of integers identifying
+the minimum and maximum values of the range.  Ranges are used to
+constrain the size of a bitmap or bitmap array.';
+
+create type veil_variable_t as (
+    name    text,
+    type    text,
+    shared  bool
+);
+comment on type veil_variable_t is
+'Veil type used as the result type of veil_variables(), to describe each
+variable known to a veil instance.';
+
+
+create or replace
+function veil_share(name text) returns bool
+     as '$libdir/veil', 'veil_share'
+     language C stable strict;
+
+comment on function veil_share(name text) is
+'Create a shared variable named NAME.
+
+Return TRUE if successful, else raise an error.
+
+Create a veil variable as a shared variable.  Variables are named
+containers for various types of values.  They are referenced by name,
+passing the name as a string to the various veil functions.
+
+Variables may be shared variables in which case they are available, with
+the same value, to all sessions, or session variables in which case each
+session will have its own instance.  Variables are by default created as
+session variables, and are created by assignment, or initialisation.  To
+create a shared variable, call veil_share() with the name of the
+variable you wish to create, then create and use the variable as you
+would a session variable.  Shared variables should only be created and
+initialised from veil_init() in order to prevent race conditions.  If a
+variable has already been created as a session variable, it cannot be
+repurposed as a shared variable.';
+
+
+create or replace
+function veil_variables() returns setof veil_variable_t
+     as '$libdir/veil', 'veil_variables'
+     language C stable;
+
+comment on function veil_variables() is
+'List all current veil_variables.
+Return a set of veil_variable_t results, detailing each existant
+variable
+
+This is intended for interactive use for debugging purposes.';
+
+
+create or replace
+function veil_init_range(name text, min int4, max int4) returns int4
+     as '$libdir/veil', 'veil_init_range'
+     language C stable strict;
+
+comment on function veil_init_range(text, int4, int4) is
+'Initialise a Range variable called NAME constrained by MIN and MAX.
+
+Return the number of  elements in the range.
+
+Ranges may be examined using the veil_range() function.';
+
+
+create or replace
+function veil_range(name text) returns veil_range_t
+     as '$libdir/veil', 'veil_range'
+     language C stable strict;
+
+comment on function veil_range(name text) is
+'Return the range for range variable NAME.';
+
+
+create or replace
+function veil_init_bitmap(bitmap_name text, range_name text) returns bool
+     as '$libdir/veil', 'veil_init_bitmap'
+     language C stable strict;
+
+comment on function veil_init_bitmap(text, text) is
+'Create or re-initialise the Bitmap named BITMAP_NAME, for the range of
+bits given by RANGE_NAME.
+
+Return TRUE on success, raise an error otherwise.
+
+All bits in the bitmap will be zero (cleared).';
+
+
+create or replace
+function veil_clear_bitmap(name text) returns bool
+     as '$libdir/veil', 'veil_clear_bitmap'
+     language C stable strict;
+
+comment on function veil_clear_bitmap(text) is
+'Clear the Bitmap or BitmapRef identified by NAME.
+
+Return TRUE, or raise an error.
+
+Clear (set to zero) all bits in the named bitmap.';
+
+
+create or replace
+function veil_bitmap_setbit(name text, bit_number int4) returns bool
+     as '$libdir/veil', 'veil_bitmap_setbit'
+     language C stable strict;
+
+comment on function veil_bitmap_setbit(text, int4) is
+'In the Bitmap or BitmapRef identified by NAME, set the bit given by
+BIT_NUMBER.
+
+Return TRUE or raise an error.
+
+Set to 1, the identified bit.';
+
+
+create or replace
+function veil_bitmap_clearbit(name text, bit_number int4) returns bool
+     as '$libdir/veil', 'veil_bitmap_clearbit'
+     language C stable strict;
+
+comment on function veil_bitmap_clearbit(text, int4) is
+'In the Bitmap or BitmapRef identified by NAME, clear the bit given by
+BIT_NUMBER.
+
+Return TRUE or raise an error.
+
+Set to 0, the identified bit.';
+
+
+create or replace
+function veil_bitmap_testbit(name text, bit_number int4) returns bool
+     as '$libdir/veil', 'veil_bitmap_testbit'
+     language C stable strict;
+
+comment on function veil_bitmap_testbit(text, int4) is
+'In the Bitmap or BitmapRef identified by NAME, test the bit given by
+BIT_NUMBER.
+
+Return TRUE if the bit is set, FALSE if it is zero.';
+
+
+create or replace
+function veil_bitmap_union(result_name text, name2 text) returns bool
+     as '$libdir/veil', 'veil_bitmap_union'
+     language C stable strict;
+
+comment on function veil_bitmap_union(text, text) is
+'Union two Bitmaps, RESULT_NAME and NAME2, with the result going into
+the first.
+
+Return TRUE, or raise an error.';
+
+
+create or replace
+function veil_bitmap_intersect(result_name text, name2 text) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_intersect'
+     language C stable strict;
+
+comment on function veil_bitmap_intersect(text, text) is
+'Intersect two Bitmaps, RESULT_NAME and NAME2, with the result going into
+the first.
+
+Return TRUE, or raise an error.';
+
+
+create or replace
+function veil_bitmap_bits(name text) returns setof int4
+     as '$libdir/veil', 'veil_bitmap_bits'
+     language C stable strict;
+
+comment on function veil_bitmap_bits(text) is
+'Return each bit in the bitmap NAME.
+
+This is primarily intended for interactive use for debugging, etc.';
+
+
+create or replace
+function veil_bitmap_range(name text) returns veil_range_t
+     as '$libdir/veil', 'veil_bitmap_range'
+     language C stable strict;
+
+comment on function veil_bitmap_range(text) is
+'Return the range of bitmap NAME.  
+
+It is primarily intended for interactive use.';
+
+
+
+create or replace
+function veil_init_bitmap_array(bmarray text, array_range text, 
+               bitmap_range text) returns bool
+     as '$libdir/veil', 'veil_init_bitmap_array'
+     language C stable strict;
+
+comment on function veil_init_bitmap_array(text, text, text) is
+'Creates or resets (clears) BMARRAY, to have ARRAY_RANGE bitmaps of
+BITMAP_RANGE bits.
+
+Returns TRUE or raises an error';
+
+
+create or replace
+function veil_clear_bitmap_array(bmarray text) returns bool
+     as '$libdir/veil', 'veil_clear_bitmap_array'
+     language C stable strict;
+
+comment on function veil_clear_bitmap_array(text) is
+'Clear all bits in all bitmaps of bitmap array BMARRAY.
+
+Return TRUE or raise an error';
+
+
+create or replace
+function veil_bitmap_from_array(bmref text, bmarray text, 
+               index int4) returns text
+     as '$libdir/veil', 
+   'veil_bitmap_from_array'
+     language C stable strict;
+
+comment on function veil_bitmap_from_array(text, text, int4) is
+'Set BitmapRef BMREF to the bitmap from BMARRAY indexed by INDEX.
+
+Return the name of the BitmapRef.
+
+This is used to isolate a single bitmap from a bitmap array, recording
+it in a BitmapRef.  That bitmap can then be manipulated by ordinary veil
+bitmap functions.  Note that BitMapRefs can only be referenced within
+the transaction they are defined.';
+
+
+create or replace
+function veil_bitmap_array_testbit(
+    bmarray text, arr_idx int4, bitno int4) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_array_testbit'
+     language C stable strict;
+
+comment on function veil_bitmap_array_testbit(text, int4, int4) is
+'Test a bit in BMARRAY, from the bitmap indexed by ARR_IDX, checking the
+bit identified by BITNO.
+
+Return TRUE if the bit is set, else FALSE';
+
+
+create or replace
+function veil_bitmap_array_setbit(
+    bmarray text, arr_idx int4, bitno int4) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_array_setbit'
+     language C stable strict;
+
+comment on function veil_bitmap_array_setbit(text, int4, int4) is
+'Set a bit in BMARRAY, from the bitmap indexed by ARR_IDX, setting the
+bit identified by BITNO.
+
+Return TRUE';
+
+
+create or replace
+function veil_bitmap_array_clearbit(
+    bmarray text, arr_idx int4, bitno int4) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_array_clearbit'
+     language C stable strict;
+
+comment on function veil_bitmap_array_clearbit(text, int4, int4) is
+'Clear a bit in BMARRAY, from the bitmap indexed by ARR_IDX, clearing the
+bit identified by BITNO.
+
+Return TRUE';
+
+
+create or replace
+function veil_union_from_bitmap_array(
+    bitmap text, bmarray text, arr_idx int4) returns bool
+     as '$libdir/veil', 
+   'veil_union_from_bitmap_array'
+     language C stable strict;
+
+comment on function veil_union_from_bitmap_array(text, text, int4) is
+'Union BITMAP with BMARRAY[ARR_IDX], with the result going into bitmap.
+
+Return TRUE';
+
+
+create or replace
+function veil_intersect_from_bitmap_array(
+    bitmap text, bmarray text, arr_idx int4) returns bool
+     as '$libdir/veil', 
+   'veil_intersect_from_bitmap_array'
+     language C stable strict;
+
+comment on function veil_intersect_from_bitmap_array(text, text, int4) is
+'Intersect BITMAP with BMARRAY[ARR_IDX], with the result going into bitmap.
+
+Return TRUE';
+
+
+create or replace
+function veil_bitmap_array_bits(bmarray text, arr_idx int4) returns setof int4
+     as '$libdir/veil', 
+   'veil_bitmap_array_bits'
+     language C stable strict;
+
+comment on function veil_bitmap_array_bits(text, int4) is
+'Return all bits in the bitmap given by BMARRAY[ARR_IDX].
+
+This is primarily intended for interactive use: for debugging, etc.';
+
+
+create or replace
+function veil_bitmap_array_arange(bmarray text) returns veil_range_t
+     as '$libdir/veil', 
+   'veil_bitmap_array_arange'
+     language C stable strict;
+
+comment on function veil_bitmap_array_arange(text) is
+'Return the array bounds for BMARRAY.';
+
+
+create or replace
+function veil_bitmap_array_brange(bmarray text) returns veil_range_t
+     as '$libdir/veil', 
+   'veil_bitmap_array_brange'
+     language C stable strict;
+
+comment on function veil_bitmap_array_brange(text) is
+'Return the range of the bitmaps in BMARRAY.';
+
+
+
+create or replace
+function veil_init_bitmap_hash(bmhash text, range text) returns bool
+     as '$libdir/veil', 'veil_init_bitmap_hash'
+     language C stable strict;
+
+comment on function veil_init_bitmap_hash(text, text) is
+'Initialise a bitmap hash variable called BMHASH to contain bitmaps of
+size RANGE.
+
+Return TRUE.';
+
+
+create or replace
+function veil_clear_bitmap_hash(bmhash text) returns bool
+     as '$libdir/veil', 'veil_clear_bitmap_hash'
+     language C stable strict;
+
+comment on function veil_clear_bitmap_hash(text) is
+'Clear all bits in an existing bitmap hash named BMHASH.
+
+Return TRUE.';
+
+
+create or replace
+function veil_bitmap_hash_key_exists(bmhash text, key text) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_hash_key_exists'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_key_exists(text, text) is
+'Determine whether in BMHASH the given KEY already exists.
+
+Return TRUE if the key exists, else FALSE.';
+
+
+create or replace
+function veil_bitmap_from_hash(text, text, text) returns text
+     as '$libdir/veil', 
+   'veil_bitmap_from_hash'
+     language C stable strict;
+
+comment on function veil_bitmap_from_hash(text, text, text) is
+'Set BitmapRef BMREF to the bitmap from BMHASH identfied by KEY.
+
+Return the name of BMREF.';
+
+
+create or replace
+function veil_bitmap_hash_testbit(text, text, int4) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_hash_testbit'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_testbit(text, text, int4) is
+'Test the bit, in the bitmap from BMHASH identified by KEY, given by
+BITNO.
+
+Return TRUE if the bit is set, else FALSE.';
+
+
+create or replace
+function veil_bitmap_hash_setbit(text, text, int4) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_hash_setbit'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_setbit(text, text, int4) is
+'Set the bit, in the bitmap from BMHASH identified by KEY, given by
+BITNO to TRUE.
+
+Return TRUE.';
+
+
+create or replace
+function veil_bitmap_hash_clearbit(text, text, int4) returns bool
+     as '$libdir/veil', 
+   'veil_bitmap_hash_clearbit'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_clearbit(text, text, int4) is
+'Set the bit, in the bitmap from BMHASH identified by KEY, given by
+BITNO to FALSE.
+
+Return TRUE.';
+
+
+create or replace
+function veil_union_into_bitmap_hash(text, text, text) returns bool
+     as '$libdir/veil', 
+   'veil_union_into_bitmap_hash'
+     language C stable strict;
+
+comment on function veil_union_into_bitmap_hash(text, text, text) is
+'Into the bitmap from BMHASH, identified by KEY, and union the bits from
+BITMAP (which may be a bitmap or bitmap_ref).
+
+Return TRUE.';
+
+
+create or replace
+function veil_union_from_bitmap_hash(text, text, text) returns bool
+     as '$libdir/veil', 
+   'veil_union_from_bitmap_hash'
+     language C stable strict;
+comment on function veil_union_from_bitmap_hash(text, text, text) is
+'Retrieve the bitmap from BMHASH, identified by KEY, and union it into
+BITMAP (which may be a bitmap or bitmap_ref).
+
+Return TRUE.';
+
+
+create or replace
+function veil_intersect_from_bitmap_hash(text, text, text) returns bool
+     as '$libdir/veil', 
+   'veil_intersect_from_bitmap_hash'
+     language C stable strict;
+comment on function veil_intersect_from_bitmap_hash(text, text, text) is
+'Into BITMAP, intersect the bits from the bitmap in BMHASH identified by
+KEY.
+
+Return TRUE.';
+
+
+create or replace
+function veil_bitmap_hash_bits(text, text) returns setof int4
+     as '$libdir/veil', 
+   'veil_bitmap_hash_bits'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_bits(text, text) is
+'Return the set of bits in the bitset from BMHASH identfied by KEY.';
+
+
+create or replace
+function veil_bitmap_hash_range(text) returns veil_range_t
+     as '$libdir/veil', 
+   'veil_bitmap_hash_range'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_range(text) is
+'Return the range of all bitmaps in BMHASH.';
+
+
+create or replace
+function veil_bitmap_hash_entries(text) returns setof text
+     as '$libdir/veil', 
+   'veil_bitmap_hash_entries'
+     language C stable strict;
+
+comment on function veil_bitmap_hash_entries(text) is
+'Return the keys of all bitmaps in BMHASH.';
+
+
+create or replace
+function veil_int4_set(text, int4) returns int4
+     as '$libdir/veil', 
+   'veil_int4_set'
+     language C stable;
+
+comment on function veil_int4_set(text, int4) is
+'Set the int4 variable NAME to VALUE.
+
+Return the new value';
+
+
+create or replace
+function veil_int4_get(text) returns int4
+     as '$libdir/veil', 
+   'veil_int4_get'
+     language C stable strict;
+
+comment on function veil_int4_get(text) is
+'Return the value of int4 variable NAME.';
+
+
+create or replace
+function veil_init_int4array(text, text) returns bool
+     as '$libdir/veil', 'veil_init_int4array'
+     language C stable strict;
+
+comment on function veil_init_int4array(text, text) is
+'Initialise the int4 array ARRAYNAME, with an index range of RANGE.
+
+Each entry in the array is set to zero.
+
+Return TRUE.';
+
+
+create or replace
+function veil_clear_int4array(text) returns bool
+     as '$libdir/veil', 
+   'veil_clear_int4array'
+     language C stable strict;
+comment on function veil_clear_int4array(text) is
+'Reset each entry in the int4 array ARRAYNAME to zero.
+
+Return TRUE.';
+
+
+
+create or replace
+function veil_int4array_set(text, int4, int4) returns int4
+     as '$libdir/veil', 
+   'veil_int4array_set'
+     language C stable;
+
+comment on function veil_int4array_set(text, int4, int4) is
+'Set the ARRAY element IDX to VALUE.
+
+Return the new value.';
+
+
+create or replace
+function veil_int4array_get(text, int4) returns int4
+     as '$libdir/veil', 
+   'veil_int4array_get'
+     language C stable strict;
+
+comment on function veil_int4array_get(text, int4) is
+'Return the value of ARRAY element IDX.';
+
+
+create or replace
+function veil_init(bool) returns bool 
+     as '$libdir/veil', 
+   'veil_init'
+     language C stable strict;
+
+comment on function veil_init(bool) is
+'This is the default version of veil_init, which does nothing except
+raise an error.';
+
+
+create or replace
+function veil_perform_reset() returns bool
+     as '$libdir/veil', 'veil_perform_reset'
+     language C stable;
+
+comment on function veil_perform_reset() is
+'Allow userspace to force call of veil_init.
+
+This function may fail due to outstanding transactions possibly holding
+shared memory that we wish to re-use.  In this case a warning will be
+issued.  Once any long running transactions have completed, a retry
+should succeed.
+
+Return TRUE if successful, FALSE otherwise.';
+
+
+create or replace
+function veil_force_reset() returns bool
+     as '$libdir/veil', 'veil_force_reset'
+     language C stable;
+
+comment on function veil_force_reset() is
+'Allow userspace to force an unconditional reset of veil shared memory.
+
+This always causes a PANIC, causing the database to fully reset.';
+
+
+create or replace
+function veil_version() returns text
+     as '$libdir/veil', 'veil_version'
+     language C stable;
+
+comment on function veil_version() is
+'Return a text string identifying the current veil version.';
+
+
+
+create or replace
+function veil_serialise(varname text) returns text
+     as '$libdir/veil', 
+   'veil_serialise'
+     language C stable strict;
+
+comment on function veil_serialise(varname text) is
+'Return a serialised copy of a variable VARNAME in text form.
+
+This is intended to be used with pgmemcache so that session variables
+can be efficiently cached.  Serialised values can be concatenated
+together as a single string and then deserialised in a single operation.
+
+The value can be restored by de-serialising it.';
+
+
+
+create or replace
+function veil_serialize(text) returns text
+     as '$libdir/veil', 
+   'veil_serialise'
+     language C stable strict;
+
+comment on function veil_serialize(varname text) is
+'Return a serialised copy of a variable VARNAME in text form.
+
+This is intended to be used with pgmemcache so that session variables
+can be efficiently cached.  Serialized values can be concatenated
+together as a single string and then deserialized in a single operation.
+
+The value can be restored by de-serializing it.';
+
+
+
+create or replace
+function veil_deserialise(text) returns int4
+     as '$libdir/veil', 
+   'veil_deserialise'
+     language C stable strict;
+
+comment on function veil_deserialise(text) is
+'Reset the contents of a set of serialised variable from STREAM.
+
+Return the number of items de-serialised.';
+
+
+-- Ditto for victims of webster.
+create or replace
+function veil_deserialize(text) returns int4
+     as '$libdir/veil', 
+   'veil_deserialise'
+     language C stable strict;
+
+comment on function veil_deserialize(text) is
+'Reset the contents of a set of serialized variable from STREAM.
+
+Return the number of items de-serialized.';
+
+
+revoke execute on function veil_share(text) from public;
+revoke execute on function veil_variables() from public;
+revoke execute on function veil_init_range(text, int4,int4) from public;
+revoke execute on function veil_range(text) from public;
+
+revoke execute on function veil_init_bitmap(text, text) from public;
+revoke execute on function veil_clear_bitmap(text) from public;
+revoke execute on function veil_bitmap_setbit(text, int4) from public;
+revoke execute on function veil_bitmap_testbit(text, int4) from public;
+revoke execute on function veil_bitmap_bits(text) from public;
+revoke execute on function veil_bitmap_range(text) from public;
+
+revoke execute on function veil_init_bitmap_array(text, text, text)
+  from public;
+revoke execute on function veil_clear_bitmap_array(text) from public;
+revoke execute on function veil_bitmap_from_array(text, text, int4)
+  from public;
+revoke execute on function veil_bitmap_array_setbit(text, int4, int4)
+  from public;
+revoke execute on function veil_bitmap_array_testbit(text, int4, int4)
+  from public;
+revoke execute on function veil_union_from_bitmap_array(text, text, int4)
+  from public;
+revoke execute on function veil_intersect_from_bitmap_array(text, text, int4)
+  from public;
+revoke execute on function veil_bitmap_array_bits(text, int4) from public;
+revoke execute on function veil_bitmap_array_arange(text) from public;
+revoke execute on function veil_bitmap_array_brange(text) from public;
+
+
+revoke execute on function veil_init_bitmap_hash(text, text) from public;
+revoke execute on function veil_clear_bitmap_hash(text) from public;
+revoke execute on function veil_bitmap_hash_key_exists(text, text)
+  from public;
+revoke execute on function veil_bitmap_from_hash(text, text, text)
+  from public;
+revoke execute on function veil_bitmap_hash_setbit(text, text, int4)
+  from public;
+revoke execute on function veil_bitmap_hash_testbit(text, text, int4)
+  from public;
+revoke execute on function veil_union_into_bitmap_hash(text, text, text)
+  from public;
+revoke execute on function veil_union_from_bitmap_hash(text, text, text)
+  from public;
+revoke execute on function veil_intersect_from_bitmap_hash(text, text, text)
+  from public;
+revoke execute on function veil_bitmap_hash_bits(text, text) from public;
+revoke execute on function veil_bitmap_hash_range(text) from public;
+revoke execute on function veil_bitmap_hash_entries(text) from public;
+
+revoke execute on function veil_init_int4array(text, text) from public;
+revoke execute on function veil_clear_int4array(text) from public;
+revoke execute on function veil_int4array_set(text, int4, int4) from public;
+revoke execute on function veil_int4array_get(text, int4) from public;
+
+revoke execute on function veil_init(bool) from public;
+
+revoke execute on function veil_serialise(text) from public;
+revoke execute on function veil_serialize(text) from public;
+revoke execute on function veil_deserialise(text) from public;
+revoke execute on function veil_deserialize(text) from public;
+
+
diff --git a/veil.control b/veil.control
new file mode 100644 (file)
index 0000000..f2350bc
--- /dev/null
@@ -0,0 +1,16 @@
+# veil.control
+#
+#      Postgres extension control file for Veil
+#
+#      Copyright (c) 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+
+directory       = 'extension/veil'
+default_version = '1.0'
+module_pathname = '$libdir/veil'
+superuser       = true
+relocatable     = true
+
+comment = 'Provides tools for building row-level access controls.'
diff --git a/veil_demo--1.0.sql b/veil_demo--1.0.sql
new file mode 100644 (file)
index 0000000..491cd1d
--- /dev/null
@@ -0,0 +1,1265 @@
+create schema hidden;
+revoke all on schema hidden from public;
+
+create table hidden.privileges (
+    privilege_id   integer not null,
+    privilege_name varchar(80) not null
+);
+alter table hidden.privileges add constraint privilege__pk
+    primary key(privilege_id);
+
+
+create table hidden.roles (
+    role_id    integer not null,
+    role_name  varchar(80) not null
+);
+alter table hidden.roles add constraint role__pk
+    primary key(role_id);
+
+
+create table hidden.role_privileges (
+    role_id        integer not null,
+    privilege_id   integer not null
+);
+
+alter table hidden.role_privileges add constraint role_privilege__pk
+    primary key(role_id, privilege_id);
+
+alter table hidden.role_privileges add constraint role_privilege__role_fk
+    foreign key(role_id)
+    references hidden.roles(role_id);
+
+alter table hidden.role_privileges add constraint role_privilege__priv_fk
+    foreign key(privilege_id)
+    references hidden.privileges(privilege_id);
+
+create table hidden.role_roles (
+    role_id        integer not null,
+    has_role_id        integer not null
+);
+
+alter table hidden.role_roles add constraint role_role__pk
+    primary key(role_id, has_role_id);
+
+alter table hidden.role_roles add constraint role_role__role_fk
+    foreign key(role_id)
+    references hidden.roles(role_id);
+
+alter table hidden.role_roles add constraint role_role__has_role_fk
+    foreign key(has_role_id)
+    references hidden.roles(role_id);
+
+
+create sequence person_id_seq;
+create table hidden.persons (
+    person_id      integer not null,
+    person_name        varchar(80) not null
+);
+alter table hidden.persons add constraint person__pk
+    primary key(person_id);
+
+
+create sequence project_id_seq;
+create table hidden.projects (
+    project_id     integer not null,
+    project_name   varchar(80) not null
+);
+alter table hidden.projects add constraint project__pk
+    primary key(project_id);
+
+
+create table hidden.detail_types (
+    detail_type_id   integer not null,
+    required_privilege_id integer not null,
+    detail_type_name     varchar(80) not null
+);
+alter table hidden.detail_types add constraint detail_type__pk
+    primary key(detail_type_id);
+
+alter table hidden.detail_types add constraint detail_type__priv_fk
+    foreign key(required_privilege_id)
+    references hidden.privileges(privilege_id);
+
+
+create table hidden.assignments (
+    project_id     integer not null,
+    person_id      integer not null,
+    role_id        integer not null
+);
+alter table hidden.assignments add constraint assignment__pk
+    primary key(project_id, person_id);
+
+alter table hidden.assignments add constraint assignment__project_fk
+    foreign key(project_id)
+    references hidden.projects(project_id);
+
+alter table hidden.assignments add constraint assignment__person_fk
+    foreign key(person_id)
+    references hidden.persons(person_id);
+
+alter table hidden.assignments add constraint assignment__role_fk
+    foreign key(role_id)
+    references hidden.roles(role_id);
+
+
+create table hidden.person_roles (
+    person_id      integer not null,
+    role_id        integer not null
+);
+alter table hidden.person_roles add constraint person_role__pk
+    primary key(person_id, role_id);
+
+alter table hidden.person_roles add constraint person_role__person_fk
+    foreign key(person_id)
+    references hidden.persons(person_id);
+
+alter table hidden.person_roles add constraint person_role__role_fk
+    foreign key(role_id)
+    references hidden.roles(role_id);
+
+
+create table hidden.project_details (
+    project_id     integer not null,
+    detail_type_id integer not null,
+    value      text not null
+);
+alter table hidden.project_details add constraint project_detail__pk
+    primary key(project_id, detail_type_id);
+
+alter table hidden.project_details add constraint project_detail__project_fk
+    foreign key(project_id)
+    references hidden.projects(project_id);
+
+alter table hidden.project_details add constraint project_detail__detail_fk
+    foreign key(detail_type_id)
+    references hidden.detail_types(detail_type_id);
+
+
+create table hidden.person_details (
+    person_id      integer not null,
+    detail_type_id integer not null,
+    value      text not null
+);
+alter table hidden.person_details add constraint person_detail__pk
+    primary key(person_id, detail_type_id);
+
+alter table hidden.person_details add constraint person_detail__person_fk
+    foreign key(person_id)
+    references hidden.persons(person_id);
+
+alter table hidden.person_details add constraint person_detail__detail_fk
+    foreign key(detail_type_id)
+    references hidden.detail_types(detail_type_id);
+
+
+insert into hidden.privileges (privilege_id, privilege_name) values
+(10001,    'select_privileges'),
+(10002,    'insert_privileges'),
+(10003,    'update_privileges'),
+(10004,    'delete_privileges'),
+(10005,    'select_roles'),
+(10006,    'insert_roles'),
+(10007,    'update_roles'),
+(10008,    'delete_roles'),
+(10009,    'select_role_privileges'),
+(10010,    'insert_role_privileges'),
+(10011,    'update_role_privileges'),
+(10012,    'delete_role_privileges'),
+(10013,    'select_persons'),
+(10014,    'insert_persons'),
+(10015,    'update_persons'),
+(10016,    'delete_persons'),
+(10017,    'select_projects'),
+(10018,    'insert_projects'),
+(10019,    'update_projects'),
+(10020,    'delete_projects'),
+(10021,    'select_detail_types'),
+(10022,    'insert_detail_types'),
+(10023,    'update_detail_types'),
+(10024,    'delete_detail_types'),
+(10025,    'select_assignments'),
+(10026,    'insert_assignments'),
+(10027,    'update_assignments'),
+(10028,    'delete_assignments'),
+(10029,    'select_person_roles'),
+(10030,    'insert_person_roles'),
+(10031,    'update_person_roles'),
+(10032,    'delete_person_roles'),
+(10033,    'select_project_details'),
+(10034,    'insert_project_details'),
+(10035,    'update_project_details'),
+(10036,    'delete_project_details'),
+(10037,    'select_person_details'),
+(10038,    'insert_person_details'),
+(10039,    'update_person_details'),
+(10040,    'delete_person_details'),
+(10041,    'select_role_roles'),
+(10042,    'insert_role_roles'),
+(10043,    'update_role_roles'),
+(10044,    'delete_role_roles'),
+(10100,    'can_connect'),
+(10150,    'view_basic'),
+(10151,    'view_personal'),
+(10152,    'view_personal_secure'),
+(10153,    'view_project_confidential');
+
+insert into hidden.roles (role_id, role_name) values
+(11001,    'DBA'),
+(11002,    'Personal Context'),
+(11003,    'Employee'),
+(11004,    'Worker'),
+(11005,    'Project Manager'),
+(11006,    'Director'),
+(11007,    'Manager');
+
+-- DBA can do anything (but is not automatically an employee)
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11001, privilege_id
+from   hidden.privileges
+where  privilege_id != 10100;
+
+-- Personal Context allows update of personal details
+insert into hidden.role_privileges (role_id, privilege_id) values
+(11002,    10013),
+(11002,    10015),
+(11002,    10025),
+(11002,    10029),
+(11002,    10037),
+(11002,    10038),
+(11002,    10039),
+(11002,    10040),
+(11002,    10150),
+(11002,    10151),
+(11002,    10152);
+
+-- Basic Access can see lookup data
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11003, privilege_id
+from   hidden.privileges
+where  privilege_name in ('select_privileges', 'select_roles',
+             'select_role_privileges', 'select_detail_types');
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11003, 10100;
+
+-- Workers can modify project info
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11004, privilege_id
+from   hidden.privileges
+where  privilege_name like '%project%'
+and    privilege_name not like 'delete%'
+and    privilege_name not like '%confidential';
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11004, 10025;
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11004, 10150;
+
+-- Project Manager can do anything to project info and can see personal info
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11005, privilege_id
+from   hidden.privileges
+where  privilege_name like '%project%'
+or     privilege_name like '%assignment%';
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11005, privilege_id
+from   hidden.privileges
+where  privilege_name like 'select_person%';
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11005, 10150;
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11005, 10151;
+
+-- Director can do anything except modify personal details
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11006, privilege_id
+from   hidden.privileges
+where  privilege_name not like '%person%';
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11006, privilege_id
+from   hidden.privileges
+where  privilege_name like 'select_person%';
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11006, 10014;
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11006, 10151;
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11006, 10152;
+
+-- Manager can see personal info
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11007, privilege_id
+from   hidden.privileges
+where  privilege_name like 'select_person%';
+
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11007, 10150;
+insert into hidden.role_privileges (role_id, privilege_id)
+select 11007, 10151;
+
+
+insert into hidden.persons (person_id, person_name) values
+(1, 'Deb (the DBA)'),
+(2, 'Pat (the PM)'),
+(3, 'Derick (the director)'),
+(4, 'Will (the worker)'),
+(5, 'Wilma (the worker)'),
+(6, 'Fred (the fired DBA)');
+
+insert into hidden.person_roles (person_id, role_id) values
+(1, 11001),
+(1, 11003),
+(2, 11003),
+(2, 11007),
+(3, 11003),
+(3, 11006),
+(4, 11003),
+(5, 11003),
+(6, 11001);
+
+insert into hidden.projects (project_id, project_name) values
+(101, 'Secret Project'),
+(102, 'Public project');
+
+insert into hidden.assignments (project_id, person_id, role_id) values
+(101, 3, 11005),
+(101, 5, 11004),
+(102, 2, 11005),
+(102, 4, 11004),
+(102, 5, 11004);
+
+insert into hidden.detail_types (detail_type_id, required_privilege_id, 
+                   detail_type_name) values
+(1001, 10150, 'start_date'),
+(1002, 10150, 'status'),
+(1003, 10150, 'join_date'),
+(1004, 10152, 'salary'),
+(1005, 10151, 'date of birth'),
+(1006, 10152, 'sin'),
+(1007, 10150, 'skills'),
+(1008, 10153, 'contract value');
+
+insert into hidden.person_details (person_id, detail_type_id, value) values
+(1, 1003, '20050102'),
+(2, 1003, '20050103'),
+(3, 1003, '20050104'),
+(4, 1003, '20050105'),
+(5, 1003, '20050106'),
+(6, 1003, '20050107'),
+(1, 1002, 'Employee'),
+(2, 1002, 'Employee'),
+(3, 1002, 'Employee'),
+(4, 1002, 'Employee'),
+(5, 1002, 'Employee'),
+(6, 1002, 'Terminated'),
+(1, 1004, '50,000'),
+(2, 1004, '50,000'),
+(3, 1004, '80,000'),
+(4, 1004, '30,000'),
+(5, 1004, '30,000'),
+(6, 1004, '40,000'),
+(1, 1005, '19610102'),
+(2, 1005, '19600102'),
+(3, 1005, '19650102'),
+(4, 1005, '19660102'),
+(5, 1005, '19670102'),
+(1, 1006, '123456789'),
+(2, 1006, '123456789'),
+(3, 1006, '123456789'),
+(4, 1006, '123456789'),
+(5, 1006, '123456789'),
+(1, 1007, 'Oracle, C, SQL'),
+(2, 1007, 'Soft peoply-stuff'),
+(3, 1007, 'None at all'),
+(4, 1007, 'Subservience'),
+(5, 1007, 'Subservience');
+
+
+insert into hidden.project_details (project_id, detail_type_id, value) values
+(101, 1001, '20050101'),
+(101, 1002, 'Secretly ongoing'),
+(101, 1008, '$800,000'),
+(102, 1001, '20050101'),
+(102, 1002, 'Ongoing'),
+(102, 1008, '$100,000');
+
+
+-- Veil Variables:
+--   Shared:
+--     privs_range Range The range of privilege_id in the privileges table
+--     roles_range Range The range of role_id in the roles table
+--     role_privs Bitmap Array Array of privilege bitmaps indexed by role_id
+--     det_types_range Range The range of detail_type_id in detail_types
+--     det_types_privs Int4Array Mapping of detail_type_id to the privilege
+--       required to access details of that type
+--     
+--   Session:
+--     person_id Int4 The id of the currently connected person
+--     global_context Bitmap The global privileges of the connected person
+--     project_context Bitmap Hash Hash of privilege bitmaps, indexed by
+--       project_id, giving the connected person's project privileges.
+--
+-- Security Contexts:
+--   Global
+--     Privileges are identified in variable global_context
+--   Personal
+--     Privileges are identified in role_privs[11002].
+--   Project
+--     A relational context.  Privileges are identified in
+--     project_context[project_id].
+
+-- Replacement for default version of veil_init.
+-- Initialise and load all shared variables.
+--
+create or replace
+function veil_init(bool) returns bool as '
+declare
+    doing_reset alias for $1;
+    exists_privs_range bool;
+    exists_roles_range bool;
+    exists_role_privs  bool;
+    exists_det_types_range bool;
+    exists_det_types_privs bool;
+    init_reqd bool;
+    dummy     bool;
+    dummy2    bool;
+    dummyint  int4;
+    _count    int4;
+begin
+    -- Declare all shared variables.
+
+    select into exists_privs_range, exists_roles_range,
+       exists_role_privs, exists_det_types_range,
+       exists_det_types_privs
+           veil_share(''privs_range''), veil_share(''roles_range''),
+      veil_share(''role_privs''), veil_share(''det_types_range''),
+      veil_share(''det_types_privs'');
+
+    init_reqd = not (exists_privs_range and exists_role_privs and
+            exists_role_privs and exists_det_types_range and
+            exists_det_types_privs);
+
+    if init_reqd or doing_reset then
+       -- Load ranges for privs and roles.
+       select into dummyint
+              veil_init_range(''roles_range'', min(role_id), max(role_id))
+       from   hidden.roles;
+
+       select into dummyint
+              veil_init_range(''privs_range'', min(privilege_id), 
+                     max(privilege_id))
+       from   hidden.privileges;
+
+   -- Load range for detail_types
+       select into dummyint
+              veil_init_range(''det_types_range'', 
+                      min(detail_type_id), max(detail_type_id))
+       from   hidden.detail_types;
+
+   -- Initialise array of required privs for detail_types
+       select into dummy
+              veil_init_int4array(''det_types_privs'', ''det_types_range'');
+       select into _count
+              count(veil_int4array_set(''det_types_privs'', 
+                               detail_type_id, required_privilege_id))
+       from   hidden.detail_types;
+
+       -- Initialise role_privs bitmap_array
+       select into dummy
+              veil_init_bitmap_array(''role_privs'', ''roles_range'', 
+                        ''privs_range'');
+
+       -- Populate role_privs bitmap_array
+       select into _count
+              count(veil_bitmap_array_setbit(''role_privs'', 
+                            role_id, privilege_id))
+       from   hidden.role_privileges;
+
+    end if;
+
+    -- Declare important session variables, so that we do not 
+    -- get odd, undefined variable, error messages.
+    select into dummyint, dummy, dummy2
+      veil_int4_set(''person_id'',  null),
+      veil_init_bitmap(''global_context'', ''privs_range''),
+      veil_init_bitmap_hash(''project_context'', ''privs_range'');
+
+    return true;               
+end;
+' language plpgsql
+stable 
+security definer;
+
+revoke execute on function veil_init(bool) from public;
+
+
+create or replace
+function connect_person(int4) returns bool as '
+declare
+    _person_id alias for $1;
+    dummy    int4;
+    _connect bool;
+    proj_roles record;
+    last_proj int4;
+    first_rec bool;
+begin
+    -- In reality this function would require some authentication token such 
+    -- as a password.  This is just a dumb demo version.
+
+    select into _connect disconnect_person();
+
+    -- Test whether provided person exists.  This is where we would, in a
+    -- real version, do proper authentication.
+    select into dummy 1
+    from   hidden.persons
+    where  person_id = _person_id;
+
+    if found then
+   -- The person exists and passes authentication
+
+   -- From the persons roles set the global_context bitmap.
+   select into dummy
+          count(veil_union_from_bitmap_array(''global_context'',
+                         ''role_privs'', role_id))
+   from   hidden.person_roles
+   where  person_id = _person_id;
+
+   -- Check that user has can_connect privilege
+   select into _connect
+       veil_bitmap_testbit(''global_context'', 10100);
+
+   if not _connect then
+       select into _connect disconnect_person();
+       return false;
+   end if;
+
+
+   -- From the persons assignments set the project_context bitmap hash.
+   select into dummy
+          count(veil_union_into_bitmap_hash(''project_context'',
+           project_id::text,
+           veil_bitmap_from_array(''scratch_bitmap'',
+                          ''role_privs'', role_id)))
+   from   hidden.assignments
+   where  person_id = _person_id;
+
+   -- Finally, record the person_id for the connection.
+   select into dummy veil_int4_set(''person_id'', _person_id);
+
+   return true;
+    else
+   return false;
+    end if;
+
+end;
+' language plpgsql
+stable 
+security definer;
+
+create or replace
+function disconnect_person() returns bool as '
+declare
+    dummy int4;
+    dummy2 bool;
+    dummy3 bool;
+begin
+    -- Clear session bitmaps, and reset the person_id
+    select into dummy, dummy2, dummy3
+      veil_int4_set(''person_id'', null),
+      veil_init_bitmap(''global_context'', ''privs_range''),
+      veil_init_bitmap_hash(''project_context'', ''privs_range'');
+    return false;
+end;
+' language plpgsql
+stable 
+security definer;
+
+
+create or replace
+function i_have_global_priv(int4) returns bool as '
+declare
+    priv_id alias for $1;
+    connection_id int4;
+    result bool;
+begin
+    select into connection_id, result 
+           veil_int4_get(''person_id''), 
+           veil_bitmap_testbit(''global_context'', priv_id);
+    if connection_id is null then
+   return false;
+    else
+        return result;
+    end if;
+end;
+' language plpgsql
+stable 
+security definer;
+
+
+create or replace
+function i_have_personal_priv(int4, int4) returns bool as '
+declare
+    priv_id alias for $1;
+    person_id alias for $2;
+    connection_id int4;
+    result bool;
+begin
+    select into connection_id, result 
+           veil_int4_get(''person_id''), 
+           veil_bitmap_testbit(''global_context'', priv_id);
+    if connection_id is null then
+   -- No-one is connected
+   return false;
+    else
+   if result then
+       -- We have the required privilege in global context.  No need
+       -- to check any further
+       return true;
+   else
+       if person_id = connection_id then
+       -- We are connected as the owner of this record.  Check
+       -- whether we have the required privilege in personal 
+       -- context.
+       select into result
+                  veil_bitmap_array_testbit(''role_privs'', 
+                           11002, priv_id);
+       return result;
+       else
+       -- We have no personal context rights to this record
+       return false;
+       end if;
+   end if;
+    end if;
+end;
+' language plpgsql
+stable 
+security definer;
+
+
+create or replace
+function i_have_project_priv(int4, int4) returns bool as '
+declare
+    priv_id alias for $1;
+    project_id alias for $2;
+    connection_id int4;
+    result bool;
+begin
+    select into connection_id, result 
+           veil_int4_get(''person_id''), 
+           veil_bitmap_testbit(''global_context'', priv_id);
+    if connection_id is null then
+   -- No-one is connected
+   return false;
+    else
+   if result then
+       -- We have the required privilege in global context.  No need
+       -- to check any further
+       return true;
+   else
+       select into result
+           veil_bitmap_hash_testbit(''project_context'', 
+                        project_id::text, priv_id);
+       return result;
+   end if;
+    end if;
+end;
+' language plpgsql
+stable 
+security definer;
+
+
+create or replace
+function i_have_proj_or_pers_priv(int4, int4, int4) returns bool as '
+declare
+    priv_id alias for $1;
+    project_id alias for $2;
+    person_id alias for $3;
+    connection_id int4;
+    result bool;
+begin
+    select into connection_id, result 
+           veil_int4_get(''person_id''), 
+           veil_bitmap_testbit(''global_context'', priv_id);
+    if connection_id is null then
+   -- No-one is connected
+   return false;
+    else
+   if result then
+       -- We have the required privilege in global context.  No need
+       -- to check any further
+       return true;
+   else
+       if person_id = connection_id then
+       -- We are connected as the owner of this record.  Check
+       -- whether we have the required privilege in personal 
+       -- context.
+       select into result
+                  veil_bitmap_array_testbit(''role_privs'', 
+                           11002, priv_id);
+       return result;
+       end if;
+       select into result
+           veil_bitmap_hash_testbit(''project_context'', 
+                        project_id::text, priv_id);
+       return result;
+       -- We have no personal context rights to this record
+       -- so check project context
+       return false;
+   end if;
+    end if;
+end;
+' language plpgsql
+stable 
+security definer;
+
+create or replace
+function i_have_project_detail_priv(int4, int4) returns bool as '
+declare
+    result bool;
+    detail_id alias for $1;
+    proj_id alias for $2;
+begin
+    select into result 
+        i_have_project_priv(veil_int4array_get(''det_types_privs'', detail_id),
+               proj_id);
+    return result;
+end;
+' language plpgsql
+stable 
+security definer;
+
+create or replace
+function i_have_person_detail_priv(int4, int4) returns bool as '
+declare
+    result bool;
+    detail_id alias for $1;
+    person_id alias for $2;
+begin
+    select into result 
+       i_have_personal_priv(veil_int4array_get(''det_types_privs'', detail_id),
+               person_id);
+    return result;
+end;
+' language plpgsql
+stable 
+security definer;
+
+create view privileges(
+       privilege_id,
+       privilege_name) as
+select privilege_id,
+       privilege_name
+from   hidden.privileges
+where  i_have_global_priv(10001);
+
+create rule ii_privileges as
+on insert to privileges
+do instead 
+    insert into hidden.privileges
+      (privilege_id, privilege_name)
+    select new.privilege_id, new.privilege_name 
+    where  i_have_global_priv(10002);
+
+create rule iu_privileges as
+on update to privileges
+do instead
+    update hidden.privileges
+    set    privilege_name = new.privilege_name,
+      privilege_id = new.privilege_id
+    where  privilege_id = old.privilege_id
+    and    i_have_global_priv(10003);
+create rule id_privileges as
+on delete to privileges
+do instead
+    delete from hidden.privileges
+    where  privilege_id = old.privilege_id
+    and    i_have_global_priv(10004);
+
+grant select, insert, update, delete on privileges to public;    
+
+create view roles(
+       role_id,
+       role_name) as
+select role_id,
+       role_name
+from   hidden.roles
+where  i_have_global_priv(10005);
+
+create rule ii_roles as
+on insert to roles
+do instead 
+    insert into hidden.roles
+      (role_id, role_name)
+    select new.role_id, new.role_name
+    where  i_have_global_priv(10006);
+
+create rule iu_roles as
+on update to roles
+do instead
+    update hidden.roles
+    set    role_name = new.role_name,
+      role_id = new.role_id
+    where  role_id = old.role_id
+    and    i_have_global_priv(10007);
+create rule id_roles as
+on delete to roles
+do instead
+    delete from hidden.roles
+    where  role_id = old.role_id
+    and    i_have_global_priv(10008);
+
+grant select, insert, update, delete on roles to public;    
+
+create view role_privileges(
+       role_id,
+       privilege_id) as
+select role_id,
+       privilege_id
+from   hidden.role_privileges
+where  i_have_global_priv(10009);
+
+create rule ii_role_privileges as
+on insert to role_privileges
+do instead 
+    insert into hidden.role_privileges
+      (role_id, privilege_id)
+    select new.role_id, new.privilege_id
+    where  i_have_global_priv(10010);
+
+create rule iu_role_privileges as
+on update to role_privileges
+do instead
+    update hidden.role_privileges
+    set    role_id = new.role_id, 
+      privilege_id = new.privilege_id
+    where  role_id = old.role_id
+    and    privilege_id = old.privilege_id
+    and    i_have_global_priv(10011);
+create rule id_role_privileges as
+on delete to role_privileges
+do instead
+    delete from hidden.role_privileges
+    where  role_id = old.role_id
+    and    i_have_global_priv(10012);
+
+grant select, insert, update, delete on role_privileges to public;    
+
+
+create view role_roles(
+       role_id,
+       has_role_id) as
+select role_id,
+       has_role_id
+from   hidden.role_roles
+where  i_have_global_priv(10041);
+
+create rule ii_role_roles as
+on insert to role_roles
+do instead 
+    insert into hidden.role_roles
+      (role_id, has_role_id)
+    select new.role_id, new.has_role_id
+    where  i_have_global_priv(10042);
+
+create rule iu_role_roles as
+on update to role_roles
+do instead
+    update hidden.role_roles
+    set    role_id = new.role_id,
+      has_role_id = new.has_role_id
+    where  role_id = old.role_id
+    and    i_have_global_priv(10043);
+create rule id_role_roles as
+on delete to role_roles
+do instead
+    delete from hidden.role_roles
+    where  role_id = old.role_id
+    and    i_have_global_priv(10044);
+
+grant select, insert, update, delete on role_roles to public;    
+
+
+create view persons(
+       person_id,
+       person_name) as
+select person_id,
+       person_name
+from   hidden.persons
+where  i_have_personal_priv(10013, person_id);
+
+create rule ii_persons as
+on insert to persons
+do instead 
+    insert into hidden.persons
+      (person_id, person_name)
+    select new.person_id, new.person_name
+    where  i_have_personal_priv(10014, new.person_id);
+
+create rule iu_persons as
+on update to persons
+do instead
+    update hidden.persons
+    set    person_id = new.person_id,
+      person_name = new.person_name
+    where  person_id = old.person_id
+    and    i_have_personal_priv(10015, old.person_id);
+create rule id_persons as
+on delete to persons
+do instead
+    delete from hidden.persons
+    where  person_id = old.person_id
+    and    i_have_personal_priv(10016, old.person_id);
+
+grant select, insert, update, delete on persons to public;    
+
+
+create view projects(
+       project_id,
+       project_name) as
+select project_id,
+       project_name
+from   hidden.projects
+where  i_have_project_priv(10017, project_id);
+
+create rule ii_projects as
+on insert to projects
+do instead 
+    insert into hidden.projects
+      (project_id, project_name)
+    select new.project_id, new.project_name
+    where  i_have_project_priv(10018, new.project_id);
+
+create rule iu_projects as
+on update to projects
+do instead
+    update hidden.projects
+    set    project_id = new.project_id,
+      project_name = new.project_name
+    where  project_id = old.project_id
+    and    i_have_project_priv(10019, old.project_id);
+create rule id_projects as
+on delete to projects
+do instead
+    delete from hidden.projects
+    where  project_id = old.project_id
+    and    i_have_project_priv(10020, old.project_id);
+
+grant select, insert, update, delete on projects to public;    
+
+
+create view detail_types (
+       detail_type_id,
+       required_privilege_id,
+       detail_type_name) as 
+select detail_type_id,
+       required_privilege_id,
+       detail_type_name
+from   hidden.detail_types
+where  i_have_global_priv(10021);
+
+create rule ii_detail_types as
+on insert to detail_types
+do instead 
+    insert into hidden.detail_types
+      (detail_type_id, detail_type_name)
+    select new.detail_type_id, new.detail_type_name
+    where  i_have_global_priv(10022);
+
+create rule iu_detail_types as
+on update to detail_types
+do instead
+    update hidden.detail_types
+    set    detail_type_id = new.detail_type_id, 
+      detail_type_name = new.detail_type_name
+    where  detail_type_id = old.detail_type_id
+    and    i_have_global_priv(10023);
+create rule id_detail_types as
+on delete to detail_types
+do instead
+    delete from hidden.detail_types
+    where  detail_type_id = old.detail_type_id
+    and    i_have_global_priv(10024);
+
+grant select, insert, update, delete on detail_types to public;    
+
+
+create view assignments (
+       project_id,
+       person_id,
+       role_id) as
+select project_id,
+       person_id,
+       role_id
+from   hidden.assignments
+where  i_have_proj_or_pers_priv(10025, project_id, person_id);
+
+create rule ii_assignments as
+on insert to assignments
+do instead 
+    insert into hidden.assignments
+      (project_id, person_id, role_id)
+    select new.project_id, new.person_id, new.role_id
+    where  i_have_proj_or_pers_priv(10026, new.project_id, new.person_id);
+
+create rule iu_assignments as
+on update to assignments
+do instead
+    update hidden.assignments
+    set    project_id = new.project_id,
+      person_id = new.person_id, 
+      role_id = new.person_id
+    where  project_id = old.project_id
+    and    person_id = old.person_id
+    and    i_have_proj_or_pers_priv(10027, old.project_id, old.person_id);
+create rule id_assignments as
+on delete to assignments
+do instead
+    delete from hidden.assignments
+    where  project_id = old.project_id
+    and    person_id = old.person_id
+    and    i_have_proj_or_pers_priv(10028, old.project_id, old.person_id);
+
+grant select, insert, update, delete on assignments to public;    
+
+
+create view person_roles (
+       person_id,
+       role_id) as
+select person_id,
+       role_id
+from   hidden.person_roles
+where  i_have_personal_priv(10029, person_id);
+
+create rule ii_person_roles as
+on insert to person_roles
+do instead 
+    insert into hidden.person_roles
+      (person_id, role_id)
+    select new.person_id, new.role_id
+    where  i_have_personal_priv(10030, new.person_id);
+
+create rule iu_person_roles as
+on update to person_roles
+do instead
+    update hidden.person_roles
+    set    person_id = new.person_id,
+      role_id = new.role_id
+    where  person_id = old.person_id
+    and    role_id = old.role_id
+    and    i_have_personal_priv(10031, old.person_id);
+create rule id_person_roles as
+on delete to person_roles
+do instead
+    delete from hidden.person_roles
+    where  person_id = old.person_id
+    and    role_id = old.role_id
+    and    i_have_personal_priv(10032, old.person_id);
+
+grant select, insert, update, delete on person_roles to public;    
+
+
+create view project_details (
+       project_id,
+       detail_type_id,
+       value) as
+select project_id,
+       detail_type_id,
+       value
+from   hidden.project_details
+where  i_have_project_priv(10033, project_id)
+and    i_have_project_detail_priv(detail_type_id, project_id);
+
+create rule ii_project_details as
+on insert to project_details
+do instead 
+    insert into hidden.project_details
+      (project_id, detail_type_id, value)
+    select new.project_id, new.detail_type_id, new.value
+    where  i_have_project_priv(10034, new.project_id)
+    and    i_have_project_detail_priv(new.detail_type_id, new.project_id);
+
+create rule iu_project_details as
+on update to project_details
+do instead
+    update hidden.project_details
+    set    project_id = new.project_id, 
+      detail_type_id = new.detail_type_id, 
+      value = new.value
+    where  project_id = old.project_id
+    and    detail_type_id = old.detail_type_id
+    and    i_have_project_priv(10035, old.project_id)
+    and    i_have_project_detail_priv(old.detail_type_id, old.project_id);
+create rule id_project_details as
+on delete to project_details
+do instead
+    delete from hidden.project_details
+    where  project_id = old.project_id
+    and    detail_type_id = old.detail_type_id
+    and    i_have_project_priv(10036, old.project_id)
+    and    i_have_project_detail_priv(old.detail_type_id, old.project_id);
+
+grant select, insert, update, delete on project_details to public;    
+
+
+create view person_details (
+       person_id,
+       detail_type_id,
+       value) as
+select person_id,
+       detail_type_id,
+       value
+from   hidden.person_details
+where  i_have_personal_priv(10037, person_id)
+and    i_have_person_detail_priv(detail_type_id, person_id);
+
+create rule ii_person_details as
+on insert to person_details
+do instead 
+    insert into hidden.person_details
+      (person_id, detail_type_id, value)
+    select new.person_id, new.detail_type_id, new.value
+    where  i_have_personal_priv(10038, new.person_id)
+    and    i_have_person_detail_priv(new.detail_type_id, new.person_id);
+
+create rule iu_person_details as
+on update to person_details
+do instead
+    update hidden.person_details
+    set    person_id = new.person_id, 
+      detail_type_id = new.detail_type_id, 
+      value = new.value
+    where  person_id = old.person_id
+    and    detail_type_id = old.detail_type_id
+    and    i_have_personal_priv(10039, old.person_id)
+    and    i_have_person_detail_priv(old.detail_type_id, old.person_id);
+create rule id_person_details as
+on delete to person_details
+do instead
+    delete from hidden.person_details
+    where  person_id = old.person_id
+    and    detail_type_id = old.detail_type_id
+    and    i_have_personal_priv(10040, old.person_id)
+    and    i_have_person_detail_priv(old.detail_type_id, old.person_id);
+
+
+grant select, insert, update, delete on person_details to public;    
+
+
+create or replace
+function global_privs_qry() returns setof int4 as '
+select * from veil_bitmap_bits(''global_context'');
+' language sql
+stable security definer;
+
+create or replace view my_global_privs (
+       privilege_id) as
+select * from global_privs_qry();
+
+grant select on my_global_privs to public;    
+
+
+create or replace
+function personal_privs_qry() returns setof int4 as '
+select * from veil_bitmap_array_bits(''role_privs'', 11002);
+' language sql
+stable security definer;
+
+create or replace view my_personal_privs (
+       privilege_id) as
+select * from personal_privs_qry();
+
+grant select on my_personal_privs to public;    
+
+
+create or replace
+function projects_qry() returns setof varchar as '
+select * from veil_bitmap_hash_entries(''project_context'');
+' language sql
+stable security definer;
+
+create or replace view my_projects (
+       project_id) as
+select * from projects_qry();
+
+grant select on my_projects to public;    
+
+
+create type hidden.proj_privs as (
+    project_id  int4,
+    privilege_id int4
+);
+
+create or replace
+function project_privs() returns setof hidden.proj_privs as '
+declare
+    _project record;
+    _priv    record;
+    _result  hidden.proj_privs;
+begin
+    for _project in 
+        select * from veil_bitmap_hash_entries(''project_context'')
+    loop
+        _result.project_id = _project.veil_bitmap_hash_entries;
+        for _priv in
+            select * from veil_bitmap_hash_bits(''project_context'', 
+                                                _result.project_id)
+        loop
+            _result.privilege_id = _priv.veil_bitmap_hash_bits;
+            return next _result;
+        end loop;
+    end loop;
+    return;
+end;
+' language plpgsql
+stable security definer;
+
+grant execute on function project_privs() to public;
+
+create or replace view my_project_privs (
+       project_id,
+       privilege_id) as
+select * from project_privs();
+
+grant select on my_project_privs to public;    
+
+
+create or replace view my_privs (
+    context,
+    project,
+    privilege_id,
+    privilege_name)
+as 
+select a.context, a.project, a.id, p.privilege_name
+from   (
+        select 'Project' as context, project_id as project,
+               privilege_id as id, 3 as seq
+        from   my_project_privs
+        union all
+        select 'Global', null, privilege_id, 1
+        from   my_global_privs
+        union all
+        select 'Personal', null, privilege_id, 2
+        from   my_personal_privs
+       ) a,
+       privileges p
+where  p.privilege_id = a.id
+order by a.seq, a.context, a.project, a.id;
+
+grant select on my_privs to public;    
diff --git a/veil_demo.control b/veil_demo.control
new file mode 100644 (file)
index 0000000..94ddf30
--- /dev/null
@@ -0,0 +1,16 @@
+# veil-demo.control
+#
+#      Postgres extension control file for veil_demo
+#
+#      Copyright (c) 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+
+directory       = 'extension'
+default_version = '1.0'
+superuser       = true
+relocatable     = false
+requires   = veil
+
+comment = 'A demonstration application for veil'
diff --git a/veil_demo.mk b/veil_demo.mk
new file mode 100644 (file)
index 0000000..f384445
--- /dev/null
@@ -0,0 +1,16 @@
+# GNUmakefile
+#
+#      PGXS-based makefile for veil_demo
+#
+#      Copyright (c) 2005 - 2011 Marc Munro
+#      Author:  Marc Munro
+#      License: BSD
+#
+# For a list of targets use make help.
+# 
+
+EXTENSION=veil_demo
+DATA = $(wildcard veil_demo--*.sql)
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)