mirror of https://github.com/postgres/postgres
parent
5aaf00f3f3
commit
951986c550
@ -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 |
@ -1,26 +1,44 @@ |
|||||||
From: Massimo Dal Zotto <dz@cs.unitn.it> |
Array iterator functions, by Massimo Dal Zotto <dz@cs.unitn.it> |
||||||
Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST) |
|
||||||
Subject: [PG95]: new operators for arrays |
|
||||||
|
|
||||||
- -----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 |
If we have defined T array types and O scalar operators we can |
||||||
clauses based on the values of single elements of arrays. |
define T x O x 2 array functions, each of them has a name like |
||||||
For example I can now select rows having some or all element of an array |
"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: |
attribute equal to a given value or matching a regular expression: |
||||||
|
|
||||||
select * from t where t.foo *= 'bar'; |
create table t(id int4[], txt text[]); |
||||||
select * from t where t.foo **~ '^ba[rz]'; |
|
||||||
|
-- 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 |
The scheme is quite general, each operator which operates on a base type |
||||||
be iterated over the elements of an array. It seem to work well but defining |
can be iterated over the elements of an array. It seem to work well but |
||||||
each new operators requires writing a different C function. Furthermore in |
defining each new operators requires writing a different C function. |
||||||
each function there are two hardcoded OIDs which reference a base type and |
Furthermore in each function there are two hardcoded OIDs which reference |
||||||
a procedure. Not very portable. Can anyone suggest a better and more portable |
a base type and a procedure. Not very portable. Can anyone suggest a |
||||||
way to do it ? Do you think this could be a useful feature for next release ? |
better and more portable way to do it ? |
||||||
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. |
|
||||||
|
|
||||||
|
See also array_iterator.sql for an example on how to use this module. |
||||||
|
@ -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 |
@ -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 |
@ -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 |
include $(SRCDIR)/Makefile.global |
||||||
ld -Bshareable -o $P datetime_functions.o
|
|
||||||
|
|
||||||
datetime_functions.sql: datetime.prot |
INCLUDE_OPT = -I ./ \
|
||||||
sh datetime.prot $P
|
-I $(SRCDIR)/ \
|
||||||
psql -c "\idatetime_functions.sql" template1
|
-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 |
||||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 */ |
@ -0,0 +1,7 @@ |
|||||||
|
#ifndef ASSERT_TEST_H |
||||||
|
#define ASSERT_TEST_H |
||||||
|
|
||||||
|
int assert_enable(int val); |
||||||
|
int assert_test(int val); |
||||||
|
|
||||||
|
#endif |
@ -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 */ |
@ -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 |
@ -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 |
@ -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) |
@ -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; |
||||||
|
} |
@ -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); |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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; |
||||||
|
} |
@ -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 |
@ -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 */ |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 */ |
@ -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. |
@ -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 |
@ -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 |
Loading…
Reference in new issue