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 --
|
* 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
|
* array and a scalar value, iterate a scalar operator over the
|
||||||
* elements of the array and the value and compute a result as
|
* elements of the array and the value and compute a result as
|
||||||
* the logical OR or AND of the results.
|
* 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:
|
|
||||||
*
|
*
|
||||||
* array_int4eq({1,2,3}, 1) --> true
|
* Copyright (c) 1997, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||||
* 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>
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
@ -33,242 +15,297 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
#include "pg_type.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "syscache.h"
|
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
|
#include "backend/fmgr.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "utils/array.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/elog.h"
|
#include "utils/memutils.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
#include "array_iterator.h"
|
||||||
|
|
||||||
static int32
|
static int32
|
||||||
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
|
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
|
||||||
{
|
{
|
||||||
HeapTuple typ_tuple;
|
HeapTuple typ_tuple;
|
||||||
TypeTupleForm typ_struct;
|
TypeTupleForm typ_struct;
|
||||||
bool typbyval;
|
bool typbyval;
|
||||||
int typlen;
|
int typlen;
|
||||||
func_ptr proc_fn;
|
func_ptr proc_fn;
|
||||||
int pronargs;
|
int pronargs;
|
||||||
int nitems,
|
int nitems, i, result;
|
||||||
i,
|
int ndim, *dim;
|
||||||
result;
|
char *p;
|
||||||
int ndim,
|
|
||||||
*dim;
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
/* Sanity checks */
|
/* Sanity checks */
|
||||||
if ((array == (ArrayType *) NULL)
|
if ((array == (ArrayType *) NULL)
|
||||||
|| (ARR_IS_LO(array) == true))
|
|| (ARR_IS_LO(array) == true)) {
|
||||||
{
|
/* elog(NOTICE, "array_iterator: array is null"); */
|
||||||
/* elog(NOTICE, "array_iterator: array is null"); */
|
return (0);
|
||||||
return (0);
|
}
|
||||||
}
|
ndim = ARR_NDIM(array);
|
||||||
ndim = ARR_NDIM(array);
|
dim = ARR_DIMS(array);
|
||||||
dim = ARR_DIMS(array);
|
nitems = getNitems(ndim, dim);
|
||||||
nitems = getNitems(ndim, dim);
|
if (nitems == 0) {
|
||||||
if (nitems == 0)
|
/* elog(NOTICE, "array_iterator: nitems = 0"); */
|
||||||
{
|
return (0);
|
||||||
/* elog(NOTICE, "array_iterator: nitems = 0"); */
|
}
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lookup element type information */
|
/* Lookup element type information */
|
||||||
typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype), 0, 0, 0);
|
typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype),0,0,0);
|
||||||
if (!HeapTupleIsValid(typ_tuple))
|
if (!HeapTupleIsValid(typ_tuple)) {
|
||||||
{
|
elog(WARN,"array_iterator: cache lookup failed for type %d", elemtype);
|
||||||
elog(WARN, "array_iterator: cache lookup failed for type %d", elemtype);
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
|
||||||
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
|
typlen = typ_struct->typlen;
|
||||||
typlen = typ_struct->typlen;
|
typbyval = typ_struct->typbyval;
|
||||||
typbyval = typ_struct->typbyval;
|
|
||||||
|
|
||||||
/* Lookup the function entry point */
|
/* Lookup the function entry point */
|
||||||
proc_fn == (func_ptr) NULL;
|
proc_fn = (func_ptr) NULL;
|
||||||
fmgr_info(proc, &proc_fn, &pronargs);
|
fmgr_info(proc, &proc_fn, &pronargs);
|
||||||
if ((proc_fn == NULL) || (pronargs != 2))
|
if ((proc_fn == NULL) || (pronargs != 2)) {
|
||||||
{
|
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
|
||||||
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
|
return (0);
|
||||||
return (0);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the array and apply the operator to each element */
|
/* Scan the array and apply the operator to each element */
|
||||||
result = 0;
|
result = 0;
|
||||||
p = ARR_DATA_PTR(array);
|
p = ARR_DATA_PTR(array);
|
||||||
for (i = 0; i < nitems; i++)
|
for (i = 0; i < nitems; i++) {
|
||||||
{
|
if (typbyval) {
|
||||||
if (typbyval)
|
switch(typlen) {
|
||||||
{
|
case 1:
|
||||||
switch (typlen)
|
result = (int) (*proc_fn)(*p, value);
|
||||||
{
|
break;
|
||||||
case 1:
|
case 2:
|
||||||
result = (int) (*proc_fn) (*p, value);
|
result = (int) (*proc_fn)(* (int16 *) p, value);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 3:
|
||||||
result = (int) (*proc_fn) (*(int16 *) p, value);
|
case 4:
|
||||||
break;
|
result = (int) (*proc_fn)(* (int32 *) p, value);
|
||||||
case 3:
|
break;
|
||||||
case 4:
|
}
|
||||||
result = (int) (*proc_fn) (*(int32 *) p, value);
|
p += typlen;
|
||||||
break;
|
} else {
|
||||||
}
|
result = (int) (*proc_fn)(p, value);
|
||||||
p += typlen;
|
if (typlen > 0) {
|
||||||
}
|
p += typlen;
|
||||||
else
|
} else {
|
||||||
{
|
p += INTALIGN(* (int32 *) p);
|
||||||
result = (int) (*proc_fn) (p, value);
|
}
|
||||||
if (typlen > 0)
|
}
|
||||||
{
|
if (result) {
|
||||||
p += typlen;
|
if (!and) {
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
p += INTALIGN(*(int32 *) p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
if (!and)
|
|
||||||
{
|
|
||||||
return (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (and)
|
|
||||||
{
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (and && result)
|
|
||||||
{
|
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
{
|
if (and) {
|
||||||
return (0);
|
return (0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (and && result) {
|
||||||
|
return (1);
|
||||||
|
} else {
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterators for type _text
|
* Iterator functions for type _text
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_texteq(ArrayType *array, char *value)
|
array_texteq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 25, /* text */
|
return array_iterator((Oid) 25, /* text */
|
||||||
(Oid) 67, /* texteq */
|
(Oid) 67, /* texteq */
|
||||||
0, /* logical or */
|
0, /* logical or */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_all_texteq(ArrayType *array, char *value)
|
array_all_texteq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 25, /* text */
|
return array_iterator((Oid) 25, /* text */
|
||||||
(Oid) 67, /* texteq */
|
(Oid) 67, /* texteq */
|
||||||
1, /* logical and */
|
1, /* logical and */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_textregexeq(ArrayType *array, char *value)
|
array_textregexeq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 25, /* text */
|
return array_iterator((Oid) 25, /* text */
|
||||||
(Oid) 81, /* textregexeq */
|
(Oid) 1254, /* textregexeq */
|
||||||
0, /* logical or */
|
0, /* logical or */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_all_textregexeq(ArrayType *array, char *value)
|
array_all_textregexeq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 25, /* text */
|
return array_iterator((Oid) 25, /* text */
|
||||||
(Oid) 81, /* textregexeq */
|
(Oid) 1254, /* textregexeq */
|
||||||
1, /* logical and */
|
1, /* logical and */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterators for type _char16. Note that the regexp operators
|
* Iterator functions for type _char16. Note that the regexp
|
||||||
* take the second argument of type text.
|
* operators take the second argument of type text.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_char16eq(ArrayType *array, char *value)
|
array_char16eq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 20, /* char16 */
|
return array_iterator((Oid) 20, /* char16 */
|
||||||
(Oid) 490, /* char16eq */
|
(Oid) 1275, /* char16eq */
|
||||||
0, /* logical or */
|
0, /* logical or */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_all_char16eq(ArrayType *array, char *value)
|
array_all_char16eq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 20, /* char16 */
|
return array_iterator((Oid) 20, /* char16 */
|
||||||
(Oid) 490, /* char16eq */
|
(Oid) 1275, /* char16eq */
|
||||||
1, /* logical and */
|
1, /* logical and */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_char16regexeq(ArrayType *array, char *value)
|
array_char16regexeq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 20, /* char16 */
|
return array_iterator((Oid) 20, /* char16 */
|
||||||
(Oid) 700, /* char16regexeq */
|
(Oid) 1288, /* char16regexeq */
|
||||||
0, /* logical or */
|
0, /* logical or */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_all_char16regexeq(ArrayType *array, char *value)
|
array_all_char16regexeq(ArrayType *array, char* value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 20, /* char16 */
|
return array_iterator((Oid) 20, /* char16 */
|
||||||
(Oid) 700, /* char16regexeq */
|
(Oid) 1288, /* char16regexeq */
|
||||||
1, /* logical and */
|
1, /* logical and */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterators for type _int4
|
* Iterator functions for type _int4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_int4eq(ArrayType *array, int4 value)
|
array_int4eq(ArrayType *array, int4 value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 23, /* int4 */
|
return array_iterator((Oid) 23, /* int4 */
|
||||||
(Oid) 65, /* int4eq */
|
(Oid) 65, /* int4eq */
|
||||||
0, /* logical or */
|
0, /* logical or */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_all_int4eq(ArrayType *array, int4 value)
|
array_all_int4eq(ArrayType *array, int4 value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 23, /* int4 */
|
return array_iterator((Oid) 23, /* int4 */
|
||||||
(Oid) 65, /* int4eq */
|
(Oid) 65, /* int4eq */
|
||||||
1, /* logical and */
|
1, /* logical and */
|
||||||
array, (Datum) value);
|
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
|
int32
|
||||||
array_int4gt(ArrayType *array, int4 value)
|
array_int4gt(ArrayType *array, int4 value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 23, /* int4 */
|
return array_iterator((Oid) 23, /* int4 */
|
||||||
(Oid) 147, /* int4gt */
|
(Oid) 147, /* int4gt */
|
||||||
0, /* logical or */
|
0, /* logical or */
|
||||||
array, (Datum) value);
|
array, (Datum)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
int32
|
||||||
array_all_int4gt(ArrayType *array, int4 value)
|
array_all_int4gt(ArrayType *array, int4 value)
|
||||||
{
|
{
|
||||||
return array_iterator((Oid) 23, /* int4 */
|
return array_iterator((Oid) 23, /* int4 */
|
||||||
(Oid) 147, /* int4gt */
|
(Oid) 147, /* int4gt */
|
||||||
1, /* logical and */
|
1, /* logical and */
|
||||||
array, (Datum) value);
|
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>
|
Array iterator functions, by Massimo Dal Zotto <dz@cs.unitn.it>
|
||||||
Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST)
|
|
||||||
Subject: [PG95]: new operators for arrays
|
|
||||||
|
|
||||||
- -----BEGIN PGP SIGNED MESSAGE-----
|
This loadable module defines a new class of functions which take
|
||||||
|
an array and a scalar value, iterate a scalar operator over the
|
||||||
|
elements of the array and the value, and compute a result as
|
||||||
|
the logical OR or AND of the iteration results.
|
||||||
|
For example array_int4eq returns true if some of the elements
|
||||||
|
of an array of int4 is equal to the given value:
|
||||||
|
|
||||||
Hi,
|
array_int4eq({1,2,3}, 1) --> true
|
||||||
|
array_int4eq({1,2,3}, 4) --> false
|
||||||
|
|
||||||
I have written an extension to Postgres95 which allows to use qualification
|
If we have defined T array types and O scalar operators we can
|
||||||
clauses based on the values of single elements of arrays.
|
define T x O x 2 array functions, each of them has a name like
|
||||||
For example I can now select rows having some or all element of an array
|
"array_[all_]<basetype><operation>" and takes an array of type T
|
||||||
|
iterating the operator O over all the elements. Note however
|
||||||
|
that some of the possible combination are invalid, for example
|
||||||
|
the array_int4_like because there is no like operator for int4.
|
||||||
|
|
||||||
|
We can then define new operators based on these functions and use
|
||||||
|
them to write queries with qualification clauses based on the
|
||||||
|
values of some of the elements of an array.
|
||||||
|
For example to select rows having some or all element of an array
|
||||||
attribute equal to a given value or matching a regular expression:
|
attribute equal to a given value or matching a regular expression:
|
||||||
|
|
||||||
select * from t where t.foo *= 'bar';
|
create table t(id int4[], txt text[]);
|
||||||
select * from t where t.foo **~ '^ba[rz]';
|
|
||||||
|
|
||||||
The scheme is quite general, each operator which operates on a base type can
|
-- select tuples with some id element equal to 123
|
||||||
be iterated over the elements of an array. It seem to work well but defining
|
select * from t where t.id *= 123;
|
||||||
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 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
|
include $(SRCDIR)/Makefile.global
|
||||||
ld -Bshareable -o $P datetime_functions.o
|
|
||||||
|
|
||||||
datetime_functions.sql: datetime.prot
|
INCLUDE_OPT = -I ./ \
|
||||||
sh datetime.prot $P
|
-I $(SRCDIR)/ \
|
||||||
psql -c "\idatetime_functions.sql" template1
|
-I $(SRCDIR)/include \
|
||||||
|
-I $(SRCDIR)/port/$(PORTNAME)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE_OPT)
|
||||||
|
|
||||||
|
ifeq ($(PORTNAME), linux)
|
||||||
|
ifdef LINUX_ELF
|
||||||
|
ifeq ($(CC), gcc)
|
||||||
|
CFLAGS += -fPIC
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(PORTNAME), i386_solaris)
|
||||||
|
CFLAGS+= -fPIC
|
||||||
|
endif
|
||||||
|
|
||||||
|
MODNAME = datetime_functions
|
||||||
|
|
||||||
|
MODULE = $(MODNAME)$(DLSUFFIX)
|
||||||
|
|
||||||
|
all: module sql
|
||||||
|
|
||||||
|
module: $(MODULE)
|
||||||
|
|
||||||
|
sql: $(MODNAME).sql
|
||||||
|
|
||||||
|
install: $(MODULE)
|
||||||
|
cp -p $(MODULE) $(LIBDIR)
|
||||||
|
cd $(LIBDIR); strip $(MODULE)
|
||||||
|
|
||||||
|
%.sql: %.sql.in
|
||||||
|
sed "s|MODULE_PATHNAME|$(LIBDIR)/$(MODULE)|" < $< > $@
|
||||||
|
|
||||||
|
.SUFFIXES: $(DLSUFFIX)
|
||||||
|
|
||||||
|
%$(DLSUFFIX): %.c
|
||||||
|
cc $(CFLAGS) -shared -o $@ $<
|
||||||
|
|
||||||
|
depend dep:
|
||||||
|
$(CC) -MM $(INCLUDE_OPT) *.c >depend
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(MODULE) $(MODNAME).sql
|
||||||
|
|
||||||
|
ifeq (depend,$(wildcard depend))
|
||||||
|
include depend
|
||||||
|
endif
|
||||||
|
|
|
@ -6,113 +6,208 @@
|
||||||
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
|
* 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 "postgres.h"
|
||||||
#include "utils/palloc.h"
|
#include "miscadmin.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "utils/nabstime.h"
|
||||||
#include "utils/datetime.h"
|
#include "utils/datetime.h"
|
||||||
|
#include "access/xact.h"
|
||||||
|
|
||||||
|
#include "datetime_functions.h"
|
||||||
|
|
||||||
TimeADT *
|
/* Constant to replace calls to date2j(2000,1,1) */
|
||||||
time_difference(TimeADT *time1, TimeADT *time2)
|
#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;
|
double fsec;
|
||||||
return (result);
|
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()
|
currenttime()
|
||||||
{
|
{
|
||||||
time_t current_time;
|
TimeADT *result = PALLOCTYPE(TimeADT);
|
||||||
struct tm *tm;
|
struct tm *tm;
|
||||||
TimeADT *result = (TimeADT *) palloc(sizeof(TimeADT));
|
time_t current_time;
|
||||||
|
|
||||||
current_time = time(NULL);
|
current_time = time(NULL);
|
||||||
tm = localtime(¤t_time);
|
tm = localtime(¤t_time);
|
||||||
*result = ((((tm->tm_hour * 60) + tm->tm_min) * 60) + tm->tm_sec);
|
*result = ((((tm->tm_hour*60)+tm->tm_min)*60)+tm->tm_sec);
|
||||||
return (result);
|
|
||||||
|
return (result);
|
||||||
}
|
}
|
||||||
|
|
||||||
DateADT
|
DateADT
|
||||||
currentdate()
|
currentdate()
|
||||||
{
|
{
|
||||||
time_t current_time;
|
DateADT date;
|
||||||
struct tm *tm;
|
struct tm tt, *tm = &tt;
|
||||||
DateADT result;
|
|
||||||
|
|
||||||
current_time = time(NULL);
|
GetCurrentTime(tm);
|
||||||
tm = localtime(¤t_time);
|
date = (date2j( tm->tm_year, tm->tm_mon, tm->tm_mday) - JDATE_2000);
|
||||||
|
return (date);
|
||||||
result = date2j(tm->tm_year, tm->tm_mon + 1, tm->tm_mday) -
|
|
||||||
date2j(100, 1, 1);
|
|
||||||
return (result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int4
|
/* end of file */
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -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/palloc.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
|
|
||||||
|
#include "string_io.h"
|
||||||
|
|
||||||
/* define this if you want to see iso-8859 characters */
|
/* define this if you want to see iso-8859 characters */
|
||||||
#define ISO8859
|
#define ISO8859
|
||||||
|
|
||||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||||
#define VALUE(char) ((char) - '0')
|
#define VALUE(char) ((char) - '0')
|
||||||
#define DIGIT(val) ((val) + '0')
|
#define DIGIT(val) ((val) + '0')
|
||||||
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
|
||||||
#ifndef ISO8859
|
#ifndef ISO8859
|
||||||
#define NOTPRINTABLE(c) (!isprint(c))
|
#define NOTPRINTABLE(c) (!isprint(c))
|
||||||
#else
|
#else
|
||||||
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
|
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -36,129 +38,115 @@
|
||||||
* The function is used by output methods of various string types.
|
* The function is used by output methods of various string types.
|
||||||
*
|
*
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* data - input data (can be NULL)
|
* data - input data (can be NULL)
|
||||||
* size - optional size of data. A negative value indicates
|
* size - optional size of data. A negative value indicates
|
||||||
* that data is a null terminated string.
|
* that data is a null terminated string.
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* a pointer to a new string containing the printable
|
* a pointer to a new string containing the printable
|
||||||
* representation of data.
|
* representation of data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char *
|
char *
|
||||||
string_output(char *data, int size)
|
string_output(char *data, int size)
|
||||||
{
|
{
|
||||||
register unsigned char c,
|
register unsigned char c, *p, *r, *result;
|
||||||
*p,
|
register int l, len;
|
||||||
*r,
|
|
||||||
*result;
|
|
||||||
register int l,
|
|
||||||
len;
|
|
||||||
|
|
||||||
if (data == NULL)
|
if (data == NULL) {
|
||||||
{
|
result = (char *) palloc(2);
|
||||||
result = (char *) palloc(2);
|
result[0] = '-';
|
||||||
result[0] = '-';
|
result[1] = '\0';
|
||||||
result[1] = '\0';
|
return (result);
|
||||||
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)
|
result = (char *) palloc(len);
|
||||||
{
|
|
||||||
size = strlen(data);
|
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 */
|
return((char *) result);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* string_input() --
|
* 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.
|
* object allocated with palloc() translating all escape sequences.
|
||||||
* An optional header can be allocatd before the string, for example
|
* An optional header can be allocatd before the string, for example
|
||||||
* to hold the length of a varlena object.
|
* to hold the length of a varlena object.
|
||||||
|
@ -167,231 +155,211 @@ string_output(char *data, int size)
|
||||||
* receive strings in internal form.
|
* receive strings in internal form.
|
||||||
*
|
*
|
||||||
* Arguments:
|
* Arguments:
|
||||||
* str - input string possibly with escapes
|
* str - input string possibly with escapes
|
||||||
* size - the required size of new data. A value of 0
|
* size - the required size of new data. A value of 0
|
||||||
* indicates a variable size string, while a
|
* indicates a variable size string, while a
|
||||||
* negative value indicates a variable size string
|
* negative value indicates a variable size string
|
||||||
* of size not greater than this absolute value.
|
* of size not greater than this absolute value.
|
||||||
* hdrsize - size of an optional header to be allocated before
|
* hdrsize - size of an optional header to be allocated before
|
||||||
* the data. It must then be filled by the caller.
|
* the data. It must then be filled by the caller.
|
||||||
* rtn_size - an optional pointer to an int variable where the
|
* rtn_size - an optional pointer to an int variable where the
|
||||||
* size of the new string is stored back.
|
* size of the new string is stored back.
|
||||||
*
|
*
|
||||||
* Returns:
|
* 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)
|
string_input(char *str, int size, int hdrsize, int *rtn_size)
|
||||||
{
|
{
|
||||||
register unsigned char *p,
|
register unsigned char *p, *r;
|
||||||
*r;
|
unsigned char *result;
|
||||||
unsigned char *result;
|
int len;
|
||||||
int len;
|
|
||||||
|
|
||||||
if ((str == NULL) || (hdrsize < 0))
|
if ((str == NULL) || (hdrsize < 0)) {
|
||||||
{
|
return (char *) NULL;
|
||||||
return (char *) NULL;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute result size */
|
/* Compute result size */
|
||||||
len = strlen(str);
|
len = strlen(str);
|
||||||
for (p = str; *p;)
|
for (p=str; *p; ) {
|
||||||
{
|
if (*p++ == '\\') {
|
||||||
if (*p++ == '\\')
|
if (ISOCTAL(*p)) {
|
||||||
{
|
if (ISOCTAL(*(p+1))) {
|
||||||
if (ISOCTAL(*p))
|
p++;
|
||||||
{
|
len--;
|
||||||
if (ISOCTAL(*(p + 1)))
|
|
||||||
{
|
|
||||||
p++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
if (ISOCTAL(*(p + 1)))
|
|
||||||
{
|
|
||||||
p++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*p)
|
|
||||||
p++;
|
|
||||||
len--;
|
|
||||||
}
|
}
|
||||||
}
|
if (ISOCTAL(*(p+1))) {
|
||||||
|
p++;
|
||||||
/* result has variable length */
|
len--;
|
||||||
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 (*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)
|
c_charout(int32 c)
|
||||||
{
|
{
|
||||||
char str[2];
|
char str[2];
|
||||||
|
|
||||||
str[0] = (char) c;
|
str[0] = (char) c;
|
||||||
str[1] = '\0';
|
str[1] = '\0';
|
||||||
|
|
||||||
return (string_output(str, 1));
|
return (string_output(str, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_char2out(uint16 s)
|
c_char2out(uint16 s)
|
||||||
{
|
{
|
||||||
return (string_output((char *) &s, 2));
|
return (string_output((char *) &s, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_char4out(uint32 s)
|
c_char4out(uint32 s)
|
||||||
{
|
{
|
||||||
return (string_output((char *) &s, 4));
|
return (string_output((char *) &s, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_char8out(char *s)
|
c_char8out(char *s)
|
||||||
{
|
{
|
||||||
return (string_output(s, 8));
|
return (string_output(s, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_char16out(char *s)
|
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
|
* This can be used for text, bytea, SET and unknown data types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_textout(struct varlena * vlena)
|
c_textout(struct varlena *vlena)
|
||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0;
|
||||||
char *s = NULL;
|
char *s = NULL;
|
||||||
|
|
||||||
if (vlena)
|
if (vlena) {
|
||||||
{
|
len = VARSIZE(vlena) - VARHDRSZ;
|
||||||
len = VARSIZE(vlena) - VARHDRSZ;
|
s = VARDATA(vlena);
|
||||||
s = VARDATA(vlena);
|
}
|
||||||
}
|
return (string_output(s, len));
|
||||||
return (string_output(s, len));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This can be used for varchar and bpchar strings
|
* This can be used for varchar and bpchar strings
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_varcharout(char *s)
|
c_varcharout(char *s)
|
||||||
{
|
{
|
||||||
int len;
|
int len = 0;
|
||||||
|
|
||||||
if (s)
|
if (s) {
|
||||||
{
|
len = *(int32*)s - 4;
|
||||||
len = *(int32 *) s - 4;
|
s += 4;
|
||||||
s += 4;
|
}
|
||||||
}
|
return (string_output(s, len));
|
||||||
return (string_output(s, len));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef 0
|
#if 0
|
||||||
struct varlena *
|
struct varlena *
|
||||||
c_textin(char *str)
|
c_textin(char *str)
|
||||||
{
|
{
|
||||||
struct varlena *result;
|
struct varlena *result;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
if (str == NULL)
|
if (str == NULL) {
|
||||||
{
|
return ((struct varlena *) NULL);
|
||||||
return ((struct varlena *) NULL);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
|
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
|
||||||
VARSIZE(result) = len;
|
VARSIZE(result) = len;
|
||||||
|
|
||||||
return (result);
|
return (result);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
c_char16in(char *str)
|
c_char16in(char *str)
|
||||||
{
|
{
|
||||||
return (string_input(str, 16, 0, NULL));
|
return (string_input(str, 16, 0, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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