Update of contrib stuff from massimo.
This commit is contained in:
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,30 +1,12 @@
|
|||
/*
|
||||
* array_iterator.c --
|
||||
*
|
||||
* This file defines a new group of operators which take an
|
||||
* This file defines a new class of operators which take an
|
||||
* array and a scalar value, iterate a scalar operator over the
|
||||
* elements of the array and the value and compute a result as
|
||||
* the logical OR or AND of the results.
|
||||
* For example array_int4eq returns true if some of the elements
|
||||
* of an array of int4 is equal to the given value:
|
||||
* the logical OR or AND of the iteration results.
|
||||
*
|
||||
* array_int4eq({1,2,3}, 1) --> true
|
||||
* array_int4eq({1,2,3}, 4) --> false
|
||||
*
|
||||
* If we have defined T array types and O scalar operators
|
||||
* we can define T x O array operators, each of them has a name
|
||||
* like "array_<basetype><operation>" and takes an array of type T
|
||||
* iterating the operator O over all the elements. Note however
|
||||
* that some of the possible combination are invalid, for example
|
||||
* the array_int4_like because there is no like operator for int4.
|
||||
* It is now possible to write queries which look inside the arrays:
|
||||
*
|
||||
* create table t(id int4[], txt text[]);
|
||||
* select * from t where t.id *= 123;
|
||||
* select * from t where t.txt *~ '[a-z]';
|
||||
* select * from t where t.txt[1:3] **~ '[a-z]';
|
||||
*
|
||||
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
* Copyright (c) 1997, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
@ -33,242 +15,297 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "postgres.h"
|
||||
#include "pg_type.h"
|
||||
#include "miscadmin.h"
|
||||
#include "syscache.h"
|
||||
#include "access/xact.h"
|
||||
#include "backend/fmgr.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/elog.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
#include "array_iterator.h"
|
||||
|
||||
static int32
|
||||
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
|
||||
{
|
||||
HeapTuple typ_tuple;
|
||||
TypeTupleForm typ_struct;
|
||||
bool typbyval;
|
||||
int typlen;
|
||||
func_ptr proc_fn;
|
||||
int pronargs;
|
||||
int nitems,
|
||||
i,
|
||||
result;
|
||||
int ndim,
|
||||
*dim;
|
||||
char *p;
|
||||
HeapTuple typ_tuple;
|
||||
TypeTupleForm typ_struct;
|
||||
bool typbyval;
|
||||
int typlen;
|
||||
func_ptr proc_fn;
|
||||
int pronargs;
|
||||
int nitems, i, result;
|
||||
int ndim, *dim;
|
||||
char *p;
|
||||
|
||||
/* Sanity checks */
|
||||
if ((array == (ArrayType *) NULL)
|
||||
|| (ARR_IS_LO(array) == true))
|
||||
{
|
||||
/* elog(NOTICE, "array_iterator: array is null"); */
|
||||
return (0);
|
||||
}
|
||||
ndim = ARR_NDIM(array);
|
||||
dim = ARR_DIMS(array);
|
||||
nitems = getNitems(ndim, dim);
|
||||
if (nitems == 0)
|
||||
{
|
||||
/* elog(NOTICE, "array_iterator: nitems = 0"); */
|
||||
return (0);
|
||||
}
|
||||
/* Sanity checks */
|
||||
if ((array == (ArrayType *) NULL)
|
||||
|| (ARR_IS_LO(array) == true)) {
|
||||
/* elog(NOTICE, "array_iterator: array is null"); */
|
||||
return (0);
|
||||
}
|
||||
ndim = ARR_NDIM(array);
|
||||
dim = ARR_DIMS(array);
|
||||
nitems = getNitems(ndim, dim);
|
||||
if (nitems == 0) {
|
||||
/* elog(NOTICE, "array_iterator: nitems = 0"); */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Lookup element type information */
|
||||
typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype), 0, 0, 0);
|
||||
if (!HeapTupleIsValid(typ_tuple))
|
||||
{
|
||||
elog(WARN, "array_iterator: cache lookup failed for type %d", elemtype);
|
||||
return 0;
|
||||
}
|
||||
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
|
||||
typlen = typ_struct->typlen;
|
||||
typbyval = typ_struct->typbyval;
|
||||
/* Lookup element type information */
|
||||
typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype),0,0,0);
|
||||
if (!HeapTupleIsValid(typ_tuple)) {
|
||||
elog(WARN,"array_iterator: cache lookup failed for type %d", elemtype);
|
||||
return 0;
|
||||
}
|
||||
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
|
||||
typlen = typ_struct->typlen;
|
||||
typbyval = typ_struct->typbyval;
|
||||
|
||||
/* Lookup the function entry point */
|
||||
proc_fn == (func_ptr) NULL;
|
||||
fmgr_info(proc, &proc_fn, &pronargs);
|
||||
if ((proc_fn == NULL) || (pronargs != 2))
|
||||
{
|
||||
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
|
||||
return (0);
|
||||
}
|
||||
/* Lookup the function entry point */
|
||||
proc_fn = (func_ptr) NULL;
|
||||
fmgr_info(proc, &proc_fn, &pronargs);
|
||||
if ((proc_fn == NULL) || (pronargs != 2)) {
|
||||
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Scan the array and apply the operator to each element */
|
||||
result = 0;
|
||||
p = ARR_DATA_PTR(array);
|
||||
for (i = 0; i < nitems; i++)
|
||||
{
|
||||
if (typbyval)
|
||||
{
|
||||
switch (typlen)
|
||||
{
|
||||
case 1:
|
||||
result = (int) (*proc_fn) (*p, value);
|
||||
break;
|
||||
case 2:
|
||||
result = (int) (*proc_fn) (*(int16 *) p, value);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
result = (int) (*proc_fn) (*(int32 *) p, value);
|
||||
break;
|
||||
}
|
||||
p += typlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = (int) (*proc_fn) (p, value);
|
||||
if (typlen > 0)
|
||||
{
|
||||
p += typlen;
|
||||
}
|
||||
else
|
||||
{
|
||||
p += INTALIGN(*(int32 *) p);
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
if (!and)
|
||||
{
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (and)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (and && result)
|
||||
{
|
||||
/* Scan the array and apply the operator to each element */
|
||||
result = 0;
|
||||
p = ARR_DATA_PTR(array);
|
||||
for (i = 0; i < nitems; i++) {
|
||||
if (typbyval) {
|
||||
switch(typlen) {
|
||||
case 1:
|
||||
result = (int) (*proc_fn)(*p, value);
|
||||
break;
|
||||
case 2:
|
||||
result = (int) (*proc_fn)(* (int16 *) p, value);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
result = (int) (*proc_fn)(* (int32 *) p, value);
|
||||
break;
|
||||
}
|
||||
p += typlen;
|
||||
} else {
|
||||
result = (int) (*proc_fn)(p, value);
|
||||
if (typlen > 0) {
|
||||
p += typlen;
|
||||
} else {
|
||||
p += INTALIGN(* (int32 *) p);
|
||||
}
|
||||
}
|
||||
if (result) {
|
||||
if (!and) {
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
if (and) {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (and && result) {
|
||||
return (1);
|
||||
} else {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterators for type _text
|
||||
* Iterator functions for type _text
|
||||
*/
|
||||
|
||||
int32
|
||||
array_texteq(ArrayType *array, char *value)
|
||||
array_texteq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 67, /* texteq */
|
||||
0, /* logical or */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 67, /* texteq */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_texteq(ArrayType *array, char *value)
|
||||
array_all_texteq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 67, /* texteq */
|
||||
1, /* logical and */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 67, /* texteq */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_textregexeq(ArrayType *array, char *value)
|
||||
array_textregexeq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 81, /* textregexeq */
|
||||
0, /* logical or */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 1254, /* textregexeq */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_textregexeq(ArrayType *array, char *value)
|
||||
array_all_textregexeq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 81, /* textregexeq */
|
||||
1, /* logical and */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 25, /* text */
|
||||
(Oid) 1254, /* textregexeq */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterators for type _char16. Note that the regexp operators
|
||||
* take the second argument of type text.
|
||||
* Iterator functions for type _char16. Note that the regexp
|
||||
* operators take the second argument of type text.
|
||||
*/
|
||||
|
||||
int32
|
||||
array_char16eq(ArrayType *array, char *value)
|
||||
array_char16eq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 490, /* char16eq */
|
||||
0, /* logical or */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 1275, /* char16eq */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_char16eq(ArrayType *array, char *value)
|
||||
array_all_char16eq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 490, /* char16eq */
|
||||
1, /* logical and */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 1275, /* char16eq */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_char16regexeq(ArrayType *array, char *value)
|
||||
array_char16regexeq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 700, /* char16regexeq */
|
||||
0, /* logical or */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 1288, /* char16regexeq */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_char16regexeq(ArrayType *array, char *value)
|
||||
array_all_char16regexeq(ArrayType *array, char* value)
|
||||
{
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 700, /* char16regexeq */
|
||||
1, /* logical and */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 20, /* char16 */
|
||||
(Oid) 1288, /* char16regexeq */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterators for type _int4
|
||||
* Iterator functions for type _int4
|
||||
*/
|
||||
|
||||
int32
|
||||
array_int4eq(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 65, /* int4eq */
|
||||
0, /* logical or */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 65, /* int4eq */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_int4eq(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 65, /* int4eq */
|
||||
1, /* logical and */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 65, /* int4eq */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_int4ne(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 144, /* int4ne */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_int4ne(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 144, /* int4ne */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_int4gt(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 147, /* int4gt */
|
||||
0, /* logical or */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 147, /* int4gt */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_int4gt(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 147, /* int4gt */
|
||||
1, /* logical and */
|
||||
array, (Datum) value);
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 147, /* int4gt */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_int4ge(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 150, /* int4ge */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_int4ge(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 150, /* int4ge */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_int4lt(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 66, /* int4lt */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_int4lt(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 66, /* int4lt */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_int4le(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 149, /* int4le */
|
||||
0, /* logical or */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
int32
|
||||
array_all_int4le(ArrayType *array, int4 value)
|
||||
{
|
||||
return array_iterator((Oid) 23, /* int4 */
|
||||
(Oid) 149, /* int4le */
|
||||
1, /* logical and */
|
||||
array, (Datum)value);
|
||||
}
|
||||
|
||||
/* end of file */
|
||||
|
|
|
@ -1,26 +1,44 @@
|
|||
From: Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST)
|
||||
Subject: [PG95]: new operators for arrays
|
||||
Array iterator functions, by Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
|
||||
- -----BEGIN PGP SIGNED MESSAGE-----
|
||||
This loadable module defines a new class of functions which take
|
||||
an array and a scalar value, iterate a scalar operator over the
|
||||
elements of the array and the value, and compute a result as
|
||||
the logical OR or AND of the iteration results.
|
||||
For example array_int4eq returns true if some of the elements
|
||||
of an array of int4 is equal to the given value:
|
||||
|
||||
Hi,
|
||||
array_int4eq({1,2,3}, 1) --> true
|
||||
array_int4eq({1,2,3}, 4) --> false
|
||||
|
||||
I have written an extension to Postgres95 which allows to use qualification
|
||||
clauses based on the values of single elements of arrays.
|
||||
For example I can now select rows having some or all element of an array
|
||||
If we have defined T array types and O scalar operators we can
|
||||
define T x O x 2 array functions, each of them has a name like
|
||||
"array_[all_]<basetype><operation>" and takes an array of type T
|
||||
iterating the operator O over all the elements. Note however
|
||||
that some of the possible combination are invalid, for example
|
||||
the array_int4_like because there is no like operator for int4.
|
||||
|
||||
We can then define new operators based on these functions and use
|
||||
them to write queries with qualification clauses based on the
|
||||
values of some of the elements of an array.
|
||||
For example to select rows having some or all element of an array
|
||||
attribute equal to a given value or matching a regular expression:
|
||||
|
||||
select * from t where t.foo *= 'bar';
|
||||
select * from t where t.foo **~ '^ba[rz]';
|
||||
create table t(id int4[], txt text[]);
|
||||
|
||||
The scheme is quite general, each operator which operates on a base type can
|
||||
be iterated over the elements of an array. It seem to work well but defining
|
||||
each new operators requires writing a different C function. Furthermore in
|
||||
each function there are two hardcoded OIDs which reference a base type and
|
||||
a procedure. Not very portable. Can anyone suggest a better and more portable
|
||||
way to do it ? Do you think this could be a useful feature for next release ?
|
||||
Here is my code, it can be compiled and loaded as a dynamic module without
|
||||
need to recompile the backend. I have defined only the few operators I needed,
|
||||
the list can be extended. Feddback is welcome.
|
||||
-- select tuples with some id element equal to 123
|
||||
select * from t where t.id *= 123;
|
||||
|
||||
-- select tuples with some txt element matching '[a-z]'
|
||||
select * from t where t.txt *~ '[a-z]';
|
||||
|
||||
-- select tuples with all txt elements matching '^[A-Z]'
|
||||
select * from t where t.txt[1:3] **~ '^[A-Z]';
|
||||
|
||||
The scheme is quite general, each operator which operates on a base type
|
||||
can be iterated over the elements of an array. It seem to work well but
|
||||
defining each new operators requires writing a different C function.
|
||||
Furthermore in each function there are two hardcoded OIDs which reference
|
||||
a base type and a procedure. Not very portable. Can anyone suggest a
|
||||
better and more portable way to do it ?
|
||||
|
||||
See also array_iterator.sql for an example on how to use this module.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,113 +6,208 @@
|
|||
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h> /* for sprintf() */
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#ifdef HAVE_FLOAT_H
|
||||
#include <float.h>
|
||||
#endif
|
||||
|
||||
#include "postgres.h"
|
||||
#include "utils/palloc.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/nabstime.h"
|
||||
#include "utils/datetime.h"
|
||||
#include "access/xact.h"
|
||||
|
||||
#include "datetime_functions.h"
|
||||
|
||||
TimeADT *
|
||||
time_difference(TimeADT *time1, TimeADT *time2)
|
||||
/* Constant to replace calls to date2j(2000,1,1) */
|
||||
#define JDATE_2000 2451545
|
||||
|
||||
/*
|
||||
* A modified version of time_in which allows the value 24:00:00 for
|
||||
* time and converts it to TimeADT data type forcing seconds to 0.
|
||||
* This can be Useful if you need to handle TimeADT values limited
|
||||
* to hh:mm like in timetables.
|
||||
*/
|
||||
|
||||
TimeADT *
|
||||
hhmm_in(char *str)
|
||||
{
|
||||
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
|
||||
TimeADT *time;
|
||||
|
||||
*result = *time1 - *time2;
|
||||
return (result);
|
||||
double fsec;
|
||||
struct tm tt, *tm = &tt;
|
||||
|
||||
int nf;
|
||||
char lowstr[MAXDATELEN+1];
|
||||
char *field[MAXDATEFIELDS];
|
||||
int dtype;
|
||||
int ftype[MAXDATEFIELDS];
|
||||
|
||||
if (!PointerIsValid(str))
|
||||
elog(WARN,"Bad (null) time external representation",NULL);
|
||||
|
||||
if ((ParseDateTime( str, lowstr, field, ftype, MAXDATEFIELDS, &nf) != 0)
|
||||
|| (DecodeTimeOnly( field, ftype, nf, &dtype, tm, &fsec) != 0))
|
||||
elog(WARN,"Bad time external representation '%s'",str);
|
||||
|
||||
if (tm->tm_hour<0 || tm->tm_hour>24 ||
|
||||
(tm->tm_hour==24 && (tm->tm_min!=0 || tm->tm_sec!=0 || fsec!= 0))) {
|
||||
elog(WARN,
|
||||
"time_in: hour must be limited to values 0 through 24:00 "
|
||||
"in \"%s\"",
|
||||
str);
|
||||
}
|
||||
if ((tm->tm_min < 0) || (tm->tm_min > 59))
|
||||
elog(WARN,"Minute must be limited to values 0 through 59 in '%s'",str);
|
||||
if ((tm->tm_sec < 0) || ((tm->tm_sec + fsec) >= 60))
|
||||
elog(WARN,"Second must be limited to values 0 through < 60 in '%s'",
|
||||
str);
|
||||
|
||||
time = PALLOCTYPE(TimeADT);
|
||||
|
||||
*time = ((((tm->tm_hour*60)+tm->tm_min)*60));
|
||||
|
||||
return(time);
|
||||
}
|
||||
|
||||
TimeADT *
|
||||
/*
|
||||
* A modified version of time_out which converts from TimeADT data type
|
||||
* omitting the seconds field when it is 0.
|
||||
* Useful if you need to handle TimeADT values limited to hh:mm.
|
||||
*/
|
||||
|
||||
char *
|
||||
hhmm_out(TimeADT *time)
|
||||
{
|
||||
char *result;
|
||||
struct tm tt, *tm = &tt;
|
||||
char buf[MAXDATELEN+1];
|
||||
|
||||
if (!PointerIsValid(time))
|
||||
return NULL;
|
||||
|
||||
tm->tm_hour = (*time / (60*60));
|
||||
tm->tm_min = (((int) (*time / 60)) % 60);
|
||||
tm->tm_sec = (((int) *time) % 60);
|
||||
|
||||
if (tm->tm_sec == 0) {
|
||||
sprintf(buf, "%02d:%02d", tm->tm_hour, tm->tm_min);
|
||||
} else {
|
||||
sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
|
||||
result = PALLOC(strlen(buf)+1);
|
||||
|
||||
strcpy( result, buf);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
TimeADT *
|
||||
hhmm(TimeADT *time)
|
||||
{
|
||||
TimeADT *result = PALLOCTYPE(TimeADT);
|
||||
|
||||
*result = (((int) *time) / 60 * 60);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
TimeADT *
|
||||
time_difference(TimeADT *time1, TimeADT *time2)
|
||||
{
|
||||
TimeADT *time = PALLOCTYPE(TimeADT);
|
||||
|
||||
*time = (*time1 - *time2);
|
||||
return(time);
|
||||
}
|
||||
|
||||
int4
|
||||
time_hours(TimeADT *time)
|
||||
{
|
||||
return (((int) *time) / 3600);
|
||||
}
|
||||
|
||||
int4
|
||||
time_minutes(TimeADT *time)
|
||||
{
|
||||
return ((((int) *time) / 60) % 60);
|
||||
}
|
||||
|
||||
int4
|
||||
time_seconds(TimeADT *time)
|
||||
{
|
||||
return (((int) *time) % 60);
|
||||
}
|
||||
|
||||
int4
|
||||
as_minutes(TimeADT *time)
|
||||
{
|
||||
return (((int) *time) / 60);
|
||||
}
|
||||
|
||||
int4
|
||||
as_seconds(TimeADT *time)
|
||||
{
|
||||
return ((int) *time);
|
||||
}
|
||||
|
||||
int4
|
||||
date_day(DateADT val)
|
||||
{
|
||||
int year, month, day;
|
||||
|
||||
j2date(val + JDATE_2000, &year, &month, &day);
|
||||
|
||||
return (day);
|
||||
}
|
||||
|
||||
int4
|
||||
date_month(DateADT val)
|
||||
{
|
||||
int year, month, day;
|
||||
|
||||
j2date(val + JDATE_2000, &year, &month, &day);
|
||||
|
||||
return (month);
|
||||
}
|
||||
|
||||
int4
|
||||
date_year(DateADT val)
|
||||
{
|
||||
int year, month, day;
|
||||
|
||||
j2date(val + JDATE_2000, &year, &month, &day);
|
||||
|
||||
return (year);
|
||||
}
|
||||
|
||||
TimeADT *
|
||||
currenttime()
|
||||
{
|
||||
time_t current_time;
|
||||
struct tm *tm;
|
||||
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
|
||||
TimeADT *result = PALLOCTYPE(TimeADT);
|
||||
struct tm *tm;
|
||||
time_t current_time;
|
||||
|
||||
current_time = time(NULL);
|
||||
tm = localtime(¤t_time);
|
||||
*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
|
||||
return (result);
|
||||
current_time = time(NULL);
|
||||
tm = localtime(¤t_time);
|
||||
*result = ((((tm->tm_hour*60)+tm->tm_min)*60)+tm->tm_sec);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
DateADT
|
||||
currentdate()
|
||||
{
|
||||
time_t current_time;
|
||||
struct tm *tm;
|
||||
DateADT result;
|
||||
DateADT date;
|
||||
struct tm tt, *tm = &tt;
|
||||
|
||||
current_time = time(NULL);
|
||||
tm = localtime(¤t_time);
|
||||
|
||||
result = date2j(tm->tm_year, tm->tm_mon + 1, tm->tm_mday) -
|
||||
date2j(100, 1, 1);
|
||||
return (result);
|
||||
GetCurrentTime(tm);
|
||||
date = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - JDATE_2000);
|
||||
return (date);
|
||||
}
|
||||
|
||||
int4
|
||||
hours(TimeADT *time)
|
||||
{
|
||||
return (*time / (60 * 60));
|
||||
}
|
||||
|
||||
int4
|
||||
minutes(TimeADT *time)
|
||||
{
|
||||
return (((int) (*time / 60)) % 60);
|
||||
}
|
||||
|
||||
int4
|
||||
seconds(TimeADT *time)
|
||||
{
|
||||
return (((int) *time) % 60);
|
||||
}
|
||||
|
||||
int4
|
||||
day(DateADT *date)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
j2date((*date + date2j(2000, 1, 1)),
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday);
|
||||
|
||||
return (tm.tm_mday);
|
||||
}
|
||||
|
||||
int4
|
||||
month(DateADT *date)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
j2date((*date + date2j(2000, 1, 1)),
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday);
|
||||
|
||||
return (tm.tm_mon);
|
||||
}
|
||||
|
||||
int4
|
||||
year(DateADT *date)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
j2date((*date + date2j(2000, 1, 1)),
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday);
|
||||
|
||||
return (tm.tm_year);
|
||||
}
|
||||
|
||||
int4
|
||||
asminutes(TimeADT *time)
|
||||
{
|
||||
int seconds = (int) *time;
|
||||
|
||||
return (seconds / 60);
|
||||
}
|
||||
|
||||
int4
|
||||
asseconds(TimeADT *time)
|
||||
{
|
||||
int seconds = (int) *time;
|
||||
|
||||
return (seconds);
|
||||
}
|
||||
/* end of file */
|
||||
|
|
|
@ -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
|
|
@ -14,17 +14,19 @@
|
|||
#include "utils/palloc.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
#include "string_io.h"
|
||||
|
||||
/* define this if you want to see iso-8859 characters */
|
||||
#define ISO8859
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define VALUE(char) ((char) - '0')
|
||||
#define DIGIT(val) ((val) + '0')
|
||||
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define VALUE(char) ((char) - '0')
|
||||
#define DIGIT(val) ((val) + '0')
|
||||
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
||||
#ifndef ISO8859
|
||||
#define NOTPRINTABLE(c) (!isprint(c))
|
||||
#define NOTPRINTABLE(c) (!isprint(c))
|
||||
#else
|
||||
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
|
||||
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -36,129 +38,115 @@
|
|||
* The function is used by output methods of various string types.
|
||||
*
|
||||
* Arguments:
|
||||
* data - input data (can be NULL)
|
||||
* size - optional size of data. A negative value indicates
|
||||
* that data is a null terminated string.
|
||||
* data - input data (can be NULL)
|
||||
* size - optional size of data. A negative value indicates
|
||||
* that data is a null terminated string.
|
||||
*
|
||||
* Returns:
|
||||
* a pointer to a new string containing the printable
|
||||
* representation of data.
|
||||
* a pointer to a new string containing the printable
|
||||
* representation of data.
|
||||
*/
|
||||
|
||||
char *
|
||||
char *
|
||||
string_output(char *data, int size)
|
||||
{
|
||||
register unsigned char c,
|
||||
*p,
|
||||
*r,
|
||||
*result;
|
||||
register int l,
|
||||
len;
|
||||
register unsigned char c, *p, *r, *result;
|
||||
register int l, len;
|
||||
|
||||
if (data == NULL)
|
||||
{
|
||||
result = (char *) palloc(2);
|
||||
result[0] = '-';
|
||||
result[1] = '\0';
|
||||
return (result);
|
||||
if (data == NULL) {
|
||||
result = (char *) palloc(2);
|
||||
result[0] = '-';
|
||||
result[1] = '\0';
|
||||
return (result);
|
||||
}
|
||||
|
||||
if (size < 0) {
|
||||
size = strlen(data);
|
||||
}
|
||||
|
||||
/* adjust string length for escapes */
|
||||
len = size;
|
||||
for (p=data,l=size; l>0; p++,l--) {
|
||||
switch (*p) {
|
||||
case '\\':
|
||||
case '"' :
|
||||
case '{':
|
||||
case '}':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
len++;
|
||||
break;
|
||||
default:
|
||||
if (NOTPRINTABLE(*p)) {
|
||||
len += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
len++;
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
size = strlen(data);
|
||||
result = (char *) palloc(len);
|
||||
|
||||
for (p=data,r=result,l=size; (l > 0) && (c = *p); p++,l--) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
case '"' :
|
||||
case '{':
|
||||
case '}':
|
||||
*r++ = '\\';
|
||||
*r++ = c;
|
||||
break;
|
||||
case '\b':
|
||||
*r++ = '\\';
|
||||
*r++ = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
*r++ = '\\';
|
||||
*r++ = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
*r++ = '\\';
|
||||
*r++ = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*r++ = '\\';
|
||||
*r++ = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
*r++ = '\\';
|
||||
*r++ = 't';
|
||||
break;
|
||||
case '\v':
|
||||
*r++ = '\\';
|
||||
*r++ = 'v';
|
||||
break;
|
||||
default:
|
||||
if (NOTPRINTABLE(c)) {
|
||||
*r = '\\';
|
||||
r += 3;
|
||||
*r-- = DIGIT(c & 07);
|
||||
c >>= 3;
|
||||
*r-- = DIGIT(c & 07);
|
||||
c >>= 3;
|
||||
*r = DIGIT(c & 03);
|
||||
r += 3;
|
||||
} else {
|
||||
*r++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
*r = '\0';
|
||||
|
||||
/* adjust string length for escapes */
|
||||
len = size;
|
||||
for (p = data, l = size; l > 0; p++, l--)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '\\':
|
||||
case '"':
|
||||
case '{':
|
||||
case '}':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
len++;
|
||||
break;
|
||||
default:
|
||||
if (NOTPRINTABLE(*p))
|
||||
{
|
||||
len += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
len++;
|
||||
|
||||
result = (char *) palloc(len);
|
||||
|
||||
for (p = data, r = result, l = size; (l > 0) && (c = *p); p++, l--)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\\':
|
||||
case '"':
|
||||
case '{':
|
||||
case '}':
|
||||
*r++ = '\\';
|
||||
*r++ = c;
|
||||
break;
|
||||
case '\b':
|
||||
*r++ = '\\';
|
||||
*r++ = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
*r++ = '\\';
|
||||
*r++ = 'f';
|
||||
break;
|
||||
case '\n':
|
||||
*r++ = '\\';
|
||||
*r++ = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*r++ = '\\';
|
||||
*r++ = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
*r++ = '\\';
|
||||
*r++ = 't';
|
||||
break;
|
||||
case '\v':
|
||||
*r++ = '\\';
|
||||
*r++ = 'v';
|
||||
break;
|
||||
default:
|
||||
if (NOTPRINTABLE(c))
|
||||
{
|
||||
*r = '\\';
|
||||
r += 3;
|
||||
*r-- = DIGIT(c & 07);
|
||||
c >>= 3;
|
||||
*r-- = DIGIT(c & 07);
|
||||
c >>= 3;
|
||||
*r = DIGIT(c & 03);
|
||||
r += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
*r = '\0';
|
||||
|
||||
return ((char *) result);
|
||||
return((char *) result);
|
||||
}
|
||||
|
||||
/*
|
||||
* string_input() --
|
||||
*
|
||||
* This function accepts a C string in input and copies it into a new
|
||||
* This function accepts a C string in input and copies it into a new
|
||||
* object allocated with palloc() translating all escape sequences.
|
||||
* An optional header can be allocatd before the string, for example
|
||||
* to hold the length of a varlena object.
|
||||
|
@ -167,231 +155,211 @@ string_output(char *data, int size)
|
|||
* receive strings in internal form.
|
||||
*
|
||||
* Arguments:
|
||||
* str - input string possibly with escapes
|
||||
* size - the required size of new data. A value of 0
|
||||
* indicates a variable size string, while a
|
||||
* negative value indicates a variable size string
|
||||
* of size not greater than this absolute value.
|
||||
* hdrsize - size of an optional header to be allocated before
|
||||
* the data. It must then be filled by the caller.
|
||||
* rtn_size - an optional pointer to an int variable where the
|
||||
* size of the new string is stored back.
|
||||
* str - input string possibly with escapes
|
||||
* size - the required size of new data. A value of 0
|
||||
* indicates a variable size string, while a
|
||||
* negative value indicates a variable size string
|
||||
* of size not greater than this absolute value.
|
||||
* hdrsize - size of an optional header to be allocated before
|
||||
* the data. It must then be filled by the caller.
|
||||
* rtn_size - an optional pointer to an int variable where the
|
||||
* size of the new string is stored back.
|
||||
*
|
||||
* Returns:
|
||||
* a pointer to the new string or the header.
|
||||
* a pointer to the new string or the header.
|
||||
*/
|
||||
|
||||
char *
|
||||
char *
|
||||
string_input(char *str, int size, int hdrsize, int *rtn_size)
|
||||
{
|
||||
register unsigned char *p,
|
||||
*r;
|
||||
unsigned char *result;
|
||||
int len;
|
||||
register unsigned char *p, *r;
|
||||
unsigned char *result;
|
||||
int len;
|
||||
|
||||
if ((str == NULL) || (hdrsize < 0))
|
||||
{
|
||||
return (char *) NULL;
|
||||
}
|
||||
if ((str == NULL) || (hdrsize < 0)) {
|
||||
return (char *) NULL;
|
||||
}
|
||||
|
||||
/* Compute result size */
|
||||
len = strlen(str);
|
||||
for (p = str; *p;)
|
||||
{
|
||||
if (*p++ == '\\')
|
||||
{
|
||||
if (ISOCTAL(*p))
|
||||
{
|
||||
if (ISOCTAL(*(p + 1)))
|
||||
{
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
if (ISOCTAL(*(p + 1)))
|
||||
{
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if (*p)
|
||||
p++;
|
||||
len--;
|
||||
/* Compute result size */
|
||||
len = strlen(str);
|
||||
for (p=str; *p; ) {
|
||||
if (*p++ == '\\') {
|
||||
if (ISOCTAL(*p)) {
|
||||
if (ISOCTAL(*(p+1))) {
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
/* result has variable length */
|
||||
if (size == 0)
|
||||
{
|
||||
size = len + 1;
|
||||
}
|
||||
else
|
||||
/* result has variable length with maximum size */
|
||||
if (size < 0)
|
||||
{
|
||||
size = MIN(len, -size) + 1;
|
||||
}
|
||||
|
||||
result = (char *) palloc(hdrsize + size);
|
||||
memset(result, 0, hdrsize + size);
|
||||
if (rtn_size)
|
||||
{
|
||||
*rtn_size = size;
|
||||
}
|
||||
|
||||
r = result + hdrsize;
|
||||
for (p = str; *p;)
|
||||
{
|
||||
register unsigned char c;
|
||||
|
||||
if ((c = *p++) == '\\')
|
||||
{
|
||||
switch (c = *p++)
|
||||
{
|
||||
case '\0':
|
||||
p--;
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
c = VALUE(c);
|
||||
if (isdigit(*p))
|
||||
{
|
||||
c = (c << 3) + VALUE(*p++);
|
||||
}
|
||||
if (isdigit(*p))
|
||||
{
|
||||
c = (c << 3) + VALUE(*p++);
|
||||
}
|
||||
*r++ = c;
|
||||
break;
|
||||
case 'b':
|
||||
*r++ = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
*r++ = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
*r++ = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*r++ = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*r++ = '\t';
|
||||
break;
|
||||
case 'v':
|
||||
*r++ = '\v';
|
||||
break;
|
||||
default:
|
||||
*r++ = c;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*r++ = c;
|
||||
if (ISOCTAL(*(p+1))) {
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if (*p) p++;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
return ((char *) result);
|
||||
/* result has variable length */
|
||||
if (size == 0) {
|
||||
size = len+1;
|
||||
} else
|
||||
|
||||
/* result has variable length with maximum size */
|
||||
if (size < 0) {
|
||||
size = MIN(len, - size)+1;
|
||||
}
|
||||
|
||||
result = (char *) palloc(hdrsize+size);
|
||||
memset(result, 0, hdrsize+size);
|
||||
if (rtn_size) {
|
||||
*rtn_size = size;
|
||||
}
|
||||
|
||||
r = result + hdrsize;
|
||||
for (p=str; *p; ) {
|
||||
register unsigned char c;
|
||||
if ((c = *p++) == '\\') {
|
||||
switch (c = *p++) {
|
||||
case '\0':
|
||||
p--;
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
c = VALUE(c);
|
||||
if (isdigit(*p)) {
|
||||
c = (c<<3) + VALUE(*p++);
|
||||
}
|
||||
if (isdigit(*p)) {
|
||||
c = (c<<3) + VALUE(*p++);
|
||||
}
|
||||
*r++ = c;
|
||||
break;
|
||||
case 'b':
|
||||
*r++ = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
*r++ = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
*r++ = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*r++ = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*r++ = '\t';
|
||||
break;
|
||||
case 'v':
|
||||
*r++ = '\v';
|
||||
break;
|
||||
default:
|
||||
*r++ = c;
|
||||
}
|
||||
} else {
|
||||
*r++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
return((char *) result);
|
||||
}
|
||||
|
||||
char *
|
||||
char *
|
||||
c_charout(int32 c)
|
||||
{
|
||||
char str[2];
|
||||
char str[2];
|
||||
|
||||
str[0] = (char) c;
|
||||
str[1] = '\0';
|
||||
str[0] = (char) c;
|
||||
str[1] = '\0';
|
||||
|
||||
return (string_output(str, 1));
|
||||
return (string_output(str, 1));
|
||||
}
|
||||
|
||||
char *
|
||||
char *
|
||||
c_char2out(uint16 s)
|
||||
{
|
||||
return (string_output((char *) &s, 2));
|
||||
return (string_output((char *) &s, 2));
|
||||
}
|
||||
|
||||
char *
|
||||
char *
|
||||
c_char4out(uint32 s)
|
||||
{
|
||||
return (string_output((char *) &s, 4));
|
||||
return (string_output((char *) &s, 4));
|
||||
}
|
||||
|
||||
char *
|
||||
char *
|
||||
c_char8out(char *s)
|
||||
{
|
||||
return (string_output(s, 8));
|
||||
return (string_output(s, 8));
|
||||
}
|
||||
|
||||
char *
|
||||
char *
|
||||
c_char16out(char *s)
|
||||
{
|
||||
return (string_output(s, 16));
|
||||
return (string_output(s, 16));
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be used for text, bytea, SET and unknown data types
|
||||
*/
|
||||
|
||||
char *
|
||||
c_textout(struct varlena * vlena)
|
||||
char *
|
||||
c_textout(struct varlena *vlena)
|
||||
{
|
||||
int len = 0;
|
||||
char *s = NULL;
|
||||
int len = 0;
|
||||
char *s = NULL;
|
||||
|
||||
if (vlena)
|
||||
{
|
||||
len = VARSIZE(vlena) - VARHDRSZ;
|
||||
s = VARDATA(vlena);
|
||||
}
|
||||
return (string_output(s, len));
|
||||
if (vlena) {
|
||||
len = VARSIZE(vlena) - VARHDRSZ;
|
||||
s = VARDATA(vlena);
|
||||
}
|
||||
return (string_output(s, len));
|
||||
}
|
||||
|
||||
/*
|
||||
* This can be used for varchar and bpchar strings
|
||||
*/
|
||||
|
||||
char *
|
||||
char *
|
||||
c_varcharout(char *s)
|
||||
{
|
||||
int len;
|
||||
int len = 0;
|
||||
|
||||
if (s)
|
||||
{
|
||||
len = *(int32 *) s - 4;
|
||||
s += 4;
|
||||
}
|
||||
return (string_output(s, len));
|
||||
if (s) {
|
||||
len = *(int32*)s - 4;
|
||||
s += 4;
|
||||
}
|
||||
return (string_output(s, len));
|
||||
}
|
||||
|
||||
#ifdef 0
|
||||
#if 0
|
||||
struct varlena *
|
||||
c_textin(char *str)
|
||||
{
|
||||
struct varlena *result;
|
||||
int len;
|
||||
struct varlena *result;
|
||||
int len;
|
||||
|
||||
if (str == NULL)
|
||||
{
|
||||
return ((struct varlena *) NULL);
|
||||
}
|
||||
if (str == NULL) {
|
||||
return ((struct varlena *) NULL);
|
||||
}
|
||||
|
||||
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
|
||||
VARSIZE(result) = len;
|
||||
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
|
||||
VARSIZE(result) = len;
|
||||
|
||||
return (result);
|
||||
return (result);
|
||||
}
|
||||
|
||||
char *
|
||||
char *
|
||||
c_char16in(char *str)
|
||||
{
|
||||
return (string_input(str, 16, 0, NULL));
|
||||
return (string_input(str, 16, 0, NULL));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* end of file */
|
||||
|
|
|
@ -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