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> |
||||
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[]); |
||||
|
||||
-- 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 ? 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. |
||||
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. |
||||
|
@ -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 |
||||
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 |
||||
|
@ -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