1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-27 12:41:57 +03:00

Support Docs & Contrib

This commit is contained in:
Marc G. Fournier
1996-08-18 22:14:33 +00:00
parent 1960a3b965
commit 9848d3655d
102 changed files with 20187 additions and 0 deletions

View File

@ -0,0 +1,251 @@
/*
* array_iterator.c --
*
* This file defines a new group of operators which take an
* array and a scalar value, iterate a scalar operator over the
* elements of the array and the value and compute a result as
* the logical OR or AND of the results.
* For example array_int4eq returns true if some of the elements
* of an array of int4 is equal to the given value:
*
* array_int4eq({1,2,3}, 1) --> true
* array_int4eq({1,2,3}, 4) --> false
*
* If we have defined T array types and O scalar operators
* we can define T x O array operators, each of them has a name
* like "array_<basetype><operation>" and takes an array of type T
* iterating the operator O over all the elements. Note however
* that some of the possible combination are invalid, for example
* the array_int4_like because there is no like operator for int4.
* It is now possible to write queries which look inside the arrays:
*
* create table t(id int4[], txt text[]);
* select * from t where t.id *= 123;
* select * from t where t.txt *~ '[a-z]';
* select * from t where t.txt[1:3] **~ '[a-z]';
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <ctype.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include "postgres.h"
#include "pg_type.h"
#include "miscadmin.h"
#include "syscache.h"
#include "access/xact.h"
#include "utils/builtins.h"
#include "utils/elog.h"
static int32
array_iterator(Oid elemtype, Oid proc, int and, ArrayType *array, Datum value)
{
HeapTuple typ_tuple;
TypeTupleForm typ_struct;
bool typbyval;
int typlen;
func_ptr proc_fn;
int pronargs;
int nitems, i, result;
int ndim, *dim;
char *p;
/* Sanity checks */
if ((array == (ArrayType *) NULL)
|| (ARR_IS_LO(array) == true)) {
/* elog(NOTICE, "array_iterator: array is null"); */
return (0);
}
ndim = ARR_NDIM(array);
dim = ARR_DIMS(array);
nitems = getNitems(ndim, dim);
if (nitems == 0) {
/* elog(NOTICE, "array_iterator: nitems = 0"); */
return (0);
}
/* Lookup element type information */
typ_tuple = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(elemtype),0,0,0);
if (!HeapTupleIsValid(typ_tuple)) {
elog(WARN,"array_iterator: cache lookup failed for type %d", elemtype);
return 0;
}
typ_struct = (TypeTupleForm) GETSTRUCT(typ_tuple);
typlen = typ_struct->typlen;
typbyval = typ_struct->typbyval;
/* Lookup the function entry point */
proc_fn == (func_ptr) NULL;
fmgr_info(proc, &proc_fn, &pronargs);
if ((proc_fn == NULL) || (pronargs != 2)) {
elog(WARN, "array_iterator: fmgr_info lookup failed for oid %d", proc);
return (0);
}
/* Scan the array and apply the operator to each element */
result = 0;
p = ARR_DATA_PTR(array);
for (i = 0; i < nitems; i++) {
if (typbyval) {
switch(typlen) {
case 1:
result = (int) (*proc_fn)(*p, value);
break;
case 2:
result = (int) (*proc_fn)(* (int16 *) p, value);
break;
case 3:
case 4:
result = (int) (*proc_fn)(* (int32 *) p, value);
break;
}
p += typlen;
} else {
result = (int) (*proc_fn)(p, value);
if (typlen > 0) {
p += typlen;
} else {
p += INTALIGN(* (int32 *) p);
}
}
if (result) {
if (!and) {
return (1);
}
} else {
if (and) {
return (0);
}
}
}
if (and && result) {
return (1);
} else {
return (0);
}
}
/*
* Iterators for type _text
*/
int32
array_texteq(ArrayType *array, char* value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */
0, /* logical or */
array, (Datum)value);
}
int32
array_all_texteq(ArrayType *array, char* value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 67, /* texteq */
1, /* logical and */
array, (Datum)value);
}
int32
array_textregexeq(ArrayType *array, char* value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */
0, /* logical or */
array, (Datum)value);
}
int32
array_all_textregexeq(ArrayType *array, char* value)
{
return array_iterator((Oid) 25, /* text */
(Oid) 81, /* textregexeq */
1, /* logical and */
array, (Datum)value);
}
/*
* Iterators for type _char16. Note that the regexp operators
* take the second argument of type text.
*/
int32
array_char16eq(ArrayType *array, char* value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */
0, /* logical or */
array, (Datum)value);
}
int32
array_all_char16eq(ArrayType *array, char* value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 490, /* char16eq */
1, /* logical and */
array, (Datum)value);
}
int32
array_char16regexeq(ArrayType *array, char* value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */
0, /* logical or */
array, (Datum)value);
}
int32
array_all_char16regexeq(ArrayType *array, char* value)
{
return array_iterator((Oid) 20, /* char16 */
(Oid) 700, /* char16regexeq */
1, /* logical and */
array, (Datum)value);
}
/*
* Iterators for type _int4
*/
int32
array_int4eq(ArrayType *array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */
0, /* logical or */
array, (Datum)value);
}
int32
array_all_int4eq(ArrayType *array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 65, /* int4eq */
1, /* logical and */
array, (Datum)value);
}
int32
array_int4gt(ArrayType *array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */
0, /* logical or */
array, (Datum)value);
}
int32
array_all_int4gt(ArrayType *array, int4 value)
{
return array_iterator((Oid) 23, /* int4 */
(Oid) 147, /* int4gt */
1, /* logical and */
array, (Datum)value);
}

View File

@ -0,0 +1,26 @@
From: Massimo Dal Zotto <dz@cs.unitn.it>
Date: Mon, 6 May 1996 01:03:37 +0200 (MET DST)
Subject: [PG95]: new operators for arrays
- -----BEGIN PGP SIGNED MESSAGE-----
Hi,
I have written an extension to Postgres95 which allows to use qualification
clauses based on the values of single elements of arrays.
For example I can now select rows having some or all element of an array
attribute equal to a given value or matching a regular expression:
select * from t where t.foo *= 'bar';
select * from t where t.foo **~ '^ba[rz]';
The scheme is quite general, each operator which operates on a base type can
be iterated over the elements of an array. It seem to work well but defining
each new operators requires writing a different C function. Furthermore in
each function there are two hardcoded OIDs which reference a base type and
a procedure. Not very portable. Can anyone suggest a better and more portable
way to do it ? Do you think this could be a useful feature for next release ?
Here is my code, it can be compiled and loaded as a dynamic module without
need to recompile the backend. I have defined only the few operators I needed,
the list can be extended. Feddback is welcome.

View File

@ -0,0 +1,137 @@
/*
* SQL code
- - -- load the new functions
- - --
load '/home/dz/lib/postgres/array_iterator.so';
- - -- define the array operators *=, **=, *~ and **~ for type _text
- - --
create function array_texteq(_text, text)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_all_texteq(_text, text)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_textregexeq(_text, text)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_all_textregexeq(_text, text)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
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 '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_all_char16eq(_char16, char16)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_char16regexeq(_char16, text)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_all_char16regexeq(_char16, text)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
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 '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_all_int4eq(_int4, int4)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_int4gt(_int4, int4)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
language 'c';
create function array_all_int4gt(_int4, int4)
returns bool
as '/home/dz/lib/postgres/array_iterator.so'
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_int4gt);
create operator **> (
leftarg=_int4,
rightarg=int4,
procedure=array_all_int4gt);
*/
/* end of file */

View File

@ -0,0 +1,147 @@
/*
* datetime_functions.c --
*
* This file defines new functions for the time and date data types.
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <time.h>
#include "postgres.h"
#include "pg_type.h"
#include "utils/palloc.h"
typedef struct DateADT {
char day;
char month;
short year;
} DateADT;
typedef struct TimeADT {
short hr;
short min;
float sec;
} TimeADT;
TimeADT *
time_difference(TimeADT *time1, TimeADT *time2)
{
TimeADT *time = (TimeADT*)palloc(sizeof(TimeADT));
time->sec = time1->sec - time2->sec;
time->min = time1->min - time2->min;
time->hr = time1->hr - time2->hr;
if (time->sec < 0) {
time->sec += 60.0;
time->min--;
} else if (time->sec >= 60.0) {
time->sec -= 60.0;
time->min++;
}
if (time->min < 0) {
time->min += 60;
time->hr--;
} else if (time->min >= 60) {
time->min -= 60;
time->hr++;
}
if (time->hr < 0) {
time->hr += 24;
} else if (time->hr >= 24) {
time->hr -= 24;
}
return (time);
}
TimeADT *
currentTime()
{
time_t current_time;
struct tm *tm;
TimeADT *result = (TimeADT*)palloc(sizeof(TimeADT));
current_time = time(NULL);
tm = localtime(&current_time);
result->sec = tm->tm_sec;
result->min = tm->tm_min;
result->hr = tm->tm_hour;
return (result);
}
int4
currentDate()
{
time_t current_time;
struct tm *tm;
int4 result;
DateADT *date = (DateADT*)&result;
current_time = time(NULL);
tm = localtime(&current_time);
date->day = tm->tm_mday;
date->month = tm->tm_mon+1;
date->year = tm->tm_year+1900;
return (result);
}
int4
hours(TimeADT *time)
{
return (time->hr);
}
int4
minutes(TimeADT *time)
{
return (time->min);
}
int4
seconds(TimeADT *time)
{
int seconds = (int)time->sec;
return (seconds);
}
int4
day(int4 val)
{
DateADT *date = (DateADT*)&val;
return (date->day);
}
int4
month(int4 val)
{
DateADT *date = (DateADT*)&val;
return (date->month);
}
int4
year(int4 val)
{
DateADT *date = (DateADT*)&val;
return (date->year);
}
int4
asMinutes(TimeADT *time)
{
int seconds = (int)time->sec;
return (time->min + 60*time->hr);
}
int4
asSeconds(TimeADT *time)
{
int seconds = (int)time->sec;
return (seconds + 60*time->min + 3600*time->hr);
}

View File

@ -0,0 +1,25 @@
From: Massimo Dal Zotto <dz@cs.unitn.it>
Date: Tue, 14 May 1996 14:31:18 +0200 (MET DST)
Subject: [PG95]: new postgres functions
- -----BEGIN PGP SIGNED MESSAGE-----
Some time ago I read in the mailing list requests of people looking
for more time and date functions. I have now written some of them:
time_difference(time1, time2) ,also defined as operator '-'
hour(time)
minutes(time)
seconds(time)
asMinutes(time)
asSeconds(time)
currentTime()
currentDate()
The file can be compiled as shared library and loaded as dynamic module
without need to recompile the backend. This can also be taken as an example
of the extensibility of postgres (user-defined functions, operators, etc).
I would be nice to see more of these user contributed modules posted to this
list and hopefully accessible from the Postgres home page.

View File

@ -0,0 +1,69 @@
-- SQL code to load and define 'datetime' functions
-- load the new functions
load '/home/dz/lib/postgres/datetime_functions.so';
-- define the new functions in postgres
create function time_difference(time,time)
returns time
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function currentDate()
returns date
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function currentTime()
returns time
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function hours(time)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function minutes(time)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function seconds(time)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function day(date)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function month(date)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function year(date)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function asMinutes(time)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create function asSeconds(time)
returns int4
as '/home/dz/lib/postgres/datetime_functions.so'
language 'c';
create operator - (
leftarg=time,
rightarg=time,
procedure=time_difference);

View File

@ -0,0 +1,19 @@
#
# Makefile
#
#
TARGET = pginsert
CFLAGS = -g -Wall -I/u/postgres95/include
LIBS = -L/u/postgres95/lib -lpq
$(TARGET) : pginsert.o pginterface.o halt.o
$(CC) -o $(TARGET) $(XFLAGS) $(CFLAGS) \
pginsert.o pginterface.o halt.o $(LIBS)
clean:
rm -f *.o $(TARGET) log core
install:
make clean
make CFLAGS=-O
install -s -o bin -g bin $(TARGET) /usr/local/bin

View File

@ -0,0 +1,42 @@
Pginterface 1.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(), and
disconnectdb(). Each call returns a structure or value, so if you need
to do more work with the result, you can. Also, I have a global
variable that allows you to disable the error checking I have added to
the doquery() routine.
I have added a function called fetch(), which allows you to pass
pointers as parameters, and on return the variables are filled with the
data from the binary cursor you opened. These binary cursors are not
useful if you are running the query engine on a system with a different
architecture than the database server. If you pass a NULL pointer, the
column is skipped, and you can use libpq to handle it as you wish.
I have used sigprocmask() to block the reception of certain signals
while the program is executing SQL queries. This prevents a user
pressing Control-C from stopping all the back ends. It blocks SIGHUP,
SIGINT, and SIGTERM, but does not block SIGQUIT or obviously kill -9.
If your platform does not support sigprocmask(), you can remove those
function calls. ( Am I correct that abnormal termination can cause
shared memory resynchronization?)
There is a demo program called pginsert that demonstrates how the
library can be used.
You can create a library of pginterface.c and halt.c, and just include
pginterface.h in your source code.
I am willing to maintain this if people find problems or want additional
functionality.
Bruce Momjian (root@candle.pha.pa.us)

View File

@ -0,0 +1,58 @@
/*
**
** halt.c
**
** This is used to print out error messages and exit
*/
#include <varargs.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/*-------------------------------------------------------------------------
**
** halt - print error message, and call clean up routine or exit
**
**------------------------------------------------------------------------*/
/*VARARGS*/
void halt(va_alist)
va_dcl
{
va_list arg_ptr;
char *format, *pstr;
void (*sig_func)();
va_start(arg_ptr);
format = va_arg(arg_ptr,char *);
if (strncmp(format,"PERROR", 6) != 0)
vfprintf(stderr,format,arg_ptr);
else
{
for (pstr=format+6; *pstr == ' ' || *pstr == ':'; pstr++)
;
vfprintf(stderr,pstr,arg_ptr);
perror("");
}
va_end(arg_ptr);
fflush(stderr);
/* call one clean up function if defined */
if ( (sig_func = signal(SIGTERM, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
else if ( (sig_func = signal(SIGHUP, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
else if ( (sig_func = signal(SIGINT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
else if ( (sig_func = signal(SIGQUIT, SIG_DFL)) != SIG_DFL &&
sig_func != SIG_IGN)
(*sig_func)(0);
exit(1);
}

View File

@ -0,0 +1,7 @@
/*
** halt.h
**
*/
void halt();

View File

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

View File

@ -0,0 +1,177 @@
/*
* pginterface.c
*
*/
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdarg.h>
#include "halt.h"
#include <libpq-fe.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_args;
num_args = PQnfields(res);
if (tuple >= PQntuples(res))
return END_OF_TUPLES;
va_start(ap, param);
for (arg = 0; arg < num_args; 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++;
}
/*
**
** on_error_stop
**
*/
void on_error_stop()
{
on_error_state = ON_ERROR_STOP;
}
/*
**
** on_error_continue
**
*/
void on_error_continue()
{
on_error_state = ON_ERROR_CONTINUE;
}
/*
**
** sig_disconnect
**
*/
static void sig_disconnect()
{
fprintf(stderr,"exiting...\n");
PQfinish(conn);
exit(1);
}
/*
**
** set_signals
**
*/
static void set_signals()
{
sigemptyset(&block_sigs);
sigemptyset(&unblock_sigs);
sigaddset(&block_sigs,SIGTERM);
sigaddset(&block_sigs,SIGHUP);
sigaddset(&block_sigs,SIGINT);
/* sigaddset(&block_sigs,SIGQUIT); no block */
sigprocmask(SIG_SETMASK,&unblock_sigs,NULL);
signal(SIGTERM,sig_disconnect);
signal(SIGHUP,sig_disconnect);
signal(SIGINT,sig_disconnect);
signal(SIGQUIT,sig_disconnect);
}

View File

@ -0,0 +1,13 @@
/*
* pglib.h
*
*/
PGresult *doquery(char *query);
PGconn *connectdb();
void disconnectdb();
int fetch(void *param, ...);
void on_error_continue();
void on_error_stop();
#define END_OF_TUPLES (-1)

83
contrib/soundex/soundex.c Normal file
View File

@ -0,0 +1,83 @@
/*****************************************************************************/
/* soundex.c */
/*****************************************************************************/
#include <string.h>
#include <stdio.h>
#include "postgres.h" /* for char16, etc. */
#include "utils/palloc.h" /* for palloc */
#include "libpq-fe.h" /* for TUPLE */
#include <stdio.h>
#include <ctype.h>
/* prototype for soundex function */
char *soundex(char *instr, char *outstr);
text *text_soundex(text *t)
{
/* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
char *table = "01230120022455012623010202";
int count = 0;
text *new_t;
char outstr[6+1]; /* max length of soundex is 6 */
char *instr;
/* make a null-terminated string */
instr=palloc(VARSIZE(t)+1);
memcpy(instr,VARDATA(t),VARSIZE(t)-VARHDRSZ);
instr[VARSIZE(t)-VARHDRSZ] = (char)0;
/* load soundex into outstr */
soundex(instr, outstr);
/* Now the outstr contains the soundex of instr */
/* copy outstr to new_t */
new_t = (text *) palloc(strlen(outstr)+VARHDRSZ);
memset(new_t, 0, strlen(outstr)+1);
VARSIZE(new_t) = strlen(outstr)+VARHDRSZ;
memcpy((void *) VARDATA(new_t),
(void *) outstr,
strlen(outstr));
/* free instr */
pfree(instr);
return(new_t);
}
char *soundex(char *instr, char *outstr)
{ /* ABCDEFGHIJKLMNOPQRSTUVWXYZ */
char *table = "01230120022455012623010202";
int count = 0;
while(!isalpha(instr[0]) && instr[0])
++instr;
if(!instr[0]) { /* Hey! Where'd the string go? */
outstr[0]=(char)0;
return outstr;
}
if(toupper(instr[0]) == 'P' && toupper(instr[1]) == 'H') {
instr[0] = 'F';
instr[1] = 'A';
}
*outstr++ = (char)toupper(*instr++);
while(*instr && count < 5) {
if(isalpha(*instr) && *instr != *(instr-1)) {
*outstr = table[toupper(instr[0]) - 'A'];
if(*outstr != '0') {
++outstr;
++count;
}
}
++instr;
}
*outstr = '\0';
return(outstr);
}

View File

@ -0,0 +1,57 @@
--------------- soundex.sql:
CREATE FUNCTION text_soundex(text) RETURNS text
AS '/usr/local/postgres/postgres95/src/funcs/soundex.so' LANGUAGE 'c';
SELECT text_soundex('hello world!');
CREATE TABLE s (nm text)\g
insert into s values ('john')\g
insert into s values ('joan')\g
insert into s values ('wobbly')\g
select * from s
where text_soundex(nm) = text_soundex('john')\g
select nm from s a, s b
where text_soundex(a.nm) = text_soundex(b.nm)
and a.oid <> b.oid\g
CREATE FUNCTION text_sx_eq(text, text) RETURNS bool AS
'select text_soundex($1) = text_soundex($2)'
LANGUAGE 'sql'\g
CREATE FUNCTION text_sx_lt(text,text) RETURNS bool AS
'select text_soundex($1) < text_soundex($2)'
LANGUAGE 'sql'\g
CREATE FUNCTION text_sx_gt(text,text) RETURNS bool AS
'select text_soundex($1) > text_soundex($2)'
LANGUAGE 'sql';
CREATE FUNCTION text_sx_le(text,text) RETURNS bool AS
'select text_soundex($1) <= text_soundex($2)'
LANGUAGE 'sql';
CREATE FUNCTION text_sx_ge(text,text) RETURNS bool AS
'select text_soundex($1) >= text_soundex($2)'
LANGUAGE 'sql';
CREATE FUNCTION text_sx_ne(text,text) RETURNS bool AS
'select text_soundex($1) <> text_soundex($2)'
LANGUAGE 'sql';
DROP OPERATOR #= (text,text)\g
CREATE OPERATOR #= (leftarg=text, rightarg=text, procedure=text_sx_eq,
commutator=text_sx_eq)\g
SELECT *
FROM s
WHERE text_sx_eq(nm,'john')\g
SELECT *
from s
where s.nm #= 'john';

361
contrib/string/string_io.c Normal file
View File

@ -0,0 +1,361 @@
/*
* string_io.c --
*
* This file defines new input/output conversion routines for strings.
*
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
*/
#include <ctype.h>
#include <string.h>
#include "postgres.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "utils/builtins.h"
/* define this if you want to see iso-8859 characters */
#define ISO8859
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define VALUE(char) ((char) - '0')
#define DIGIT(val) ((val) + '0')
#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
#ifndef ISO8859
#define NOTPRINTABLE(c) (!isprint(c))
#else
#define NOTPRINTABLE(c) (!isprint(c) && ((c) < 0xa0))
#endif
/*
* string_output() --
*
* This function takes a pointer to a string data and an optional
* data size and returns a printable representation of the data
* translating all escape sequences to C-like \nnn or \c escapes.
* The function is used by output methods of various string types.
*
* Arguments:
* data - input data (can be NULL)
* size - optional size of data. A negative value indicates
* that data is a null terminated string.
*
* Returns:
* a pointer to a new string containing the printable
* representation of data.
*/
char *
string_output(char *data, int size)
{
register unsigned char c, *p, *r, *result;
register int l, len;
if (data == NULL) {
result = (char *) palloc(2);
result[0] = '-';
result[1] = '\0';
return (result);
}
if (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(c)) {
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() --
*
* This function accepts a C string in input and copies it into a new
* object allocated with palloc() translating all escape sequences.
* An optional header can be allocatd before the string, for example
* to hold the length of a varlena object.
* This function is not necessary for input from sql commands because
* the parser already does escape translation, all data input routines
* receive strings in internal form.
*
* Arguments:
* str - input string possibly with escapes
* size - the required size of new data. A value of 0
* indicates a variable size string, while a
* negative value indicates a variable size string
* of size not greater than this absolute value.
* hdrsize - size of an optional header to be allocated before
* the data. It must then be filled by the caller.
* rtn_size - an optional pointer to an int variable where the
* size of the new string is stored back.
*
* Returns:
* a pointer to the new string or the header.
*/
char *
string_input(char *str, int size, int hdrsize, int *rtn_size)
{
register unsigned char *p, *r;
unsigned char *result;
int len;
if ((str == NULL) || (hdrsize < 0)) {
return (char *) NULL;
}
/* Compute result size */
len = strlen(str);
for (p=str; *p; ) {
if (*p++ == '\\') {
if (ISOCTAL(*p)) {
if (ISOCTAL(*(p+1))) {
p++;
len--;
}
if (ISOCTAL(*(p+1))) {
p++;
len--;
}
}
if (*p) p++;
len--;
}
}
/* 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 *
c_charout(int32 c)
{
char str[2];
str[0] = (char) c;
str[1] = '\0';
return (string_output(str, 1));
}
char *
c_char2out(uint16 s)
{
return (string_output((char *) &s, 2));
}
char *
c_char4out(uint32 s)
{
return (string_output((char *) &s, 4));
}
char *
c_char8out(char *s)
{
return (string_output(s, 8));
}
char *
c_char16out(char *s)
{
return (string_output(s, 16));
}
/*
* This can be used for text, bytea, SET and unknown data types
*/
char *
c_textout(struct varlena *vlena)
{
int len = 0;
char *s = NULL;
if (vlena) {
len = VARSIZE(vlena) - VARHDRSZ;
s = VARDATA(vlena);
}
return (string_output(s, len));
}
/*
* This can be used for varchar and bpchar strings
*/
char *
c_varcharout(char *s)
{
int len;
if (s) {
len = *(int32*)s - 4;
s += 4;
}
return (string_output(s, len));
}
#ifdef 0
struct varlena *
c_textin(char *str)
{
struct varlena *result;
int len;
if (str == NULL) {
return ((struct varlena *) NULL);
}
result = (struct varlena *) string_input(str, 0, VARHDRSZ, &len);
VARSIZE(result) = len;
return (result);
}
char *
c_char16in(char *str)
{
return (string_input(str, 16, 0, NULL));
}
#endif

View File

@ -0,0 +1,111 @@
- - -- load the new functions
- - --
load '/home/dz/lib/postgres/string_output.so';
- - -- create function c_textin(opaque)
- - -- returns text
- - -- as '/home/dz/lib/postgres/string_output.so'
- - -- language 'c';
create function c_charout(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
language 'c';
create function c_char2out(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
language 'c';
create function c_char4out(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
language 'c';
create function c_char8out(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
language 'c';
create function c_char16out(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
language 'c';
create function c_textout(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
language 'c';
create function c_varcharout(opaque)
returns int4
as '/home/dz/lib/postgres/string_output.so'
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';
- - -- or do the changes 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';

180
contrib/zap_ltv/README Normal file
View File

@ -0,0 +1,180 @@
From ccshag@cclabs.missouri.edu Wed Jul 10 04:27:16 1996
Received: from realtime.cc.missouri.edu (realtime.cc.missouri.edu [128.206.212.69]) by ki.net (8.7.5/8.7.5) with ESMTP id EAA12722 for <scrappy@ki.net>; Wed, 10 Jul 1996 04:27:13 -0400 (EDT)
Received: from localhost (ccshag@localhost) by realtime.cc.missouri.edu (8.7.1/8.7.1) with SMTP id DAA00915; Wed, 10 Jul 1996 03:26:42 -0500 (CDT)
X-Authentication-Warning: realtime.cc.missouri.edu: ccshag owned process doing -bs
Date: Wed, 10 Jul 1996 03:26:41 -0500 (CDT)
From: "Paul 'Shag' Walmsley" <ccshag@cclabs.missouri.edu>
Sender: ccshag@cclabs.missouri.edu
To: postgres95-hackers@oozoo.vnet.net
cc: pixel@tiger.coe.missouri.edu, michael.siebenborn@ae3.Hypo.DE,
scrappy@ki.net
Subject: Workaround for database corruption problems [long]
Message-ID: <Pine.SGI.3.93.960710023021.673B-100000@realtime.cc.missouri.edu>
X-Disclaimer: The opinions of this writer do not necessarily represent those of the University of Missouri-Columbia.
MIME-Version: 1.0
Content-Type: TEXT/PLAIN; charset=US-ASCII
Status: RO
X-Status:
I've been poking through the guts of POSTGRES95 in pursuit of the
previously-mentioned "database corruption" problem, and I think that I
have some preliminary answers and a workaround for people to play around
with.
Specifically, the problem that I'm referring to is manifested by
pg_log, pg_time, or pg_variable either growing to huge sizes (usually
128MB) or showing evidence of internal corruption. Symptoms that appear
to the user include receiving messages like the following in psql or
monitor:
"WARN:cannot open segment 1 of relation pg_time"
"WARN:cannot write block 16384 of pg_log"
.. which are followed up in a 'postmaster -d 3' debug log by a
"NOTICE:AbortTransaction and not in in-progress state"
It's been my experience that this problem is difficult to reproduce on
demand; I've tried several different ways and haven't succeeded. (If
anyone knows how to reliably reproduce this, please E-mail me.) I was,
however, able to obtain a database in a corrupted state from one of the
fellows on campus here running POSTGRES95; and following is something of a
post-mortem.
----------
The problem with the database is that after any query, POSTGRES95 attempts
to generate a huge pg_log file and fails (running out of disk space) with
a "WARN:cannot write block 16384 of pg_log" message. The contents of the
database and its internal structure were reasonably intact - I discovered
that I was able to execute a single SQL query per-backend, and could
therefore back the contents of the database up with a COPY command. (I
wasn't able to use pg_dump; it failed on its BEGIN TRANSACTION step.)
Inspection of data/pg_log, data/pg_variable, and data/pg_time revealed the
following;
realtime /usr/postgres95 > od -x data/pg_log.old | head
0000000 0060 2000 2000 2000 f680 0000 ed00 0000
0000020 e300 0000 dc00 0000 ce00 0000 bc00 0000
0000040 b140 0000 a740 0000 9dc0 0000 9180 0000
0000060 8580 0000 7c80 0000 6fc0 0000 6480 0000
0000100 58c0 0000 4fc0 0000 4640 0000 3a80 0000
0000120 30c0 0000 23c0 0000 1900 0000 0c40 0000
0000140 0000 0000 0000 0000 0000 0000 0000 0000
*
0000200 8800 0000 0000 0000 0800 0000 0000 0000
0000220 0800 0000 0000 0000 0800 0000 0000 0000
Now, already something is obviously wrong: pg_log shouldn't have any
values other than 0, 1, 2, 4, 8, a, c, or e for each nybble (see the XID_
... definitions in src/backend/access/transam.h:35; two of these can be
stored per nybble.)
realtime /usr/postgres95 > od -x data/pg_variable.old | head
0000000 0058 2000 2000 2740 f240 0000 e400 0000
0000020 d780 0000 cac0 0000 b840 0000 a900 0000
0000040 9f80 0000 9280 0000 8580 0000 78c0 0000
0000060 6c80 0000 6200 0000 5800 0000 4f80 0000
0000100 44c0 0000 39c0 0000 2d80 0000 2600 0000
0000120 1c00 0000 0e00 0000 0000 0000 0000 0000
0000140 0000 0000 0000 0000 0000 0000 0000 0000
*
0000700 0000 01bc 0000 009c 0014 ffff ffff 0000
0000720 0004 5f0b 0000 0000 0000 0ba3 0000 0c23
This is obviously broken too; the only bytes that should be non-zero in
pg_variable are the first 16 (see src/backend/access/transam.h:99).
realtime /usr/postgres95 > od -x data/pg_time.old | head -10
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0004000 31c6 09ae 0000 0000 31c6 09b3 31a9 79a6
0004020 31a9 79a6 31a9 79a8 31a9 79a9 31a9 79a9
0004040 31a9 79a9 31a9 79aa 31a9 79aa 31a9 79aa
0004060 31a9 79aa 31a9 79aa 31a9 79ab 31a9 79ab
0004100 31a9 79ab 31a9 79ac 31a9 79ac 31a9 79ac
0004120 31a9 79ac 31a9 79ad 31a9 79ad 31a9 79ae
0004140 31a9 79ae 31a9 79af 31a9 79af 31a9 79af
0004160 31a9 79b0 31a9 79b0 31a9 79b4 31a9 79b4
realtime /usr/postgres95 > od -x data/pg_time.old +32b | head -10
0032000 6665 6520 666f 7220 31c5 cb61 696e 6720
0032020 6465 7665 6c6f 7065 7273 2062 7579 2061
0032040 206c 6f77 2d69 6e63 6f6d 6520 6170 6172
0032060 746d 656e 7420 636f 6d70 6c65 7869 6e20
0032100 7468 6520 7472 6561 7375 7265 7227 7320
0032120 7761 7264 2066 726f 6d20 7468 6520 6665
0032140 6465 7261 6c20 676f 7665 726e 6d65 6e74
0032160 2066 6f72 2024 313b 2064 6576 656c 6f70
0032200 6572 7361 6c73 6f20 31c5 cb7c 31be 91a2
0032220 31be 91a2 31be 91a2 31be 91a2 31be 91a3
The first part of pg_time looked okay -- filled with 4-byte time values
with the occasional "00 00 00 00" here and there -- but inspection into
pg_time's guts revealed some preposterous numbers.
For your comparing pleasure, I include copies of what these files
_should_ look like -- more or less -- from a newly initdb'd database
directory:
realtime /usr/postgres95 > od -x data-new-initdb/pg_log
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0000200 8aaa aaaa aaaa aaa8 0000 0000 0000 0000
0000220 0000 0000 0000 0000 0000 0000 0000 0000
*
0020000
realtime /usr/postgres95 > od -x data-new-initdb/pg_variable
0000000 0000 0000 0000 0222 0000 021e 0000 5120
0000020 0000 0000 0000 0000 0000 0000 0000 0000
*
0020000
realtime /usr/postgres95 > od -x data-new-initdb/pg_time
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0004000 31e3 4fb3 0000 0000 31e3 4fb4 31e3 4fb4
0004020 31e3 4fb4 31e3 4fb5 31e3 4fb5 31e3 4fb5
0004040 31e3 4fb6 31e3 4fb6 31e3 4fb6 31e3 4fb6
0004060 31e3 4fb7 31e3 4fb7 31e3 4fb7 31e3 4fb7
0004100 31e3 4fb8 31e3 4fb8 31e3 4fb8 31e3 4fb9
0004120 31e3 4fb9 31e3 4fb9 31e3 4fb9 31e3 4fba
0004140 31e3 4fba 31e3 4fba 31e3 4fbb 31e3 4fbb
0004160 31e3 4fbb 31e3 4fbb 31e3 4fbb 0000 0000
0004200 0000 0000 0000 0000 0000 0000 0000 0000
*
0020000
What's more, if I view pg_log, pg_variable, and pg_time with a text
editor, I discover that the corrupted areas contain fragments of tuples
from the database!
----------
Since I have been unable to reproduce this problem on demand (and
therefore have a complete picture of all of the queries executed on the
database), I've only been able to speculate on the original cause of the
problem. One explanation would be some kind of buffer manager bug; or
perhaps the backend is running low on file descriptors and bollixing
existing ones? I intend to continue tracking this down as I have the
time, but in the interim, I've created a quick Perl script that is
designed to restore some sense of rationality to the
pg_{log,time,variable} files. It's a horribly barbaric way to do this --
at the very least, it almost certainly breaks time travel, and will
almost certainly result in duplicate oid problems -- but it restored my
pathological database here to a sufficiently working state such that I
could run pg_dump on it and do various other queries with it. The script
is included at the bottom of this message.
Anyway, fellow posthackers, assuming that no one raises any red flags
about this, I'll post it on postgres95@oozoo.vnet.net as a temporary
stop-gap workaround to allow people to recover their databases when this
sort of problem occurs.
I wish I had more time to do this kind of thing.
- Paul "Shag" Walmsley <ccshag@cclabs.missouri.edu>
"Knowing is not enough." -- Hal Hartley, "Surviving Desire"

View File

@ -0,0 +1,93 @@
#!/bin/perl
#
# zap_ltv - attempt to restore a POSTGRES95 database afflicted with
# pg_log, pg_time, or pg_variable corruption to
# minimal functionality
#
# Paul Walmsley <ccshag@cclabs.missouri.edu>
#
# Legalese:
#
# In no event shall Paul Walmsley be liable to any party for direct,
# indirect, special, incidental, or consequential damages, including
# lost profits, arising from the use of this software, even if Paul Walmsley
# has been advised of the possibility of such damage. Paul Walmsley
# specifically disclaims any warranties, including, but not limited to,
# the implied warranties of merchantability and fitness for a particular
# purpose. The software provided hereunder is on an "as is" basis,
# and Paul Walmsley has no obligations to provide maintenance, support,
# updates, enhancements, or modifications.
#
# Thanks, Berkeley ;-)
print "This program should only be run if POSTGRES95 is not currently\n";
print "running on this system. It should also not be run unless you \n";
print "are having seemingly unrecoverable problems with your POSTGRES95\n";
print "database related to pg_log, pg_time, or pg_variable corruption.\n\n";
print "This program replaces the existing pg_log, pg_time, and pg_variable\n";
print "files with \"clean\" copies of those files. This will almost \n";
print "certainly result in duplicate IDs when any INSERTs are attempted,\n";
print "and probably has other side-effects as well. Back up your databases\n"
print "and re-initdb from scratch after using this!\n\n";
print "This program will attempt to make a backup of your pg_time,\n";
print "pg_log, and pg_variable files (to pg_time.backup, pg_log.backup,\n";
print "and pg_variable.backup, respectively).\n\n";
print "This program bears no guarantees nor any warranties whatsoever.\n";
print "View the source for details.\n\n";
print "Press ENTER to zap your pg_log, pg_time, and pg_variable files:";
$trash=<STDIN>;
$pg_log_data=pack('xxxx@8192',0);
$pg_time_data=pack('xxxx@8192',0);
# next_tid, last_tid, and oid are pulled from a fresh initdb
$pg_variable_data=pack('xxxxxxCCxxCCxxCC@8192',2,34,2,30,81,32);
if (length($pg_log_data)!=8192) {
die "pg_log_data must be exactly 8192 bytes long";
}
if (length($pg_time_data)!=8192) {
die "pg_time_data must be exactly 8192 bytes long";
}
if (length($pg_variable_data)!=8192) {
die "pg_variable_data must be exactly 8192 bytes long";
}
if (! -f 'pg_database') {
die "This program must be run from your POSTGRES95 data directory.";
}
system('cp pg_log pg_log.backup');
open(PG_LOG,'>pg_log');
binmode(PG_LOG);
$written=syswrite(PG_LOG,$pg_log_data,8192);
close(PG_LOG);
if ($written!=8192) {
die "pg_log write failed: $!";
}
$written=0;
system('cp pg_time pg_time.backup');
open(PG_TIME,'>pg_time');
binmode(PG_TIME);
$written=syswrite(PG_TIME,$pg_time_data,8192);
close(PG_TIME);
if ($written!=8192) {
die "pg_time write failed: $!";
}
$written=0;
system('cp pg_variable pg_variable.backup');
open(PG_VARIABLE,'>pg_variable');
binmode(PG_VARIABLE);
$written=syswrite(PG_VARIABLE,$pg_variable_data,8192);
close(PG_VARIABLE);
if ($written!=8192) {
die "pg_variable write failed: $!";
}
print "Done.\n";