diff --git a/contrib/array/Makefile b/contrib/array/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..03c57e57c6822f77a517d2beac5fc7795056d78f
--- /dev/null
+++ b/contrib/array/Makefile
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for array iterator functions.
+#
+#-------------------------------------------------------------------------
+
+PGDIR = ../..
+SRCDIR = $(PGDIR)/src
+
+include $(SRCDIR)/Makefile.global
+
+INCLUDE_OPT =	-I ./ \
+		-I $(SRCDIR)/ \
+		-I $(SRCDIR)/include \
+		-I $(SRCDIR)/port/$(PORTNAME)
+
+CFLAGS += $(INCLUDE_OPT)
+
+ifeq ($(PORTNAME), linux)
+  ifdef LINUX_ELF
+    ifeq ($(CC), gcc)
+      CFLAGS += -fPIC
+    endif
+  endif
+endif
+
+ifeq ($(PORTNAME), i386_solaris)
+  CFLAGS+= -fPIC
+endif
+
+MODNAME =	array_iterator
+
+MODULE =	$(MODNAME)$(DLSUFFIX)
+
+all:		module sql
+
+module:		$(MODULE)
+
+sql:		$(MODNAME).sql
+
+install:	$(MODULE)
+		cp -p $(MODULE) $(LIBDIR)
+		cd $(LIBDIR); strip $(MODULE)
+
+%.sql: %.sql.in
+		sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
+
+.SUFFIXES: $(DLSUFFIX)
+
+%$(DLSUFFIX): %.c
+		cc $(CFLAGS) -shared -o $@ $<
+
+depend dep:
+		$(CC) -MM $(INCLUDE_OPT) *.c >depend
+
+clean:
+		rm -f $(MODULE) $(MODNAME).sql
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/contrib/array/array_iterator.c b/contrib/array/array_iterator.c
index f4ecfad903fbb58e72c1026e89884a5652de195d..de8dac95de0d371b0998e73bfdb022d23d753ab2 100644
--- a/contrib/array/array_iterator.c
+++ b/contrib/array/array_iterator.c
@@ -1,30 +1,12 @@
 /*
  * array_iterator.c --
  *
- * This file defines a new group of operators which take an
+ * This file defines a new class of operators which take an
  * array and a scalar value, iterate a scalar operator over the
  * elements of the array and the value and compute a result as
- * the logical OR or AND of the results.
- * For example array_int4eq returns true if some of the elements
- * of an array of int4 is equal to the given value:
+ * the logical OR or AND of the iteration results.
  *
- *		array_int4eq({1,2,3}, 1)  -->  true
- *		array_int4eq({1,2,3}, 4)  -->  false
- *
- * If we have defined T array types and O scalar operators
- * we can define T x O array operators, each of them has a name
- * like "array_<basetype><operation>" and takes an array of type T
- * iterating the operator O over all the elements. Note however
- * that some of the possible combination are invalid, for example
- * the array_int4_like because there is no like operator for int4.
- * It is now possible to write queries which look inside the arrays:
- *
- *		create table t(id int4[], txt text[]);
- *		select * from t where t.id *= 123;
- *		select * from t where t.txt *~ '[a-z]';
- *		select * from t where t.txt[1:3] **~ '[a-z]';
- *
- * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
+ * Copyright (c) 1997, Massimo Dal Zotto <dz@cs.unitn.it>
  */
 
 #include <ctype.h>
@@ -33,242 +15,297 @@
 #include <string.h>
 
 #include "postgres.h"
-#include "pg_type.h"
 #include "miscadmin.h"
-#include "syscache.h"
 #include "access/xact.h"
+#include "backend/fmgr.h"
+#include "catalog/pg_type.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
-#include "utils/elog.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+
+#include "array_iterator.h"
 
 static int32
 array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
 {
-	HeapTuple	typ_tuple;
-	TypeTupleForm typ_struct;
-	bool		typbyval;
-	int			typlen;
-	func_ptr	proc_fn;
-	int			pronargs;
-	int			nitems,
-				i,
-				result;
-	int			ndim,
-			   *dim;
-	char	   *p;
-
-	/* Sanity checks */
-	if ((array == (ArrayType *) NULL)
-		|| (ARR_IS_LO(array) == true))
-	{
-		/* elog(NOTICE, "array_iterator: array is null"); */
-		return (0);
-	}
-	ndim = ARR_NDIM(array);
-	dim = ARR_DIMS(array);
-	nitems = getNitems(ndim, dim);
-	if (nitems == 0)
-	{
-		/* elog(NOTICE, "array_iterator: nitems = 0"); */
-		return (0);
-	}
+    HeapTuple typ_tuple;
+    TypeTupleForm typ_struct;
+    bool typbyval;
+    int typlen;
+    func_ptr proc_fn;
+    int pronargs;
+    int nitems, i, result;
+    int ndim, *dim;
+    char *p;
 
-	/* Lookup element type information */
-	typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype), 0, 0, 0);
-	if (!HeapTupleIsValid(typ_tuple))
-	{
-		elog(WARN, "array_iterator: cache lookup failed for type %d", elemtype);
-		return 0;
-	}
-	typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
-	typlen = typ_struct->typlen;
-	typbyval = typ_struct->typbyval;
-
-	/* Lookup the function entry point */
-	proc_fn == (func_ptr) NULL;
-	fmgr_info(proc, &proc_fn, &pronargs);
-	if ((proc_fn == NULL) || (pronargs != 2))
-	{
-		elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
-		return (0);
-	}
+    /* Sanity checks */
+    if ((array == (ArrayType *) NULL)
+	|| (ARR_IS_LO(array) == true)) {
+	/* elog(NOTICE, "array_iterator: array is null"); */
+        return (0);
+    }
+    ndim = ARR_NDIM(array);
+    dim = ARR_DIMS(array);
+    nitems = getNitems(ndim, dim);
+    if (nitems == 0) {
+	/* elog(NOTICE, "array_iterator: nitems = 0"); */
+        return (0);
+    }
 
-	/* Scan the array and apply the operator to each element */
-	result = 0;
-	p = ARR_DATA_PTR(array);
-	for (i = 0; i < nitems; i++)
-	{
-		if (typbyval)
-		{
-			switch (typlen)
-			{
-				case 1:
-					result = (int) (*proc_fn) (*p, value);
-					break;
-				case 2:
-					result = (int) (*proc_fn) (*(int16 *) p, value);
-					break;
-				case 3:
-				case 4:
-					result = (int) (*proc_fn) (*(int32 *) p, value);
-					break;
-			}
-			p += typlen;
-		}
-		else
-		{
-			result = (int) (*proc_fn) (p, value);
-			if (typlen > 0)
-			{
-				p += typlen;
-			}
-			else
-			{
-				p += INTALIGN(*(int32 *) p);
-			}
-		}
-		if (result)
-		{
-			if (!and)
-			{
-				return (1);
-			}
-		}
-		else
-		{
-			if (and)
-			{
-				return (0);
-			}
-		}
-	}
+    /* Lookup element type information */
+    typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype),0,0,0);
+    if (!HeapTupleIsValid(typ_tuple)) {
+        elog(WARN,"array_iterator: cache lookup failed for type %d", elemtype);
+        return 0;
+    }
+    typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
+    typlen   = typ_struct->typlen;
+    typbyval = typ_struct->typbyval;
 
-	if (and && result)
-	{
+    /* Lookup the function entry point */
+    proc_fn = (func_ptr) NULL;
+    fmgr_info(proc, &proc_fn, &pronargs);
+    if ((proc_fn == NULL) || (pronargs != 2)) {
+	elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
+        return (0);
+    }
+
+    /* Scan the array and apply the operator to each element */
+    result = 0;
+    p = ARR_DATA_PTR(array);
+    for (i = 0; i < nitems; i++) {
+        if (typbyval) {
+            switch(typlen) {
+	      case 1:
+		result = (int) (*proc_fn)(*p, value);
+		break;
+	    case 2:
+		result = (int) (*proc_fn)(* (int16 *) p, value);
+		break;
+	    case 3:
+	    case 4:
+		result = (int) (*proc_fn)(* (int32 *) p, value);
+		break;
+            }
+            p += typlen;
+        } else {
+	    result = (int) (*proc_fn)(p, value);
+            if (typlen > 0) {
+		p += typlen;
+	    } else {
+                p += INTALIGN(* (int32 *) p);
+	    }
+        }
+	if (result) {
+	    if (!and) {
 		return (1);
-	}
-	else
-	{
+	    }
+	} else {
+	    if (and) {
 		return (0);
+	    }
 	}
+    }
+
+    if (and && result) {
+	return (1);
+    } else {
+	return (0);
+    }
 }
 
 /*
- * Iterators for type _text
+ * Iterator functions for type _text
  */
 
 int32
-array_texteq(ArrayType *array, char *value)
+array_texteq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 25,		/* text */
-						  (Oid) 67,		/* texteq */
-						  0,	/* logical or */
-						  array, (Datum) value);
+    return array_iterator((Oid) 25,	/* text */
+			  (Oid) 67,	/* texteq */
+			  0,		/* logical or */
+			  array, (Datum)value);
 }
 
 int32
-array_all_texteq(ArrayType *array, char *value)
+array_all_texteq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 25,		/* text */
-						  (Oid) 67,		/* texteq */
-						  1,	/* logical and */
-						  array, (Datum) value);
+    return array_iterator((Oid) 25,	/* text */
+			  (Oid) 67,	/* texteq */
+			  1,		/* logical and */
+			  array, (Datum)value);
 }
 
 int32
-array_textregexeq(ArrayType *array, char *value)
+array_textregexeq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 25,		/* text */
-						  (Oid) 81,		/* textregexeq */
-						  0,	/* logical or */
-						  array, (Datum) value);
+    return array_iterator((Oid) 25,	/* text */
+			  (Oid) 1254,	/* textregexeq */
+			  0,		/* logical or */
+			  array, (Datum)value);
 }
 
 int32
-array_all_textregexeq(ArrayType *array, char *value)
+array_all_textregexeq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 25,		/* text */
-						  (Oid) 81,		/* textregexeq */
-						  1,	/* logical and */
-						  array, (Datum) value);
+    return array_iterator((Oid) 25,	/* text */
+			  (Oid) 1254,	/* textregexeq */
+			  1,		/* logical and */
+			  array, (Datum)value);
 }
 
 /*
- * Iterators for type _char16. Note that the regexp operators
- * take the second argument of type text.
+ * Iterator functions for type _char16. Note that the regexp
+ * operators take the second argument of type text.
  */
 
 int32
-array_char16eq(ArrayType *array, char *value)
+array_char16eq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 20,		/* char16 */
-						  (Oid) 490,	/* char16eq */
-						  0,	/* logical or */
-						  array, (Datum) value);
+    return array_iterator((Oid) 20,	/* char16 */
+			  (Oid) 1275,	/* char16eq */
+			  0,		/* logical or */
+			  array, (Datum)value);
 }
 
 int32
-array_all_char16eq(ArrayType *array, char *value)
+array_all_char16eq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 20,		/* char16 */
-						  (Oid) 490,	/* char16eq */
-						  1,	/* logical and */
-						  array, (Datum) value);
+    return array_iterator((Oid) 20,	/* char16 */
+			  (Oid) 1275,	/* char16eq */
+			  1,		/* logical and */
+			  array, (Datum)value);
 }
 
 int32
-array_char16regexeq(ArrayType *array, char *value)
+array_char16regexeq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 20,		/* char16 */
-						  (Oid) 700,	/* char16regexeq */
-						  0,	/* logical or */
-						  array, (Datum) value);
+    return array_iterator((Oid) 20,	/* char16 */
+			  (Oid) 1288,	/* char16regexeq */
+			  0,		/* logical or */
+			  array, (Datum)value);
 }
 
 int32
-array_all_char16regexeq(ArrayType *array, char *value)
+array_all_char16regexeq(ArrayType *array, char* value)
 {
-	return array_iterator((Oid) 20,		/* char16 */
-						  (Oid) 700,	/* char16regexeq */
-						  1,	/* logical and */
-						  array, (Datum) value);
+    return array_iterator((Oid) 20,	/* char16 */
+			  (Oid) 1288,	/* char16regexeq */
+			  1,		/* logical and */
+			  array, (Datum)value);
 }
 
 /*
- * Iterators for type _int4
+ * Iterator functions for type _int4
  */
 
 int32
 array_int4eq(ArrayType *array, int4 value)
 {
-	return array_iterator((Oid) 23,		/* int4 */
-						  (Oid) 65,		/* int4eq */
-						  0,	/* logical or */
-						  array, (Datum) value);
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 65,	/* int4eq */
+			  0,		/* logical or */
+			  array, (Datum)value);
 }
 
 int32
 array_all_int4eq(ArrayType *array, int4 value)
 {
-	return array_iterator((Oid) 23,		/* int4 */
-						  (Oid) 65,		/* int4eq */
-						  1,	/* logical and */
-						  array, (Datum) value);
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 65,	/* int4eq */
+			  1,		/* logical and */
+			  array, (Datum)value);
+}
+
+int32
+array_int4ne(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 144,	/* int4ne */
+			  0,		/* logical or */
+			  array, (Datum)value);
+}
+
+int32
+array_all_int4ne(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 144,	/* int4ne */
+			  1,		/* logical and */
+			  array, (Datum)value);
 }
 
 int32
 array_int4gt(ArrayType *array, int4 value)
 {
-	return array_iterator((Oid) 23,		/* int4 */
-						  (Oid) 147,	/* int4gt */
-						  0,	/* logical or */
-						  array, (Datum) value);
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 147,	/* int4gt */
+			  0,		/* logical or */
+			  array, (Datum)value);
 }
 
 int32
 array_all_int4gt(ArrayType *array, int4 value)
 {
-	return array_iterator((Oid) 23,		/* int4 */
-						  (Oid) 147,	/* int4gt */
-						  1,	/* logical and */
-						  array, (Datum) value);
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 147,	/* int4gt */
+			  1,		/* logical and */
+			  array, (Datum)value);
+}
+
+int32
+array_int4ge(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 150,	/* int4ge */
+			  0,		/* logical or */
+			  array, (Datum)value);
+}
+
+int32
+array_all_int4ge(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 150,	/* int4ge */
+			  1,		/* logical and */
+			  array, (Datum)value);
+}
+
+int32
+array_int4lt(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 66,	/* int4lt */
+			  0,		/* logical or */
+			  array, (Datum)value);
+}
+
+int32
+array_all_int4lt(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 66,	/* int4lt */
+			  1,		/* logical and */
+			  array, (Datum)value);
+}
+
+int32
+array_int4le(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 149,	/* int4le */
+			  0,		/* logical or */
+			  array, (Datum)value);
+}
+
+int32
+array_all_int4le(ArrayType *array, int4 value)
+{
+    return array_iterator((Oid) 23,	/* int4 */
+			  (Oid) 149,	/* int4le */
+			  1,		/* logical and */
+			  array, (Datum)value);
 }
+
+/* end of file */
diff --git a/contrib/array/array_iterator.doc b/contrib/array/array_iterator.doc
index 01c1b2195cfbef6822ca6c47660942231ddbd8ec..031301799c6fc673180f699de563dd9deae30e73 100644
--- a/contrib/array/array_iterator.doc
+++ b/contrib/array/array_iterator.doc
@@ -1,26 +1,44 @@
-From: Massimo Dal Zotto <dz@cs.unitn.it>
-Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST)
-Subject: [PG95]: new operators for arrays
+Array iterator functions, by Massimo Dal Zotto <dz@cs.unitn.it>
 
-- -----BEGIN PGP SIGNED MESSAGE-----
+This loadable module defines a new class of functions which take
+an array and a scalar value, iterate a scalar operator over the
+elements of the array and the value, and compute a result as
+the logical OR or AND of the iteration results.
+For example array_int4eq returns true if some of the elements
+of an array of int4 is equal to the given value:
 
-Hi,
+	array_int4eq({1,2,3}, 1)  -->  true
+	array_int4eq({1,2,3}, 4)  -->  false
 
-I have written an extension to Postgres95 which allows to use qualification
-clauses based on the values of single elements of arrays.
-For example I can now select rows having some or all element of an array
+If we have defined T array types and O scalar operators we can
+define T x O x 2 array functions, each of them has a name like
+"array_[all_]<basetype><operation>" and takes an array of type T
+iterating the operator O over all the elements. Note however
+that some of the possible combination are invalid, for example
+the array_int4_like because there is no like operator for int4.
+
+We can then define new operators based on these functions and use
+them to write queries with qualification clauses based on the
+values of some of the elements of an array.
+For example to select rows having some or all element of an array
 attribute equal to a given value or matching a regular expression:
 
-select * from t where t.foo *= 'bar';
-select * from t where t.foo **~ '^ba[rz]';
+	create table t(id int4[], txt text[]);
+
+	-- select tuples with some id element equal to 123
+	select * from t where t.id *= 123;
+
+	-- select tuples with some txt element matching '[a-z]'
+	select * from t where t.txt *~ '[a-z]';
+
+	-- select tuples with all txt elements matching '^[A-Z]'
+	select * from t where t.txt[1:3] **~ '^[A-Z]';
 
-The scheme is quite general, each operator which operates on a base type can
-be iterated over the elements of an array. It seem to work well but defining
-each new operators requires writing a different C function. Furthermore in
-each function there are two hardcoded OIDs which reference a base type and
-a procedure. Not very portable. Can anyone suggest a better and more portable
-way to do it ?  Do you think this could be a useful feature for next release ?
-Here is my code, it can be compiled and loaded as a dynamic module without
-need to recompile the backend. I have defined only the few operators I needed,
-the list can be extended. Feddback is welcome.
+The scheme is quite general, each operator which operates on a base type
+can be iterated over the elements of an array. It seem to work well but
+defining each new operators requires writing a different C function.
+Furthermore in each function there are two hardcoded OIDs which reference
+a base type and a procedure. Not very portable. Can anyone suggest a
+better and more portable way to do it ?
 
+See also array_iterator.sql for an example on how to use this module.
diff --git a/contrib/array/array_iterator.h b/contrib/array/array_iterator.h
new file mode 100644
index 0000000000000000000000000000000000000000..0d9c58ed00b3789b6ba2eb8ea7c3cb50c8fa0bfe
--- /dev/null
+++ b/contrib/array/array_iterator.h
@@ -0,0 +1,27 @@
+#ifndef ARRAY_ITERATOR_H
+#define ARRAY_ITERATOR_H
+
+static int32 array_iterator(Oid elemtype, Oid proc, int and, 
+			    ArrayType *array, Datum value);
+int32 array_texteq(ArrayType *array, char* value);
+int32 array_all_texteq(ArrayType *array, char* value);
+int32 array_textregexeq(ArrayType *array, char* value);
+int32 array_all_textregexeq(ArrayType *array, char* value);
+int32 array_char16eq(ArrayType *array, char* value);
+int32 array_all_char16eq(ArrayType *array, char* value);
+int32 array_char16regexeq(ArrayType *array, char* value);
+int32 array_all_char16regexeq(ArrayType *array, char* value);
+int32 array_int4eq(ArrayType *array, int4 value);
+int32 array_all_int4eq(ArrayType *array, int4 value);
+int32 array_int4ne(ArrayType *array, int4 value);
+int32 array_all_int4ne(ArrayType *array, int4 value);
+int32 array_int4gt(ArrayType *array, int4 value);
+int32 array_all_int4gt(ArrayType *array, int4 value);
+int32 array_int4ge(ArrayType *array, int4 value);
+int32 array_all_int4ge(ArrayType *array, int4 value);
+int32 array_int4lt(ArrayType *array, int4 value);
+int32 array_all_int4lt(ArrayType *array, int4 value);
+int32 array_int4le(ArrayType *array, int4 value);
+int32 array_all_int4le(ArrayType *array, int4 value);
+
+#endif
diff --git a/contrib/array/array_iterator.sql.in b/contrib/array/array_iterator.sql.in
new file mode 100644
index 0000000000000000000000000000000000000000..6489545d97d1e60e6a908fb2be25fe5a8b5a5a71
--- /dev/null
+++ b/contrib/array/array_iterator.sql.in
@@ -0,0 +1,191 @@
+-- SQL code to define the new array iterator functions and operators
+
+-- define the array operators *=, **=, *~ and **~ for type _text
+--
+create function array_texteq(_text, text) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_texteq(_text, text) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_textregexeq(_text, text) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_textregexeq(_text, text) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create operator *= (
+  leftarg=_text, 
+  rightarg=text, 
+  procedure=array_texteq);
+
+create operator **= (
+  leftarg=_text,
+  rightarg=text,
+  procedure=array_all_texteq);
+
+create operator *~ (
+  leftarg=_text,
+  rightarg=text,
+  procedure=array_textregexeq);
+
+create operator **~ (
+  leftarg=_text,
+  rightarg=text,
+  procedure=array_all_textregexeq);
+
+
+-- define the array operators *=, **=, *~ and **~ for type _char16
+--
+create function array_char16eq(_char16, char16) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_char16eq(_char16, char16) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_char16regexeq(_char16, text) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_char16regexeq(_char16, text) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create operator *= (
+  leftarg=_char16,
+  rightarg=char16,
+  procedure=array_char16eq);
+
+create operator **= (
+  leftarg=_char16,
+  rightarg=char16,
+  procedure=array_all_char16eq);
+
+create operator *~ (
+  leftarg=_char16,
+  rightarg=text,
+  procedure=array_char16regexeq);
+
+create operator **~ (
+  leftarg=_char16,
+  rightarg=text,
+  procedure=array_all_char16regexeq);
+
+
+-- define the array operators *=, **=, *> and **> for type _int4
+--
+create function array_int4eq(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_int4eq(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_int4ne(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_int4ne(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_int4gt(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_int4gt(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_int4ge(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_int4ge(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_int4lt(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_int4lt(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_int4le(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function array_all_int4le(_int4, int4) returns bool
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create operator *= (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_int4eq);
+
+create operator **= (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_all_int4eq);
+
+create operator *<> (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_int4ne);
+
+create operator **<> (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_all_int4ne);
+
+create operator *> (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_int4gt);
+
+create operator **> (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_all_int4gt);
+
+create operator *>= (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_int4ge);
+
+create operator **>= (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_all_int4ge);
+
+create operator *< (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_int4lt);
+
+create operator **< (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_all_int4lt);
+
+create operator *<= (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_int4le);
+
+create operator **<= (
+  leftarg=_int4,
+  rightarg=int4,
+  procedure=array_all_int4le);
+
+-- end of file
diff --git a/contrib/datetime/Makefile b/contrib/datetime/Makefile
index 930d6e57cfd9e6c271cc980f9f221bdf59ff9474..52023b8e8aff37cad5143eea77e75930f64d3644 100644
--- a/contrib/datetime/Makefile
+++ b/contrib/datetime/Makefile
@@ -1,12 +1,62 @@
-D=/usr/postgres
-P=$D/lib/datetime_functions.so
-CFLAGS=-fpic -O -I../../src/include -I../../src/backend
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for new date/time functions.
+#
+#-------------------------------------------------------------------------
 
-all: $P datetime_functions.sql
+PGDIR = ../..
+SRCDIR = $(PGDIR)/src
 
-$P:datetime_functions.o 
-	ld -Bshareable -o $P datetime_functions.o 
+include $(SRCDIR)/Makefile.global
 
-datetime_functions.sql: datetime.prot
-	sh datetime.prot $P
-	psql -c "\idatetime_functions.sql" template1
+INCLUDE_OPT =	-I ./ \
+		-I $(SRCDIR)/ \
+		-I $(SRCDIR)/include \
+		-I $(SRCDIR)/port/$(PORTNAME)
+
+CFLAGS += $(INCLUDE_OPT)
+
+ifeq ($(PORTNAME), linux)
+  ifdef LINUX_ELF
+    ifeq ($(CC), gcc)
+      CFLAGS += -fPIC
+    endif
+  endif
+endif
+
+ifeq ($(PORTNAME), i386_solaris)
+  CFLAGS+= -fPIC
+endif
+
+MODNAME =	datetime_functions
+
+MODULE =	$(MODNAME)$(DLSUFFIX)
+
+all:		module sql
+
+module:		$(MODULE)
+
+sql:		$(MODNAME).sql
+
+install:	$(MODULE)
+		cp -p $(MODULE) $(LIBDIR)
+		cd $(LIBDIR); strip $(MODULE)
+
+%.sql: %.sql.in
+		sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
+
+.SUFFIXES: $(DLSUFFIX)
+
+%$(DLSUFFIX): %.c
+		cc $(CFLAGS) -shared -o $@ $<
+
+depend dep:
+		$(CC) -MM $(INCLUDE_OPT) *.c >depend
+
+clean:
+		rm -f $(MODULE) $(MODNAME).sql
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/contrib/datetime/datetime_functions.c b/contrib/datetime/datetime_functions.c
index d2c097583cf91d61a3644b2c7c405cc132e8f3a5..e925d985bfb6fb7b0ca433663c48056bd5f574b8 100644
--- a/contrib/datetime/datetime_functions.c
+++ b/contrib/datetime/datetime_functions.c
@@ -6,113 +6,208 @@
  * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
  */
 
-#include <time.h>
+#include <stdio.h>		/* for sprintf() */
+#include <string.h>
+#include <limits.h>
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
 
 #include "postgres.h"
-#include "utils/palloc.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/nabstime.h"
 #include "utils/datetime.h"
+#include "access/xact.h"
 
+#include "datetime_functions.h"
 
-TimeADT    *
-time_difference(TimeADT *time1, TimeADT *time2)
+/* Constant to replace calls to date2j(2000,1,1) */
+#define JDATE_2000	2451545
+
+/*
+ * A modified version of time_in which allows the value 24:00:00 for
+ * time and converts it to TimeADT data type forcing seconds to 0.
+ * This can be Useful if you need to handle TimeADT values limited
+ * to hh:mm like in timetables.
+ */
+
+TimeADT *
+hhmm_in(char *str)
+{
+    TimeADT *time;
+
+    double fsec;
+    struct tm tt, *tm = &tt;
+
+    int nf;
+    char lowstr[MAXDATELEN+1];
+    char *field[MAXDATEFIELDS];
+    int dtype;
+    int ftype[MAXDATEFIELDS];
+
+    if (!PointerIsValid(str))
+        elog(WARN,"Bad (null) time external representation",NULL);
+
+    if ((ParseDateTime( str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
+     || (DecodeTimeOnly( field, ftype, nf, &dtype, tm, &fsec) != 0))
+        elog(WARN,"Bad time external representation '%s'",str);
+
+    if (tm->tm_hour<0 || tm->tm_hour>24 || 
+	(tm->tm_hour==24 && (tm->tm_min!=0 || tm->tm_sec!=0 || fsec!= 0))) {
+        elog(WARN,
+	     "time_in: hour must be limited to values 0 through 24:00 "
+	     "in \"%s\"",
+	     str);
+    }
+    if ((tm->tm_min < 0) || (tm->tm_min > 59))
+	elog(WARN,"Minute must be limited to values 0 through 59 in '%s'",str);
+    if ((tm->tm_sec < 0) || ((tm->tm_sec + fsec) >= 60))
+	elog(WARN,"Second must be limited to values 0 through < 60 in '%s'",
+	     str);
+
+    time = PALLOCTYPE(TimeADT);
+
+    *time = ((((tm->tm_hour*60)+tm->tm_min)*60));
+
+    return(time);
+}
+
+/*
+ * A modified version of time_out which converts from TimeADT data type
+ * omitting the seconds field when it is 0.
+ * Useful if you need to handle TimeADT values limited to hh:mm.
+ */
+
+char *
+hhmm_out(TimeADT *time)
 {
-	TimeADT    *result = (TimeADT *) palloc(sizeof(TimeADT));
+    char *result;
+    struct tm tt, *tm = &tt;
+    char buf[MAXDATELEN+1];
+
+    if (!PointerIsValid(time))
+	return NULL;
+
+    tm->tm_hour = (*time / (60*60));
+    tm->tm_min = (((int) (*time / 60)) % 60);
+    tm->tm_sec = (((int) *time) % 60);
+
+    if (tm->tm_sec == 0) {
+	sprintf(buf, "%02d:%02d", tm->tm_hour, tm->tm_min);
+    } else {
+	sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+    }
 
-	*result = *time1 - *time2;
-	return (result);
+    result = PALLOC(strlen(buf)+1);
+
+    strcpy( result, buf);
+
+    return(result);
 }
 
-TimeADT    *
-currenttime()
+TimeADT *
+hhmm(TimeADT *time)
 {
-	time_t		current_time;
-	struct tm  *tm;
-	TimeADT    *result = (TimeADT *) palloc(sizeof(TimeADT));
-
-	current_time = time(NULL);
-	tm = localtime(&current_time);
-	*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
-	return (result);
+    TimeADT *result = PALLOCTYPE(TimeADT);
+
+    *result = (((int) *time) / 60 * 60);
+
+    return(result);
 }
 
-DateADT
-currentdate()
+TimeADT *
+time_difference(TimeADT *time1, TimeADT *time2)
 {
-	time_t		current_time;
-	struct tm  *tm;
-	DateADT		result;
+    TimeADT *time = PALLOCTYPE(TimeADT);
 
-	current_time = time(NULL);
-	tm = localtime(&current_time);
+    *time = (*time1 - *time2);
+    return(time);
+}
+
+int4
+time_hours(TimeADT *time)
+{
+    return (((int) *time) / 3600);
+}
 
-	result = date2j(tm->tm_year, tm->tm_mon + 1, tm->tm_mday) -
-		date2j(100, 1, 1);
-	return (result);
+int4
+time_minutes(TimeADT *time)
+{
+    return ((((int) *time) / 60) % 60);
 }
 
 int4
-hours(TimeADT *time)
+time_seconds(TimeADT *time)
 {
-	return (*time / (60 * 60));
+    return (((int) *time) % 60);
 }
 
 int4
-minutes(TimeADT *time)
+as_minutes(TimeADT *time)
 {
-	return (((int) (*time / 60)) % 60);
+    return (((int) *time) / 60);
 }
 
 int4
-seconds(TimeADT *time)
+as_seconds(TimeADT *time)
 {
-	return (((int) *time) % 60);
+    return ((int) *time);
 }
 
 int4
-day(DateADT *date)
+date_day(DateADT val)
 {
-	struct tm	tm;
+    int year, month, day;
 
-	j2date((*date + date2j(2000, 1, 1)),
-		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
+    j2date(val + JDATE_2000, &year, &month, &day);
 
-	return (tm.tm_mday);
+    return (day);
 }
 
 int4
-month(DateADT *date)
+date_month(DateADT val)
 {
-	struct tm	tm;
+    int year, month, day;
 
-	j2date((*date + date2j(2000, 1, 1)),
-		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
+    j2date(val + JDATE_2000, &year, &month, &day);
 
-	return (tm.tm_mon);
+    return (month);
 }
 
 int4
-year(DateADT *date)
+date_year(DateADT val)
 {
-	struct tm	tm;
+    int year, month, day;
 
-	j2date((*date + date2j(2000, 1, 1)),
-		   &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
+    j2date(val + JDATE_2000, &year, &month, &day);
 
-	return (tm.tm_year);
+    return (year);
 }
 
-int4
-asminutes(TimeADT *time)
+TimeADT *
+currenttime()
 {
-	int			seconds = (int) *time;
+    TimeADT *result = PALLOCTYPE(TimeADT);
+    struct tm *tm;
+    time_t current_time;
+
+    current_time = time(NULL);
+    tm = localtime(&current_time);
+    *result = ((((tm->tm_hour*60)+tm->tm_min)*60)+tm->tm_sec);
 
-	return (seconds / 60);
+    return (result);
 }
 
-int4
-asseconds(TimeADT *time)
+DateADT
+currentdate()
 {
-	int			seconds = (int) *time;
+    DateADT date;
+    struct tm tt, *tm = &tt;
 
-	return (seconds);
+    GetCurrentTime(tm);
+    date = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - JDATE_2000);
+    return (date);
 }
+
+/* end of file */
diff --git a/contrib/datetime/datetime_functions.h b/contrib/datetime/datetime_functions.h
new file mode 100644
index 0000000000000000000000000000000000000000..684749840598102e53d3397a0f243b01822d89fd
--- /dev/null
+++ b/contrib/datetime/datetime_functions.h
@@ -0,0 +1,19 @@
+#ifndef DATETIME_FUNCTIONS_H
+#define DATETIME_FUNCTIONS_H
+
+TimeADT *hhmm_in(char *str);
+char *hhmm_out(TimeADT *time);
+TimeADT *hhmm(TimeADT *time);
+TimeADT *time_difference(TimeADT *time1, TimeADT *time2);
+int4 time_hours(TimeADT *time);
+int4 time_minutes(TimeADT *time);
+int4 time_seconds(TimeADT *time);
+int4 as_minutes(TimeADT *time);
+int4 as_seconds(TimeADT *time);
+int4 date_day(DateADT val);
+int4 date_month(DateADT val);
+int4 date_year(DateADT val);
+TimeADT *currenttime(void);
+DateADT currentdate(void);
+
+#endif
diff --git a/contrib/datetime/datetime_functions.sql.in b/contrib/datetime/datetime_functions.sql.in
new file mode 100644
index 0000000000000000000000000000000000000000..81e40aa9af19636df0aea6fd445c075ca38a00a3
--- /dev/null
+++ b/contrib/datetime/datetime_functions.sql.in
@@ -0,0 +1,96 @@
+-- SQL code to define the new date and time functions and operators
+
+-- Define the new functions
+--
+create function hhmm_in(opaque) returns time
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function hhmm_out(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function hhmm(time) returns time
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function time_difference(time,time) returns time
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function time_hours(time) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function time_minutes(time) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function time_seconds(time) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function as_minutes(time) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function as_seconds(time) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function date_day(date) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function date_month(date) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function date_year(date) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function currenttime() returns time
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function currentdate() returns date
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+
+-- Define a new operator - for time
+--
+create operator - (
+  leftarg=time, 
+  rightarg=time, 
+  procedure=time_difference);
+
+
+-- Define functions to switch from time to hhmm representation
+--
+--   select hhmm_mode();
+--   select time_mode();
+--
+create function hhmm_mode() returns text
+  as 'update pg_type set typinput =''hhmm_in''  where typname=''time'';
+      update pg_type set typoutput=''hhmm_out'' where typname=''time''
+      select ''hhmm_mode''::text'
+  language 'sql';
+
+create function time_mode() returns text
+  as 'update pg_type set typinput =''time_in''  where typname=''time'';
+      update pg_type set typoutput=''time_out'' where typname=''time''
+      select ''time_mode''::text'
+  language 'sql';
+
+
+-- Use these to do the updates manually
+--
+-- update pg_type set typinput ='hhmm_in'  where typname='time';
+-- update pg_type set typoutput='hhmm_out' where typname='time';
+--
+-- update pg_type set typinput ='time_in'  where typname='time';
+-- update pg_type set typoutput='time_out' where typname='time';
+
+-- end of file
diff --git a/contrib/miscutil/Makefile b/contrib/miscutil/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..982515ab296af05d41df3cea2da712622c0e445d
--- /dev/null
+++ b/contrib/miscutil/Makefile
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for array iterator functions.
+#
+#-------------------------------------------------------------------------
+
+PGDIR = ../..
+SRCDIR = $(PGDIR)/src
+
+include $(SRCDIR)/Makefile.global
+
+INCLUDE_OPT =	-I ./ \
+		-I $(SRCDIR)/ \
+		-I $(SRCDIR)/include \
+		-I $(SRCDIR)/port/$(PORTNAME)
+
+CFLAGS += $(INCLUDE_OPT)
+
+ifeq ($(PORTNAME), linux)
+  ifdef LINUX_ELF
+    ifeq ($(CC), gcc)
+      CFLAGS += -fPIC
+    endif
+  endif
+endif
+
+ifeq ($(PORTNAME), i386_solaris)
+  CFLAGS+= -fPIC
+endif
+
+MODNAME =	misc_utils
+
+MODULE =	$(MODNAME)$(DLSUFFIX)
+
+all:		module sql
+
+module:		$(MODULE)
+
+sql:		$(MODNAME).sql
+
+install:	$(MODULE)
+		cp -p $(MODULE) $(LIBDIR)
+		cd $(LIBDIR); strip $(MODULE)
+
+%.sql: %.sql.in
+		sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
+
+.SUFFIXES: $(DLSUFFIX)
+
+%$(DLSUFFIX): %.c
+		cc $(CFLAGS) -shared -o $@ $<
+
+depend dep:
+		$(CC) -MM $(INCLUDE_OPT) *.c >depend
+
+clean:
+		rm -f $(MODULE) $(MODNAME).sql assert_test.so
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/contrib/miscutil/assert_test.c b/contrib/miscutil/assert_test.c
new file mode 100644
index 0000000000000000000000000000000000000000..fa2ec1fcaada4ef4615c9d02afb61096c5a507cb
--- /dev/null
+++ b/contrib/miscutil/assert_test.c
@@ -0,0 +1,43 @@
+/*
+ * assert_test.c --
+ *
+ * This file tests Postgres assert checking.
+ *
+ * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
+ */
+
+#include "postgres.h"
+#include "assert_test.h"
+
+extern int assertTest(int val);
+extern int assertEnable(int val);
+
+int
+assert_enable(int val)
+{
+    return assertEnable(val);
+}
+
+int
+assert_test(int val)
+{
+    return assertTest(val);
+}
+
+/*
+
+-- Enable/disable Postgres assert checking.
+--
+create function assert_enable(int4) returns int4
+    as '/usr/local/pgsql/lib/assert_test.so'
+    language 'C';
+
+-- Test Postgres assert checking.
+--
+create function assert_test(int4) returns int4
+    as '/usr/local/pgsql/lib/assert_test.so'
+    language 'C';
+
+*/
+
+/* end of file */
diff --git a/contrib/miscutil/assert_test.h b/contrib/miscutil/assert_test.h
new file mode 100644
index 0000000000000000000000000000000000000000..2af288729aadaac36f084e979a54db54639c149f
--- /dev/null
+++ b/contrib/miscutil/assert_test.h
@@ -0,0 +1,7 @@
+#ifndef ASSERT_TEST_H
+#define ASSERT_TEST_H
+
+int assert_enable(int val);
+int assert_test(int val);
+
+#endif
diff --git a/contrib/miscutil/misc_utils.c b/contrib/miscutil/misc_utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b8f379d21f6096872906442a4510a3566f06fa0
--- /dev/null
+++ b/contrib/miscutil/misc_utils.c
@@ -0,0 +1,50 @@
+/*
+ * utils.c --
+ *
+ * This file defines various Postgres utility functions.
+ *
+ * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
+ */
+
+#include <unistd.h>
+
+#include "postgres.h"
+#include "utils/palloc.h"
+
+#include "misc_utils.h"
+
+extern int ExecutorLimit(int limit);
+extern void Async_Unlisten(char *relname, int pid);
+
+int
+query_limit(int limit)
+{
+    return ExecutorLimit(limit);
+}
+
+int
+backend_pid()
+{
+    return getpid();
+}
+
+int
+unlisten(char *relname)
+{
+    Async_Unlisten(relname, getpid());
+    return 0;
+}
+
+int
+max(int x, int y)
+{
+    return ((x > y) ? x : y);
+}
+
+int
+min(int x, int y)
+{
+    return ((x < y) ? x : y);
+}
+
+/* end of file */
diff --git a/contrib/miscutil/misc_utils.h b/contrib/miscutil/misc_utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..7dd583c2646a8ac61c94a43c654085d9f95a6b73
--- /dev/null
+++ b/contrib/miscutil/misc_utils.h
@@ -0,0 +1,10 @@
+#ifndef MISC_UTILS_H
+#define MISC_UTILS_H
+
+int query_limit(int limit);
+int backend_pid(void);
+int unlisten(char *relname);
+int max(int x, int y);
+int min(int x, int y);
+
+#endif
diff --git a/contrib/miscutil/misc_utils.sql.in b/contrib/miscutil/misc_utils.sql.in
new file mode 100644
index 0000000000000000000000000000000000000000..0c90ba52eb9b8ece752f97ac228e001f40eabde0
--- /dev/null
+++ b/contrib/miscutil/misc_utils.sql.in
@@ -0,0 +1,40 @@
+-- SQL code to define the new array iterator functions and operators
+
+-- min(x,y)
+--
+create function min(int4,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- max(x,y)
+--
+create function max(int4,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Set the maximum number of tuples returned by a single query
+--
+create function query_limit(int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Return the pid of the backend
+--
+create function backend_pid() returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Unlisten from a relation
+--
+create function unlisten(name) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Unlisten from all relations for this backend
+--
+create function unlisten() returns int4
+  as 'delete from pg_listener where listenerpid = backend_pid();
+      select 0'
+  language 'sql';
+
+-- end of file
diff --git a/contrib/pginterface/README.orig b/contrib/pginterface/README.orig
new file mode 100644
index 0000000000000000000000000000000000000000..c52b5d1190a19db53cb3c25201d6cc0accc5f0ab
--- /dev/null
+++ b/contrib/pginterface/README.orig
@@ -0,0 +1,42 @@
+
+
+   			      Pginterface 2.0
+
+Attached is a copy of the Postgres support routines I wrote to allow me
+to more cleanly interface to the libpg library, more like a 4gl SQL
+interface.
+
+It has several features that may be useful for others:
+
+I have simplified the C code that calls libpq by wrapping all the
+functionality of libpq in calls to connectdb(), doquery(), fetch(),
+fetchwithnulls() and disconnectdb().  Each call returns a structure or
+value, so if you need to do more work with the result, you can.  Also, I
+have a global variable that allows you to disable the error checking I
+have added to the doquery() routine.
+
+I have added a function called fetch(), which allows you to pass
+pointers as parameters, and on return the variables are filled with the
+data from the binary cursor you opened.  These binary cursors are not
+useful if you are running the query engine on a system with a different
+architecture than the database server.  If you pass a NULL pointer, the
+column is skipped, and you can use libpq to handle it as you wish.
+
+I have used sigprocmask() to block the reception of certain signals
+while the program is executing SQL queries.  This prevents a user
+pressing Control-C from stopping all the back ends.  It blocks SIGHUP,
+SIGINT, and SIGTERM, but does not block SIGQUIT or obviously kill -9. 
+If your platform does not support sigprocmask(), you can remove those
+function calls.  ( Am I correct that abnormal termination can cause
+shared memory resynchronization?)
+
+There is a demo program called pginsert that demonstrates how the
+library can be used.
+
+You can create a library of pginterface.c and halt.c, and just include
+pginterface.h in your source code.
+
+I am willing to maintain this if people find problems or want additional
+functionality. 
+
+Bruce Momjian (root@candle.pha.pa.us)
diff --git a/contrib/pginterface/pginsert.c.orig b/contrib/pginterface/pginsert.c.orig
new file mode 100644
index 0000000000000000000000000000000000000000..82838c2f8ccdb08474e72d8767a7bf7e5821ce44
--- /dev/null
+++ b/contrib/pginterface/pginsert.c.orig
@@ -0,0 +1,102 @@
+/*
+ * insert.c
+ *
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <libpq-fe.h>
+#include "halt.h"
+#include "pginterface.h"
+
+int
+main(int argc, char **argv)
+{
+	char		query[4000];
+	int			row = 1;
+	int			aint;
+	float		afloat;
+	double		adouble;
+	char		achar[11],
+				achar16[17],
+				abpchar[11],
+				avarchar[51],
+				atext[51];
+	time_t		aabstime;
+
+	if (argc != 2)
+		halt("Usage:  %s database\n", argv[0]);
+
+	connectdb(argv[1], NULL, NULL, NULL, NULL);
+
+	on_error_continue();
+	doquery("DROP TABLE testfetch");
+	on_error_stop();
+
+	doquery("\
+		CREATE TABLE testfetch( \
+			aint 	int4, \
+			afloat 	float4, \
+			adouble float8, \
+			achar	char, \
+			achar16	char16, \
+			abpchar char(10), \
+			avarchar varchar(50), \
+			atext	text, \
+			aabstime abstime) \
+		");
+
+	while (1)
+	{
+		sprintf(query, "INSERT INTO testfetch VALUES ( \
+			%d, \
+			2322.12, \
+			'923121.0323'::float8, \
+			'A', \
+			'Betty', \
+			'Charley', \
+			'Doug', \
+			'Ernie', \
+			'now' )", row);
+		doquery(query);
+
+		doquery("BEGIN WORK");
+		doquery("DECLARE c_testfetch BINARY CURSOR FOR \
+					SELECT * FROM testfetch");
+
+		doquery("FETCH ALL IN c_testfetch");
+
+		while (fetch(
+					 &aint,
+					 &afloat,
+					 &adouble,
+					 achar,
+					 achar16,
+					 abpchar,
+					 avarchar,
+					 atext,
+					 &aabstime) != END_OF_TUPLES)
+			printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
+bpchar %s\nvarchar %s\ntext %s\nabstime %s",
+				   aint,
+				   afloat,
+				   adouble,
+				   achar,
+				   achar16,
+				   abpchar,
+				   avarchar,
+				   atext,
+				   ctime(&aabstime));
+
+
+		doquery("CLOSE c_testfetch");
+		doquery("COMMIT WORK");
+		printf("--- %-d rows inserted so far\n", row);
+
+		row++;
+	}
+
+	disconnectdb();
+	return 0;
+}
diff --git a/contrib/pginterface/pginterface.c.orig b/contrib/pginterface/pginterface.c.orig
new file mode 100644
index 0000000000000000000000000000000000000000..cdc419352aaaf8379ce47d47579ef856f9dae9d5
--- /dev/null
+++ b/contrib/pginterface/pginterface.c.orig
@@ -0,0 +1,232 @@
+/*
+ * pginterface.c
+ *
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <libpq-fe.h>
+#include "halt.h"
+#include "pginterface.h"
+
+static void sig_disconnect();
+static void set_signals();
+
+#define NUL '\0'
+
+/* GLOBAL VARIABLES */
+static PGconn *conn;
+static PGresult *res = NULL;
+
+#define ON_ERROR_STOP	0
+#define ON_ERROR_CONTINUE		1
+
+static int	on_error_state = ON_ERROR_STOP;
+
+/* LOCAL VARIABLES */
+static sigset_t block_sigs,
+			unblock_sigs;
+static int	tuple;
+
+/*
+**
+**		connectdb - returns PGconn structure
+**
+*/
+PGconn	   *
+connectdb(char *dbName,
+		  char *pghost,
+		  char *pgport,
+		  char *pgoptions,
+		  char *pgtty)
+{
+	/* make a connection to the database */
+	conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
+	if (PQstatus(conn) == CONNECTION_BAD)
+		halt("Connection to database '%s' failed.\n%s\n", dbName,
+			 PQerrorMessage(conn));
+	set_signals();
+	return conn;
+}
+
+/*
+**
+**		disconnectdb
+**
+*/
+void
+disconnectdb()
+{
+	PQfinish(conn);
+}
+
+/*
+**
+**		doquery - returns PGresult structure
+**
+*/
+PGresult   *
+doquery(char *query)
+{
+	if (res != NULL)
+		PQclear(res);
+
+	sigprocmask(SIG_SETMASK, &block_sigs, NULL);
+	res = PQexec(conn, query);
+	sigprocmask(SIG_SETMASK, &unblock_sigs, NULL);
+
+	if (on_error_state == ON_ERROR_STOP &&
+		(res == NULL ||
+		 PQresultStatus(res) == PGRES_BAD_RESPONSE ||
+		 PQresultStatus(res) == PGRES_NONFATAL_ERROR ||
+		 PQresultStatus(res) == PGRES_FATAL_ERROR))
+	{
+		if (res != NULL)
+			fprintf(stderr, "query error:  %s\n", PQcmdStatus(res));
+		else
+			fprintf(stderr, "connection error:  %s\n", PQerrorMessage(conn));
+		PQfinish(conn);
+		halt("failed request:  %s\n", query);
+	}
+	tuple = 0;
+	return res;
+}
+
+/*
+**
+**		fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES
+**						NULL pointers are skipped
+**
+*/
+int
+fetch(void *param,...)
+{
+	va_list		ap;
+	int			arg,
+				num_fields;
+
+	num_fields = PQnfields(res);
+
+	if (tuple >= PQntuples(res))
+		return END_OF_TUPLES;
+
+	va_start(ap, param);
+	for (arg = 0; arg < num_fields; arg++)
+	{
+		if (param != NULL)
+		{
+			if (PQfsize(res, arg) == -1)
+			{
+				memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
+				((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
+			}
+			else
+				memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
+		}
+		param = va_arg(ap, char *);
+	}
+	va_end(ap);
+	return tuple++;
+}
+
+/*
+**
+**		fetchwithnulls - returns tuple number (starts at 0),
+**																						or the value END_OF_TUPLES
+**								Returns true or false into null indicator variables
+**								NULL pointers are skipped
+*/
+int
+fetchwithnulls(void *param,...)
+{
+	va_list		ap;
+	int			arg,
+				num_fields;
+
+	num_fields = PQnfields(res);
+
+	if (tuple >= PQntuples(res))
+		return END_OF_TUPLES;
+
+	va_start(ap, param);
+	for (arg = 0; arg < num_fields; arg++)
+	{
+		if (param != NULL)
+		{
+			if (PQfsize(res, arg) == -1)
+			{
+				memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
+				((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
+			}
+			else
+				memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
+		}
+		param = va_arg(ap, char *);
+		if (PQgetisnull(res, tuple, arg) != 0)
+			*(int *) param = 1;
+		else
+			*(int *) param = 0;
+		param = va_arg(ap, char *);
+	}
+	va_end(ap);
+	return tuple++;
+}
+
+/*
+**
+**		on_error_stop
+**
+*/
+void
+on_error_stop()
+{
+	on_error_state = ON_ERROR_STOP;
+}
+
+/*
+**
+**		on_error_continue
+**
+*/
+void
+on_error_continue()
+{
+	on_error_state = ON_ERROR_CONTINUE;
+}
+
+/*
+**
+**		sig_disconnect
+**
+*/
+static void
+sig_disconnect()
+{
+	fprintf(stderr, "exiting...\n");
+	PQfinish(conn);
+	exit(1);
+}
+
+/*
+**
+**		set_signals
+**
+*/
+static void
+set_signals()
+{
+	sigemptyset(&block_sigs);
+	sigemptyset(&unblock_sigs);
+	sigaddset(&block_sigs, SIGTERM);
+	sigaddset(&block_sigs, SIGHUP);
+	sigaddset(&block_sigs, SIGINT);
+/*		sigaddset(&block_sigs,SIGQUIT); no block */
+	sigprocmask(SIG_SETMASK, &unblock_sigs, NULL);
+	signal(SIGTERM, sig_disconnect);
+	signal(SIGHUP, sig_disconnect);
+	signal(SIGINT, sig_disconnect);
+	signal(SIGQUIT, sig_disconnect);
+}
diff --git a/contrib/pginterface/pgnulltest.c.orig b/contrib/pginterface/pgnulltest.c.orig
new file mode 100644
index 0000000000000000000000000000000000000000..96873ca7c814dc739c19d43f4e3ac65e9820e0ff
--- /dev/null
+++ b/contrib/pginterface/pgnulltest.c.orig
@@ -0,0 +1,143 @@
+/*
+ * pgnulltest.c
+ *
+*/
+
+#define TEST_NON_NULLS
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include <halt.h>
+#include <libpq-fe.h>
+#include <pginterface.h>
+
+int
+main(int argc, char **argv)
+{
+	char		query[4000];
+	int			row = 1;
+	int			aint;
+	float		afloat;
+	double		adouble;
+	char		achar[11],
+				achar16[17],
+				abpchar[11],
+				avarchar[51],
+				atext[51];
+	time_t		aabstime;
+	int			aint_null,
+				afloat_null,
+				adouble_null,
+				achar_null,
+				achar16_null,
+				abpchar_null,
+				avarchar_null,
+				atext_null,
+				aabstime_null;
+
+	if (argc != 2)
+		halt("Usage:  %s database\n", argv[0]);
+
+	connectdb(argv[1], NULL, NULL, NULL, NULL);
+
+	on_error_continue();
+	doquery("DROP TABLE testfetch");
+	on_error_stop();
+
+	doquery("\
+        CREATE TABLE testfetch( \
+            aint    int4, \
+            afloat  float4, \
+            adouble float8, \
+            achar   char, \
+            achar16 char16, \
+            abpchar char(10), \
+            avarchar varchar(50), \
+            atext   text, \
+            aabstime abstime) \
+        ");
+
+#ifdef TEST_NON_NULLS
+	sprintf(query, "INSERT INTO testfetch VALUES ( \
+            0, \
+			0, \
+			0, \
+			'', \
+			'', \
+			'', \
+			'', \
+			'', \
+			'');");
+#else
+	sprintf(query, "INSERT INTO testfetch VALUES ( \
+            NULL, \
+			NULL, \
+			NULL, \
+			NULL, \
+			NULL, \
+			NULL, \
+			NULL, \
+			NULL, \
+			NULL);");
+#endif
+	doquery(query);
+
+	doquery("BEGIN WORK");
+	doquery("DECLARE c_testfetch BINARY CURSOR FOR \
+                    SELECT * FROM testfetch");
+
+	doquery("FETCH ALL IN c_testfetch");
+
+	if (fetchwithnulls(
+					   &aint,
+					   &aint_null,
+					   &afloat,
+					   &afloat_null,
+					   &adouble,
+					   &adouble_null,
+					   achar,
+					   &achar_null,
+					   achar16,
+					   &achar16_null,
+					   abpchar,
+					   &abpchar_null,
+					   avarchar,
+					   &avarchar_null,
+					   atext,
+					   &atext_null,
+					   &aabstime,
+					   &aabstime_null) != END_OF_TUPLES)
+		printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
+bpchar %s\nvarchar %s\ntext %s\nabstime %s\n",
+			   aint,
+			   afloat,
+			   adouble,
+			   achar,
+			   achar16,
+			   abpchar,
+			   avarchar,
+			   atext,
+			   ctime(&aabstime));
+	printf("NULL:\nint %d\nfloat %d\ndouble %d\nchar %d\nchar16 %d\n\
+bpchar %d\nvarchar %d\ntext %d\nabstime %d\n",
+		   aint_null,
+		   afloat_null,
+		   adouble_null,
+		   achar_null,
+		   achar16_null,
+		   abpchar_null,
+		   avarchar_null,
+		   atext_null,
+		   aabstime_null);
+
+
+	doquery("CLOSE c_testfetch");
+	doquery("COMMIT WORK");
+	printf("--- %-d rows inserted so far\n", row);
+
+	row++;
+
+	disconnectdb();
+	return 0;
+}
diff --git a/contrib/pginterface/pgwordcount.c.orig b/contrib/pginterface/pgwordcount.c.orig
new file mode 100644
index 0000000000000000000000000000000000000000..859cf90b2ad1e27c9aeb20627fae976ef5ea6d0c
--- /dev/null
+++ b/contrib/pginterface/pgwordcount.c.orig
@@ -0,0 +1,72 @@
+/*
+ * wordcount.c
+ *
+*/
+
+#include <stdio.h>
+#include <signal.h>
+#include <time.h>
+#include "halt.h"
+#include <libpq-fe.h>
+#include "pginterface.h"
+
+int
+main(int argc, char **argv)
+{
+	char		query[4000];
+	int			row = 0;
+	int			count;
+	char		line[4000];
+
+	if (argc != 2)
+		halt("Usage:  %s database\n", argv[0]);
+
+	connectdb(argv[1], NULL, NULL, NULL, NULL);
+	on_error_continue();
+	doquery("DROP TABLE words");
+	on_error_stop();
+
+	doquery("\
+		CREATE TABLE words( \
+			matches	int4, \
+			word	text ) \
+		");
+	doquery("\
+		CREATE INDEX i_words_1 ON words USING btree ( \
+			word text_ops )\
+		");
+
+	while (1)
+	{
+		if (scanf("%s", line) != 1)
+			break;
+		doquery("BEGIN WORK");
+		sprintf(query, "\
+				DECLARE c_words BINARY CURSOR FOR \
+				SELECT count(*) \
+				FROM words \
+				WHERE word = '%s'", line);
+		doquery(query);
+		doquery("FETCH ALL IN c_words");
+
+		while (fetch(&count) == END_OF_TUPLES)
+			count = 0;
+		doquery("CLOSE c_words");
+		doquery("COMMIT WORK");
+
+		if (count == 0)
+			sprintf(query, "\
+				INSERT INTO words \
+				VALUES (1, '%s')", line);
+		else
+			sprintf(query, "\
+				UPDATE words \
+				SET matches = matches + 1 \
+				WHERE word = '%s'", line);
+		doquery(query);
+		row++;
+	}
+
+	disconnectdb();
+	return 0;
+}
diff --git a/contrib/sequence/Makefile b/contrib/sequence/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e82817fd15a648adbf9d5516938819fde44f3c30
--- /dev/null
+++ b/contrib/sequence/Makefile
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for new sequence functions.
+#
+#-------------------------------------------------------------------------
+
+PGDIR = ../..
+SRCDIR = $(PGDIR)/src
+
+include $(SRCDIR)/Makefile.global
+
+INCLUDE_OPT =	-I ./ \
+		-I $(SRCDIR)/ \
+		-I $(SRCDIR)/include \
+		-I $(SRCDIR)/port/$(PORTNAME)
+
+CFLAGS += $(INCLUDE_OPT)
+
+ifeq ($(PORTNAME), linux)
+  ifdef LINUX_ELF
+    ifeq ($(CC), gcc)
+      CFLAGS += -fPIC
+    endif
+  endif
+endif
+
+ifeq ($(PORTNAME), i386_solaris)
+  CFLAGS+= -fPIC
+endif
+
+MODNAME =	set_sequence
+
+MODULE =	$(MODNAME)$(DLSUFFIX)
+
+all:		module sql
+
+module:		$(MODULE)
+
+sql:		$(MODNAME).sql
+
+install:	$(MODULE)
+		cp -p $(MODULE) $(LIBDIR)
+		cd $(LIBDIR); strip $(MODULE)
+
+%.sql: %.sql.in
+		sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
+
+.SUFFIXES: $(DLSUFFIX)
+
+%$(DLSUFFIX): %.c
+		cc $(CFLAGS) -shared -o $@ $<
+
+depend dep:
+		$(CC) -MM $(INCLUDE_OPT) *.c >depend
+
+clean:
+		rm -f $(MODULE) $(MODNAME).sql
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/contrib/sequence/set_sequence.c b/contrib/sequence/set_sequence.c
new file mode 100644
index 0000000000000000000000000000000000000000..7468efb5fd7a1a2ec0768a2097c4d4afdff6391d
--- /dev/null
+++ b/contrib/sequence/set_sequence.c
@@ -0,0 +1,41 @@
+/*
+ * set_sequence.c --
+ *
+ * Set a new sequence value.
+ *
+ * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
+ */
+
+#include "postgres.h"
+#include "nodes/parsenodes.h"
+#include "commands/sequence.h"
+
+#include "set_sequence.h"
+
+extern int setval(struct varlena *seqin, int4 val);
+
+int
+set_currval(struct varlena *sequence, int4 nextval)
+{
+    return setval(sequence, nextval);
+}
+
+int
+next_id(struct varlena *sequence)
+{
+    return nextval(sequence);
+}
+
+int
+last_id(struct varlena *sequence)
+{
+    return currval(sequence);
+}
+
+int
+set_last_id(struct varlena *sequence, int4 nextval)
+{
+    return setval(sequence, nextval);
+}
+
+/* end of file */
diff --git a/contrib/sequence/set_sequence.h b/contrib/sequence/set_sequence.h
new file mode 100644
index 0000000000000000000000000000000000000000..f5c62949663e6aaf01b4725fa43c829d4cc4b7be
--- /dev/null
+++ b/contrib/sequence/set_sequence.h
@@ -0,0 +1,9 @@
+#ifndef SET_SEQUENCE_H
+#define SET_SEQUENCE_H
+
+int set_currval(struct varlena *sequence, int4 nextval);
+int next_id(struct varlena *sequence);
+int last_id(struct varlena *sequence);
+int set_last_id(struct varlena *sequence, int4 nextval);
+
+#endif
diff --git a/contrib/sequence/set_sequence.sql.in b/contrib/sequence/set_sequence.sql.in
new file mode 100644
index 0000000000000000000000000000000000000000..0f1421b71bdfc56ed989c4cac4b1535bc388efeb
--- /dev/null
+++ b/contrib/sequence/set_sequence.sql.in
@@ -0,0 +1,33 @@
+-- SQL code to define new sequence utilities
+
+-- Set a new sequence value
+--
+create function set_currval(text, int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Increment the value of sequence
+--
+--	select next_id('sequence_name');
+--
+create function next_id(text) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Return the last value set for a sequence
+--
+--	select last_id('sequence_name');
+--
+create function last_id(text) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- Set the current value of a sequence
+--
+--	select set_last_id('sequence_name', 1);
+--
+create function set_last_id(text,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'C';
+
+-- end of file
diff --git a/contrib/string/Makefile b/contrib/string/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b9ff534137e15a2b05cacce7e6c9e5a5bb45e245
--- /dev/null
+++ b/contrib/string/Makefile
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for new string I/O functions.
+#
+#-------------------------------------------------------------------------
+
+PGDIR = ../..
+SRCDIR = $(PGDIR)/src
+
+include $(SRCDIR)/Makefile.global
+
+INCLUDE_OPT =	-I ./ \
+		-I $(SRCDIR)/ \
+		-I $(SRCDIR)/include \
+		-I $(SRCDIR)/port/$(PORTNAME)
+
+CFLAGS += $(INCLUDE_OPT)
+
+ifeq ($(PORTNAME), linux)
+  ifdef LINUX_ELF
+    ifeq ($(CC), gcc)
+      CFLAGS += -fPIC
+    endif
+  endif
+endif
+
+ifeq ($(PORTNAME), i386_solaris)
+  CFLAGS+= -fPIC
+endif
+
+MODNAME =	string_io
+
+MODULE =	$(MODNAME)$(DLSUFFIX)
+
+all:		module sql
+
+module:		$(MODULE)
+
+sql:		$(MODNAME).sql
+
+install:	$(MODULE)
+		cp -p $(MODULE) $(LIBDIR)
+		cd $(LIBDIR); strip $(MODULE)
+
+%.sql: %.sql.in
+		sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
+
+.SUFFIXES: $(DLSUFFIX)
+
+%$(DLSUFFIX): %.c
+		cc $(CFLAGS) -shared -o $@ $<
+
+depend dep:
+		$(CC) -MM $(INCLUDE_OPT) *.c >depend
+
+clean:
+		rm -f $(MODULE) $(MODNAME).sql
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/contrib/string/string_io.c b/contrib/string/string_io.c
index c45db69187c752666feca6835bd8603e20348e91..5dd6346b56fd1fc0935fa03158f6c5133450e4f3 100644
--- a/contrib/string/string_io.c
+++ b/contrib/string/string_io.c
@@ -14,17 +14,19 @@
 #include "utils/palloc.h"
 #include "utils/builtins.h"
 
+#include "string_io.h"
+
 /* define this if you want to see iso-8859 characters */
 #define ISO8859
 
-#define MIN(x, y)		((x) < (y) ? (x) : (y))
-#define VALUE(char)		((char) - '0')
-#define DIGIT(val)		((val) + '0')
-#define ISOCTAL(c)		(((c) >= '0') && ((c) <= '7'))
+#define MIN(x, y)	((x) < (y) ? (x) : (y))
+#define	VALUE(char) 	((char) - '0')
+#define	DIGIT(val)	((val) + '0')
+#define ISOCTAL(c) 	(((c) >= '0') && ((c) <= '7'))
 #ifndef ISO8859
-#define NOTPRINTABLE(c) (!isprint(c))
+#define NOTPRINTABLE(c)	(!isprint(c))
 #else
-#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
+#define NOTPRINTABLE(c)	(!isprint(c) && ((c) < 0xa0))
 #endif
 
 /*
@@ -36,129 +38,115 @@
  * The function is used by output methods of various string types.
  *
  * Arguments:
- *		data -			input data (can be NULL)
- *		size -			optional size of data. A negative value indicates
- *						that data is a null terminated string.
+ *	data -		input data (can be NULL)
+ *	size -		optional size of data. A negative value indicates
+ *			that data is a null terminated string.
  *
  * Returns:
- *		a pointer to a new string containing the printable
- *		representation of data.
+ *	a pointer to a new string containing the printable
+ *	representation of data.
  */
 
-char	   *
+char *
 string_output(char *data, int size)
 {
-	register unsigned char c,
-			   *p,
-			   *r,
-			   *result;
-	register int l,
-				len;
-
-	if (data == NULL)
-	{
-		result = (char *) palloc(2);
-		result[0] = '-';
-		result[1] = '\0';
-		return (result);
-	}
-
-	if (size < 0)
-	{
-		size = strlen(data);
-	}
+    register unsigned char c, *p, *r, *result;
+    register int l, len;
 
-	/* adjust string length for escapes */
-	len = size;
-	for (p = data, l = size; l > 0; p++, l--)
-	{
-		switch (*p)
-		{
-			case '\\':
-			case '"':
-			case '{':
-			case '}':
-			case '\b':
-			case '\f':
-			case '\n':
-			case '\r':
-			case '\t':
-			case '\v':
-				len++;
-				break;
-			default:
-				if (NOTPRINTABLE(*p))
-				{
-					len += 3;
-				}
-		}
+    if (data == NULL) {
+	result = (char *) palloc(2);
+	result[0] = '-';
+	result[1] = '\0';
+	return (result);
+    }
+
+    if (size < 0) {
+	size = strlen(data);
+    }
+
+    /* adjust string length for escapes */
+    len = size;
+    for (p=data,l=size; l>0; p++,l--) {
+	switch (*p) {
+	  case '\\':
+	  case '"' :
+	  case '{':
+	  case '}':
+	  case '\b':
+	  case '\f':
+	  case '\n':
+	  case '\r':
+	  case '\t':
+	  case '\v':
+	    len++;
+	    break;
+	  default:
+	    if (NOTPRINTABLE(*p)) {
+		len += 3;
+	    }
 	}
-	len++;
-
-	result = (char *) palloc(len);
-
-	for (p = data, r = result, l = size; (l > 0) && (c = *p); p++, l--)
-	{
-		switch (c)
-		{
-			case '\\':
-			case '"':
-			case '{':
-			case '}':
-				*r++ = '\\';
-				*r++ = c;
-				break;
-			case '\b':
-				*r++ = '\\';
-				*r++ = 'b';
-				break;
-			case '\f':
-				*r++ = '\\';
-				*r++ = 'f';
-				break;
-			case '\n':
-				*r++ = '\\';
-				*r++ = 'n';
-				break;
-			case '\r':
-				*r++ = '\\';
-				*r++ = 'r';
-				break;
-			case '\t':
-				*r++ = '\\';
-				*r++ = 't';
-				break;
-			case '\v':
-				*r++ = '\\';
-				*r++ = 'v';
-				break;
-			default:
-				if (NOTPRINTABLE(c))
-				{
-					*r = '\\';
-					r += 3;
-					*r-- = DIGIT(c & 07);
-					c >>= 3;
-					*r-- = DIGIT(c & 07);
-					c >>= 3;
-					*r = DIGIT(c & 03);
-					r += 3;
-				}
-				else
-				{
-					*r++ = c;
-				}
-		}
+    }
+    len++;
+
+    result = (char *) palloc(len);
+
+    for (p=data,r=result,l=size; (l > 0) && (c = *p); p++,l--) {
+	switch (c) {
+	  case '\\':
+	  case '"' :
+	  case '{':
+	  case '}':
+	    *r++ = '\\';
+	    *r++ = c;
+	    break;
+	  case '\b':
+	    *r++ = '\\';
+	    *r++ = 'b';
+	    break;
+	  case '\f':
+	    *r++ = '\\';
+	    *r++ = 'f';
+	    break;
+	  case '\n':
+	    *r++ = '\\';
+	    *r++ = 'n';
+	    break;
+	  case '\r':
+	    *r++ = '\\';
+	    *r++ = 'r';
+	    break;
+	  case '\t':
+	    *r++ = '\\';
+	    *r++ = 't';
+	    break;
+	  case '\v':
+	    *r++ = '\\';
+	    *r++ = 'v';
+	    break;
+	  default:
+	    if (NOTPRINTABLE(c)) {
+		*r = '\\';
+		r += 3;
+		*r-- = DIGIT(c & 07);
+		c >>= 3;
+		*r-- = DIGIT(c & 07);
+		c >>= 3;
+		*r   = DIGIT(c & 03);
+		r += 3;
+	    } else {
+		*r++ = c;
+	    }
 	}
-	*r = '\0';
+    }
+    *r = '\0';
 
-	return ((char *) result);
+    return((char *) result);
 }
 
 /*
  * string_input() --
  *
- * This function accepts a C string in input and copies it into a new
+ * This function accepts a C string in input and copies it into a new 
  * object allocated with palloc() translating all escape sequences.
  * An optional header can be allocatd before the string, for example
  * to hold the length of a varlena object.
@@ -167,231 +155,211 @@ string_output(char *data, int size)
  * receive strings in internal form.
  *
  * Arguments:
- *		str -			input string possibly with escapes
- *		size -			the required size of new data. A value of 0
- *						indicates a variable size string, while a
- *						negative value indicates a variable size string
- *						of size not greater than this absolute value.
- *		hdrsize -		size of an optional header to be allocated before
- *						the data. It must then be filled by the caller.
- *		rtn_size -		an optional pointer to an int variable where the
- *						size of the new string is stored back.
+ *	str -		input string possibly with escapes
+ *	size -		the required size of new data. A value of 0
+ *			indicates a variable size string, while a
+ *			negative value indicates a variable size string
+ *			of size not greater than this absolute value.
+ *	hdrsize -	size of an optional header to be allocated before
+ *			the data. It must then be filled by the caller.
+ *	rtn_size -	an optional pointer to an int variable where the
+ *			size of the new string is stored back.
  *
  * Returns:
- *		a pointer to the new string or the header.
+ *	a pointer to the new string or the header.
  */
 
-char	   *
+char *
 string_input(char *str, int size, int hdrsize, int *rtn_size)
 {
-	register unsigned char *p,
-			   *r;
-	unsigned char *result;
-	int			len;
-
-	if ((str == NULL) || (hdrsize < 0))
-	{
-		return (char *) NULL;
-	}
-
-	/* Compute result size */
-	len = strlen(str);
-	for (p = str; *p;)
-	{
-		if (*p++ == '\\')
-		{
-			if (ISOCTAL(*p))
-			{
-				if (ISOCTAL(*(p + 1)))
-				{
-					p++;
-					len--;
-				}
-				if (ISOCTAL(*(p + 1)))
-				{
-					p++;
-					len--;
-				}
-			}
-			if (*p)
-				p++;
-			len--;
+    register unsigned char *p, *r;
+    unsigned char *result;
+    int len;
+
+    if ((str == NULL) || (hdrsize < 0)) {
+	return (char *) NULL;
+    }
+
+    /* Compute result size */
+    len = strlen(str);
+    for (p=str; *p; ) {
+	if (*p++ == '\\') {
+	    if (ISOCTAL(*p)) {
+		if (ISOCTAL(*(p+1))) {
+		    p++;
+		    len--;
 		}
+		if (ISOCTAL(*(p+1))) {
+		    p++;
+		    len--;
+		}
+	    }
+	    if (*p) p++;
+	    len--;
 	}
-
-	/* result has variable length */
-	if (size == 0)
-	{
-		size = len + 1;
-	}
-	else
-		/* result has variable length with maximum size */
-	if (size < 0)
-	{
-		size = MIN(len, -size) + 1;
-	}
-
-	result = (char *) palloc(hdrsize + size);
-	memset(result, 0, hdrsize + size);
-	if (rtn_size)
-	{
-		*rtn_size = size;
-	}
-
-	r = result + hdrsize;
-	for (p = str; *p;)
-	{
-		register unsigned char c;
-
-		if ((c = *p++) == '\\')
-		{
-			switch (c = *p++)
-			{
-				case '\0':
-					p--;
-					break;
-				case '0':
-				case '1':
-				case '2':
-				case '3':
-				case '4':
-				case '5':
-				case '6':
-				case '7':
-					c = VALUE(c);
-					if (isdigit(*p))
-					{
-						c = (c << 3) + VALUE(*p++);
-					}
-					if (isdigit(*p))
-					{
-						c = (c << 3) + VALUE(*p++);
-					}
-					*r++ = c;
-					break;
-				case 'b':
-					*r++ = '\b';
-					break;
-				case 'f':
-					*r++ = '\f';
-					break;
-				case 'n':
-					*r++ = '\n';
-					break;
-				case 'r':
-					*r++ = '\r';
-					break;
-				case 't':
-					*r++ = '\t';
-					break;
-				case 'v':
-					*r++ = '\v';
-					break;
-				default:
-					*r++ = c;
-			}
+    }
+
+    /* result has variable length */
+    if (size == 0) {
+	size = len+1;
+    } else
+
+    /* result has variable length with maximum size */
+    if (size < 0) {
+	size = MIN(len, - size)+1;
+    }
+
+    result = (char *) palloc(hdrsize+size);
+    memset(result, 0, hdrsize+size);
+    if (rtn_size) {
+	*rtn_size = size;
+    }
+
+    r = result + hdrsize;
+    for (p=str; *p; ) {
+	register unsigned char c;
+	if ((c = *p++) == '\\') {
+	    switch (c = *p++) {
+	      case '\0':
+		p--;
+		break;
+	      case '0':
+	      case '1':
+	      case '2':
+	      case '3':
+	      case '4':
+	      case '5':
+	      case '6':
+	      case '7':
+		c = VALUE(c);
+		if (isdigit(*p)) {
+		    c = (c<<3) + VALUE(*p++);
 		}
-		else
-		{
-			*r++ = c;
+		if (isdigit(*p)) {
+		    c = (c<<3) + VALUE(*p++);
 		}
+		*r++ = c;
+		break;
+	      case 'b':
+		*r++ = '\b';
+		break;
+	      case 'f':
+		*r++ = '\f';
+		break;
+	      case 'n':
+		*r++ = '\n';
+		break;
+	      case 'r':
+		*r++ = '\r';
+		break;
+	      case 't':
+		*r++ = '\t';
+		break;
+	      case 'v':
+		*r++ = '\v';
+		break;
+	      default:
+		*r++ = c;
+	    }
+	} else {
+	    *r++ = c;
 	}
+    }
 
-	return ((char *) result);
+    return((char *) result);
 }
 
-char	   *
+char *
 c_charout(int32 c)
 {
-	char		str[2];
+    char str[2];
 
-	str[0] = (char) c;
-	str[1] = '\0';
+    str[0] = (char) c;
+    str[1] = '\0';
 
-	return (string_output(str, 1));
+    return (string_output(str, 1));
 }
 
-char	   *
+char *
 c_char2out(uint16 s)
 {
-	return (string_output((char *) &s, 2));
+    return (string_output((char *) &s, 2));
 }
 
-char	   *
+char *
 c_char4out(uint32 s)
 {
-	return (string_output((char *) &s, 4));
+    return (string_output((char *) &s, 4));
 }
 
-char	   *
+char *
 c_char8out(char *s)
 {
-	return (string_output(s, 8));
+    return (string_output(s, 8));
 }
 
-char	   *
+char *
 c_char16out(char *s)
 {
-	return (string_output(s, 16));
+    return (string_output(s, 16));
 }
 
 /*
  * This can be used for text, bytea, SET and unknown data types
  */
 
-char	   *
-c_textout(struct varlena * vlena)
+char *
+c_textout(struct varlena *vlena)
 {
-	int			len = 0;
-	char	   *s = NULL;
-
-	if (vlena)
-	{
-		len = VARSIZE(vlena) - VARHDRSZ;
-		s = VARDATA(vlena);
-	}
-	return (string_output(s, len));
+    int len = 0;
+    char *s = NULL;
+
+    if (vlena) {
+	len = VARSIZE(vlena) - VARHDRSZ;
+	s = VARDATA(vlena);
+    }
+    return (string_output(s, len));
 }
 
 /*
  * This can be used for varchar and bpchar strings
  */
 
-char	   *
+char *
 c_varcharout(char *s)
 {
-	int			len;
+    int len = 0;
 
-	if (s)
-	{
-		len = *(int32 *) s - 4;
-		s += 4;
-	}
-	return (string_output(s, len));
+    if (s) {
+	len = *(int32*)s - 4;
+	s += 4;
+    }
+    return (string_output(s, len));
 }
 
-#ifdef 0
+#if 0
 struct varlena *
 c_textin(char *str)
 {
-	struct varlena *result;
-	int			len;
+    struct varlena *result;
+    int len;
 
-	if (str == NULL)
-	{
-		return ((struct varlena *) NULL);
-	}
+    if (str == NULL) {
+	return ((struct varlena *) NULL);
+    }
 
-	result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
-	VARSIZE(result) = len;
+    result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
+    VARSIZE(result) = len;
 
-	return (result);
+    return (result);
 }
 
-char	   *
+char *
 c_char16in(char *str)
 {
-	return (string_input(str, 16, 0, NULL));
+    return (string_input(str, 16, 0, NULL));
 }
-
 #endif
+
+
+/* end of file */
diff --git a/contrib/string/string_io.h b/contrib/string/string_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..974f1f42623901b52f9123c61879bcf565dff322
--- /dev/null
+++ b/contrib/string/string_io.h
@@ -0,0 +1,19 @@
+#ifndef STRING_IO_H
+#define STRING_IO_H
+
+char *string_output(char *data, int size);
+char *string_input(char *str, int size, int hdrsize, int *rtn_size);
+char *c_charout(int32 c);
+char *c_char2out(uint16 s);
+char *c_char4out(uint32 s);
+char *c_char8out(char *s);
+char *c_char16out(char *s);
+char *c_textout(struct varlena *vlena);
+char *c_varcharout(char *s);
+
+#if 0
+struct varlena *c_textin(char *str);
+char *c_char16in(char *str);
+#endif
+
+#endif
diff --git a/contrib/string/string_io.sql.in b/contrib/string/string_io.sql.in
new file mode 100644
index 0000000000000000000000000000000000000000..ad1a51607cddad5bf13be6ee5ce94ec3ecc761d5
--- /dev/null
+++ b/contrib/string/string_io.sql.in
@@ -0,0 +1,104 @@
+-- SQL code to define the new string I/O functions
+
+-- This is not needed because escapes are handled by the parser
+--
+-- create function c_textin(opaque)
+--   returns text
+--   as 'MODULE_PATHNAME' 
+--   language 'c';
+
+create function c_charout(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function c_char2out(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function c_char4out(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function c_char8out(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function c_char16out(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function c_textout(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+create function c_varcharout(opaque) returns int4
+  as 'MODULE_PATHNAME' 
+  language 'c';
+
+
+-- Define a function which sets the new output routines for char types
+--
+--   select c_mode();
+--
+create function c_mode() returns text
+  as 'update pg_type set typoutput=''c_charout''    where typname=''char'';
+      update pg_type set typoutput=''c_char2out''   where typname=''char2'';
+      update pg_type set typoutput=''c_char4out''   where typname=''char4'';
+      update pg_type set typoutput=''c_char8out''   where typname=''char8'';
+      update pg_type set typoutput=''c_char16out''  where typname=''char16'';
+      update pg_type set typoutput=''c_textout''    where typname=''text'';
+      update pg_type set typoutput=''c_textout''    where typname=''bytea'';
+      update pg_type set typoutput=''c_textout''    where typname=''unknown'';
+      update pg_type set typoutput=''c_textout''    where typname=''SET'';
+      update pg_type set typoutput=''c_varcharout'' where typname=''varchar'';
+      update pg_type set typoutput=''c_varcharout'' where typname=''bpchar'';
+      select ''c_mode''::text'
+  language 'sql';
+
+-- Define a function which restores the original routines for char types
+--
+--   select pg_mode();
+--
+create function pg_mode() returns text
+  as 'update pg_type set typoutput=''charout''    where typname=''char'';
+      update pg_type set typoutput=''char2out''   where typname=''char2'';
+      update pg_type set typoutput=''char4out''   where typname=''char4'';
+      update pg_type set typoutput=''char8out''   where typname=''char8'';
+      update pg_type set typoutput=''char16out''  where typname=''char16'';
+      update pg_type set typoutput=''textout''    where typname=''text'';
+      update pg_type set typoutput=''textout''    where typname=''bytea'';
+      update pg_type set typoutput=''textout''    where typname=''unknown'';
+      update pg_type set typoutput=''textout''    where typname=''SET'';
+      update pg_type set typoutput=''varcharout'' where typname=''varchar'';
+      update pg_type set typoutput=''varcharout'' where typname=''bpchar'';
+      select ''pg_mode''::text'
+  language 'sql';
+
+
+-- Use these if you want do the updates manually
+--
+-- update pg_type set typoutput='charout'    where typname='char';
+-- update pg_type set typoutput='char2out'   where typname='char2';
+-- update pg_type set typoutput='char4out'   where typname='char4';
+-- update pg_type set typoutput='char8out'   where typname='char8';
+-- update pg_type set typoutput='char16out'  where typname='char16';
+-- update pg_type set typoutput='textout'    where typname='text';
+-- update pg_type set typoutput='textout'    where typname='bytea';
+-- update pg_type set typoutput='textout'    where typname='unknown';
+-- update pg_type set typoutput='textout'    where typname='SET';
+-- update pg_type set typoutput='varcharout' where typname='varchar';
+-- update pg_type set typoutput='varcharout' where typname='bpchar';
+--
+-- update pg_type set typoutput='c_charout'    where typname='char';
+-- update pg_type set typoutput='c_char2out'   where typname='char2';
+-- update pg_type set typoutput='c_char4out'   where typname='char4';
+-- update pg_type set typoutput='c_char8out'   where typname='char8';
+-- update pg_type set typoutput='c_char16out'  where typname='char16';
+-- update pg_type set typoutput='c_textout'    where typname='text';
+-- update pg_type set typoutput='c_textout'    where typname='bytea';
+-- update pg_type set typoutput='c_textout'    where typname='unknown';
+-- update pg_type set typoutput='c_textout'    where typname='SET';
+-- update pg_type set typoutput='c_varcharout' where typname='varchar';
+-- update pg_type set typoutput='c_varcharout' where typname='bpchar';
+
+-- end of file
diff --git a/contrib/userlock/Makefile b/contrib/userlock/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..0865bde7321b18a35fcec05e55ce7d708da36413
--- /dev/null
+++ b/contrib/userlock/Makefile
@@ -0,0 +1,62 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for new string I/O functions.
+#
+#-------------------------------------------------------------------------
+
+PGDIR = ../..
+SRCDIR = $(PGDIR)/src
+
+include $(SRCDIR)/Makefile.global
+
+INCLUDE_OPT =	-I ./ \
+		-I $(SRCDIR)/ \
+		-I $(SRCDIR)/include \
+		-I $(SRCDIR)/port/$(PORTNAME)
+
+CFLAGS += $(INCLUDE_OPT)
+
+ifeq ($(PORTNAME), linux)
+  ifdef LINUX_ELF
+    ifeq ($(CC), gcc)
+      CFLAGS += -fPIC
+    endif
+  endif
+endif
+
+ifeq ($(PORTNAME), i386_solaris)
+  CFLAGS+= -fPIC
+endif
+
+MODNAME =	user_locks
+
+MODULE =	$(MODNAME)$(DLSUFFIX)
+
+all:		module sql
+
+module:		$(MODULE)
+
+sql:		$(MODNAME).sql
+
+install:	$(MODULE)
+		cp -p $(MODULE) $(LIBDIR)
+		cd $(LIBDIR); strip $(MODULE)
+
+%.sql: %.sql.in
+		sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
+
+.SUFFIXES: $(DLSUFFIX)
+
+%$(DLSUFFIX): %.c
+		cc $(CFLAGS) -shared -o $@ $<
+
+depend dep:
+		$(CC) -MM $(INCLUDE_OPT) *.c >depend
+
+clean:
+		rm -f $(MODULE) $(MODNAME).sql
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff --git a/contrib/userlock/user_locks.c b/contrib/userlock/user_locks.c
new file mode 100644
index 0000000000000000000000000000000000000000..efc9b0a46448d7c9ba1a7ac3d9d0d11a6c08a588
--- /dev/null
+++ b/contrib/userlock/user_locks.c
@@ -0,0 +1,100 @@
+/*
+ * user_locks.c --
+ *
+ * This loadable module, together with my user-lock.patch applied to the
+ * backend, provides support for user-level long-term cooperative locks.
+ *
+ * Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "postgres.h"
+#include "miscadmin.h"
+#include "storage/lock.h"
+#include "storage/lmgr.h"
+#include "storage/proc.h"
+#include "storage/block.h"
+#include "storage/multilev.h"
+#include "utils/elog.h"
+
+#include "user_locks.h"
+
+#define USER_LOCKS_TABLE_ID	0
+
+extern Oid MyDatabaseId;
+
+int
+user_lock(unsigned int id1, unsigned int id2, LOCKT lockt)
+{
+    LOCKTAG tag;
+
+    memset(&tag,0,sizeof(LOCKTAG));
+    tag.relId = 0;
+    tag.dbId = MyDatabaseId;
+    tag.tupleId.ip_blkid.bi_hi = id2 >> 16;
+    tag.tupleId.ip_blkid.bi_lo = id2 & 0xffff;
+    tag.tupleId.ip_posid = (unsigned short) (id1 & 0xffff);
+
+    return LockAcquire(USER_LOCKS_TABLE_ID, &tag, lockt);
+}
+
+int
+user_unlock(unsigned int id1, unsigned int id2, LOCKT lockt)
+{
+    LOCKTAG tag;
+
+    memset(&tag, 0,sizeof(LOCKTAG));
+    tag.relId = 0;
+    tag.dbId = MyDatabaseId;
+    tag.tupleId.ip_blkid.bi_hi = id2 >> 16;
+    tag.tupleId.ip_blkid.bi_lo = id2 & 0xffff;
+    tag.tupleId.ip_posid = (unsigned short) (id1 & 0xffff);
+    
+    return LockRelease(USER_LOCKS_TABLE_ID, &tag, lockt);
+}
+
+int
+user_write_lock(unsigned int id1, unsigned int id2)
+{
+    return user_lock(id1, id2, WRITE_LOCK);
+}
+
+
+int
+user_write_unlock(unsigned int id1, unsigned int id2)
+{
+    return user_unlock(id1, id2, WRITE_LOCK);
+}
+
+int
+user_write_lock_oid(Oid oid)
+{
+    return user_lock(0, oid, WRITE_LOCK);
+}
+
+int
+user_write_unlock_oid(Oid oid)
+{
+    return user_unlock(0, oid, WRITE_LOCK);
+}
+
+int
+user_unlock_all()
+{
+    PROC	*proc;
+    SHMEM_OFFSET location;
+
+    ShmemPIDLookup(getpid(),&location);
+    if (location == INVALID_OFFSET) {
+	elog(NOTICE, "UserUnlockAll: unable to get proc ptr");
+	return -1;
+    }
+
+    proc = (PROC *) MAKE_PTR(location);
+    return LockReleaseAll(USER_LOCKS_TABLE_ID, &proc->lockQueue);
+}
+
+/* end of file */
diff --git a/contrib/userlock/user_locks.doc b/contrib/userlock/user_locks.doc
new file mode 100644
index 0000000000000000000000000000000000000000..15ffe48f13b48ba9aadb7ebfd37af6db5cc8685b
--- /dev/null
+++ b/contrib/userlock/user_locks.doc
@@ -0,0 +1,30 @@
+User locks, by Massimo Dal Zotto <dz@cs.unitn.it>
+
+This loadable module, together with my user-lock.patch applied to the
+backend, provides support for user-level long-term cooperative locks.
+
+For example one can write (this example is written in TclX):
+
+  set rec [sql "select ...,user_write_lock_oid(oid) from table where id=$id"]
+  if {[keylget rec user_write_lock_oid] == 1} {
+      # the write lock has been acquired with the record, start
+      # a long editing session, then update the database and
+      # release the lock.
+      sql "update table set ... where id=$id"
+      sql "select user_write_unlock_oid([keylget rec oid])"
+  } else {
+      # the record has been read but the write lock couldn't be acquired,
+      # so it should not be modified by the application.
+      messageBox "This record is in use by another user, retry later"
+  }
+
+This could also be done by setting a flag in the record itself but in
+this case you have the overhead of the updates to the record and there
+may be some locks not released if the backend or the application crashes
+before resetting the flag.
+It could also be done with a begin/end block but in this case the entire
+table would be locked by postgres and it is not acceptable to do this for
+a long period because other transactions would block completely.
+Note that this type of locks are handled cooperatively by the application
+and do not interfere with the normal locks used by postgres.  So an user
+could still modify an user-locked record if he wanted to ignore the lock.
diff --git a/contrib/userlock/user_locks.h b/contrib/userlock/user_locks.h
new file mode 100644
index 0000000000000000000000000000000000000000..ab890483fa45ab0392021b1406b185bd27e3179a
--- /dev/null
+++ b/contrib/userlock/user_locks.h
@@ -0,0 +1,12 @@
+#ifndef USER_LOCKS_H
+#define USER_LOCKS_H
+
+int user_lock(unsigned int id1, unsigned int id2, LOCKT lockt);
+int user_unlock(unsigned int id1, unsigned int id2, LOCKT lockt);
+int user_write_lock(unsigned int id1, unsigned int id2);
+int user_write_unlock(unsigned int id1, unsigned int id2);
+int user_write_lock_oid(Oid oid);
+int user_write_unlock_oid(Oid oid);
+int user_unlock_all(void);
+
+#endif
diff --git a/contrib/userlock/user_locks.sql.in b/contrib/userlock/user_locks.sql.in
new file mode 100644
index 0000000000000000000000000000000000000000..da8d105de9c9856624afb7c902a64a468c4478c4
--- /dev/null
+++ b/contrib/userlock/user_locks.sql.in
@@ -0,0 +1,69 @@
+-- SQL code to define the user locks functions
+
+-- select user_lock(group,id,type);
+--
+create function user_lock(int4,int4,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_unlock(group,id,type);
+--
+create function user_unlock(int4,int4,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_lock(group,id);
+--
+create function user_write_lock(int4,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_unlock(group,id);
+--
+create function user_write_unlock(int4,int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_lock(group,oid);
+--
+create function user_write_lock(int4,oid) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_unlock(group,oid);
+--
+create function user_write_unlock(int4,oid) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_lock_oid(oid);
+--
+create function user_write_lock_oid(oid) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_unlock_oid(oid);
+--
+create function user_write_unlock_oid(oid) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_lock_oid(int4);
+--
+create function user_write_lock_oid(int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_write_unlock_oid(int4);
+--
+create function user_write_unlock_oid(int4) returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- select user_unlock_all();
+--
+create function user_unlock_all() returns int4
+  as 'MODULE_PATHNAME'
+  language 'c';
+
+-- end of file