From 951986c550dccfdafffcf2eda30980c7310b41b4 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Wed, 5 Nov 1997 21:38:25 +0000 Subject: [PATCH] Update of contrib stuff from massimo. --- contrib/array/Makefile | 62 +++ contrib/array/array_iterator.c | 405 ++++++++-------- contrib/array/array_iterator.doc | 56 ++- contrib/array/array_iterator.h | 27 ++ contrib/array/array_iterator.sql.in | 191 ++++++++ contrib/datetime/Makefile | 68 ++- contrib/datetime/datetime_functions.c | 275 +++++++---- contrib/datetime/datetime_functions.h | 19 + contrib/datetime/datetime_functions.sql.in | 96 ++++ contrib/miscutil/Makefile | 62 +++ contrib/miscutil/assert_test.c | 43 ++ contrib/miscutil/assert_test.h | 7 + contrib/miscutil/misc_utils.c | 50 ++ contrib/miscutil/misc_utils.h | 10 + contrib/miscutil/misc_utils.sql.in | 40 ++ contrib/pginterface/README.orig | 42 ++ contrib/pginterface/pginsert.c.orig | 102 ++++ contrib/pginterface/pginterface.c.orig | 232 +++++++++ contrib/pginterface/pgnulltest.c.orig | 143 ++++++ contrib/pginterface/pgwordcount.c.orig | 72 +++ contrib/sequence/Makefile | 62 +++ contrib/sequence/set_sequence.c | 41 ++ contrib/sequence/set_sequence.h | 9 + contrib/sequence/set_sequence.sql.in | 33 ++ contrib/string/Makefile | 62 +++ contrib/string/string_io.c | 520 ++++++++++----------- contrib/string/string_io.h | 19 + contrib/string/string_io.sql.in | 104 +++++ contrib/userlock/Makefile | 62 +++ contrib/userlock/user_locks.c | 100 ++++ contrib/userlock/user_locks.doc | 30 ++ contrib/userlock/user_locks.h | 12 + contrib/userlock/user_locks.sql.in | 69 +++ 33 files changed, 2547 insertions(+), 578 deletions(-) create mode 100644 contrib/array/Makefile create mode 100644 contrib/array/array_iterator.h create mode 100644 contrib/array/array_iterator.sql.in create mode 100644 contrib/datetime/datetime_functions.h create mode 100644 contrib/datetime/datetime_functions.sql.in create mode 100644 contrib/miscutil/Makefile create mode 100644 contrib/miscutil/assert_test.c create mode 100644 contrib/miscutil/assert_test.h create mode 100644 contrib/miscutil/misc_utils.c create mode 100644 contrib/miscutil/misc_utils.h create mode 100644 contrib/miscutil/misc_utils.sql.in create mode 100644 contrib/pginterface/README.orig create mode 100644 contrib/pginterface/pginsert.c.orig create mode 100644 contrib/pginterface/pginterface.c.orig create mode 100644 contrib/pginterface/pgnulltest.c.orig create mode 100644 contrib/pginterface/pgwordcount.c.orig create mode 100644 contrib/sequence/Makefile create mode 100644 contrib/sequence/set_sequence.c create mode 100644 contrib/sequence/set_sequence.h create mode 100644 contrib/sequence/set_sequence.sql.in create mode 100644 contrib/string/Makefile create mode 100644 contrib/string/string_io.h create mode 100644 contrib/string/string_io.sql.in create mode 100644 contrib/userlock/Makefile create mode 100644 contrib/userlock/user_locks.c create mode 100644 contrib/userlock/user_locks.doc create mode 100644 contrib/userlock/user_locks.h create mode 100644 contrib/userlock/user_locks.sql.in diff --git a/contrib/array/Makefile b/contrib/array/Makefile new file mode 100644 index 0000000000..03c57e57c6 --- /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 f4ecfad903..de8dac95de 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_" 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 + * Copyright (c) 1997, Massimo Dal Zotto */ #include @@ -33,242 +15,297 @@ #include #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; + 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); - } + /* 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); + } - /* 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 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); - } + /* 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 - { - if (and) - { - return (0); - } - } - } - - if (and && result) - { + /* 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 01c1b2195c..031301799c 100644 --- a/contrib/array/array_iterator.doc +++ b/contrib/array/array_iterator.doc @@ -1,26 +1,44 @@ -From: Massimo Dal Zotto -Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST) -Subject: [PG95]: new operators for arrays +Array iterator functions, by Massimo Dal Zotto -- -----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_]" 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[]); -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. + -- 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 ? + +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 0000000000..0d9c58ed00 --- /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 0000000000..6489545d97 --- /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 930d6e57cf..52023b8e8a 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 d2c097583c..e925d985bf 100644 --- a/contrib/datetime/datetime_functions.c +++ b/contrib/datetime/datetime_functions.c @@ -6,113 +6,208 @@ * Copyright (c) 1996, Massimo Dal Zotto */ -#include +#include /* for sprintf() */ +#include +#include +#ifdef HAVE_FLOAT_H +#include +#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 *result = (TimeADT *) palloc(sizeof(TimeADT)); + TimeADT *time; - *result = *time1 - *time2; - return (result); + 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); } -TimeADT * +/* + * 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) +{ + 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 = PALLOC(strlen(buf)+1); + + strcpy( result, buf); + + return(result); +} + +TimeADT * +hhmm(TimeADT *time) +{ + TimeADT *result = PALLOCTYPE(TimeADT); + + *result = (((int) *time) / 60 * 60); + + return(result); +} + +TimeADT * +time_difference(TimeADT *time1, TimeADT *time2) +{ + TimeADT *time = PALLOCTYPE(TimeADT); + + *time = (*time1 - *time2); + return(time); +} + +int4 +time_hours(TimeADT *time) +{ + return (((int) *time) / 3600); +} + +int4 +time_minutes(TimeADT *time) +{ + return ((((int) *time) / 60) % 60); +} + +int4 +time_seconds(TimeADT *time) +{ + return (((int) *time) % 60); +} + +int4 +as_minutes(TimeADT *time) +{ + return (((int) *time) / 60); +} + +int4 +as_seconds(TimeADT *time) +{ + return ((int) *time); +} + +int4 +date_day(DateADT val) +{ + int year, month, day; + + j2date(val + JDATE_2000, &year, &month, &day); + + return (day); +} + +int4 +date_month(DateADT val) +{ + int year, month, day; + + j2date(val + JDATE_2000, &year, &month, &day); + + return (month); +} + +int4 +date_year(DateADT val) +{ + int year, month, day; + + j2date(val + JDATE_2000, &year, &month, &day); + + return (year); +} + +TimeADT * currenttime() { - time_t current_time; - struct tm *tm; - TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT)); + TimeADT *result = PALLOCTYPE(TimeADT); + struct tm *tm; + time_t current_time; - current_time = time(NULL); - tm = localtime(¤t_time); - *result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec); - return (result); + current_time = time(NULL); + tm = localtime(¤t_time); + *result = ((((tm->tm_hour*60)+tm->tm_min)*60)+tm->tm_sec); + + return (result); } DateADT currentdate() { - time_t current_time; - struct tm *tm; - DateADT result; + DateADT date; + struct tm tt, *tm = &tt; - current_time = time(NULL); - tm = localtime(¤t_time); - - result = date2j(tm->tm_year, tm->tm_mon + 1, tm->tm_mday) - - date2j(100, 1, 1); - return (result); + GetCurrentTime(tm); + date = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - JDATE_2000); + return (date); } -int4 -hours(TimeADT *time) -{ - return (*time / (60 * 60)); -} - -int4 -minutes(TimeADT *time) -{ - return (((int) (*time / 60)) % 60); -} - -int4 -seconds(TimeADT *time) -{ - return (((int) *time) % 60); -} - -int4 -day(DateADT *date) -{ - struct tm tm; - - j2date((*date + date2j(2000, 1, 1)), - &tm.tm_year, &tm.tm_mon, &tm.tm_mday); - - return (tm.tm_mday); -} - -int4 -month(DateADT *date) -{ - struct tm tm; - - j2date((*date + date2j(2000, 1, 1)), - &tm.tm_year, &tm.tm_mon, &tm.tm_mday); - - return (tm.tm_mon); -} - -int4 -year(DateADT *date) -{ - struct tm tm; - - j2date((*date + date2j(2000, 1, 1)), - &tm.tm_year, &tm.tm_mon, &tm.tm_mday); - - return (tm.tm_year); -} - -int4 -asminutes(TimeADT *time) -{ - int seconds = (int) *time; - - return (seconds / 60); -} - -int4 -asseconds(TimeADT *time) -{ - int seconds = (int) *time; - - return (seconds); -} +/* end of file */ diff --git a/contrib/datetime/datetime_functions.h b/contrib/datetime/datetime_functions.h new file mode 100644 index 0000000000..6847498405 --- /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 0000000000..81e40aa9af --- /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 0000000000..982515ab29 --- /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 0000000000..fa2ec1fcaa --- /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 + */ + +#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 0000000000..2af288729a --- /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 0000000000..3b8f379d21 --- /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 + */ + +#include + +#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 0000000000..7dd583c264 --- /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 0000000000..0c90ba52eb --- /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 0000000000..c52b5d1190 --- /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 0000000000..82838c2f8c --- /dev/null +++ b/contrib/pginterface/pginsert.c.orig @@ -0,0 +1,102 @@ +/* + * insert.c + * +*/ + +#include +#include +#include +#include +#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 0000000000..cdc419352a --- /dev/null +++ b/contrib/pginterface/pginterface.c.orig @@ -0,0 +1,232 @@ +/* + * pginterface.c + * +*/ + +#include +#include +#include +#include + +#include +#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 0000000000..96873ca7c8 --- /dev/null +++ b/contrib/pginterface/pgnulltest.c.orig @@ -0,0 +1,143 @@ +/* + * pgnulltest.c + * +*/ + +#define TEST_NON_NULLS + +#include +#include +#include +#include +#include +#include + +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 0000000000..859cf90b2a --- /dev/null +++ b/contrib/pginterface/pgwordcount.c.orig @@ -0,0 +1,72 @@ +/* + * wordcount.c + * +*/ + +#include +#include +#include +#include "halt.h" +#include +#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 0000000000..e82817fd15 --- /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 0000000000..7468efb5fd --- /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 + */ + +#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 0000000000..f5c6294966 --- /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 0000000000..0f1421b71b --- /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 0000000000..b9ff534137 --- /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 c45db69187..5dd6346b56 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; + 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 (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++; - if (size < 0) - { - size = strlen(data); + 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'; - /* 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; - } - } - } - *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; + register unsigned char *p, *r; + unsigned char *result; + int len; - if ((str == NULL) || (hdrsize < 0)) - { - return (char *) NULL; - } + 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--; + /* Compute result size */ + len = strlen(str); + for (p=str; *p; ) { + if (*p++ == '\\') { + if (ISOCTAL(*p)) { + if (ISOCTAL(*(p+1))) { + 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; - } - } - else - { - *r++ = c; + if (ISOCTAL(*(p+1))) { + p++; + len--; } + } + if (*p) p++; + len--; } + } - return ((char *) result); + /* 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; + } + } else { + *r++ = c; + } + } + + 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; + int len = 0; + char *s = NULL; - if (vlena) - { - len = VARSIZE(vlena) - VARHDRSZ; - s = VARDATA(vlena); - } - return (string_output(s, len)); + 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 0000000000..974f1f4262 --- /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 0000000000..ad1a51607c --- /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 0000000000..0865bde732 --- /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 0000000000..efc9b0a464 --- /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 + */ + +#include +#include +#include + +#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 0000000000..15ffe48f13 --- /dev/null +++ b/contrib/userlock/user_locks.doc @@ -0,0 +1,30 @@ +User locks, by Massimo Dal Zotto + +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 0000000000..ab890483fa --- /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 0000000000..da8d105de9 --- /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