--- /dev/null
+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.
--- /dev/null
+# 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\
+"
--- /dev/null
+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.
--- /dev/null
+# ----------
+# GNUmakefile
+#
+# Copyright (c) 2005 - 2011 Marc Munro
+# Author: Marc Munro
+# License: BSD
+#
+# ----------
+#
+
+all:
+
+%::
+ cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+<?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 -->
--- /dev/null
+# 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
--- /dev/null
+# ----------
+# GNUmakefile
+#
+# Copyright (c) 2005 - 2011 Marc Munro
+# Author: Marc Munro
+# License: BSD
+#
+# ----------
+#
+
+all:
+
+%::
+ cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
--- /dev/null
+# 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
--- /dev/null
+<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>
--- /dev/null
+# ----------
+# GNUmakefile
+#
+# Copyright (c) 2005 - 2011 Marc Munro
+# Author: Marc Munro
+# License: BSD
+#
+#
+
+DEFAULT:
+
+%::
+ cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+- 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
--- /dev/null
+#! /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
--- /dev/null
+------------
+-- 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
--- /dev/null
+------------
+-- 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);
+
--- /dev/null
+# ----------
+# GNUmakefile
+#
+# Copyright (c) 2005 - 2011 Marc Munro
+# Author: Marc Munro
+# License: BSD
+#
+# ----------
+#
+
+all:
+
+%::
+ cd ..; $(MAKE) MAKEFLAGS="$(MAKEFLAGS)" $@
\ No newline at end of file
--- /dev/null
+# 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)
+
--- /dev/null
+/**
+ * @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;
+}
--- /dev/null
+src/veil_bitmap.o src/veil_bitmap.d: \
+ src/veil_bitmap.c \
+ src/veil_datatypes.h \
+ src/veil_funcs.h
--- /dev/null
+/**
+ * @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;
+}
+
+
--- /dev/null
+src/veil_config.o src/veil_config.d: \
+ src/veil_config.c \
+ src/veil_version.h \
+ src/veil_funcs.h \
+ src/veil_datatypes.h
--- /dev/null
+/**
+ * @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;
+}
+
--- /dev/null
+src/veil_datatypes.o src/veil_datatypes.d: \
+ src/veil_datatypes.c \
+ src/veil_datatypes.h \
+ src/veil_funcs.h
--- /dev/null
+/**
+ * @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
+
--- /dev/null
+
+\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
+\.
+
--- /dev/null
+/**
+ * @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);
--- /dev/null
+/**
+ * @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);
+}
+
+
--- /dev/null
+src/veil_interface.o src/veil_interface.d: \
+ src/veil_interface.c \
+ src/veil_version.h \
+ src/veil_funcs.h \
+ src/veil_datatypes.h
--- /dev/null
+/* ----------
+ * 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;
+
+
--- /dev/null
+/* ----------
+ * 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(<bitmap>, veil_bitmap_from_array(<bitmap_array>, <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(<bitmap>, veil_bitmap_from_array(<bitmap_array>, <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(<bitmap_hash>, <key>), <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(<bitmap>, veil_bitmap_from_hash(<bitmap_array>, <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(<bitmap>, veil_bitmap_from_hash(<bitmap_array>, <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.
+*/
+
--- /dev/null
+src/veil_mainpage.o src/veil_mainpage.d: \
+ src/veil_mainpage.c
--- /dev/null
+/**
+ * @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);
+}
+
+
--- /dev/null
+src/veil_query.o src/veil_query.d: \
+ src/veil_query.c \
+ src/veil_version.h \
+ src/veil_funcs.h \
+ src/veil_datatypes.h
--- /dev/null
+/**
+ * @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;
+}
--- /dev/null
+src/veil_serialise.o src/veil_serialise.d: \
+ src/veil_serialise.c \
+ src/veil_version.h \
+ src/veil_funcs.h \
+ src/veil_datatypes.h
--- /dev/null
+/**
+ * @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;
+}
+
--- /dev/null
+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
--- /dev/null
+/**
+ * @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
+
--- /dev/null
+/**
+ * @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];
+ }
+}
+
--- /dev/null
+src/veil_utils.o src/veil_utils.d: \
+ src/veil_utils.c \
+ src/veil_funcs.h \
+ src/veil_datatypes.h
--- /dev/null
+/**
+ * @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];
+}
+
--- /dev/null
+src/veil_variables.o src/veil_variables.d: \
+ src/veil_variables.c \
+ src/veil_datatypes.h \
+ src/veil_funcs.h
--- /dev/null
+/**
+ * @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"
+
--- /dev/null
+#! /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)
+ }
+ '
+}
--- /dev/null
+/* ----------
+ * 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;
+
+
--- /dev/null
+# 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.'
--- /dev/null
+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;
--- /dev/null
+# 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'
--- /dev/null
+# 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)