Update of contrib stuff from massimo.

This commit is contained in:
Bruce Momjian 1997-11-05 21:38:25 +00:00
parent 5aaf00f3f3
commit 951986c550
33 changed files with 2547 additions and 578 deletions

62
contrib/array/Makefile Normal file
View File

@ -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

View File

@ -1,30 +1,12 @@
/*
* array_iterator.c --
*
* This file defines a new group of operators which take an
* This file defines a new class of operators which take an
* array and a scalar value, iterate a scalar operator over the
* elements of the array and the value and compute a result as
* the logical OR or AND of the results.
* For example array_int4eq returns true if some of the elements
* of an array of int4 is equal to the given value:
* the logical OR or AND of the iteration results.
*
* array_int4eq({1,2,3}, 1) --> true
* array_int4eq({1,2,3}, 4) --> false
*
* If we have defined T array types and O scalar operators
* we can define T x O array operators, each of them has a name
* like "array_<basetype><operation>" and takes an array of type T
* iterating the operator O over all the elements. Note however
* that some of the possible combination are invalid, for example
* the array_int4_like because there is no like operator for int4.
* It is now possible to write queries which look inside the arrays:
*
* create table t(id int4[], txt text[]);
* select * from t where t.id *= 123;
* select * from t where t.txt *~ '[a-z]';
* select * from t where t.txt[1:3] **~ '[a-z]';
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
* Copyright (c) 1997, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <ctype.h>
@ -33,242 +15,297 @@
#include <string.h>
#include "postgres.h"
#include "pg_type.h"
#include "miscadmin.h"
#include "syscache.h"
#include "access/xact.h"
#include "backend/fmgr.h"
#include "catalog/pg_type.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
#include "array_iterator.h"
static int32
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
{
HeapTuple typ_tuple;
TypeTupleForm typ_struct;
bool typbyval;
int typlen;
func_ptr proc_fn;
int pronargs;
int nitems,
i,
result;
int ndim,
*dim;
char *p;
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 */

View File

@ -1,26 +1,44 @@
From: Massimo Dal Zotto <dz@cs.unitn.it>
Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST)
Subject: [PG95]: new operators for arrays
Array iterator functions, by Massimo Dal Zotto <dz@cs.unitn.it>
- -----BEGIN PGP SIGNED MESSAGE-----
This loadable module defines a new class of functions which take
an array and a scalar value, iterate a scalar operator over the
elements of the array and the value, and compute a result as
the logical OR or AND of the iteration results.
For example array_int4eq returns true if some of the elements
of an array of int4 is equal to the given value:
Hi,
array_int4eq({1,2,3}, 1) --> true
array_int4eq({1,2,3}, 4) --> false
I have written an extension to Postgres95 which allows to use qualification
clauses based on the values of single elements of arrays.
For example I can now select rows having some or all element of an array
If we have defined T array types and O scalar operators we can
define T x O x 2 array functions, each of them has a name like
"array_[all_]<basetype><operation>" and takes an array of type T
iterating the operator O over all the elements. Note however
that some of the possible combination are invalid, for example
the array_int4_like because there is no like operator for int4.
We can then define new operators based on these functions and use
them to write queries with qualification clauses based on the
values of some of the elements of an array.
For example to select rows having some or all element of an array
attribute equal to a given value or matching a regular expression:
select * from t where t.foo *= 'bar';
select * from t where t.foo **~ '^ba[rz]';
create table t(id int4[], txt text[]);
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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -6,113 +6,208 @@
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <time.h>
#include <stdio.h> /* for sprintf() */
#include <string.h>
#include <limits.h>
#ifdef HAVE_FLOAT_H
#include <float.h>
#endif
#include "postgres.h"
#include "utils/palloc.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/nabstime.h"
#include "utils/datetime.h"
#include "access/xact.h"
#include "datetime_functions.h"
TimeADT *
time_difference(TimeADT *time1, TimeADT *time2)
/* Constant to replace calls to date2j(2000,1,1) */
#define JDATE_2000 2451545
/*
* A modified version of time_in which allows the value 24:00:00 for
* time and converts it to TimeADT data type forcing seconds to 0.
* This can be Useful if you need to handle TimeADT values limited
* to hh:mm like in timetables.
*/
TimeADT *
hhmm_in(char *str)
{
TimeADT *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(&current_time);
*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
return (result);
current_time = time(NULL);
tm = localtime(&current_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(&current_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 */

View File

@ -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

View File

@ -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

62
contrib/miscutil/Makefile Normal file
View File

@ -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

View File

@ -0,0 +1,43 @@
/*
* assert_test.c --
*
* This file tests Postgres assert checking.
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include "postgres.h"
#include "assert_test.h"
extern int assertTest(int val);
extern int assertEnable(int val);
int
assert_enable(int val)
{
return assertEnable(val);
}
int
assert_test(int val)
{
return assertTest(val);
}
/*
-- Enable/disable Postgres assert checking.
--
create function assert_enable(int4) returns int4
as '/usr/local/pgsql/lib/assert_test.so'
language 'C';
-- Test Postgres assert checking.
--
create function assert_test(int4) returns int4
as '/usr/local/pgsql/lib/assert_test.so'
language 'C';
*/
/* end of file */

View File

@ -0,0 +1,7 @@
#ifndef ASSERT_TEST_H
#define ASSERT_TEST_H
int assert_enable(int val);
int assert_test(int val);
#endif

View File

@ -0,0 +1,50 @@
/*
* utils.c --
*
* This file defines various Postgres utility functions.
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <unistd.h>
#include "postgres.h"
#include "utils/palloc.h"
#include "misc_utils.h"
extern int ExecutorLimit(int limit);
extern void Async_Unlisten(char *relname, int pid);
int
query_limit(int limit)
{
return ExecutorLimit(limit);
}
int
backend_pid()
{
return getpid();
}
int
unlisten(char *relname)
{
Async_Unlisten(relname, getpid());
return 0;
}
int
max(int x, int y)
{
return ((x > y) ? x : y);
}
int
min(int x, int y)
{
return ((x < y) ? x : y);
}
/* end of file */

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,102 @@
/*
* insert.c
*
*/
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <libpq-fe.h>
#include "halt.h"
#include "pginterface.h"
int
main(int argc, char **argv)
{
char query[4000];
int row = 1;
int aint;
float afloat;
double adouble;
char achar[11],
achar16[17],
abpchar[11],
avarchar[51],
atext[51];
time_t aabstime;
if (argc != 2)
halt("Usage: %s database\n", argv[0]);
connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue();
doquery("DROP TABLE testfetch");
on_error_stop();
doquery("\
CREATE TABLE testfetch( \
aint int4, \
afloat float4, \
adouble float8, \
achar char, \
achar16 char16, \
abpchar char(10), \
avarchar varchar(50), \
atext text, \
aabstime abstime) \
");
while (1)
{
sprintf(query, "INSERT INTO testfetch VALUES ( \
%d, \
2322.12, \
'923121.0323'::float8, \
'A', \
'Betty', \
'Charley', \
'Doug', \
'Ernie', \
'now' )", row);
doquery(query);
doquery("BEGIN WORK");
doquery("DECLARE c_testfetch BINARY CURSOR FOR \
SELECT * FROM testfetch");
doquery("FETCH ALL IN c_testfetch");
while (fetch(
&aint,
&afloat,
&adouble,
achar,
achar16,
abpchar,
avarchar,
atext,
&aabstime) != END_OF_TUPLES)
printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
bpchar %s\nvarchar %s\ntext %s\nabstime %s",
aint,
afloat,
adouble,
achar,
achar16,
abpchar,
avarchar,
atext,
ctime(&aabstime));
doquery("CLOSE c_testfetch");
doquery("COMMIT WORK");
printf("--- %-d rows inserted so far\n", row);
row++;
}
disconnectdb();
return 0;
}

View File

@ -0,0 +1,232 @@
/*
* pginterface.c
*
*/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdarg.h>
#include <libpq-fe.h>
#include "halt.h"
#include "pginterface.h"
static void sig_disconnect();
static void set_signals();
#define NUL '\0'
/* GLOBAL VARIABLES */
static PGconn *conn;
static PGresult *res = NULL;
#define ON_ERROR_STOP 0
#define ON_ERROR_CONTINUE 1
static int on_error_state = ON_ERROR_STOP;
/* LOCAL VARIABLES */
static sigset_t block_sigs,
unblock_sigs;
static int tuple;
/*
**
** connectdb - returns PGconn structure
**
*/
PGconn *
connectdb(char *dbName,
char *pghost,
char *pgport,
char *pgoptions,
char *pgtty)
{
/* make a connection to the database */
conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);
if (PQstatus(conn) == CONNECTION_BAD)
halt("Connection to database '%s' failed.\n%s\n", dbName,
PQerrorMessage(conn));
set_signals();
return conn;
}
/*
**
** disconnectdb
**
*/
void
disconnectdb()
{
PQfinish(conn);
}
/*
**
** doquery - returns PGresult structure
**
*/
PGresult *
doquery(char *query)
{
if (res != NULL)
PQclear(res);
sigprocmask(SIG_SETMASK, &block_sigs, NULL);
res = PQexec(conn, query);
sigprocmask(SIG_SETMASK, &unblock_sigs, NULL);
if (on_error_state == ON_ERROR_STOP &&
(res == NULL ||
PQresultStatus(res) == PGRES_BAD_RESPONSE ||
PQresultStatus(res) == PGRES_NONFATAL_ERROR ||
PQresultStatus(res) == PGRES_FATAL_ERROR))
{
if (res != NULL)
fprintf(stderr, "query error: %s\n", PQcmdStatus(res));
else
fprintf(stderr, "connection error: %s\n", PQerrorMessage(conn));
PQfinish(conn);
halt("failed request: %s\n", query);
}
tuple = 0;
return res;
}
/*
**
** fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES
** NULL pointers are skipped
**
*/
int
fetch(void *param,...)
{
va_list ap;
int arg,
num_fields;
num_fields = PQnfields(res);
if (tuple >= PQntuples(res))
return END_OF_TUPLES;
va_start(ap, param);
for (arg = 0; arg < num_fields; arg++)
{
if (param != NULL)
{
if (PQfsize(res, arg) == -1)
{
memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
}
else
memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
}
param = va_arg(ap, char *);
}
va_end(ap);
return tuple++;
}
/*
**
** fetchwithnulls - returns tuple number (starts at 0),
** or the value END_OF_TUPLES
** Returns true or false into null indicator variables
** NULL pointers are skipped
*/
int
fetchwithnulls(void *param,...)
{
va_list ap;
int arg,
num_fields;
num_fields = PQnfields(res);
if (tuple >= PQntuples(res))
return END_OF_TUPLES;
va_start(ap, param);
for (arg = 0; arg < num_fields; arg++)
{
if (param != NULL)
{
if (PQfsize(res, arg) == -1)
{
memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
}
else
memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
}
param = va_arg(ap, char *);
if (PQgetisnull(res, tuple, arg) != 0)
*(int *) param = 1;
else
*(int *) param = 0;
param = va_arg(ap, char *);
}
va_end(ap);
return tuple++;
}
/*
**
** on_error_stop
**
*/
void
on_error_stop()
{
on_error_state = ON_ERROR_STOP;
}
/*
**
** on_error_continue
**
*/
void
on_error_continue()
{
on_error_state = ON_ERROR_CONTINUE;
}
/*
**
** sig_disconnect
**
*/
static void
sig_disconnect()
{
fprintf(stderr, "exiting...\n");
PQfinish(conn);
exit(1);
}
/*
**
** set_signals
**
*/
static void
set_signals()
{
sigemptyset(&block_sigs);
sigemptyset(&unblock_sigs);
sigaddset(&block_sigs, SIGTERM);
sigaddset(&block_sigs, SIGHUP);
sigaddset(&block_sigs, SIGINT);
/* sigaddset(&block_sigs,SIGQUIT); no block */
sigprocmask(SIG_SETMASK, &unblock_sigs, NULL);
signal(SIGTERM, sig_disconnect);
signal(SIGHUP, sig_disconnect);
signal(SIGINT, sig_disconnect);
signal(SIGQUIT, sig_disconnect);
}

View File

@ -0,0 +1,143 @@
/*
* pgnulltest.c
*
*/
#define TEST_NON_NULLS
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <halt.h>
#include <libpq-fe.h>
#include <pginterface.h>
int
main(int argc, char **argv)
{
char query[4000];
int row = 1;
int aint;
float afloat;
double adouble;
char achar[11],
achar16[17],
abpchar[11],
avarchar[51],
atext[51];
time_t aabstime;
int aint_null,
afloat_null,
adouble_null,
achar_null,
achar16_null,
abpchar_null,
avarchar_null,
atext_null,
aabstime_null;
if (argc != 2)
halt("Usage: %s database\n", argv[0]);
connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue();
doquery("DROP TABLE testfetch");
on_error_stop();
doquery("\
CREATE TABLE testfetch( \
aint int4, \
afloat float4, \
adouble float8, \
achar char, \
achar16 char16, \
abpchar char(10), \
avarchar varchar(50), \
atext text, \
aabstime abstime) \
");
#ifdef TEST_NON_NULLS
sprintf(query, "INSERT INTO testfetch VALUES ( \
0, \
0, \
0, \
'', \
'', \
'', \
'', \
'', \
'');");
#else
sprintf(query, "INSERT INTO testfetch VALUES ( \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL, \
NULL);");
#endif
doquery(query);
doquery("BEGIN WORK");
doquery("DECLARE c_testfetch BINARY CURSOR FOR \
SELECT * FROM testfetch");
doquery("FETCH ALL IN c_testfetch");
if (fetchwithnulls(
&aint,
&aint_null,
&afloat,
&afloat_null,
&adouble,
&adouble_null,
achar,
&achar_null,
achar16,
&achar16_null,
abpchar,
&abpchar_null,
avarchar,
&avarchar_null,
atext,
&atext_null,
&aabstime,
&aabstime_null) != END_OF_TUPLES)
printf("int %d\nfloat %f\ndouble %f\nchar %s\nchar16 %s\n\
bpchar %s\nvarchar %s\ntext %s\nabstime %s\n",
aint,
afloat,
adouble,
achar,
achar16,
abpchar,
avarchar,
atext,
ctime(&aabstime));
printf("NULL:\nint %d\nfloat %d\ndouble %d\nchar %d\nchar16 %d\n\
bpchar %d\nvarchar %d\ntext %d\nabstime %d\n",
aint_null,
afloat_null,
adouble_null,
achar_null,
achar16_null,
abpchar_null,
avarchar_null,
atext_null,
aabstime_null);
doquery("CLOSE c_testfetch");
doquery("COMMIT WORK");
printf("--- %-d rows inserted so far\n", row);
row++;
disconnectdb();
return 0;
}

View File

@ -0,0 +1,72 @@
/*
* wordcount.c
*
*/
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include "halt.h"
#include <libpq-fe.h>
#include "pginterface.h"
int
main(int argc, char **argv)
{
char query[4000];
int row = 0;
int count;
char line[4000];
if (argc != 2)
halt("Usage: %s database\n", argv[0]);
connectdb(argv[1], NULL, NULL, NULL, NULL);
on_error_continue();
doquery("DROP TABLE words");
on_error_stop();
doquery("\
CREATE TABLE words( \
matches int4, \
word text ) \
");
doquery("\
CREATE INDEX i_words_1 ON words USING btree ( \
word text_ops )\
");
while (1)
{
if (scanf("%s", line) != 1)
break;
doquery("BEGIN WORK");
sprintf(query, "\
DECLARE c_words BINARY CURSOR FOR \
SELECT count(*) \
FROM words \
WHERE word = '%s'", line);
doquery(query);
doquery("FETCH ALL IN c_words");
while (fetch(&count) == END_OF_TUPLES)
count = 0;
doquery("CLOSE c_words");
doquery("COMMIT WORK");
if (count == 0)
sprintf(query, "\
INSERT INTO words \
VALUES (1, '%s')", line);
else
sprintf(query, "\
UPDATE words \
SET matches = matches + 1 \
WHERE word = '%s'", line);
doquery(query);
row++;
}
disconnectdb();
return 0;
}

62
contrib/sequence/Makefile Normal file
View File

@ -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

View File

@ -0,0 +1,41 @@
/*
* set_sequence.c --
*
* Set a new sequence value.
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include "postgres.h"
#include "nodes/parsenodes.h"
#include "commands/sequence.h"
#include "set_sequence.h"
extern int setval(struct varlena *seqin, int4 val);
int
set_currval(struct varlena *sequence, int4 nextval)
{
return setval(sequence, nextval);
}
int
next_id(struct varlena *sequence)
{
return nextval(sequence);
}
int
last_id(struct varlena *sequence)
{
return currval(sequence);
}
int
set_last_id(struct varlena *sequence, int4 nextval)
{
return setval(sequence, nextval);
}
/* end of file */

View File

@ -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

View File

@ -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

62
contrib/string/Makefile Normal file
View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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

62
contrib/userlock/Makefile Normal file
View File

@ -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

View File

@ -0,0 +1,100 @@
/*
* user_locks.c --
*
* This loadable module, together with my user-lock.patch applied to the
* backend, provides support for user-level long-term cooperative locks.
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "postgres.h"
#include "miscadmin.h"
#include "storage/lock.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/block.h"
#include "storage/multilev.h"
#include "utils/elog.h"
#include "user_locks.h"
#define USER_LOCKS_TABLE_ID 0
extern Oid MyDatabaseId;
int
user_lock(unsigned int id1, unsigned int id2, LOCKT lockt)
{
LOCKTAG tag;
memset(&tag,0,sizeof(LOCKTAG));
tag.relId = 0;
tag.dbId = MyDatabaseId;
tag.tupleId.ip_blkid.bi_hi = id2 >> 16;
tag.tupleId.ip_blkid.bi_lo = id2 & 0xffff;
tag.tupleId.ip_posid = (unsigned short) (id1 & 0xffff);
return LockAcquire(USER_LOCKS_TABLE_ID, &tag, lockt);
}
int
user_unlock(unsigned int id1, unsigned int id2, LOCKT lockt)
{
LOCKTAG tag;
memset(&tag, 0,sizeof(LOCKTAG));
tag.relId = 0;
tag.dbId = MyDatabaseId;
tag.tupleId.ip_blkid.bi_hi = id2 >> 16;
tag.tupleId.ip_blkid.bi_lo = id2 & 0xffff;
tag.tupleId.ip_posid = (unsigned short) (id1 & 0xffff);
return LockRelease(USER_LOCKS_TABLE_ID, &tag, lockt);
}
int
user_write_lock(unsigned int id1, unsigned int id2)
{
return user_lock(id1, id2, WRITE_LOCK);
}
int
user_write_unlock(unsigned int id1, unsigned int id2)
{
return user_unlock(id1, id2, WRITE_LOCK);
}
int
user_write_lock_oid(Oid oid)
{
return user_lock(0, oid, WRITE_LOCK);
}
int
user_write_unlock_oid(Oid oid)
{
return user_unlock(0, oid, WRITE_LOCK);
}
int
user_unlock_all()
{
PROC *proc;
SHMEM_OFFSET location;
ShmemPIDLookup(getpid(),&location);
if (location == INVALID_OFFSET) {
elog(NOTICE, "UserUnlockAll: unable to get proc ptr");
return -1;
}
proc = (PROC *) MAKE_PTR(location);
return LockReleaseAll(USER_LOCKS_TABLE_ID, &proc->lockQueue);
}
/* end of file */

View File

@ -0,0 +1,30 @@
User locks, by Massimo Dal Zotto <dz@cs.unitn.it>
This loadable module, together with my user-lock.patch applied to the
backend, provides support for user-level long-term cooperative locks.
For example one can write (this example is written in TclX):
set rec [sql "select ...,user_write_lock_oid(oid) from table where id=$id"]
if {[keylget rec user_write_lock_oid] == 1} {
# the write lock has been acquired with the record, start
# a long editing session, then update the database and
# release the lock.
sql "update table set ... where id=$id"
sql "select user_write_unlock_oid([keylget rec oid])"
} else {
# the record has been read but the write lock couldn't be acquired,
# so it should not be modified by the application.
messageBox "This record is in use by another user, retry later"
}
This could also be done by setting a flag in the record itself but in
this case you have the overhead of the updates to the record and there
may be some locks not released if the backend or the application crashes
before resetting the flag.
It could also be done with a begin/end block but in this case the entire
table would be locked by postgres and it is not acceptable to do this for
a long period because other transactions would block completely.
Note that this type of locks are handled cooperatively by the application
and do not interfere with the normal locks used by postgres. So an user
could still modify an user-locked record if he wanted to ignore the lock.

View File

@ -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

View File

@ -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