diff --git a/contrib/odbc/Makefile b/contrib/odbc/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..34138c1f32140ca6d384d6e42072f63c0652b9d9
--- /dev/null
+++ b/contrib/odbc/Makefile
@@ -0,0 +1,48 @@
+# ODBC extensions
+# Thomas Lockhart 2000-04-03
+
+SRCDIR= ../../src
+
+include $(SRCDIR)/Makefile.global
+
+ifndef PGLIB
+  PGLIB= .
+endif
+
+CFLAGS+= $(CFLAGS_SL) -I$(SRCDIR)/include
+
+TARGETS_7= odbc.sql
+TARGETS_PRE7= odbc$(DLSUFFIX) odbc-pre7.sql
+
+TARGETS= $(TARGETS_7)
+
+CLEANFILES+= $(TARGETS) $(TARGETS_PRE7)
+
+all:: $(TARGETS)
+
+install: all
+ifneq ($(filter odbc$(DLSUFFIX), $(TARGETS)),)
+	-test -d $(PGLIB) || $(INSTALL) -d $(PGLIB)
+	$(INSTALL) odbc$(DLSUFFIX) $(PGLIB)
+endif
+
+pre7:
+	$(MAKE) TARGETS="$(TARGETS) $(TARGETS_PRE7)"
+
+install-pre7:
+	$(MAKE) TARGETS="$(TARGETS) $(TARGETS_PRE7)" install
+
+odbc-pre7.sql: odbc-pre7.source odbc.sql
+	rm -f $@; \
+	cat $^ \
+	| sed -e "s:_OBJWD_:$(PGLIB):g" \
+	      -e "s:_DLSUFFIX_:$(DLSUFFIX):g" \
+	      -e "s:float(15):float8:g" > $@
+
+%.sql: %.source
+	rm -f $@; \
+	sed -e "s:_OBJWD_:$(PGLIB):g" \
+	    -e "s:_DLSUFFIX_:$(DLSUFFIX):g" < $< > $@
+
+clean:
+	rm -f $(TARGETS) *.o
diff --git a/contrib/odbc/README b/contrib/odbc/README
new file mode 100644
index 0000000000000000000000000000000000000000..c36f183c0544b29f043fa69128394310c2f8bfa7
--- /dev/null
+++ b/contrib/odbc/README
@@ -0,0 +1,38 @@
+This directory contains support functions for the ODBC driver
+supplied with PostgreSQL-7.0.
+
+To enable additional ODBC functions with PostgreSQL-7.0, simply
+execute the commands in odbc.sql:
+
+psql
+Welcome to psql, the PostgreSQL interactive terminal.
+
+Type:  \copyright for distribution terms
+       \h for help with SQL commands
+       \? for help on internal slash commands
+       \g or terminate with semicolon to execute query
+       \q to quit
+
+postgres=# \i odbc.sql
+CREATE
+...
+
+
+To enable additional ODBC functions with versions of PostgreSQL
+prior to PostgreSQL-7.0 (e.g. PostgreSQL-6.5.3), build the shared
+library and SQL commands as follows:
+
+make pre7
+psql
+Welcome to psql, the PostgreSQL interactive terminal.
+
+Type:  \copyright for distribution terms
+       \h for help with SQL commands
+       \? for help on internal slash commands
+       \g or terminate with semicolon to execute query
+       \q to quit
+
+postgres=# \i odbc-pre7.sql
+CREATE
+...
+
diff --git a/contrib/odbc/odbc-pre7.source b/contrib/odbc/odbc-pre7.source
new file mode 100644
index 0000000000000000000000000000000000000000..4fa06c4264d64e8d05c7578966f9ec336f821e8e
--- /dev/null
+++ b/contrib/odbc/odbc-pre7.source
@@ -0,0 +1,162 @@
+-- ODBC-pre7.sql
+--
+-- Use float8 rather than float(15) since pre-7.0 does not accept
+-- SQL92 type names of this form in the CREATE FUNCTION command.
+--
+
+--
+-- Character string manipulation
+--
+
+--
+-- Compatibility functions for pre-v7.0.
+-- These should be applied to pre-v7.0 databases
+-- when using the v7.0 ODBC driver.
+--
+
+CREATE FUNCTION char_length(text)
+    RETURNS integer
+    AS 'SELECT length(CAST($1 AS text))'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION pow(float8)
+    RETURNS float8
+    AS 'SELECT dpow($1)'
+    LANGUAGE 'SQL';
+
+--
+-- Extensions for ODBC compliance in v7.0.
+-- In the current driver, ODBC functions must map directly into a
+-- Postgres function. So in some cases we must create a compatible
+-- function.
+--
+
+CREATE FUNCTION ascii(text)
+    RETURNS integer
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'ascii'
+    LANGUAGE 'C';
+
+CREATE FUNCTION ichar(integer)
+    RETURNS char(1)
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'ichar'
+    LANGUAGE 'C';
+
+CREATE FUNCTION insert(text, integer, integer, text)
+    RETURNS text
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'insert'
+    LANGUAGE 'C';
+
+-- replace all occurences of $2 with $3
+CREATE FUNCTION replace(text, text, text)
+    RETURNS text
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'replace'
+    LANGUAGE 'C';
+
+-- return the string repeated n times
+CREATE FUNCTION repeat(text, integer)
+    RETURNS text
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'repeat'
+    LANGUAGE 'C';
+
+--
+-- Mathematical functions for pre-v7.0
+--
+
+CREATE FUNCTION dround(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dround'
+    LANGUAGE 'C';
+
+CREATE FUNCTION round(float8)
+    RETURNS float8
+    AS 'SELECT dround($1)'
+    LANGUAGE 'SQL';
+
+--
+-- Math functions present in backend, but not in catalog for v7.0
+--
+
+CREATE FUNCTION acos(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dacos'
+    LANGUAGE 'C';
+
+CREATE FUNCTION asin(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dasin'
+    LANGUAGE 'C';
+
+CREATE FUNCTION atan(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'datan'
+    LANGUAGE 'C';
+
+CREATE FUNCTION atan2(float8,float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'datan2'
+    LANGUAGE 'C';
+
+CREATE FUNCTION cos(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dcos'
+    LANGUAGE 'C';
+
+CREATE FUNCTION cot(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dcot'
+    LANGUAGE 'C';
+
+CREATE FUNCTION sin(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dsin'
+    LANGUAGE 'C';
+
+CREATE FUNCTION dtan(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dtan'
+    LANGUAGE 'C';
+
+CREATE FUNCTION degrees(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'degrees'
+    LANGUAGE 'C';
+
+CREATE FUNCTION pi()
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'dpi'
+    LANGUAGE 'C';
+
+CREATE FUNCTION radians(float8)
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'radians'
+    LANGUAGE 'C';
+
+-- random number generator currently requires RAND_MAX be available
+CREATE FUNCTION random()
+    RETURNS float8
+    AS '_OBJWD_/odbc_DLSUFFIX_', 'drandom'
+    LANGUAGE 'C';
+
+CREATE FUNCTION truncate(numeric,integer)
+    RETURNS numeric
+    AS 'SELECT trunc($1, $2)'
+    LANGUAGE 'SQL';
+
+--
+-- Date/time functions for v7.0
+--
+
+CREATE FUNCTION interval_mul(interval,float8)
+    RETURNS interval
+    AS '_OBJWD_/odbc_DLSUFFIX_'
+    LANGUAGE 'C';
+
+CREATE OPERATOR * (
+    LEFTARG = interval,
+    RIGHTARG = float8,
+    PROCEDURE = interval_mul
+);
+
+
+
+
diff --git a/contrib/odbc/odbc.c b/contrib/odbc/odbc.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d72c031febd56a0980b56db7e41060ac5165128
--- /dev/null
+++ b/contrib/odbc/odbc.c
@@ -0,0 +1,413 @@
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <float.h>				/* faked on sunos4 */
+
+#include <math.h>
+
+#include "postgres.h"
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#ifndef MAXINT
+#define MAXINT		  INT_MAX
+#endif
+#else
+#ifdef HAVE_VALUES_H
+#include <values.h>
+#endif
+#endif
+#include "fmgr.h"
+#include "utils/timestamp.h"
+#include "utils/builtins.h"
+
+
+int4 ascii(text *string);
+text *ichar(int4 cvalue);
+text *repeat(text *string, int4 count);
+Interval *interval_mul(Interval *span1, float8 *arg2);
+float64 dasin(float64 arg1);
+float64 datan(float64 arg1);
+float64 datan2(float64 arg1, float64 arg2);
+float64 dcos(float64 arg1);
+float64 dcot(float64 arg1);
+float64 dsin(float64 arg1);
+float64 dtan(float64 arg1);
+float64 degrees(float64 arg1);
+float64 dpi(void);
+float64 radians(float64 arg1);
+float64 drandom(void);
+void setseed(int32 seed);
+
+
+int4
+ascii(text *string)
+{
+	if (!PointerIsValid(string))
+		return 0;
+
+	if (VARSIZE(string) <= VARHDRSZ)
+		return 0;
+
+	return ((int) *(VARDATA(string)));
+} /* ascii() */
+
+
+text *
+ichar(int4 cvalue)
+{
+	text   *result;
+
+	result = (text *) palloc(VARHDRSZ + 1);
+	VARSIZE(result) = VARHDRSZ + 1;
+	*VARDATA(result) = (char) cvalue;
+
+	return result;
+} /* ichar() */
+
+
+text *
+repeat(text *string, int4 count)
+{
+	text   *result;
+	int		slen, tlen;
+	int		i;
+	char   *cp;
+
+	if (count < 0)
+		count = 0;
+
+	slen = (VARSIZE(string)-VARHDRSZ);
+	tlen = (VARHDRSZ + (count * slen));
+
+	result = (text *) palloc(tlen);
+
+	VARSIZE(result) = tlen;
+	cp = VARDATA(result);
+	for (i = 0; i < count; i++)
+	{
+		memcpy(cp, VARDATA(string), slen);
+		cp += slen;
+	}
+
+	return result;
+} /* ichar() */
+
+Interval   *
+interval_mul(Interval *span1, float8 *arg2)
+{
+	Interval   *result;
+	double		months;
+
+	if ((!PointerIsValid(span1)) || (!PointerIsValid(arg2)))
+		return NULL;
+
+	if (!PointerIsValid(result = palloc(sizeof(Interval))))
+		elog(ERROR, "Memory allocation failed, can't divide intervals");
+
+	months = (span1->month * *arg2);
+	result->month = rint(months);
+	result->time = JROUND(span1->time * *arg2);
+	result->time += JROUND((months - result->month) * 30);
+
+	return result;
+}	/* interval_mul() */
+
+/*
+ *		dasin			- returns a pointer to the arcsin of arg1 (radians)
+ */
+float64
+dasin(float64 arg1)
+{
+	float64		result;
+	double		tmp;
+
+	if (!PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	tmp = *arg1;
+	errno = 0;
+	*result = (float64data) asin(tmp);
+	if (errno != 0
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "dasin(%f) input is out of range", *arg1);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* dasin() */
+
+
+/*
+ *		datan			- returns a pointer to the arctan of arg1 (radians)
+ */
+float64
+datan(float64 arg1)
+{
+	float64		result;
+	double		tmp;
+
+	if (!PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	tmp = *arg1;
+	errno = 0;
+	*result = (float64data) atan(tmp);
+	if (errno != 0
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "atan(%f) input is out of range", *arg1);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* datan() */
+
+
+/*
+ *		atan2			- returns a pointer to the arctan2 of arg1 (radians)
+ */
+float64
+datan2(float64 arg1, float64 arg2)
+{
+	float64		result;
+
+	if (!PointerIsValid(arg1) || !PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	errno = 0;
+	*result = (float64data) atan2(*arg1, *arg2);
+	if (errno != 0
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "atan2(%f,%f) input is out of range", *arg1, *arg2);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* datan2() */
+
+
+/*
+ *		dcos			- returns a pointer to the cosine of arg1 (radians)
+ */
+float64
+dcos(float64 arg1)
+{
+	float64		result;
+	double		tmp;
+
+	if (!PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	tmp = *arg1;
+	errno = 0;
+	*result = (float64data) cos(tmp);
+	if (errno != 0
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "dcos(%f) input is out of range", *arg1);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* dcos() */
+
+
+/*
+ *		dcot			- returns a pointer to the cotangent of arg1 (radians)
+ */
+float64
+dcot(float64 arg1)
+{
+	float64		result;
+	double		tmp;
+
+	if (!PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	tmp = *arg1;
+	errno = 0;
+	*result = (float64data) tan(tmp);
+	if ((errno != 0) || (*result == 0.0)
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "dcot(%f) input is out of range", *arg1);
+
+	*result = 1.0/(*result);
+	CheckFloat8Val(*result);
+	return result;
+} /* dcot() */
+
+
+/*
+ *		dsin			- returns a pointer to the sine of arg1 (radians)
+ */
+float64
+dsin(float64 arg1)
+{
+	float64		result;
+	double		tmp;
+
+	if (!PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	tmp = *arg1;
+	errno = 0;
+	*result = (float64data) sin(tmp);
+	if (errno != 0
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "dsin(%f) input is out of range", *arg1);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* dsin() */
+
+
+/*
+ *		dtan			- returns a pointer to the tangent of arg1 (radians)
+ */
+float64
+dtan(float64 arg1)
+{
+	float64		result;
+	double		tmp;
+
+	if (!PointerIsValid(arg1))
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	tmp = *arg1;
+	errno = 0;
+	*result = (float64data) tan(tmp);
+	if (errno != 0
+#ifdef HAVE_FINITE
+		|| !finite(*result)
+#endif
+		)
+		elog(ERROR, "dtan(%f) input is out of range", *arg1);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* dtan() */
+
+
+#ifndef M_PI
+/* from my RH5.2 gcc math.h file - thomas 2000-04-03 */
+#define M_PI 3.14159265358979323846
+#endif
+
+
+/*
+ *		degrees		- returns a pointer to degrees converted from radians
+ */
+float64
+degrees(float64 arg1)
+{
+	float64		result;
+
+	if (!arg1)
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	*result = ((*arg1) * (180.0 / M_PI));
+
+	CheckFloat8Val(*result);
+	return result;
+} /* degrees() */
+
+
+/*
+ *		dpi				- returns a pointer to degrees converted to radians
+ */
+float64
+dpi(void)
+{
+	float64		result;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	*result = (M_PI);
+
+	return result;
+} /* dpi() */
+
+
+/*
+ *		radians		- returns a pointer to radians converted from degrees
+ */
+float64
+radians(float64 arg1)
+{
+	float64		result;
+
+	if (!arg1)
+		return (float64) NULL;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	*result = ((*arg1) * (M_PI / 180.0));
+
+	CheckFloat8Val(*result);
+	return result;
+} /* radians() */
+
+
+#ifdef RAND_MAX
+
+/*
+ *		drandom   	- returns a random number
+ */
+float64
+drandom(void)
+{
+	float64		result;
+
+	result = (float64) palloc(sizeof(float64data));
+
+	/* result 0.0-1.0 */
+	*result = (((double)rand()) / RAND_MAX);
+
+	CheckFloat8Val(*result);
+	return result;
+} /* drandom() */
+
+
+/*
+ *		setseed   	- set seed for the random number generator
+ */
+void
+setseed(int32 seed)
+{
+	srand(seed);
+
+	return;
+} /* setseed() */
+
+#endif
+
+
diff --git a/contrib/odbc/odbc.sql b/contrib/odbc/odbc.sql
new file mode 100644
index 0000000000000000000000000000000000000000..bec7f486b4fe4ee43e4cca2b1572aa2c8827c7c6
--- /dev/null
+++ b/contrib/odbc/odbc.sql
@@ -0,0 +1,145 @@
+-- ODBC.sql
+--
+
+--
+-- Character string manipulation
+--
+
+--
+-- Extensions for ODBC compliance in v7.0.
+-- In the current driver, ODBC functions must map directly into a
+-- Postgres function. So in some cases we must create a compatible
+-- function.
+--
+
+-- truncate on the left
+CREATE FUNCTION ltrunc(text, integer)
+    RETURNS text
+    AS 'SELECT substring($1 FROM 1 FOR $2)'
+    LANGUAGE 'SQL';
+
+-- truncate on the right
+CREATE FUNCTION rtrunc(text, integer)
+    RETURNS text
+    AS 'SELECT substring($1 FROM (char_length($1)-($2)+1) FOR $2)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION space(integer)
+    RETURNS text
+    AS 'SELECT lpad('''', $1, '' '')'
+    LANGUAGE 'SQL';
+
+--
+-- Mathematical functions
+--
+
+CREATE FUNCTION truncate(numeric,integer)
+    RETURNS numeric
+    AS 'SELECT trunc($1, $2)'
+    LANGUAGE 'SQL';
+
+--
+-- Date/time functions for v7.0
+--
+
+CREATE FUNCTION curdate()
+    RETURNS date
+    AS 'SELECT CAST(''now'' AS date)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION curtime()
+    RETURNS time
+    AS 'SELECT CAST(''now'' AS time)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION dayname(timestamp)
+    RETURNS text
+    AS 'SELECT to_char($1,''Day'')'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION dayofmonth(timestamp)
+    RETURNS integer
+    AS 'SELECT  CAST(date_part(''day'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION dayofweek(timestamp)
+    RETURNS integer
+    AS 'SELECT ( CAST(date_part(''dow'', $1) AS integer) + 1)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION dayofyear(timestamp)
+    RETURNS integer
+    AS 'SELECT  CAST(date_part(''doy'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION hour(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''hour'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION minute(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''minute'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION odbc_month(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''month'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION monthname(timestamp)
+    RETURNS text
+    AS 'SELECT to_char($1, ''Month'')'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION quarter(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''quarter'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION second(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''second'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+/*
+-- The first argument is an integer constant denoting the units
+-- of the second argument. Until we know the actual values, we
+-- cannot implement these. - thomas 2000-04-11
+CREATE FUNCTION timestampadd(integer,integer,timestamp)
+    RETURNS timestamp
+    AS 'SELECT CAST(($3 + ($2 * $1)) AS timestamp)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION timestampdiff(integer,integer,timestamp)
+    RETURNS timestamp
+    AS 'SELECT CAST(($3 + ($2 * $1)) AS timestamp)'
+    LANGUAGE 'SQL';
+*/
+
+CREATE FUNCTION week(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''week'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+CREATE FUNCTION year(timestamp)
+    RETURNS integer
+    AS 'SELECT CAST(date_part(''year'', $1) AS integer)'
+    LANGUAGE 'SQL';
+
+--
+-- System functions.
+--
+
+/*
+CREATE FUNCTION database()
+    RETURNS text
+    AS 'SELECT ...'
+    LANGUAGE 'SQL';
+*/
+
+CREATE FUNCTION odbc_user()
+    RETURNS text
+    AS 'SELECT CAST(USER AS text)'
+    LANGUAGE 'SQL';
+