From 9f2a07d891dd1ff2ab39be4b9b83cb5d262753e3 Mon Sep 17 00:00:00 2001 From: "Thomas G. Lockhart" Date: Tue, 11 Apr 2000 15:53:13 +0000 Subject: [PATCH] Internal functions to support newest ODBC driver {fn ...} conventions. Includes compiled code to support pre-7.0 backends, but for 7.0 only requires executing odbc.sql. --- contrib/odbc/Makefile | 48 ++++ contrib/odbc/README | 38 ++++ contrib/odbc/odbc-pre7.source | 162 +++++++++++++ contrib/odbc/odbc.c | 413 ++++++++++++++++++++++++++++++++++ contrib/odbc/odbc.sql | 145 ++++++++++++ 5 files changed, 806 insertions(+) create mode 100644 contrib/odbc/Makefile create mode 100644 contrib/odbc/README create mode 100644 contrib/odbc/odbc-pre7.source create mode 100644 contrib/odbc/odbc.c create mode 100644 contrib/odbc/odbc.sql diff --git a/contrib/odbc/Makefile b/contrib/odbc/Makefile new file mode 100644 index 0000000000..34138c1f32 --- /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 0000000000..c36f183c05 --- /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 0000000000..4fa06c4264 --- /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 0000000000..8d72c031fe --- /dev/null +++ b/contrib/odbc/odbc.c @@ -0,0 +1,413 @@ +#include +#include +#include + +#include /* faked on sunos4 */ + +#include + +#include "postgres.h" +#ifdef HAVE_LIMITS_H +#include +#ifndef MAXINT +#define MAXINT INT_MAX +#endif +#else +#ifdef HAVE_VALUES_H +#include +#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 0000000000..bec7f486b4 --- /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'; + -- 2.40.0