diff --git a/contrib/fuzzystrmatch/dmetaphone.c b/contrib/fuzzystrmatch/dmetaphone.c index 820cced..2db3b43 100644 --- a/contrib/fuzzystrmatch/dmetaphone.c +++ b/contrib/fuzzystrmatch/dmetaphone.c @@ -48,7 +48,7 @@ /* - * $Revision: 1.6 $ + * $Revision$ * $Id$ */ diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile new file mode 100644 index 0000000..fef7f81 --- /dev/null +++ b/contrib/pg_buffercache/Makefile @@ -0,0 +1,17 @@ +# $PostgreSQL$ + +MODULE_big = pg_buffercache +OBJS = pg_buffercache_pages.o + +DATA_built = pg_buffercache.sql +DOCS = README.pg_buffercache + +ifdef USE_PGXS +PGXS := $(shell pg_config --pgxs) +include $(PGXS) +else +subdir = contrib/pg_buffercache +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/contrib/pg_buffercache/README.pg_buffercache b/contrib/pg_buffercache/README.pg_buffercache new file mode 100644 index 0000000..b5c9321 --- /dev/null +++ b/contrib/pg_buffercache/README.pg_buffercache @@ -0,0 +1,112 @@ +Pg_buffercache - Real time queries on the shared buffer cache. +-------------- + + This module consists of a C function 'pg_buffercache_pages()' that returns + a set of records, plus a view 'pg_buffercache' to wrapper the function. + + The intent is to do for the buffercache what pg_locks does for locks, i.e - + ability to examine what is happening at any given time without having to + restart or rebuild the server with debugging code added. + + By default public access is REVOKED from both of these, just in case there + are security issues lurking. + + +Installation +------------ + + Build and install the main Postgresql source, then this contrib module: + + $ cd contrib/pg_buffercache + $ gmake + $ gmake install + + + To register the functions: + + $ psql -d -f pg_buffercache.sql + + +Notes +----- + + The definition of the columns exposed in the view is: + + Column | references | Description + ----------------+----------------------+------------------------------------ + bufferid | | Id, 1..shared_buffers. + relfilenode | pg_class.relfilenode | Refilenode of the relation. + reltablespace | pg_tablespace.oid | Tablespace oid of the relation. + reldatabase | pg_database.oid | Database for the relation. + relblocknumber | | Offset of the page in the relation. + isdirty | | Is the page dirty? + + + There is one row for each buffer in the shared cache. Unused buffers are + shown with all fields null except bufferid. + + Because the cache is shared by all the databases, there are pages from + relations not belonging to the current database. + + When the pg_buffercache view is accessed, internal buffer manager locks are + taken, and a copy of the buffer cache data is made for the view to display. + This ensures that the view produces a consistent set of results, while not + blocking normal buffer activity longer than necessary. Nonetheless there + could be some impact on database performance if this view is read often. + + +Sample output +------------- + + regression=# \d pg_buffercache; + View "public.pg_buffercache" + Column | Type | Modifiers + ----------------+---------+----------- + bufferid | integer | + relfilenode | oid | + reltablespace | oid | + reldatabase | oid | + relblocknumber | bigint | + isdirty | boolean | + View definition: + SELECT p.bufferid, p.relfilenode, p.reltablespace, p.reldatabase, + p.relblocknumber, p.isdirty + FROM pg_buffercache_pages() p(bufferid integer, relfilenode oid, + reltablespace oid, reldatabase oid, relblocknumber bigint, + isdirty boolean); + + regression=# SELECT c.relname, count(*) AS buffers + FROM pg_class c, pg_buffercache b + WHERE b.relfilenode = c.relfilenode + GROUP BY c.relname + ORDER BY 2 DESC LIMIT 10; + relname | buffers + ---------------------------------+--------- + tenk2 | 345 + tenk1 | 141 + pg_proc | 46 + pg_class | 45 + pg_attribute | 43 + pg_class_relname_nsp_index | 30 + pg_proc_proname_args_nsp_index | 28 + pg_attribute_relid_attnam_index | 26 + pg_depend | 22 + pg_depend_reference_index | 20 + (10 rows) + + regression=# + + +Author +------ + + * Mark Kirkwood + + +Help +---- + + * Design suggestions : Neil Conway + * Debugging advice : Tom Lane + + Thanks guys! diff --git a/contrib/pg_buffercache/pg_buffercache.sql.in b/contrib/pg_buffercache/pg_buffercache.sql.in new file mode 100644 index 0000000..1dd0b2e --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache.sql.in @@ -0,0 +1,18 @@ +-- Adjust this setting to control where the objects get created. +SET search_path = public; + +-- Register the function. +CREATE OR REPLACE FUNCTION pg_buffercache_pages() +RETURNS SETOF RECORD +AS 'MODULE_PATHNAME', 'pg_buffercache_pages' +LANGUAGE 'C'; + +-- Create a view for convenient access. +CREATE VIEW pg_buffercache AS + SELECT P.* FROM pg_buffercache_pages() AS P + (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid, + relblocknumber int8, isdirty bool); + +-- Don't want these to be available at public. +REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC; +REVOKE ALL ON pg_buffercache FROM PUBLIC; diff --git a/contrib/pg_buffercache/pg_buffercache_pages.c b/contrib/pg_buffercache/pg_buffercache_pages.c new file mode 100644 index 0000000..22bb8e5 --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache_pages.c @@ -0,0 +1,230 @@ +/*------------------------------------------------------------------------- + * + * pg_buffercache_pages.c + * display some contents of the buffer cache + * + * $PostgreSQL$ + *------------------------------------------------------------------------- + */ +#include "postgres.h" +#include "funcapi.h" +#include "catalog/pg_type.h" +#include "storage/buf_internals.h" +#include "storage/bufmgr.h" +#include "utils/relcache.h" + + +#define NUM_BUFFERCACHE_PAGES_ELEM 6 + +#if defined(WIN32) || defined(__CYGWIN__) +extern DLLIMPORT BufferDesc *BufferDescriptors; +extern DLLIMPORT volatile uint32 InterruptHoldoffCount; +#endif + +Datum pg_buffercache_pages(PG_FUNCTION_ARGS); + + +/* + * Record structure holding the to be exposed cache data. + */ +typedef struct +{ + + uint32 bufferid; + Oid relfilenode; + Oid reltablespace; + Oid reldatabase; + BlockNumber blocknum; + bool isvalid; + bool isdirty; + +} BufferCachePagesRec; + + +/* + * Function context for data persisting over repeated calls. + */ +typedef struct +{ + + AttInMetadata *attinmeta; + BufferCachePagesRec *record; + char *values[NUM_BUFFERCACHE_PAGES_ELEM]; + +} BufferCachePagesContext; + + +/* + * Function returning data from the shared buffer cache - buffer number, + * relation node/tablespace/database/blocknum and dirty indicator. + */ +PG_FUNCTION_INFO_V1(pg_buffercache_pages); +Datum +pg_buffercache_pages(PG_FUNCTION_ARGS) +{ + + FuncCallContext *funcctx; + Datum result; + MemoryContext oldcontext; + BufferCachePagesContext *fctx; /* User function context. */ + TupleDesc tupledesc; + HeapTuple tuple; + + if (SRF_IS_FIRSTCALL()) + { + uint32 i; + volatile BufferDesc *bufHdr; + + funcctx = SRF_FIRSTCALL_INIT(); + + /* Switch context when allocating stuff to be used in later calls */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* Construct a tuple to return. */ + tupledesc = CreateTemplateTupleDesc(NUM_BUFFERCACHE_PAGES_ELEM, false); + TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid", + INT4OID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode", + OIDOID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace", + OIDOID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase", + OIDOID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relblocknumber", + INT8OID, -1, 0); + TupleDescInitEntry(tupledesc, (AttrNumber) 6, "isdirty", + BOOLOID, -1, 0); + + /* Generate attribute metadata needed later to produce tuples */ + funcctx->attinmeta = TupleDescGetAttInMetadata(tupledesc); + + /* + * Create a function context for cross-call persistence and initialize + * the buffer counters. + */ + fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext)); + funcctx->max_calls = NBuffers; + funcctx->user_fctx = fctx; + + + /* Allocate NBuffers worth of BufferCachePagesRec records. */ + fctx->record = (BufferCachePagesRec *) palloc(sizeof(BufferCachePagesRec) * NBuffers); + + /* allocate the strings for tuple formation */ + fctx->values[0] = (char *) palloc(3 * sizeof(uint32) + 1); + fctx->values[1] = (char *) palloc(3 * sizeof(uint32) + 1); + fctx->values[2] = (char *) palloc(3 * sizeof(uint32) + 1); + fctx->values[3] = (char *) palloc(3 * sizeof(uint32) + 1); + fctx->values[4] = (char *) palloc(3 * sizeof(uint32) + 1); + fctx->values[5] = (char *) palloc(2); + + + /* Return to original context when allocating transient memory */ + MemoryContextSwitchTo(oldcontext); + + + /* + * Lock Buffer map and scan though all the buffers, saving the + * relevant fields in the fctx->record structure. + */ + LWLockAcquire(BufMappingLock, LW_SHARED); + + for (i = 0, bufHdr = BufferDescriptors; i < NBuffers; i++, bufHdr++) + { + /* Lock each buffer header before inspecting. */ + LockBufHdr(bufHdr); + + fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr); + fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode; + fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode; + fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode; + fctx->record[i].blocknum = bufHdr->tag.blockNum; + + if (bufHdr->flags & BM_DIRTY) + fctx->record[i].isdirty = true; + else + fctx->record[i].isdirty = false; + + /* Note if the buffer is valid, and has storage created */ + if ((bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_TAG_VALID)) + fctx->record[i].isvalid = true; + else + fctx->record[i].isvalid = false; + + UnlockBufHdr(bufHdr); + } + + /* Release Buffer map. */ + LWLockRelease(BufMappingLock); + } + + funcctx = SRF_PERCALL_SETUP(); + + /* Get the saved state */ + fctx = funcctx->user_fctx; + + + if (funcctx->call_cntr < funcctx->max_calls) + { + uint32 i = funcctx->call_cntr; + char *values[NUM_BUFFERCACHE_PAGES_ELEM]; + int j; + + /* + * Use a temporary values array, initially pointing to fctx->values, + * so it can be reassigned w/o losing the storage for subsequent + * calls. + */ + for (j = 0; j < NUM_BUFFERCACHE_PAGES_ELEM; j++) + { + values[j] = fctx->values[j]; + } + + + /* + * Set all fields except the bufferid to null if the buffer is unused + * or not valid. + */ + if (fctx->record[i].blocknum == InvalidBlockNumber || + fctx->record[i].isvalid == false) + { + + sprintf(values[0], "%u", fctx->record[i].bufferid); + values[1] = NULL; + values[2] = NULL; + values[3] = NULL; + values[4] = NULL; + values[5] = NULL; + + } + else + { + + sprintf(values[0], "%u", fctx->record[i].bufferid); + sprintf(values[1], "%u", fctx->record[i].relfilenode); + sprintf(values[2], "%u", fctx->record[i].reltablespace); + sprintf(values[3], "%u", fctx->record[i].reldatabase); + sprintf(values[4], "%u", fctx->record[i].blocknum); + if (fctx->record[i].isdirty) + { + strcpy(values[5], "t"); + } + else + { + strcpy(values[5], "f"); + } + + } + + + /* Build and return the tuple. */ + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + result = HeapTupleGetDatum(tuple); + + + SRF_RETURN_NEXT(funcctx, result); + } + else + SRF_RETURN_DONE(funcctx); + +} diff --git a/contrib/pgcrypto/blf.c b/contrib/pgcrypto/blf.c index eb83f83..5b3bc1f 100644 --- a/contrib/pgcrypto/blf.c +++ b/contrib/pgcrypto/blf.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: blf.c,v 1.3 2000/06/17 23:36:22 provos Exp $ */ /* * Blowfish block cipher for OpenBSD diff --git a/contrib/pgcrypto/blf.h b/contrib/pgcrypto/blf.h index 7ee728e..81159b1 100644 --- a/contrib/pgcrypto/blf.h +++ b/contrib/pgcrypto/blf.h @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: blf.h,v 1.3 2001/05/15 02:40:35 deraadt Exp $ */ /* * Blowfish - a fast block cipher designed by Bruce Schneier diff --git a/contrib/pgcrypto/crypt-des.c b/contrib/pgcrypto/crypt-des.c index dcd875c..6ed7188 100644 --- a/contrib/pgcrypto/crypt-des.c +++ b/contrib/pgcrypto/crypt-des.c @@ -36,7 +36,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/secure/lib/libcrypt/crypt-des.c,v 1.12 1999/09/20 12:39:20 markm Exp $ * * This is an original implementation of the DES and the crypt(3) interfaces * by David Burren . diff --git a/contrib/pgcrypto/crypt-md5.c b/contrib/pgcrypto/crypt-md5.c index b2305ab..200e5ac 100644 --- a/contrib/pgcrypto/crypt-md5.c +++ b/contrib/pgcrypto/crypt-md5.c @@ -1,7 +1,7 @@ /* * File imported from FreeBSD, original by Poul-Henning Kamp. * - * $FreeBSD$ + * $FreeBSD: src/lib/libcrypt/crypt-md5.c,v 1.5 1999/12/17 20:21:45 peter Exp $ * * $PostgreSQL$ */ diff --git a/contrib/pgcrypto/expected/3des.out b/contrib/pgcrypto/expected/3des.out new file mode 100644 index 0000000..3e6a88e --- /dev/null +++ b/contrib/pgcrypto/expected/3des.out @@ -0,0 +1,69 @@ +-- +-- 3DES cipher +-- +-- test vector from somewhere +SELECT encode(encrypt( +decode('80 00 00 00 00 00 00 00', 'hex'), +decode('01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 01', 'hex'), +'3des-ecb/pad:none'), 'hex'); + encode +------------------ + 95f8a5e5dd31d900 +(1 row) + +-- val 95 F8 A5 E5 DD 31 D9 00 +select encode( encrypt('', 'foo', '3des'), 'hex'); + encode +------------------ + 752111e37a2d7ac3 +(1 row) + +-- 10 bytes key +select encode( encrypt('foo', '0123456789', '3des'), 'hex'); + encode +------------------ + d2fb8baa1717cb02 +(1 row) + +-- 22 bytes key +select encode( encrypt('foo', '0123456789012345678901', '3des'), 'hex'); + encode +------------------ + a44360e699269817 +(1 row) + +-- decrypt +select decrypt(encrypt('foo', '0123456', '3des'), '0123456', '3des'); + decrypt +--------- + foo +(1 row) + +-- iv +select encode(encrypt_iv('foo', '0123456', 'abcd', '3des'), 'hex'); + encode +------------------ + 50735067b073bb93 +(1 row) + +select decrypt_iv(decode('50735067b073bb93', 'hex'), '0123456', 'abcd', '3des'); + decrypt_iv +------------ + foo +(1 row) + +-- long message +select encode(encrypt('Lets try a longer message.', '0123456789012345678901', '3des'), 'hex'); + encode +------------------------------------------------------------------ + b71e3422269d0ded19468f33d65cd663c28e0871984792a7b3ba0ddcecec8d2c +(1 row) + +select decrypt(encrypt('Lets try a longer message.', '0123456789012345678901', '3des'), '0123456789012345678901', '3des'); + decrypt +---------------------------- + Lets try a longer message. +(1 row) + diff --git a/contrib/pgcrypto/expected/cast5.out b/contrib/pgcrypto/expected/cast5.out new file mode 100644 index 0000000..4ca824e --- /dev/null +++ b/contrib/pgcrypto/expected/cast5.out @@ -0,0 +1,86 @@ +-- +-- Cast5 cipher +-- +-- test vectors from RFC2144 +-- 128 bit key +SELECT encode(encrypt( +decode('01 23 45 67 89 AB CD EF', 'hex'), +decode('01 23 45 67 12 34 56 78 23 45 67 89 34 56 78 9A', 'hex'), +'cast5-ecb/pad:none'), 'hex'); + encode +------------------ + 238b4fe5847e44b2 +(1 row) + +-- result: 23 8B 4F E5 84 7E 44 B2 +-- 80 bit key +SELECT encode(encrypt( +decode('01 23 45 67 89 AB CD EF', 'hex'), +decode('01 23 45 67 12 34 56 78 23 45', 'hex'), +'cast5-ecb/pad:none'), 'hex'); + encode +------------------ + eb6a711a2c02271b +(1 row) + +-- result: EB 6A 71 1A 2C 02 27 1B +-- 40 bit key +SELECT encode(encrypt( +decode('01 23 45 67 89 AB CD EF', 'hex'), +decode('01 23 45 67 12', 'hex'), +'cast5-ecb/pad:none'), 'hex'); + encode +------------------ + 7ac816d16e9b302e +(1 row) + +-- result: 7A C8 16 D1 6E 9B 30 2E +-- cbc +-- empty data +select encode( encrypt('', 'foo', 'cast5'), 'hex'); + encode +------------------ + a48bd1aabde4de10 +(1 row) + +-- 10 bytes key +select encode( encrypt('foo', '0123456789', 'cast5'), 'hex'); + encode +------------------ + b07f19255e60cb6d +(1 row) + +-- decrypt +select decrypt(encrypt('foo', '0123456', 'cast5'), '0123456', 'cast5'); + decrypt +--------- + foo +(1 row) + +-- iv +select encode(encrypt_iv('foo', '0123456', 'abcd', 'cast5'), 'hex'); + encode +------------------ + 384a970695ce016a +(1 row) + +select decrypt_iv(decode('384a970695ce016a', 'hex'), + '0123456', 'abcd', 'cast5'); + decrypt_iv +------------ + foo +(1 row) + +-- long message +select encode(encrypt('Lets try a longer message.', '0123456789', 'cast5'), 'hex'); + encode +------------------------------------------------------------------ + 04fcffc91533e1505dadcb10766d9fed0937818e663e402384e049942ba60fff +(1 row) + +select decrypt(encrypt('Lets try a longer message.', '0123456789', 'cast5'), '0123456789', 'cast5'); + decrypt +---------------------------- + Lets try a longer message. +(1 row) + diff --git a/contrib/pgcrypto/expected/des.out b/contrib/pgcrypto/expected/des.out new file mode 100644 index 0000000..00513c4 --- /dev/null +++ b/contrib/pgcrypto/expected/des.out @@ -0,0 +1,61 @@ +-- +-- DES cipher +-- +-- no official test vectors atm +-- from blowfish.sql +SELECT encode(encrypt( +decode('0123456789abcdef', 'hex'), +decode('fedcba9876543210', 'hex'), +'des-ecb/pad:none'), 'hex'); + encode +------------------ + ed39d950fa74bcc4 +(1 row) + +-- empty data +select encode( encrypt('', 'foo', 'des'), 'hex'); + encode +------------------ + 752111e37a2d7ac3 +(1 row) + +-- 8 bytes key +select encode( encrypt('foo', '01234589', 'des'), 'hex'); + encode +------------------ + dec0f9c602b647a8 +(1 row) + +-- decrypt +select decrypt(encrypt('foo', '0123456', 'des'), '0123456', 'des'); + decrypt +--------- + foo +(1 row) + +-- iv +select encode(encrypt_iv('foo', '0123456', 'abcd', 'des'), 'hex'); + encode +------------------ + 50735067b073bb93 +(1 row) + +select decrypt_iv(decode('50735067b073bb93', 'hex'), '0123456', 'abcd', 'des'); + decrypt_iv +------------ + foo +(1 row) + +-- long message +select encode(encrypt('Lets try a longer message.', '01234567', 'des'), 'hex'); + encode +------------------------------------------------------------------ + 5ad146043e5f30967e06a0fcbae602daf4ff2a5fd0ed12d6c5913cf85f1e36ca +(1 row) + +select decrypt(encrypt('Lets try a longer message.', '01234567', 'des'), '01234567', 'des'); + decrypt +---------------------------- + Lets try a longer message. +(1 row) + diff --git a/contrib/pgcrypto/expected/pgp-armor.out b/contrib/pgcrypto/expected/pgp-armor.out new file mode 100644 index 0000000..60a89e5 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-armor.out @@ -0,0 +1,102 @@ +-- +-- PGP Armor +-- +select armor(''); + armor +--------------------------------------------------------------- + -----BEGIN PGP MESSAGE----- + +=twTO +-----END PGP MESSAGE----- + +(1 row) + +select armor('test'); + armor +------------------------------------------------------------------------ + -----BEGIN PGP MESSAGE----- + +dGVzdA== +=+G7Q +-----END PGP MESSAGE----- + +(1 row) + +select dearmor(armor('')); + dearmor +--------- + +(1 row) + +select dearmor(armor('zooka')); + dearmor +--------- + zooka +(1 row) + +select armor('0123456789abcdef0123456789abcdef0123456789abcdef +0123456789abcdef0123456789abcdef0123456789abcdef'); + armor +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + -----BEGIN PGP MESSAGE----- + +MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmCjAxMjM0NTY3 +ODlhYmNkZWYwMTIzNDU2Nzg5YWJjZGVmMDEyMzQ1Njc4OWFiY2RlZg== +=JFw5 +-----END PGP MESSAGE----- + +(1 row) + +-- lots formatting +select dearmor(' a pgp msg: + +-----BEGIN PGP MESSAGE----- +Comment: Some junk + +em9va2E= + + =D5cR + +-----END PGP MESSAGE-----'); + dearmor +--------- + zooka +(1 row) + +-- lots messages +select dearmor(' +wrong packet: + -----BEGIN PGP MESSAGE----- + + d3Jvbmc= + =vCYP + -----END PGP MESSAGE----- + +right packet: +-----BEGIN PGP MESSAGE----- + +cmlnaHQ= +=nbpj +-----END PGP MESSAGE----- + +use only first packet +-----BEGIN PGP MESSAGE----- + +d3Jvbmc= +=vCYP +-----END PGP MESSAGE----- +'); + dearmor +--------- + right +(1 row) + +-- bad crc +select dearmor(' +-----BEGIN PGP MESSAGE----- + +em9va2E= +=ZZZZ +-----END PGP MESSAGE----- +'); +ERROR: Corrupt ascii-armor diff --git a/contrib/pgcrypto/expected/pgp-compression.out b/contrib/pgcrypto/expected/pgp-compression.out new file mode 100644 index 0000000..32b350b --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-compression.out @@ -0,0 +1,50 @@ +-- +-- PGP compression support +-- +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- + +ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+ +DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA== +=tbSn +-----END PGP MESSAGE----- +'), 'key', 'expect-compress-algo=1'); + pgp_sym_decrypt +----------------- + Secret message +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'), + 'key', 'expect-compress-algo=0'); + pgp_sym_decrypt +----------------- + Secret message +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'), + 'key', 'expect-compress-algo=1'); + pgp_sym_decrypt +----------------- + Secret message +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'), + 'key', 'expect-compress-algo=2'); + pgp_sym_decrypt +----------------- + Secret message +(1 row) + +-- level=0 should turn compression off +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', + 'compress-algo=2, compress-level=0'), + 'key', 'expect-compress-algo=0'); + pgp_sym_decrypt +----------------- + Secret message +(1 row) + diff --git a/contrib/pgcrypto/expected/pgp-decrypt.out b/contrib/pgcrypto/expected/pgp-decrypt.out new file mode 100644 index 0000000..8ecb56f --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-decrypt.out @@ -0,0 +1,366 @@ +-- +-- pgp_descrypt tests +-- +-- Checking ciphers +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.blowfish.sha1.mdc.s2k3.z0 + +jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS +yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE= +=JcP+ +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest +UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA== +=XtrP +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k3.z0 + +jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI +5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g== +=rCZt +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k3.z0 + +jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/ +lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog== +=fB6S +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking MDC modes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.nomdc.s2k3.z0 + +jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+ +u9YkgfJfsuRJmgQ9tmo= +=60ui +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE +8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q== +=moGf +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking hashes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.md5.mdc.s2k3.z0 + +jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO +KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA== +=NyLk +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m +/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw== +=FxbQ +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking S2K modes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k0.z0 + +jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw +Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw== +=YvkV +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k1.z0 + +jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK +4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz +=dbXm +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl +z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg== +=VJKg +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k0.z0 + +jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E +Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw== +=cg+i +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k1.z0 + +jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9 +7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt +=aHmC +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k3.z0 + +jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv +q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw== +=K0LS +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k0.z0 + +jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu +rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w== +=RGts +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k1.z0 + +jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO ++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR +=SUrU +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k3.z0 + +jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+ +4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA== +=XZrG +-----END PGP MESSAGE----- +'), 'foobar'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking longer passwords +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi +tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw== +=XKKG +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6 +CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg== +=gWDh +-----END PGP MESSAGE----- +'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt +FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q== +=OxOF +-----END PGP MESSAGE----- +'), 'x'); + pgp_sym_decrypt +----------------- + Secret message. +(1 row) + +-- Checking various data +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8 +Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg== +=W/ik +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); + encode +------------------------------------------ + 0225e3ede6f2587b076d021a189ff60aad67e066 +(1 row) + +-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066 +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat2.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB +SaV9L04ky1qECNDx3XjnoKLC+H7IOQ== +=Fxen +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); + encode +------------------------------------------ + da39a3ee5e6b4b0d3255bfef95601890afd80709 +(1 row) + +-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709 +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat3.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8 +gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk +73Hb8m1yRhQK +=ivrD +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); + encode +------------------------------------------ + 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c +(1 row) + +-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c +-- Checking CRLF +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: crlf mess + +ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms +a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs +=mBP9 +-----END PGP MESSAGE----- +'), 'key', 'convert-crlf=0'), 'sha1'), 'hex'); + encode +------------------------------------------ + 9353062be7720f1446d30b9e75573a4833886784 +(1 row) + +-- expected: 9353062be7720f1446d30b9e75573a4833886784 +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: crlf mess + +ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms +a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs +=mBP9 +-----END PGP MESSAGE----- +'), 'key', 'convert-crlf=1'), 'sha1'), 'hex'); + encode +------------------------------------------ + 7efefcab38467f7484d6fa43dc86cf5281bd78e2 +(1 row) + +-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2 diff --git a/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out b/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out new file mode 100644 index 0000000..4122300 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-encrypt-DISABLED.out @@ -0,0 +1 @@ +-- no random source diff --git a/contrib/pgcrypto/expected/pgp-encrypt.out b/contrib/pgcrypto/expected/pgp-encrypt.out new file mode 100644 index 0000000..ab33a04 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-encrypt.out @@ -0,0 +1,189 @@ +-- +-- PGP encrypt +-- +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- check whether the defaults are ok +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), + 'key', 'expect-cipher-algo=aes128, + expect-disable-mdc=0, + expect-sess-key=0, + expect-s2k-mode=3, + expect-s2k-digest-algo=sha1, + expect-compress-algo=0 + '); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- maybe the expect- stuff simply does not work +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), + 'key', 'expect-cipher-algo=bf, + expect-disable-mdc=1, + expect-sess-key=1, + expect-s2k-mode=0, + expect-s2k-digest-algo=md5, + expect-compress-algo=1 + '); +NOTICE: pgp_decrypt: unexpected cipher_algo: expected 4 got 7 +NOTICE: pgp_decrypt: unexpected s2k_mode: expected 0 got 3 +NOTICE: pgp_decrypt: unexpected s2k_digest_algo: expected 1 got 2 +NOTICE: pgp_decrypt: unexpected use_sess_key: expected 1 got 0 +NOTICE: pgp_decrypt: unexpected disable_mdc: expected 1 got 0 +NOTICE: pgp_decrypt: unexpected compress_algo: expected 1 got 0 + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- bytea as text +select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz'); +ERROR: Not text data +-- text as bytea +select pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz'); + pgp_sym_decrypt_bytea +----------------------- + Text +(1 row) + +-- algorithm change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'), + 'key', 'expect-cipher-algo=bf'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'), + 'key', 'expect-cipher-algo=aes128'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'), + 'key', 'expect-cipher-algo=aes192'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- s2k change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'), + 'key', 'expect-s2k-mode=0'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'), + 'key', 'expect-s2k-mode=1'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'), + 'key', 'expect-s2k-mode=3'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- s2k digest change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'), + 'key', 'expect-s2k-digest-algo=md5'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'), + 'key', 'expect-s2k-digest-algo=sha1'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- sess key +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'), + 'key', 'expect-sess-key=0'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'), + 'key', 'expect-sess-key=1'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'), + 'key', 'expect-sess-key=1, expect-cipher-algo=bf'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'), + 'key', 'expect-sess-key=1, expect-cipher-algo=aes192'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'), + 'key', 'expect-sess-key=1, expect-cipher-algo=aes256'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- no mdc +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'), + 'key', 'expect-disable-mdc=1'); + pgp_sym_decrypt +----------------- + Secret. +(1 row) + +-- crlf +select encode(pgp_sym_decrypt_bytea( + pgp_sym_encrypt('1\n2\n3\r\n', 'key', 'convert-crlf=1'), + 'key'), 'hex'); + encode +---------------------- + 310d0a320d0a330d0d0a +(1 row) + +-- conversion should be lossless +select encode(digest(pgp_sym_decrypt( + pgp_sym_encrypt('\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'), + 'key', 'convert-crlf=1'), 'sha1'), 'hex') as result, + encode(digest('\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect; + result | expect +------------------------------------------+------------------------------------------ + 47bde5d88d6ef8770572b9cbb4278b402aa69966 | 47bde5d88d6ef8770572b9cbb4278b402aa69966 +(1 row) + diff --git a/contrib/pgcrypto/expected/pgp-info.out b/contrib/pgcrypto/expected/pgp-info.out new file mode 100644 index 0000000..1fe0088 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-info.out @@ -0,0 +1,78 @@ +-- +-- PGP info functions +-- +-- pgp_key_id +select pgp_key_id(dearmor(pubkey)) from keytbl where id=1; + pgp_key_id +------------------ + D936CF64BB73F466 +(1 row) + +select pgp_key_id(dearmor(pubkey)) from keytbl where id=2; + pgp_key_id +------------------ + 2C226E1FFE5CC7D4 +(1 row) + +select pgp_key_id(dearmor(pubkey)) from keytbl where id=3; + pgp_key_id +------------------ + B68504FD128E1FF9 +(1 row) + +select pgp_key_id(dearmor(pubkey)) from keytbl where id=4; -- should fail +ERROR: No encryption key found +select pgp_key_id(dearmor(pubkey)) from keytbl where id=5; + pgp_key_id +------------------ + D936CF64BB73F466 +(1 row) + +select pgp_key_id(dearmor(pubkey)) from keytbl where id=6; + pgp_key_id +------------------ + FD0206C409B74875 +(1 row) + +select pgp_key_id(dearmor(seckey)) from keytbl where id=1; + pgp_key_id +------------------ + D936CF64BB73F466 +(1 row) + +select pgp_key_id(dearmor(seckey)) from keytbl where id=2; + pgp_key_id +------------------ + 2C226E1FFE5CC7D4 +(1 row) + +select pgp_key_id(dearmor(seckey)) from keytbl where id=3; + pgp_key_id +------------------ + B68504FD128E1FF9 +(1 row) + +select pgp_key_id(dearmor(seckey)) from keytbl where id=4; -- should fail +ERROR: No encryption key found +select pgp_key_id(dearmor(seckey)) from keytbl where id=5; + pgp_key_id +------------------ + D936CF64BB73F466 +(1 row) + +select pgp_key_id(dearmor(seckey)) from keytbl where id=6; + pgp_key_id +------------------ + FD0206C409B74875 +(1 row) + +select pgp_key_id(dearmor(data)) as data_key_id +from encdata order by id; + data_key_id +------------------ + D936CF64BB73F466 + 2C226E1FFE5CC7D4 + B68504FD128E1FF9 + FD0206C409B74875 +(4 rows) + diff --git a/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out b/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out new file mode 100644 index 0000000..d35c097 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-pubkey-DISABLED.out @@ -0,0 +1 @@ +-- no bignum support diff --git a/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out b/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out new file mode 100644 index 0000000..7d16a43 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-pubkey-decrypt.out @@ -0,0 +1,555 @@ +-- +-- PGP Public Key Encryption +-- +-- As most of the low-level stuff is tested in symmetric key +-- tests, here's only public-key specific tests +create table keytbl ( + id int4, + name text, + pubkey text, + seckey text +); +create table encdata ( + id int4, + data text +); +insert into keytbl (id, name, pubkey, seckey) +values (1, 'elg1024', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx +MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV +AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc +AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd +ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P +sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI ++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9 +6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF +k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v +iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F +ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80 +=RWci +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQG7BELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQAAAnj4i4st+s+C6 +WKTIDcL1Iy0Saq8lCp60H0VsZ2FtYWwgMTAyNCA8dGVzdEBleGFtcGxlLm9yZz6I +XgQTEQIAHgUCQsghSAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAcKbwNGBdz +ZDrbAJ9cp6AsjOhiLxwznsMJheGf4xkH8wCfUPjMCLm4tAEnyYn2hDNt7CB8B6Kd +ATEEQsghShAEAIeUjW2yTALCfrEG3FhM3ZLvAHWAec2O0Mn/RDr59IN/W8wDYcZp +m+oG0ZUDdIqMppQ8K2kylAH7gmYDXIP9D7MRRm/Zw3L4yFfKnVaZ6tT7szBbgW5h +iOsHoOz49NXZT4jtMLdZS1/krm5Lam2MSPod9XN0Q2asY/igIMUfGDRjAAMGA/sE +LNh3tWefqeDkoDBEYjcxdGnGVGJnNHvv/eoHy9H7dyD/kkhaOoRAa5ClYWSqD0kk +a+SqTWhKG4XcbJyo1GsP6sqGhXDTM2+LBZPMKuVJQpEfoe9ruob/BbpXglfEiVE9 +VNiY7ZVyUdj3svYn4fK2X7ue1G3cHR2tL4lnOA4pYQAA9030E4u2ZKOfJBpUM+EM +m9VmsGjaQZV4teB0R/q3W8sRIYhJBBgRAgAJBQJCyCFKAhsMAAoJEBwpvA0YF3Nk +7a8AniFFotw1x2X+oryu3Q3nNtmxoKHpAJ9HU7jw7ydg33dI9J8gVkrmsSZ2/w== +=nvqq +-----END PGP PRIVATE KEY BLOCK----- +'); +insert into keytbl (id, name, pubkey, seckey) +values (2, 'elg2048', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n +vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke +5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO +RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij +8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl +Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt +J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/ +T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5 +0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6a7QjRWxnYW1hbCAy +MDQ4IDx0ZXN0MjA0OEBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgiCgIbAwYLCQgH +AwIDFQIDAxYCAQIeAQIXgAAKCRBI6c1W/qZo29PDAKCG724enIxRog1j+aeCp/uq +or6mbwCePuKy2/1kD1FvnhkZ/R5fpm+pdm25Ag0EQsgiIhAIAJI3Gb2Ehtz1taQ9 +AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMX +MhoWyw8ZF5Zs1mLIjFGVorePrm94N3MNPWM7x9M36bHUjx0vCZKFIhcGY1g+htE/ +QweaJzNVeA5z4qZmik41FbQyQSyHa3bOkTZu++/U6ghP+iDp5UDBjMTkVyqITUVN +gC+MR+da/I60irBVhue7younh4ovF+CrVDQJC06HZl6CAJJyA81SmRfi+dmKbbjZ +LF6rhz0norPjISJvkIqvdtM4VPBKI5wpgwCzpEqjuiKrAVujRT68zvBvJ4aVqb11 +k5QdJscAAwUH/jVJh0HbWAoiFTe+NvohfrA8vPcD0rtU3Y+siiqrabotnxJd2NuC +bxghJYGfNtnx0KDjFbCRKJVeTFok4UnuVYhXdH/c6i0/rCTNdeW2D6pmR4GfBozR +Pw/ARf+jONawGLyUj7uq13iquwMSE7VyNuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0R +QsetMq/iNBWraayKZnWUd+eQqNzE+NUo7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiF +Z1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qunfGW00ZMMTCWabg0ZgxPzMfMeIcm6525A +Yn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/RdWaISQQYEQIACQUCQsgiIgIbDAAKCRBI +6c1W/qZo25ZSAJ98WTrtl2HiX8ZqZq95v1+9cHtZPQCfZDoWQPybkNescLmXC7q5 +1kNTmEU= +=8QM5 +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQG7BELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n +vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke +5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO +RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij +8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl +Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt +J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/ +T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5 +0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6awAAn2F+iNBElfJS +8azqO/kEiIfpqu6/DQG0I0VsZ2FtYWwgMjA0OCA8dGVzdDIwNDhAZXhhbXBsZS5v +cmc+iF0EExECAB4FAkLIIgoCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQSOnN +Vv6maNvTwwCYkpcJmpl3aHCQdGomz7dFohDgjgCgiThZt2xTEi6GhBB1vuhk+f55 +n3+dAj0EQsgiIhAIAJI3Gb2Ehtz1taQ9AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D +29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMXMhoWyw8ZF5Zs1mLIjFGVorePrm94N3MN +PWM7x9M36bHUjx0vCZKFIhcGY1g+htE/QweaJzNVeA5z4qZmik41FbQyQSyHa3bO +kTZu++/U6ghP+iDp5UDBjMTkVyqITUVNgC+MR+da/I60irBVhue7younh4ovF+Cr +VDQJC06HZl6CAJJyA81SmRfi+dmKbbjZLF6rhz0norPjISJvkIqvdtM4VPBKI5wp +gwCzpEqjuiKrAVujRT68zvBvJ4aVqb11k5QdJscAAwUH/jVJh0HbWAoiFTe+Nvoh +frA8vPcD0rtU3Y+siiqrabotnxJd2NuCbxghJYGfNtnx0KDjFbCRKJVeTFok4Unu +VYhXdH/c6i0/rCTNdeW2D6pmR4GfBozRPw/ARf+jONawGLyUj7uq13iquwMSE7Vy +NuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0RQsetMq/iNBWraayKZnWUd+eQqNzE+NUo +7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiFZ1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qun +fGW00ZMMTCWabg0ZgxPzMfMeIcm6525AYn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/R +dWYAAVQKFPXbRaxbdArwRVXMzSD3qj/+VwwhwEDt8zmBGnlBfwVdkjQQrDUMmV1S +EwyISQQYEQIACQUCQsgiIgIbDAAKCRBI6c1W/qZo25ZSAJ4sgUfHTVsG/x3p3fcM +3b5R86qKEACggYKSwPWCs0YVRHOWqZY0pnHtLH8= +=3Dgk +-----END PGP PRIVATE KEY BLOCK----- +'); +insert into keytbl (id, name, pubkey, seckey) +values (3, 'elg4096', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY +05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz +2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98 +cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN +SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4 +18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG +7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt +q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh +uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk27QjRWxnYW1hbCA0 +MDk2IDx0ZXN0NDA5NkBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgjvAIbAwYLCQgH +AwIDFQIDAxYCAQIeAQIXgAAKCRBj+HX2P2d0oAEDAJ9lI+CNmb42z3+a6TnVusM6 +FI7oLwCfUwA1zEcRdsT3nIkoYh0iKxFSDFW5BA0EQsgkdhAQAJQbLXlgcJ/jq+Xh +Eujb77/eeftFJObNIRYD9fmJ7HFIXbUcknEpbs+cRH/nrj5dGSY3OT3jCXOUtvec +sCoX/CpZWL0oqDjAiZtNSFiulw5Gav4gHYkWKgKdSo+2rkavEPqKIVHvMeXaJtGT +d7v/AmL/P8T7gls93o5WFBOLtPbDvWqaKRy2U5TAhl1laiM0vGALRVjvSCgnGw9g +FpSnXbO3AfenUSjDzZujfGLHtU44ixHSS/D4DepiF3YaYLsN4CBqZRv6FbMZD5W3 +DnJY4kS1kH0MzdcF19TlcZ3itTCcGIt1tMKf84mccPoqdMzH7vumBGTeFEly5Afp +9berJcirqh2fzlunN0GS02z6SGWnjTbDlkNDxuxPSBbpcpNyD3jpYAUqSwRsZ/+5 +zkzcbGtDmvy9sJ5lAXkxGoIoQ1tEVX/LOHnh2NQHK8ourVOnr7MS0nozssITZJ5E +XqtHiREjiYEuPyZiVZKJHLWuYYaF+n40znnz3sJuXFRreHhHbbvRdlYUU5mJV+XZ +BLgKuS33NdpGeMIngnCc/9IQ6OZb6ixc94kbkd3w2PVr8CbKlu/IHTjWOO2mAo+D ++OydlYl23FiM3KOyMP1HcEOJMB/nwkMtrvd+522Lu9n77ktKfot9IPrQDIQTyXjR +3pCOFtCOBnk2tJHMPoG9jn9ah/LHAAMHEACDZ5I/MHGfmiKg2hrmqBu2J2j/deC8 +CpwcyDH1ovQ0gHvb9ESa+CVRU2Wdy2CD7Q9SmtMverB5eneL418iPVRcQdwRmQ2y +IH4udlBa6ce9HTUCaecAZ4/tYBnaC0Av/9l9tz14eYcwRMDpB+bnkhgF+PZ1KAfD +9wcY2aHbtsf3lZBc5h4owPJkxpe/BNzuJxW3q4VpSbLsZhwnCZ2wg7DRwP44wFIk +00ptmoBY59gsU6I40XtzrF8JDr0cA57xND5RY21Z8lnnYRE1Tc8h5REps9ZIxW3/ +yl91404bPLqxczpUHQAMSTAmBaStPYX1nS51uofOhLs5SKPCUmxfGKIOhsD0oLUn +78DnkONVGeXzBibSwwtbgfMzee4G8wSUfJ7w8WXz1TyanaGLnJ+DuKASSOrFoBCD +HEDuWZWgSL74NOQupFRk0gxOPmqU94Y8HziQWma/cETbmD83q8rxN+GM2oBxQkQG +xcbqMTHE7aVhV3tymbSWVaYhww3oIwsZS9oUIi1DnPEowS6CpVRrwdvLjLJnJzzV +O3AFPn9eZ1Q7R1tNx+zZ4OOfhvI/OlRJ3HBx2L53embkbdY9gFYCCdTjPyjKoDIx +kALgCajjCYMNUsAKNSd6mMCQ8TtvukSzkZS1RGKP27ohsdnzIVsiEAbxDMMcI4k1 +ul0LExUTCXSjeIhJBBgRAgAJBQJCyCR2AhsMAAoJEGP4dfY/Z3Sg19sAn0NDS8pb +qrMpQAxSb7zRTmcXEFd9AJ435H0ttP/NhLHXC9ezgbCMmpXMOQ== +=kRxT +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQG7BELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY +05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz +2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98 +cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN +SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4 +18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG +7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt +q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh +uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk2wAAoJCUNy6awTkw +XfbLbpqh0fvDst7jDLa0I0VsZ2FtYWwgNDA5NiA8dGVzdDQwOTZAZXhhbXBsZS5v +cmc+iF4EExECAB4FAkLII7wCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQY/h1 +9j9ndKABAwCeNEOVK87EzXYbtxYBsnjrUI948NIAn2+f3BXiBFDV5NvqPwIZ0m77 +Fwy4nQRMBELIJHYQEACUGy15YHCf46vl4RLo2++/3nn7RSTmzSEWA/X5iexxSF21 +HJJxKW7PnER/564+XRkmNzk94wlzlLb3nLAqF/wqWVi9KKg4wImbTUhYrpcORmr+ +IB2JFioCnUqPtq5GrxD6iiFR7zHl2ibRk3e7/wJi/z/E+4JbPd6OVhQTi7T2w71q +mikctlOUwIZdZWojNLxgC0VY70goJxsPYBaUp12ztwH3p1Eow82bo3xix7VOOIsR +0kvw+A3qYhd2GmC7DeAgamUb+hWzGQ+Vtw5yWOJEtZB9DM3XBdfU5XGd4rUwnBiL +dbTCn/OJnHD6KnTMx+77pgRk3hRJcuQH6fW3qyXIq6odn85bpzdBktNs+khlp402 +w5ZDQ8bsT0gW6XKTcg946WAFKksEbGf/uc5M3GxrQ5r8vbCeZQF5MRqCKENbRFV/ +yzh54djUByvKLq1Tp6+zEtJ6M7LCE2SeRF6rR4kRI4mBLj8mYlWSiRy1rmGGhfp+ +NM55897CblxUa3h4R2270XZWFFOZiVfl2QS4Crkt9zXaRnjCJ4JwnP/SEOjmW+os +XPeJG5Hd8Nj1a/AmypbvyB041jjtpgKPg/jsnZWJdtxYjNyjsjD9R3BDiTAf58JD +La73fudti7vZ++5LSn6LfSD60AyEE8l40d6QjhbQjgZ5NrSRzD6BvY5/WofyxwAD +BxAAg2eSPzBxn5oioNoa5qgbtido/3XgvAqcHMgx9aL0NIB72/REmvglUVNlnctg +g+0PUprTL3qweXp3i+NfIj1UXEHcEZkNsiB+LnZQWunHvR01AmnnAGeP7WAZ2gtA +L//Zfbc9eHmHMETA6Qfm55IYBfj2dSgHw/cHGNmh27bH95WQXOYeKMDyZMaXvwTc +7icVt6uFaUmy7GYcJwmdsIOw0cD+OMBSJNNKbZqAWOfYLFOiONF7c6xfCQ69HAOe +8TQ+UWNtWfJZ52ERNU3PIeURKbPWSMVt/8pfdeNOGzy6sXM6VB0ADEkwJgWkrT2F +9Z0udbqHzoS7OUijwlJsXxiiDobA9KC1J+/A55DjVRnl8wYm0sMLW4HzM3nuBvME +lHye8PFl89U8mp2hi5yfg7igEkjqxaAQgxxA7lmVoEi++DTkLqRUZNIMTj5qlPeG +PB84kFpmv3BE25g/N6vK8TfhjNqAcUJEBsXG6jExxO2lYVd7cpm0llWmIcMN6CML +GUvaFCItQ5zxKMEugqVUa8Hby4yyZyc81TtwBT5/XmdUO0dbTcfs2eDjn4byPzpU +Sdxwcdi+d3pm5G3WPYBWAgnU4z8oyqAyMZAC4Amo4wmDDVLACjUnepjAkPE7b7pE +s5GUtURij9u6IbHZ8yFbIhAG8QzDHCOJNbpdCxMVEwl0o3gAAckBdfKuasiNUn5G +L5XRnSvaOFzftr8zteOlZChCSNvzH5k+i1j7RJbWq06OeKRywPzjfjgM2MvRzI43 +ICeISQQYEQIACQUCQsgkdgIbDAAKCRBj+HX2P2d0oNfbAJ9+G3SeXrk+dWwo9EGi +hqMi2GVTsgCfeoQJPsc8FLYUgfymc/3xqAVLUtg= +=Gjq6 +-----END PGP PRIVATE KEY BLOCK----- +'); +insert into keytbl (id, name, pubkey, seckey) +values (4, 'rsa2048', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQELBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP +ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2 +55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx +5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K +MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz +R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYptB5SU0EgMjA0OCA8cnNhMjA0 +OEBleGFtcGxlLm9yZz6JATQEEwECAB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgEC +HgECF4AACgkQnc+OnJvTHyQqHwf8DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liR +nrLuVlLBpdO6CWmMUzfKRvyZlx54GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzw +bLZyM5Gb3lsE/FEmE7Dxw/0Utf59uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDP +D3dnU4uzKPhMcjnSN00pzjusP7C9NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv +9bgGWopumlOkt8Zu5YG6+CtTbJXprPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhv +S3NZKoJ/1DrGgoDAu1mGkM4KvLAxfDs/qQ9dZhtEmDbKPLTVEA== +=lR4n +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQOWBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP +ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2 +55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx +5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K +MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz +R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYpAAf/QZsrrz0c7dgWwGqMIpw6 +fP+/lLa74+fa2CFRWtYowEiKsfDg/wN7Ua07036dNhPa8aZPsU6SRzm5PybKOURe +D9pNt0FxJkX0j5pCWfjSJgTbc1rCdqZ/oyBk/U6pQtf//zfw3PbDl7I8TC6GOt2w +5NgcXdsWHP7LAmPctOVUyzFsenevR0MFTHkMbmKI1HpFm8XN/e1Fl+qIAD+OagTF +5B32VvpoJtkh5nxnIuToNJsa9Iy7F9MM2CeFOyTMihMcjXKBBUaAYoF115irBvqu +7N/qWmzqLg8yxBZ56mh6meCF3+67VA2y7fL8rhw2QuqgLg1JFlKAVL+9crCSrn// +GQQA1kT7FytW6BNOffblFYZkrJer3icoRDqa/ljgH/yVaWoVT1igy0E9XzYO7MwP +2usj/resLy0NC1qCthk51cZ/wthooMl88e5Wb4l5FYwBEac7muSBTo4W8cAH1hFj +TWL6XAGvEzGX3Mt9pn8uYGlQLZAhJoNCAU2EOCbN1PchDvsEAOWNKYesuUVk8+sQ +St0NDNhd9BWtTWTHkCZb1dKC3JTfr9PqkTBLrWFbYjkOtvdPAW7FDaXXXZfdH1jH +WfwP3Q+I6sqgSaWpCS4dBAns3/RVtO7czVgyIwma04iIvJqderYrfvkUq95KfwP2 +V8wXkhrPPPxyrg5y3wQlpY2jb5RBBAC17SK1ms+DBtck4vpdjp3SJ32SbyC/DU30 +89Q12j74S7Zdu1qZlKnvy3kWPYX/hMuSzGZ+mLVJNFEqH2X01aFzppYz0hdI9PGB +9tTFEqZWQL9ZkXfjc79Cgnt12pNukRbtw0N/kyutOdIFHVT79wVAd+powqziXJsC +Kc+4xjwSCkZitB5SU0EgMjA0OCA8cnNhMjA0OEBleGFtcGxlLm9yZz6JATQEEwEC +AB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQnc+OnJvTHyQqHwf8 +DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liRnrLuVlLBpdO6CWmMUzfKRvyZlx54 +GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzwbLZyM5Gb3lsE/FEmE7Dxw/0Utf59 +uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDPD3dnU4uzKPhMcjnSN00pzjusP7C9 +NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv9bgGWopumlOkt8Zu5YG6+CtTbJXp +rPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhvS3NZKoJ/1DrGgoDAu1mGkM4KvLAx +fDs/qQ9dZhtEmDbKPLTVEA== +=WKAv +-----END PGP PRIVATE KEY BLOCK----- +'); +insert into keytbl (id, name, pubkey, seckey) +values (5, 'psw-elg1024', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx +MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV +AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc +AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd +ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P +sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI ++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9 +6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF +k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v +iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F +ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80 +=RWci +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQHhBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQP4DAwL3TCgrYdj6 ++GAnoSqGa87twi8a6QRRYIlEx3ddUCDCjzkJmRfF+LFtvX3OtWWK0+Syi3kj2IK9 +YT7pF7QfRWxnYW1hbCAxMDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJC +yCFIAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAn1ynoCyM +6GIvHDOewwmF4Z/jGQfzAJ9Q+MwIubi0ASfJifaEM23sIHwHop0BVwRCyCFKEAQA +h5SNbbJMAsJ+sQbcWEzdku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioym +lDwraTKUAfuCZgNcg/0PsxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlP +iO0wt1lLX+SubktqbYxI+h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSg +MERiNzF0acZUYmc0e+/96gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxs +nKjUaw/qyoaFcNMzb4sFk8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey +9ifh8rZfu57UbdwdHa0viWc4Dilh/gMDAvdMKCth2Pr4YCCPsELdgJuzhGfDNRSg +nKMRWBWHSJRk6JmCjM1iJQNHc4mMhR8gvi2TeqYLOhYjcF7nr/LA+JvLV+adj/mI +SQQYEQIACQUCQsghSgIbDAAKCRAcKbwNGBdzZO2vAJ4hRaLcNcdl/qK8rt0N5zbZ +saCh6QCfR1O48O8nYN93SPSfIFZK5rEmdv8= +=Y6Qv +-----END PGP PRIVATE KEY BLOCK----- +'); +insert into keytbl (id, name, pubkey, seckey) +values (6, 'rsaenc2048', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQELBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj +UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW +czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT +4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ +dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4 +NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYptCVSU0EgMjA0OCBFbmMgPHJz +YTIwNDhlbmNAZXhhbXBsZS5vcmc+iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMV +AgMDFgIBAh4BAheAAAoJEMiZ6pNEGVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8 +JCaxHa31wH3PKqGtq2M+cpb2rXf7gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw ++IkhihEb5bWxQBNj+3zAFs1YX6v2HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzku +UOhcgfpBgIt3Q+MpT6M2+OIF7lVfSb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQ +RJ6DWH61F8fFIDJg1z+A/Obx4fqX6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO8 +0g/oVYBbuvOYedffDBeQarhERZ5W2TnIE+nqY61YOLBqosliygdZTXULzNi5AQsE +QuvaugEIAOuCJZdkzORA6e1lr81Lnr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPX +z1YIrMTu+1rMIiy10IWbA6zgMTpzPhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfK +lAXIxJurCHXTbEa+YvPdn76vJ3HsXOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zE +FZcAoKbFqAAjDLEai64SoOFh0W3CsD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9U +rHlolqYNhYze/uRLyfnUx9PN4r/GhEzauyDMV0smo91uB3aewPft+eCpmeWnu0PF +JVK4xyRmhIq2rVCw16a1pBJirvGM+y0ABimJAR8EGAECAAkFAkLr2roCGwwACgkQ +yJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+1iqYE0B0 +WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwarANhHxkWg +w/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx2eVM1k+X +dmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOyla+wJdro +PYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CChbt6LhKh +CLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug== +=pwU2 +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQOWBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj +UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW +czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT +4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ +dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4 +NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYpAAf9GuKpxrXp267eSPw9ZeSw +Ik6ob1I0MHbhhHeaXQnF0SuOViJ1+Bs74hUB3/F5fqrnjVLIS/ysYzegYpbpXOIa +MZwYcp2e+dpmVb7tkGQgzXH0igGtBQBqoSUVq9mG2XKPVh2JmiYgOH6GrHSGmnCq +GCgEK4ezSomB/3OtPFSjAxOlSw6dXSkapSxW3pEGvCdaWd9p8yl4rSpGsZEErPPL +uSbZZrHtWfgq5UXdPeE1UnMlBcvSruvpN4qgWMgSMs4d2lXvzXJLcht/nryP+atT +H1gwnRmlDCVv5BeJepKo3ORJDvcPlXkJPhqS9If3BhTqt6QgQEFI4aIYYZOZpZoi +2QQA2Zckzktmsc1MS04zS9gm1CbxM9d2KK8EOlh7fycRQhYYqqavhTBH2MgEp+Dd +ZtuEN5saNDe9x/fwi2ok1Bq6luGMWPZU/nZe7fxadzwfliy/qPzStWFW3vY9mMLu +6uEqgjin/lf4YrAswXDZaEc5e4GuNgGfwr27hpjxE1jg3PsEAPMqXEOMT2yh+yRu +DlLRbFhYOI4aUHY2CGoQQONnwv2O5gFvmOcPlg3J5lvnwlOYCx0c3bDxAtHyjPJq +FAZqcJBaB9RDhKHwlWDrbx/6FPH2SuKE+u4msIhPFin4V3FAP+yTem/TKrdnaWy6 +EUrhCWTXVRTijBaCudfjFd/ipHZbA/0dv7UAcoWK6kiVLzyE+jOvtN+ZxTzxq7CW +mlFPgAC966hgJmz9IXqadtMgPAoL3PK9q1DbPM3JhsQcJrNzTJqZrdN1/kPU0HHa ++aof1BVy3wSvp2mXgaRUULStyhUIyBRM6hAYp3/MoWEYn/bwr+zQkIU8Zsk6OsZ6 +q1xE3cowrUWFtCVSU0EgMjA0OCBFbmMgPHJzYTIwNDhlbmNAZXhhbXBsZS5vcmc+ +iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEMiZ6pNE +GVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8JCaxHa31wH3PKqGtq2M+cpb2rXf7 +gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw+IkhihEb5bWxQBNj+3zAFs1YX6v2 +HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzkuUOhcgfpBgIt3Q+MpT6M2+OIF7lVf +Sb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQRJ6DWH61F8fFIDJg1z+A/Obx4fqX +6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO80g/oVYBbuvOYedffDBeQarhERZ5W +2TnIE+nqY61YOLBqosliygdZTXULzNidA5YEQuvaugEIAOuCJZdkzORA6e1lr81L +nr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPXz1YIrMTu+1rMIiy10IWbA6zgMTpz +PhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfKlAXIxJurCHXTbEa+YvPdn76vJ3Hs +XOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zEFZcAoKbFqAAjDLEai64SoOFh0W3C +sD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9UrHlolqYNhYze/uRLyfnUx9PN4r/G +hEzauyDMV0smo91uB3aewPft+eCpmeWnu0PFJVK4xyRmhIq2rVCw16a1pBJirvGM ++y0ABikAB/oC3z7lv6sVg+ngjbpWy9lZu2/ECZ9FqViVz7bUkjfvSuowgpncryLW +4EpVV4U6mMSgU6kAi5VGT/BvYGSAtnqDWGiPs7Kk+h4Adz74bEAXzU280pNBtSfX +tGvzlS4a376KzYFSCJDRBdMebEhJMbY0wQmR8lTZu5JSUI4YYEuN0c7ckdsw8w42 +QWTLonG8HC6h8UPKS0EAcaCo7tFubMIesU6cWuTYucsHE+wjbADjuSNX968qczNe +NoL2BUznXOQoPu6HQO4/8cr7ib+VQkB2bHQcMoZazPUStIID1e4CL4XcxfuAmT8o +3XDvMLgVqNp5W2f8Mzmk3/DbtsLXLOv5BADsCzQpseC8ikSYJC72hcon1wlUmGeH +3qgGiiHhYXFa18xgI5juoO8DaWno0rPPlgr36Y8mSB5qjYHMXwjKnKyUmt11H+hU ++6uk4hq3Rjd8l+vfuOSr1xoTrtBUg9Rwfw6JVo0DC+8CWg4oBWsLXVM6KQXPFdJs +8kyFQplR/iP1XQQA/2tbDANjAYGNNDjJO9/0kEnSAUyYMasFJDrA2q17J5CroVQw +QpMmWwdDkRANUVPKnWHS5sS65BRc7UytKe2f3A3ZInGXJIK2Hl+TzapWYcYxql+4 +ol5mEDDMDbhEE8Wmj9KyB6iifdLI0K+yxNb9T4Jpj3J18+St+G8+9AcFcBEEAM1b +M9C+/05cnV8gjcByqH9M9ypo8fzPvMKVXWwCLQXpaL50QIkzLURkiMoEWrCdELaA +sVPotRzePTIQ1ooLeDxd1gRnDqjZiIR0kwmv6vq8tfzY96O2ZbGWFI5eth89aWEJ +WB8AR3zYcXpwJLwPuhXW2/NlZF0bclJ3jNzAfTIeQmeJAR8EGAECAAkFAkLr2roC +GwwACgkQyJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+ +1iqYE0B0WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwar +ANhHxkWgw/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx +2eVM1k+XdmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOy +la+wJdroPYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CC +hbt6LhKhCLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug== +=UKh3 +-----END PGP PRIVATE KEY BLOCK----- +'); +-- elg1024 / aes128 +insert into encdata (id, data) values (1, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQEOA9k2z2S7c/RmEAQAgVWW0DeLrZ+1thWJGBPp2WRFL9HeNqqWHbKJCXJbz1Uy +faUY7yxVvG5Eutmo+JMiY3mg23/DgVVXHQZsTWpGvGM6djgUNGKUjZDbW6Nog7Mr +e78IywattCOmgUP9vIwwg3OVjuDCN/nVirGQFnXpJBc8DzWqDMWRWDy1M0ZsK7AD +/2JTosSFxUdpON0DKtIY3GLzmh6Nk3iV0g8VgJKUBT1rhCXuMDj3snm//EMm7hTY +PlnObq4mIhgz8NqprmhooxnU0Kapofb3P3wCHPpU14zxhXY8iKO/3JhBq2uFcx4X +uBMwkW4AdNxY/mzJZELteTL8Tr0s7PISk+owb4URpG3n0jsBc0CVULxrjh5Ejkdw +wCM195J6+KbQxOOFQ0b3uOVvv4dEgd/hRERCOq5EPaFhlHegyYJ7YO842vnSDA== +=PABx +-----END PGP MESSAGE----- +'); +-- elg2048 / blowfish +insert into encdata (id, data) values (2, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQIOAywibh/+XMfUEAf+OINhBngEsw4a/IJIeJvUgv1gTQzBwOdQEuc/runr4Oa8 +Skw/Bj0X/zgABVZLem1a35NHaNwaQaCFwMQ41YyWCu+jTdsiyX/Nw0w8LKKz0rNC +vVpG6YuV7Turtsf8a5lXy1K0SHkLlgxQ6c76GS4gtSl5+bsL2+5R1gSRJ9NXqCQP +OHRipEiYwBPqr5R21ZG0FXXNKGOGkj6jt/M/wh3WVtAhYuBI+HPKRfAEjd/Pu/eD +e1zYtkH1dKKFmp44+nF0tTI274xpuso7ShfKYrOK3saFWrl0DWiWteUinjSA1YBY +m7dG7NZ8PW+g1SZWhEoPjEEEHz3kWMvlKheMRDudnQf/dDyX6kZVIAQF/5B012hq +QyVewgTGysowFIDn01uIewoEA9cASw699jw9IoJp+k5WZXnU+INllBLzQxniQCSu +iEcr0x3fYqNtj9QBfbIqyRcY6HTWcmzyOUeGaSyX76j+tRAvtVtXpraFFFnaHB70 +YpXTjLkp8EBafzMghFaKDeXlr2TG/T7rbwcwWrFIwPqEAUKWN5m97Q3eyo8/ioMd +YoFD64J9ovSsgbuU5IpIGAsjxK+NKzg/2STH7zZFEVCtgcIXsTHTZfiwS98/+1H9 +p1DIDaXIcUFV2ztmcKxh9gt2sXRz1W+x6D8O0k3nanU5yGG4miLKaq18fbcA0BD1 ++NIzAfelq6nvvxYKcGcamBMgLo5JkZOBHvyr6RsAKIT5QYc0QTjysTk9l0Am3gYc +G2pAE+3k +=TBHV +-----END PGP MESSAGE----- +'); +-- elg4096 / aes256 +insert into encdata (id, data) values (3, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQQOA7aFBP0Sjh/5EA/+JCgncc8IZmmRjPStWnGf9tVJhgHTn+smIclibGzs0deS +SPSCitzpblwbUDvu964+/5e5Q1l7rRuNN+AgETlEd4eppv7Swn2ChdgOXxRwukcT +Nh3G+PTFvD4ayi7w1db3qvXIt0MwN4Alt436wJmK1oz2Ka9IcyO+wHWrDy1nSGSx +z5x7YEj+EZPgWc/YAvudqE8Jpzd/OT5zSHN09UFkIAk6NxisKaIstbEGFgpqtoDZ +1SJM84XAdL2IcaJ3YY7k/yzwlawhsakKd4GSd5vWmAwvyzzbSiBMfKsDE16ePLNU +ZBF7CzmlCBPZ7YrFAHLpXBXXkCQvzD2BEYOjse50ZEfJ036T7950Ozcdy1EQbGon +nyQ4Gh0PBpnMcBuiXOceWuYzhlzFOzDtlVKdNTxFRDcbEyW2jo9xQYvCCLnYy8EH +2M7S8jCtVYJBbn63a82ELv+3+kWYcsvBJv2ZVBh4ncrBu9o0P+OYS7ApoOU+j6p2 ++t0RXHksqXS1YiUwYF5KSw09EbYMgNZ9G04Px/PxLU6fSC9iDrGX7Xt3kOUP0mku +C518fPckT0zzRXqfFruJNRzDytW50KxkOQZzU1/Az1YlYN9QzWeU4EtLPb2fftZo +D0qH/ln+f9Op5t6sD2fcxZVECU1b/bFtZsxvwH406YL+UQ7hU/XnZrzVVzODal8P +/j1hg7v7BdJqu1DTp9nFWUuwMFcYAczuXn29IG183NZ7Ts4whDeYEhS8eNoLPX4j +txY12ILD/w/3Q4LoW/hPa6OdfEzsn0U5GLf1WiGmJE1H6ft2U/xUnerc/u0kt+FU +WAisArd4MuKtf7B5Vu/VF3kUdrR0hTniUKUivmC4o1jSId31Dufxj4aadVyldXAr +6TNBcdyragZjxEZ6hsBCYzA0Rd1a8atd6OaQoIEEfAzCu5Ks29pydHErStYGjWJ1 +KA5KPLVvjbHpDmRhlCcm8vgpYQsBYEB5gE9fx5yCTlsVhCB6y23h7hfdMqerDqkO +ZOPsO5h+tiHCdIrQ36sMjuINy1/K2rYcXd+Crh2iHcfidpU9fvDz2ihTRNQlhjuT +0cQZM5JhctEx4VXF4LDctRhit7Hn0iqsk604woQfJVvP8O673xSXT/kBY0A/v9C0 +3C4YoFNeSaKwbfZQ/4u1ZFPJxK2IIJa8UGpyAUewLMlzGVVagljybv/f4Z9ERAhy +huq5sMmw8UPsrJF2TUGHz5WSIwoh0J/qovoQI09I9sdEnFczDvRavMO2Mldy3E5i +exz9oewtel6GOmsZQSYWT/vJzbYMmvHNmNpVwwoKrLV6oI3kyQ80GHBwI1WlwHoK +2iRB0w8q4VVvJeYAz8ZIp380cqC3pfO0uZsrOx4g3k4X0jsB5y7rF5xXcZfnVbvG +DYKcOy60/OHMWVvpw6trAoA+iP+cVWPtrbRvLglTVTfYmi1ToZDDipkALBhndQ== +=L/M/ +-----END PGP MESSAGE----- +'); +-- rsaenc2048 / aes128 +insert into encdata (id, data) values (4, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQEMA/0CBsQJt0h1AQf+JyYnCiortj26P11zk28MKOGfWpWyAhuIgwbJXsdQ+e6r +pEyyqs9GC6gI7SNF6+J8B/gsMwvkAL4FHAQCvA4ZZ6eeXR1Of4YG22JQGmpWVWZg +DTyfhA2vkczuqfAD2tgUpMT6sdyGkQ/fnQ0lknlfHgC5GRx7aavOoAKtMqiZW5PR +yae/qR48mjX7Mb+mLvbagv9mHEgQSmHwFpaq2k456BbcZ23bvCmBnCvqV/90Ggfb +VP6gkSoFVsJ19RHsOhW1dk9ehbl51WB3zUOO5FZWwUTY9DJvKblRK/frF0+CXjE4 +HfcZXHSpSjx4haGGTsMvEJ85qFjZpr0eTGOdY5cFhNJAAVP8MZfji7OhPRAoOOIK +eRGOCkao12pvPyFTFnPd5vqmyBbdNpK4Q0hS82ljugMJvM0p3vJZVzW402Kz6iBL +GQ== +=XHkF +-----END PGP MESSAGE----- +'); +-- successful decrypt +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=1 and encdata.id=1; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=2 and encdata.id=2; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=3 and encdata.id=3; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=6 and encdata.id=4; + pgp_pub_decrypt +----------------- + Secret message. +(1 row) + +-- wrong key +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=2 and encdata.id=1; +ERROR: Wrong key +-- sign-only key +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=4 and encdata.id=1; +ERROR: No encryption key found +-- password-protected secret key, no password +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=5 and encdata.id=1; +ERROR: Need password for secret key +-- password-protected secret key, wrong password +select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'foo') +from keytbl, encdata where keytbl.id=5 and encdata.id=1; +ERROR: Corrupt data +-- password-protected secret key, right password +select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool') +from keytbl, encdata where keytbl.id=5 and encdata.id=1; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + diff --git a/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out b/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out new file mode 100644 index 0000000..e222541 --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-pubkey-encrypt.out @@ -0,0 +1,68 @@ +-- +-- PGP Public Key Encryption +-- +-- successful encrypt/decrypt +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=2; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=3; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=6; + pgp_pub_decrypt +----------------- + Secret msg +(1 row) + +-- try with rsa-sign only +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=4; +ERROR: No encryption key found +-- try with secret key +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(seckey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; +ERROR: Refusing to encrypt with secret key +-- does text-to-bytea works +select pgp_pub_decrypt_bytea( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; + pgp_pub_decrypt_bytea +----------------------- + Secret msg +(1 row) + +-- and bytea-to-text? +select pgp_pub_decrypt( + pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; +ERROR: Not text data diff --git a/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out b/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out new file mode 100644 index 0000000..6f4eccd --- /dev/null +++ b/contrib/pgcrypto/expected/pgp-zlib-DISABLED.out @@ -0,0 +1 @@ +-- zlib is disabled diff --git a/contrib/pgcrypto/expected/sha2.out b/contrib/pgcrypto/expected/sha2.out new file mode 100644 index 0000000..20bdf0e --- /dev/null +++ b/contrib/pgcrypto/expected/sha2.out @@ -0,0 +1,108 @@ +-- +-- SHA2 family +-- +-- SHA256 +SELECT encode(digest('', 'sha256'), 'hex'); + encode +------------------------------------------------------------------ + e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +(1 row) + +SELECT encode(digest('a', 'sha256'), 'hex'); + encode +------------------------------------------------------------------ + ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb +(1 row) + +SELECT encode(digest('abc', 'sha256'), 'hex'); + encode +------------------------------------------------------------------ + ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad +(1 row) + +SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256'), 'hex'); + encode +------------------------------------------------------------------ + 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1 +(1 row) + +SELECT encode(digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256'), 'hex'); + encode +------------------------------------------------------------------ + f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e +(1 row) + +-- SHA384 +SELECT encode(digest('', 'sha384'), 'hex'); + encode +-------------------------------------------------------------------------------------------------- + 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b +(1 row) + +SELECT encode(digest('a', 'sha384'), 'hex'); + encode +-------------------------------------------------------------------------------------------------- + 54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31 +(1 row) + +SELECT encode(digest('abc', 'sha384'), 'hex'); + encode +-------------------------------------------------------------------------------------------------- + cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7 +(1 row) + +SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384'), 'hex'); + encode +-------------------------------------------------------------------------------------------------- + 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b +(1 row) + +SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384'), 'hex'); + encode +-------------------------------------------------------------------------------------------------- + 09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039 +(1 row) + +SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384'), 'hex'); + encode +-------------------------------------------------------------------------------------------------- + 3d208973ab3508dbbd7e2c2862ba290ad3010e4978c198dc4d8fd014e582823a89e16f9b2a7bbc1ac938e2d199e8bea4 +(1 row) + +-- SHA512 +SELECT encode(digest('', 'sha512'), 'hex'); + encode +---------------------------------------------------------------------------------------------------------------------------------- + cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e +(1 row) + +SELECT encode(digest('a', 'sha512'), 'hex'); + encode +---------------------------------------------------------------------------------------------------------------------------------- + 1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75 +(1 row) + +SELECT encode(digest('abc', 'sha512'), 'hex'); + encode +---------------------------------------------------------------------------------------------------------------------------------- + ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f +(1 row) + +SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512'), 'hex'); + encode +---------------------------------------------------------------------------------------------------------------------------------- + 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445 +(1 row) + +SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512'), 'hex'); + encode +---------------------------------------------------------------------------------------------------------------------------------- + 8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909 +(1 row) + +SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512'), 'hex'); + encode +---------------------------------------------------------------------------------------------------------------------------------- + 930d0cefcb30ff1133b6898121f1cf3d27578afcafe8677c5257cf069911f75d8f5831b56ebfda67b278e66dff8b84fe2b2870f742a580d8edb41987232850c9 +(1 row) + diff --git a/contrib/pgcrypto/fortuna.h b/contrib/pgcrypto/fortuna.h new file mode 100644 index 0000000..03e91a2 --- /dev/null +++ b/contrib/pgcrypto/fortuna.h @@ -0,0 +1,38 @@ +/* + * fortuna.c + * Fortuna PRNG. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#ifndef __FORTUNA_H +#define __FORTUNA_H + +void fortuna_get_bytes(unsigned len, uint8 *dst); +void fortuna_add_entropy(const uint8 *data, unsigned len); + +#endif diff --git a/contrib/pgcrypto/mbuf.c b/contrib/pgcrypto/mbuf.c new file mode 100644 index 0000000..2103609 --- /dev/null +++ b/contrib/pgcrypto/mbuf.c @@ -0,0 +1,563 @@ +/* + * mbuf.c + * Memory buffer operations. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" + +#define STEP (16*1024) + +struct MBuf +{ + uint8 *data; + uint8 *data_end; + uint8 *read_pos; + uint8 *buf_end; + int no_write:1; + int own_data:1; +}; + +int +mbuf_avail(MBuf * mbuf) +{ + return mbuf->data_end - mbuf->read_pos; +} + +int +mbuf_size(MBuf * mbuf) +{ + return mbuf->data_end - mbuf->data; +} + +int +mbuf_tell(MBuf * mbuf) +{ + return mbuf->read_pos - mbuf->data; +} + +int +mbuf_free(MBuf * mbuf) +{ + if (mbuf->own_data) + { + memset(mbuf->data, 0, mbuf->buf_end - mbuf->data); + px_free(mbuf->data); + } + px_free(mbuf); + return 0; +} + +static void +prepare_room(MBuf * mbuf, int block_len) +{ + uint8 *newbuf; + unsigned newlen; + + if (mbuf->data_end + block_len <= mbuf->buf_end) + return; + + newlen = (mbuf->buf_end - mbuf->data) + + ((block_len + STEP + STEP - 1) & -STEP); + + newbuf = px_realloc(mbuf->data, newlen); + + mbuf->buf_end = newbuf + newlen; + mbuf->data_end = newbuf + (mbuf->data_end - mbuf->data); + mbuf->read_pos = newbuf + (mbuf->read_pos - mbuf->data); + mbuf->data = newbuf; + + return; +} + +int +mbuf_append(MBuf * dst, const uint8 *buf, int len) +{ + if (dst->no_write) + { + px_debug("mbuf_append: no_write"); + return PXE_BUG; + } + + prepare_room(dst, len); + + memcpy(dst->data_end, buf, len); + dst->data_end += len; + + return 0; +} + +MBuf * +mbuf_create(int len) +{ + MBuf *mbuf; + + if (!len) + len = 8192; + + mbuf = px_alloc(sizeof *mbuf); + mbuf->data = px_alloc(len); + mbuf->buf_end = mbuf->data + len; + mbuf->data_end = mbuf->data; + mbuf->read_pos = mbuf->data; + + mbuf->no_write = 0; + mbuf->own_data = 1; + + return mbuf; +} + +MBuf * +mbuf_create_from_data(const uint8 *data, int len) +{ + MBuf *mbuf; + + mbuf = px_alloc(sizeof *mbuf); + mbuf->data = (uint8 *) data; + mbuf->buf_end = mbuf->data + len; + mbuf->data_end = mbuf->data + len; + mbuf->read_pos = mbuf->data; + + mbuf->no_write = 1; + mbuf->own_data = 0; + + return mbuf; +} + + +int +mbuf_grab(MBuf * mbuf, int len, uint8 **data_p) +{ + if (len > mbuf_avail(mbuf)) + len = mbuf_avail(mbuf); + + mbuf->no_write = 1; + + *data_p = mbuf->read_pos; + mbuf->read_pos += len; + return len; +} + +int +mbuf_rewind(MBuf * mbuf) +{ + mbuf->read_pos = mbuf->data; + return 0; +} + +int +mbuf_steal_data(MBuf * mbuf, uint8 **data_p) +{ + int len = mbuf_size(mbuf); + + mbuf->no_write = 1; + mbuf->own_data = 0; + + *data_p = mbuf->data; + + mbuf->data = mbuf->data_end = mbuf->read_pos = mbuf->buf_end = NULL; + + return len; +} + +/* + * PullFilter + */ + +struct PullFilter +{ + PullFilter *src; + const PullFilterOps *op; + int buflen; + uint8 *buf; + int pos; + void *priv; +}; + +int +pullf_create(PullFilter ** pf_p, const PullFilterOps * op, void *init_arg, PullFilter * src) +{ + PullFilter *pf; + void *priv; + int res; + + if (op->init != NULL) + { + res = op->init(&priv, init_arg, src); + if (res < 0) + return res; + } + else + { + priv = init_arg; + res = 0; + } + + pf = px_alloc(sizeof(*pf)); + memset(pf, 0, sizeof(*pf)); + pf->buflen = res; + pf->op = op; + pf->priv = priv; + pf->src = src; + if (pf->buflen > 0) + { + pf->buf = px_alloc(pf->buflen); + pf->pos = 0; + } + else + { + pf->buf = NULL; + pf->pos = 0; + } + *pf_p = pf; + return 0; +} + +void +pullf_free(PullFilter * pf) +{ + if (pf->op->free) + pf->op->free(pf->priv); + + if (pf->buf) + { + memset(pf->buf, 0, pf->buflen); + px_free(pf->buf); + } + + memset(pf, 0, sizeof(*pf)); + px_free(pf); +} + +/* may return less data than asked, 0 means eof */ +int +pullf_read(PullFilter * pf, int len, uint8 **data_p) +{ + int res; + + if (pf->op->pull) + { + if (pf->buflen && len > pf->buflen) + len = pf->buflen; + res = pf->op->pull(pf->priv, pf->src, len, data_p, + pf->buf, pf->buflen); + } + else + res = pullf_read(pf->src, len, data_p); + return res; +} + +int +pullf_read_max(PullFilter * pf, int len, uint8 **data_p, uint8 *tmpbuf) +{ + int res, + total; + uint8 *tmp; + + res = pullf_read(pf, len, data_p); + if (res <= 0 || res == len) + return res; + + /* read was shorter, use tmpbuf */ + memcpy(tmpbuf, *data_p, res); + *data_p = tmpbuf; + len -= res; + total = res; + + while (len > 0) + { + res = pullf_read(pf, len, &tmp); + if (res < 0) + { + /* so the caller must clear only on success */ + memset(tmpbuf, 0, total); + return res; + } + if (res == 0) + break; + memcpy(tmpbuf + total, tmp, res); + total += res; + } + return total; +} + +/* + * caller wants exatly len bytes and dont bother with references + */ +int +pullf_read_fixed(PullFilter * src, int len, uint8 *dst) +{ + int res; + uint8 *p; + + res = pullf_read_max(src, len, &p, dst); + if (res < 0) + return res; + if (res != len) + { + px_debug("pullf_read_fixed: need=%d got=%d", len, res); + return PXE_MBUF_SHORT_READ; + } + if (p != dst) + memcpy(dst, p, len); + return 0; +} + +/* + * read from MBuf + */ +static int +pull_from_mbuf(void *arg, PullFilter * src, int len, + uint8 **data_p, uint8 *buf, int buflen) +{ + MBuf *mbuf = arg; + + return mbuf_grab(mbuf, len, data_p); +} + +static const struct PullFilterOps mbuf_reader = { + NULL, pull_from_mbuf, NULL +}; + +int +pullf_create_mbuf_reader(PullFilter ** mp_p, MBuf * src) +{ + return pullf_create(mp_p, &mbuf_reader, src, NULL); +} + + +/* + * PushFilter + */ + +struct PushFilter +{ + PushFilter *next; + const PushFilterOps *op; + int block_size; + uint8 *buf; + int pos; + void *priv; +}; + +int +pushf_create(PushFilter ** mp_p, const PushFilterOps * op, void *init_arg, PushFilter * next) +{ + PushFilter *mp; + void *priv; + int res; + + if (op->init != NULL) + { + res = op->init(next, init_arg, &priv); + if (res < 0) + return res; + } + else + { + priv = init_arg; + res = 0; + } + + mp = px_alloc(sizeof(*mp)); + memset(mp, 0, sizeof(*mp)); + mp->block_size = res; + mp->op = op; + mp->priv = priv; + mp->next = next; + if (mp->block_size > 0) + { + mp->buf = px_alloc(mp->block_size); + mp->pos = 0; + } + else + { + mp->buf = NULL; + mp->pos = 0; + } + *mp_p = mp; + return 0; +} + +void +pushf_free(PushFilter * mp) +{ + if (mp->op->free) + mp->op->free(mp->priv); + + if (mp->buf) + { + memset(mp->buf, 0, mp->block_size); + px_free(mp->buf); + } + + memset(mp, 0, sizeof(*mp)); + px_free(mp); +} + +void +pushf_free_all(PushFilter * mp) +{ + PushFilter *tmp; + + while (mp) + { + tmp = mp->next; + pushf_free(mp); + mp = tmp; + } +} + +static int +wrap_process(PushFilter * mp, const uint8 *data, int len) +{ + int res; + + if (mp->op->push != NULL) + res = mp->op->push(mp->next, mp->priv, data, len); + else + res = pushf_write(mp->next, data, len); + if (res > 0) + return PXE_BUG; + return res; +} + +/* consumes all data, returns len on success */ +int +pushf_write(PushFilter * mp, const uint8 *data, int len) +{ + int need, + res; + + /* + * no buffering + */ + if (mp->block_size <= 0) + return wrap_process(mp, data, len); + + /* + * try to empty buffer + */ + need = mp->block_size - mp->pos; + if (need > 0) + { + if (len < need) + { + memcpy(mp->buf + mp->pos, data, len); + mp->pos += len; + return 0; + } + memcpy(mp->buf + mp->pos, data, need); + len -= need; + data += need; + } + + /* + * buffer full, process + */ + res = wrap_process(mp, mp->buf, mp->block_size); + if (res < 0) + return res; + mp->pos = 0; + + /* + * now process directly from data + */ + while (len > 0) + { + if (len > mp->block_size) + { + res = wrap_process(mp, data, mp->block_size); + if (res < 0) + return res; + data += mp->block_size; + len -= mp->block_size; + } + else + { + memcpy(mp->buf, data, len); + mp->pos += len; + break; + } + } + return 0; +} + +int +pushf_flush(PushFilter * mp) +{ + int res; + + while (mp) + { + if (mp->block_size > 0) + { + res = wrap_process(mp, mp->buf, mp->pos); + if (res < 0) + return res; + } + + if (mp->op->flush) + { + res = mp->op->flush(mp->next, mp->priv); + if (res < 0) + return res; + } + + mp = mp->next; + } + return 0; +} + + +/* + * write to MBuf + */ +static int +push_into_mbuf(PushFilter * next, void *arg, const uint8 *data, int len) +{ + int res = 0; + MBuf *mbuf = arg; + + if (len > 0) + res = mbuf_append(mbuf, data, len); + return res < 0 ? res : 0; +} + +static const struct PushFilterOps mbuf_filter = { + NULL, push_into_mbuf, NULL, NULL +}; + +int +pushf_create_mbuf_writer(PushFilter ** res, MBuf * dst) +{ + return pushf_create(res, &mbuf_filter, dst, NULL); +} diff --git a/contrib/pgcrypto/mbuf.h b/contrib/pgcrypto/mbuf.h new file mode 100644 index 0000000..3dae077 --- /dev/null +++ b/contrib/pgcrypto/mbuf.h @@ -0,0 +1,124 @@ +/* + * mbuf.h + * Memory buffer operations. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#ifndef __PX_MBUF_H +#define __PX_MBUF_H + +typedef struct MBuf MBuf; +typedef struct PushFilter PushFilter; +typedef struct PullFilter PullFilter; +typedef struct PushFilterOps PushFilterOps; +typedef struct PullFilterOps PullFilterOps; + +struct PushFilterOps +{ + /* + * should return needed buffer size, 0- no buffering, <0 on error if NULL, + * no buffering, and priv=init_arg + */ + int (*init) (PushFilter * next, void *init_arg, void **priv_p); + + /* + * send data to next. should consume all? if null, it will be simply + * copied (in-place) returns 0 on error + */ + int (*push) (PushFilter * next, void *priv, + const uint8 *src, int len); + int (*flush) (PushFilter * next, void *priv); + void (*free) (void *priv); +}; + +struct PullFilterOps +{ + /* + * should return needed buffer size, 0- no buffering, <0 on error if NULL, + * no buffering, and priv=init_arg + */ + int (*init) (void **priv_p, void *init_arg, PullFilter * src); + + /* + * request data from src, put result ptr to data_p can use ptr from src or + * use buf as work area if NULL in-place copy + */ + int (*pull) (void *priv, PullFilter * src, int len, + uint8 **data_p, uint8 *buf, int buflen); + void (*free) (void *priv); +}; + +/* + * Memory buffer + */ +MBuf *mbuf_create(int len); +MBuf *mbuf_create_from_data(const uint8 *data, int len); +int mbuf_tell(MBuf * mbuf); +int mbuf_avail(MBuf * mbuf); +int mbuf_size(MBuf * mbuf); +int mbuf_grab(MBuf * mbuf, int len, uint8 **data_p); +int mbuf_steal_data(MBuf * mbuf, uint8 **data_p); +int mbuf_append(MBuf * dst, const uint8 *buf, int cnt); +int mbuf_rewind(MBuf * mbuf); +int mbuf_free(MBuf * mbuf); + +/* + * Push filter + */ +int pushf_create(PushFilter ** res, const PushFilterOps * ops, void *init_arg, + PushFilter * next); +int pushf_write(PushFilter * mp, const uint8 *data, int len); +void pushf_free_all(PushFilter * mp); +void pushf_free(PushFilter * mp); +int pushf_flush(PushFilter * mp); + +int pushf_create_mbuf_writer(PushFilter ** mp_p, MBuf * mbuf); + +/* + * Pull filter + */ +int pullf_create(PullFilter ** res, const PullFilterOps * ops, + void *init_arg, PullFilter * src); +int pullf_read(PullFilter * mp, int len, uint8 **data_p); +int pullf_read_max(PullFilter * mp, int len, + uint8 **data_p, uint8 *tmpbuf); +void pullf_free(PullFilter * mp); +int pullf_read_fixed(PullFilter * src, int len, uint8 *dst); + +int pullf_create_mbuf_reader(PullFilter ** pf_p, MBuf * mbuf); + +#define GETBYTE(pf, dst) \ + do { \ + uint8 __b; \ + int __res = pullf_read_fixed(pf, 1, &__b); \ + if (__res < 0) \ + return __res; \ + (dst) = __b; \ + } while (0) + +#endif /* __PX_MBUF_H */ diff --git a/contrib/pgcrypto/md5.c b/contrib/pgcrypto/md5.c index 3a47a9c..1ec5740 100644 --- a/contrib/pgcrypto/md5.c +++ b/contrib/pgcrypto/md5.c @@ -1,4 +1,4 @@ -/* $KAME$ */ +/* $KAME: md5.c,v 1.3 2000/02/22 14:01:17 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. diff --git a/contrib/pgcrypto/md5.h b/contrib/pgcrypto/md5.h index 3be106e..eb803f5 100644 --- a/contrib/pgcrypto/md5.h +++ b/contrib/pgcrypto/md5.h @@ -1,5 +1,5 @@ /* $PostgreSQL$ */ -/* $KAME$ */ +/* $KAME: md5.h,v 1.3 2000/02/22 14:01:18 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. diff --git a/contrib/pgcrypto/misc.c b/contrib/pgcrypto/misc.c index 734ad9f..5c803c0 100644 --- a/contrib/pgcrypto/misc.c +++ b/contrib/pgcrypto/misc.c @@ -26,7 +26,7 @@ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/lib/libcrypt/misc.c,v 1.1 1999/09/20 12:45:49 markm Exp $ * */ diff --git a/contrib/pgcrypto/pgp-armor.c b/contrib/pgcrypto/pgp-armor.c new file mode 100644 index 0000000..b2e3ce0 --- /dev/null +++ b/contrib/pgcrypto/pgp-armor.c @@ -0,0 +1,383 @@ +/* + * pgp-armor.c + * PGP ascii-armor. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +/* + * BASE64 - duplicated :( + */ + +static const unsigned char _base64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int +b64_encode(const uint8 *src, unsigned len, uint8 *dst) +{ + uint8 *p, + *lend = dst + 76; + const uint8 *s, + *end = src + len; + int pos = 2; + unsigned long buf = 0; + + s = src; + p = dst; + + while (s < end) + { + buf |= *s << (pos << 3); + pos--; + s++; + + /* + * write it out + */ + if (pos < 0) + { + *p++ = _base64[(buf >> 18) & 0x3f]; + *p++ = _base64[(buf >> 12) & 0x3f]; + *p++ = _base64[(buf >> 6) & 0x3f]; + *p++ = _base64[buf & 0x3f]; + + pos = 2; + buf = 0; + } + if (p >= lend) + { + *p++ = '\n'; + lend = p + 76; + } + } + if (pos != 2) + { + *p++ = _base64[(buf >> 18) & 0x3f]; + *p++ = _base64[(buf >> 12) & 0x3f]; + *p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '='; + *p++ = '='; + } + + return p - dst; +} + +/* probably should use lookup table */ +static int +b64_decode(const uint8 *src, unsigned len, uint8 *dst) +{ + const uint8 *srcend = src + len, + *s = src; + uint8 *p = dst; + char c; + unsigned b = 0; + unsigned long buf = 0; + int pos = 0, + end = 0; + + while (s < srcend) + { + c = *s++; + if (c >= 'A' && c <= 'Z') + b = c - 'A'; + else if (c >= 'a' && c <= 'z') + b = c - 'a' + 26; + else if (c >= '0' && c <= '9') + b = c - '0' + 52; + else if (c == '+') + b = 62; + else if (c == '/') + b = 63; + else if (c == '=') + { + /* + * end sequence + */ + if (!end) + { + if (pos == 2) + end = 1; + else if (pos == 3) + end = 2; + else + return PXE_PGP_CORRUPT_ARMOR; + } + b = 0; + } + else if (c == ' ' || c == '\t' || c == '\n' || c == '\r') + continue; + else + return PXE_PGP_CORRUPT_ARMOR; + + /* + * add it to buffer + */ + buf = (buf << 6) + b; + pos++; + if (pos == 4) + { + *p++ = (buf >> 16) & 255; + if (end == 0 || end > 1) + *p++ = (buf >> 8) & 255; + if (end == 0 || end > 2) + *p++ = buf & 255; + buf = 0; + pos = 0; + } + } + + if (pos != 0) + return PXE_PGP_CORRUPT_ARMOR; + return p - dst; +} + +static unsigned +b64_enc_len(unsigned srclen) +{ + /* + * 3 bytes will be converted to 4, linefeed after 76 chars + */ + return (srclen + 2) * 4 / 3 + srclen / (76 * 3 / 4); +} + +static unsigned +b64_dec_len(unsigned srclen) +{ + return (srclen * 3) >> 2; +} + +/* + * PGP armor + */ + +static const char *armor_header = "-----BEGIN PGP MESSAGE-----\n\n"; +static const char *armor_footer = "\n-----END PGP MESSAGE-----\n"; + +/* CRC24 implementation from rfc2440 */ +#define CRC24_INIT 0x00b704ceL +#define CRC24_POLY 0x01864cfbL +static long +crc24(const uint8 *data, unsigned len) +{ + unsigned crc = CRC24_INIT; + int i; + + while (len--) + { + crc ^= (*data++) << 16; + for (i = 0; i < 8; i++) + { + crc <<= 1; + if (crc & 0x1000000) + crc ^= CRC24_POLY; + } + } + return crc & 0xffffffL; +} + +int +pgp_armor_encode(const uint8 *src, unsigned len, uint8 *dst) +{ + int n; + uint8 *pos = dst; + unsigned crc = crc24(src, len); + + n = strlen(armor_header); + memcpy(pos, armor_header, n); + pos += n; + + n = b64_encode(src, len, pos); + pos += n; + + if (*(pos - 1) != '\n') + *pos++ = '\n'; + + *pos++ = '='; + pos[3] = _base64[crc & 0x3f]; + crc >>= 6; + pos[2] = _base64[crc & 0x3f]; + crc >>= 6; + pos[1] = _base64[crc & 0x3f]; + crc >>= 6; + pos[0] = _base64[crc & 0x3f]; + pos += 4; + + n = strlen(armor_footer); + memcpy(pos, armor_footer, n); + pos += n; + + return pos - dst; +} + +static const uint8 * +find_str(const uint8 *data, const uint8 *data_end, const char *str, int strlen) +{ + const uint8 *p = data; + + if (!strlen) + return NULL; + if (data_end - data < strlen) + return NULL; + while (p < data_end) + { + p = memchr(p, str[0], data_end - p); + if (p == NULL) + return NULL; + if (p + strlen > data_end) + return NULL; + if (memcmp(p, str, strlen) == 0) + return p; + p++; + } + return NULL; +} + +static int +find_header(const uint8 *data, const uint8 *datend, + const uint8 **start_p, int is_end) +{ + const uint8 *p = data; + static const char *start_sep = "-----BEGIN"; + static const char *end_sep = "-----END"; + const char *sep = is_end ? end_sep : start_sep; + + /* find header line */ + while (1) + { + p = find_str(p, datend, sep, strlen(sep)); + if (p == NULL) + return PXE_PGP_CORRUPT_ARMOR; + /* it must start at beginning of line */ + if (p == data || *(p - 1) == '\n') + break; + p += strlen(sep); + } + *start_p = p; + p += strlen(sep); + + /* check if header text ok */ + for (; p < datend && *p != '-'; p++) + { + /* various junk can be there, but definitely not line-feed */ + if (*p >= ' ') + continue; + return PXE_PGP_CORRUPT_ARMOR; + } + if (datend - p < 5 || memcmp(p, sep, 5) != 0) + return PXE_PGP_CORRUPT_ARMOR; + p += 5; + + /* check if at end of line */ + if (p < datend) + { + if (*p != '\n' && *p != '\r') + return PXE_PGP_CORRUPT_ARMOR; + if (*p == '\r') + p++; + if (p < datend && *p == '\n') + p++; + } + return p - *start_p; +} + +int +pgp_armor_decode(const uint8 *src, unsigned len, uint8 *dst) +{ + const uint8 *p = src; + const uint8 *data_end = src + len; + long crc; + const uint8 *base64_start, + *armor_end; + const uint8 *base64_end = NULL; + uint8 buf[4]; + int hlen; + int res = PXE_PGP_CORRUPT_ARMOR; + + /* armor start */ + hlen = find_header(src, data_end, &p, 0); + if (hlen <= 0) + goto out; + p += hlen; + + /* armor end */ + hlen = find_header(p, data_end, &armor_end, 1); + if (hlen <= 0) + goto out; + + /* skip comments - find empty line */ + while (p < armor_end && *p != '\n' && *p != '\r') + { + p = memchr(p, '\n', armor_end - p); + if (!p) + goto out; + + /* step to start of next line */ + p++; + } + base64_start = p; + + /* find crc pos */ + for (p = armor_end; p >= base64_start; p--) + if (*p == '=') + { + base64_end = p - 1; + break; + } + if (base64_end == NULL) + goto out; + + /* decode crc */ + if (b64_decode(p + 1, 4, buf) != 3) + goto out; + crc = (((long) buf[0]) << 16) + (((long) buf[1]) << 8) + (long) buf[2]; + + /* decode data */ + res = b64_decode(base64_start, base64_end - base64_start, dst); + + /* check crc */ + if (res >= 0 && crc24(dst, res) != crc) + res = PXE_PGP_CORRUPT_ARMOR; +out: + return res; +} + +unsigned +pgp_armor_enc_len(unsigned len) +{ + return b64_enc_len(len) + strlen(armor_header) + strlen(armor_footer) + 16; +} + +unsigned +pgp_armor_dec_len(unsigned len) +{ + return b64_dec_len(len); +} diff --git a/contrib/pgcrypto/pgp-cfb.c b/contrib/pgcrypto/pgp-cfb.c new file mode 100644 index 0000000..16d0428 --- /dev/null +++ b/contrib/pgcrypto/pgp-cfb.c @@ -0,0 +1,265 @@ +/* + * pgp-cfb.c + * Implements both normal and PGP-specific CFB mode. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include "mbuf.h" +#include "px.h" +#include "pgp.h" + +typedef int (*mix_data_t) (PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst); + +struct PGP_CFB +{ + PX_Cipher *ciph; + int block_size; + int pos; + int block_no; + int resync; + uint8 fr[PGP_MAX_BLOCK]; + uint8 fre[PGP_MAX_BLOCK]; + uint8 encbuf[PGP_MAX_BLOCK]; +}; + +int +pgp_cfb_create(PGP_CFB ** ctx_p, int algo, const uint8 *key, int key_len, + int resync, uint8 *iv) +{ + int res; + PX_Cipher *ciph; + PGP_CFB *ctx; + + res = pgp_load_cipher(algo, &ciph); + if (res < 0) + return res; + + res = px_cipher_init(ciph, key, key_len, NULL); + if (res < 0) + { + px_cipher_free(ciph); + return res; + } + + ctx = px_alloc(sizeof(*ctx)); + memset(ctx, 0, sizeof(*ctx)); + ctx->ciph = ciph; + ctx->block_size = px_cipher_block_size(ciph); + ctx->resync = resync; + + if (iv) + memcpy(ctx->fr, iv, ctx->block_size); + + *ctx_p = ctx; + return 0; +} + +void +pgp_cfb_free(PGP_CFB * ctx) +{ + px_cipher_free(ctx->ciph); + memset(ctx, 0, sizeof(*ctx)); + px_free(ctx); +} + +/* + * Data processing for normal CFB. (PGP_PKT_SYMENCRYPTED_DATA_MDC) + */ +static int +mix_encrypt_normal(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst) +{ + int i; + + for (i = ctx->pos; i < ctx->pos + len; i++) + *dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++); + ctx->pos += len; + return len; +} + +static int +mix_decrypt_normal(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst) +{ + int i; + + for (i = ctx->pos; i < ctx->pos + len; i++) + { + ctx->encbuf[i] = *data++; + *dst++ = ctx->fre[i] ^ ctx->encbuf[i]; + } + ctx->pos += len; + return len; +} + +/* + * Data processing for old PGP CFB mode. (PGP_PKT_SYMENCRYPTED_DATA) + * + * The goal is to hide the horror from the rest of the code, + * thus its all concentrated here. + */ +static int +mix_encrypt_resync(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst) +{ + int i, + n; + + /* block #2 is 2 bytes long */ + if (ctx->block_no == 2) + { + n = 2 - ctx->pos; + if (len < n) + n = len; + for (i = ctx->pos; i < ctx->pos + n; i++) + *dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++); + + ctx->pos += n; + len -= n; + + if (ctx->pos == 2) + { + memcpy(ctx->fr, ctx->encbuf + 2, ctx->block_size - 2); + memcpy(ctx->fr + ctx->block_size - 2, ctx->encbuf, 2); + ctx->pos = 0; + return n; + } + } + for (i = ctx->pos; i < ctx->pos + len; i++) + *dst++ = ctx->encbuf[i] = ctx->fre[i] ^ (*data++); + ctx->pos += len; + return len; +} + +static int +mix_decrypt_resync(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst) +{ + int i, + n; + + /* block #2 is 2 bytes long */ + if (ctx->block_no == 2) + { + n = 2 - ctx->pos; + if (len < n) + n = len; + for (i = ctx->pos; i < ctx->pos + n; i++) + { + ctx->encbuf[i] = *data++; + *dst++ = ctx->fre[i] ^ ctx->encbuf[i]; + } + ctx->pos += n; + len -= n; + + if (ctx->pos == 2) + { + memcpy(ctx->fr, ctx->encbuf + 2, ctx->block_size - 2); + memcpy(ctx->fr + ctx->block_size - 2, ctx->encbuf, 2); + ctx->pos = 0; + return n; + } + } + for (i = ctx->pos; i < ctx->pos + len; i++) + { + ctx->encbuf[i] = *data++; + *dst++ = ctx->fre[i] ^ ctx->encbuf[i]; + } + ctx->pos += len; + return len; +} + +/* + * common code for both encrypt and decrypt. + */ +static int +cfb_process(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst, + mix_data_t mix_data) +{ + int n; + int res; + + while (len > 0 && ctx->pos > 0) + { + n = ctx->block_size - ctx->pos; + if (len < n) + n = len; + + n = mix_data(ctx, data, n, dst); + data += n; + dst += n; + len -= n; + + if (ctx->pos == ctx->block_size) + { + memcpy(ctx->fr, ctx->encbuf, ctx->block_size); + ctx->pos = 0; + } + } + + while (len > 0) + { + px_cipher_encrypt(ctx->ciph, ctx->fr, ctx->block_size, ctx->fre); + if (ctx->block_no < 5) + ctx->block_no++; + + n = ctx->block_size; + if (len < n) + n = len; + + res = mix_data(ctx, data, n, dst); + data += res; + dst += res; + len -= res; + + if (ctx->pos == ctx->block_size) + { + memcpy(ctx->fr, ctx->encbuf, ctx->block_size); + ctx->pos = 0; + } + } + return 0; +} + +/* + * public interface + */ + +int +pgp_cfb_encrypt(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst) +{ + mix_data_t mix = ctx->resync ? mix_encrypt_resync : mix_encrypt_normal; + + return cfb_process(ctx, data, len, dst, mix); +} + +int +pgp_cfb_decrypt(PGP_CFB * ctx, const uint8 *data, int len, uint8 *dst) +{ + mix_data_t mix = ctx->resync ? mix_decrypt_resync : mix_decrypt_normal; + + return cfb_process(ctx, data, len, dst, mix); +} diff --git a/contrib/pgcrypto/pgp-compress.c b/contrib/pgcrypto/pgp-compress.c new file mode 100644 index 0000000..ceb6fdf --- /dev/null +++ b/contrib/pgcrypto/pgp-compress.c @@ -0,0 +1,329 @@ +/* + * pgp-compress.c + * ZIP and ZLIB compression via zlib. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include "mbuf.h" +#include "px.h" +#include "pgp.h" + + +/* + * Compressed pkt writer + */ + +#ifndef DISABLE_ZLIB + +#include + +#define ZIP_OUT_BUF 8192 +#define ZIP_IN_BLOCK 8192 + +struct ZipStat +{ + uint8 type; + int buf_len; + int hdr_done; + z_stream stream; + uint8 buf[ZIP_OUT_BUF]; +}; + +static void * +z_alloc(void *priv, unsigned n_items, unsigned item_len) +{ + return px_alloc(n_items * item_len); +} + +static void +z_free(void *priv, void *addr) +{ + px_free(addr); +} + +static int +compress_init(PushFilter * next, void *init_arg, void **priv_p) +{ + int res; + struct ZipStat *st; + PGP_Context *ctx = init_arg; + uint8 type = ctx->compress_algo; + + if (type != PGP_COMPR_ZLIB && type != PGP_COMPR_ZIP) + return PXE_PGP_UNSUPPORTED_COMPR; + + /* + * init + */ + st = px_alloc(sizeof(*st)); + memset(st, 0, sizeof(*st)); + st->buf_len = ZIP_OUT_BUF; + st->stream.zalloc = z_alloc; + st->stream.zfree = z_free; + + if (type == PGP_COMPR_ZIP) + res = deflateInit2(&st->stream, ctx->compress_level, + Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY); + else + res = deflateInit(&st->stream, ctx->compress_level); + if (res != Z_OK) + { + px_free(st); + return PXE_PGP_COMPRESSION_ERROR; + } + *priv_p = st; + + return ZIP_IN_BLOCK; +} + +/* writes compressed data packet */ + +/* cant handle zero-len incoming data, but shouldnt */ +static int +compress_process(PushFilter * next, void *priv, const uint8 *data, int len) +{ + int res, + n_out; + struct ZipStat *st = priv; + + /* + * process data + */ + while (len > 0) + { + st->stream.next_in = (void *) data; + st->stream.avail_in = len; + st->stream.next_out = st->buf; + st->stream.avail_out = st->buf_len; + res = deflate(&st->stream, 0); + if (res != Z_OK) + return PXE_PGP_COMPRESSION_ERROR; + + n_out = st->buf_len - st->stream.avail_out; + if (n_out > 0) + { + res = pushf_write(next, st->buf, n_out); + if (res < 0) + return res; + } + len = st->stream.avail_in; + } + + return 0; +} + +static int +compress_flush(PushFilter * next, void *priv) +{ + int res, + zres, + n_out; + struct ZipStat *st = priv; + + st->stream.next_in = NULL; + st->stream.avail_in = 0; + while (1) + { + st->stream.next_out = st->buf; + st->stream.avail_out = st->buf_len; + zres = deflate(&st->stream, Z_FINISH); + if (zres != Z_STREAM_END && zres != Z_OK) + return PXE_PGP_COMPRESSION_ERROR; + n_out = st->buf_len - st->stream.avail_out; + if (n_out > 0) + { + res = pushf_write(next, st->buf, n_out); + if (res < 0) + return res; + } + if (zres == Z_STREAM_END) + break; + } + return 0; +} + +static void +compress_free(void *priv) +{ + struct ZipStat *st = priv; + + deflateEnd(&st->stream); + memset(st, 0, sizeof(*st)); + px_free(st); +} + +static const PushFilterOps + compress_filter = { + compress_init, compress_process, compress_flush, compress_free +}; + +int +pgp_compress_filter(PushFilter ** res, PGP_Context * ctx, PushFilter * dst) +{ + return pushf_create(res, &compress_filter, ctx, dst); +} + +/* + * Decompress + */ +struct DecomprData +{ + int buf_len; /* = ZIP_OUT_BUF */ + int buf_data; /* available data */ + uint8 *pos; + z_stream stream; + int eof; + uint8 buf[ZIP_OUT_BUF]; +}; + +static int +decompress_init(void **priv_p, void *arg, PullFilter * src) +{ + PGP_Context *ctx = arg; + struct DecomprData *dec; + int res; + + if (ctx->compress_algo != PGP_COMPR_ZLIB + && ctx->compress_algo != PGP_COMPR_ZIP) + return PXE_PGP_UNSUPPORTED_COMPR; + + dec = px_alloc(sizeof(*dec)); + memset(dec, 0, sizeof(*dec)); + dec->buf_len = ZIP_OUT_BUF; + *priv_p = dec; + + dec->stream.zalloc = z_alloc; + dec->stream.zfree = z_free; + + if (ctx->compress_algo == PGP_COMPR_ZIP) + res = inflateInit2(&dec->stream, -15); + else + res = inflateInit(&dec->stream); + if (res != Z_OK) + { + px_free(dec); + px_debug("decompress_init: inflateInit error"); + return PXE_PGP_COMPRESSION_ERROR; + } + + return 0; +} + +static int +decompress_read(void *priv, PullFilter * src, int len, + uint8 **data_p, uint8 *buf, int buflen) +{ + int res; + int flush; + struct DecomprData *dec = priv; + +restart: + if (dec->buf_data > 0) + { + if (len > dec->buf_data) + len = dec->buf_data; + *data_p = dec->pos; + dec->pos += len; + dec->buf_data -= len; + return len; + } + + if (dec->eof) + return 0; + + if (dec->stream.avail_in == 0) + { + uint8 *tmp; + + res = pullf_read(src, 8192, &tmp); + if (res < 0) + return res; + dec->stream.next_in = tmp; + dec->stream.avail_in = res; + } + + dec->stream.next_out = dec->buf; + dec->stream.avail_out = dec->buf_len; + dec->pos = dec->buf; + + /* + * Z_SYNC_FLUSH is tell zlib to output as much as possible. It should do + * it anyway (Z_NO_FLUSH), but seems to reserve the right not to. So lets + * follow the API. + */ + flush = dec->stream.avail_in ? Z_SYNC_FLUSH : Z_FINISH; + res = inflate(&dec->stream, flush); + if (res != Z_OK && res != Z_STREAM_END) + { + px_debug("decompress_read: inflate error: %d", res); + return PXE_PGP_CORRUPT_DATA; + } + + dec->buf_data = dec->buf_len - dec->stream.avail_out; + if (res == Z_STREAM_END) + dec->eof = 1; + goto restart; +} + +static void +decompress_free(void *priv) +{ + struct DecomprData *dec = priv; + + inflateEnd(&dec->stream); + memset(dec, 0, sizeof(*dec)); + px_free(dec); +} + +static const PullFilterOps + decompress_filter = { + decompress_init, decompress_read, decompress_free +}; + +int +pgp_decompress_filter(PullFilter ** res, PGP_Context * ctx, PullFilter * src) +{ + return pullf_create(res, &decompress_filter, ctx, src); +} +#else /* DISABLE_ZLIB */ + +int +pgp_compress_filter(PushFilter ** res, PGP_Context * ctx, PushFilter * dst) +{ + return PXE_PGP_UNSUPPORTED_COMPR; +} + +int +pgp_decompress_filter(PullFilter ** res, PGP_Context * ctx, PullFilter * src) +{ + return PXE_PGP_UNSUPPORTED_COMPR; +} + +#endif diff --git a/contrib/pgcrypto/pgp-encrypt.c b/contrib/pgcrypto/pgp-encrypt.c new file mode 100644 index 0000000..882e00a --- /dev/null +++ b/contrib/pgcrypto/pgp-encrypt.c @@ -0,0 +1,708 @@ +/* + * pgp-encrypt.c + * OpenPGP encrypt. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include + +#include "mbuf.h" +#include "px.h" +#include "pgp.h" + + +#define MDC_DIGEST_LEN 20 +#define STREAM_ID 0xE0 +#define STREAM_BLOCK_SHIFT 14 + +static uint8 * +render_newlen(uint8 *h, int len) +{ + if (len <= 191) + { + *h++ = len & 255; + } + else if (len > 191 && len <= 8383) + { + *h++ = ((len - 192) >> 8) + 192; + *h++ = (len - 192) & 255; + } + else + { + *h++ = 255; + *h++ = (len >> 24) & 255; + *h++ = (len >> 16) & 255; + *h++ = (len >> 8) & 255; + *h++ = len & 255; + } + return h; +} + +static int +write_tag_only(PushFilter * dst, int tag) +{ + uint8 hdr = 0xC0 | tag; + + return pushf_write(dst, &hdr, 1); +} + +static int +write_normal_header(PushFilter * dst, int tag, int len) +{ + uint8 hdr[8]; + uint8 *h = hdr; + + *h++ = 0xC0 | tag; + h = render_newlen(h, len); + return pushf_write(dst, hdr, h - hdr); +} + + +/* + * MAC writer + */ + +static int +mdc_init(PushFilter * dst, void *init_arg, void **priv_p) +{ + int res; + PX_MD *md; + + res = pgp_load_digest(PGP_DIGEST_SHA1, &md); + if (res < 0) + return res; + + *priv_p = md; + return 0; +} + +static int +mdc_write(PushFilter * dst, void *priv, const uint8 *data, int len) +{ + PX_MD *md = priv; + + px_md_update(md, data, len); + return pushf_write(dst, data, len); +} + +static int +mdc_flush(PushFilter * dst, void *priv) +{ + int res; + uint8 pkt[2 + MDC_DIGEST_LEN]; + PX_MD *md = priv; + + /* + * create mdc pkt + */ + pkt[0] = 0xD3; + pkt[1] = 0x14; /* MDC_DIGEST_LEN */ + px_md_update(md, pkt, 2); + px_md_finish(md, pkt + 2); + + res = pushf_write(dst, pkt, 2 + MDC_DIGEST_LEN); + memset(pkt, 0, 2 + MDC_DIGEST_LEN); + return res; +} + +static void +mdc_free(void *priv) +{ + PX_MD *md = priv; + + px_md_free(md); +} + +static const PushFilterOps mdc_filter = { + mdc_init, mdc_write, mdc_flush, mdc_free +}; + + +/* + * Encrypted pkt writer + */ +#define ENCBUF 8192 +struct EncStat +{ + PGP_CFB *ciph; + uint8 buf[ENCBUF]; +}; + +static int +encrypt_init(PushFilter * next, void *init_arg, void **priv_p) +{ + struct EncStat *st; + PGP_Context *ctx = init_arg; + PGP_CFB *ciph; + int resync = 1; + int res; + + /* should we use newer packet format? */ + if (ctx->disable_mdc == 0) + { + uint8 ver = 1; + + resync = 0; + res = pushf_write(next, &ver, 1); + if (res < 0) + return res; + } + res = pgp_cfb_create(&ciph, ctx->cipher_algo, + ctx->sess_key, ctx->sess_key_len, resync, NULL); + if (res < 0) + return res; + + st = px_alloc(sizeof(*st)); + memset(st, 0, sizeof(*st)); + st->ciph = ciph; + + *priv_p = st; + return ENCBUF; +} + +static int +encrypt_process(PushFilter * next, void *priv, const uint8 *data, int len) +{ + int res; + struct EncStat *st = priv; + int avail = len; + + while (avail > 0) + { + int tmplen = avail > ENCBUF ? ENCBUF : avail; + + res = pgp_cfb_encrypt(st->ciph, data, tmplen, st->buf); + if (res < 0) + return res; + + res = pushf_write(next, st->buf, tmplen); + if (res < 0) + return res; + + data += tmplen; + avail -= tmplen; + } + return 0; +} + +static void +encrypt_free(void *priv) +{ + struct EncStat *st = priv; + + memset(st, 0, sizeof(*st)); + px_free(st); +} + +static const PushFilterOps encrypt_filter = { + encrypt_init, encrypt_process, NULL, encrypt_free +}; + +/* + * Write Streamable pkts + */ + +struct PktStreamStat +{ + int final_done; + int pkt_block; +}; + +static int +pkt_stream_init(PushFilter * next, void *init_arg, void **priv_p) +{ + struct PktStreamStat *st; + + st = px_alloc(sizeof(*st)); + st->final_done = 0; + st->pkt_block = 1 << STREAM_BLOCK_SHIFT; + *priv_p = st; + + return st->pkt_block; +} + +static int +pkt_stream_process(PushFilter * next, void *priv, const uint8 *data, int len) +{ + int res; + uint8 hdr[8]; + uint8 *h = hdr; + struct PktStreamStat *st = priv; + + if (st->final_done) + return PXE_BUG; + + if (len == st->pkt_block) + *h++ = STREAM_ID | STREAM_BLOCK_SHIFT; + else + { + h = render_newlen(h, len); + st->final_done = 1; + } + + res = pushf_write(next, hdr, h - hdr); + if (res < 0) + return res; + + return pushf_write(next, data, len); +} + +static int +pkt_stream_flush(PushFilter * next, void *priv) +{ + int res; + uint8 hdr[8]; + uint8 *h = hdr; + struct PktStreamStat *st = priv; + + /* stream MUST end with normal packet. */ + if (!st->final_done) + { + h = render_newlen(h, 0); + res = pushf_write(next, hdr, h - hdr); + if (res < 0) + return res; + st->final_done = 1; + } + return 0; +} + +static void +pkt_stream_free(void *priv) +{ + struct PktStreamStat *st = priv; + + memset(st, 0, sizeof(*st)); + px_free(st); +} + +static const PushFilterOps pkt_stream_filter = { + pkt_stream_init, pkt_stream_process, pkt_stream_flush, pkt_stream_free +}; + +int +pgp_create_pkt_writer(PushFilter * dst, int tag, PushFilter ** res_p) +{ + int res; + + res = write_tag_only(dst, tag); + if (res < 0) + return res; + + return pushf_create(res_p, &pkt_stream_filter, NULL, dst); +} + +/* + * Text conversion filter + */ + +static int +crlf_process(PushFilter * dst, void *priv, const uint8 *data, int len) +{ + const uint8 *data_end = data + len; + const uint8 *p2, + *p1 = data; + int line_len; + static const uint8 crlf[] = {'\r', '\n'}; + int res = 0; + + while (p1 < data_end) + { + p2 = memchr(p1, '\n', data_end - p1); + if (p2 == NULL) + p2 = data_end; + + line_len = p2 - p1; + + /* write data */ + res = 0; + if (line_len > 0) + { + res = pushf_write(dst, p1, line_len); + if (res < 0) + break; + p1 += line_len; + } + + /* write crlf */ + while (p1 < data_end && *p1 == '\n') + { + res = pushf_write(dst, crlf, 2); + if (res < 0) + break; + p1++; + } + } + return res; +} + +static const PushFilterOps crlf_filter = { + NULL, crlf_process, NULL, NULL +}; + +/* + * Initialize literal data packet + */ +static int +init_litdata_packet(PushFilter ** pf_res, PGP_Context * ctx, PushFilter * dst) +{ + int res; + int hdrlen; + uint8 hdr[6]; + uint32 t; + PushFilter *pkt; + int type; + + /* + * Create header + */ + + if (ctx->text_mode) + type = ctx->unicode_mode ? 'u' : 't'; + else + type = 'b'; + + /* + * Store the creation time into packet. The goal is to have as few known + * bytes as possible. + */ + t = (uint32) time(NULL); + + hdr[0] = type; + hdr[1] = 0; + hdr[2] = (t >> 24) & 255; + hdr[3] = (t >> 16) & 255; + hdr[4] = (t >> 8) & 255; + hdr[5] = t & 255; + hdrlen = 6; + + res = write_tag_only(dst, PGP_PKT_LITERAL_DATA); + if (res < 0) + return res; + + res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst); + if (res < 0) + return res; + + res = pushf_write(pkt, hdr, hdrlen); + if (res < 0) + { + pushf_free(pkt); + return res; + } + + *pf_res = pkt; + return 0; +} + +/* + * Initialize compression filter + */ +static int +init_compress(PushFilter ** pf_res, PGP_Context * ctx, PushFilter * dst) +{ + int res; + uint8 type = ctx->compress_algo; + PushFilter *pkt; + + res = write_tag_only(dst, PGP_PKT_COMPRESSED_DATA); + if (res < 0) + return res; + + res = pushf_create(&pkt, &pkt_stream_filter, ctx, dst); + if (res < 0) + return res; + + res = pushf_write(pkt, &type, 1); + if (res >= 0) + res = pgp_compress_filter(pf_res, ctx, pkt); + + if (res < 0) + pushf_free(pkt); + + return res; +} + +/* + * Initialize encdata packet + */ +static int +init_encdata_packet(PushFilter ** pf_res, PGP_Context * ctx, PushFilter * dst) +{ + int res; + int tag; + + if (ctx->disable_mdc) + tag = PGP_PKT_SYMENCRYPTED_DATA; + else + tag = PGP_PKT_SYMENCRYPTED_DATA_MDC; + + res = write_tag_only(dst, tag); + if (res < 0) + return res; + + return pushf_create(pf_res, &pkt_stream_filter, ctx, dst); +} + +/* + * write prefix + */ +static int +write_prefix(PGP_Context * ctx, PushFilter * dst) +{ + uint8 prefix[PGP_MAX_BLOCK + 2]; + int res, + bs; + + bs = pgp_get_cipher_block_size(ctx->cipher_algo); + res = px_get_random_bytes(prefix, bs); + if (res < 0) + return res; + + prefix[bs + 0] = prefix[bs - 2]; + prefix[bs + 1] = prefix[bs - 1]; + + res = pushf_write(dst, prefix, bs + 2); + memset(prefix, 0, bs + 2); + return res < 0 ? res : 0; +} + +/* + * write symmetrically encrypted session key packet + */ + +static int +symencrypt_sesskey(PGP_Context * ctx, uint8 *dst) +{ + int res; + PGP_CFB *cfb; + uint8 algo = ctx->cipher_algo; + + res = pgp_cfb_create(&cfb, ctx->s2k_cipher_algo, + ctx->s2k.key, ctx->s2k.key_len, 0, NULL); + if (res < 0) + return res; + + pgp_cfb_encrypt(cfb, &algo, 1, dst); + pgp_cfb_encrypt(cfb, ctx->sess_key, ctx->sess_key_len, dst + 1); + + pgp_cfb_free(cfb); + return ctx->sess_key_len + 1; +} + +/* 5.3: Symmetric-Key Encrypted Session-Key */ +static int +write_symenc_sesskey(PGP_Context * ctx, PushFilter * dst) +{ + uint8 pkt[256]; + int pktlen; + int res; + uint8 *p = pkt; + + *p++ = 4; /* 5.3 - version number */ + *p++ = ctx->s2k_cipher_algo; + + *p++ = ctx->s2k.mode; + *p++ = ctx->s2k.digest_algo; + if (ctx->s2k.mode > 0) + { + memcpy(p, ctx->s2k.salt, 8); + p += 8; + } + if (ctx->s2k.mode == 3) + *p++ = ctx->s2k.iter; + + if (ctx->use_sess_key) + { + res = symencrypt_sesskey(ctx, p); + if (res < 0) + return res; + p += res; + } + + pktlen = p - pkt; + res = write_normal_header(dst, PGP_PKT_SYMENCRYPTED_SESSKEY, pktlen); + if (res >= 0) + res = pushf_write(dst, pkt, pktlen); + + memset(pkt, 0, pktlen); + return res; +} + +/* + * key setup + */ +static int +init_s2k_key(PGP_Context * ctx) +{ + int res; + + if (ctx->s2k_cipher_algo < 0) + ctx->s2k_cipher_algo = ctx->cipher_algo; + + res = pgp_s2k_fill(&ctx->s2k, ctx->s2k_mode, ctx->s2k_digest_algo); + if (res < 0) + return res; + + return pgp_s2k_process(&ctx->s2k, ctx->s2k_cipher_algo, + ctx->sym_key, ctx->sym_key_len); +} + +static int +init_sess_key(PGP_Context * ctx) +{ + int res; + + if (ctx->use_sess_key || ctx->pub_key) + { + ctx->sess_key_len = pgp_get_cipher_key_size(ctx->cipher_algo); + res = px_get_random_bytes(ctx->sess_key, ctx->sess_key_len); + if (res < 0) + return res; + } + else + { + ctx->sess_key_len = ctx->s2k.key_len; + memcpy(ctx->sess_key, ctx->s2k.key, ctx->s2k.key_len); + } + + return 0; +} + +/* + * combine + */ +int +pgp_encrypt(PGP_Context * ctx, MBuf * src, MBuf * dst) +{ + int res; + int len; + uint8 *buf; + PushFilter *pf, + *pf_tmp; + + /* + * do we have any key + */ + if (!ctx->sym_key && !ctx->pub_key) + return PXE_ARGUMENT_ERROR; + + /* MBuf writer */ + res = pushf_create_mbuf_writer(&pf, dst); + if (res < 0) + goto out; + + /* + * initialize symkey + */ + if (ctx->sym_key) + { + res = init_s2k_key(ctx); + if (res < 0) + goto out; + } + + res = init_sess_key(ctx); + if (res < 0) + goto out; + + /* + * write keypkt + */ + if (ctx->pub_key) + res = pgp_write_pubenc_sesskey(ctx, pf); + else + res = write_symenc_sesskey(ctx, pf); + if (res < 0) + goto out; + + /* encrypted data pkt */ + res = init_encdata_packet(&pf_tmp, ctx, pf); + if (res < 0) + goto out; + pf = pf_tmp; + + /* encrypter */ + res = pushf_create(&pf_tmp, &encrypt_filter, ctx, pf); + if (res < 0) + goto out; + pf = pf_tmp; + + /* hasher */ + if (ctx->disable_mdc == 0) + { + res = pushf_create(&pf_tmp, &mdc_filter, ctx, pf); + if (res < 0) + goto out; + pf = pf_tmp; + } + + /* prefix */ + res = write_prefix(ctx, pf); + if (res < 0) + goto out; + + /* compressor */ + if (ctx->compress_algo > 0 && ctx->compress_level > 0) + { + res = init_compress(&pf_tmp, ctx, pf); + if (res < 0) + goto out; + pf = pf_tmp; + } + + /* data streamer */ + res = init_litdata_packet(&pf_tmp, ctx, pf); + if (res < 0) + goto out; + pf = pf_tmp; + + + /* text conversion? */ + if (ctx->text_mode && ctx->convert_crlf) + { + res = pushf_create(&pf_tmp, &crlf_filter, ctx, pf); + if (res < 0) + goto out; + pf = pf_tmp; + } + + /* + * chain complete + */ + + len = mbuf_grab(src, mbuf_avail(src), &buf); + res = pushf_write(pf, buf, len); + if (res >= 0) + res = pushf_flush(pf); +out: + pushf_free_all(pf); + return res; +} diff --git a/contrib/pgcrypto/pgp-info.c b/contrib/pgcrypto/pgp-info.c new file mode 100644 index 0000000..6f9868e --- /dev/null +++ b/contrib/pgcrypto/pgp-info.c @@ -0,0 +1,235 @@ +/* + * pgp-info.c + * Provide info about PGP data. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +static int +read_pubkey_keyid(PullFilter * pkt, uint8 *keyid_buf) +{ + int res; + PGP_PubKey *pk = NULL; + + res = _pgp_read_public_key(pkt, &pk); + if (res < 0) + goto err; + + /* skip secret key part, if it exists */ + res = pgp_skip_packet(pkt); + if (res < 0) + goto err; + + /* is it encryption key */ + switch (pk->algo) + { + case PGP_PUB_ELG_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + memcpy(keyid_buf, pk->key_id, 8); + res = 1; + break; + default: + res = 0; + } + +err: + pgp_key_free(pk); + return res; +} + +static int +read_pubenc_keyid(PullFilter * pkt, uint8 *keyid_buf) +{ + uint8 ver; + int res; + + GETBYTE(pkt, ver); + if (ver != 3) + return -1; + + res = pullf_read_fixed(pkt, 8, keyid_buf); + if (res < 0) + return res; + + return pgp_skip_packet(pkt); +} + +static const char hextbl[] = "0123456789ABCDEF"; + +static int +print_key(uint8 *keyid, char *dst) +{ + int i; + unsigned c; + + for (i = 0; i < 8; i++) + { + c = keyid[i]; + *dst++ = hextbl[(c >> 4) & 0x0F]; + *dst++ = hextbl[c & 0x0F]; + } + *dst = 0; + return 8 * 2; +} + +static const uint8 any_key[] = +{0, 0, 0, 0, 0, 0, 0, 0}; + +/* + * dst should have room for 17 bytes + */ +int +pgp_get_keyid(MBuf * pgp_data, char *dst) +{ + int res; + PullFilter *src; + PullFilter *pkt = NULL; + int len; + uint8 tag; + int got_pub_key = 0, + got_symenc_key = 0, + got_pubenc_key = 0; + int got_data = 0; + uint8 keyid_buf[8]; + int got_main_key = 0; + + + res = pullf_create_mbuf_reader(&src, pgp_data); + if (res < 0) + return res; + + while (1) + { + res = pgp_parse_pkt_hdr(src, &tag, &len, 0); + if (res <= 0) + break; + res = pgp_create_pkt_reader(&pkt, src, len, res, NULL); + if (res < 0) + break; + + switch (tag) + { + case PGP_PKT_SECRET_KEY: + case PGP_PKT_PUBLIC_KEY: + /* main key is for signing, so ignore it */ + if (!got_main_key) + { + got_main_key = 1; + res = pgp_skip_packet(pkt); + } + else + res = PXE_PGP_MULTIPLE_KEYS; + break; + case PGP_PKT_SECRET_SUBKEY: + case PGP_PKT_PUBLIC_SUBKEY: + res = read_pubkey_keyid(pkt, keyid_buf); + if (res < 0) + break; + if (res > 0) + got_pub_key++; + break; + case PGP_PKT_PUBENCRYPTED_SESSKEY: + got_pubenc_key++; + res = read_pubenc_keyid(pkt, keyid_buf); + break; + case PGP_PKT_SYMENCRYPTED_DATA: + case PGP_PKT_SYMENCRYPTED_DATA_MDC: + /* don't skip it, just stop */ + got_data = 1; + break; + case PGP_PKT_SYMENCRYPTED_SESSKEY: + got_symenc_key++; + /* fallthru */ + case PGP_PKT_SIGNATURE: + case PGP_PKT_MARKER: + case PGP_PKT_TRUST: + case PGP_PKT_USER_ID: + case PGP_PKT_USER_ATTR: + case PGP_PKT_PRIV_61: + res = pgp_skip_packet(pkt); + break; + default: + res = PXE_PGP_CORRUPT_DATA; + } + + if (pkt) + pullf_free(pkt); + pkt = NULL; + + if (res < 0 || got_data) + break; + } + + pullf_free(src); + if (pkt) + pullf_free(pkt); + + if (res < 0) + return res; + + /* now check sanity */ + if (got_pub_key && got_pubenc_key) + res = PXE_PGP_CORRUPT_DATA; + + if (got_pub_key > 1) + res = PXE_PGP_MULTIPLE_KEYS; + + if (got_pubenc_key > 1) + res = PXE_PGP_MULTIPLE_KEYS; + + /* + * if still ok, look what we got + */ + if (res >= 0) + { + if (got_pubenc_key || got_pub_key) + { + if (memcmp(keyid_buf, any_key, 8) == 0) + { + memcpy(dst, "ANYKEY", 7); + res = 6; + } + else + res = print_key(keyid_buf, dst); + } + else if (got_symenc_key) + { + memcpy(dst, "SYMKEY", 7); + res = 6; + } + else + res = PXE_PGP_NO_USABLE_KEY; + } + + return res; +} diff --git a/contrib/pgcrypto/pgp-mpi-internal.c b/contrib/pgcrypto/pgp-mpi-internal.c new file mode 100644 index 0000000..6206241 --- /dev/null +++ b/contrib/pgcrypto/pgp-mpi-internal.c @@ -0,0 +1,61 @@ +/* + * pgp-mpi-internal.c + * OpenPGP MPI functions. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +int +pgp_elgamal_encrypt(PGP_PubKey * pk, PGP_MPI * _m, + PGP_MPI ** c1_p, PGP_MPI ** c2_p) +{ + return PXE_PGP_NO_BIGNUM; +} + +int +pgp_elgamal_decrypt(PGP_PubKey * pk, PGP_MPI * _c1, PGP_MPI * _c2, + PGP_MPI ** msg_p) +{ + return PXE_PGP_NO_BIGNUM; +} + +int +pgp_rsa_encrypt(PGP_PubKey * pk, PGP_MPI * m, PGP_MPI ** c) +{ + return PXE_PGP_NO_BIGNUM; +} + +int +pgp_rsa_decrypt(PGP_PubKey * pk, PGP_MPI * c, PGP_MPI ** m) +{ + return PXE_PGP_NO_BIGNUM; +} diff --git a/contrib/pgcrypto/pgp-mpi-openssl.c b/contrib/pgcrypto/pgp-mpi-openssl.c new file mode 100644 index 0000000..a06ed22 --- /dev/null +++ b/contrib/pgcrypto/pgp-mpi-openssl.c @@ -0,0 +1,285 @@ +/* + * pgp-mpi-openssl.c + * OpenPGP MPI functions using OpenSSL BIGNUM code. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +static BIGNUM * +mpi_to_bn(PGP_MPI * n) +{ + BIGNUM *bn = BN_bin2bn(n->data, n->bytes, NULL); + + if (!bn) + return NULL; + if (BN_num_bits(bn) != n->bits) + { + px_debug("mpi_to_bn: bignum conversion failed: mpi=%d, bn=%d", + n->bits, BN_num_bits(bn)); + BN_clear_free(bn); + return NULL; + } + return bn; +} + +static PGP_MPI * +bn_to_mpi(BIGNUM *bn) +{ + int res; + PGP_MPI *n; + + res = pgp_mpi_alloc(BN_num_bits(bn), &n); + if (res < 0) + return NULL; + + if (BN_num_bytes(bn) != n->bytes) + { + px_debug("bn_to_mpi: bignum conversion failed: bn=%d, mpi=%d", + BN_num_bytes(bn), n->bytes); + pgp_mpi_free(n); + return NULL; + } + BN_bn2bin(bn, n->data); + return n; +} + +/* + * Decide the number of bits in the random componont k + * + * It should be in the same range as p for signing (which + * is deprecated), but can be much smaller for encrypting. + * + * Until I research it further, I just mimic gpg behaviour. + * It has a special mapping table, for values <= 5120, + * above that it uses 'arbitrary high number'. Following + * algorihm hovers 10-70 bits above gpg values. And for + * larger p, it uses gpg's algorihm. + * + * The point is - if k gets large, encryption will be + * really slow. It does not matter for decryption. + */ +static int +decide_k_bits(int p_bits) +{ + if (p_bits <= 5120) + return p_bits / 10 + 160; + else + return (p_bits / 8 + 200) * 3 / 2; +} + +int +pgp_elgamal_encrypt(PGP_PubKey * pk, PGP_MPI * _m, + PGP_MPI ** c1_p, PGP_MPI ** c2_p) +{ + int res = PXE_PGP_MATH_FAILED; + int k_bits; + BIGNUM *m = mpi_to_bn(_m); + BIGNUM *p = mpi_to_bn(pk->pub.elg.p); + BIGNUM *g = mpi_to_bn(pk->pub.elg.g); + BIGNUM *y = mpi_to_bn(pk->pub.elg.y); + BIGNUM *k = BN_new(); + BIGNUM *yk = BN_new(); + BIGNUM *c1 = BN_new(); + BIGNUM *c2 = BN_new(); + BN_CTX *tmp = BN_CTX_new(); + + if (!m || !p || !g || !y || !k || !yk || !c1 || !c2 || !tmp) + goto err; + + /* + * generate k + */ + k_bits = decide_k_bits(BN_num_bits(p)); + if (!BN_rand(k, k_bits, 0, 0)) + goto err; + + /* + * c1 = g^k c2 = m * y^k + */ + if (!BN_mod_exp(c1, g, k, p, tmp)) + goto err; + if (!BN_mod_exp(yk, y, k, p, tmp)) + goto err; + if (!BN_mod_mul(c2, m, yk, p, tmp)) + goto err; + + /* result */ + *c1_p = bn_to_mpi(c1); + *c2_p = bn_to_mpi(c2); + if (*c1_p && *c2_p) + res = 0; +err: + if (tmp) + BN_CTX_free(tmp); + if (c2) + BN_clear_free(c2); + if (c1) + BN_clear_free(c1); + if (yk) + BN_clear_free(yk); + if (k) + BN_clear_free(k); + if (y) + BN_clear_free(y); + if (g) + BN_clear_free(g); + if (p) + BN_clear_free(p); + if (m) + BN_clear_free(m); + return res; +} + +int +pgp_elgamal_decrypt(PGP_PubKey * pk, PGP_MPI * _c1, PGP_MPI * _c2, + PGP_MPI ** msg_p) +{ + int res = PXE_PGP_MATH_FAILED; + BIGNUM *c1 = mpi_to_bn(_c1); + BIGNUM *c2 = mpi_to_bn(_c2); + BIGNUM *p = mpi_to_bn(pk->pub.elg.p); + BIGNUM *x = mpi_to_bn(pk->sec.elg.x); + BIGNUM *c1x = BN_new(); + BIGNUM *div = BN_new(); + BIGNUM *m = BN_new(); + BN_CTX *tmp = BN_CTX_new(); + + if (!c1 || !c2 || !p || !x || !c1x || !div || !m || !tmp) + goto err; + + /* + * m = c2 / (c1^x) + */ + if (!BN_mod_exp(c1x, c1, x, p, tmp)) + goto err; + if (!BN_mod_inverse(div, c1x, p, tmp)) + goto err; + if (!BN_mod_mul(m, c2, div, p, tmp)) + goto err; + + /* result */ + *msg_p = bn_to_mpi(m); + if (*msg_p) + res = 0; +err: + if (tmp) + BN_CTX_free(tmp); + if (m) + BN_clear_free(m); + if (div) + BN_clear_free(div); + if (c1x) + BN_clear_free(c1x); + if (x) + BN_clear_free(x); + if (p) + BN_clear_free(p); + if (c2) + BN_clear_free(c2); + if (c1) + BN_clear_free(c1); + return res; +} + +int +pgp_rsa_encrypt(PGP_PubKey * pk, PGP_MPI * _m, PGP_MPI ** c_p) +{ + int res = PXE_PGP_MATH_FAILED; + BIGNUM *m = mpi_to_bn(_m); + BIGNUM *e = mpi_to_bn(pk->pub.rsa.e); + BIGNUM *n = mpi_to_bn(pk->pub.rsa.n); + BIGNUM *c = BN_new(); + BN_CTX *tmp = BN_CTX_new(); + + if (!m || !e || !n || !c || !tmp) + goto err; + + /* + * c = m ^ e + */ + if (!BN_mod_exp(c, m, e, n, tmp)) + goto err; + + *c_p = bn_to_mpi(c); + if (*c_p) + res = 0; +err: + if (tmp) + BN_CTX_free(tmp); + if (c) + BN_clear_free(c); + if (n) + BN_clear_free(n); + if (e) + BN_clear_free(e); + if (m) + BN_clear_free(m); + return res; +} + +int +pgp_rsa_decrypt(PGP_PubKey * pk, PGP_MPI * _c, PGP_MPI ** m_p) +{ + int res = PXE_PGP_MATH_FAILED; + BIGNUM *c = mpi_to_bn(_c); + BIGNUM *d = mpi_to_bn(pk->sec.rsa.d); + BIGNUM *n = mpi_to_bn(pk->pub.rsa.n); + BIGNUM *m = BN_new(); + BN_CTX *tmp = BN_CTX_new(); + + if (!m || !d || !n || !c || !tmp) + goto err; + + /* + * m = c ^ d + */ + if (!BN_mod_exp(m, c, d, n, tmp)) + goto err; + + *m_p = bn_to_mpi(m); + if (*m_p) + res = 0; +err: + if (tmp) + BN_CTX_free(tmp); + if (m) + BN_clear_free(m); + if (n) + BN_clear_free(n); + if (d) + BN_clear_free(d); + if (c) + BN_clear_free(c); + return res; +} diff --git a/contrib/pgcrypto/pgp-mpi.c b/contrib/pgcrypto/pgp-mpi.c new file mode 100644 index 0000000..cd45f33 --- /dev/null +++ b/contrib/pgcrypto/pgp-mpi.c @@ -0,0 +1,143 @@ +/* + * pgp-mpi.c + * OpenPGP MPI helper functions. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +int +pgp_mpi_alloc(int bits, PGP_MPI ** mpi) +{ + PGP_MPI *n; + int len = (bits + 7) / 8; + + if (bits < 0 || bits > 0xFFFF) + { + px_debug("pgp_mpi_alloc: unreasonable request: bits=%d", bits); + return PXE_PGP_CORRUPT_DATA; + } + n = px_alloc(sizeof(*n) + len); + n->bits = bits; + n->bytes = len; + n->data = (uint8 *) (n) + sizeof(*n); + *mpi = n; + return 0; +} + +int +pgp_mpi_create(uint8 *data, int bits, PGP_MPI ** mpi) +{ + int res; + PGP_MPI *n; + + res = pgp_mpi_alloc(bits, &n); + if (res < 0) + return res; + memcpy(n->data, data, n->bytes); + *mpi = n; + return 0; +} + +int +pgp_mpi_free(PGP_MPI * mpi) +{ + if (mpi == NULL) + return 0; + memset(mpi, 0, sizeof(*mpi) + mpi->bytes); + px_free(mpi); + return 0; +} + +int +pgp_mpi_read(PullFilter * src, PGP_MPI ** mpi) +{ + int res; + uint8 hdr[2]; + int bits; + PGP_MPI *n; + + res = pullf_read_fixed(src, 2, hdr); + if (res < 0) + return res; + bits = ((unsigned) hdr[0] << 8) + hdr[1]; + + res = pgp_mpi_alloc(bits, &n); + if (res < 0) + return res; + + res = pullf_read_fixed(src, n->bytes, n->data); + if (res < 0) + pgp_mpi_free(n); + else + *mpi = n; + return res; +} + +int +pgp_mpi_write(PushFilter * dst, PGP_MPI * n) +{ + int res; + uint8 buf[2]; + + buf[0] = n->bits >> 8; + buf[1] = n->bits & 0xFF; + res = pushf_write(dst, buf, 2); + if (res >= 0) + res = pushf_write(dst, n->data, n->bytes); + return res; +} + +int +pgp_mpi_hash(PX_MD * md, PGP_MPI * n) +{ + uint8 buf[2]; + + buf[0] = n->bits >> 8; + buf[1] = n->bits & 0xFF; + px_md_update(md, buf, 2); + px_md_update(md, n->data, n->bytes); + + return 0; +} + +unsigned +pgp_mpi_cksum(unsigned cksum, PGP_MPI * n) +{ + int i; + + cksum += n->bits >> 8; + cksum += n->bits & 0xFF; + for (i = 0; i < n->bytes; i++) + cksum += n->data[i]; + + return cksum & 0xFFFF; +} diff --git a/contrib/pgcrypto/pgp-pubdec.c b/contrib/pgcrypto/pgp-pubdec.c new file mode 100644 index 0000000..4819761 --- /dev/null +++ b/contrib/pgcrypto/pgp-pubdec.c @@ -0,0 +1,236 @@ +/* + * pgp-pubdec.c + * Decrypt public-key encrypted session key. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +/* + * padded msg = 02 || PS || 00 || M + * PS - pad bytes + * M - msg + */ +static uint8 * +check_eme_pkcs1_v15(uint8 *data, int len) +{ + uint8 *data_end = data + len; + uint8 *p = data; + int rnd = 0; + + if (len < 1 + 8 + 1) + return NULL; + + if (*p++ != 2) + return NULL; + + while (p < data_end && *p) + { + p++; + rnd++; + } + + if (p == data_end) + return NULL; + if (*p != 0) + return NULL; + if (rnd < 8) + return NULL; + return p + 1; +} + +/* + * secret message: 1 byte algo, sesskey, 2 byte cksum + * ignore algo in cksum + */ +static int +control_cksum(uint8 *msg, int msglen) +{ + int i; + unsigned my_cksum, + got_cksum; + + if (msglen < 3) + return PXE_PGP_WRONG_KEY; + + my_cksum = 0; + for (i = 1; i < msglen - 2; i++) + my_cksum += msg[i]; + my_cksum &= 0xFFFF; + got_cksum = ((unsigned) (msg[msglen - 2]) << 8) + msg[msglen - 1]; + if (my_cksum != got_cksum) + { + px_debug("pubenc cksum failed"); + return PXE_PGP_WRONG_KEY; + } + return 0; +} + +static int +decrypt_elgamal(PGP_PubKey * pk, PullFilter * pkt, PGP_MPI ** m_p) +{ + int res; + PGP_MPI *c1 = NULL; + PGP_MPI *c2 = NULL; + + if (pk->algo != PGP_PUB_ELG_ENCRYPT) + return PXE_PGP_WRONG_KEY; + + /* read elgamal encrypted data */ + res = pgp_mpi_read(pkt, &c1); + if (res < 0) + goto out; + res = pgp_mpi_read(pkt, &c2); + if (res < 0) + goto out; + + /* decrypt */ + res = pgp_elgamal_decrypt(pk, c1, c2, m_p); + +out: + pgp_mpi_free(c1); + pgp_mpi_free(c2); + return res; +} + +static int +decrypt_rsa(PGP_PubKey * pk, PullFilter * pkt, PGP_MPI ** m_p) +{ + int res; + PGP_MPI *c; + + if (pk->algo != PGP_PUB_RSA_ENCRYPT + && pk->algo != PGP_PUB_RSA_ENCRYPT_SIGN) + return PXE_PGP_WRONG_KEY; + + /* read rsa encrypted data */ + res = pgp_mpi_read(pkt, &c); + if (res < 0) + return res; + + /* decrypt */ + res = pgp_rsa_decrypt(pk, c, m_p); + + pgp_mpi_free(c); + return res; +} + +/* key id is missing - user is expected to try all keys */ +static const uint8 + any_key[] = {0, 0, 0, 0, 0, 0, 0, 0}; + +int +pgp_parse_pubenc_sesskey(PGP_Context * ctx, PullFilter * pkt) +{ + int ver; + int algo; + int res; + uint8 key_id[8]; + PGP_PubKey *pk; + uint8 *msg; + int msglen; + PGP_MPI *m; + + pk = ctx->pub_key; + if (pk == NULL) + { + px_debug("no pubkey?"); + return PXE_BUG; + } + + GETBYTE(pkt, ver); + if (ver != 3) + { + px_debug("unknown pubenc_sesskey pkt ver=%d", ver); + return PXE_PGP_CORRUPT_DATA; + } + + /* + * check if keyid's match - user-friendly msg + */ + res = pullf_read_fixed(pkt, 8, key_id); + if (res < 0) + return res; + if (memcmp(key_id, any_key, 8) != 0 + && memcmp(key_id, pk->key_id, 8) != 0) + { + px_debug("key_id's does not match"); + return PXE_PGP_WRONG_KEY; + } + + /* + * Decrypt + */ + GETBYTE(pkt, algo); + switch (algo) + { + case PGP_PUB_ELG_ENCRYPT: + res = decrypt_elgamal(pk, pkt, &m); + break; + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + res = decrypt_rsa(pk, pkt, &m); + break; + default: + res = PXE_PGP_UNKNOWN_PUBALGO; + } + if (res < 0) + return res; + + /* + * extract message + */ + msg = check_eme_pkcs1_v15(m->data, m->bytes); + if (msg == NULL) + { + px_debug("check_eme_pkcs1_v15 failed"); + res = PXE_PGP_WRONG_KEY; + goto out; + } + msglen = m->bytes - (msg - m->data); + + res = control_cksum(msg, msglen); + if (res < 0) + goto out; + + /* + * got sesskey + */ + ctx->cipher_algo = *msg; + ctx->sess_key_len = msglen - 3; + memcpy(ctx->sess_key, msg + 1, ctx->sess_key_len); + +out: + pgp_mpi_free(m); + if (res < 0) + return res; + return pgp_expect_packet_end(pkt); +} diff --git a/contrib/pgcrypto/pgp-pubenc.c b/contrib/pgcrypto/pgp-pubenc.c new file mode 100644 index 0000000..321d2c3 --- /dev/null +++ b/contrib/pgcrypto/pgp-pubenc.c @@ -0,0 +1,248 @@ +/* + * pgp-pubenc.c + * Encrypt session key with public key. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +/* + * padded msg: 02 || non-zero pad bytes || 00 || msg + */ +static int +pad_eme_pkcs1_v15(uint8 *data, int data_len, int res_len, uint8 **res_p) +{ + int res; + uint8 *buf, + *p; + int pad_len = res_len - 2 - data_len; + + if (pad_len < 8) + return PXE_BUG; + + buf = px_alloc(res_len); + buf[0] = 0x02; + res = px_get_random_bytes(buf + 1, pad_len); + if (res < 0) + { + px_free(buf); + return res; + } + + /* pad must not contain zero bytes */ + p = buf + 1; + while (p < buf + 1 + pad_len) + { + if (*p == 0) + { + res = px_get_random_bytes(p, 1); + if (res < 0) + break; + } + if (*p != 0) + p++; + } + + if (res < 0) + { + memset(buf, 0, res_len); + px_free(buf); + return res; + } + + buf[pad_len + 1] = 0; + memcpy(buf + pad_len + 2, data, data_len); + *res_p = buf; + + return 0; +} + +static int +create_secmsg(PGP_Context * ctx, PGP_MPI ** msg_p, int full_bytes) +{ + uint8 *secmsg; + int res, + i; + unsigned cksum = 0; + int klen = ctx->sess_key_len; + uint8 *padded = NULL; + PGP_MPI *m = NULL; + + /* calc checksum */ + for (i = 0; i < klen; i++) + cksum += ctx->sess_key[i]; + + /* + * create "secret message" + */ + secmsg = px_alloc(klen + 3); + secmsg[0] = ctx->cipher_algo; + memcpy(secmsg + 1, ctx->sess_key, klen); + secmsg[klen + 1] = (cksum >> 8) & 0xFF; + secmsg[klen + 2] = cksum & 0xFF; + + /* + * now create a large integer of it + */ + res = pad_eme_pkcs1_v15(secmsg, klen + 3, full_bytes, &padded); + if (res >= 0) + { + /* first byte will be 0x02 */ + int full_bits = full_bytes * 8 - 6; + + res = pgp_mpi_create(padded, full_bits, &m); + } + + if (padded) + { + memset(padded, 0, full_bytes); + px_free(padded); + } + memset(secmsg, 0, klen + 3); + px_free(secmsg); + + if (res >= 0) + *msg_p = m; + + return res; +} + +static int +encrypt_and_write_elgamal(PGP_Context * ctx, PGP_PubKey * pk, PushFilter * pkt) +{ + int res; + PGP_MPI *m = NULL, + *c1 = NULL, + *c2 = NULL; + + /* create padded msg */ + res = create_secmsg(ctx, &m, pk->pub.elg.p->bytes - 1); + if (res < 0) + goto err; + + /* encrypt it */ + res = pgp_elgamal_encrypt(pk, m, &c1, &c2); + if (res < 0) + goto err; + + /* write out */ + res = pgp_mpi_write(pkt, c1); + if (res < 0) + goto err; + res = pgp_mpi_write(pkt, c2); + +err: + pgp_mpi_free(m); + pgp_mpi_free(c1); + pgp_mpi_free(c2); + return res; +} + +static int +encrypt_and_write_rsa(PGP_Context * ctx, PGP_PubKey * pk, PushFilter * pkt) +{ + int res; + PGP_MPI *m = NULL, + *c = NULL; + + /* create padded msg */ + res = create_secmsg(ctx, &m, pk->pub.rsa.n->bytes - 1); + if (res < 0) + goto err; + + /* encrypt it */ + res = pgp_rsa_encrypt(pk, m, &c); + if (res < 0) + goto err; + + /* write out */ + res = pgp_mpi_write(pkt, c); + +err: + pgp_mpi_free(m); + pgp_mpi_free(c); + return res; +} + +int +pgp_write_pubenc_sesskey(PGP_Context * ctx, PushFilter * dst) +{ + int res; + PGP_PubKey *pk = ctx->pub_key; + uint8 ver = 3; + PushFilter *pkt = NULL; + uint8 algo = pk->algo; + + if (pk == NULL) + { + px_debug("no pubkey?\n"); + return PXE_BUG; + } + + /* + * now write packet + */ + res = pgp_create_pkt_writer(dst, PGP_PKT_PUBENCRYPTED_SESSKEY, &pkt); + if (res < 0) + goto err; + res = pushf_write(pkt, &ver, 1); + if (res < 0) + goto err; + res = pushf_write(pkt, pk->key_id, 8); + if (res < 0) + goto err; + res = pushf_write(pkt, &algo, 1); + if (res < 0) + goto err; + + switch (algo) + { + case PGP_PUB_ELG_ENCRYPT: + res = encrypt_and_write_elgamal(ctx, pk, pkt); + break; + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + res = encrypt_and_write_rsa(ctx, pk, pkt); + break; + } + if (res < 0) + goto err; + + /* + * done, signal packet end + */ + res = pushf_flush(pkt); +err: + if (pkt) + pushf_free(pkt); + + return res; +} diff --git a/contrib/pgcrypto/pgp-pubkey.c b/contrib/pgcrypto/pgp-pubkey.c new file mode 100644 index 0000000..2c13111 --- /dev/null +++ b/contrib/pgcrypto/pgp-pubkey.c @@ -0,0 +1,584 @@ +/* + * pgp-pubkey.c + * Read public or secret key. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +int +pgp_key_alloc(PGP_PubKey ** pk_p) +{ + PGP_PubKey *pk; + + pk = px_alloc(sizeof(*pk)); + memset(pk, 0, sizeof(*pk)); + *pk_p = pk; + return 0; +} + +void +pgp_key_free(PGP_PubKey * pk) +{ + if (pk == NULL) + return; + + switch (pk->algo) + { + case PGP_PUB_ELG_ENCRYPT: + pgp_mpi_free(pk->pub.elg.p); + pgp_mpi_free(pk->pub.elg.g); + pgp_mpi_free(pk->pub.elg.y); + pgp_mpi_free(pk->sec.elg.x); + break; + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + pgp_mpi_free(pk->pub.rsa.n); + pgp_mpi_free(pk->pub.rsa.e); + pgp_mpi_free(pk->sec.rsa.d); + pgp_mpi_free(pk->sec.rsa.p); + pgp_mpi_free(pk->sec.rsa.q); + pgp_mpi_free(pk->sec.rsa.u); + break; + case PGP_PUB_DSA_SIGN: + pgp_mpi_free(pk->pub.dsa.p); + pgp_mpi_free(pk->pub.dsa.q); + pgp_mpi_free(pk->pub.dsa.g); + pgp_mpi_free(pk->pub.dsa.y); + pgp_mpi_free(pk->sec.dsa.x); + break; + } + memset(pk, 0, sizeof(*pk)); + px_free(pk); +} + +static int +calc_key_id(PGP_PubKey * pk) +{ + int res; + PX_MD *md; + int len; + uint8 hdr[3]; + uint8 hash[20]; + + res = pgp_load_digest(PGP_DIGEST_SHA1, &md); + if (res < 0) + return res; + + len = 1 + 4 + 1; + switch (pk->algo) + { + case PGP_PUB_ELG_ENCRYPT: + len += 2 + pk->pub.elg.p->bytes; + len += 2 + pk->pub.elg.g->bytes; + len += 2 + pk->pub.elg.y->bytes; + break; + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + len += 2 + pk->pub.rsa.n->bytes; + len += 2 + pk->pub.rsa.e->bytes; + break; + case PGP_PUB_DSA_SIGN: + len += 2 + pk->pub.dsa.p->bytes; + len += 2 + pk->pub.dsa.q->bytes; + len += 2 + pk->pub.dsa.g->bytes; + len += 2 + pk->pub.dsa.y->bytes; + break; + } + + hdr[0] = 0x99; + hdr[1] = len >> 8; + hdr[2] = len & 0xFF; + px_md_update(md, hdr, 3); + + px_md_update(md, &pk->ver, 1); + px_md_update(md, pk->time, 4); + px_md_update(md, &pk->algo, 1); + + switch (pk->algo) + { + case PGP_PUB_ELG_ENCRYPT: + pgp_mpi_hash(md, pk->pub.elg.p); + pgp_mpi_hash(md, pk->pub.elg.g); + pgp_mpi_hash(md, pk->pub.elg.y); + break; + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + pgp_mpi_hash(md, pk->pub.rsa.n); + pgp_mpi_hash(md, pk->pub.rsa.e); + break; + case PGP_PUB_DSA_SIGN: + pgp_mpi_hash(md, pk->pub.dsa.p); + pgp_mpi_hash(md, pk->pub.dsa.q); + pgp_mpi_hash(md, pk->pub.dsa.g); + pgp_mpi_hash(md, pk->pub.dsa.y); + break; + } + + px_md_finish(md, hash); + px_md_free(md); + + memcpy(pk->key_id, hash + 12, 8); + memset(hash, 0, 20); + + return 0; +} + +int +_pgp_read_public_key(PullFilter * pkt, PGP_PubKey ** pk_p) +{ + int res; + PGP_PubKey *pk; + + res = pgp_key_alloc(&pk); + if (res < 0) + return res; + + /* get version */ + GETBYTE(pkt, pk->ver); + if (pk->ver != 4) + { + res = PXE_PGP_NOT_V4_KEYPKT; + goto out; + } + + /* read time */ + res = pullf_read_fixed(pkt, 4, pk->time); + if (res < 0) + goto out; + + /* pubkey algorithm */ + GETBYTE(pkt, pk->algo); + + switch (pk->algo) + { + case PGP_PUB_DSA_SIGN: + res = pgp_mpi_read(pkt, &pk->pub.dsa.p); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->pub.dsa.q); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->pub.dsa.g); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->pub.dsa.y); + if (res < 0) + break; + + res = calc_key_id(pk); + break; + + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + res = pgp_mpi_read(pkt, &pk->pub.rsa.n); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->pub.rsa.e); + if (res < 0) + break; + + res = calc_key_id(pk); + + if (pk->algo != PGP_PUB_RSA_SIGN) + pk->can_encrypt = 1; + break; + + case PGP_PUB_ELG_ENCRYPT: + res = pgp_mpi_read(pkt, &pk->pub.elg.p); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->pub.elg.g); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->pub.elg.y); + if (res < 0) + break; + + res = calc_key_id(pk); + + pk->can_encrypt = 1; + break; + + default: + px_debug("unknown public algo: %d", pk->algo); + res = PXE_PGP_UNKNOWN_PUBALGO; + } + +out: + if (res < 0) + pgp_key_free(pk); + else + *pk_p = pk; + + return res; +} + +#define HIDE_CLEAR 0 +#define HIDE_CKSUM 255 +#define HIDE_SHA1 254 + +static int +check_key_sha1(PullFilter * src, PGP_PubKey * pk) +{ + int res; + uint8 got_sha1[20]; + uint8 my_sha1[20]; + PX_MD *md; + + res = pullf_read_fixed(src, 20, got_sha1); + if (res < 0) + return res; + + res = pgp_load_digest(PGP_DIGEST_SHA1, &md); + if (res < 0) + goto err; + switch (pk->algo) + { + case PGP_PUB_ELG_ENCRYPT: + pgp_mpi_hash(md, pk->sec.elg.x); + break; + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + pgp_mpi_hash(md, pk->sec.rsa.d); + pgp_mpi_hash(md, pk->sec.rsa.p); + pgp_mpi_hash(md, pk->sec.rsa.q); + pgp_mpi_hash(md, pk->sec.rsa.u); + break; + case PGP_PUB_DSA_SIGN: + pgp_mpi_hash(md, pk->sec.dsa.x); + break; + } + px_md_finish(md, my_sha1); + px_md_free(md); + + if (memcmp(my_sha1, got_sha1, 20) != 0) + { + px_debug("key sha1 check failed"); + res = PXE_PGP_KEYPKT_CORRUPT; + } +err: + memset(got_sha1, 0, 20); + memset(my_sha1, 0, 20); + return res; +} + +static int +check_key_cksum(PullFilter * src, PGP_PubKey * pk) +{ + int res; + unsigned got_cksum, + my_cksum = 0; + uint8 buf[2]; + + res = pullf_read_fixed(src, 2, buf); + if (res < 0) + return res; + + got_cksum = ((unsigned) buf[0] << 8) + buf[1]; + switch (pk->algo) + { + case PGP_PUB_ELG_ENCRYPT: + my_cksum = pgp_mpi_cksum(0, pk->sec.elg.x); + break; + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + my_cksum = pgp_mpi_cksum(0, pk->sec.rsa.d); + my_cksum = pgp_mpi_cksum(my_cksum, pk->sec.rsa.p); + my_cksum = pgp_mpi_cksum(my_cksum, pk->sec.rsa.q); + my_cksum = pgp_mpi_cksum(my_cksum, pk->sec.rsa.u); + break; + case PGP_PUB_DSA_SIGN: + my_cksum = pgp_mpi_cksum(0, pk->sec.dsa.x); + break; + } + if (my_cksum != got_cksum) + { + px_debug("key cksum check failed"); + return PXE_PGP_KEYPKT_CORRUPT; + } + return 0; +} + +static int +process_secret_key(PullFilter * pkt, PGP_PubKey ** pk_p, + const uint8 *key, int key_len) +{ + int res; + int hide_type; + int cipher_algo; + int bs; + uint8 iv[512]; + PullFilter *pf_decrypt = NULL, + *pf_key; + PGP_CFB *cfb = NULL; + PGP_S2K s2k; + PGP_PubKey *pk; + + /* first read public key part */ + res = _pgp_read_public_key(pkt, &pk); + if (res < 0) + return res; + + /* + * is secret key encrypted? + */ + GETBYTE(pkt, hide_type); + if (hide_type == HIDE_SHA1 || hide_type == HIDE_CKSUM) + { + if (key == NULL) + return PXE_PGP_NEED_SECRET_PSW; + GETBYTE(pkt, cipher_algo); + res = pgp_s2k_read(pkt, &s2k); + if (res < 0) + return res; + + res = pgp_s2k_process(&s2k, cipher_algo, key, key_len); + if (res < 0) + return res; + + bs = pgp_get_cipher_block_size(cipher_algo); + if (bs == 0) + { + px_debug("unknown cipher algo=%d", cipher_algo); + return PXE_PGP_UNSUPPORTED_CIPHER; + } + res = pullf_read_fixed(pkt, bs, iv); + if (res < 0) + return res; + + /* + * create decrypt filter + */ + res = pgp_cfb_create(&cfb, cipher_algo, s2k.key, s2k.key_len, 0, iv); + if (res < 0) + return res; + res = pullf_create(&pf_decrypt, &pgp_decrypt_filter, cfb, pkt); + if (res < 0) + return res; + pf_key = pf_decrypt; + } + else if (hide_type == HIDE_CLEAR) + { + pf_key = pkt; + } + else + { + px_debug("unknown hide type"); + return PXE_PGP_KEYPKT_CORRUPT; + } + + /* read secret key */ + switch (pk->algo) + { + case PGP_PUB_RSA_SIGN: + case PGP_PUB_RSA_ENCRYPT: + case PGP_PUB_RSA_ENCRYPT_SIGN: + res = pgp_mpi_read(pkt, &pk->sec.rsa.d); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->sec.rsa.p); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->sec.rsa.q); + if (res < 0) + break; + res = pgp_mpi_read(pkt, &pk->sec.rsa.u); + if (res < 0) + break; + break; + case PGP_PUB_ELG_ENCRYPT: + res = pgp_mpi_read(pf_key, &pk->sec.elg.x); + break; + case PGP_PUB_DSA_SIGN: + res = pgp_mpi_read(pf_key, &pk->sec.dsa.x); + break; + default: + px_debug("unknown public algo: %d", pk->algo); + res = PXE_PGP_KEYPKT_CORRUPT; + } + /* read checksum / sha1 */ + if (res >= 0) + { + if (hide_type == HIDE_SHA1) + res = check_key_sha1(pf_key, pk); + else + res = check_key_cksum(pf_key, pk); + } + if (res >= 0) + res = pgp_expect_packet_end(pf_key); + + if (pf_decrypt) + pullf_free(pf_decrypt); + if (cfb) + pgp_cfb_free(cfb); + + if (res < 0) + pgp_key_free(pk); + else + *pk_p = pk; + + return res; +} + +static int +internal_read_key(PullFilter * src, PGP_PubKey ** pk_p, + const uint8 *psw, int psw_len, int pubtype) +{ + PullFilter *pkt = NULL; + int res; + uint8 tag; + int len; + PGP_PubKey *enc_key = NULL; + PGP_PubKey *pk = NULL; + int got_main_key = 0; + + /* + * Search for encryption key. + * + * Error out on anything fancy. + */ + while (1) + { + res = pgp_parse_pkt_hdr(src, &tag, &len, 0); + if (res <= 0) + break; + res = pgp_create_pkt_reader(&pkt, src, len, res, NULL); + if (res < 0) + break; + + switch (tag) + { + case PGP_PKT_PUBLIC_KEY: + case PGP_PKT_SECRET_KEY: + if (got_main_key) + { + res = PXE_PGP_MULTIPLE_KEYS; + break; + } + got_main_key = 1; + res = pgp_skip_packet(pkt); + break; + + case PGP_PKT_PUBLIC_SUBKEY: + if (pubtype != 0) + res = PXE_PGP_EXPECT_SECRET_KEY; + else + res = _pgp_read_public_key(pkt, &pk); + break; + + case PGP_PKT_SECRET_SUBKEY: + if (pubtype != 1) + res = PXE_PGP_EXPECT_PUBLIC_KEY; + else + res = process_secret_key(pkt, &pk, psw, psw_len); + break; + + case PGP_PKT_SIGNATURE: + case PGP_PKT_MARKER: + case PGP_PKT_TRUST: + case PGP_PKT_USER_ID: + case PGP_PKT_USER_ATTR: + case PGP_PKT_PRIV_61: + res = pgp_skip_packet(pkt); + break; + default: + px_debug("unknown/unexpected packet: %d", tag); + res = PXE_PGP_UNEXPECTED_PKT; + } + pullf_free(pkt); + pkt = NULL; + + if (pk != NULL) + { + if (res >= 0 && pk->can_encrypt) + { + if (enc_key == NULL) + { + enc_key = pk; + pk = NULL; + } + else + res = PXE_PGP_MULTIPLE_SUBKEYS; + } + + if (pk) + pgp_key_free(pk); + pk = NULL; + } + + if (res < 0) + break; + } + + if (pkt) + pullf_free(pkt); + + if (res < 0) + { + if (enc_key) + pgp_key_free(enc_key); + return res; + } + + if (!enc_key) + res = PXE_PGP_NO_USABLE_KEY; + else + *pk_p = enc_key; + return res; +} + +int +pgp_set_pubkey(PGP_Context * ctx, MBuf * keypkt, + const uint8 *key, int key_len, int pubtype) +{ + int res; + PullFilter *src; + PGP_PubKey *pk = NULL; + + res = pullf_create_mbuf_reader(&src, keypkt); + if (res < 0) + return res; + + res = internal_read_key(src, &pk, key, key_len, pubtype); + pullf_free(src); + + if (res >= 0) + ctx->pub_key = pk; + + return res < 0 ? res : 0; +} diff --git a/contrib/pgcrypto/pgp-s2k.c b/contrib/pgcrypto/pgp-s2k.c new file mode 100644 index 0000000..205d701 --- /dev/null +++ b/contrib/pgcrypto/pgp-s2k.c @@ -0,0 +1,302 @@ +/* + * pgp-s2k.c + * OpenPGP string2key functions. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +static int +calc_s2k_simple(PGP_S2K * s2k, PX_MD * md, const uint8 *key, + unsigned key_len) +{ + unsigned md_bs, + md_rlen; + uint8 buf[PGP_MAX_DIGEST]; + unsigned preload; + unsigned remain; + uint8 *dst = s2k->key; + + md_bs = px_md_block_size(md); + md_rlen = px_md_result_size(md); + + remain = s2k->key_len; + preload = 0; + while (remain > 0) + { + px_md_reset(md); + + if (preload) + { + memset(buf, 0, preload); + px_md_update(md, buf, preload); + } + preload++; + + px_md_update(md, key, key_len); + px_md_finish(md, buf); + + if (remain > md_rlen) + { + memcpy(dst, buf, md_rlen); + dst += md_rlen; + remain -= md_rlen; + } + else + { + memcpy(dst, buf, remain); + remain = 0; + } + } + return 0; +} + +static int +calc_s2k_salted(PGP_S2K * s2k, PX_MD * md, const uint8 *key, unsigned key_len) +{ + unsigned md_bs, + md_rlen; + uint8 buf[PGP_MAX_DIGEST]; + unsigned preload = 0; + uint8 *dst; + unsigned remain; + + md_bs = px_md_block_size(md); + md_rlen = px_md_result_size(md); + + dst = s2k->key; + remain = s2k->key_len; + while (remain > 0) + { + px_md_reset(md); + + if (preload > 0) + { + memset(buf, 0, preload); + px_md_update(md, buf, preload); + } + preload++; + + px_md_update(md, s2k->salt, PGP_S2K_SALT); + px_md_update(md, key, key_len); + px_md_finish(md, buf); + + if (remain > md_rlen) + { + memcpy(dst, buf, md_rlen); + remain -= md_rlen; + dst += md_rlen; + } + else + { + memcpy(dst, buf, remain); + remain = 0; + } + } + return 0; +} + +static int +calc_s2k_iter_salted(PGP_S2K * s2k, PX_MD * md, const uint8 *key, + unsigned key_len) +{ + unsigned md_bs, + md_rlen; + uint8 buf[PGP_MAX_DIGEST]; + uint8 *dst; + unsigned preload = 0; + unsigned remain, + c, + cval, + curcnt, + count; + + cval = s2k->iter; + count = ((unsigned) 16 + (cval & 15)) << ((cval >> 4) + 6); + + md_bs = px_md_block_size(md); + md_rlen = px_md_result_size(md); + + remain = s2k->key_len; + dst = s2k->key; + while (remain > 0) + { + px_md_reset(md); + + if (preload) + { + memset(buf, 0, preload); + px_md_update(md, buf, preload); + } + preload++; + + px_md_update(md, s2k->salt, PGP_S2K_SALT); + px_md_update(md, key, key_len); + curcnt = PGP_S2K_SALT + key_len; + + while (curcnt < count) + { + if (curcnt + PGP_S2K_SALT < count) + c = PGP_S2K_SALT; + else + c = count - curcnt; + px_md_update(md, s2k->salt, c); + curcnt += c; + + if (curcnt + key_len < count) + c = key_len; + else if (curcnt < count) + c = count - curcnt; + else + break; + px_md_update(md, key, c); + curcnt += c; + } + px_md_finish(md, buf); + + if (remain > md_rlen) + { + memcpy(dst, buf, md_rlen); + remain -= md_rlen; + dst += md_rlen; + } + else + { + memcpy(dst, buf, remain); + remain = 0; + } + } + return 0; +} + +/* + * Decide S2K_ISALTED iteration count + * + * Too small: weak + * Too big: slow + * gpg defaults to 96 => 65536 iters + * let it float a bit: 96 + 32 => 262144 iters + */ +static int +decide_count(unsigned rand_byte) +{ + return 96 + (rand_byte & 0x1F); +} + +int +pgp_s2k_fill(PGP_S2K * s2k, int mode, int digest_algo) +{ + int res = 0; + uint8 tmp; + + s2k->mode = mode; + s2k->digest_algo = digest_algo; + + switch (s2k->mode) + { + case 0: + break; + case 1: + res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT); + break; + case 3: + res = px_get_pseudo_random_bytes(s2k->salt, PGP_S2K_SALT); + if (res < 0) + break; + res = px_get_pseudo_random_bytes(&tmp, 1); + if (res < 0) + break; + s2k->iter = decide_count(tmp); + break; + default: + res = PXE_PGP_BAD_S2K_MODE; + } + return res; +} + +int +pgp_s2k_read(PullFilter * src, PGP_S2K * s2k) +{ + int res = 0; + + GETBYTE(src, s2k->mode); + GETBYTE(src, s2k->digest_algo); + switch (s2k->mode) + { + case 0: + break; + case 1: + res = pullf_read_fixed(src, 8, s2k->salt); + break; + case 3: + res = pullf_read_fixed(src, 8, s2k->salt); + if (res < 0) + break; + GETBYTE(src, s2k->iter); + break; + default: + res = PXE_PGP_BAD_S2K_MODE; + } + return res; +} + +int +pgp_s2k_process(PGP_S2K * s2k, int cipher, const uint8 *key, int key_len) +{ + int res; + PX_MD *md; + + s2k->key_len = pgp_get_cipher_key_size(cipher); + if (s2k->key_len <= 0) + return PXE_PGP_UNSUPPORTED_CIPHER; + + res = pgp_load_digest(s2k->digest_algo, &md); + if (res < 0) + return res; + + switch (s2k->mode) + { + case 0: + res = calc_s2k_simple(s2k, md, key, key_len); + break; + case 1: + res = calc_s2k_salted(s2k, md, key, key_len); + break; + case 3: + res = calc_s2k_iter_salted(s2k, md, key, key_len); + break; + default: + res = PXE_PGP_BAD_S2K_MODE; + } + px_md_free(md); + return res; +} diff --git a/contrib/pgcrypto/pgp.c b/contrib/pgcrypto/pgp.c new file mode 100644 index 0000000..77486e4 --- /dev/null +++ b/contrib/pgcrypto/pgp.c @@ -0,0 +1,360 @@ +/* + * pgp.c + * Various utility stuff. + * + * Copyright (c) 2005 Marko Kreen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $PostgreSQL$ + */ + +#include "postgres.h" + +#include "px.h" +#include "mbuf.h" +#include "pgp.h" + +/* + * Defaults. + */ +static int def_cipher_algo = PGP_SYM_AES_128; +static int def_s2k_cipher_algo = -1; +static int def_s2k_mode = PGP_S2K_ISALTED; +static int def_s2k_digest_algo = PGP_DIGEST_SHA1; +static int def_compress_algo = PGP_COMPR_NONE; +static int def_compress_level = 6; +static int def_disable_mdc = 0; +static int def_use_sess_key = 0; +static int def_text_mode = 0; +static int def_unicode_mode = 0; +static int def_convert_crlf = 0; + +struct digest_info +{ + const char *name; + int code; + const char *int_name; +}; + +struct cipher_info +{ + const char *name; + int code; + const char *int_name; + int key_len; + int block_len; +}; + +static const struct digest_info digest_list[] = { + {"md5", PGP_DIGEST_MD5}, + {"sha1", PGP_DIGEST_SHA1}, + {"sha-1", PGP_DIGEST_SHA1}, + {"ripemd160", PGP_DIGEST_RIPEMD160}, + {"sha256", PGP_DIGEST_SHA256}, + {"sha384", PGP_DIGEST_SHA384}, + {"sha512", PGP_DIGEST_SHA512}, + {NULL, 0} +}; + +static const struct cipher_info cipher_list[] = { + {"3des", PGP_SYM_DES3, "3des-ecb", 192 / 8, 64 / 8}, + {"cast5", PGP_SYM_CAST5, "cast5-ecb", 128 / 8, 64 / 8}, + {"bf", PGP_SYM_BLOWFISH, "bf-ecb", 128 / 8, 64 / 8}, + {"blowfish", PGP_SYM_BLOWFISH, "bf-ecb", 128 / 8, 64 / 8}, + {"aes", PGP_SYM_AES_128, "aes-ecb", 128 / 8, 128 / 8}, + {"aes128", PGP_SYM_AES_128, "aes-ecb", 128 / 8, 128 / 8}, + {"aes192", PGP_SYM_AES_192, "aes-ecb", 192 / 8, 128 / 8}, + {"aes256", PGP_SYM_AES_256, "aes-ecb", 256 / 8, 128 / 8}, + {"twofish", PGP_SYM_TWOFISH, "twofish-ecb", 256 / 8, 128 / 8}, + {NULL, 0, NULL} +}; + +static const struct cipher_info * +get_cipher_info(int code) +{ + const struct cipher_info *i; + + for (i = cipher_list; i->name; i++) + if (i->code == code) + return i; + return NULL; +} + +int +pgp_get_digest_code(const char *name) +{ + const struct digest_info *i; + + for (i = digest_list; i->name; i++) + if (pg_strcasecmp(i->name, name) == 0) + return i->code; + return PXE_PGP_UNSUPPORTED_HASH; +} + +int +pgp_get_cipher_code(const char *name) +{ + const struct cipher_info *i; + + for (i = cipher_list; i->name; i++) + if (pg_strcasecmp(i->name, name) == 0) + return i->code; + return PXE_PGP_UNSUPPORTED_CIPHER; +} + +const char * +pgp_get_digest_name(int code) +{ + const struct digest_info *i; + + for (i = digest_list; i->name; i++) + if (i->code == code) + return i->name; + return NULL; +} + +const char * +pgp_get_cipher_name(int code) +{ + const struct cipher_info *i = get_cipher_info(code); + + if (i != NULL) + return i->name; + return NULL; +} + +int +pgp_get_cipher_key_size(int code) +{ + const struct cipher_info *i = get_cipher_info(code); + + if (i != NULL) + return i->key_len; + return 0; +} + +int +pgp_get_cipher_block_size(int code) +{ + const struct cipher_info *i = get_cipher_info(code); + + if (i != NULL) + return i->block_len; + return 0; +} + +int +pgp_load_cipher(int code, PX_Cipher ** res) +{ + int err; + const struct cipher_info *i = get_cipher_info(code); + + if (i == NULL) + return PXE_PGP_CORRUPT_DATA; + + err = px_find_cipher(i->int_name, res); + if (err == 0) + return 0; + + return PXE_PGP_UNSUPPORTED_CIPHER; +} + +int +pgp_load_digest(int code, PX_MD ** res) +{ + int err; + const char *name = pgp_get_digest_name(code); + + if (name == NULL) + return PXE_PGP_CORRUPT_DATA; + + err = px_find_digest(name, res); + if (err == 0) + return 0; + + return PXE_PGP_UNSUPPORTED_HASH; +} + +int +pgp_init(PGP_Context ** ctx_p) +{ + PGP_Context *ctx; + + ctx = px_alloc(sizeof *ctx); + memset(ctx, 0, sizeof *ctx); + + ctx->cipher_algo = def_cipher_algo; + ctx->s2k_cipher_algo = def_s2k_cipher_algo; + ctx->s2k_mode = def_s2k_mode; + ctx->s2k_digest_algo = def_s2k_digest_algo; + ctx->compress_algo = def_compress_algo; + ctx->compress_level = def_compress_level; + ctx->disable_mdc = def_disable_mdc; + ctx->use_sess_key = def_use_sess_key; + ctx->unicode_mode = def_unicode_mode; + ctx->convert_crlf = def_convert_crlf; + ctx->text_mode = def_text_mode; + + *ctx_p = ctx; + return 0; +} + +int +pgp_free(PGP_Context * ctx) +{ + if (ctx->pub_key) + pgp_key_free(ctx->pub_key); + memset(ctx, 0, sizeof *ctx); + px_free(ctx); + return 0; +} + +int +pgp_disable_mdc(PGP_Context * ctx, int disable) +{ + ctx->disable_mdc = disable ? 1 : 0; + return 0; +} + +int +pgp_set_sess_key(PGP_Context * ctx, int use) +{ + ctx->use_sess_key = use ? 1 : 0; + return 0; +} + +int +pgp_set_convert_crlf(PGP_Context * ctx, int doit) +{ + ctx->convert_crlf = doit ? 1 : 0; + return 0; +} + +int +pgp_set_s2k_mode(PGP_Context * ctx, int mode) +{ + int err = PXE_OK; + + switch (mode) + { + case PGP_S2K_SIMPLE: + case PGP_S2K_SALTED: + case PGP_S2K_ISALTED: + ctx->s2k_mode = mode; + break; + default: + err = PXE_ARGUMENT_ERROR; + break; + } + return err; +} + +int +pgp_set_compress_algo(PGP_Context * ctx, int algo) +{ + switch (algo) + { + case PGP_COMPR_NONE: + case PGP_COMPR_ZIP: + case PGP_COMPR_ZLIB: + case PGP_COMPR_BZIP2: + ctx->compress_algo = algo; + return 0; + } + return PXE_ARGUMENT_ERROR; +} + +int +pgp_set_compress_level(PGP_Context * ctx, int level) +{ + if (level >= 0 && level <= 9) + { + ctx->compress_level = level; + return 0; + } + return PXE_ARGUMENT_ERROR; +} + +int +pgp_set_text_mode(PGP_Context * ctx, int mode) +{ + ctx->text_mode = mode; + return 0; +} + +int +pgp_set_cipher_algo(PGP_Context * ctx, const char *name) +{ + int code = pgp_get_cipher_code(name); + + if (code < 0) + return code; + ctx->cipher_algo = code; + return 0; +} + +int +pgp_set_s2k_cipher_algo(PGP_Context * ctx, const char *name) +{ + int code = pgp_get_cipher_code(name); + + if (code < 0) + return code; + ctx->s2k_cipher_algo = code; + return 0; +} + +int +pgp_set_s2k_digest_algo(PGP_Context * ctx, const char *name) +{ + int code = pgp_get_digest_code(name); + + if (code < 0) + return code; + ctx->s2k_digest_algo = code; + return 0; +} + +int +pgp_get_unicode_mode(PGP_Context * ctx) +{ + return ctx->unicode_mode; +} + +int +pgp_set_unicode_mode(PGP_Context * ctx, int mode) +{ + ctx->unicode_mode = mode ? 1 : 0; + return 0; +} + +int +pgp_set_symkey(PGP_Context * ctx, const uint8 *key, int len) +{ + if (key == NULL || len < 1) + return PXE_ARGUMENT_ERROR; + ctx->sym_key = key; + ctx->sym_key_len = len; + return 0; +} diff --git a/contrib/pgcrypto/rijndael.c b/contrib/pgcrypto/rijndael.c index cb65d55..5ebbaae 100644 --- a/contrib/pgcrypto/rijndael.c +++ b/contrib/pgcrypto/rijndael.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: rijndael.c,v 1.6 2000/12/09 18:51:34 markus Exp $ */ /* $PostgreSQL$ */ diff --git a/contrib/pgcrypto/rijndael.h b/contrib/pgcrypto/rijndael.h index aab1f51..41c1a2a 100644 --- a/contrib/pgcrypto/rijndael.h +++ b/contrib/pgcrypto/rijndael.h @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: rijndael.h,v 1.3 2001/05/09 23:01:32 markus Exp $ */ /* This is an independent implementation of the encryption algorithm: */ /* */ diff --git a/contrib/pgcrypto/sha1.c b/contrib/pgcrypto/sha1.c index 0025f57..7272917 100644 --- a/contrib/pgcrypto/sha1.c +++ b/contrib/pgcrypto/sha1.c @@ -1,4 +1,4 @@ -/* $KAME$ */ +/* $KAME: sha1.c,v 1.3 2000/02/22 14:01:18 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. diff --git a/contrib/pgcrypto/sha1.h b/contrib/pgcrypto/sha1.h index 1a1d664..a08b294 100644 --- a/contrib/pgcrypto/sha1.h +++ b/contrib/pgcrypto/sha1.h @@ -1,5 +1,5 @@ /* $PostgreSQL$ */ -/* $KAME$ */ +/* $KAME: sha1.h,v 1.4 2000/02/22 14:01:18 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. diff --git a/contrib/pgcrypto/sha2.c b/contrib/pgcrypto/sha2.c index 340471c..e138f75 100644 --- a/contrib/pgcrypto/sha2.c +++ b/contrib/pgcrypto/sha2.c @@ -1,4 +1,4 @@ -/* $OpenBSD$ */ +/* $OpenBSD: sha2.c,v 1.6 2004/05/03 02:57:36 millert Exp $ */ /* * FILE: sha2.c diff --git a/contrib/pgcrypto/sha2.h b/contrib/pgcrypto/sha2.h index 2463330..8d01989 100644 --- a/contrib/pgcrypto/sha2.h +++ b/contrib/pgcrypto/sha2.h @@ -1,5 +1,5 @@ /* $PostgreSQL$ */ -/* $OpenBSD$ */ +/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */ /* * FILE: sha2.h diff --git a/contrib/pgcrypto/sql/3des.sql b/contrib/pgcrypto/sql/3des.sql new file mode 100644 index 0000000..99b936f --- /dev/null +++ b/contrib/pgcrypto/sql/3des.sql @@ -0,0 +1,30 @@ +-- +-- 3DES cipher +-- + +-- test vector from somewhere +SELECT encode(encrypt( +decode('80 00 00 00 00 00 00 00', 'hex'), +decode('01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 01 + 01 01 01 01 01 01 01 01', 'hex'), +'3des-ecb/pad:none'), 'hex'); +-- val 95 F8 A5 E5 DD 31 D9 00 + +select encode( encrypt('', 'foo', '3des'), 'hex'); +-- 10 bytes key +select encode( encrypt('foo', '0123456789', '3des'), 'hex'); +-- 22 bytes key +select encode( encrypt('foo', '0123456789012345678901', '3des'), 'hex'); + +-- decrypt +select decrypt(encrypt('foo', '0123456', '3des'), '0123456', '3des'); + +-- iv +select encode(encrypt_iv('foo', '0123456', 'abcd', '3des'), 'hex'); +select decrypt_iv(decode('50735067b073bb93', 'hex'), '0123456', 'abcd', '3des'); + +-- long message +select encode(encrypt('Lets try a longer message.', '0123456789012345678901', '3des'), 'hex'); +select decrypt(encrypt('Lets try a longer message.', '0123456789012345678901', '3des'), '0123456789012345678901', '3des'); + diff --git a/contrib/pgcrypto/sql/cast5.sql b/contrib/pgcrypto/sql/cast5.sql new file mode 100644 index 0000000..0761f34 --- /dev/null +++ b/contrib/pgcrypto/sql/cast5.sql @@ -0,0 +1,46 @@ +-- +-- Cast5 cipher +-- + +-- test vectors from RFC2144 + +-- 128 bit key +SELECT encode(encrypt( +decode('01 23 45 67 89 AB CD EF', 'hex'), +decode('01 23 45 67 12 34 56 78 23 45 67 89 34 56 78 9A', 'hex'), +'cast5-ecb/pad:none'), 'hex'); +-- result: 23 8B 4F E5 84 7E 44 B2 + +-- 80 bit key +SELECT encode(encrypt( +decode('01 23 45 67 89 AB CD EF', 'hex'), +decode('01 23 45 67 12 34 56 78 23 45', 'hex'), +'cast5-ecb/pad:none'), 'hex'); +-- result: EB 6A 71 1A 2C 02 27 1B + +-- 40 bit key +SELECT encode(encrypt( +decode('01 23 45 67 89 AB CD EF', 'hex'), +decode('01 23 45 67 12', 'hex'), +'cast5-ecb/pad:none'), 'hex'); +-- result: 7A C8 16 D1 6E 9B 30 2E + +-- cbc + +-- empty data +select encode( encrypt('', 'foo', 'cast5'), 'hex'); +-- 10 bytes key +select encode( encrypt('foo', '0123456789', 'cast5'), 'hex'); + +-- decrypt +select decrypt(encrypt('foo', '0123456', 'cast5'), '0123456', 'cast5'); + +-- iv +select encode(encrypt_iv('foo', '0123456', 'abcd', 'cast5'), 'hex'); +select decrypt_iv(decode('384a970695ce016a', 'hex'), + '0123456', 'abcd', 'cast5'); + +-- long message +select encode(encrypt('Lets try a longer message.', '0123456789', 'cast5'), 'hex'); +select decrypt(encrypt('Lets try a longer message.', '0123456789', 'cast5'), '0123456789', 'cast5'); + diff --git a/contrib/pgcrypto/sql/des.sql b/contrib/pgcrypto/sql/des.sql new file mode 100644 index 0000000..179bd83 --- /dev/null +++ b/contrib/pgcrypto/sql/des.sql @@ -0,0 +1,28 @@ +-- +-- DES cipher +-- + +-- no official test vectors atm + +-- from blowfish.sql +SELECT encode(encrypt( +decode('0123456789abcdef', 'hex'), +decode('fedcba9876543210', 'hex'), +'des-ecb/pad:none'), 'hex'); + +-- empty data +select encode( encrypt('', 'foo', 'des'), 'hex'); +-- 8 bytes key +select encode( encrypt('foo', '01234589', 'des'), 'hex'); + +-- decrypt +select decrypt(encrypt('foo', '0123456', 'des'), '0123456', 'des'); + +-- iv +select encode(encrypt_iv('foo', '0123456', 'abcd', 'des'), 'hex'); +select decrypt_iv(decode('50735067b073bb93', 'hex'), '0123456', 'abcd', 'des'); + +-- long message +select encode(encrypt('Lets try a longer message.', '01234567', 'des'), 'hex'); +select decrypt(encrypt('Lets try a longer message.', '01234567', 'des'), '01234567', 'des'); + diff --git a/contrib/pgcrypto/sql/pgp-armor.sql b/contrib/pgcrypto/sql/pgp-armor.sql new file mode 100644 index 0000000..040c4ac --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-armor.sql @@ -0,0 +1,56 @@ +-- +-- PGP Armor +-- + +select armor(''); +select armor('test'); +select dearmor(armor('')); +select dearmor(armor('zooka')); + +select armor('0123456789abcdef0123456789abcdef0123456789abcdef +0123456789abcdef0123456789abcdef0123456789abcdef'); + +-- lots formatting +select dearmor(' a pgp msg: + +-----BEGIN PGP MESSAGE----- +Comment: Some junk + +em9va2E= + + =D5cR + +-----END PGP MESSAGE-----'); + +-- lots messages +select dearmor(' +wrong packet: + -----BEGIN PGP MESSAGE----- + + d3Jvbmc= + =vCYP + -----END PGP MESSAGE----- + +right packet: +-----BEGIN PGP MESSAGE----- + +cmlnaHQ= +=nbpj +-----END PGP MESSAGE----- + +use only first packet +-----BEGIN PGP MESSAGE----- + +d3Jvbmc= +=vCYP +-----END PGP MESSAGE----- +'); + +-- bad crc +select dearmor(' +-----BEGIN PGP MESSAGE----- + +em9va2E= +=ZZZZ +-----END PGP MESSAGE----- +'); diff --git a/contrib/pgcrypto/sql/pgp-compression.sql b/contrib/pgcrypto/sql/pgp-compression.sql new file mode 100644 index 0000000..f71c176 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-compression.sql @@ -0,0 +1,31 @@ +-- +-- PGP compression support +-- + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- + +ww0ECQMCsci6AdHnELlh0kQB4jFcVwHMJg0Bulop7m3Mi36s15TAhBo0AnzIrRFrdLVCkKohsS6+ +DMcmR53SXfLoDJOv/M8uKj3QSq7oWNIp95pxfA== +=tbSn +-----END PGP MESSAGE----- +'), 'key', 'expect-compress-algo=1'); + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=0'), + 'key', 'expect-compress-algo=0'); + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=1'), + 'key', 'expect-compress-algo=1'); + +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', 'compress-algo=2'), + 'key', 'expect-compress-algo=2'); + +-- level=0 should turn compression off +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret message', 'key', + 'compress-algo=2, compress-level=0'), + 'key', 'expect-compress-algo=0'); + diff --git a/contrib/pgcrypto/sql/pgp-decrypt.sql b/contrib/pgcrypto/sql/pgp-decrypt.sql new file mode 100644 index 0000000..93535ab --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-decrypt.sql @@ -0,0 +1,266 @@ +-- +-- pgp_descrypt tests +-- + +-- Checking ciphers +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.blowfish.sha1.mdc.s2k3.z0 + +jA0EBAMCfFNwxnvodX9g0jwB4n4s26/g5VmKzVab1bX1SmwY7gvgvlWdF3jKisvS +yA6Ce1QTMK3KdL2MPfamsTUSAML8huCJMwYQFfE= +=JcP+ +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCci97v0Q6Z0Zg0kQBsVf5Oe3iC+FBzUmuMV9KxmAyOMyjCc/5i8f1Eest +UTAsG35A1vYs02VARKzGz6xI2UHwFUirP+brPBg3Ee7muOx8pA== +=XtrP +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k3.z0 + +jA0ECAMCI7YQpWqp3D1g0kQBCjB7GlX7+SQeXNleXeXQ78ZAPNliquGDq9u378zI +5FPTqAhIB2/2fjY8QEIs1ai00qphjX2NitxV/3Wn+6dufB4Q4g== +=rCZt +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k3.z0 + +jA0ECQMC4f/5djqCC1Rg0kQBTHEPsD+Sw7biBsM2er3vKyGPAQkuTBGKC5ie7hT/ +lceMfQdbAg6oTFyJpk/wH18GzRDphCofg0X8uLgkAKMrpcmgog== +=fB6S +-----END PGP MESSAGE----- +'), 'foobar'); + +-- Checking MDC modes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.nomdc.s2k3.z0 + +jA0EBwMCnv07rlXqWctgyS2Dm2JfOKCRL4sLSLJUC8RS2cH7cIhKSuLitOtyquB+ +u9YkgfJfsuRJmgQ9tmo= +=60ui +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCEeP3idNjQ1Bg0kQBf4G0wX+2QNzLh2YNwYkQgQkfYhn/hLXjV4nK9nsE +8Ex1Dsdt5UPvOz8W8VKQRS6loOfOe+yyXil8W3IYFwUpdDUi+Q== +=moGf +-----END PGP MESSAGE----- +'), 'foobar'); + +-- Checking hashes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.md5.mdc.s2k3.z0 + +jA0EBwMClrXXtOXetohg0kQBn0Kl1ymevQZRHkdoYRHgzCwSQEiss7zYff2UNzgO +KyRrHf7zEBuZiZ2AG34jNVMOLToj1jJUg5zTSdecUzQVCykWTA== +=NyLk +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCApbdlrURoWJg0kQBzHM/E0o7djY82bNuspjxjAcPFrrtp0uvDdMQ4z2m +/PM8jhgI5vxFYfNQjLl8y3fHYIomk9YflN9K/Q13iq8A8sjeTw== +=FxbQ +-----END PGP MESSAGE----- +'), 'foobar'); + +-- Checking S2K modes +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k0.z0 + +jAQEBwAC0kQBKTaLAKE3xzps+QIZowqRNb2eAdzBw2LxEW2YD5PgNlbhJdGg+dvw +Ah9GXjGS1TVALzTImJbz1uHUZRfhJlFbc5yGQw== +=YvkV +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k1.z0 + +jAwEBwEC/QTByBLI3b/SRAHPxKzI6SZBo5lAEOD+EsvKQWO4adL9tDY+++Iqy1xK +4IaWXVKEj9R2Lr2xntWWMGZtcKtjD2lFFRXXd9dZp1ZThNDz +=dbXm +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCEq4Su3ZqNEJg0kQB4QG5jBTKF0i04xtH+avzmLhstBNRxvV3nsmB3cwl +z+9ZaA/XdSx5ZiFnMym8P6r8uY9rLjjNptvvRHlxIReF+p9MNg== +=VJKg +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k0.z0 + +jAQECAAC0kQBBDnQWkgsx9YFaqDfWmpsiyAJ6y2xG/sBvap1dySYEMuZ+wJTXQ9E +Cr3i2M7TgVZ0M4jp4QL0adG1lpN5iK7aQeOwMw== +=cg+i +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k1.z0 + +jAwECAECruOfyNDFiTnSRAEVoGXm4A9UZKkWljdzjEO/iaE7mIraltIpQMkiqCh9 +7h8uZ2u9uRBOv222fZodGvc6bvq/4R4hAa/6qSHtm8mdmvGt +=aHmC +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes192.sha1.mdc.s2k3.z0 + +jA0ECAMCjFn6SRi3SONg0kQBqtSHPaD0m7rXfDAhCWU/ypAsI93GuHGRyM99cvMv +q6eF6859ZVnli3BFSDSk3a4e/pXhglxmDYCfjAXkozKNYLo6yw== +=K0LS +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k0.z0 + +jAQECQAC0kQB4L1eMbani07XF2ZYiXNK9LW3v8w41oUPl7dStmrJPQFwsdxmrDHu +rQr3WbdKdY9ufjOE5+mXI+EFkSPrF9rL9NCq6w== +=RGts +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k1.z0 + +jAwECQECKHhrou7ZOIXSRAHWIVP+xjVQcjAVBTt+qh9SNzYe248xFTwozkwev3mO ++KVJW0qhk0An+Y2KF99/bYFl9cL5D3Tl43fC8fXGl3x3m7pR +=SUrU +-----END PGP MESSAGE----- +'), 'foobar'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes256.sha1.mdc.s2k3.z0 + +jA0ECQMCjc8lwZu8Fz1g0kQBkEzjImi21liep5jj+3dAJ2aZFfUkohi8b3n9z+7+ +4+NRzL7cMW2RLAFnJbiqXDlRHMwleeuLN1up2WIxsxtYYuaBjA== +=XZrG +-----END PGP MESSAGE----- +'), 'foobar'); + +-- Checking longer passwords +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCx6dBiuqrYNRg0kQBEo63AvA1SCslxP7ayanLf1H0/hlk2nONVhTwVEWi +tTGup1mMz6Cfh1uDRErUuXpx9A0gdMu7zX0o5XjrL7WGDAZdSw== +=XKKG +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCBDvYuS990iFg0kQBW31UK5OiCjWf5x6KJ8qNNT2HZWQCjCBZMU0XsOC6 +CMxFKadf144H/vpoV9GA0f22keQgCl0EsTE4V4lweVOPTKCMJg== +=gWDh +-----END PGP MESSAGE----- +'), '0123456789abcdefghij2jk4h5g2j54khg23h54g2kh54g2khj54g23hj54'); + +select pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCqXbFafC+ofVg0kQBejyiPqH0QMERVGfmPOjtAxvyG5KDIJPYojTgVSDt +FwsDabdQUz5O7bgNSnxfmyw1OifGF+W2bIn/8W+0rDf8u3+O+Q== +=OxOF +-----END PGP MESSAGE----- +'), 'x'); + +-- Checking various data +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat1.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCGJ+SpuOysINg0kQBJfSjzsW0x4OVcAyr17O7FBvMTwIGeGcJd99oTQU8 +Xtx3kDqnhUq9Z1fS3qPbi5iNP2A9NxOBxPWz2JzxhydANlgbxg== +=W/ik +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); +-- expected: 0225e3ede6f2587b076d021a189ff60aad67e066 + +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat2.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCvdpDvidNzMxg0jUBvj8eS2+1t/9/zgemxvhtc0fvdKGGbjH7dleaTJRB +SaV9L04ky1qECNDx3XjnoKLC+H7IOQ== +=Fxen +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); +-- expected: da39a3ee5e6b4b0d3255bfef95601890afd80709 + +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: dat3.aes.sha1.mdc.s2k3.z0 + +jA0EBwMCxQvxJZ3G/HRg0lgBeYmTa7/uDAjPyFwSX4CYBgpZWVn/JS8JzILrcWF8 +gFnkUKIE0PSaYFp+Yi1VlRfUtRQ/X/LYNGa7tWZS+4VQajz2Xtz4vUeAEiYFYPXk +73Hb8m1yRhQK +=ivrD +-----END PGP MESSAGE----- +'), '0123456789abcdefghij'), 'sha1'), 'hex'); +-- expected: 5e5c135efc0dd00633efc6dfd6e731ea408a5b4c + +-- Checking CRLF +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: crlf mess + +ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms +a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs +=mBP9 +-----END PGP MESSAGE----- +'), 'key', 'convert-crlf=0'), 'sha1'), 'hex'); +-- expected: 9353062be7720f1446d30b9e75573a4833886784 + +select encode(digest(pgp_sym_decrypt(dearmor(' +-----BEGIN PGP MESSAGE----- +Comment: crlf mess + +ww0ECQMCt7VAtby6l4Bi0lgB5KMIZiiF/b3CfMfUyY0eDncsGXtkbu1X+l9brjpMP8eJnY79Amms +a3nsOzKTXUfS9VyaXo8IrncM6n7fdaXpwba/3tNsAhJG4lDv1k4g9v8Ix2dfv6Rs +=mBP9 +-----END PGP MESSAGE----- +'), 'key', 'convert-crlf=1'), 'sha1'), 'hex'); +-- expected: 7efefcab38467f7484d6fa43dc86cf5281bd78e2 diff --git a/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql b/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql new file mode 100644 index 0000000..80fb4d1 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-encrypt-DISABLED.sql @@ -0,0 +1,3 @@ + +-- no random source + diff --git a/contrib/pgcrypto/sql/pgp-encrypt.sql b/contrib/pgcrypto/sql/pgp-encrypt.sql new file mode 100644 index 0000000..b663e05 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-encrypt.sql @@ -0,0 +1,96 @@ +-- +-- PGP encrypt +-- + +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), 'key'); + +-- check whether the defaults are ok +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), + 'key', 'expect-cipher-algo=aes128, + expect-disable-mdc=0, + expect-sess-key=0, + expect-s2k-mode=3, + expect-s2k-digest-algo=sha1, + expect-compress-algo=0 + '); + +-- maybe the expect- stuff simply does not work +select pgp_sym_decrypt(pgp_sym_encrypt('Secret.', 'key'), + 'key', 'expect-cipher-algo=bf, + expect-disable-mdc=1, + expect-sess-key=1, + expect-s2k-mode=0, + expect-s2k-digest-algo=md5, + expect-compress-algo=1 + '); + +-- bytea as text +select pgp_sym_decrypt(pgp_sym_encrypt_bytea('Binary', 'baz'), 'baz'); + +-- text as bytea +select pgp_sym_decrypt_bytea(pgp_sym_encrypt('Text', 'baz'), 'baz'); + + +-- algorithm change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=bf'), + 'key', 'expect-cipher-algo=bf'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes'), + 'key', 'expect-cipher-algo=aes128'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'cipher-algo=aes192'), + 'key', 'expect-cipher-algo=aes192'); + +-- s2k change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=0'), + 'key', 'expect-s2k-mode=0'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=1'), + 'key', 'expect-s2k-mode=1'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-mode=3'), + 'key', 'expect-s2k-mode=3'); + +-- s2k digest change +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=md5'), + 'key', 'expect-s2k-digest-algo=md5'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 's2k-digest-algo=sha1'), + 'key', 'expect-s2k-digest-algo=sha1'); + +-- sess key +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=0'), + 'key', 'expect-sess-key=0'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1'), + 'key', 'expect-sess-key=1'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=bf'), + 'key', 'expect-sess-key=1, expect-cipher-algo=bf'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes192'), + 'key', 'expect-sess-key=1, expect-cipher-algo=aes192'); +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'sess-key=1, cipher-algo=aes256'), + 'key', 'expect-sess-key=1, expect-cipher-algo=aes256'); + +-- no mdc +select pgp_sym_decrypt( + pgp_sym_encrypt('Secret.', 'key', 'disable-mdc=1'), + 'key', 'expect-disable-mdc=1'); + +-- crlf +select encode(pgp_sym_decrypt_bytea( + pgp_sym_encrypt('1\n2\n3\r\n', 'key', 'convert-crlf=1'), + 'key'), 'hex'); + +-- conversion should be lossless +select encode(digest(pgp_sym_decrypt( + pgp_sym_encrypt('\r\n0\n1\r\r\n\n2\r', 'key', 'convert-crlf=1'), + 'key', 'convert-crlf=1'), 'sha1'), 'hex') as result, + encode(digest('\r\n0\n1\r\r\n\n2\r', 'sha1'), 'hex') as expect; + diff --git a/contrib/pgcrypto/sql/pgp-info.sql b/contrib/pgcrypto/sql/pgp-info.sql new file mode 100644 index 0000000..c525ce3 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-info.sql @@ -0,0 +1,23 @@ +-- +-- PGP info functions +-- + +-- pgp_key_id + +select pgp_key_id(dearmor(pubkey)) from keytbl where id=1; +select pgp_key_id(dearmor(pubkey)) from keytbl where id=2; +select pgp_key_id(dearmor(pubkey)) from keytbl where id=3; +select pgp_key_id(dearmor(pubkey)) from keytbl where id=4; -- should fail +select pgp_key_id(dearmor(pubkey)) from keytbl where id=5; +select pgp_key_id(dearmor(pubkey)) from keytbl where id=6; + +select pgp_key_id(dearmor(seckey)) from keytbl where id=1; +select pgp_key_id(dearmor(seckey)) from keytbl where id=2; +select pgp_key_id(dearmor(seckey)) from keytbl where id=3; +select pgp_key_id(dearmor(seckey)) from keytbl where id=4; -- should fail +select pgp_key_id(dearmor(seckey)) from keytbl where id=5; +select pgp_key_id(dearmor(seckey)) from keytbl where id=6; + +select pgp_key_id(dearmor(data)) as data_key_id +from encdata order by id; + diff --git a/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql b/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql new file mode 100644 index 0000000..b4aec5d --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-pubkey-DISABLED.sql @@ -0,0 +1,3 @@ + +-- no bignum support + diff --git a/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql b/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql new file mode 100644 index 0000000..82c6086 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-pubkey-decrypt.sql @@ -0,0 +1,549 @@ +-- +-- PGP Public Key Encryption +-- + +-- As most of the low-level stuff is tested in symmetric key +-- tests, here's only public-key specific tests + +create table keytbl ( + id int4, + name text, + pubkey text, + seckey text +); +create table encdata ( + id int4, + data text +); + +insert into keytbl (id, name, pubkey, seckey) +values (1, 'elg1024', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx +MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV +AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc +AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd +ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P +sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI ++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9 +6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF +k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v +iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F +ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80 +=RWci +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQG7BELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQAAAnj4i4st+s+C6 +WKTIDcL1Iy0Saq8lCp60H0VsZ2FtYWwgMTAyNCA8dGVzdEBleGFtcGxlLm9yZz6I +XgQTEQIAHgUCQsghSAIbAwYLCQgHAwIDFQIDAxYCAQIeAQIXgAAKCRAcKbwNGBdz +ZDrbAJ9cp6AsjOhiLxwznsMJheGf4xkH8wCfUPjMCLm4tAEnyYn2hDNt7CB8B6Kd +ATEEQsghShAEAIeUjW2yTALCfrEG3FhM3ZLvAHWAec2O0Mn/RDr59IN/W8wDYcZp +m+oG0ZUDdIqMppQ8K2kylAH7gmYDXIP9D7MRRm/Zw3L4yFfKnVaZ6tT7szBbgW5h +iOsHoOz49NXZT4jtMLdZS1/krm5Lam2MSPod9XN0Q2asY/igIMUfGDRjAAMGA/sE +LNh3tWefqeDkoDBEYjcxdGnGVGJnNHvv/eoHy9H7dyD/kkhaOoRAa5ClYWSqD0kk +a+SqTWhKG4XcbJyo1GsP6sqGhXDTM2+LBZPMKuVJQpEfoe9ruob/BbpXglfEiVE9 +VNiY7ZVyUdj3svYn4fK2X7ue1G3cHR2tL4lnOA4pYQAA9030E4u2ZKOfJBpUM+EM +m9VmsGjaQZV4teB0R/q3W8sRIYhJBBgRAgAJBQJCyCFKAhsMAAoJEBwpvA0YF3Nk +7a8AniFFotw1x2X+oryu3Q3nNtmxoKHpAJ9HU7jw7ydg33dI9J8gVkrmsSZ2/w== +=nvqq +-----END PGP PRIVATE KEY BLOCK----- +'); + +insert into keytbl (id, name, pubkey, seckey) +values (2, 'elg2048', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n +vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke +5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO +RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij +8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl +Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt +J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/ +T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5 +0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6a7QjRWxnYW1hbCAy +MDQ4IDx0ZXN0MjA0OEBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgiCgIbAwYLCQgH +AwIDFQIDAxYCAQIeAQIXgAAKCRBI6c1W/qZo29PDAKCG724enIxRog1j+aeCp/uq +or6mbwCePuKy2/1kD1FvnhkZ/R5fpm+pdm25Ag0EQsgiIhAIAJI3Gb2Ehtz1taQ9 +AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMX +MhoWyw8ZF5Zs1mLIjFGVorePrm94N3MNPWM7x9M36bHUjx0vCZKFIhcGY1g+htE/ +QweaJzNVeA5z4qZmik41FbQyQSyHa3bOkTZu++/U6ghP+iDp5UDBjMTkVyqITUVN +gC+MR+da/I60irBVhue7younh4ovF+CrVDQJC06HZl6CAJJyA81SmRfi+dmKbbjZ +LF6rhz0norPjISJvkIqvdtM4VPBKI5wpgwCzpEqjuiKrAVujRT68zvBvJ4aVqb11 +k5QdJscAAwUH/jVJh0HbWAoiFTe+NvohfrA8vPcD0rtU3Y+siiqrabotnxJd2NuC +bxghJYGfNtnx0KDjFbCRKJVeTFok4UnuVYhXdH/c6i0/rCTNdeW2D6pmR4GfBozR +Pw/ARf+jONawGLyUj7uq13iquwMSE7VyNuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0R +QsetMq/iNBWraayKZnWUd+eQqNzE+NUo7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiF +Z1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qunfGW00ZMMTCWabg0ZgxPzMfMeIcm6525A +Yn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/RdWaISQQYEQIACQUCQsgiIgIbDAAKCRBI +6c1W/qZo25ZSAJ98WTrtl2HiX8ZqZq95v1+9cHtZPQCfZDoWQPybkNescLmXC7q5 +1kNTmEU= +=8QM5 +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQG7BELIIgoRBAC1onBpxKYgDvrgCaUWPY34947X3ogxGOfCN0p6Eqrx+2PUhm4n +vFvmczpMT4iDc0mUO+iwnwsEkXQI1eC99g8c0jnZAvzJZ5miAHL8hukMAMfDkYke +5aVvcPPc8uPDlItpszGmH0rM0V9TIt/i9QEXetpyNWhk4jj5qnohYhLeZwCgkOdO +RFAdNi4vfFPivvtAp2ffjU8D/R3x/UJCvkzi7i9rQHGo313xxmQu5BuqIjANBUij +8IE7LRPI/Qhg2hYy3sTJwImDi7VkS+fuvNVk0d6MTWplAXYU96bn12JaD21R9sKl +Fzcc+0iZI1wYA1PczisUkoTISE+dQFUsoGHfpDLhoBuesXQrhBavI8t8VPd+nkdt +J+oKA/9iRQ87FzxdYTkh2drrv69FZHc3Frsjw9nPcBq/voAvXH0MRilqyCg7HpW/ +T9naeOERksa+Rj4R57IF1l4e5oiiGJo9QmaKZcsCsXrREJCycrlEtMqXfSPy+bi5 +0yDZE/Qm1dwu13+OXOsRvkoNYjO8Mzo9K8wU12hMqN0a2bu6awAAn2F+iNBElfJS +8azqO/kEiIfpqu6/DQG0I0VsZ2FtYWwgMjA0OCA8dGVzdDIwNDhAZXhhbXBsZS5v +cmc+iF0EExECAB4FAkLIIgoCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQSOnN +Vv6maNvTwwCYkpcJmpl3aHCQdGomz7dFohDgjgCgiThZt2xTEi6GhBB1vuhk+f55 +n3+dAj0EQsgiIhAIAJI3Gb2Ehtz1taQ9AhPY4Avad2BsqD3S5X/R11Cm0KBE/04D +29dxn3f8QfxDsexYvNIZjoJPBqqZ7iMXMhoWyw8ZF5Zs1mLIjFGVorePrm94N3MN +PWM7x9M36bHUjx0vCZKFIhcGY1g+htE/QweaJzNVeA5z4qZmik41FbQyQSyHa3bO +kTZu++/U6ghP+iDp5UDBjMTkVyqITUVNgC+MR+da/I60irBVhue7younh4ovF+Cr +VDQJC06HZl6CAJJyA81SmRfi+dmKbbjZLF6rhz0norPjISJvkIqvdtM4VPBKI5wp +gwCzpEqjuiKrAVujRT68zvBvJ4aVqb11k5QdJscAAwUH/jVJh0HbWAoiFTe+Nvoh +frA8vPcD0rtU3Y+siiqrabotnxJd2NuCbxghJYGfNtnx0KDjFbCRKJVeTFok4Unu +VYhXdH/c6i0/rCTNdeW2D6pmR4GfBozRPw/ARf+jONawGLyUj7uq13iquwMSE7Vy +NuF3ycL2OxXjgOWMjkH8c+zfHHpjaZ0RQsetMq/iNBWraayKZnWUd+eQqNzE+NUo +7w1jAu7oDpy+8a1eipxzK+O0HfU5LTiFZ1Oe4Um0P2l3Xtx8nEgj4vSeoEkl2qun +fGW00ZMMTCWabg0ZgxPzMfMeIcm6525AYn2qL+X/qBJTInAl7/hgPz2D1Yd7d5/R +dWYAAVQKFPXbRaxbdArwRVXMzSD3qj/+VwwhwEDt8zmBGnlBfwVdkjQQrDUMmV1S +EwyISQQYEQIACQUCQsgiIgIbDAAKCRBI6c1W/qZo25ZSAJ4sgUfHTVsG/x3p3fcM +3b5R86qKEACggYKSwPWCs0YVRHOWqZY0pnHtLH8= +=3Dgk +-----END PGP PRIVATE KEY BLOCK----- +'); + +insert into keytbl (id, name, pubkey, seckey) +values (3, 'elg4096', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY +05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz +2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98 +cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN +SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4 +18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG +7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt +q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh +uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk27QjRWxnYW1hbCA0 +MDk2IDx0ZXN0NDA5NkBleGFtcGxlLm9yZz6IXgQTEQIAHgUCQsgjvAIbAwYLCQgH +AwIDFQIDAxYCAQIeAQIXgAAKCRBj+HX2P2d0oAEDAJ9lI+CNmb42z3+a6TnVusM6 +FI7oLwCfUwA1zEcRdsT3nIkoYh0iKxFSDFW5BA0EQsgkdhAQAJQbLXlgcJ/jq+Xh +Eujb77/eeftFJObNIRYD9fmJ7HFIXbUcknEpbs+cRH/nrj5dGSY3OT3jCXOUtvec +sCoX/CpZWL0oqDjAiZtNSFiulw5Gav4gHYkWKgKdSo+2rkavEPqKIVHvMeXaJtGT +d7v/AmL/P8T7gls93o5WFBOLtPbDvWqaKRy2U5TAhl1laiM0vGALRVjvSCgnGw9g +FpSnXbO3AfenUSjDzZujfGLHtU44ixHSS/D4DepiF3YaYLsN4CBqZRv6FbMZD5W3 +DnJY4kS1kH0MzdcF19TlcZ3itTCcGIt1tMKf84mccPoqdMzH7vumBGTeFEly5Afp +9berJcirqh2fzlunN0GS02z6SGWnjTbDlkNDxuxPSBbpcpNyD3jpYAUqSwRsZ/+5 +zkzcbGtDmvy9sJ5lAXkxGoIoQ1tEVX/LOHnh2NQHK8ourVOnr7MS0nozssITZJ5E +XqtHiREjiYEuPyZiVZKJHLWuYYaF+n40znnz3sJuXFRreHhHbbvRdlYUU5mJV+XZ +BLgKuS33NdpGeMIngnCc/9IQ6OZb6ixc94kbkd3w2PVr8CbKlu/IHTjWOO2mAo+D ++OydlYl23FiM3KOyMP1HcEOJMB/nwkMtrvd+522Lu9n77ktKfot9IPrQDIQTyXjR +3pCOFtCOBnk2tJHMPoG9jn9ah/LHAAMHEACDZ5I/MHGfmiKg2hrmqBu2J2j/deC8 +CpwcyDH1ovQ0gHvb9ESa+CVRU2Wdy2CD7Q9SmtMverB5eneL418iPVRcQdwRmQ2y +IH4udlBa6ce9HTUCaecAZ4/tYBnaC0Av/9l9tz14eYcwRMDpB+bnkhgF+PZ1KAfD +9wcY2aHbtsf3lZBc5h4owPJkxpe/BNzuJxW3q4VpSbLsZhwnCZ2wg7DRwP44wFIk +00ptmoBY59gsU6I40XtzrF8JDr0cA57xND5RY21Z8lnnYRE1Tc8h5REps9ZIxW3/ +yl91404bPLqxczpUHQAMSTAmBaStPYX1nS51uofOhLs5SKPCUmxfGKIOhsD0oLUn +78DnkONVGeXzBibSwwtbgfMzee4G8wSUfJ7w8WXz1TyanaGLnJ+DuKASSOrFoBCD +HEDuWZWgSL74NOQupFRk0gxOPmqU94Y8HziQWma/cETbmD83q8rxN+GM2oBxQkQG +xcbqMTHE7aVhV3tymbSWVaYhww3oIwsZS9oUIi1DnPEowS6CpVRrwdvLjLJnJzzV +O3AFPn9eZ1Q7R1tNx+zZ4OOfhvI/OlRJ3HBx2L53embkbdY9gFYCCdTjPyjKoDIx +kALgCajjCYMNUsAKNSd6mMCQ8TtvukSzkZS1RGKP27ohsdnzIVsiEAbxDMMcI4k1 +ul0LExUTCXSjeIhJBBgRAgAJBQJCyCR2AhsMAAoJEGP4dfY/Z3Sg19sAn0NDS8pb +qrMpQAxSb7zRTmcXEFd9AJ435H0ttP/NhLHXC9ezgbCMmpXMOQ== +=kRxT +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQG7BELII7wRBACFuaAvb11cIvjJK9LkZr4cYuYhLWh3DJdojNNnLNiym5OEksvY +05cw8OgqKtPzICU7o/mHXTWhzJYUt3i50/AeYygI8Q0uATS6RnDAKNlES1EMoHKz +2a5iFbYs4bm4IwlkvYd8uWjcu+U0YLbxir39u+anIc6eT+q3WiH/q3zDRwCgkT98 +cnIG8iO8PdwDSP8G4Lt6TYED/R45GvCzJ4onQALLE92KkLUz8aFWSl05r84kczEN +SxiP9Ss6m465RmwWHfwYAu4b+c4GeNyU8fIU2EM8cezchC+edEi3xu1s+pCV0Dk4 +18DGC8WKCICO30vBynuNmYg7W/7Zd4wtjss454fMW7+idVDNM701mmXBtI1nsBtG +7Z4tA/9FxjFbJK9jh24RewfjHpLYqcfCo2SsUjOwsnMZ5yg2yv9KyVVQhRqwmrqt +q8MRyjGmfoD9PPdCgvqgzy0hHvAHUtTm2zUczGTG+0g4hNIklxC/Mv6J4KE+NWTh +uB4acqofHyaw2WnKOuRUsoDi6rG5AyjNMyAK/vVcEGj7J1tk2wAAoJCUNy6awTkw +XfbLbpqh0fvDst7jDLa0I0VsZ2FtYWwgNDA5NiA8dGVzdDQwOTZAZXhhbXBsZS5v +cmc+iF4EExECAB4FAkLII7wCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQY/h1 +9j9ndKABAwCeNEOVK87EzXYbtxYBsnjrUI948NIAn2+f3BXiBFDV5NvqPwIZ0m77 +Fwy4nQRMBELIJHYQEACUGy15YHCf46vl4RLo2++/3nn7RSTmzSEWA/X5iexxSF21 +HJJxKW7PnER/564+XRkmNzk94wlzlLb3nLAqF/wqWVi9KKg4wImbTUhYrpcORmr+ +IB2JFioCnUqPtq5GrxD6iiFR7zHl2ibRk3e7/wJi/z/E+4JbPd6OVhQTi7T2w71q +mikctlOUwIZdZWojNLxgC0VY70goJxsPYBaUp12ztwH3p1Eow82bo3xix7VOOIsR +0kvw+A3qYhd2GmC7DeAgamUb+hWzGQ+Vtw5yWOJEtZB9DM3XBdfU5XGd4rUwnBiL +dbTCn/OJnHD6KnTMx+77pgRk3hRJcuQH6fW3qyXIq6odn85bpzdBktNs+khlp402 +w5ZDQ8bsT0gW6XKTcg946WAFKksEbGf/uc5M3GxrQ5r8vbCeZQF5MRqCKENbRFV/ +yzh54djUByvKLq1Tp6+zEtJ6M7LCE2SeRF6rR4kRI4mBLj8mYlWSiRy1rmGGhfp+ +NM55897CblxUa3h4R2270XZWFFOZiVfl2QS4Crkt9zXaRnjCJ4JwnP/SEOjmW+os +XPeJG5Hd8Nj1a/AmypbvyB041jjtpgKPg/jsnZWJdtxYjNyjsjD9R3BDiTAf58JD +La73fudti7vZ++5LSn6LfSD60AyEE8l40d6QjhbQjgZ5NrSRzD6BvY5/WofyxwAD +BxAAg2eSPzBxn5oioNoa5qgbtido/3XgvAqcHMgx9aL0NIB72/REmvglUVNlnctg +g+0PUprTL3qweXp3i+NfIj1UXEHcEZkNsiB+LnZQWunHvR01AmnnAGeP7WAZ2gtA +L//Zfbc9eHmHMETA6Qfm55IYBfj2dSgHw/cHGNmh27bH95WQXOYeKMDyZMaXvwTc +7icVt6uFaUmy7GYcJwmdsIOw0cD+OMBSJNNKbZqAWOfYLFOiONF7c6xfCQ69HAOe +8TQ+UWNtWfJZ52ERNU3PIeURKbPWSMVt/8pfdeNOGzy6sXM6VB0ADEkwJgWkrT2F +9Z0udbqHzoS7OUijwlJsXxiiDobA9KC1J+/A55DjVRnl8wYm0sMLW4HzM3nuBvME +lHye8PFl89U8mp2hi5yfg7igEkjqxaAQgxxA7lmVoEi++DTkLqRUZNIMTj5qlPeG +PB84kFpmv3BE25g/N6vK8TfhjNqAcUJEBsXG6jExxO2lYVd7cpm0llWmIcMN6CML +GUvaFCItQ5zxKMEugqVUa8Hby4yyZyc81TtwBT5/XmdUO0dbTcfs2eDjn4byPzpU +Sdxwcdi+d3pm5G3WPYBWAgnU4z8oyqAyMZAC4Amo4wmDDVLACjUnepjAkPE7b7pE +s5GUtURij9u6IbHZ8yFbIhAG8QzDHCOJNbpdCxMVEwl0o3gAAckBdfKuasiNUn5G +L5XRnSvaOFzftr8zteOlZChCSNvzH5k+i1j7RJbWq06OeKRywPzjfjgM2MvRzI43 +ICeISQQYEQIACQUCQsgkdgIbDAAKCRBj+HX2P2d0oNfbAJ9+G3SeXrk+dWwo9EGi +hqMi2GVTsgCfeoQJPsc8FLYUgfymc/3xqAVLUtg= +=Gjq6 +-----END PGP PRIVATE KEY BLOCK----- +'); + +insert into keytbl (id, name, pubkey, seckey) +values (4, 'rsa2048', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQELBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP +ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2 +55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx +5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K +MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz +R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYptB5SU0EgMjA0OCA8cnNhMjA0 +OEBleGFtcGxlLm9yZz6JATQEEwECAB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgEC +HgECF4AACgkQnc+OnJvTHyQqHwf8DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liR +nrLuVlLBpdO6CWmMUzfKRvyZlx54GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzw +bLZyM5Gb3lsE/FEmE7Dxw/0Utf59uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDP +D3dnU4uzKPhMcjnSN00pzjusP7C9NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv +9bgGWopumlOkt8Zu5YG6+CtTbJXprPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhv +S3NZKoJ/1DrGgoDAu1mGkM4KvLAxfDs/qQ9dZhtEmDbKPLTVEA== +=lR4n +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQOWBELIJbEBCADAIdtcoLAmQfl8pb73pPRuEYx8qW9klLfCGG5A4OUOi00JHNwP +ZaABe1PGzjoeXrgM1MTQZhoZu1Vdg+KDI6XAtiy9P6bLg7ntsXksD4wBoIKtQKc2 +55pdukxTiu+xeJJG2q8ZZPOp97CV9fbQ9vPCwgnuSsDCoQlibZikDVPAyVTvp7Jx +5rz8yXsl4sxvaeMZPqqFPtA/ENeQ3cpsyR1BQXSvoZpH1Fq0b8GcZTEdWWD/w6/K +MCRC8TmgEd+z3e8kIsCwFQ+TSHbCcxRWdgZE7gE31sJHHVkrZlXtLU8MPXWqslVz +R0cX+yC8j6bXI6/BqZ2SvRndJwuunRAr4um7AAYpAAf/QZsrrz0c7dgWwGqMIpw6 +fP+/lLa74+fa2CFRWtYowEiKsfDg/wN7Ua07036dNhPa8aZPsU6SRzm5PybKOURe +D9pNt0FxJkX0j5pCWfjSJgTbc1rCdqZ/oyBk/U6pQtf//zfw3PbDl7I8TC6GOt2w +5NgcXdsWHP7LAmPctOVUyzFsenevR0MFTHkMbmKI1HpFm8XN/e1Fl+qIAD+OagTF +5B32VvpoJtkh5nxnIuToNJsa9Iy7F9MM2CeFOyTMihMcjXKBBUaAYoF115irBvqu +7N/qWmzqLg8yxBZ56mh6meCF3+67VA2y7fL8rhw2QuqgLg1JFlKAVL+9crCSrn// +GQQA1kT7FytW6BNOffblFYZkrJer3icoRDqa/ljgH/yVaWoVT1igy0E9XzYO7MwP +2usj/resLy0NC1qCthk51cZ/wthooMl88e5Wb4l5FYwBEac7muSBTo4W8cAH1hFj +TWL6XAGvEzGX3Mt9pn8uYGlQLZAhJoNCAU2EOCbN1PchDvsEAOWNKYesuUVk8+sQ +St0NDNhd9BWtTWTHkCZb1dKC3JTfr9PqkTBLrWFbYjkOtvdPAW7FDaXXXZfdH1jH +WfwP3Q+I6sqgSaWpCS4dBAns3/RVtO7czVgyIwma04iIvJqderYrfvkUq95KfwP2 +V8wXkhrPPPxyrg5y3wQlpY2jb5RBBAC17SK1ms+DBtck4vpdjp3SJ32SbyC/DU30 +89Q12j74S7Zdu1qZlKnvy3kWPYX/hMuSzGZ+mLVJNFEqH2X01aFzppYz0hdI9PGB +9tTFEqZWQL9ZkXfjc79Cgnt12pNukRbtw0N/kyutOdIFHVT79wVAd+powqziXJsC +Kc+4xjwSCkZitB5SU0EgMjA0OCA8cnNhMjA0OEBleGFtcGxlLm9yZz6JATQEEwEC +AB4FAkLIJbECGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQnc+OnJvTHyQqHwf8 +DtzuAGmObfe3ggtn14x2wnU1Nigebe1K5liRnrLuVlLBpdO6CWmMUzfKRvyZlx54 +GlA9uUQSjW+RlgejdOTQqesDrcTEukYd4yzwbLZyM5Gb3lsE/FEmE7Dxw/0Utf59 +uACqzG8LACQn9J6sEgZWKxAupuYTHXd12lDPD3dnU4uzKPhMcjnSN00pzjusP7C9 +NZd3OLkAx2vw/dmb4Q+/QxeZhVYYsAUuR2hv9bgGWopumlOkt8Zu5YG6+CtTbJXp +rPI7pJ1jHbeE+q/29hWJQtS8Abx82AcOkzhvS3NZKoJ/1DrGgoDAu1mGkM4KvLAx +fDs/qQ9dZhtEmDbKPLTVEA== +=WKAv +-----END PGP PRIVATE KEY BLOCK----- +'); + +insert into keytbl (id, name, pubkey, seckey) +values (5, 'psw-elg1024', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQGiBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQLQfRWxnYW1hbCAx +MDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJCyCFIAhsDBgsJCAcDAgMV +AgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAniI9W2bC3CxARTpYrev7ihreDzFc +AJ9WYLQxDQAi5Ec9AQoodPkIagzZ4LkBDQRCyCFKEAQAh5SNbbJMAsJ+sQbcWEzd +ku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioymlDwraTKUAfuCZgNcg/0P +sxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlPiO0wt1lLX+SubktqbYxI ++h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSgMERiNzF0acZUYmc0e+/9 +6gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxsnKjUaw/qyoaFcNMzb4sF +k8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey9ifh8rZfu57UbdwdHa0v +iWc4DilhiEkEGBECAAkFAkLIIUoCGwwACgkQHCm8DRgXc2TtrwCfdPom+HlNVE9F +ig3hGY1Rb4NEk1gAn1u9IuQB+BgDP40YHHz6bKWS/x80 +=RWci +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQHhBELIIUgRBACp401L6jXrLB28c3YA4sM3OJKnxM1GT9YTkWyE3Vyte65H8WU9 +tGPBX7OMuaX5eGZ84LFUGvaP0k7anfmXcDkCO3P9GgL+ro/dS2Ps/vChQPZqHaxE +xpKDUt47B7DGdRJrC8DRnIR4wbSyQA6ma3S1yFqC5pJhSs+mqf9eExOjiwCgntth +klRxIYw352ZX9Ov9oht/p/ED/1Xi4PS+tkXVvyIw5aZfa61bT6XvDkoPI0Aj3GE5 +YmCHJlKA/IhEr8QJOLV++5VEv4l6KQ1/DFoJzoNdr1AGJukgTc6X/WcQRzfQtUic +PHQme5oAWoHa6bVQZOwvbJh3mOXDq/Tk/KF22go8maM44vMn4bvv+SBbslviYLiL +jZJ1A/9JXF1esNq+X9HehJyqHHU7LEEf/ck6zC7o2erM3/LZlZuLNPD2cv3oL3Nv +saEgcTSZl+8XmO8pLmzjKIb+hi70qVx3t2IhMqbb4B/dMY1Ck62gPBKa81/Wwi7v +IsEBQLEtyBmGmI64YpzoRNFeaaF9JY+sAKqROqe6dLjJ7vebQP4DAwL3TCgrYdj6 ++GAnoSqGa87twi8a6QRRYIlEx3ddUCDCjzkJmRfF+LFtvX3OtWWK0+Syi3kj2IK9 +YT7pF7QfRWxnYW1hbCAxMDI0IDx0ZXN0QGV4YW1wbGUub3JnPoheBBMRAgAeBQJC +yCFIAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEBwpvA0YF3NkOtsAn1ynoCyM +6GIvHDOewwmF4Z/jGQfzAJ9Q+MwIubi0ASfJifaEM23sIHwHop0BVwRCyCFKEAQA +h5SNbbJMAsJ+sQbcWEzdku8AdYB5zY7Qyf9EOvn0g39bzANhxmmb6gbRlQN0ioym +lDwraTKUAfuCZgNcg/0PsxFGb9nDcvjIV8qdVpnq1PuzMFuBbmGI6weg7Pj01dlP +iO0wt1lLX+SubktqbYxI+h31c3RDZqxj+KAgxR8YNGMAAwYD+wQs2He1Z5+p4OSg +MERiNzF0acZUYmc0e+/96gfL0ft3IP+SSFo6hEBrkKVhZKoPSSRr5KpNaEobhdxs +nKjUaw/qyoaFcNMzb4sFk8wq5UlCkR+h72u6hv8FuleCV8SJUT1U2JjtlXJR2Pey +9ifh8rZfu57UbdwdHa0viWc4Dilh/gMDAvdMKCth2Pr4YCCPsELdgJuzhGfDNRSg +nKMRWBWHSJRk6JmCjM1iJQNHc4mMhR8gvi2TeqYLOhYjcF7nr/LA+JvLV+adj/mI +SQQYEQIACQUCQsghSgIbDAAKCRAcKbwNGBdzZO2vAJ4hRaLcNcdl/qK8rt0N5zbZ +saCh6QCfR1O48O8nYN93SPSfIFZK5rEmdv8= +=Y6Qv +-----END PGP PRIVATE KEY BLOCK----- +'); + +insert into keytbl (id, name, pubkey, seckey) +values (6, 'rsaenc2048', ' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +mQELBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj +UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW +czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT +4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ +dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4 +NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYptCVSU0EgMjA0OCBFbmMgPHJz +YTIwNDhlbmNAZXhhbXBsZS5vcmc+iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMV +AgMDFgIBAh4BAheAAAoJEMiZ6pNEGVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8 +JCaxHa31wH3PKqGtq2M+cpb2rXf7gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw ++IkhihEb5bWxQBNj+3zAFs1YX6v2HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzku +UOhcgfpBgIt3Q+MpT6M2+OIF7lVfSb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQ +RJ6DWH61F8fFIDJg1z+A/Obx4fqX6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO8 +0g/oVYBbuvOYedffDBeQarhERZ5W2TnIE+nqY61YOLBqosliygdZTXULzNi5AQsE +QuvaugEIAOuCJZdkzORA6e1lr81Lnr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPX +z1YIrMTu+1rMIiy10IWbA6zgMTpzPhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfK +lAXIxJurCHXTbEa+YvPdn76vJ3HsXOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zE +FZcAoKbFqAAjDLEai64SoOFh0W3CsD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9U +rHlolqYNhYze/uRLyfnUx9PN4r/GhEzauyDMV0smo91uB3aewPft+eCpmeWnu0PF +JVK4xyRmhIq2rVCw16a1pBJirvGM+y0ABimJAR8EGAECAAkFAkLr2roCGwwACgkQ +yJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+1iqYE0B0 +WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwarANhHxkWg +w/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx2eVM1k+X +dmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOyla+wJdro +PYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CChbt6LhKh +CLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug== +=pwU2 +-----END PGP PUBLIC KEY BLOCK----- +', ' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: GnuPG v1.4.1 (GNU/Linux) + +lQOWBELr2m0BCADOrnknlnXI0EzRExf/TgoHvK7Xx/E0keWqV3KrOyC3/tY2KOrj +UVxaAX5pkFX9wdQObGPIJm06u6D16CH6CildX/vxG7YgvvKzK8JGAbwrXAfk7OIW +czO2zRaZGDynoK3mAxHRBReyTKtNv8rDQhuZs6AOozJNARdbyUO/yqUnqNNygWuT +4htFDEuLPIJwAbMSD0BvFW6YQaPdxzaAZm3EWVNbwDzjgbBUdBiUUwRdZIFUhsjJ +dirFdy5+uuZru6y6CNC1OERkJ7P8EyoFiZckAIE5gshVZzNuyLOZjc5DhWBvLbX4 +NZElAnfiv+4nA6y8wQLSIbmHA3nqJaBklj85AAYpAAf9GuKpxrXp267eSPw9ZeSw +Ik6ob1I0MHbhhHeaXQnF0SuOViJ1+Bs74hUB3/F5fqrnjVLIS/ysYzegYpbpXOIa +MZwYcp2e+dpmVb7tkGQgzXH0igGtBQBqoSUVq9mG2XKPVh2JmiYgOH6GrHSGmnCq +GCgEK4ezSomB/3OtPFSjAxOlSw6dXSkapSxW3pEGvCdaWd9p8yl4rSpGsZEErPPL +uSbZZrHtWfgq5UXdPeE1UnMlBcvSruvpN4qgWMgSMs4d2lXvzXJLcht/nryP+atT +H1gwnRmlDCVv5BeJepKo3ORJDvcPlXkJPhqS9If3BhTqt6QgQEFI4aIYYZOZpZoi +2QQA2Zckzktmsc1MS04zS9gm1CbxM9d2KK8EOlh7fycRQhYYqqavhTBH2MgEp+Dd +ZtuEN5saNDe9x/fwi2ok1Bq6luGMWPZU/nZe7fxadzwfliy/qPzStWFW3vY9mMLu +6uEqgjin/lf4YrAswXDZaEc5e4GuNgGfwr27hpjxE1jg3PsEAPMqXEOMT2yh+yRu +DlLRbFhYOI4aUHY2CGoQQONnwv2O5gFvmOcPlg3J5lvnwlOYCx0c3bDxAtHyjPJq +FAZqcJBaB9RDhKHwlWDrbx/6FPH2SuKE+u4msIhPFin4V3FAP+yTem/TKrdnaWy6 +EUrhCWTXVRTijBaCudfjFd/ipHZbA/0dv7UAcoWK6kiVLzyE+jOvtN+ZxTzxq7CW +mlFPgAC966hgJmz9IXqadtMgPAoL3PK9q1DbPM3JhsQcJrNzTJqZrdN1/kPU0HHa ++aof1BVy3wSvp2mXgaRUULStyhUIyBRM6hAYp3/MoWEYn/bwr+zQkIU8Zsk6OsZ6 +q1xE3cowrUWFtCVSU0EgMjA0OCBFbmMgPHJzYTIwNDhlbmNAZXhhbXBsZS5vcmc+ +iQE0BBMBAgAeBQJC69ptAhsDBgsJCAcDAgMVAgMDFgIBAh4BAheAAAoJEMiZ6pNE +GVVZHMkIAJtGHHZ9iM8Yq1rr0zl1L6SvlQP8JCaxHa31wH3PKqGtq2M+cpb2rXf7 +gAY/doHJPXggfVzkyFrysmQ1gPbDGYLyOutw+IkhihEb5bWxQBNj+3zAFs1YX6v2 +HXWbSUSmyY1V9/+NTtKk03olDc/swd3lXzkuUOhcgfpBgIt3Q+MpT6M2+OIF7lVf +Sb1rWdpwTfGhZzW9szQOeoS4gPvxCCRyuabQRJ6DWH61F8fFIDJg1z+A/Obx4fqX +6GOA69RzgZ3oukFBIXxNwV9PZNnAmHtZVYO80g/oVYBbuvOYedffDBeQarhERZ5W +2TnIE+nqY61YOLBqosliygdZTXULzNidA5YEQuvaugEIAOuCJZdkzORA6e1lr81L +nr4JzMsVBFA+X/yIkBbV6qX/A4nVSLAZKNPXz1YIrMTu+1rMIiy10IWbA6zgMTpz +PhJRfgePONgdnCYyK5Ksh5/C5ntzKwwGwxfKlAXIxJurCHXTbEa+YvPdn76vJ3Hs +XOXVEL+fLb4U3l3Ng87YM202Lh1Ha2MeS2zEFZcAoKbFqAAjDLEai64SoOFh0W3C +sD1DL4zmfp+YZrUPHTtZadsi53i4KKW/ws9UrHlolqYNhYze/uRLyfnUx9PN4r/G +hEzauyDMV0smo91uB3aewPft+eCpmeWnu0PFJVK4xyRmhIq2rVCw16a1pBJirvGM ++y0ABikAB/oC3z7lv6sVg+ngjbpWy9lZu2/ECZ9FqViVz7bUkjfvSuowgpncryLW +4EpVV4U6mMSgU6kAi5VGT/BvYGSAtnqDWGiPs7Kk+h4Adz74bEAXzU280pNBtSfX +tGvzlS4a376KzYFSCJDRBdMebEhJMbY0wQmR8lTZu5JSUI4YYEuN0c7ckdsw8w42 +QWTLonG8HC6h8UPKS0EAcaCo7tFubMIesU6cWuTYucsHE+wjbADjuSNX968qczNe +NoL2BUznXOQoPu6HQO4/8cr7ib+VQkB2bHQcMoZazPUStIID1e4CL4XcxfuAmT8o +3XDvMLgVqNp5W2f8Mzmk3/DbtsLXLOv5BADsCzQpseC8ikSYJC72hcon1wlUmGeH +3qgGiiHhYXFa18xgI5juoO8DaWno0rPPlgr36Y8mSB5qjYHMXwjKnKyUmt11H+hU ++6uk4hq3Rjd8l+vfuOSr1xoTrtBUg9Rwfw6JVo0DC+8CWg4oBWsLXVM6KQXPFdJs +8kyFQplR/iP1XQQA/2tbDANjAYGNNDjJO9/0kEnSAUyYMasFJDrA2q17J5CroVQw +QpMmWwdDkRANUVPKnWHS5sS65BRc7UytKe2f3A3ZInGXJIK2Hl+TzapWYcYxql+4 +ol5mEDDMDbhEE8Wmj9KyB6iifdLI0K+yxNb9T4Jpj3J18+St+G8+9AcFcBEEAM1b +M9C+/05cnV8gjcByqH9M9ypo8fzPvMKVXWwCLQXpaL50QIkzLURkiMoEWrCdELaA +sVPotRzePTIQ1ooLeDxd1gRnDqjZiIR0kwmv6vq8tfzY96O2ZbGWFI5eth89aWEJ +WB8AR3zYcXpwJLwPuhXW2/NlZF0bclJ3jNzAfTIeQmeJAR8EGAECAAkFAkLr2roC +GwwACgkQyJnqk0QZVVku1wgAg1bLSjPkhw+ldG5HzumpqR84+JKyozdJaJzefu2+ +1iqYE0B0WLz2PJVIiK41xiEkKhBvTOQYuXmtWqAWXptD91P5SoXoNJWLQO3TNwar +ANhHxkWgw/TOUxQqoctlRUej5NDD+4eW5G9lcS1FEGuKDWtX096u80vO+TbyJjvx +2eVM1k+XdmeYsGOiNgDimCreJGYc14G7eY9jt24gw10n1sMAKI1qm6lcoHqZ9OOy +la+wJdroPYZGO7R8+1O9R22WrK6BYDT5j/1JwMZqbOESjNvDEVT0yOHClCHRN4CC +hbt6LhKhCLUNdz/udIt0JAC6c/HdPLSW3HnmM3+iNj+Kug== +=UKh3 +-----END PGP PRIVATE KEY BLOCK----- +'); + + +-- elg1024 / aes128 +insert into encdata (id, data) values (1, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQEOA9k2z2S7c/RmEAQAgVWW0DeLrZ+1thWJGBPp2WRFL9HeNqqWHbKJCXJbz1Uy +faUY7yxVvG5Eutmo+JMiY3mg23/DgVVXHQZsTWpGvGM6djgUNGKUjZDbW6Nog7Mr +e78IywattCOmgUP9vIwwg3OVjuDCN/nVirGQFnXpJBc8DzWqDMWRWDy1M0ZsK7AD +/2JTosSFxUdpON0DKtIY3GLzmh6Nk3iV0g8VgJKUBT1rhCXuMDj3snm//EMm7hTY +PlnObq4mIhgz8NqprmhooxnU0Kapofb3P3wCHPpU14zxhXY8iKO/3JhBq2uFcx4X +uBMwkW4AdNxY/mzJZELteTL8Tr0s7PISk+owb4URpG3n0jsBc0CVULxrjh5Ejkdw +wCM195J6+KbQxOOFQ0b3uOVvv4dEgd/hRERCOq5EPaFhlHegyYJ7YO842vnSDA== +=PABx +-----END PGP MESSAGE----- +'); + +-- elg2048 / blowfish +insert into encdata (id, data) values (2, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQIOAywibh/+XMfUEAf+OINhBngEsw4a/IJIeJvUgv1gTQzBwOdQEuc/runr4Oa8 +Skw/Bj0X/zgABVZLem1a35NHaNwaQaCFwMQ41YyWCu+jTdsiyX/Nw0w8LKKz0rNC +vVpG6YuV7Turtsf8a5lXy1K0SHkLlgxQ6c76GS4gtSl5+bsL2+5R1gSRJ9NXqCQP +OHRipEiYwBPqr5R21ZG0FXXNKGOGkj6jt/M/wh3WVtAhYuBI+HPKRfAEjd/Pu/eD +e1zYtkH1dKKFmp44+nF0tTI274xpuso7ShfKYrOK3saFWrl0DWiWteUinjSA1YBY +m7dG7NZ8PW+g1SZWhEoPjEEEHz3kWMvlKheMRDudnQf/dDyX6kZVIAQF/5B012hq +QyVewgTGysowFIDn01uIewoEA9cASw699jw9IoJp+k5WZXnU+INllBLzQxniQCSu +iEcr0x3fYqNtj9QBfbIqyRcY6HTWcmzyOUeGaSyX76j+tRAvtVtXpraFFFnaHB70 +YpXTjLkp8EBafzMghFaKDeXlr2TG/T7rbwcwWrFIwPqEAUKWN5m97Q3eyo8/ioMd +YoFD64J9ovSsgbuU5IpIGAsjxK+NKzg/2STH7zZFEVCtgcIXsTHTZfiwS98/+1H9 +p1DIDaXIcUFV2ztmcKxh9gt2sXRz1W+x6D8O0k3nanU5yGG4miLKaq18fbcA0BD1 ++NIzAfelq6nvvxYKcGcamBMgLo5JkZOBHvyr6RsAKIT5QYc0QTjysTk9l0Am3gYc +G2pAE+3k +=TBHV +-----END PGP MESSAGE----- +'); + +-- elg4096 / aes256 +insert into encdata (id, data) values (3, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQQOA7aFBP0Sjh/5EA/+JCgncc8IZmmRjPStWnGf9tVJhgHTn+smIclibGzs0deS +SPSCitzpblwbUDvu964+/5e5Q1l7rRuNN+AgETlEd4eppv7Swn2ChdgOXxRwukcT +Nh3G+PTFvD4ayi7w1db3qvXIt0MwN4Alt436wJmK1oz2Ka9IcyO+wHWrDy1nSGSx +z5x7YEj+EZPgWc/YAvudqE8Jpzd/OT5zSHN09UFkIAk6NxisKaIstbEGFgpqtoDZ +1SJM84XAdL2IcaJ3YY7k/yzwlawhsakKd4GSd5vWmAwvyzzbSiBMfKsDE16ePLNU +ZBF7CzmlCBPZ7YrFAHLpXBXXkCQvzD2BEYOjse50ZEfJ036T7950Ozcdy1EQbGon +nyQ4Gh0PBpnMcBuiXOceWuYzhlzFOzDtlVKdNTxFRDcbEyW2jo9xQYvCCLnYy8EH +2M7S8jCtVYJBbn63a82ELv+3+kWYcsvBJv2ZVBh4ncrBu9o0P+OYS7ApoOU+j6p2 ++t0RXHksqXS1YiUwYF5KSw09EbYMgNZ9G04Px/PxLU6fSC9iDrGX7Xt3kOUP0mku +C518fPckT0zzRXqfFruJNRzDytW50KxkOQZzU1/Az1YlYN9QzWeU4EtLPb2fftZo +D0qH/ln+f9Op5t6sD2fcxZVECU1b/bFtZsxvwH406YL+UQ7hU/XnZrzVVzODal8P +/j1hg7v7BdJqu1DTp9nFWUuwMFcYAczuXn29IG183NZ7Ts4whDeYEhS8eNoLPX4j +txY12ILD/w/3Q4LoW/hPa6OdfEzsn0U5GLf1WiGmJE1H6ft2U/xUnerc/u0kt+FU +WAisArd4MuKtf7B5Vu/VF3kUdrR0hTniUKUivmC4o1jSId31Dufxj4aadVyldXAr +6TNBcdyragZjxEZ6hsBCYzA0Rd1a8atd6OaQoIEEfAzCu5Ks29pydHErStYGjWJ1 +KA5KPLVvjbHpDmRhlCcm8vgpYQsBYEB5gE9fx5yCTlsVhCB6y23h7hfdMqerDqkO +ZOPsO5h+tiHCdIrQ36sMjuINy1/K2rYcXd+Crh2iHcfidpU9fvDz2ihTRNQlhjuT +0cQZM5JhctEx4VXF4LDctRhit7Hn0iqsk604woQfJVvP8O673xSXT/kBY0A/v9C0 +3C4YoFNeSaKwbfZQ/4u1ZFPJxK2IIJa8UGpyAUewLMlzGVVagljybv/f4Z9ERAhy +huq5sMmw8UPsrJF2TUGHz5WSIwoh0J/qovoQI09I9sdEnFczDvRavMO2Mldy3E5i +exz9oewtel6GOmsZQSYWT/vJzbYMmvHNmNpVwwoKrLV6oI3kyQ80GHBwI1WlwHoK +2iRB0w8q4VVvJeYAz8ZIp380cqC3pfO0uZsrOx4g3k4X0jsB5y7rF5xXcZfnVbvG +DYKcOy60/OHMWVvpw6trAoA+iP+cVWPtrbRvLglTVTfYmi1ToZDDipkALBhndQ== +=L/M/ +-----END PGP MESSAGE----- +'); + +-- rsaenc2048 / aes128 +insert into encdata (id, data) values (4, ' +-----BEGIN PGP MESSAGE----- +Version: GnuPG v1.4.1 (GNU/Linux) + +hQEMA/0CBsQJt0h1AQf+JyYnCiortj26P11zk28MKOGfWpWyAhuIgwbJXsdQ+e6r +pEyyqs9GC6gI7SNF6+J8B/gsMwvkAL4FHAQCvA4ZZ6eeXR1Of4YG22JQGmpWVWZg +DTyfhA2vkczuqfAD2tgUpMT6sdyGkQ/fnQ0lknlfHgC5GRx7aavOoAKtMqiZW5PR +yae/qR48mjX7Mb+mLvbagv9mHEgQSmHwFpaq2k456BbcZ23bvCmBnCvqV/90Ggfb +VP6gkSoFVsJ19RHsOhW1dk9ehbl51WB3zUOO5FZWwUTY9DJvKblRK/frF0+CXjE4 +HfcZXHSpSjx4haGGTsMvEJ85qFjZpr0eTGOdY5cFhNJAAVP8MZfji7OhPRAoOOIK +eRGOCkao12pvPyFTFnPd5vqmyBbdNpK4Q0hS82ljugMJvM0p3vJZVzW402Kz6iBL +GQ== +=XHkF +-----END PGP MESSAGE----- +'); + +-- successful decrypt +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=1 and encdata.id=1; + +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=2 and encdata.id=2; + +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=3 and encdata.id=3; + +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=6 and encdata.id=4; + +-- wrong key +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=2 and encdata.id=1; + +-- sign-only key +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=4 and encdata.id=1; + +-- password-protected secret key, no password +select pgp_pub_decrypt(dearmor(data), dearmor(seckey)) +from keytbl, encdata where keytbl.id=5 and encdata.id=1; + +-- password-protected secret key, wrong password +select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'foo') +from keytbl, encdata where keytbl.id=5 and encdata.id=1; + +-- password-protected secret key, right password +select pgp_pub_decrypt(dearmor(data), dearmor(seckey), 'parool') +from keytbl, encdata where keytbl.id=5 and encdata.id=1; + diff --git a/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql b/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql new file mode 100644 index 0000000..62dd487 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-pubkey-encrypt.sql @@ -0,0 +1,50 @@ +-- +-- PGP Public Key Encryption +-- + +-- successful encrypt/decrypt +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; + +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=2; + +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=3; + +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=6; + +-- try with rsa-sign only +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=4; + +-- try with secret key +select pgp_pub_decrypt( + pgp_pub_encrypt('Secret msg', dearmor(seckey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; + +-- does text-to-bytea works +select pgp_pub_decrypt_bytea( + pgp_pub_encrypt('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; + +-- and bytea-to-text? +select pgp_pub_decrypt( + pgp_pub_encrypt_bytea('Secret msg', dearmor(pubkey)), + dearmor(seckey)) +from keytbl where keytbl.id=1; + + diff --git a/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql b/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql new file mode 100644 index 0000000..85c2468 --- /dev/null +++ b/contrib/pgcrypto/sql/pgp-zlib-DISABLED.sql @@ -0,0 +1,3 @@ + +-- zlib is disabled + diff --git a/contrib/pgcrypto/sql/sha2.sql b/contrib/pgcrypto/sql/sha2.sql new file mode 100644 index 0000000..f1d0ca6 --- /dev/null +++ b/contrib/pgcrypto/sql/sha2.sql @@ -0,0 +1,28 @@ +-- +-- SHA2 family +-- + +-- SHA256 +SELECT encode(digest('', 'sha256'), 'hex'); +SELECT encode(digest('a', 'sha256'), 'hex'); +SELECT encode(digest('abc', 'sha256'), 'hex'); +SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha256'), 'hex'); +SELECT encode(digest('12345678901234567890123456789012345678901234567890123456789012345678901234567890', 'sha256'), 'hex'); + +-- SHA384 +SELECT encode(digest('', 'sha384'), 'hex'); +SELECT encode(digest('a', 'sha384'), 'hex'); +SELECT encode(digest('abc', 'sha384'), 'hex'); +SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha384'), 'hex'); +SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha384'), 'hex'); +SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha384'), 'hex'); + +-- SHA512 +SELECT encode(digest('', 'sha512'), 'hex'); +SELECT encode(digest('a', 'sha512'), 'hex'); +SELECT encode(digest('abc', 'sha512'), 'hex'); +SELECT encode(digest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', 'sha512'), 'hex'); +SELECT encode(digest('abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu', 'sha512'), 'hex'); +SELECT encode(digest('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 'sha512'), 'hex'); + + diff --git a/doc/FAQ_AIX b/doc/FAQ_AIX index 36fab80..b04fe63 100644 --- a/doc/FAQ_AIX +++ b/doc/FAQ_AIX @@ -1,5 +1,5 @@ From: Zeugswetter Andreas -$Date: 2006/10/10 00:29:13 $ +$Date$ On AIX 4.3.2 PostgreSQL compiled with the native IBM compiler xlc (vac.C 5.0.1) passes all regression tests. Other versions of OS and diff --git a/doc/FAQ_CYGWIN b/doc/FAQ_CYGWIN index 281974c..e28e5db 100644 --- a/doc/FAQ_CYGWIN +++ b/doc/FAQ_CYGWIN @@ -1,7 +1,7 @@ Installing PostgreSQL on Windows Using Cygwin FAQ ================================================= -$Date: 2004/10/15 16:18:35 $ +$Date$ PostgreSQL requires the appropriate subset of Cygwin DLLs to be installed in order that it functions under Windows. diff --git a/doc/FAQ_HPUX b/doc/FAQ_HPUX index 5f624f0..4304e84 100644 --- a/doc/FAQ_HPUX +++ b/doc/FAQ_HPUX @@ -3,7 +3,7 @@ Frequently Asked Questions (FAQ) for PostgreSQL 7.3 HP-UX Specific TO BE READ IN CONJUNCTION WITH THE NORMAL FAQ ======================================================= -last updated: $Date: 2006/10/10 20:11:44 $ +last updated: $Date$ current maintainer: Tom Lane (tgl@sss.pgh.pa.us) original author: Tom Lane (tgl@sss.pgh.pa.us) diff --git a/doc/FAQ_IRIX b/doc/FAQ_IRIX index ad2fef5..b27b7b0 100644 --- a/doc/FAQ_IRIX +++ b/doc/FAQ_IRIX @@ -3,7 +3,7 @@ Frequently Asked Questions (FAQ) for PostgreSQL IRIX Specific TO BE READ IN CONJUNCTION WITH THE NORMAL FAQ ======================================================= -last updated: $Date: 2002/11/11 20:04:05 $ +last updated: $Date$ current maintainer: Luis Amigo (lamigo@atc.unican.es) original author: Luis Amigo (lamigo@atc.unican.es) diff --git a/doc/FAQ_QNX4 b/doc/FAQ_QNX4 index a7eb083..8d211c6 100644 --- a/doc/FAQ_QNX4 +++ b/doc/FAQ_QNX4 @@ -1,6 +1,6 @@ PostgreSQL on QNX 4 -------------------- -last updated: $Date: 2004/03/23 01:23:47 $ +last updated: $Date$ current maintainer: Bernd Tegge (tegge@repas-aeg.de) original author: Andreas Kardos (kardos@repas-aeg.de) diff --git a/doc/FAQ_SCO b/doc/FAQ_SCO index 02da450..fa92d6b 100644 --- a/doc/FAQ_SCO +++ b/doc/FAQ_SCO @@ -3,7 +3,7 @@ Frequently Asked Questions (FAQ) for PostgreSQL 7.3 SCO UnixWare and OpenServer specific to be read in conjunction with the installation instructions ============================================================ -last updated: $Date: 2004/08/04 21:33:39 $ +last updated: $Date$ current maintainer: Billy G. Allie (Bill.Allie@mug.org) original author: Andrew Merrill (andrew@compclass.com) diff --git a/doc/FAQ_Solaris b/doc/FAQ_Solaris index 78a0031..5372c28 100644 --- a/doc/FAQ_Solaris +++ b/doc/FAQ_Solaris @@ -3,7 +3,7 @@ Frequently Asked Questions (FAQ) for PostgreSQL Sun Solaris specific To be read in conjunction with the installation instructions. ============================================================ -Last updated: $Date: 2006/10/10 00:29:14 $ +Last updated: $Date$ Contents: diff --git a/doc/TODO.detail/pg_upgrade b/doc/TODO.detail/pg_upgrade new file mode 100644 index 0000000..953413a --- /dev/null +++ b/doc/TODO.detail/pg_upgrade @@ -0,0 +1,615 @@ +From pgsql-hackers-owner+M59479@postgresql.org Thu Sep 30 15:55:23 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i8UJtHw26165 + for ; Thu, 30 Sep 2004 15:55:19 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id A4BDD32A219 + for ; Thu, 30 Sep 2004 20:55:10 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 24195-05 for ; + Thu, 30 Sep 2004 19:55:08 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 537C532A216 + for ; Thu, 30 Sep 2004 20:55:10 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 333B932A1EF + for ; Thu, 30 Sep 2004 20:49:20 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 21793-04 + for ; + Thu, 30 Sep 2004 19:49:09 +0000 (GMT) +Received: from authenticity.encs.concordia.ca (authenticity-96.encs.concordia.ca [132.205.96.93]) + by svr1.postgresql.org (Postfix) with ESMTP id BEB6A32A156 + for ; Thu, 30 Sep 2004 20:49:03 +0100 (BST) +Received: from haida.cs.concordia.ca (IDENT:mokhov@haida.cs.concordia.ca [132.205.64.45]) + by authenticity.encs.concordia.ca (8.12.11/8.12.11) with ESMTP id i8UJn0Xe022202; + Thu, 30 Sep 2004 15:49:00 -0400 +Date: Thu, 30 Sep 2004 15:49:00 -0400 (EDT) +From: "Serguei A. Mokhov" +To: pgsql-hackers@postgresql.org +cc: "Serguei A. Mokhov" +Subject: [HACKERS] pg_upgrade project: high-level design proposal of in-place upgrade + facility +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=US-ASCII +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +Hello dear all, + +[Please CC your replies to me as I am on the digest mode] + +Here's finally a very high-level design proposal of the pg_upgrade feature +I was handwaiving a couple of weeks ago. Since, I am almost done with the +moving, I can allocate some time for this for 8.1/8.2. + +If this topic is of interest to you, please read on until the very end +before flaming or bashing the ideas out. I had designed that thing and +kept updating (the design) more or less regularly, and also reflected some +issues from the nearby threads [1] and [2]. + +This design is very high-level at the moment and is not very detailed. I +will need to figure out more stuff as I go and design some aspects in +finer detail. I started to poke around asking for initdb-forcing code +paths in [3], but got no response so far. But I guess if the general idea +or, rather, ideas accepted I will insist on more information more +aggressively :) if I can't figure something out for myself. + +[1] http://archives.postgresql.org/pgsql-hackers/2004-09/msg00000.php +[2] http://archives.postgresql.org/pgsql-hackers/2004-09/msg00382.php +[3] http://archives.postgresql.org/pgsql-hackers/2004-08/msg01594.php + +Comments are very welcome, especially _*CONSTRUCTIVE*_... + +Thank you, and now sit back and read... + +CONTENTS: +========= + +1. The Need +1. Utilities and User's View of the pg_upgrade Feature +2. Storage Management + - Storage Managers and the smgr API +3. Source Code Maintenance Aspects +2. The Upgrade Sequence +4. Proposed Implementation Plan + - initdb() API + - upgrade API + + +1. The Need +----------- + +It's been a problem for PG for quite awhile now to have a less painful +upgrade procedure with every new revision of PostgreSQL, so the +dump/restore sequence is required. That can take a while for a production +DB, while keeping it offline. The new replication-related solutions, such +as Slony I, pg_pool, and others can remedy the problem somewhat, but +require to roughly double the storage requirements of a given database +while replicating from the older server to a newer one. + +The proposed implementation of an in-server pg_upgrade facility attempts +to address both issues at the same time -- a possibility to keep the +server running and upgrading lazily w/o doubling the storage requirements +(there will be some extra disk space taken, but far from doubling the +size). The in-process upgrade will not take much of down time and won't +require that much memory/disk/network resources as replication solutions +do. + + +Prerequisites +------------- + +Ideally, the (maybe not so anymore) ambitious goal is to simply be able to +"drop in" the new binaries of the new server and kick off on the older +version of data files. I think is this feasible now a lot more than before +since we have those things available, which should ease up the +implementation: + + - bgwriter + - pg_autovacuum (the one to be integrated into the backend in 8.1) + - smgr API for pluggable storage managers + - initdb in C + - ... + +initdb in C, bgwriter and pg_autovacuum, and pluggable storage manager +have made the possibility of creation of the Upgrade Subsystem for +PostgreSQL to be something more reasonable, complete, feasible, and sane +to a point. + + +Utilities and the User's (DBA) View of the Feature +-------------------------------------------------- + +Two instances exist: + + pg_upgrade (in C) + + A standalone utility to upgrade the binary on-disk format from one + version to another when the database is offline. + We should always have this as an option. + pg_upgrade will accept sub/super set of pg_dump(all)/pg_restore + options that do not require a connection. I haven't + thought through this in detail yet. + + pg_autoupgrade + + a postgres subprocess, modeled after bgwriter and pg_autovacuum + daemons. This will work when the database system is running + on old data directory, and lazily converting relations to the new + format. + +pg_autoupgrade daemon can be triggered by the following events in addition +to the lazy upgrade process: + + "SQL" level: UPGRADE [NOW | time] + +While the database won't be offline running over older database files, +SELECT/read-only queries would be allowed using older storage managers*. +Any write operation on old data will act using write-invalidate approach +that will force the upgrade the affected relations to the new format to be +scheduled after the relation-in-progress. + +(* See the "Storage Management" section.) + +Availability of the relations while upgrade is in progress is likely to be +the same as in VACUUM FULL for that relation, i.e. the entire relation is +locked until the upgrade is complete. Maybe we could optimize that by +locking only particular pages of relations, I have to figure that out. + +The upgrade of indices can be done using REINDEX, which seems far less +complicated than trying to convert its on-disk representation. This has +to be done after the relation is converted. Alternatively, the index +upgrade can simply be done by "CREATE INDEX" after the upgrade of +relations. + +The relations to be upgraded are ordered according to some priority, e.g. +system relations being first, then user-owned relations. System relations +upgrade is forced upon the postmaster startup, and then user relations are +processed lazily. + +So, in a sense, pg_autoupgrade will act like a proxy choosing appropriate +storage manager (like a driver) between the new server and the old data +file upgrading them on-demand. For that purpose we might need to add a +pg_upgradeproxy to intercept backend requests and use appropriate storage +manager. There will be one proxy process per backend. + + +Storage Management +================== + +Somebody has made a possibility to plug a different storage manager in +postgres and we even had two of them at some point . for the magnetic disk +and the main memory. The main memory one is gone, but the smgr API is +still there. Some were dubious why we would ever need another third-party +storage manager, but here I propose to "plug in" storage managers from the +older Postgres versions itself! Here is where the pluggable storage +manager API would be handy once fully resurrected. Instead of trying to +plug some third party storage managers it will primarily be used by the +storage managers of different versions of Postgres. + +We can take the storage manager code from the past maintenance releases, +namely 6.5.3, 7.0.3, 7.1.3, 7.2.5, 7.3.7, 7.4.5, and 8.0, and arrange them +in appropriate fashion and have them implement the API properly. Anyone +can contribute a storage manager as they see fit, there's no need to get +them all at once. As a trial implementation I will try to do the last +three or four maybe. + + +Where to put relations being upgraded? +-------------------------------------- + +At the beginning of the upgrade process if pg detects the old version of +data files, it moves them under $PGDATA/, and keeps the old relations +there until upgraded. The relations to be upgraded will be kept in the +pg_upgrade_catalog. Once all relations upgraded, the directory is +removed and the auto and proxy processes are shut down. The contents of +the pg_upgrade_catalog emptied. The only issue remains is how to deal +with tablespaces (or LOCATION in 7.* releases) elsewhere .- this can +probably be addressed in the similar fashion, but having a +/my/tablespace/ directory. + +Source Code Maintenance +======================= + +Now, after the above some of you may get scared on the amount of similar +code to possibly maintain in all those storage managers, but in reality +they would require as much maintenance as the corresponding releases do +get back-patched in that code area, and some are not being maintained for +quite some time already. Plus, I should be around to maintain it, should +this become realized. + +Release-time Maintenance +------------------------ + +For maintenance of pg_upgrade itself, one will have to fork out a new +storage manager from the previous stable release and "register" it within +the system. Alternatively, the new storage manager can be forked when the +new release cycle begins. Additionally, a pg_upgrade version has to be +added implementing the API steps outlined in the pg_upgrade API section. + + +Implementation Steps +==================== + +To materialize the above idea, I'd proceed as follows: + +*) Provide the initdb() API (quick) + +*) Resurrect the pluggable storage manager API to be usable for the + purpose. + +*) Document it + +*) Implement pg_upgrade API for 8.0 and 7.4.5. + +*) Extract 8.0 and 7.4.5 storage managers and have them implement the API + as a proof of concept. Massage the API as needed. + +*) Document the process of adding new storage managers and pg_upgrade + drivers. + +*) Extract other versions storage managers. + + +pg_upgrade sequence +------------------- + +pg_upgrade API for the steps below to update for the next release. + +What to do with WAL?? Maybe upgrade can simply be done using WAL replay +with old WAL manager? Not, fully, because not everything is in WAL, but +some WAL recovery maybe needed in case the server was not shutdown cleanly +before the upgrade. + +pg_upgrade will proceed as follows: + +- move PGDATA to PGDATA/ +- move tablespaces likewise +- optional recovery from WAL in case old server was not shutdown properly +-? Shall I upgrade PITR logs of 8.x??? So one can recover to a + point-in-time in the upgraded database? +- CLUSTER all old data +- ANALYZE all old data +- initdb() new system catalogs +- Merge in modifications from old system catalogs +- upgrade schemas/users + -- variations +- upgrade user relations + +Upgrade API: +------------ + +First draft, to be refined multiple times, but to convey the ideas behind: + +moveData() + movePGData() + moveTablespaces() 8.0+ + moveDbLocation() < 8.0 + +preliminaryRecovery() + - WAL?? + - PITR 8.0+?? + +preliminaryCleanup() + CLUSTER -- recover some dead space + ANALYZE -- gives us stats + +upgradeSystemInfo() + initdb() + mergeOldCatalogs() + mergeOldTemplates() + +upgradeUsers() + +upgradeSchemas() + - > 7.2, else NULL + +upgradeUserRelations() + upgradeIndices() + DROP/CREATE + +upgradeInit() +{ + +} + +The main body in pseudocode: + +upgradeLoop() +{ + moveData(); + preliminaryRecovery(); + preliminaryCleanup(); + upgradeSystemInfo(); + upgradeUsers(); + upgradeSchemas(); + upgradeUserRelations(); +} + +Something along these lines the API would be: + +typedef struct t_upgrade +{ + bool (*moveData) (void); + bool (*preliminaryRecovery) (void); /* may be NULL */ + bool (*preliminaryCleanup) (void); /* may be NULL */ + bool (*upgradeSystemInfo) (void); /* may be NULL */ + bool (*upgradeUsers) (void); /* may be NULL */ + bool (*upgradeSchemas) (void); /* may be NULL */ + bool (*upgradeUserRelations) (void); /* may be NULL */ +} t_upgrade; + + +The above sequence is executed by either pg_upgrade utility uninterrupted +or by the pg_autoupgrade daemon. In the former the upgrade priority is +simply by OID, in the latter also, but can be overridden by the user using +the UPGRADE command to schedule relations upgrade, write operation can +also change such schedule, with user's selected choice to be first. The +more write requests a relation receives while in the upgrade queue, its +priority increases; thus, the relation with most hits is on top. In case +of tie, OID is the decision mark. + +Some issues to look into: + +- catalog merger +- a crash in the middle of upgrade +- PITR logs for 8.x+ +- ... + +Flames and Handwaiving +---------------------- + +Okay, flame is on, but before you flame, mind you, this is a very initial +version of the design. Some of the ideas may seem far fetched, the +contents may seem messy, but I believe it's now more doable than ever and +I am willing to put effort in it for the next release or two and then +maintain it afterwards. It's not going to be done in one shot maybe, but +incrementally, using input, feedback, and hints from you, guys. + +Thank you for reading till this far :-) I.d like to hear from you if any +of this made sense to you. + +Truly yours, + +-- +Serguei A. Mokhov | /~\ The ASCII +Computer Science Department | \ / Ribbon Campaign +Concordia University | X Against HTML +Montreal, Quebec, Canada | / \ Email! + +---------------------------(end of broadcast)--------------------------- +TIP 5: Have you checked our extensive FAQ? + + http://www.postgresql.org/docs/faqs/FAQ.html + +From pgsql-hackers-owner+M59486@postgresql.org Thu Sep 30 16:39:54 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i8UKdrw02740 + for ; Thu, 30 Sep 2004 16:39:53 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id EF25F329E3B + for ; Thu, 30 Sep 2004 21:39:48 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 38456-02 for ; + Thu, 30 Sep 2004 20:39:46 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 88673329C6B + for ; Thu, 30 Sep 2004 21:39:48 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id AA522329DAE + for ; Thu, 30 Sep 2004 21:37:43 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 38130-02 + for ; + Thu, 30 Sep 2004 20:37:39 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 846E9329C63 + for ; Thu, 30 Sep 2004 21:37:39 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i8UKa3jk025254; + Thu, 30 Sep 2004 16:36:03 -0400 (EDT) +To: "Serguei A. Mokhov" +cc: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] pg_upgrade project: high-level design proposal of in-place upgrade facility +In-Reply-To: +References: +Comments: In-reply-to "Serguei A. Mokhov" + message dated "Thu, 30 Sep 2004 15:49:00 -0400" +Date: Thu, 30 Sep 2004 16:36:02 -0400 +Message-ID: <25253.1096576562@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +"Serguei A. Mokhov" writes: +> Comments are very welcome, especially _*CONSTRUCTIVE*_... + +This is fundamentally wrong, because you are assigning the storage +manager functionality that it does not have. In particular, the +storage manager knows nothing of the contents or format of the files +it is managing, and so you can't realistically expect to use the smgr +switch as a way to support access to tables with different internal +formats. The places that change in interesting ways across versions +are usually far above the smgr switch. + +I don't believe in the idea of incremental "lazy" upgrades very much +either. It certainly will not work on the system catalogs --- you have +to convert those in a big-bang fashion, because how are you going to +find the other stuff otherwise? And the real problem with it IMHO is +that if something goes wrong partway through the process, you're in +deep trouble because you have no way to back out. You can't just revert +to the old version because it won't understand your data, and your old +backups that are compatible with it are now out of date. If there are +going to be any problems, you really need to find out about them +immediately while your old backups are still current, not in a "lazy" +fashion. + +The design we'd pretty much all bought into six months ago involved +being able to do in-place upgrades when the format/contents of user +relations and indexes is not changing. All you'd have to do is dump +and restore the schema data (system catalogs) which is a reasonably +short process even on a large DB, so the big-bang nature of the +conversion isn't a problem. Admittedly this will not work for every +single upgrade, but we had agreed that we could schedule upgrades +so that the majority of releases do not change user data. Historically +that's been mostly true anyway, even without any deliberate attempt to +group user-data-changing features together. + +I think the last major discussion about it started here: +http://archives.postgresql.org/pgsql-hackers/2003-12/msg00379.php +(I got distracted by other stuff and never did the promised work, +but I still think the approach is sound.) + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-hackers-owner+M59497@postgresql.org Thu Sep 30 17:44:44 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i8ULihw11377 + for ; Thu, 30 Sep 2004 17:44:43 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id D0A6B329E2E + for ; Thu, 30 Sep 2004 22:44:38 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 55636-04 for ; + Thu, 30 Sep 2004 21:44:36 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 6ED67329DFC + for ; Thu, 30 Sep 2004 22:44:38 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 040D2329E2E + for ; Thu, 30 Sep 2004 22:42:24 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 55767-04 + for ; + Thu, 30 Sep 2004 21:42:18 +0000 (GMT) +Received: from authenticity.encs.concordia.ca (authenticity-96.encs.concordia.ca [132.205.96.93]) + by svr1.postgresql.org (Postfix) with ESMTP id E3A4D329DFC + for ; Thu, 30 Sep 2004 22:42:19 +0100 (BST) +Received: from haida.cs.concordia.ca (IDENT:mokhov@haida.cs.concordia.ca [132.205.64.45]) + by authenticity.encs.concordia.ca (8.12.11/8.12.11) with ESMTP id i8ULgJrP001049; + Thu, 30 Sep 2004 17:42:19 -0400 +Date: Thu, 30 Sep 2004 17:42:19 -0400 (EDT) +From: "Serguei A. Mokhov" +To: Tom Lane +cc: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] pg_upgrade project: high-level design proposal of +In-Reply-To: <25253.1096576562@sss.pgh.pa.us> +Message-ID: +References: + <25253.1096576562@sss.pgh.pa.us> +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=US-ASCII +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +On Thu, 30 Sep 2004, Tom Lane wrote: + +> Date: Thu, 30 Sep 2004 16:36:02 -0400 +> +> "Serguei A. Mokhov" writes: +> > Comments are very welcome, especially _*CONSTRUCTIVE*_... +> +> This is fundamentally wrong, because you are assigning the storage +> manager functionality that it does not have. In particular, the + +Maybe, that's why I was asking of all init-db forcing paths, so I can go +level above smgr to upgrade stuff, let say in access/ and other parts. I +did ask that before and never got a reply. So the concept of "Storage +Managers" may and will go well beyond the smgt API. That's the design +refinement stage. + +> I don't believe in the idea of incremental "lazy" upgrades very much +> either. It certainly will not work on the system catalogs --- you have +> to convert those in a big-bang fashion, + +I never proposed to do that to system catalogs, on the contrary, I said +the system catalogs are to be upgraded upon the postmaster startup. only +user relations are upgraded lazily: + +> The relations to be upgraded are ordered according to some priority, +> e.g. system relations being first, then user-owned relations. System +> relations upgrade is forced upon the postmaster startup, and then user +> relations are processed lazily. + +So looks like we don't disagree here :) + +> The design we'd pretty much all bought into six months ago involved +> being able to do in-place upgrades when the format/contents of user +> relations and indexes is not changing. All you'd have to do is dump and +> restore the schema data (system catalogs) which is a reasonably short +> process even on a large DB, so the big-bang nature of the conversion +> isn't a problem. Admittedly this will not work for every single +> upgrade, but we had agreed that we could schedule upgrades so that the +> majority of releases do not change user data. Historically that's been +> mostly true anyway, even without any deliberate attempt to group +> user-data-changing features together. + +Annoyingly enough, they still do change. + +> I think the last major discussion about it started here: +> http://archives.postgresql.org/pgsql-hackers/2003-12/msg00379.php +> (I got distracted by other stuff and never did the promised work, +> but I still think the approach is sound.) + +I'll go over that discussion and maybe will combine useful ideas together. +I'll open a pgfoundry project to develop it there and then will submit for +evaluation UNLESS you reserved it for yourself, Tom, to fullfill the +promise... If anybody objects the pgfoundry idea to test the concepts, +I'll apply for a project there. + +Thank you for the comments! + +> regards, tom lane +> + +-- +Serguei A. Mokhov | /~\ The ASCII +Computer Science Department | \ / Ribbon Campaign +Concordia University | X Against HTML +Montreal, Quebec, Canada | / \ Email! + +---------------------------(end of broadcast)--------------------------- +TIP 7: don't forget to increase your free space map settings + diff --git a/doc/TODO.detail/pitr b/doc/TODO.detail/pitr new file mode 100644 index 0000000..d4d4c41 --- /dev/null +++ b/doc/TODO.detail/pitr @@ -0,0 +1,827 @@ +From pgsql-admin-owner+M15281=pgman=candle.pha.pa.us@postgresql.org Thu Oct 21 18:57:36 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9LLvYf17059 + for ; Thu, 21 Oct 2004 17:57:34 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 79D9132A71A + for ; Thu, 21 Oct 2004 22:57:29 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 80515-02 for ; + Thu, 21 Oct 2004 21:57:26 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 1209432A70E + for ; Thu, 21 Oct 2004 22:57:29 +0100 (BST) +X-Original-To: pgsql-admin-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 4B39932A6C3 + for ; Thu, 21 Oct 2004 22:51:01 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 78125-02 + for ; + Thu, 21 Oct 2004 21:50:48 +0000 (GMT) +Received: from news.hub.org (news.hub.org [200.46.204.72]) + by svr1.postgresql.org (Postfix) with ESMTP id 27F0632A6C2 + for ; Thu, 21 Oct 2004 22:50:49 +0100 (BST) +Received: from news.hub.org (news.hub.org [200.46.204.72]) + by news.hub.org (8.12.9/8.12.9) with ESMTP id i9LLojJ7079086 + for ; Thu, 21 Oct 2004 21:50:45 GMT + (envelope-from news@news.hub.org) +Received: (from news@localhost) + by news.hub.org (8.12.9/8.12.9/Submit) id i9LLnd7p078783 + for pgsql-admin@postgresql.org; Thu, 21 Oct 2004 21:49:39 GMT +From: Gaetano Mendola +X-Newsgroups: comp.databases.postgresql.admin +Subject: Re: [ADMIN] replication using WAL archives +Date: Thu, 21 Oct 2004 23:49:35 +0200 +Organization: PYRENET Midi-pyrenees Provider +Lines: 216 +Message-ID: <41782EEF.5040708@bigfoot.com> +References: <002801c4b739$68450870$7201a8c0@mst1x5r347kymb> <1098384082.15573.14.camel@camel> +MIME-Version: 1.0 +Content-Type: multipart/mixed; + boundary="------------060900090803090101060101" +X-Complaints-To: abuse@pyrenet.fr +cc: iain@mst.co.jp +To: Robert Treat +User-Agent: Mozilla Thunderbird 0.8 (Windows/20040913) +X-Accept-Language: en-us, en +In-Reply-To: <1098384082.15573.14.camel@camel> +X-Enigmail-Version: 0.86.1.0 +X-Enigmail-Supports: pgp-inline, pgp-mime +To: pgsql-admin@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-admin +Precedence: bulk +Sender: pgsql-admin-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +This is a multi-part message in MIME format. +--------------060900090803090101060101 +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 7bit + +Robert Treat wrote: +> On Thu, 2004-10-21 at 02:44, Iain wrote: +> +>>Hi, +>> +>>I thought I read something about this in relation to v8, but I can't +>>find any reference to it now... is it (or will it be) possible to do +>>master-slave style database replication by transmitting log files to the +>>standby server and having it process them? +>> +> +> +> I'm not certain if this is 8.0, but some folks have created a working +> version against the 8.0 code that will do something like this. Search +> the pgsql-hacker mail list archives for more information on it. + +I sent a post on hackers, I put it here: + +======================================================================= +Hi all, +I seen that Eric Kerin did the work suggested by Tom about +how to use the PITR in order to have an hot spare postgres, +writing a C program. + +I did the same writing 2 shell scripts, one of them perform +the restore the other one deliver the partial filled wal and +check if the postmaster is alive ( check if the pid process +still exist ). + +With these two scripts I'm able to have an hot spare installation, +and the spare one go alive when the first postmaster dies. + +How test it: + +1) Master node: + modify postgresql.conf using: + +~ archive_command = 'cp %p /mnt/server/archivedir/%f' + +~ launch postgres and perform a backup as doc + +~ http://developer.postgresql.org/docs/postgres/backup-online.html + + suggest to do + + launch the script: + + partial_wal_deliver.sh /mnt/server/partialdir + +~ this script will delivery each 10 seconds the "current" wal file, +~ and touch the "alive" file in order to notify the spare node that +~ the master node is up and running + + +2) Spare node: + create a recovery.conf with the line: + +~ restore_command = 'restore.sh /mnt/server/archivedir/%f %p /mnt/server/partialdir' + +~ replace the content of data directory with the backup performed at point 1, +~ remove any file present in the pg_xlog directory ( leaving there the archive_status +~ directory ) and remove the postmaster.pid file ( this is necessary if you are running +~ the spare postgres on the same hw ). + +~ launch the postmaster, the restore will continue till the "alive" file present in the +~ /mnt/server/partialdir directory is not updated for 60 seconds ( you can modify this +~ values inside the restore.sh script ). + +Be sure that restore.sh and all directories involved are accessible + +Let me know. + + +This is a first step, of course, as Eric Kerin did, is better port these script +in C and make it more robust. + +Postgres can help this process, as suggested by Tom creating a pg_current_wal() +or even better having two new GUC parameters: archive_current_wal_command and +archive_current_wal_delay. + +I problem I discover during the tests is that if you shut down the spare node +and the restore_command is still waiting for a file then the postmaster will never +exit :-( +========================================================================== + +I hope that is clear. + + + +Regards +Gaetano Mendola + + + +--------------060900090803090101060101 +Content-Type: text/plain; + name="restore.sh" +Content-Transfer-Encoding: 7bit +Content-Disposition: inline; + filename="restore.sh" + +#!/bin/bash + + +SOURCE=$1 +TARGET=$2 +PARTIAL=$3 + +SIZE_EXPECTED=16777216 #bytes 16 MB +DIED_TIME=60 #seconds + +function test_existence +{ + if [ -f ${SOURCE} ] + then + COUNTER=0 + + #I have to check if the file is begin copied + #I assume that it will reach the right + #size in a few seconds + + while [ $(stat -c '%s' ${SOURCE} ) -lt $SIZE_EXPECTED ] + do + sleep 1 + let COUNTER+=1 + if [ 20 -lt $COUNTER ] + then + exit 1 # BAILING OUT + fi + done + + cp $SOURCE $TARGET + exit 0 + fi + echo ${SOURCE}"> not found" + + #if is looking for a history file and not exist + #I have suddenly exit + echo $SOURCE | grep history > /dev/null 2>&1 && exit 1 +} + + +while [ 1 ] +do + + test_existence + + #CHECK IF THE MASTER IS ALIVE + DELTA_TIME=$(( $( date +'%s' ) - $( stat -c '%Z' ${PARTIAL}/alive ) )) + if [ $DIED_TIME -lt $DELTA_TIME ] + then + echo "Master is dead..." + # Master is dead + CURRENT_WAL=$( basename $SOURCE ) + echo "Partial: " ${PARTIAL} + echo "Current wal: " ${CURRENT_WAL} + echo "Target: " ${TARGET} + cp ${PARTIAL}/${CURRENT_WAL}.partial ${TARGET} > /dev/null 2>&1 && exit 0 + exit 1 + fi + + sleep 1 + +done + +--------------060900090803090101060101 +Content-Type: text/plain; + name="partial_wal_deliver.sh" +Content-Transfer-Encoding: 7bit +Content-Disposition: inline; + filename="partial_wal_deliver.sh" + +#!/bin/bash + +PID=$1 +PARTIAL=$2 +PGXLOG=$3 + +function copy_last_wal +{ + FILE=$( ls -t1p $PGXLOG | grep -v / | head -1 ) + + echo "Last Wal> " $FILE + + cp ${PGXLOG}/${FILE} ${PARTIAL}/${FILE}.tmp + mv ${PARTIAL}/${FILE}.tmp ${PARTIAL}/${FILE}.partial + find ${PARTIAL} -name *.partial | grep -v ${FILE} | xargs -i rm -fr {} +} + + +while [ 1 ] +do + ps --pid $PID > /dev/null 2>&1 + ALIVE=$? + + if [ "${ALIVE}" == "1" ] + then + #The process is dead + echo "Process dead" + copy_last_wal + exit 1 + fi + + #The process still exist + touch ${PARTIAL}/alive + copy_last_wal + + sleep 10 +done + +--------------060900090803090101060101 +Content-Type: text/plain +Content-Disposition: inline +Content-Transfer-Encoding: 8bit +MIME-Version: 1.0 + + +---------------------------(end of broadcast)--------------------------- +TIP 5: Have you checked our extensive FAQ? + + http://www.postgresql.org/docs/faqs/FAQ.html + +--------------060900090803090101060101-- + +From pgsql-admin-owner+M15295=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 06:32:38 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9M9Waf18397 + for ; Fri, 22 Oct 2004 05:32:36 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 9C9A532AC61 + for ; Fri, 22 Oct 2004 10:32:32 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 53654-01 for ; + Fri, 22 Oct 2004 09:32:26 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 3132D32AC53 + for ; Fri, 22 Oct 2004 10:32:32 +0100 (BST) +X-Original-To: pgsql-admin-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id DC46E32A095 + for ; Fri, 22 Oct 2004 10:23:07 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 49812-03 + for ; + Fri, 22 Oct 2004 09:22:52 +0000 (GMT) +Received: from cmailg3.svr.pol.co.uk (cmailg3.svr.pol.co.uk [195.92.195.173]) + by svr1.postgresql.org (Postfix) with ESMTP id 5AEA6329F2F + for ; Fri, 22 Oct 2004 10:22:57 +0100 (BST) +Received: from modem-21.monkey.dialup.pol.co.uk ([217.135.208.21] helo=Nightingale) + by cmailg3.svr.pol.co.uk with smtp (Exim 4.41) + id 1CKvdM-0005eh-NO; Fri, 22 Oct 2004 10:22:53 +0100 +Message-ID: <011a01c4b818$b7370a20$06e887d9@Nightingale> +From: "Simon Riggs" +To: "Gaetano Mendola" , + "Robert Treat" , +cc: +References: <002801c4b739$68450870$7201a8c0@mst1x5r347kymb> <1098384082.15573.14.camel@camel> <41782EEF.5040708@bigfoot.com> +Subject: Re: [ADMIN] replication using WAL archives +Date: Fri, 22 Oct 2004 10:22:54 +0100 +MIME-Version: 1.0 +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: 7bit +X-Priority: 3 +X-MSMail-Priority: Normal +X-Mailer: Microsoft Outlook Express 6.00.2800.1409 +X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1409 +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-admin +Precedence: bulk +Sender: pgsql-admin-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +> Gaetano Mendola wrote +> Postgres can help this process, as suggested by Tom creating a +pg_current_wal() +> or even better having two new GUC parameters: archive_current_wal_command +and +> archive_current_wal_delay. + +OK, we can modify the archiver to do this as well as the archive-when-full +functionality. I'd already agreed to do something similar for 8.1 + +PROPOSAL: +By default, archive_max_delay would be 10 seconds. +By default, archive_current_wal_command is not set. +If archive_current_wal_command is not set, the archiver will archive a file +using archive_command only when the file is full. +If archive_current_wal_command is set, the archiver would archive a file +whichever of these occurs first... +- it is full +- the archive_max_delay timeout occurs (default: disabled) +...as you can see I've renamed archive_current_wal_delay to reflect the fact +that there is an interaction between the current mechanism (only when full) +and this additional mechanism (no longer than X secs between log files). +With that design, if the logs are being created quickly enough, then a +partial log file is never created, only full ones. + +When an xlog file is archived because it is full, then it is sent to both +archive_current_wal_command and archive_command (in that order). When the +timeout occurs and we have a partial xlog file, it would only be sent to +archive_current_wal_command. It may also be desirable to not use +archive_command at all, only to use archive_current_wal_command. That's not +currently possible because archive_command is the switch by which all of the +archive functioanlity is enabled, so you can't actually turn this off. + +There is already a timeout feature designed into archiver for safety...so I +can make that read the GUCs, above and act accordingly. + +There is an unresolved resilience issue: if the archiver goes down (or +whatever does the partial_wal copy functionality) then it it is possible +that users will continue writing to the database and creating xlog records. +It would be up to the user to define how to handle records that had been +committed to the first database in the interim before cutover. It would also +be up to the user to shut down the first node from the second - Shoot the +Other Node in the Head, as its known. All of that is up to the second node, +and as Tom says, is "the hard part"....I'm not proposing to do anything +about that at this stage, since it is implementation dependant. + +I was thinking perhaps to move to having variable size xlog files, since +their contents are now variable - no padded records at EOF. If we did that, +then the archiver could simply issue a "switch logfile" and then the +archiver would cut in anyway to copy away the xlog. Having said that it is +lots easier just to put a blind timeout in the archiver and copy the file - +though I'm fairly uneasy about the point that we'd be ignoring the fact that +many people are still writing to it. But I propose doing the easy way.... + +Thoughts? + += - = - = + +Gaetano - skim-reading your script, how do you handle the situation when a +new xlog file has been written within 10 seconds? That way the current file +number will have jumped by 2, so when your script looks for the "Last wal" +using head -1 it will find the N+2 and the intermediate file will never be +copied. Looks like a problem to me... + +> I problem I discover during the tests is that if you shut down the spare +node +> and the restore_command is still waiting for a file then the postmaster +will never +> exit :-( + +Hmm....Are you reporting this as a bug for 8.0? It's not on the bug list... + +Do we consider that to be desirable or not? + +Best Regards, Simon Riggs + + + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-admin-owner+M15302=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 13:56:14 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MGuCf28637 + for ; Fri, 22 Oct 2004 12:56:13 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 54E77EAEDAA + for ; Fri, 22 Oct 2004 17:55:51 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 86116-09 for ; + Fri, 22 Oct 2004 16:55:57 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 0EC98EAEDA7 + for ; Fri, 22 Oct 2004 17:55:51 +0100 (BST) +X-Original-To: pgsql-admin-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 5DB98EAEDBE + for ; Fri, 22 Oct 2004 17:45:13 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 82473-08 + for ; + Fri, 22 Oct 2004 16:45:11 +0000 (GMT) +Received: from pns.mm.eutelsat.org (pns.mm.eutelsat.org [194.214.173.227]) + by svr1.postgresql.org (Postfix) with ESMTP id E49F0EAEDB5 + for ; Fri, 22 Oct 2004 17:45:00 +0100 (BST) +Received: from nts-03.mm.eutelsat.org (localhost [127.0.0.1]) + by pns.mm.eutelsat.org (8.11.6/linuxconf) with ESMTP id i9MGh0U26124; + Fri, 22 Oct 2004 18:43:01 +0200 +Received: from [127.0.0.1] (accesspoint.mm.eutelsat.org [194.214.173.4]) + by nts-03.mm.eutelsat.org (8.11.6/linuxconf) with ESMTP id i9MGj5f09681; + Fri, 22 Oct 2004 18:45:05 +0200 +Message-ID: <4179390B.10700@bigfoot.com> +Date: Fri, 22 Oct 2004 18:44:59 +0200 +From: Gaetano Mendola +User-Agent: Mozilla Thunderbird 0.8 (Windows/20040913) +X-Accept-Language: en-us, en +MIME-Version: 1.0 +To: Simon Riggs +cc: Robert Treat , pgsql-admin@postgresql.org, + iain@mst.co.jp +Subject: Re: [ADMIN] replication using WAL archives +References: <002801c4b739$68450870$7201a8c0@mst1x5r347kymb> <1098384082.15573.14.camel@camel> <41782EEF.5040708@bigfoot.com> <011a01c4b818$b7370a20$06e887d9@Nightingale> +In-Reply-To: <011a01c4b818$b7370a20$06e887d9@Nightingale> +X-Enigmail-Version: 0.86.1.0 +X-Enigmail-Supports: pgp-inline, pgp-mime +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 7bit +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-admin +Precedence: bulk +Sender: pgsql-admin-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +Simon Riggs wrote: +|>Gaetano Mendola wrote +|>Postgres can help this process, as suggested by Tom creating a +| +| pg_current_wal() +| +|>or even better having two new GUC parameters: archive_current_wal_command +| +| and +| +|>archive_current_wal_delay. +| +| +| OK, we can modify the archiver to do this as well as the archive-when-full +| functionality. I'd already agreed to do something similar for 8.1 +| +| PROPOSAL: +| By default, archive_max_delay would be 10 seconds. +| By default, archive_current_wal_command is not set. +| If archive_current_wal_command is not set, the archiver will archive a file +| using archive_command only when the file is full. +| If archive_current_wal_command is set, the archiver would archive a file +| whichever of these occurs first... +| - it is full +| - the archive_max_delay timeout occurs (default: disabled) +| ...as you can see I've renamed archive_current_wal_delay to reflect the fact +| that there is an interaction between the current mechanism (only when full) +| and this additional mechanism (no longer than X secs between log files). +| With that design, if the logs are being created quickly enough, then a +| partial log file is never created, only full ones. +| +| When an xlog file is archived because it is full, then it is sent to both +| archive_current_wal_command and archive_command (in that order). When the +| timeout occurs and we have a partial xlog file, it would only be sent to +| archive_current_wal_command. It may also be desirable to not use +| archive_command at all, only to use archive_current_wal_command. That's not +| currently possible because archive_command is the switch by which all of the +| archive functioanlity is enabled, so you can't actually turn this off. + +It seems good to me, the script behind archive command can be a nop if someone +want use the archive_current_wal_command + + +| = - = - = +| +| Gaetano - skim-reading your script, how do you handle the situation when a +| new xlog file has been written within 10 seconds? That way the current file +| number will have jumped by 2, so when your script looks for the "Last wal" +| using head -1 it will find the N+2 and the intermediate file will never be +| copied. Looks like a problem to me... + + +Yes, the only window failure I seen ( but I don't know if it's possible ) + +Master: +~ log N created + log N filled + archive log N + log N+1 created + log N+1 filled +~ log N+2 created +~ <---- the master die here before to archive the log N+1 +~ archive log N+1 + + +in this case as you underline tha last log archived is the N and the N+2 +partial wal is added to archived wal collection and in the recovery fase +the recovery stop after processing the log N. + +Is it possible that the postmaster create the N+2 file without finish to archive +the N+1 ? ( I suspect yes :-( ) + +The only cure I see here is to look for not archived WAL ( if possible ). + + +|>I problem I discover during the tests is that if you shut down the spare +|>node and the restore_command is still waiting for a file then the postmaster +|>will never exit :-( +| +| +| Hmm....Are you reporting this as a bug for 8.0? It's not on the bug list... + +For me is a behave to avoid. + + + +Regards +Gaetano Mendola + + + + + + + +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.2.5 (MingW32) +Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org + +iD8DBQFBeTkJ7UpzwH2SGd4RAsMxAKCbV7W+wrGBocf2Ftlthm0egAlIWACgp87L +KU/YusyHuvT7jSFwZVKpP3M= +=rWZx +-----END PGP SIGNATURE----- + + +---------------------------(end of broadcast)--------------------------- +TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org + +From pgsql-admin-owner+M15303=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 14:43:36 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MHhZf06453 + for ; Fri, 22 Oct 2004 13:43:35 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 01DD2EADBB7 + for ; Fri, 22 Oct 2004 18:43:13 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 01872-03 for ; + Fri, 22 Oct 2004 17:43:19 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 9E633EADAD4 + for ; Fri, 22 Oct 2004 18:43:12 +0100 (BST) +X-Original-To: pgsql-admin-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id C1133EAED89 + for ; Fri, 22 Oct 2004 18:31:20 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 97130-03 + for ; + Fri, 22 Oct 2004 17:31:17 +0000 (GMT) +Received: from cmailm2.svr.pol.co.uk (cmailm2.svr.pol.co.uk [195.92.193.210]) + by svr1.postgresql.org (Postfix) with ESMTP id 276CAEADBBD + for ; Fri, 22 Oct 2004 18:31:07 +0100 (BST) +Received: from modem-558.snake.dialup.pol.co.uk ([62.137.114.46] helo=[192.168.0.102]) + by cmailm2.svr.pol.co.uk with esmtp (Exim 4.41) + id 1CL3G3-0001Tx-K5; Fri, 22 Oct 2004 18:31:20 +0100 +Subject: Re: [ADMIN] replication using WAL archives +From: Simon Riggs +To: Gaetano Mendola +cc: Robert Treat , pgsql-admin@postgresql.org, + iain@mst.co.jp +In-Reply-To: <4179390B.10700@bigfoot.com> +References: <002801c4b739$68450870$7201a8c0@mst1x5r347kymb> + <1098384082.15573.14.camel@camel> <41782EEF.5040708@bigfoot.com> + <011a01c4b818$b7370a20$06e887d9@Nightingale> <4179390B.10700@bigfoot.com> +Content-Type: text/plain +Organization: 2nd Quadrant +Message-ID: <1098466150.20926.13.camel@localhost.localdomain> +MIME-Version: 1.0 +X-Mailer: Ximian Evolution 1.4.6 (1.4.6-2) +Date: Fri, 22 Oct 2004 18:29:10 +0100 +Content-Transfer-Encoding: 7bit +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-admin +Precedence: bulk +Sender: pgsql-admin-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +On Fri, 2004-10-22 at 17:44, Gaetano Mendola wrote: +> | Gaetano - skim-reading your script, how do you handle the situation when a +> | new xlog file has been written within 10 seconds? That way the current file +> | number will have jumped by 2, so when your script looks for the "Last wal" +> | using head -1 it will find the N+2 and the intermediate file will never be +> | copied. Looks like a problem to me... +> +> +> Yes, the only window failure I seen ( but I don't know if it's possible ) +> +> Master: +> ~ log N created +> log N filled +> archive log N +> log N+1 created +> log N+1 filled +> ~ log N+2 created +> ~ <---- the master die here before to archive the log N+1 +> ~ archive log N+1 +> +> +> in this case as you underline tha last log archived is the N and the N+2 +> partial wal is added to archived wal collection and in the recovery fase +> the recovery stop after processing the log N. +> +> Is it possible that the postmaster create the N+2 file without finish to archive +> the N+1 ? ( I suspect yes :-( ) +> +> The only cure I see here is to look for not archived WAL ( if possible ). +> + +Hmm...well you aren't looking for archived wal, you're just looking at +wal...which is a different thing... + +Situation I thought I saw was: + +- copy away current partial filled xlog N +- xlog N fills, N+1 starts +- xlog N+1 fills, N+2 starts +- copy away current partial filled xlog: N+2 (+10 secs later) + +i.e. if time to fill xlog (is ever) < time to copy away current xlog, +then you miss one. + +So problem: you can miss one and never know you've missed one until the +recovery can't find it, which it never returns from...so it just hangs. + +[Just so we're all clear: we're talking about Gaetano's script, not the +PostgreSQL archver. The postgresql archiver doesn't do it that way, so +it never misses one.] + +-- +Best Regards, Simon Riggs + + +---------------------------(end of broadcast)--------------------------- +TIP 9: the planner will ignore your desire to choose an index scan if your + joining column's datatypes do not match + +From pgsql-admin-owner+M15306=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 17:56:07 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MKu6f05264 + for ; Fri, 22 Oct 2004 16:56:06 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 4F4C2EAE4AE + for ; Fri, 22 Oct 2004 21:55:41 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 62857-05 for ; + Fri, 22 Oct 2004 20:55:48 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 095CEEAE4AC + for ; Fri, 22 Oct 2004 21:55:41 +0100 (BST) +X-Original-To: pgsql-admin-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 3FC9BEAE486 + for ; Fri, 22 Oct 2004 21:50:48 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 62565-02 + for ; + Fri, 22 Oct 2004 20:50:48 +0000 (GMT) +Received: from news.hub.org (news.hub.org [200.46.204.72]) + by svr1.postgresql.org (Postfix) with ESMTP id 06C49EAE46B + for ; Fri, 22 Oct 2004 21:50:40 +0100 (BST) +Received: from news.hub.org (news.hub.org [200.46.204.72]) + by news.hub.org (8.12.9/8.12.9) with ESMTP id i9MKolJB062812 + for ; Fri, 22 Oct 2004 20:50:48 GMT + (envelope-from news@news.hub.org) +Received: (from news@localhost) + by news.hub.org (8.12.9/8.12.9/Submit) id i9MKoRHh062731 + for pgsql-admin@postgresql.org; Fri, 22 Oct 2004 20:50:27 GMT +From: Gaetano Mendola +X-Newsgroups: comp.databases.postgresql.admin +Subject: Re: [ADMIN] replication using WAL archives +Date: Fri, 22 Oct 2004 22:50:34 +0200 +Organization: PYRENET Midi-pyrenees Provider +Lines: 39 +Message-ID: <4179729A.5020401@bigfoot.com> +References: <002801c4b739$68450870$7201a8c0@mst1x5r347kymb> <1098384082.15573.14.camel@camel> <41782EEF.5040708@bigfoot.com> <011a01c4b818$b7370a20$06e887d9@Nightingale> <4179390B.10700@bigfoot.com> <1098466150.20926.13.camel@localhost.localdomain> +MIME-Version: 1.0 +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 7bit +X-Complaints-To: abuse@pyrenet.fr +To: Simon Riggs +User-Agent: Mozilla Thunderbird 0.8 (Windows/20040913) +X-Accept-Language: en-us, en +In-Reply-To: <1098466150.20926.13.camel@localhost.localdomain> +X-Enigmail-Version: 0.86.1.0 +X-Enigmail-Supports: pgp-inline, pgp-mime +To: pgsql-admin@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-admin +Precedence: bulk +Sender: pgsql-admin-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +Simon Riggs wrote: + + > Situation I thought I saw was: + > + > - copy away current partial filled xlog N + > - xlog N fills, N+1 starts + > - xlog N+1 fills, N+2 starts + > - copy away current partial filled xlog: N+2 (+10 secs later) + > + > i.e. if time to fill xlog (is ever) < time to copy away current xlog, + > then you miss one. + > + > So problem: you can miss one and never know you've missed one until the + > recovery can't find it, which it never returns from...so it just hangs. + +No. The restore.sh is not smart enough to know the last wal that must be +replayed, the only "smart thing" is to copy the supposed "current wal" in the +archive directory. + +The script hang (and is a feature not a bug) if and only if the master is alive +( at least I'm not seeing any other hang ). + +In your example in the archived directory will be present the files until logN +and logN+2 ( the current wal ) is in the partial directory, if the master die, +the restore.sh will copy logN+2 in the archived directory, the spare node will +execute restore.sh with file logN+1 as argument and if is not found then the +restore.sh will exit. + + +Regards +Gaetano Mendola + + + + + + + + + +---------------------------(end of broadcast)--------------------------- +TIP 8: explain analyze is your friend + diff --git a/doc/TODO.detail/timezone b/doc/TODO.detail/timezone new file mode 100644 index 0000000..f4fabc1 --- /dev/null +++ b/doc/TODO.detail/timezone @@ -0,0 +1,2829 @@ +From pgsql-hackers-owner+M60207=pgman=candle.pha.pa.us@postgresql.org Thu Oct 21 07:25:41 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9LBPdf26430 + for ; Thu, 21 Oct 2004 07:25:40 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 0548B32A593 + for ; Thu, 21 Oct 2004 12:25:37 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 74257-06 for ; + Thu, 21 Oct 2004 11:25:33 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id B0DB532A544 + for ; Thu, 21 Oct 2004 12:25:36 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 1124A329FAB + for ; Thu, 21 Oct 2004 12:23:00 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 70900-09 + for ; + Thu, 21 Oct 2004 11:22:43 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 301C532A301 + for ; Thu, 21 Oct 2004 12:22:43 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP id A022C8467 + for ; Thu, 21 Oct 2004 13:22:40 +0200 (CEST) +Date: Thu, 21 Oct 2004 13:22:40 +0200 (CEST) +From: Dennis Bjorklund +To: pgsql-hackers@postgresql.org +Subject: [HACKERS] timestamp with time zone a la sql99 +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +I've made a partial implementation of a datatype "timestamp with time +zone" as described in the sql standard. The current type "timestamptz" +does not store the time zone as a standard one should do. So I've made a +new type I've called timestampstdtz that does store the time zone as the +standard demands. + +Let me show a bit of what currently works in my implementation: + + dennis=# CREATE TABLE foo ( + a timestampstdtz, + + primary key (a) + ); + dennis=# INSERT INTO foo VALUES ('1993-02-04 13:00 UTC'); + dennis=# INSERT INTO foo VALUES ('1999-06-01 14:00 CET'); + dennis=# INSERT INTO foo VALUES ('2003-08-21 15:00 PST'); + + dennis=# SELECT a FROM foo; + a + ------------------------ + 1993-02-04 13:00:00+00 + 1999-06-01 14:00:00+01 + 2003-08-21 15:00:00-08 + + dennis=# SELECT a AT TIME ZONE 'CET' FROM foo; + timezone + ------------------------ + 1993-02-04 14:00:00+01 + 1999-06-01 14:00:00+01 + 2003-08-22 00:00:00+01 + +My plan is to make a GUC variable so that one can tell PG that constructs +like "timestamp with time zone" will map to timestampstdtz instead of +timestamptz (some old databases might need the old so unless we want to +break old code this is the easiest solution I can find). + +I've made an implicit cast from timestampstdtz to timestamptz that just +forgets about the time zone. In the other direction I've made an +assignment cast that make a timestamp with time zone 0 (that's what a +timestamptz is anyway). Would it be possible to make it implicit in both +directions? I currently don't think that you want that, but is it +possible? + +With the implicit cast in place I assume it would be safe to change +functions like now() to return a timestampstdtz? I've not tried yet but I +will. As far as I can tell the cast would make old code that use now() to +still work as before. + +Any comments before I invest more time into this subject? + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-hackers-owner+M60208=pgman=candle.pha.pa.us@postgresql.org Thu Oct 21 10:34:36 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9LEYYf08049 + for ; Thu, 21 Oct 2004 10:34:35 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 34B0F32A2AB + for ; Thu, 21 Oct 2004 15:34:30 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 38287-03 for ; + Thu, 21 Oct 2004 14:34:26 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 6A38132A1B6 + for ; Thu, 21 Oct 2004 15:34:29 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 535FE32A9EE + for ; Thu, 21 Oct 2004 15:29:17 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 34535-09 + for ; + Thu, 21 Oct 2004 14:29:10 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 41E4F32A9D6 + for ; Thu, 21 Oct 2004 15:29:12 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9LET86O015233; + Thu, 21 Oct 2004 10:29:10 -0400 (EDT) +To: Dennis Bjorklund +cc: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: +References: +Comments: In-reply-to Dennis Bjorklund + message dated "Thu, 21 Oct 2004 13:22:40 +0200" +Date: Thu, 21 Oct 2004 10:29:07 -0400 +Message-ID: <15232.1098368947@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis Bjorklund writes: +> I've made a partial implementation of a datatype "timestamp with time +> zone" as described in the sql standard. The current type "timestamptz" +> does not store the time zone as a standard one should do. + +I'm aware that there are aspects of the spec behavior that appear to +require that, but is it really an improvement over the implementation +we have? This is an area in which the standard is pretty brain-dead +--- the entire concept of a "time with time zone" datatype is rather +suspect, for instance. + +In particular, I wonder how you will handle daylight-savings issues. +The spec definition seems to preclude doing anything intelligent with +DST, as they equate a timezone with a fixed offset from UTC. That's +not how it works in (large parts of) the real world. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 6: Have you searched our list archives? + + http://archives.postgresql.org + +From pgsql-hackers-owner+M60210=pgman=candle.pha.pa.us@postgresql.org Thu Oct 21 11:08:02 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9LF7xf13992 + for ; Thu, 21 Oct 2004 11:08:00 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id AEBEE32AB8E + for ; Thu, 21 Oct 2004 16:07:55 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 51273-03 for ; + Thu, 21 Oct 2004 15:07:55 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 506C832AB6A + for ; Thu, 21 Oct 2004 16:07:55 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 59130329F96 + for ; Thu, 21 Oct 2004 16:02:14 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 48602-06 + for ; + Thu, 21 Oct 2004 15:01:59 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 8ED0932A095 + for ; Thu, 21 Oct 2004 16:01:54 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id D08B88467; Thu, 21 Oct 2004 17:01:52 +0200 (CEST) +Date: Thu, 21 Oct 2004 17:01:52 +0200 (CEST) +From: Dennis Bjorklund +To: Tom Lane +cc: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <15232.1098368947@sss.pgh.pa.us> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Thu, 21 Oct 2004, Tom Lane wrote: + +> > I've made a partial implementation of a datatype "timestamp with time +> > zone" as described in the sql standard. The current type "timestamptz" +> > does not store the time zone as a standard one should do. +> +> I'm aware that there are aspects of the spec behavior that appear to +> require that, but is it really an improvement over the implementation +> we have? + +Improvement and improvement. The actual time value is of course the same +(the utc part of a timestamp) and the only thing extra you get is that the +time zone is stored. The extra information you do have now, when stored in +this way, is that you store both a utc time and a local time. Will any +application ever need that? Who knows? I think it makes sense and is an +easier model to think about then what pg uses today. So I would use it +even if it means using 2 bytes more storage then what timestamptz do + +Just that it is standard also makes it useful. The more things of the +standard we support the easier it is to move between databases. This is +important to me. + +I also want to make a general statement that I think that whenever we use +standard syntax we should give it a standard semantics. I don't mind +extensions at all, but as much as we can we should make sure that they +don't clash with standard syntax and semantics. + +> This is an area in which the standard is pretty brain-dead +> --- the entire concept of a "time with time zone" datatype is rather +> suspect, for instance. + +I havn't look that much at "time with time zone" yet, just timestamps. + +I can't see why time with time zone should not also be supported. I can't +really imagine it being used without a date, but if someone wants to store +timestamps as a date+time with time zone, then why not. It would be extra +work tu is it instead of a timestamp (especially for cases where the time +wraps over to the prev/next day), but hey. + +> In particular, I wonder how you will handle daylight-savings issues. +> The spec definition seems to preclude doing anything intelligent with +> DST, as they equate a timezone with a fixed offset from UTC. That's +> not how it works in (large parts of) the real world. + +The tz in the standard is a offset from utc, yes. So when you store a +value you tell it what offset you use. If you are using daylight-savings +time it might be +02 and if not dst it might be +01. What else would you +want to do with it? It's not like you can do anything else with it in pg +as of today, can you? + +The stored tz does not say what region of the globe you are in, it says +the distance away from utc in minutes that you are. I could imagine +another datatype that stores the time zone as name, but that's not what +timestamp with time zone does. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 8: explain analyze is your friend + +From pgsql-hackers-owner+M60232=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 08:43:06 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MCh4f16646 + for ; Fri, 22 Oct 2004 08:43:04 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id D389532A4BA + for ; Fri, 22 Oct 2004 13:42:58 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 01914-09 for ; + Fri, 22 Oct 2004 12:42:51 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 82BD832A4B3 + for ; Fri, 22 Oct 2004 13:42:58 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 05C2132A3F5 + for ; Fri, 22 Oct 2004 13:39:37 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 03281-02 + for ; + Fri, 22 Oct 2004 12:39:20 +0000 (GMT) +Received: from lakermmtao07.cox.net (lakermmtao07.cox.net [68.230.240.32]) + by svr1.postgresql.org (Postfix) with ESMTP id 1971732A32B + for ; Fri, 22 Oct 2004 13:39:26 +0100 (BST) +Received: from [192.168.0.9] (really [24.250.237.182]) + by lakermmtao07.cox.net + (InterMail vM.6.01.03.04 201-2131-111-106-20040729) with ESMTP + id <20041022123912.IJSP14063.lakermmtao07.cox.net@[192.168.0.9]>; + Fri, 22 Oct 2004 08:39:12 -0400 +From: Robert Treat +To: Dennis Bjorklund +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Date: Fri, 22 Oct 2004 08:37:33 -0400 +User-Agent: KMail/1.6.2 +cc: Tom Lane , pgsql-hackers@postgresql.org +References: +In-Reply-To: +MIME-Version: 1.0 +Content-Disposition: inline +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: 7bit +Message-ID: <200410220837.33886.xzilla@users.sourceforge.net> +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Thursday 21 October 2004 11:01, Dennis Bjorklund wrote: +> On Thu, 21 Oct 2004, Tom Lane wrote: +> > I'm aware that there are aspects of the spec behavior that appear to +> > require that, but is it really an improvement over the implementation +> > we have? +> +> Improvement and improvement. The actual time value is of course the same +> (the utc part of a timestamp) and the only thing extra you get is that the +> time zone is stored. The extra information you do have now, when stored in +> this way, is that you store both a utc time and a local time. Will any +> application ever need that? Who knows? I think it makes sense and is an +> easier model to think about then what pg uses today. So I would use it +> even if it means using 2 bytes more storage then what timestamptz do +> + +In a fit of early morning, pre-coffee thoughts, I'm thinking this might be +just what I've been looking for. In one of my apps we take calls from around +the country for customers and store the time that call came in. Unfortunately +we need to know things like how many calls did we take in an hour across +customers, but also how many calls did we take at 6AM local time to the +customer. The way PostgreSQL works now, you have to store some extra bits +of info in another column and then reassemble it to be able to determine +those two queries, but it sounds like your timestampstdtz would allow that +information to be stored together, as it should be. + +-- +Robert Treat +Build A Brighter Lamp :: Linux Apache {middleware} PostgreSQL + +---------------------------(end of broadcast)--------------------------- +TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org + +From pgsql-hackers-owner+M60235=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 10:18:44 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MEIhf03000 + for ; Fri, 22 Oct 2004 10:18:44 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 63ACB329FE3 + for ; Fri, 22 Oct 2004 15:18:38 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 35128-09 for ; + Fri, 22 Oct 2004 14:18:30 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 3439A329E73 + for ; Fri, 22 Oct 2004 15:18:38 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id C332832A5AA + for ; Fri, 22 Oct 2004 15:13:18 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 32986-06 + for ; + Fri, 22 Oct 2004 14:13:09 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id E7E6F32A576 + for ; Fri, 22 Oct 2004 15:13:16 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9MEDIA4006541; + Fri, 22 Oct 2004 10:13:18 -0400 (EDT) +To: Robert Treat +cc: Dennis Bjorklund , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410220837.33886.xzilla@users.sourceforge.net> +References: <200410220837.33886.xzilla@users.sourceforge.net> +Comments: In-reply-to Robert Treat + message dated "Fri, 22 Oct 2004 08:37:33 -0400" +Date: Fri, 22 Oct 2004 10:13:18 -0400 +Message-ID: <6540.1098454398@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Robert Treat writes: +> In a fit of early morning, pre-coffee thoughts, I'm thinking this might be +> just what I've been looking for. In one of my apps we take calls from around +> the country for customers and store the time that call came in. Unfortunately +> we need to know things like how many calls did we take in an hour across +> customers, but also how many calls did we take at 6AM local time to the +> customer. The way PostgreSQL works now, you have to store some extra bits +> of info in another column and then reassemble it to be able to determine +> those two queries, but it sounds like your timestampstdtz would allow that +> information to be stored together, as it should be. + +As far as I can tell, Dennis is planning slavish adherence to the spec, +which will mean that the datatype is unable to cope effectively with +daylight-savings issues. So I'm unconvinced that it will be very +helpful to you for remembering local time in addition to true +(universal) time. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60237=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 10:33:04 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MEX3f05134 + for ; Fri, 22 Oct 2004 10:33:03 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id AB3B432A907 + for ; Fri, 22 Oct 2004 15:32:57 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 42366-04 for ; + Fri, 22 Oct 2004 14:32:49 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 28B5E32A6BE + for ; Fri, 22 Oct 2004 15:32:56 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 5366A32A923 + for ; Fri, 22 Oct 2004 15:28:12 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 41328-02 + for ; + Fri, 22 Oct 2004 14:28:03 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 0277A32A916 + for ; Fri, 22 Oct 2004 15:28:10 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 336C88467; Fri, 22 Oct 2004 16:28:12 +0200 (CEST) +Date: Fri, 22 Oct 2004 16:28:12 +0200 (CEST) +From: Dennis Bjorklund +To: Tom Lane +cc: Robert Treat , + +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <6540.1098454398@sss.pgh.pa.us> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Fri, 22 Oct 2004, Tom Lane wrote: + +> As far as I can tell, Dennis is planning slavish adherence to the spec, +> which will mean that the datatype is unable to cope effectively with +> daylight-savings issues. So I'm unconvinced that it will be very +> helpful to you for remembering local time in addition to true +> (universal) time. + +And exactly what issues is it that you see? The only thing I can think of +is if you have a timestamp and then add an interval to it so we jump past +the daylight saving time change date. Then the new timestamp will keep the +old timezone data of say +01 even though we now have jumped into the +daylight saving period of +02. + +If you are just storing actual timestamps then the standard definition +works just fine. If I store '2004-10-22 16:20:04 +02' then that's exactly +what I get back. No problem what so ever. There is no DST problem with +that. + +It's possible that I will introduce some daylight saving bit or something +like that, I'm not sure yet and I will not commit to anything until I've +thought it over. I don't think there are that much of a problem as you +claim however. Could you give a concret example where it will be a +problem? + +My current thinking is that storing the time zone value as HH:MM is +just fine and you avoid all the problems with political changes of when +the DST is in effect or not. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60240=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 10:58:26 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MEwOf10545 + for ; Fri, 22 Oct 2004 10:58:24 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 8F3BC32A0AC + for ; Fri, 22 Oct 2004 15:58:18 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 53613-02 for ; + Fri, 22 Oct 2004 14:58:10 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 32CD532A0A2 + for ; Fri, 22 Oct 2004 15:58:18 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 65D9932A0AC + for ; Fri, 22 Oct 2004 15:54:30 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 49578-08 + for ; + Fri, 22 Oct 2004 14:54:12 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id A7D2A329FB7 + for ; Fri, 22 Oct 2004 15:54:17 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9MEsJsB006995; + Fri, 22 Oct 2004 10:54:19 -0400 (EDT) +To: Dennis Bjorklund +cc: Robert Treat , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: +References: +Comments: In-reply-to Dennis Bjorklund + message dated "Fri, 22 Oct 2004 16:28:12 +0200" +Date: Fri, 22 Oct 2004 10:54:19 -0400 +Message-ID: <6994.1098456859@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis Bjorklund writes: +> And exactly what issues is it that you see? The only thing I can think of +> is if you have a timestamp and then add an interval to it so we jump past +> the daylight saving time change date. Then the new timestamp will keep the +> old timezone data of say +01 even though we now have jumped into the +> daylight saving period of +02. + +Isn't that sufficient? You can't design a datatype by thinking only of +the data values it stores; you have to think about the operations you +intend to provide as well. A non-DST-capable timestamp datatype is +inherently a few bricks shy of a load. (BTW we really need to fix +the interval type as well...) + +At bottom, what I want to be able to do is say + '2004-10-22 10:50:16.916003 America/New_York' +and have the datatype preserve *all* of the information in that. You +are complaining because the existing type only remembers the equivalent +universal time and not the timezone spec. Why should I be satisfied if +it stores only the GMT offset and not the knowledge of which timezone +this really is? + +> My current thinking is that storing the time zone value as HH:MM is +> just fine and you avoid all the problems with political changes of when +> the DST is in effect or not. + +This is fundamentally misguided. Time zones *are* political whether you +like it or not, and people *do* expect DST-awareness whether you like it +or not. If you still use any computer systems that need to be reset +twice a year because their designers thought DST was not their problem, +don't you roundly curse them every time you have to do it? + +If you were planning to store a real (potentially DST-aware) timezone +spec in the data values, I'd be happy. But storing a fixed GMT offset +is going to be a step backwards compared to existing functionality. The +fact that it's sufficient to satisfy the DST-ignorant SQL spec does not +make it a reasonable design for the real world. + +One way to do this would be to create a system catalog with entries for +all known timezones, and then represent timestamptz values as universal +time plus an OID from that catalog. There are other ways that small +integer codes could be mapped to timezones of course. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-hackers-owner+M60239=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 10:49:13 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MEnCf08566 + for ; Fri, 22 Oct 2004 10:49:12 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 5B4D432A0A2 + for ; Fri, 22 Oct 2004 15:49:07 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 50773-01 for ; + Fri, 22 Oct 2004 14:48:59 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 230EA329F96 + for ; Fri, 22 Oct 2004 15:49:07 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 7CCE332A20E + for ; Fri, 22 Oct 2004 15:46:10 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 45902-10 + for ; + Fri, 22 Oct 2004 14:45:52 +0000 (GMT) +Received: from wolff.to (wolff.to [66.93.249.74]) + by svr1.postgresql.org (Postfix) with SMTP id 30F0F32A1FA + for ; Fri, 22 Oct 2004 15:45:58 +0100 (BST) +Received: (qmail 17526 invoked by uid 500); 22 Oct 2004 14:56:44 -0000 +Date: Fri, 22 Oct 2004 09:56:44 -0500 +From: Bruno Wolff III +To: Dennis Bjorklund +cc: Tom Lane , Robert Treat , + pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Message-ID: <20041022145644.GA17238@wolff.to> +Mail-Followup-To: Dennis Bjorklund , + Tom Lane , + Robert Treat , + pgsql-hackers@postgresql.org +References: <6540.1098454398@sss.pgh.pa.us> +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline +In-Reply-To: +User-Agent: Mutt/1.5.6i +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Fri, Oct 22, 2004 at 16:28:12 +0200, + Dennis Bjorklund wrote: +> On Fri, 22 Oct 2004, Tom Lane wrote: +> +> > As far as I can tell, Dennis is planning slavish adherence to the spec, +> > which will mean that the datatype is unable to cope effectively with +> > daylight-savings issues. So I'm unconvinced that it will be very +> > helpful to you for remembering local time in addition to true +> > (universal) time. +> +> And exactly what issues is it that you see? The only thing I can think of +> is if you have a timestamp and then add an interval to it so we jump past +> the daylight saving time change date. Then the new timestamp will keep the +> old timezone data of say +01 even though we now have jumped into the +> daylight saving period of +02. + +I think for just storing values you are fine. When it comes to adding or +subtracting intervals you might get some unexpected results. + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60253=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 14:44:05 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MIi3f14607 + for ; Fri, 22 Oct 2004 14:44:03 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id B336CEAEDAF + for ; Fri, 22 Oct 2004 19:43:39 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 21148-08 for ; + Fri, 22 Oct 2004 18:43:46 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 4FC1AEAEDAB + for ; Fri, 22 Oct 2004 19:43:39 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 178B1EAEF44 + for ; Fri, 22 Oct 2004 19:38:04 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 20287-01 + for ; + Fri, 22 Oct 2004 18:38:00 +0000 (GMT) +Received: from www.postgresql.com (www.postgresql.com [200.46.204.209]) + by svr1.postgresql.org (Postfix) with ESMTP id D1F79EAEE4F + for ; Fri, 22 Oct 2004 19:37:45 +0100 (BST) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by www.postgresql.com (Postfix) with ESMTP id 358575A1CED + for ; Fri, 22 Oct 2004 16:34:33 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 89B7E8467; Fri, 22 Oct 2004 17:34:19 +0200 (CEST) +Date: Fri, 22 Oct 2004 17:34:19 +0200 (CEST) +From: Dennis Bjorklund +To: Tom Lane +cc: Robert Treat , + +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <6994.1098456859@sss.pgh.pa.us> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Fri, 22 Oct 2004, Tom Lane wrote: + +> At bottom, what I want to be able to do is say +> '2004-10-22 10:50:16.916003 America/New_York' + +Yes, that's what we said in the last mail and I think there is a value in +having something like this. + +> universal time and not the timezone spec. Why should I be satisfied if +> it stores only the GMT offset and not the knowledge of which timezone +> this really is? + +You don't need to be satisfied with it. I think a type like the above +would be fine to have. It should however not be called "TIMESTAMP WITH +TIME ZONE" because there is already a definition of that type. We can not +hijack standard types. I would not mind a type like TIMESTAMP WITH TIME +ZONE NAME (or some other name). I could even imagine that I could +implement something like that one day. + +> > My current thinking is that storing the time zone value as HH:MM is +> > just fine and you avoid all the problems with political changes of when +> > the DST is in effect or not. +> +> This is fundamentally misguided. Time zones *are* political whether you +> like it or not, and people *do* expect DST-awareness whether you like it +> or not. + +And I never said that time zones are not political, just that HH:MM is a +usable approximation that works fairly well. + +> But storing a fixed GMT offset is going to be a step backwards compared +> to existing functionality. + +It's not a step backwards since you can do everything you can do with the +current type plus a little bit more. It's however not a step to the +datatype discussed above. + +> One way to do this would be to create a system catalog with entries for +> all known timezones, and then represent timestamptz values as universal +> time plus an OID from that catalog. There are other ways that small +> integer codes could be mapped to timezones of course. + +This is just fine. You try to make it sound like I am against such a +datatype, I am not. It's however not the datatype that we can expect +applications and other databases to use. So why should we settle for only +that type. Just because you can make a perfect datatype it doesn't mean +that the standard datatype should just be ignored. + +What would you store when the user supplies a timestamp like '2004-10-22 +17:21:00 +0200'. Should you reject that because you don't know the +time zone name? So your datatype will not work for applications that try +to be compatable with many databases by using the standard? + +Maybe one could make a datatype called TIMESTAMP WITH TIME ZONE that can +accept both HH:MM and TimeZoneName. Whenever you store values with HH:MM +time zones you will get the same problem when you add an interval as the +standard type has. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 8: explain analyze is your friend + +From pgsql-hackers-owner+M60266=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 17:04:46 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9ML4jf06676 + for ; Fri, 22 Oct 2004 17:04:45 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id C13F9EAE46B + for ; Fri, 22 Oct 2004 22:04:20 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 66740-03 for ; + Fri, 22 Oct 2004 21:04:28 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 75764EAE1A2 + for ; Fri, 22 Oct 2004 22:04:20 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 2E4AFEAE486 + for ; Fri, 22 Oct 2004 22:01:22 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 63290-09 + for ; + Fri, 22 Oct 2004 21:01:22 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 88B3FEAE492 + for ; Fri, 22 Oct 2004 22:01:13 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9ML1WrB018877; + Fri, 22 Oct 2004 17:01:34 -0400 (EDT) +To: Dennis Bjorklund +cc: Robert Treat , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: +References: +Comments: In-reply-to Dennis Bjorklund + message dated "Fri, 22 Oct 2004 17:34:19 +0200" +Date: Fri, 22 Oct 2004 17:01:32 -0400 +Message-ID: <18876.1098478892@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis Bjorklund writes: +> You don't need to be satisfied with it. I think a type like the above +> would be fine to have. It should however not be called "TIMESTAMP WITH +> TIME ZONE" because there is already a definition of that type. We can not +> hijack standard types. + +Sure we can, as long as they are upward compatible with the standard +behavior. The spec says you can put a numeric-GMT-offset zone in and +get a numeric-GMT-offset zone out. We can do that and also support +named, possibly DST-aware zones. This seems a whole lot better to me +than having two different types (the idea of a GUC variable to choose +which one is selected by a given type name is just horrid). + +>> But storing a fixed GMT offset is going to be a step backwards compared +>> to existing functionality. + +> It's not a step backwards since you can do everything you can do with the +> current type plus a little bit more. + +... except get useful answers from interval addition ... + +> What would you store when the user supplies a timestamp like '2004-10-22 +> 17:21:00 +0200'. Should you reject that because you don't know the +> time zone name? + +You are attacking a straw man. + +We have put a great deal of work into 8.0 to add the ability to support +real-world zones fully. We did not import src/timezone because we +needed it to implement the SQL spec; we did so because we needed it to +implement what real users want. We are not fully there yet (can't do AT +TIME ZONE conversions with all zones yet, for instance) but I am hoping +to be there by 8.1. It would be folly to invent a timestamp with time +zone type that is going in the other direction while we are trying to +bring the rest of the system up to full speed by allowing all timezone +kinds everywhere. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60267=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 17:22:15 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MLMEf09207 + for ; Fri, 22 Oct 2004 17:22:14 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 0B2D7EAE4AC + for ; Fri, 22 Oct 2004 22:21:49 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 70387-04 for ; + Fri, 22 Oct 2004 21:21:56 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 0930BEAE4AA + for ; Fri, 22 Oct 2004 22:21:48 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 292E8EAC955 + for ; Fri, 22 Oct 2004 22:18:05 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 68984-06 + for ; + Fri, 22 Oct 2004 21:18:04 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id A3C4DEAE489 + for ; Fri, 22 Oct 2004 22:17:51 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 065508469; Fri, 22 Oct 2004 23:18:08 +0200 (CEST) +Date: Fri, 22 Oct 2004 23:18:07 +0200 (CEST) +From: Dennis Bjorklund +To: Tom Lane +cc: Robert Treat , + +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <18876.1098478892@sss.pgh.pa.us> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Fri, 22 Oct 2004, Tom Lane wrote: + +> than having two different types (the idea of a GUC variable to choose +> which one is selected by a given type name is just horrid). + +That is needed no matter what change you do if you want old programs that +use the current timestamp with time zone to work. Today you don't get back +the same time zone as you insert, programs might depend on that. + +> We are not fully there yet (can't do AT TIME ZONE conversions with all +> zones yet, for instance) + +Why is that? When one start with a utc value, performing a AT TIME ZONE +operation doesn't look so complicated. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 5: Have you checked our extensive FAQ? + + http://www.postgresql.org/docs/faqs/FAQ.html + +From pgsql-hackers-owner+M60268=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 17:41:36 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MLfYf12294 + for ; Fri, 22 Oct 2004 17:41:35 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id EFAEAEAE4AA + for ; Fri, 22 Oct 2004 22:41:08 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 75185-08 for ; + Fri, 22 Oct 2004 21:41:16 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 311A7EAE4AF + for ; Fri, 22 Oct 2004 22:41:08 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id AC0CFEAE486 + for ; Fri, 22 Oct 2004 22:38:14 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 76579-03 + for ; + Fri, 22 Oct 2004 21:38:13 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id BD0AAEAD7E2 + for ; Fri, 22 Oct 2004 22:38:03 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9MLcOnT019118; + Fri, 22 Oct 2004 17:38:25 -0400 (EDT) +To: Dennis Bjorklund +cc: Robert Treat , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: +References: +Comments: In-reply-to Dennis Bjorklund + message dated "Fri, 22 Oct 2004 23:18:07 +0200" +Date: Fri, 22 Oct 2004 17:38:24 -0400 +Message-ID: <19117.1098481104@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis Bjorklund writes: +> On Fri, 22 Oct 2004, Tom Lane wrote: +>> than having two different types (the idea of a GUC variable to choose +>> which one is selected by a given type name is just horrid). + +> That is needed no matter what change you do if you want old programs that +> use the current timestamp with time zone to work. Today you don't get back +> the same time zone as you insert, programs might depend on that. + +[ shrug... ] We've made much larger changes than that in the name of +standards compliance. In practice I think the majority of apps are +working in contexts where they will get back the same zone as they +inserted, if they inserted a zone explicitly at all, so the risk of +breakage is not that high. Having a GUC variable that changes the +semantics underneath you is *much* riskier, to judge by past experience. + +>> We are not fully there yet (can't do AT TIME ZONE conversions with all +>> zones yet, for instance) + +> Why is that? + +Because it's not done yet. There's a set of GMT-offset-only zone names +wired into the datetime code (look in the "datetime token table") and +those are what AT TIME ZONE knows how to deal with. We need to unify +that old stuff with the src/timezone code, but we ran out of time to do +it in 8.0. + +The way I see it, we have three sorts of zones to deal with: fixed +numeric offsets from UTC, names that represent fixed offsets (eg, "EST" +is the same as UTC-5), and names that represent DST-variable offsets +(eg, "EST5EDT"). For what are now entirely historical reasons, various +parts of the system cope with different subsets of these three types. +I want to get to a state where you can use any of them in any context +and it Just Works. (While we are at it, we need to make the set of +recognized zone names user-configurable; the australian_timezones kluge +satisfies our contributors Down Under, but there are a lot of unhappy +people still, because for instance IST means different things in Israel +and India.) + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-hackers-owner+M60269=pgman=candle.pha.pa.us@postgresql.org Fri Oct 22 17:51:14 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9MLpCf14192 + for ; Fri, 22 Oct 2004 17:51:13 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 43D6BEAE489 + for ; Fri, 22 Oct 2004 22:50:47 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 78632-05 for ; + Fri, 22 Oct 2004 21:50:55 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id EFC45EAE486 + for ; Fri, 22 Oct 2004 22:50:46 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 42518EAC955 + for ; Fri, 22 Oct 2004 22:48:36 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 77399-07 + for ; + Fri, 22 Oct 2004 21:48:35 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 8BB75EAE46B + for ; Fri, 22 Oct 2004 22:48:27 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9MLmm1l019192; + Fri, 22 Oct 2004 17:48:48 -0400 (EDT) +To: Dennis Bjorklund +cc: Robert Treat , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <19117.1098481104@sss.pgh.pa.us> +References: <19117.1098481104@sss.pgh.pa.us> +Comments: In-reply-to Tom Lane + message dated "Fri, 22 Oct 2004 17:38:24 -0400" +Date: Fri, 22 Oct 2004 17:48:48 -0400 +Message-ID: <19191.1098481728@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +>> That is needed no matter what change you do if you want old programs that +>> use the current timestamp with time zone to work. Today you don't get back +>> the same time zone as you insert, programs might depend on that. + +> [ shrug... ] We've made much larger changes than that in the name of +> standards compliance. + +BTW, even if you do want output like that, that doesn't make two +datatypes a good idea. It'd be better to add a couple of DateStyle-like +formatting options: + * rotate all timestamps into current TimeZone for display, or not; + * display the timezone numerically, or as originally given. + +A DateStyle kind of GUC variable is a lot less dangerous than what you +were proposing, because getting it wrong doesn't mean you have the wrong +data stored in the database ... + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 4: Don't 'kill -9' the postmaster + +From pgsql-hackers-owner+M60274=pgman=candle.pha.pa.us@postgresql.org Sat Oct 23 02:11:58 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9N6Buf20583 + for ; Sat, 23 Oct 2004 02:11:57 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id B3D75EAE4AC + for ; Sat, 23 Oct 2004 07:11:22 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 75750-09 for ; + Sat, 23 Oct 2004 06:11:32 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 560D3EADBC0 + for ; Sat, 23 Oct 2004 07:11:22 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 9D862EAE4CB + for ; Sat, 23 Oct 2004 07:08:48 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 77538-02 + for ; + Sat, 23 Oct 2004 06:08:54 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 29BC1EAC8F4 + for ; Sat, 23 Oct 2004 07:08:40 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 846788467; Sat, 23 Oct 2004 08:09:05 +0200 (CEST) +Date: Sat, 23 Oct 2004 08:09:05 +0200 (CEST) +From: Dennis Bjorklund +To: Tom Lane +cc: Robert Treat , + +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <18876.1098478892@sss.pgh.pa.us> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Fri, 22 Oct 2004, Tom Lane wrote: + +> behavior. The spec says you can put a numeric-GMT-offset zone in and +> get a numeric-GMT-offset zone out. We can do that and also support +> named, possibly DST-aware zones. + +So if I understand you correctly you are planning to extend the current +timestamp type to work with both named time zones and HH:MM ones? I didn't +think you wanted the last one since your plan was to store a UTC+OID where +the OID pointed to a named time zone. And I guess that you don't plan to +add 00:00, 00:01, 00:02, ... as named zones with an OID. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 9: the planner will ignore your desire to choose an index scan if your + joining column's datatypes do not match + +From pgsql-hackers-owner+M60329=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 12:45:39 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PGjcf25576 + for ; Mon, 25 Oct 2004 12:45:38 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 27F433A4146 + for ; Mon, 25 Oct 2004 17:45:35 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 63030-04 for ; + Mon, 25 Oct 2004 16:45:34 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id D15153A413C + for ; Mon, 25 Oct 2004 17:45:34 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 4D4653A415D + for ; Mon, 25 Oct 2004 17:43:32 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 60516-10 + for ; + Mon, 25 Oct 2004 16:43:26 +0000 (GMT) +Received: from davinci.ethosmedia.com (server226.ethosmedia.com [209.128.84.226]) + by svr1.postgresql.org (Postfix) with ESMTP id CAB1E3A4170 + for ; Mon, 25 Oct 2004 17:43:26 +0100 (BST) +Received: from [63.195.55.98] (account josh@agliodbs.com HELO spooky) + by davinci.ethosmedia.com (CommuniGate Pro SMTP 4.1.8) + with ESMTP id 6553366 for pgsql-hackers@postgresql.org; Mon, 25 Oct 2004 09:44:52 -0700 +From: Josh Berkus +Organization: Aglio Database Solutions +To: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Date: Mon, 25 Oct 2004 09:42:38 -0700 +User-Agent: KMail/1.6.2 +References: <20041022184636.62D39EAEE02@svr1.postgresql.org> +In-Reply-To: <20041022184636.62D39EAEE02@svr1.postgresql.org> +MIME-Version: 1.0 +Content-Disposition: inline +Content-Type: text/plain; + charset="utf-8" +Message-ID: <200410250942.38212.josh@agliodbs.com> +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Content-Transfer-Encoding: 8bit +X-MIME-Autoconverted: from quoted-printable to 8bit by candle.pha.pa.us id i9PGjcf25576 +X-Spam-Checker-Version: SpamAssassin 2.61 (1.212.2.1-2003-12-09-exp) on + candle.pha.pa.us +X-Spam-Status: No, hits=-4.9 required=5.0 tests=BAYES_00 autolearn=ham + version=2.61 +Status: OR + +Tom, + +> As far as I can tell, Dennis is planning slavish adherence to the spec, +> which will mean that the datatype is unable to cope effectively with +> daylight-savings issues.  So I'm unconvinced that it will be very +> helpful to you for remembering local time in addition to true +> (universal) time. + +As somebody who codes calendar apps, I have to say that I have yet to see an +implementation of time zones which is at all useful for this purpose, +including the current implementation. My calendar apps on PostgreSQL 7.4 +use "timestamp without time zone" and keep the time zone in a seperate field. + +The reason is simple: our current implementation, which does include DST, +does not include any provision for the exceptions to DST -- such as Arizona +-- or for the difference between "1 day" and "24 hours". (Try adding "30 +days" to "2004-10-05 10:00 PDT", you'll see what I mean). Nor do I see a +way out of this without raising the complexity, and configurability, level of +timezones significantly. + +So if we're going to be broken (at least from the perspective of calendar +applications) we might as well be broken in a spec-compliant way. + +-- +Josh Berkus +Aglio Database Solutions +San Francisco + +---------------------------(end of broadcast)--------------------------- +TIP 6: Have you searched our list archives? + + http://archives.postgresql.org + +From pgsql-hackers-owner+M60334=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 13:56:27 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PHuQf06230 + for ; Mon, 25 Oct 2004 13:56:26 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 024493A3C12 + for ; Mon, 25 Oct 2004 18:56:23 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 90234-01 for ; + Mon, 25 Oct 2004 17:56:21 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id B12643A3BDD + for ; Mon, 25 Oct 2004 18:56:22 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 038CC3A412E + for ; Mon, 25 Oct 2004 18:54:57 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 89206-06 + for ; + Mon, 25 Oct 2004 17:54:54 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 0D5D13A3B06 + for ; Mon, 25 Oct 2004 18:54:54 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9PHsrvv021835; + Mon, 25 Oct 2004 13:54:53 -0400 (EDT) +To: Josh Berkus +cc: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410250942.38212.josh@agliodbs.com> +References: <20041022184636.62D39EAEE02@svr1.postgresql.org> <200410250942.38212.josh@agliodbs.com> +Comments: In-reply-to Josh Berkus + message dated "Mon, 25 Oct 2004 09:42:38 -0700" +Date: Mon, 25 Oct 2004 13:54:53 -0400 +Message-ID: <21834.1098726893@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Josh Berkus writes: +> The reason is simple: our current implementation, which does include DST, +> does not include any provision for the exceptions to DST -- such as Arizona + +Say what? + +regression=# set timezone to 'MST7MDT'; +SET +regression=# select now(); + now +------------------------------- + 2004-10-25 11:52:47.093538-06 +(1 row) + +regression=# set timezone to 'US/Arizona'; +SET +regression=# select now(); + now +------------------------------- + 2004-10-25 10:52:49.441559-07 +(1 row) + +> -- or for the difference between "1 day" and "24 hours". (Try adding "30 +> days" to "2004-10-05 10:00 PDT", you'll see what I mean). + +This is the point about how interval needs to treat "day" as different +from "24 hours". I agree with that; the fact that it's not done already +is just a reflection of limited supply of round tuits. I think it's +orthogonal to the question of how flexible timestamp with time zone +needs to be, though. + +> Nor do I see a way out of this without raising the complexity, and +> configurability, level of timezones significantly. + +This does not seem to me to be an argument why timestamp with time zone +ought to be incapable of dealing with DST-aware time zones. That simply +guarantees that calendar apps won't be able to use the datatype. If +they still can't use it when it can do that, then we can look at the +next blocking factor. + +> So if we're going to be broken (at least from the perspective of calendar +> applications) we might as well be broken in a spec-compliant way. + +I have not said that we can't comply with the spec. I have said that +our ambitions need to be higher than merely complying with the spec. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60335=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 14:08:52 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PI8of08458 + for ; Mon, 25 Oct 2004 14:08:51 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id EB0003A4181 + for ; Mon, 25 Oct 2004 19:08:47 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 94404-01 for ; + Mon, 25 Oct 2004 18:08:46 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 984283A417A + for ; Mon, 25 Oct 2004 19:08:47 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 058F13A3B6E + for ; Mon, 25 Oct 2004 19:06:53 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 91573-10 + for ; + Mon, 25 Oct 2004 18:06:42 +0000 (GMT) +Received: from davinci.ethosmedia.com (server226.ethosmedia.com [209.128.84.226]) + by svr1.postgresql.org (Postfix) with ESMTP id B6FB73A3AC2 + for ; Mon, 25 Oct 2004 19:06:43 +0100 (BST) +Received: from [64.81.245.111] (account josh@agliodbs.com HELO temoku.sf.agliodbs.com) + by davinci.ethosmedia.com (CommuniGate Pro SMTP 4.1.8) + with ESMTP id 6553760; Mon, 25 Oct 2004 11:08:08 -0700 +From: Josh Berkus +Reply-To: josh@agliodbs.com +Organization: Aglio Database Solutions +To: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Date: Mon, 25 Oct 2004 11:08:52 -0700 +User-Agent: KMail/1.6.2 +cc: Tom Lane +References: <20041022184636.62D39EAEE02@svr1.postgresql.org> <200410250942.38212.josh@agliodbs.com> <21834.1098726893@sss.pgh.pa.us> +In-Reply-To: <21834.1098726893@sss.pgh.pa.us> +MIME-Version: 1.0 +Content-Disposition: inline +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: 7bit +Message-ID: <200410251108.52164.josh@agliodbs.com> +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Tom, + +> regression=# set timezone to 'US/Arizona'; +> SET +> regression=# select now(); +> now +> ------------------------------- +> 2004-10-25 10:52:49.441559-07 + +Wow! When did that get fixed? How do I keep track of this stuff if you +guys keep fixing it? ;-) + +Of course, it would be very helpful if the result above could display +"Arizona" instead of the non-specific "-07", but I'm pretty sure that's +already a TODO. + +> This is the point about how interval needs to treat "day" as different +> from "24 hours". I agree with that; the fact that it's not done already +> is just a reflection of limited supply of round tuits. + +Well, when I first brought up the issue (2001) I was shot down on the basis of +spec-compliance, since SQL92 recognizes only Year/Month and +Day/Hour/Minute/etc. partitions. Glad it's up for consideration again. + +Come to think of it, it was Thomas Lockhart who shot down the idea of fixing +Interval, and he's retired now ... + +> This does not seem to me to be an argument why timestamp with time zone +> ought to be incapable of dealing with DST-aware time zones. That simply +> guarantees that calendar apps won't be able to use the datatype. If +> they still can't use it when it can do that, then we can look at the +> next blocking factor. + +That's definitely a progressive attitude .... pardon me for being pessimistic. + +> I have not said that we can't comply with the spec. I have said that +> our ambitions need to be higher than merely complying with the spec. + +Hmmm ... well, does the spec specifically prohibit DST, or just leave it out? + +-- +--Josh + +Josh Berkus +Aglio Database Solutions +San Francisco + +---------------------------(end of broadcast)--------------------------- +TIP 7: don't forget to increase your free space map settings + +From pgsql-hackers-owner+M60336=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 14:21:12 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PILAf10029 + for ; Mon, 25 Oct 2004 14:21:11 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 96C283A3B0E + for ; Mon, 25 Oct 2004 19:21:07 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 96954-06 for ; + Mon, 25 Oct 2004 18:21:06 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 528B73A3B06 + for ; Mon, 25 Oct 2004 19:21:07 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id E010F3A4192 + for ; Mon, 25 Oct 2004 19:19:46 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 96137-04 + for ; + Mon, 25 Oct 2004 18:19:41 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id A1BDA3A4190 + for ; Mon, 25 Oct 2004 19:19:42 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9PIJegg022094; + Mon, 25 Oct 2004 14:19:40 -0400 (EDT) +To: josh@agliodbs.com +cc: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251108.52164.josh@agliodbs.com> +References: <20041022184636.62D39EAEE02@svr1.postgresql.org> <200410250942.38212.josh@agliodbs.com> <21834.1098726893@sss.pgh.pa.us> <200410251108.52164.josh@agliodbs.com> +Comments: In-reply-to Josh Berkus + message dated "Mon, 25 Oct 2004 11:08:52 -0700" +Date: Mon, 25 Oct 2004 14:19:40 -0400 +Message-ID: <22093.1098728380@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Josh Berkus writes: +>> regression=# set timezone to 'US/Arizona'; +>> SET +>> regression=# select now(); +>> now +>> ------------------------------- +>> 2004-10-25 10:52:49.441559-07 + +> Wow! When did that get fixed? How do I keep track of this stuff if you +> guys keep fixing it? ;-) + +> Of course, it would be very helpful if the result above could display +> "Arizona" instead of the non-specific "-07", but I'm pretty sure that's +> already a TODO. + +Well, that is *exactly what I'm talking about*. I want timestamp with +time zone to carry "US/Arizona" not just "-07". Obviously there needs +to be some option to get the latter displayed when that's all you want, +but internally a value of the datatype needs to be able to carry full +knowledge of which timezone it's supposed to be in. Dumbing that down +to a simple numeric GMT offset isn't good enough. + +>> I have not said that we can't comply with the spec. I have said that +>> our ambitions need to be higher than merely complying with the spec. + +> Hmmm ... well, does the spec specifically prohibit DST, or just leave it out? + +It just doesn't talk about it AFAICS. + +To comply with the spec we definitely need to be *able* to support +timezone values that are simple numeric GMT offsets. But I think we +ought also to be able to store values that are references to any of +the zic database entries. This looks to me like a straightforward +extension of the spec. + +We went to all the trouble of importing src/timezone in order that we +could make a significant upgrade in our timezone capability, and now +it's time to take the steps that that enables. Before we were limited +to the lowest-common-denominator of the libc timezone routines on all +our different platforms, but now we are not... + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 6: Have you searched our list archives? + + http://archives.postgresql.org + +From pgsql-hackers-owner+M60337=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 14:28:59 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PISvf11118 + for ; Mon, 25 Oct 2004 14:28:58 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 928B13A3B43 + for ; Mon, 25 Oct 2004 19:28:54 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 00197-04 for ; + Mon, 25 Oct 2004 18:28:53 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 518C33A3B06 + for ; Mon, 25 Oct 2004 19:28:54 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 3AA743A3B0E + for ; Mon, 25 Oct 2004 19:27:17 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 99849-02 + for ; + Mon, 25 Oct 2004 18:27:07 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id B62843A418A + for ; Mon, 25 Oct 2004 19:27:01 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 5AD8C8467; Mon, 25 Oct 2004 20:26:59 +0200 (CEST) +Date: Mon, 25 Oct 2004 20:26:59 +0200 (CEST) +From: Dennis Bjorklund +To: Josh Berkus +cc: pgsql-hackers@postgresql.org, Tom Lane +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251108.52164.josh@agliodbs.com> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, 25 Oct 2004, Josh Berkus wrote: + +> Hmmm ... well, does the spec specifically prohibit DST, or just leave it +> out? + +It doesn't discuss it. According to the spec a timestamp with time zone is +a UTC value + a HH:MM offset from GMT. And intervals in the spec is either +a year-month value or a day-time value. One can only compare year-month +values with each other and day-time values with each other. So they avoid +the problem of the how many days is a month by not allowing it. + +The spec is not a full solution, it's also not a useless solution. I'm +happy as long as the spec is a subset of what pg implements. If not then I +would like to be able to have both but with different names or something +similar (but I think that should not be needed). + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60338=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 14:54:31 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PIsUf15052 + for ; Mon, 25 Oct 2004 14:54:30 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 3F9063A41BC + for ; Mon, 25 Oct 2004 19:54:27 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 09766-01 for ; + Mon, 25 Oct 2004 18:54:25 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id E25B63A41BA + for ; Mon, 25 Oct 2004 19:54:26 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 8FBF23A41A0 + for ; Mon, 25 Oct 2004 19:52:27 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 05475-10 + for ; + Mon, 25 Oct 2004 18:52:24 +0000 (GMT) +Received: from davinci.ethosmedia.com (server226.ethosmedia.com [209.128.84.226]) + by svr1.postgresql.org (Postfix) with ESMTP id E7C333A4182 + for ; Mon, 25 Oct 2004 19:52:24 +0100 (BST) +Received: from [64.81.245.111] (account josh@agliodbs.com HELO temoku.sf.agliodbs.com) + by davinci.ethosmedia.com (CommuniGate Pro SMTP 4.1.8) + with ESMTP id 6553982; Mon, 25 Oct 2004 11:53:49 -0700 +From: Josh Berkus +Reply-To: josh@agliodbs.com +Organization: Aglio Database Solutions +To: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Date: Mon, 25 Oct 2004 11:54:33 -0700 +User-Agent: KMail/1.6.2 +cc: Dennis Bjorklund , Tom Lane +References: +In-Reply-To: +MIME-Version: 1.0 +Content-Disposition: inline +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: 7bit +Message-ID: <200410251154.33532.josh@agliodbs.com> +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis, + +> It doesn't discuss it. According to the spec a timestamp with time zone is +> a UTC value + a HH:MM offset from GMT. And intervals in the spec is either +> a year-month value or a day-time value. One can only compare year-month +> values with each other and day-time values with each other. So they avoid +> the problem of the how many days is a month by not allowing it. + +That's not what Tom and I were talking about. The issue is that the spec +defines Days/Weeks as being an agglomeration of hours and not an atomic +entity like Months/Years are. This leads to some wierd and +calendar-breaking behavior when combined with DST, for example: + +template1=> select '2004-10-09 10:00 PDT'::TIMESTAMPTZ + '45 days'::INTERVAL +template1-> ; + ?column? +------------------------ + 2004-11-23 09:00:00-08 +(1 row) + +Because of the DST shift, you get an hour shift which is most decidely not +anything real human beings would expect from a calendar. The answer is to +try-partition INTERVAL values, as: + +Hour/Minute/Second/ms +Day/Week +Month/Year + +However, this could be considered to break the spec; certainly Thomas thought +it did. My defense is that the SQL committee made some mistakes, and +interval is a big one. + +-- +--Josh + +Josh Berkus +Aglio Database Solutions +San Francisco + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-hackers-owner+M60339=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 15:07:28 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PJ7Qf17120 + for ; Mon, 25 Oct 2004 15:07:27 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 666B73A41B0 + for ; Mon, 25 Oct 2004 20:07:23 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 13808-02 for ; + Mon, 25 Oct 2004 19:07:21 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 207F03A41AE + for ; Mon, 25 Oct 2004 20:07:23 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id D243F3A4182 + for ; Mon, 25 Oct 2004 20:04:23 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 12122-06 + for ; + Mon, 25 Oct 2004 19:04:18 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 52FFB3A3CF2 + for ; Mon, 25 Oct 2004 20:04:18 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9PJ4GUm022503; + Mon, 25 Oct 2004 15:04:16 -0400 (EDT) +To: josh@agliodbs.com +cc: pgsql-hackers@postgresql.org, Dennis Bjorklund +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251154.33532.josh@agliodbs.com> +References: <200410251154.33532.josh@agliodbs.com> +Comments: In-reply-to Josh Berkus + message dated "Mon, 25 Oct 2004 11:54:33 -0700" +Date: Mon, 25 Oct 2004 15:04:16 -0400 +Message-ID: <22502.1098731056@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Josh Berkus writes: +>> It doesn't discuss it. According to the spec a timestamp with time zone is +>> a UTC value + a HH:MM offset from GMT. And intervals in the spec is either +>> a year-month value or a day-time value. One can only compare year-month +>> values with each other and day-time values with each other. So they avoid +>> the problem of the how many days is a month by not allowing it. + +> That's not what Tom and I were talking about. The issue is that the spec +> defines Days/Weeks as being an agglomeration of hours and not an atomic +> entity like Months/Years are. + +I think though that these points are closely related. The reason the +spec does that is exactly that they are ignoring DST and so they can +assume that 1 day == 24 hours == 86400 seconds. In a DST-aware world +you have to make a separation between days and the smaller units, just +as months are separated from smaller units because there's not a fixed +conversion factor. + +To some extent the interval and timestamptz issues are orthogonal, but +I think it would be good to fix them in the same release if possible. +There will undoubtedly be some backwards-compatibility problems, and +I suppose that users would prefer to take them all at once than via +the chinese water torture method ... + +> However, this could be considered to break the spec; certainly Thomas +> thought it did. My defense is that the SQL committee made some +> mistakes, and interval is a big one. + +I'm not clear to what extent we have to actually break the spec, as +opposed to extend it, in order to do this to the "interval" type. To do +everything the spec says we need to do, we'll have to be able to make +some comparisons that aren't strictly valid (which amounts to assuming +that 1 day == 24 hours for some limited purposes) but we already do much +the same things with respect to months. (See other thread about whether +1 year == 360 days...) + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 5: Have you checked our extensive FAQ? + + http://www.postgresql.org/docs/faqs/FAQ.html + +From pgsql-hackers-owner+M60340=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 15:12:25 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PJCNf17962 + for ; Mon, 25 Oct 2004 15:12:24 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id AA6B73A41C2 + for ; Mon, 25 Oct 2004 20:12:20 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 13059-08 for ; + Mon, 25 Oct 2004 19:12:19 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 50BAF3A41C1 + for ; Mon, 25 Oct 2004 20:12:20 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 2035E3A41B9 + for ; Mon, 25 Oct 2004 20:11:08 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 13808-08 + for ; + Mon, 25 Oct 2004 19:11:04 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 5D4C03A41B2 + for ; Mon, 25 Oct 2004 20:11:04 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 156808467; Mon, 25 Oct 2004 21:11:04 +0200 (CEST) +Date: Mon, 25 Oct 2004 21:11:04 +0200 (CEST) +From: Dennis Bjorklund +To: Josh Berkus +cc: pgsql-hackers@postgresql.org, Tom Lane +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251154.33532.josh@agliodbs.com> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, 25 Oct 2004, Josh Berkus wrote: + +> Dennis, +> +> > It doesn't discuss it. According to the spec a timestamp with time zone is +> > a UTC value + a HH:MM offset from GMT. And intervals in the spec is either +> > a year-month value or a day-time value. One can only compare year-month +> > values with each other and day-time values with each other. So they avoid +> > the problem of the how many days is a month by not allowing it. +> +> That's not what Tom and I were talking about. + +You wanted to know what the standard said, and I told what I knew. + +> The issue is that the spec defines Days/Weeks as being an agglomeration +> of hours and not an atomic entity like Months/Years are. + +I don't know what you mean with this. The standard does treat them as + +year +month +day +hour +minute +second (with fractions) + +There is no weeks there, if that is what you mean. + +> This leads to some wierd and calendar-breaking behavior when combined +> with DST, for example: +> +> template1=> select '2004-10-09 10:00 PDT'::TIMESTAMPTZ + '45 days'::INTERVAL +> template1-> ; +> ?column? +> ------------------------ +> 2004-11-23 09:00:00-08 +> (1 row) +> +> Because of the DST shift, you get an hour shift which is most decidely not +> anything real human beings would expect from a calendar. + +I don't see how the above can be caused by the representation of an +interval. The above timestamp is + +2004-10-09 10:00 PDT + +which in the standard would be + +2004-10-09 10:00 -07 + +and after the additon would be + +2004-11-23 10:00:00-07 + +Here the time zone is wrong since the standard does not know about named +zones and dst. + +An implementation like the one Tom (and I) want would start with + +2004-10-09 10:00 PDT + +and then after the addition one would get + +2004-11-23 10:00:00 PST + +At least that's my understanding of what we want and what we can get (plus +that we also need to support HH:MM tz values since those also exist in the +world, check this emails header for example). + +It's possible that you discuss something else, but that has been lost on +me so far. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60341=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 15:15:18 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PJFHf18332 + for ; Mon, 25 Oct 2004 15:15:17 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id D2DE53A41BD + for ; Mon, 25 Oct 2004 20:15:13 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 16799-02 for ; + Mon, 25 Oct 2004 19:15:12 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 928BF3A41A6 + for ; Mon, 25 Oct 2004 20:15:13 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 2AE4F3A41C4 + for ; Mon, 25 Oct 2004 20:13:02 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 15871-02 + for ; + Mon, 25 Oct 2004 19:12:59 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 6881F3A41BC + for ; Mon, 25 Oct 2004 20:12:59 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 09CC48467; Mon, 25 Oct 2004 21:12:59 +0200 (CEST) +Date: Mon, 25 Oct 2004 21:12:59 +0200 (CEST) +From: Dennis Bjorklund +To: Josh Berkus +cc: pgsql-hackers@postgresql.org, Tom Lane +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251154.33532.josh@agliodbs.com> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, 25 Oct 2004, Josh Berkus wrote: + +> Hour/Minute/Second/ms +> Day/Week +> Month/Year + +And just when I pressed "send" on the previous mail I got the problem +:-) + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 2: you can get off all lists at once with the unregister command + (send "unregister YourEmailAddressHere" to majordomo@postgresql.org) + +From pgsql-hackers-owner+M60342=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 15:20:32 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PJKVf19055 + for ; Mon, 25 Oct 2004 15:20:32 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 0D6103A418E + for ; Mon, 25 Oct 2004 20:20:28 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 17860-02 for ; + Mon, 25 Oct 2004 19:20:26 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id AA4353A41B2 + for ; Mon, 25 Oct 2004 20:20:27 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 131A23A41BB + for ; Mon, 25 Oct 2004 20:19:02 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 16799-06 + for ; + Mon, 25 Oct 2004 19:18:51 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id BBD663A3B06 + for ; Mon, 25 Oct 2004 20:18:52 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 36B248467; Mon, 25 Oct 2004 21:18:52 +0200 (CEST) +Date: Mon, 25 Oct 2004 21:18:52 +0200 (CEST) +From: Dennis Bjorklund +To: Josh Berkus +cc: pgsql-hackers@postgresql.org, Tom Lane +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251154.33532.josh@agliodbs.com> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, 25 Oct 2004, Josh Berkus wrote: + +> Hour/Minute/Second/ms +> Day/Week +> Month/Year + +This is embarrasing. I'm still a bit confused :-) + +The standard treat days as a separate entry, it does not assume that a day +is 24 hours. It restricts the hour field to the interval 0-23 so one can +never have something like 25 hours. So it does not need to worry about how +many days that translate to. + +And why do we need weeks also? + +Well, this is the last mail I send before I've been thinking about this +for a while more :-) + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 6: Have you searched our list archives? + + http://archives.postgresql.org + +From pgsql-hackers-owner+M60344=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 15:36:30 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PJaTf21902 + for ; Mon, 25 Oct 2004 15:36:29 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id E2F703A3CF2 + for ; Mon, 25 Oct 2004 20:36:25 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 23154-01 for ; + Mon, 25 Oct 2004 19:36:24 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 7935E3A3AC2 + for ; Mon, 25 Oct 2004 20:36:25 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 13AB03A41BC + for ; Mon, 25 Oct 2004 20:35:09 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 21223-08 + for ; + Mon, 25 Oct 2004 19:35:05 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id EA1E73A41B3 + for ; Mon, 25 Oct 2004 20:35:06 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9PJZ429022817; + Mon, 25 Oct 2004 15:35:04 -0400 (EDT) +To: Dennis Bjorklund +cc: Josh Berkus , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: +References: +Comments: In-reply-to Dennis Bjorklund + message dated "Mon, 25 Oct 2004 21:18:52 +0200" +Date: Mon, 25 Oct 2004 15:35:04 -0400 +Message-ID: <22816.1098732904@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis Bjorklund writes: +> The standard treat days as a separate entry, it does not assume that a day +> is 24 hours. + +SQL92 says + + 4.5.2 Intervals + + There are two classes of intervals. One class, called year-month + intervals, has an express or implied datetime precision that in- + cludes no fields other than YEAR and MONTH, though not both are + required. The other class, called day-time intervals, has an ex- + press or implied interval precision that can include any fields + other than YEAR or MONTH. + +AFAICS the reason for this rule is that they expect all Y/M intervals to +be comparable (which they are) and they also expect all D/H/M/S intervals +to be comparable, which you can only do by assuming that 1 D == 24 H. + +It seems to me though that we can store days separately and do interval +comparisons with the assumption 1 D == 24 H, and be perfectly +SQL-compatible as far as that goes, and still make good use of the +separate day info when adding to a timestamptz that has a DST-aware +timezone. In a non-DST-aware timezone the addition will act the same as +if we weren't distinguishing days from h/m/s. Therefore, an application +using only the spec-defined features (ie, only fixed-numeric-offset +timezones) will see no deviation from the spec behavior. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 4: Don't 'kill -9' the postmaster + +From pgsql-hackers-owner+M60343=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 15:31:38 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PJVZf21237 + for ; Mon, 25 Oct 2004 15:31:36 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 139FF3A41C0 + for ; Mon, 25 Oct 2004 20:31:32 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 21073-05 for ; + Mon, 25 Oct 2004 19:31:30 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 9E1723A41B8 + for ; Mon, 25 Oct 2004 20:31:31 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id C7A9E3A41A0 + for ; Mon, 25 Oct 2004 20:30:15 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 19517-04 + for ; + Mon, 25 Oct 2004 19:30:05 +0000 (GMT) +Received: from wolff.to (wolff.to [66.93.249.74]) + by svr1.postgresql.org (Postfix) with SMTP id 9A0483A4182 + for ; Mon, 25 Oct 2004 20:30:05 +0100 (BST) +Received: (qmail 26726 invoked by uid 500); 25 Oct 2004 19:40:57 -0000 +Date: Mon, 25 Oct 2004 14:40:57 -0500 +From: Bruno Wolff III +To: Dennis Bjorklund +cc: Josh Berkus , pgsql-hackers@postgresql.org, + Tom Lane +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Message-ID: <20041025194057.GA26356@wolff.to> +Mail-Followup-To: Dennis Bjorklund , + Josh Berkus , pgsql-hackers@postgresql.org, + Tom Lane +References: <200410251154.33532.josh@agliodbs.com> +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline +In-Reply-To: +User-Agent: Mutt/1.5.6i +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, Oct 25, 2004 at 21:18:52 +0200, + Dennis Bjorklund wrote: +> On Mon, 25 Oct 2004, Josh Berkus wrote: +> +> > Hour/Minute/Second/ms +> > Day/Week +> > Month/Year +> +> This is embarrasing. I'm still a bit confused :-) +> +> The standard treat days as a separate entry, it does not assume that a day +> is 24 hours. It restricts the hour field to the interval 0-23 so one can +> never have something like 25 hours. So it does not need to worry about how +> many days that translate to. +> +> And why do we need weeks also? + +For convenience. Just like years are a group of months, weeks are a group +of days. + +---------------------------(end of broadcast)--------------------------- +TIP 4: Don't 'kill -9' the postmaster + +From pgsql-hackers-owner+M60346=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 16:10:09 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PKA8f26656 + for ; Mon, 25 Oct 2004 16:10:08 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 07A343A4201 + for ; Mon, 25 Oct 2004 21:10:05 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 62669-07 for ; + Mon, 25 Oct 2004 20:10:03 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id ACA1C3A4200 + for ; Mon, 25 Oct 2004 21:10:04 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 251663A41FE + for ; Mon, 25 Oct 2004 21:08:18 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 62513-04 + for ; + Mon, 25 Oct 2004 20:08:15 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id 3C94D3A3AC2 + for ; Mon, 25 Oct 2004 21:08:14 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 025568467; Mon, 25 Oct 2004 22:08:14 +0200 (CEST) +Date: Mon, 25 Oct 2004 22:08:14 +0200 (CEST) +From: Dennis Bjorklund +To: Tom Lane +cc: Josh Berkus , +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <22816.1098732904@sss.pgh.pa.us> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, 25 Oct 2004, Tom Lane wrote: + +> There are two classes of intervals. One class, called year-month +> intervals, has an express or implied datetime precision that in- +> cludes no fields other than YEAR and MONTH, though not both are +> required. The other class, called day-time intervals, has an ex- +> press or implied interval precision that can include any fields +> other than YEAR or MONTH. +> +> AFAICS the reason for this rule is that they expect all Y/M intervals to +> be comparable (which they are) and they also expect all D/H/M/S intervals +> to be comparable, which you can only do by assuming that 1 D == 24 H. + +I said I was not going to send any more mails, but here we go again :-) + +The standard restrict the hour field to the interval 0-23, so there can +never be any compare between for example '1 day 1 hour' and '25 hours'. +This means that one can not add two intervals together to get a bigger +one but that it would still work to do timestamp+interval+interval. + +> It seems to me though that we can store days separately and do interval +> comparisons with the assumption 1 D == 24 H, and be perfectly +> SQL-compatible as far as that goes, and still make good use of the +> separate day info when adding to a timestamptz that has a DST-aware +> timezone. In a non-DST-aware timezone the addition will act the same as +> if we weren't distinguishing days from h/m/s. Therefore, an application +> using only the spec-defined features (ie, only fixed-numeric-offset +> timezones) will see no deviation from the spec behavior. + +I agree with this. + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 7: don't forget to increase your free space map settings + +From pgsql-hackers-owner+M60347=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 16:20:45 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PKKif28309 + for ; Mon, 25 Oct 2004 16:20:44 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 65B453A4215 + for ; Mon, 25 Oct 2004 21:20:40 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 66092-07 for ; + Mon, 25 Oct 2004 20:20:38 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 1DCD53A4210 + for ; Mon, 25 Oct 2004 21:20:40 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 803933A3AC2 + for ; Mon, 25 Oct 2004 21:18:00 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 64513-06 + for ; + Mon, 25 Oct 2004 20:17:49 +0000 (GMT) +Received: from davinci.ethosmedia.com (server226.ethosmedia.com [209.128.84.226]) + by svr1.postgresql.org (Postfix) with ESMTP id 726633A3CF2 + for ; Mon, 25 Oct 2004 21:17:51 +0100 (BST) +Received: from [64.81.245.111] (account josh@agliodbs.com HELO temoku.sf.agliodbs.com) + by davinci.ethosmedia.com (CommuniGate Pro SMTP 4.1.8) + with ESMTP id 6554308; Mon, 25 Oct 2004 13:19:17 -0700 +From: Josh Berkus +Reply-To: josh@agliodbs.com +Organization: Aglio Database Solutions +To: pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +Date: Mon, 25 Oct 2004 13:20:01 -0700 +User-Agent: KMail/1.6.2 +cc: Dennis Bjorklund , Tom Lane +References: +In-Reply-To: +MIME-Version: 1.0 +Content-Disposition: inline +Content-Type: text/plain; + charset="iso-8859-1" +Content-Transfer-Encoding: 7bit +Message-ID: <200410251320.01311.josh@agliodbs.com> +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis, + +> An implementation like the one Tom (and I) want would start with +> +> 2004-10-09 10:00 PDT +> +> and then after the addition one would get +> +> 2004-11-23 10:00:00 PST + +Sounds like we're on the same page then. + +> The standard restrict the hour field to the interval 0-23, so there can +> never be any compare between for example '1 day 1 hour' and '25 hours'. +> This means that one can not add two intervals together to get a bigger +> one but that it would still work to do timestamp+interval+interval. + +Hour field of the timestamp, or hour field of interval? There a world of +difference. + +As long as we're willing to live with the understanding that +1day 1 hour may +produce a slightly different result than + 25 hours, I don't see the problem. +Currently I can add +900 hours if I like, postgreSQL will support it. + +-- +--Josh + +Josh Berkus +Aglio Database Solutions +San Francisco + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60348=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 16:24:27 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PKOQf28960 + for ; Mon, 25 Oct 2004 16:24:26 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 2AD843A419B + for ; Mon, 25 Oct 2004 21:24:22 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 68942-02 for ; + Mon, 25 Oct 2004 20:24:20 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id E08EE3A4182 + for ; Mon, 25 Oct 2004 21:24:21 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id C8C413A419B + for ; Mon, 25 Oct 2004 21:22:31 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 68322-02 + for ; + Mon, 25 Oct 2004 20:22:28 +0000 (GMT) +Received: from zigo.dhs.org (as2-4-3.an.g.bonet.se [194.236.34.191]) + by svr1.postgresql.org (Postfix) with ESMTP id BB2F53A41EC + for ; Mon, 25 Oct 2004 21:22:29 +0100 (BST) +Received: from zigo.zigo.dhs.org (zigo.zigo.dhs.org [192.168.0.1]) + by zigo.dhs.org (Postfix) with ESMTP + id 2B7368467; Mon, 25 Oct 2004 22:22:29 +0200 (CEST) +Date: Mon, 25 Oct 2004 22:22:29 +0200 (CEST) +From: Dennis Bjorklund +To: Josh Berkus +cc: pgsql-hackers@postgresql.org, Tom Lane +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251320.01311.josh@agliodbs.com> +Message-ID: +MIME-Version: 1.0 +Content-Type: TEXT/PLAIN; charset=ISO-8859-1 +Content-Transfer-Encoding: 8BIT +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +On Mon, 25 Oct 2004, Josh Berkus wrote: + +> > The standard restrict the hour field to the interval 0-23, so there can +> > never be any compare between for example '1 day 1 hour' and '25 hours'. +> > This means that one can not add two intervals together to get a bigger +> > one but that it would still work to do timestamp+interval+interval. +> +> Hour field of the timestamp, or hour field of interval? There a world of +> difference. + +Hour field of an interval can be 0-23 according to the spec (doesn't say +that we need that restriction, but we do need to understand what the spec +say). + +-- +/Dennis Bjrklund + + +---------------------------(end of broadcast)--------------------------- +TIP 3: if posting/reading through Usenet, please send an appropriate + subscribe-nomail command to majordomo@postgresql.org so that your + message can get through to the mailing list cleanly + +From pgsql-hackers-owner+M60349=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 16:34:48 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9PKYlf00828 + for ; Mon, 25 Oct 2004 16:34:47 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id D7AD63A3BF2 + for ; Mon, 25 Oct 2004 21:34:43 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 72374-02 for ; + Mon, 25 Oct 2004 20:34:41 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 99E183A3B88 + for ; Mon, 25 Oct 2004 21:34:43 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 534763A3C84 + for ; Mon, 25 Oct 2004 21:32:42 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 71412-04 + for ; + Mon, 25 Oct 2004 20:32:36 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 798CA3A3B88 + for ; Mon, 25 Oct 2004 21:32:38 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9PKWbDK023330; + Mon, 25 Oct 2004 16:32:37 -0400 (EDT) +To: josh@agliodbs.com +cc: pgsql-hackers@postgresql.org, Dennis Bjorklund +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <200410251320.01311.josh@agliodbs.com> +References: <200410251320.01311.josh@agliodbs.com> +Comments: In-reply-to Josh Berkus + message dated "Mon, 25 Oct 2004 13:20:01 -0700" +Date: Mon, 25 Oct 2004 16:32:37 -0400 +Message-ID: <23329.1098736357@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Josh Berkus writes: +> As long as we're willing to live with the understanding that +1day 1 hour may +> produce a slightly different result than + 25 hours, I don't see the problem. + +Right, which is exactly why we can't accept the spec's restriction that +the hour field be limited to 0-23. People may legitimately want to add +48 hours to a timestamp, and *not* have that mean the same as adding +"2 days". Besides, we would have a backwards-compatibility problem if +we tried to forbid it, since as you note we've always accepted such input. + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 5: Have you checked our extensive FAQ? + + http://www.postgresql.org/docs/faqs/FAQ.html + +From pgsql-hackers-owner+M60371=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 21:26:49 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9Q1Qmf13334 + for ; Mon, 25 Oct 2004 21:26:49 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id A8E2F3A42A1 + for ; Tue, 26 Oct 2004 02:26:43 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 52441-04 for ; + Tue, 26 Oct 2004 01:26:39 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 5F7503A42A0 + for ; Tue, 26 Oct 2004 02:26:43 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 02D5D3A427F + for ; Tue, 26 Oct 2004 02:25:27 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 52284-03 + for ; + Tue, 26 Oct 2004 01:25:22 +0000 (GMT) +Received: from houston.familyhealth.com.au (houston.au.fhnetwork.com [203.22.197.21]) + by svr1.postgresql.org (Postfix) with ESMTP id 5C0723A42A0 + for ; Tue, 26 Oct 2004 02:25:25 +0100 (BST) +Received: from houston.familyhealth.com.au (localhost [127.0.0.1]) + by houston.familyhealth.com.au (Postfix) with ESMTP id 1BDCB2506C; + Tue, 26 Oct 2004 09:25:25 +0800 (WST) +Received: from [192.168.0.40] (work-40.internal [192.168.0.40]) + by houston.familyhealth.com.au (Postfix) with ESMTP id 14E452506B; + Tue, 26 Oct 2004 09:25:25 +0800 (WST) +Message-ID: <417DA785.7010208@familyhealth.com.au> +Date: Tue, 26 Oct 2004 09:25:25 +0800 +From: Christopher Kings-Lynne +User-Agent: Mozilla Thunderbird 0.8 (Windows/20040913) +X-Accept-Language: en-us, en +MIME-Version: 1.0 +To: Tom Lane +cc: josh@agliodbs.com, pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +References: <20041022184636.62D39EAEE02@svr1.postgresql.org> <200410250942.38212.josh@agliodbs.com> <21834.1098726893@sss.pgh.pa.us> <200410251108.52164.josh@agliodbs.com> <22093.1098728380@sss.pgh.pa.us> +In-Reply-To: <22093.1098728380@sss.pgh.pa.us> +Content-Type: text/plain; charset=ISO-8859-1; format=flowed +Content-Transfer-Encoding: 7bit +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +>>>regression=# set timezone to 'US/Arizona'; +>>>SET +>>>regression=# select now(); +>>>now +>>>------------------------------- +>>>2004-10-25 10:52:49.441559-07 +> +> +>>Wow! When did that get fixed? How do I keep track of this stuff if you +>>guys keep fixing it? ;-) + +That's worked for ages. What doesn't work is this: + +usatest=# select current_timestamp at time zone 'US/Arizona'; +ERROR: time zone "us/arizona" not recognized + +Chris + +---------------------------(end of broadcast)--------------------------- +TIP 4: Don't 'kill -9' the postmaster + +From pgsql-hackers-owner+M60372=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 21:41:27 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9Q1fPf15148 + for ; Mon, 25 Oct 2004 21:41:26 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id A941F3A4278 + for ; Tue, 26 Oct 2004 02:41:20 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 54196-09 for ; + Tue, 26 Oct 2004 01:41:16 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id 50EEA3A4255 + for ; Tue, 26 Oct 2004 02:41:20 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 34AC13A4288 + for ; Tue, 26 Oct 2004 02:39:55 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 54779-07 + for ; + Tue, 26 Oct 2004 01:39:42 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 3E9493A1D91 + for ; Tue, 26 Oct 2004 02:39:45 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9Q1ditA003223; + Mon, 25 Oct 2004 21:39:46 -0400 (EDT) +To: Dennis Bjorklund +cc: Robert Treat , pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: +References: +Comments: In-reply-to Dennis Bjorklund + message dated "Sat, 23 Oct 2004 08:09:05 +0200" +Date: Mon, 25 Oct 2004 21:39:43 -0400 +Message-ID: <3222.1098754783@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Dennis Bjorklund writes: +> So if I understand you correctly you are planning to extend the current +> timestamp type to work with both named time zones and HH:MM ones? I didn't +> think you wanted the last one since your plan was to store a UTC+OID where +> the OID pointed to a named time zone. And I guess that you don't plan to +> add 00:00, 00:01, 00:02, ... as named zones with an OID. + +I missed getting back to you on this, but I think we can do both. Some +random points: + +* Once we expand timestamptz to bigger than 8 bytes, there's essentially +zero cost to making it 12 bytes, and for that matter we could go to 16 +without much penalty, because of alignment considerations. So there's +plenty of space. + +* What we need is to be able to represent either a fixed offset from UTC +or a reference of some kind to a zic database entry. The most +bit-splurging way of doing the former is a signed offset in seconds from +Greenwich, which would take 17 bits. It'd be good enough to represent +the offset in minutes, which needs only 11 bits. + +* I suggested OIDs for referencing zic entries, but we don't have to do +that; any old mapping table will do. 16 bits would surely be plenty to +assign a unique label to every present and future zic entry. + +* My inclination therefore is to extend timestamptz with two 16-bit +fields, one being the offset from UTC (in minutes) and one being the +zic identifier. If the identifier is zero then it's a straight numeric +offset from UTC and the offset field is all you need (this is the SQL +spec compatible case). If the identifier is not zero then it gives you +an index to look up the timezone rules. However, there is no need for +the offset field to go to waste; we should store the offset anyway, +since that might save a trip to the zic database in some cases. + +* It's not clear to me yet whether the stored offset in the second case +should be the zone's standard UTC offset (thus always the same for a +given zone ID) or the current-time offset for the timestamp (thus +different if the timestamp is in daylight-savings or standard time). + +* If we store the current-time offset then it almost doesn't matter +whether the timestamp itself is stored as a UTC or local time value; +you can trivially translate either to the other by adding or subtracting +the offset (*60). But I'm inclined to store UTC for consistency with +past practice, and because it will make comparisons a bit faster: you +can compare the timestamps without adjusting first. Generally I think +comparisons ought to be the best-optimized operations in a Postgres +datatype, because index operations will do a ton of 'em. (We definitely +do NOT want to have to visit the zic database in order to compare two +timestamptz values.) + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 4: Don't 'kill -9' the postmaster + +From pgsql-hackers-owner+M60373=pgman=candle.pha.pa.us@postgresql.org Mon Oct 25 21:53:52 2004 +Return-path: +Received: from svr1.postgresql.org (svr1.postgresql.org [200.46.204.71]) + by candle.pha.pa.us (8.11.6/8.11.6) with ESMTP id i9Q1rpf17038 + for ; Mon, 25 Oct 2004 21:53:51 -0400 (EDT) +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 1B6153A42AC + for ; Tue, 26 Oct 2004 02:53:46 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 58642-09 for ; + Tue, 26 Oct 2004 01:53:42 +0000 (GMT) +Received: from postgresql.org (svr1.postgresql.org [200.46.204.71]) + by svr1.postgresql.org (Postfix) with ESMTP id CE6DE3A42A7 + for ; Tue, 26 Oct 2004 02:53:45 +0100 (BST) +X-Original-To: pgsql-hackers-postgresql.org@localhost.postgresql.org +Received: from localhost (unknown [200.46.204.144]) + by svr1.postgresql.org (Postfix) with ESMTP id 768EE3A429E + for ; Tue, 26 Oct 2004 02:52:13 +0100 (BST) +Received: from svr1.postgresql.org ([200.46.204.71]) + by localhost (av.hub.org [200.46.204.144]) (amavisd-new, port 10024) + with ESMTP id 58205-07 + for ; + Tue, 26 Oct 2004 01:52:07 +0000 (GMT) +Received: from sss.pgh.pa.us (sss.pgh.pa.us [66.207.139.130]) + by svr1.postgresql.org (Postfix) with ESMTP id 397C43A4294 + for ; Tue, 26 Oct 2004 02:52:11 +0100 (BST) +Received: from sss2.sss.pgh.pa.us (tgl@localhost [127.0.0.1]) + by sss.pgh.pa.us (8.13.1/8.13.1) with ESMTP id i9Q1q07B003307; + Mon, 25 Oct 2004 21:52:00 -0400 (EDT) +To: Christopher Kings-Lynne +cc: josh@agliodbs.com, pgsql-hackers@postgresql.org +Subject: Re: [HACKERS] timestamp with time zone a la sql99 +In-Reply-To: <417DA785.7010208@familyhealth.com.au> +References: <20041022184636.62D39EAEE02@svr1.postgresql.org> <200410250942.38212.josh@agliodbs.com> <21834.1098726893@sss.pgh.pa.us> <200410251108.52164.josh@agliodbs.com> <22093.1098728380@sss.pgh.pa.us> <417DA785.7010208@familyhealth.com.au> +Comments: In-reply-to Christopher Kings-Lynne + message dated "Tue, 26 Oct 2004 09:25:25 +0800" +Date: Mon, 25 Oct 2004 21:52:00 -0400 +Message-ID: <3306.1098755520@sss.pgh.pa.us> +From: Tom Lane +X-Virus-Scanned: by amavisd-new at hub.org +X-Mailing-List: pgsql-hackers +Precedence: bulk +Sender: pgsql-hackers-owner@postgresql.org +X-Virus-Scanned: by amavisd-new at hub.org +Status: OR + +Christopher Kings-Lynne writes: +> That's worked for ages. What doesn't work is this: + +> usatest=# select current_timestamp at time zone 'US/Arizona'; +> ERROR: time zone "us/arizona" not recognized + +Right, and similarly you can do + +regression=# select '2004-10-25 21:32:33.430222 MST'::timestamptz; + timestamptz +------------------------------- + 2004-10-26 00:32:33.430222-04 +(1 row) + +but not + +regression=# select '2004-10-25 21:32:33.430222 US/Arizona'::timestamptz; +ERROR: invalid input syntax for type timestamp with time zone: "2004-10-25 21:32:33.430222 US/Arizona" + +I would like to see both of these cases working in 8.1; and furthermore +I'd like to see the timezone specs coming back as entered, not as bare +numeric offsets. (This will need to be adjustable via a DateStyle +option, of course, but I want the information to be in there whether it +is displayed or not.) + + regards, tom lane + +---------------------------(end of broadcast)--------------------------- +TIP 7: don't forget to increase your free space map settings + diff --git a/doc/src/FAQ/README b/doc/src/FAQ/README new file mode 100644 index 0000000..9ef5cc6 --- /dev/null +++ b/doc/src/FAQ/README @@ -0,0 +1,13 @@ +The FAQ* files in this directory are the master versions, and the +../../FAQ* text files are created using lynx: + + lynx -force_html -dont_wrap_pre -dump -hiddenlinks=ignore -nolist FAQ* + +The TODO.html file in this directory is not the master, but ../../TODO +is. The conversion is done using txt2html: + + txt2html -m -s 100 -p 100 --xhtml --titlefirst \ + --body_deco ' bgcolor="#FFFFFF" text="#000000" link="#FF0000" vlink="#A00000" alink="#0000FF"' \ + --caps_tag '' \ + /pgtop/doc/TODO | + sed 's;\[\([^]]*\)\];[\1];g' > /pgtop/doc/src/FAQ/TODO.html diff --git a/doc/src/FAQ/TODO.html b/doc/src/FAQ/TODO.html new file mode 100644 index 0000000..597f31c --- /dev/null +++ b/doc/src/FAQ/TODO.html @@ -0,0 +1,1032 @@ + + + +PostgreSQL TODO List + + + +

PostgreSQL TODO List

+

Current maintainer: Bruce Momjian (pgman@candle.pha.pa.us)
+Last updated: Thu Oct 27 10:15:56 EDT 2005 +

+

The most recent version of this document can be viewed at
+http://www.postgresql.org/docs/faqs.TODO.html. +

+

A hyphen, "-", marks changes that will appear in the upcoming 8.1 release.
+A percent sign, "%", marks items that are easier to implement. +

+

Bracketed items, "[]", have more detail. +

+

This list contains all known PostgreSQL bugs and feature requests. If
+you would like to work on an item, please read the Developer's FAQ
+first. +

+

Administration

+ +
    +
  • %Remove behavior of postmaster -o after making postmaster/postgres + flags unique +
  • %Allow pooled connections to list all prepared queries +

    This would allow an application inheriting a pooled connection to know + the queries prepared in the current session. +

    +
  • Allow major upgrades without dump/reload, perhaps using pg_upgrade + [pg_upgrade] +
  • Check for unreferenced table files created by transactions that were + in-progress when the server terminated abruptly +
  • Allow administrators to safely terminate individual sessions either + via an SQL function or SIGTERM +

    Lock table corruption following SIGTERM of an individual backend + has been reported in 8.0. A possible cause was fixed in 8.1, but + it is unknown whether other problems exist. This item mostly + requires additional testing rather than of writing any new code. +

    +
  • %Set proper permissions on non-system schemas during db creation +

    Currently all schemas are owned by the super-user because they are + copied from the template1 database. +

    +
  • Support table partitioning that allows a single table to be stored + in subtables that are partitioned based on the primary key or a WHERE + clause +
  • Add function to report the time of the most recent server reload +
  • Improve replication solutions +
      +
    • Load balancing +

      You can use any of the master/slave replication servers to use a + standby server for data warehousing. To allow read/write queries to + multiple servers, you need multi-master replication like pgcluster. +

      +
    • Allow replication over unreliable or non-persistent links +
    +
  • Configuration files +
      +
    • %Add "include file" functionality in postgresql.conf +
    • %Allow commenting of variables in postgresql.conf to restore them + to defaults +

      Currently, if a variable is commented out, it keeps the + previous uncommented value until a server restarted. +

      +
    • %Allow pg_hba.conf settings to be controlled via SQL +

      This would add a function to load the SQL table from + pg_hba.conf, and one to writes its contents to the flat file. + The table should have a line number that is a float so rows + can be inserted between existing rows, e.g. row 2.5 goes + between row 2 and row 3. +

      +
    • %Allow postgresql.conf file values to be changed via an SQL + API, perhaps using SET GLOBAL +
    • Allow the server to be stopped/restarted via an SQL API +
    • Issue a warning if a change-on-restart-only postgresql.conf value + is modified and the server config files are reloaded +
    • Mark change-on-restart-only values in postgresql.conf +
    +
  • Tablespaces +
      +
    • Allow a database in tablespace t1 with tables created in + tablespace t2 to be used as a template for a new database created + with default tablespace t2 +

      All objects in the default database tablespace must have default + tablespace specifications. This is because new databases are + created by copying directories. If you mix default tablespace + tables and tablespace-specified tables in the same directory, + creating a new database from such a mixed directory would create a + new database with tables that had incorrect explicit tablespaces. + To fix this would require modifying pg_class in the newly copied + database, which we don't currently do. +

      +
    • Allow reporting of which objects are in which tablespaces +

      This item is difficult because a tablespace can contain objects + from multiple databases. There is a server-side function that + returns the databases which use a specific tablespace, so this + requires a tool that will call that function and connect to each + database to find the objects in each database for that tablespace. +

      +
        +
      • %Add a GUC variable to control the tablespace for temporary objects + and sort files +

        It could start with a random tablespace from a supplied list and + cycle through the list. +

        +
      • Allow WAL replay of CREATE TABLESPACE to work when the directory + structure on the recovery computer is different from the original +
      • Allow per-tablespace quotas +
      +
    +
  • Point-In-Time Recovery (PITR) +
      +
    • Allow point-in-time recovery to archive partially filled + write-ahead logs [pitr] +

      Currently only full WAL files are archived. This means that the + most recent transactions aren't available for recovery in case + of a disk failure. This could be triggered by a user command or + a timer. +

      +
    • Automatically force archiving of partially-filled WAL files when + pg_stop_backup() is called or the server is stopped +

      Doing this will allow administrators to know more easily when + the archive contins all the files needed for point-in-time + recovery. +

      +
    • %Create dump tool for write-ahead logs for use in determining + transaction id for point-in-time recovery +
    • Allow a warm standby system to also allow read-only queries + [pitr] +

      This is useful for checking PITR recovery. +

      +
    • Allow the PITR process to be debugged and data examined +
    +
+

Monitoring

+ +
    +
  • Allow server log information to be output as INSERT statements +

    This would allow server log information to be easily loaded into + a database for analysis. +

    +
  • %Add ability to monitor the use of temporary sort files +
  • Allow server logs to be remotely read and removed using SQL commands +
  • Allow protocol-level BIND parameter values to be logged +
+

Data Types

+ +
    +
  • Improve the MONEY data type +

    Change the MONEY data type to use DECIMAL internally, with special + locale-aware output formatting. +

    +
  • Change NUMERIC to enforce the maximum precision, and increase it +
  • Add NUMERIC division operator that doesn't round? +

    Currently NUMERIC _rounds_ the result to the specified precision. + This means division can return a result that multiplied by the + divisor is greater than the dividend, e.g. this returns a value > 10: +

    +
+

SELECT (10::numeric(2,0) / 6::numeric(2,0))::numeric(2,0) * 6; +

+

The positive modulus result returned by NUMERICs might be considered
+ inaccurate, in one sense. +

+
    +
  • %Disallow changing default expression of a SERIAL column? +
  • Fix data types where equality comparison isn't intuitive, e.g. box +
  • %Prevent INET cast to CIDR if the unmasked bits are not zero, or + zero the bits +
  • %Prevent INET cast to CIDR from droping netmask, SELECT '1.1.1.1'::inet::cidr +
  • Allow INET + INT4 to increment the host part of the address, or + throw an error on overflow +
  • %Add 'tid != tid ' operator for use in corruption recovery +
  • Allow user-defined types to specify a type modifier at table creation + time +
  • Dates and Times +
      +
    • Allow infinite dates just like infinite timestamps +
    • Merge hardwired timezone names with the TZ database; allow either + kind everywhere a TZ name is currently taken +
    • Allow customization of the known set of TZ names (generalize the + present australian_timezones hack) +
    • Allow TIMESTAMP WITH TIME ZONE to store the original timezone + information, either zone name or offset from UTC [timezone] +

      If the TIMESTAMP value is stored with a time zone name, interval + computations should adjust based on the time zone rules. +

      +
    • Fix SELECT '0.01 years'::interval, '0.01 months'::interval +
    • Fix SELECT INTERVAL '1' MONTH +
    • Add a GUC variable to allow output of interval values in ISO8601 + format +
    • Improve timestamptz subtraction to be DST-aware +

      Currently, subtracting one date from another that crosses a + daylight savings time adjustment can return '1 day 1 hour', but + adding that back to the first date returns a time one hour in + the future. This is caused by the adjustment of '25 hours' to + '1 day 1 hour', and '1 day' is the same time the next day, even + if daylight savings adjustments are involved. +

      +
    • Fix interval display to support values exceeding 2^31 hours +
    • Add overflow checking to timestamp and interval arithmetic +
    • Add ISO INTERVAL handling +
        +
      • Add support for day-time syntax, INTERVAL '1 2:03:04' DAY TO + SECOND +
      • Add support for year-month syntax, INTERVAL '50-6' YEAR TO MONTH +
      • For syntax that isn't uniquely ISO or PG syntax, like '1:30' or + '1', treat as ISO if there is a range specification clause, + and as PG if there no clause is present, e.g. interpret +

        '1:30' MINUTE TO SECOND as '1 minute 30 seconds', and + interpret '1:30' as '1 hour, 30 minutes' +

      • Interpret INTERVAL '1 year' MONTH as CAST (INTERVAL '1 year' AS + INTERVAL MONTH), and this should return '12 months' +
      • Round or truncate values to the requested precision, e.g. + INTERVAL '11 months' AS YEAR should return one or zero +
      • Support precision, CREATE TABLE foo (a INTERVAL MONTH(3)) +
      +

    +
  • Arrays +
      +
    • Allow NULLs in arrays +
    • Delay resolution of array expression's data type so assignment + coercion can be performed on empty array expressions +
    +
  • Binary Data +
      +
    • Improve vacuum of large objects, like /contrib/vacuumlo? +
    • Add security checking for large objects +
    • Auto-delete large objects when referencing row is deleted +

      /contrib/lo offers this functionality. +

      +
    • Allow read/write into TOAST values like large objects +

      This requires the TOAST column to be stored EXTERNAL. +

      +
    +
+

Functions

+ +
    +
  • Allow INET subnet tests using non-constants to be indexed +
  • Add transaction_timestamp(), statement_timestamp(), clock_timestamp() + functionality +

    Current CURRENT_TIMESTAMP returns the start time of the current + transaction, and gettimeofday() returns the wallclock time. This will + make time reporting more consistent and will allow reporting of + the statement start time. +

    +
  • %Add pg_get_acldef(), pg_get_typedefault(), and pg_get_attrdef() +
  • Allow to_char() to print localized month names +
  • Allow functions to have a schema search path specified at creation time +
  • Allow substring/replace() to get/set bit values +
  • Allow to_char() on interval values to accumulate the highest unit + requested +

    Some special format flag would be required to request such + accumulation. Such functionality could also be added to EXTRACT. + Prevent accumulation that crosses the month/day boundary because of + the uneven number of days in a month. +

    +
      +
    • to_char(INTERVAL '1 hour 5 minutes', 'MI') => 65 +
    • to_char(INTERVAL '43 hours 20 minutes', 'MI' ) => 2600 +
    • to_char(INTERVAL '43 hours 20 minutes', 'WK:DD:HR:MI') => 0:1:19:20 +
    • to_char(INTERVAL '3 years 5 months','MM') => 41 +
    +
  • Add sleep() function, remove from regress.c +
  • Allow user-defined functions retuning a domain value to enforce domain + constraints +
+

Multi-Language Support

+ +
    +
  • Add NCHAR (as distinguished from ordinary varchar), +
  • Allow locale to be set at database creation +

    Currently locale can only be set during initdb. No global tables have + locale-aware columns. However, the database template used during + database creation might have locale-aware indexes. The indexes would + need to be reindexed to match the new locale. +

    +
  • Allow encoding on a per-column basis +

    Right now only one encoding is allowed per database. +

    +
  • Support multiple simultaneous character sets, per SQL92 +
  • Improve UTF8 combined character handling? +
  • Add octet_length_server() and octet_length_client() +
  • Make octet_length_client() the same as octet_length()? +
  • Fix problems with wrong runtime encoding conversion for NLS message files +
+

Views / Rules

+ +
    +
  • %Automatically create rules on views so they are updateable, per SQL99 +

    We can only auto-create rules for simple views. For more complex + cases users will still have to write rules. +

    +
  • Add the functionality for WITH CHECK OPTION clause of CREATE VIEW +
  • Allow NOTIFY in rules involving conditionals +
  • Allow VIEW/RULE recompilation when the underlying tables change +

    Another issue is whether underlying table changes should be reflected + in the view, e.g. should SELECT * show additional columns if they + are added after the view is created. +

    +
+

SQL Commands

+ +
    +
  • Change LIMIT/OFFSET and FETCH/MOVE to use int8 +
  • Add CORRESPONDING BY to UNION/INTERSECT/EXCEPT +
  • Add ROLLUP, CUBE, GROUPING SETS options to GROUP BY +
  • %Allow SET CONSTRAINTS to be qualified by schema/table name +
  • %Allow TRUNCATE ... CASCADE/RESTRICT +

    This is like DELETE CASCADE, but truncates. +

    +
  • %Add a separate TRUNCATE permission +

    Currently only the owner can TRUNCATE a table because triggers are not + called, and the table is locked in exclusive mode. +

    +
  • Allow PREPARE of cursors +
  • Allow PREPARE to automatically determine parameter types based on the SQL + statement +
  • Allow finer control over the caching of prepared query plans +

    Currently, queries prepared via the libpq API are planned on first + execute using the supplied parameters --- allow SQL PREPARE to do the + same. Also, allow control over replanning prepared queries either + manually or automatically when statistics for execute parameters + differ dramatically from those used during planning. +

    +
  • Allow LISTEN/NOTIFY to store info in memory rather than tables? +

    Currently LISTEN/NOTIFY information is stored in pg_listener. Storing + such information in memory would improve performance. +

    +
  • Add optional textual message to NOTIFY +

    This would allow an informational message to be added to the notify + message, perhaps indicating the row modified or other custom + information. +

    +
  • Add a GUC variable to warn about non-standard SQL usage in queries +
  • Add MERGE command that does UPDATE/DELETE, or on failure, INSERT (rules, + triggers?) +
  • Add NOVICE output level for helpful messages like automatic sequence/index + creation +
  • %Add COMMENT ON for all cluster global objects (roles, databases + and tablespaces) +
  • %Make row-wise comparisons work per SQL spec +
  • Add RESET CONNECTION command to reset all session state +

    This would include resetting of all variables (RESET ALL), dropping of + temporary tables, removing any NOTIFYs, cursors, open transactions, + prepared queries, currval()s, etc. This could be used for connection + pooling. We could also change RESET ALL to have this functionality. + The difficult of this features is allowing RESET ALL to not affect + changes made by the interface driver for its internal use. One idea + is for this to be a protocol-only feature. Another approach is to + notify the protocol when a RESET CONNECTION command is used. +

    +
  • Add GUC to issue notice about queries that use unjoined tables +
  • Allow EXPLAIN to identify tables that were skipped because of + constraint_exclusion +
  • Allow EXPLAIN output to be more easily processed by scripts +
  • Eventually enable escape_stringwarning and standardconforming_strings +
  • Simplify dropping roles that have objects in several databases +
  • CREATE +
      +
    • Allow CREATE TABLE AS to determine column lengths for complex + expressions like SELECT col1 || col2 +
    • Use more reliable method for CREATE DATABASE to get a consistent + copy of db? +
    • Add ON COMMIT capability to CREATE TABLE AS ... SELECT +
    +
  • UPDATE +
      +
    • Allow UPDATE to handle complex aggregates [update]? +
    • Allow an alias to be provided for the target table in + UPDATE/DELETE +

      This is not SQL-spec but many DBMSs allow it. +

      +
    • Allow UPDATE tab SET ROW (col, ...) = (...) for updating multiple + columns +
    +
  • ALTER +
      +
    • %Have ALTER TABLE RENAME rename SERIAL sequence names +
    • Add ALTER DOMAIN to modify the underlying data type +
    • %Allow ALTER TABLE ... ALTER CONSTRAINT ... RENAME +
    • %Allow ALTER TABLE to change constraint deferrability and actions +
    • Add missing object types for ALTER ... SET SCHEMA +
    • Allow ALTER TABLESPACE to move to different directories +
    • Allow databases to be moved to different tablespaces +
    • Allow moving system tables to other tablespaces, where possible +

      Currently non-global system tables must be in the default database + tablespace. Global system tables can never be moved. +

      +
    • %Disallow dropping of an inherited constraint +
    • %Prevent child tables from altering or dropping constraints + like CHECK that were inherited from the parent table +
    • Have ALTER INDEX update the name of a constraint using that index +
    • Add ALTER TABLE RENAME CONSTRAINT, update index name also +
    +
  • CLUSTER +
      +
    • Automatically maintain clustering on a table +

      This might require some background daemon to maintain clustering + during periods of low usage. It might also require tables to be only + paritally filled for easier reorganization. Another idea would + be to create a merged heap/index data file so an index lookup would + automatically access the heap data too. A third idea would be to + store heap rows in hashed groups, perhaps using a user-supplied + hash function. +

      +
    • %Add default clustering to system tables +

      To do this, determine the ideal cluster index for each system + table and set the cluster setting during initdb. +

      +
    +
  • COPY +
      +
    • Allow COPY to report error lines and continue +

      This requires the use of a savepoint before each COPY line is + processed, with ROLLBACK on COPY failure. +

      +
    • %Have COPY return the number of rows loaded/unloaded? +
    • Allow COPY on a newly-created table to skip WAL logging +

      On crash recovery, the table involved in the COPY would + be removed or have its heap and index files truncated. One + issue is that no other backend should be able to add to + the table at the same time, which is something that is + currently allowed. +

      +
    • Allow COPY to output from views +

      Another idea would be to allow actual SELECT queries in a COPY. +

      +
    +
  • GRANT/REVOKE +
      +
    • Allow column-level privileges +
    • %Allow GRANT/REVOKE permissions to be applied to all schema objects + with one command +

      The proposed syntax is: +

      GRANT SELECT ON ALL TABLES IN public TO phpuser; + GRANT SELECT ON NEW TABLES IN public TO phpuser; +

      +
        +
      • Allow GRANT/REVOKE permissions to be inherited by objects based on + schema permissions +
      +
    +
  • CURSOR +
      +
    • Allow UPDATE/DELETE WHERE CURRENT OF cursor +

      This requires using the row ctid to map cursor rows back to the + original heap row. This become more complicated if WITH HOLD cursors + are to be supported because WITH HOLD cursors have a copy of the row + and no FOR UPDATE lock. +

      +
    • Prevent DROP TABLE from dropping a row referenced by its own open + cursor? +
    • %Allow pooled connections to list all open WITH HOLD cursors +

      Because WITH HOLD cursors exist outside transactions, this allows + them to be listed so they can be closed. +

      +
    +
  • INSERT +
      +
    • Allow INSERT/UPDATE of the system-generated oid value for a row +
    • Allow INSERT INTO tab (col1, ..) VALUES (val1, ..), (val2, ..) +
    • Allow INSERT/UPDATE ... RETURNING new.col or old.col +

      This is useful for returning the auto-generated key for an INSERT. + One complication is how to handle rules that run as part of + the insert. +

      +
    +
  • SHOW/SET +
      +
    • Add SET PERFORMANCE_TIPS option to suggest INDEX, VACUUM, VACUUM + ANALYZE, and CLUSTER +
    • Add SET PATH for schemas? +

      This is basically the same as SET search_path. +

      +
    +
  • Server-Side Languages +
      +
    • Fix PL/pgSQL RENAME to work on variables other than OLD/NEW +
    • Allow function parameters to be passed by name, + get_employee_salary(emp_id => 12345, tax_year => 2001) +
    • Add Oracle-style packages +
    • Add table function support to pltcl, plpython +
    • Add capability to create and call PROCEDURES +
    • Allow PL/pgSQL to handle %TYPE arrays, e.g. tab.col%TYPE[] +
    • Allow function argument names to be queries from PL/PgSQL +
    • Add MOVE to PL/pgSQL +
    • Add support for polymorphic arguments and return types to + languages other than PL/PgSQL +
    • Add support for OUT and INOUT parameters to languages other + than PL/PgSQL +
    • Add single-step debugging of PL/PgSQL functions +
    • Allow PL/PgSQL to support WITH HOLD cursors +
    +
+

Clients

+ +
    +
  • Add a libpq function to support Parse/DescribeStatement capability +
  • Add PQescapeIdentifier() to libpq +
  • Prevent PQfnumber() from lowercasing unquoted the column name +

    PQfnumber() should never have been doing lowercasing, but historically + it has so we need a way to prevent it +

    +
  • Have initdb set the input DateStyle (MDY or DMY) based on locale? +
  • Have pg_ctl look at PGHOST in case it is a socket directory? +
  • Allow pg_ctl to work properly with configuration files located outside + the PGDATA directory +

    pg_ctl can not read the pid file because it isn't located in the + config directory but in the PGDATA directory. The solution is to + allow pg_ctl to read and understand postgresql.conf to find the + data_directory value. +

    +
  • psql +
      +
    • Have psql show current values for a sequence +
    • Move psql backslash database information into the backend, use + mnemonic commands? [psql] +

      This would allow non-psql clients to pull the same information out + of the database as psql. +

      +
    • Fix psql's display of schema information (Neil) +
    • Allow psql \pset boolean variables to set to fixed values, rather + than toggle +
    • Consistently display privilege information for all objects in psql +
    • Improve psql's handling of multi-line queries +

      Currently, while \e saves a single query as one entry, interactive + queries are saved one line at a time. Ideally all queries + whould be saved like \e does. +

      +
    • Allow multi-line column values to align in the proper columns +

      If the second output column value is 'a\nb', the 'b' should appear + in the second display column, rather than the first column as it + does now. +

      +
    • Display IN, INOUT, and OUT parameters in \df+ +

      It probably requires psql to output newlines in the proper + column, which is already on the TODO list. +

      +
    • Add auto-expanded mode so expanded output is used if the row + length is wider than the screen width. +

      Consider using auto-expanded mode for backslash commands like \df+. +

      +
    +
  • pg_dump +
      +
    • %Have pg_dump use multi-statement transactions for INSERT dumps +
    • %Allow pg_dump to use multiple -t and -n switches [pg_dump] +
    • %Add dumping of comments on index columns and composite type columns +
    • %Add full object name to the tag field. eg. for operators we need + '=(integer, integer)', instead of just '='. +
    • Add pg_dumpall custom format dumps? +
    • %Add CSV output format +
    • Update pg_dump and psql to use the new COPY libpq API (Christopher) +
    • Remove unnecessary function pointer abstractions in pg_dump source + code +
    • Allow selection of individual object(s) of all types, not just + tables +
    • In a selective dump, allow dumping of an object and all its + dependencies +
    • Add options like pg_restore -l and -L to pg_dump +
    • Stop dumping CASCADE on DROP TYPE commands in clean mode +
    • Allow pg_dump --clean to drop roles that own objects or have + privileges +
    +
  • ecpg +
      +
    • Docs +

      Document differences between ecpg and the SQL standard and + information about the Informix-compatibility module. +

      +
    • Solve cardinality > 1 for input descriptors / variables? +
    • Add a semantic check level, e.g. check if a table really exists +
    • fix handling of DB attributes that are arrays +
    • Use backend PREPARE/EXECUTE facility for ecpg where possible +
    • Implement SQLDA +
    • Fix nested C comments +
    • %sqlwarn[6] should be 'W' if the PRECISION or SCALE value specified +
    • Make SET CONNECTION thread-aware, non-standard? +
    • Allow multidimensional arrays +
    • Add internationalized message strings +
    +
+

Referential Integrity

+ +
    +
  • Add MATCH PARTIAL referential integrity +
  • Add deferred trigger queue file +

    Right now all deferred trigger information is stored in backend + memory. This could exhaust memory for very large trigger queues. + This item involves dumping large queues into files. +

    +
  • Change foreign key constraint for array -> element to mean element + in array? +
  • Allow DEFERRABLE UNIQUE constraints? +
  • Allow triggers to be disabled in only the current session. +

    This is currently possible by starting a multi-statement transaction, + modifying the system tables, performing the desired SQL, restoring the + system tables, and committing the transaction. ALTER TABLE ... + TRIGGER requires a table lock so it is not ideal for this usage. +

    +
  • With disabled triggers, allow pg_dump to use ALTER TABLE ADD FOREIGN KEY +

    If the dump is known to be valid, allow foreign keys to be added + without revalidating the data. +

    +
  • Allow statement-level triggers to access modified rows +
  • Support triggers on columns (Greg Sabino Mullane) +
  • Enforce referential integrity for system tables +
  • Allow AFTER triggers on system tables +

    System tables are modified in many places in the backend without going + through the executor and therefore not causing triggers to fire. To + complete this item, the functions that modify system tables will have + to fire triggers. +

    +
+

Dependency Checking

+ +
    +
  • Flush cached query plans when the dependent objects change +
  • Track dependencies in function bodies and recompile/invalidate +

    This is particularly important for references to temporary tables + in PL/PgSQL because PL/PgSQL caches query plans. The only workaround + in PL/PgSQL is to use EXECUTE. One complexity is that a function + might itself drop and recreate dependent tables, causing it to + invalidate its own query plan. +

    +
+

Exotic Features

+ +
    +
  • Add SQL99 WITH clause to SELECT +
  • Add SQL99 WITH RECURSIVE to SELECT +
  • Add pre-parsing phase that converts non-ISO syntax to supported + syntax +

    This could allow SQL written for other databases to run without + modification. +

    +
  • Allow plug-in modules to emulate features from other databases +
  • SQL*Net listener that makes PostgreSQL appear as an Oracle database + to clients +
  • Allow queries across databases or servers with transaction + semantics +

    This can be done using dblink and two-phase commit. +

    +
  • Add the features of packages +
      +
    • Make private objects accessable only to objects in the same schema +
    • Allow current_schema.objname to access current schema objects +
    • Add session variables +
    • Allow nested schemas +
    +
+

Indexes

+ +
    +
  • Allow inherited tables to inherit index, UNIQUE constraint, and primary + key, foreign key +
  • UNIQUE INDEX on base column not honored on INSERTs/UPDATEs from + inherited table: INSERT INTO inherit_table (unique_index_col) VALUES + (dup) should fail +

    The main difficulty with this item is the problem of creating an index + that can span more than one table. +

    +
  • Allow SELECT ... FOR UPDATE on inherited tables +
  • Add UNIQUE capability to non-btree indexes +
  • Prevent index uniqueness checks when UPDATE does not modify the column +

    Uniqueness (index) checks are done when updating a column even if the + column is not modified by the UPDATE. +

    +
  • Allow the creation of on-disk bitmap indexes which can be quickly + combined with other bitmap indexes +

    Such indexes could be more compact if there are only a few distinct values. + Such indexes can also be compressed. Keeping such indexes updated can be + costly. +

    +
  • Allow use of indexes to search for NULLs +

    One solution is to create a partial index on an IS NULL expression. +

    +
  • Allow accurate statistics to be collected on indexes with more than + one column or expression indexes, perhaps using per-index statistics +
  • Add fillfactor to control reserved free space during index creation +
  • Allow the creation of indexes with mixed ascending/descending specifiers +
  • Allow constraint_exclusion to work for UNIONs like it does for + inheritance, allow it to work for UPDATE and DELETE queries, and allow + it to be used for all queries with little performance impact +
  • Allow CREATE INDEX to take an additional parameter for use with + special index types +
  • Consider compressing indexes by storing key values duplicated in + several rows as a single index entry +

    This is difficult because it requires datatype-specific knowledge. +

    +
  • GIST +
      +
    • Add more GIST index support for geometric data types +
    • Allow GIST indexes to create certain complex index types, like + digital trees (see Aoki) +
    +
  • Hash +
      +
    • Pack hash index buckets onto disk pages more efficiently +

      Currently only one hash bucket can be stored on a page. Ideally + several hash buckets could be stored on a single page and greater + granularity used for the hash algorithm. +

      +
    • Consider sorting hash buckets so entries can be found using a + binary search, rather than a linear scan +
    • In hash indexes, consider storing the hash value with or instead + of the key itself +
    • Add WAL logging for crash recovery +
    • Allow multi-column hash indexes +
    +
+

Fsync

+ +
    +
  • Improve commit_delay handling to reduce fsync() +
  • Determine optimal fdatasync/fsync, O_SYNC/O_DSYNC options +

    Ideally this requires a separate test program that can be run + at initdb time or optionally later. +

    +
  • %Add an option to sync() before fsync()'ing checkpoint files +
  • Add program to test if fsync has a delay compared to non-fsync +
+

Cache Usage

+ +
    +
  • Allow free-behind capability for large sequential scans, perhaps using + posix_fadvise() +

    Posix_fadvise() can control both sequential/random file caching and + free-behind behavior, but it is unclear how the setting affects other + backends that also have the file open, and the feature is not supported + on all operating systems. +

    +
  • Speed up COUNT(*) +

    We could use a fixed row count and a +/- count to follow MVCC + visibility rules, or a single cached value could be used and + invalidated if anyone modifies the table. Another idea is to + get a count directly from a unique index, but for this to be + faster than a sequential scan it must avoid access to the heap + to obtain tuple visibility information. +

    +
  • Allow data to be pulled directly from indexes +

    Currently indexes do not have enough tuple visibility information + to allow data to be pulled from the index without also accessing + the heap. One way to allow this is to set a bit to index tuples + to indicate if a tuple is currently visible to all transactions + when the first valid heap lookup happens. This bit would have to + be cleared when a heap tuple is expired. +

    +
  • Consider automatic caching of queries at various levels: +
      +
    • Parsed query tree +
    • Query execute plan +
    • Query results +
    +
  • Allow sequential scans to take advantage of other concurrent + sequentiqal scans, also called "Synchronised Scanning" +

    One possible implementation is to start sequential scans from the lowest + numbered buffer in the shared cache, and when reaching the end wrap + around to the beginning, rather than always starting sequential scans + at the start of the table. +

    +
+

Vacuum

+ +
    +
  • Improve speed with indexes +

    For large table adjustements during VACUUM FULL, it is faster to + reindex rather than update the index. +

    +
  • Reduce lock time during VACUUM FULL by moving tuples with read lock, + then write lock and truncate table +

    Moved tuples are invisible to other backends so they don't require a + write lock. However, the read lock promotion to write lock could lead + to deadlock situations. +

    +
  • Auto-fill the free space map by scanning the buffer cache or by + checking pages written by the background writer +
  • Create a bitmap of pages that need vacuuming +

    Instead of sequentially scanning the entire table, have the background + writer or some other process record pages that have expired rows, then + VACUUM can look at just those pages rather than the entire table. In + the event of a system crash, the bitmap would probably be invalidated. + One complexity is that index entries still have to be vacuumed, and + doing this without an index scan (by using the heap values to find the + index entry) might be slow and unreliable, especially for user-defined + index functions. +

    +
  • %Add system view to show free space map contents +
  • Auto-vacuum +
      +
    • Use free-space map information to guide refilling +
    • %Issue log message to suggest VACUUM FULL if a table is nearly + empty? +
    • Improve xid wraparound detection by recording per-table rather + than per-database +
    +
+

Locking

+ +
    +
  • Fix priority ordering of read and write light-weight locks (Neil) +
+

Startup Time Improvements

+ +
    +
  • Experiment with multi-threaded backend [thread] +

    This would prevent the overhead associated with process creation. Most + operating systems have trivial process creation time compared to + database startup overhead, but a few operating systems (WIn32, + Solaris) might benefit from threading. Also explore the idea of + a single session using multiple threads to execute a query faster. +

    +
  • Add connection pooling +

    It is unclear if this should be done inside the backend code or done + by something external like pgpool. The passing of file descriptors to + existing backends is one of the difficulties with a backend approach. +

    +
+

Write-Ahead Log

+ +
    +
  • Eliminate need to write full pages to WAL before page modification [wal] +

    Currently, to protect against partial disk page writes, we write + full page images to WAL before they are modified so we can correct any + partial page writes during recovery. These pages can also be + eliminated from point-in-time archive files. +

    +
      +
    • When off, write CRC to WAL and check file system blocks + on recovery +

      If CRC check fails during recovery, remember the page in case + a later CRC for that page properly matches. +

      +
    • Write full pages during file system write and not when + the page is modified in the buffer cache +

      This allows most full page writes to happen in the background + writer. It might cause problems for applying WAL on recovery + into a partially-written page, but later the full page will be + replaced from WAL. +

      +
    +
  • Allow WAL traffic to be streamed to another server for stand-by + replication +
  • Reduce WAL traffic so only modified values are written rather than + entire rows? +
  • Allow the pg_xlog directory location to be specified during initdb + with a symlink back to the /data location +
  • Allow WAL information to recover corrupted pg_controldata +
  • Find a way to reduce rotational delay when repeatedly writing + last WAL page +

    Currently fsync of WAL requires the disk platter to perform a full + rotation to fsync again. One idea is to write the WAL to different + offsets that might reduce the rotational delay. +

    +
  • Allow buffered WAL writes and fsync +

    Instead of guaranteeing recovery of all committed transactions, this + would provide improved performance by delaying WAL writes and fsync + so an abrupt operating system restart might lose a few seconds of + committed transactions but still be consistent. We could perhaps + remove the 'fsync' parameter (which results in an an inconsistent + database) in favor of this capability. +

    +
+

Optimizer / Executor

+ +
    +
  • Add missing optimizer selectivities for date, r-tree, etc +
  • Allow ORDER BY ... LIMIT # to select high/low value without sort or + index using a sequential scan for highest/lowest values +

    Right now, if no index exists, ORDER BY ... LIMIT # requires we sort + all values to return the high/low value. Instead The idea is to do a + sequential scan to find the high/low value, thus avoiding the sort. + MIN/MAX already does this, but not for LIMIT > 1. +

    +
  • Precompile SQL functions to avoid overhead +
  • Create utility to compute accurate random_page_cost value +
  • Improve ability to display optimizer analysis using OPTIMIZER_DEBUG +
  • Have EXPLAIN ANALYZE highlight poor optimizer estimates +
  • Consider using hash buckets to do DISTINCT, rather than sorting +

    This would be beneficial when there are few distinct values. This is + already used by GROUP BY. +

    +
  • Log queries where the optimizer row estimates were dramatically + different from the number of rows actually found? +
+

Miscellaneous Performance

+ +
    +
  • Do async I/O for faster random read-ahead of data +

    Async I/O allows multiple I/O requests to be sent to the disk with + results coming back asynchronously. +

    +
  • Use mmap() rather than SYSV shared memory or to write WAL files? +

    This would remove the requirement for SYSV SHM but would introduce + portability issues. Anonymous mmap (or mmap to /dev/zero) is required + to prevent I/O overhead. +

    +
  • Consider mmap()'ing files into a backend? +

    Doing I/O to large tables would consume a lot of address space or + require frequent mapping/unmapping. Extending the file also causes + mapping problems that might require mapping only individual pages, + leading to thousands of mappings. Another problem is that there is no + way to _prevent_ I/O to disk from the dirty shared buffers so changes + could hit disk before WAL is written. +

    +
  • Add a script to ask system configuration questions and tune postgresql.conf +
  • Merge xmin/xmax/cmin/cmax back into three header fields +

    Before subtransactions, there used to be only three fields needed to + store these four values. This was possible because only the current + transaction looks at the cmin/cmax values. If the current transaction + created and expired the row the fields stored where xmin (same as + xmax), cmin, cmax, and if the transaction was expiring a row from a + another transaction, the fields stored were xmin (cmin was not + needed), xmax, and cmax. Such a system worked because a transaction + could only see rows from another completed transaction. However, + subtransactions can see rows from outer transactions, and once the + subtransaction completes, the outer transaction continues, requiring + the storage of all four fields. With subtransactions, an outer + transaction can create a row, a subtransaction expire it, and when the + subtransaction completes, the outer transaction still has to have + proper visibility of the row's cmin, for example, for cursors. +

    +

    One possible solution is to create a phantom cid which represents a + cmin/cmax pair and is stored in local memory. Another idea is to + store both cmin and cmax only in local memory. +

    +
  • Research storing disk pages with no alignment/padding +
+

Source Code

+ +
    +
  • Add use of 'const' for variables in source tree +
  • Rename some /contrib modules from pg* to pg_* +
  • Move some things from /contrib into main tree +
  • Move some /contrib modules out to their own project sites +
  • %Remove warnings created by -Wcast-align +
  • Move platform-specific ps status display info from ps_status.c to ports +
  • Add optional CRC checksum to heap and index pages +
  • Improve documentation to build only interfaces (Marc) +
  • Remove or relicense modules that are not under the BSD license, if possible +
  • %Remove memory/file descriptor freeing before ereport(ERROR) +
  • Acquire lock on a relation before building a relcache entry for it +
  • %Promote debug_query_string into a server-side function current_query() +
  • %Allow the identifier length to be increased via a configure option +
  • Remove Win32 rename/unlink looping if unnecessary +
  • Allow cross-compiling by generating the zic database on the target system +
  • Improve NLS maintenace of libpgport messages linked onto applications +
  • Allow ecpg to work with MSVC and BCC +
  • Add xpath_array() to /contrib/xml2 to return results as an array +
  • Allow building in directories containing spaces +

    This is probably not possible because 'gmake' and other compiler tools + do not fully support quoting of paths with spaces. +

    +
  • Allow installing to directories containing spaces +

    This is possible if proper quoting is added to the makefiles for the + install targets. Because PostgreSQL supports relocatable installs, it + is already possible to install into a directory that doesn't contain + spaces and then copy the install to a directory with spaces. +

    +
  • Fix sgmltools so PDFs can be generated with bookmarks +
  • %Clean up compiler warnings (especially with gcc version 4) +
  • Add function to return the thread safety status of libpq and ecpg +
  • Use UTF8 encoding for NLS messages so all server encodings can + read them properly +
  • Update Bonjour to work with newer cross-platform SDK +
  • Remove BeOS and QNX-specific code +
  • Win32 +
      +
    • Remove configure.in check for link failure when cause is found +
    • Remove readdir() errno patch when runtime/mingwex/dirent.c rev + 1.4 is released +
    • Remove psql newline patch when we find out why mingw outputs an + extra newline +
    • Allow psql to use readline once non-US code pages work with + backslashes +
    • Re-enable timezone output on log_line_prefix '%t' when a + shorter timezone string is available +
    • Fix problem with shared memory on the Win32 Terminal Server +
    • Improve signal handling, + http://archives.postgresql.org/pgsql-patches/2005-06/msg00027.php +
    +
  • Wire Protocol Changes +
      +
    • Allow dynamic character set handling +
    • Add decoded type, length, precision +
    • Use compression? +
    • Update clients to use data types, typmod, schema.table.column/ names + of result sets using new query protocol +
    +
+
+ +

Developers who have claimed items are:

+ + + + diff --git a/doc/src/sgml/indexam.sgml b/doc/src/sgml/indexam.sgml new file mode 100644 index 0000000..343ec58 --- /dev/null +++ b/doc/src/sgml/indexam.sgml @@ -0,0 +1,883 @@ + + + + Index Access Method Interface Definition + + + This chapter defines the interface between the core + PostgreSQL system and index access + methods, which manage individual index types. The core system + knows nothing about indexes beyond what is specified here, so it is + possible to develop entirely new index types by writing add-on code. + + + + All indexes in PostgreSQL are what are known + technically as secondary indexes; that is, the index is + physically separate from the table file that it describes. Each index + is stored as its own physical relation and so is described + by an entry in the pg_class catalog. The contents of an + index are entirely under the control of its index access method. In + practice, all index access methods divide indexes into standard-size + pages so that they can use the regular storage manager and buffer manager + to access the index contents. (All the existing index access methods + furthermore use the standard page layout described in , and they all use the same format for index + tuple headers; but these decisions are not forced on an access method.) + + + + An index is effectively a mapping from some data key values to + tuple identifiers, or TIDs, of row versions + (tuples) in the index's parent table. A TID consists of a + block number and an item number within that block (see ). This is sufficient + information to fetch a particular row version from the table. + Indexes are not directly aware that under MVCC, there may be multiple + extant versions of the same logical row; to an index, each tuple is + an independent object that needs its own index entry. Thus, an + update of a row always creates all-new index entries for the row, even if + the key values did not change. Index entries for dead tuples are + reclaimed (by vacuuming) when the dead tuples themselves are reclaimed. + + + + Catalog Entries for Indexes + + + Each index access method is described by a row in the + pg_am system catalog (see + ). The principal contents of a + pg_am row are references to + pg_proc + entries that identify the index access + functions supplied by the access method. The APIs for these functions + are defined later in this chapter. In addition, the + pg_am row specifies a few fixed properties of + the access method, such as whether it can support multicolumn indexes. + There is not currently any special support + for creating or deleting pg_am entries; + anyone able to write a new access method is expected to be competent + to insert an appropriate row for themselves. + + + + To be useful, an index access method must also have one or more + operator classes defined in + pg_opclass, + pg_amop, and + pg_amproc. + These entries allow the planner + to determine what kinds of query qualifications can be used with + indexes of this access method. Operator classes are described + in , which is prerequisite material for reading + this chapter. + + + + An individual index is defined by a + pg_class + entry that describes it as a physical relation, plus a + pg_index + entry that shows the logical content of the index — that is, the set + of index columns it has and the semantics of those columns, as captured by + the associated operator classes. The index columns (key values) can be + either simple columns of the underlying table or expressions over the table + rows. The index access method normally has no interest in where the index + key values come from (it is always handed precomputed key values) but it + will be very interested in the operator class information in + pg_index. Both of these catalog entries can be + accessed as part of the Relation data structure that is + passed to all operations on the index. + + + + Some of the flag columns of pg_am have nonobvious + implications. The requirements of amcanunique + are discussed in , and those of + amconcurrent in . + The amcanmulticol flag asserts that the + access method supports multicolumn indexes, while + amoptionalkey asserts that it allows scans + where no indexable restriction clause is given for the first index column. + When amcanmulticol is false, + amoptionalkey essentially says whether the + access method allows full-index scans without any restriction clause. + Access methods that support multiple index columns must + support scans that omit restrictions on any or all of the columns after + the first; however they are permitted to require some restriction to + appear for the first index column, and this is signaled by setting + amoptionalkey false. + amindexnulls asserts that index entries are + created for NULL key values. Since most indexable operators are + strict and hence cannot return TRUE for NULL inputs, + it is at first sight attractive to not store index entries for null values: + they could never be returned by an index scan anyway. However, this + argument fails when an index scan has no restriction clause for a given + index column. In practice this means that + indexes that have amoptionalkey true must + index nulls, since the planner might decide to use such an index + with no scan keys at all. A related restriction is that an index + access method that supports multiple index columns must + support indexing null values in columns after the first, because the planner + will assume the index can be used for queries that do not restrict + these columns. For example, consider an index on (a,b) and a query with + WHERE a = 4. The system will assume the index can be + used to scan for rows with a = 4, which is wrong if the + index omits rows where b is null. + It is, however, OK to omit rows where the first indexed column is null. + (GiST currently does so.) Thus, + amindexnulls should be set true only if the + index access method indexes all rows, including arbitrary combinations of + null values. + + + + + + Index Access Method Functions + + + The index construction and maintenance functions that an index access + method must provide are: + + + + +void +ambuild (Relation heapRelation, + Relation indexRelation, + IndexInfo *indexInfo); + + Build a new index. The index relation has been physically created, + but is empty. It must be filled in with whatever fixed data the + access method requires, plus entries for all tuples already existing + in the table. Ordinarily the ambuild function will call + IndexBuildHeapScan() to scan the table for existing tuples + and compute the keys that need to be inserted into the index. + + + + +bool +aminsert (Relation indexRelation, + Datum *values, + bool *isnull, + ItemPointer heap_tid, + Relation heapRelation, + bool check_uniqueness); + + Insert a new tuple into an existing index. The values and + isnull arrays give the key values to be indexed, and + heap_tid is the TID to be indexed. + If the access method supports unique indexes (its + pg_am.amcanunique flag is true) then + check_uniqueness may be true, in which case the access method + must verify that there is no conflicting row; this is the only situation in + which the access method normally needs the heapRelation + parameter. See for details. + The result is TRUE if an index entry was inserted, FALSE if not. (A FALSE + result does not denote an error condition, but is used for cases such + as an index AM refusing to index a NULL.) + + + + +IndexBulkDeleteResult * +ambulkdelete (Relation indexRelation, + IndexBulkDeleteCallback callback, + void *callback_state); + + Delete tuple(s) from the index. This is a bulk delete operation + that is intended to be implemented by scanning the whole index and checking + each entry to see if it should be deleted. + The passed-in callback function may be called, in the style + callback(TID, callback_state) returns bool, + to determine whether any particular index entry, as identified by its + referenced TID, is to be deleted. Must return either NULL or a palloc'd + struct containing statistics about the effects of the deletion operation. + + + + +IndexBulkDeleteResult * +amvacuumcleanup (Relation indexRelation, + IndexVacuumCleanupInfo *info, + IndexBulkDeleteResult *stats); + + Clean up after a VACUUM operation (one or more + ambulkdelete calls). An index access method does not have + to provide this function (if so, the entry in pg_am must + be zero). If it is provided, it is typically used for bulk cleanup + such as reclaiming empty index pages. info + provides some additional arguments such as a message level for statistical + reports, and stats is whatever the last + ambulkdelete call returned. amvacuumcleanup + may replace or modify this struct before returning it. If the result + is not NULL it must be a palloc'd struct. The statistics it contains + will be reported by VACUUM if VERBOSE is given. + + + + The purpose of an index, of course, is to support scans for tuples matching + an indexable WHERE condition, often called a + qualifier or scan key. The semantics of + index scanning are described more fully in , + below. The scan-related functions that an index access method must provide + are: + + + + +IndexScanDesc +ambeginscan (Relation indexRelation, + int nkeys, + ScanKey key); + + Begin a new scan. The key array (of length nkeys) + describes the scan key(s) for the index scan. The result must be a + palloc'd struct. For implementation reasons the index access method + must create this struct by calling + RelationGetIndexScan(). In most cases + ambeginscan itself does little beyond making that call; + the interesting parts of index-scan startup are in amrescan. + + + + +boolean +amgettuple (IndexScanDesc scan, + ScanDirection direction); + + Fetch the next tuple in the given scan, moving in the given + direction (forward or backward in the index). Returns TRUE if a tuple was + obtained, FALSE if no matching tuples remain. In the TRUE case the tuple + TID is stored into the scan structure. Note that + success means only that the index contains an entry that matches + the scan keys, not that the tuple necessarily still exists in the heap or + will pass the caller's snapshot test. + + + + +boolean +amgetmulti (IndexScanDesc scan, + ItemPointer tids, + int32 max_tids, + int32 *returned_tids); + + Fetch multiple tuples in the given scan. Returns TRUE if the scan should + continue, FALSE if no matching tuples remain. tids points to + a caller-supplied array of max_tids + ItemPointerData records, which the call fills with TIDs of + matching tuples. *returned_tids is set to the number of TIDs + actually returned. This can be less than max_tids, or even + zero, even when the return value is TRUE. (This provision allows the + access method to choose the most efficient stopping points in its scan, + for example index page boundaries.) amgetmulti and + amgettuple cannot be used in the same index scan; there + are other restrictions too when using amgetmulti, as explained + in . + + + + +void +amrescan (IndexScanDesc scan, + ScanKey key); + + Restart the given scan, possibly with new scan keys (to continue using + the old keys, NULL is passed for key). Note that it is not + possible for the number of keys to be changed. In practice the restart + feature is used when a new outer tuple is selected by a nested-loop join + and so a new key comparison value is needed, but the scan key structure + remains the same. This function is also called by + RelationGetIndexScan(), so it is used for initial setup + of an index scan as well as rescanning. + + + + +void +amendscan (IndexScanDesc scan); + + End a scan and release resources. The scan struct itself + should not be freed, but any locks or pins taken internally by the + access method must be released. + + + + +void +ammarkpos (IndexScanDesc scan); + + Mark current scan position. The access method need only support one + remembered scan position per scan. + + + + +void +amrestrpos (IndexScanDesc scan); + + Restore the scan to the most recently marked position. + + + + +void +amcostestimate (PlannerInfo *root, + IndexOptInfo *index, + List *indexQuals, + Cost *indexStartupCost, + Cost *indexTotalCost, + Selectivity *indexSelectivity, + double *indexCorrelation); + + Estimate the costs of an index scan. This function is described fully + in , below. + + + + By convention, the pg_proc entry for any index + access method function should show the correct number of arguments, + but declare them all as type internal (since most of the arguments + have types that are not known to SQL, and we don't want users calling + the functions directly anyway). The return type is declared as + void, internal, or boolean as appropriate. + + + + + + Index Scanning + + + In an index scan, the index access method is responsible for regurgitating + the TIDs of all the tuples it has been told about that match the + scan keys. The access method is not involved in + actually fetching those tuples from the index's parent table, nor in + determining whether they pass the scan's time qualification test or other + conditions. + + + + A scan key is the internal representation of a WHERE clause of + the form index_key operator + constant, where the index key is one of the columns of the + index and the operator is one of the members of the operator class + associated with that index column. An index scan has zero or more scan + keys, which are implicitly ANDed — the returned tuples are expected + to satisfy all the indicated conditions. + + + + The operator class may indicate that the index is lossy for a + particular operator; this implies that the index scan will return all the + entries that pass the scan key, plus possibly additional entries that do + not. The core system's index-scan machinery will then apply that operator + again to the heap tuple to verify whether or not it really should be + selected. For non-lossy operators, the index scan must return exactly the + set of matching entries, as there is no recheck. + + + + Note that it is entirely up to the access method to ensure that it + correctly finds all and only the entries passing all the given scan keys. + Also, the core system will simply hand off all the WHERE + clauses that match the index keys and operator classes, without any + semantic analysis to determine whether they are redundant or + contradictory. As an example, given + WHERE x > 4 AND x > 14 where x is a b-tree + indexed column, it is left to the b-tree amrescan function + to realize that the first scan key is redundant and can be discarded. + The extent of preprocessing needed during amrescan will + depend on the extent to which the index access method needs to reduce + the scan keys to a normalized form. + + + + The amgettuple function has a direction argument, + which can be either ForwardScanDirection (the normal case) + or BackwardScanDirection. If the first call after + amrescan specifies BackwardScanDirection, then the + set of matching index entries is to be scanned back-to-front rather than in + the normal front-to-back direction, so amgettuple must return + the last matching tuple in the index, rather than the first one as it + normally would. (This will only occur for access + methods that advertise they support ordered scans by setting + pg_am.amorderstrategy nonzero.) After the + first call, amgettuple must be prepared to advance the scan in + either direction from the most recently returned entry. + + + + The access method must support marking a position in a scan + and later returning to the marked position. The same position may be + restored multiple times. However, only one position need be remembered + per scan; a new ammarkpos call overrides the previously + marked position. + + + + Both the scan position and the mark position (if any) must be maintained + consistently in the face of concurrent insertions or deletions in the + index. It is OK if a freshly-inserted entry is not returned by a scan that + would have found the entry if it had existed when the scan started, or for + the scan to return such an entry upon rescanning or backing + up even though it had not been returned the first time through. Similarly, + a concurrent delete may or may not be reflected in the results of a scan. + What is important is that insertions or deletions not cause the scan to + miss or multiply return entries that were not themselves being inserted or + deleted. (For an index type that does not set + pg_am.amconcurrent, it is sufficient to + handle these cases for insertions or deletions performed by the same + backend that's doing the scan. But when amconcurrent is + true, insertions or deletions from other backends must be handled as well.) + + + + Instead of using amgettuple, an index scan can be done with + amgetmulti to fetch multiple tuples per call. This can be + noticeably more efficient than amgettuple because it allows + avoiding lock/unlock cycles within the access method. In principle + amgetmulti should have the same effects as repeated + amgettuple calls, but we impose several restrictions to + simplify matters. In the first place, amgetmulti does not + take a direction argument, and therefore it does not support + backwards scan nor intrascan reversal of direction. The access method + need not support marking or restoring scan positions during an + amgetmulti scan, either. (These restrictions cost little + since it would be difficult to use these features in an + amgetmulti scan anyway: adjusting the caller's buffered + list of TIDs would be complex.) Finally, amgetmulti does + not guarantee any locking of the returned tuples, with implications + spelled out in . + + + + + + Index Locking Considerations + + + An index access method can choose whether it supports concurrent updates + of the index by multiple processes. If the method's + pg_am.amconcurrent flag is true, then + the core PostgreSQL system obtains + AccessShareLock on the index during an index scan, and + RowExclusiveLock when updating the index. Since these lock + types do not conflict, the access method is responsible for handling any + fine-grained locking it may need. An exclusive lock on the index as a whole + will be taken only during index creation, destruction, or + REINDEX. When amconcurrent is false, + PostgreSQL still obtains + AccessShareLock during index scans, but it obtains + AccessExclusiveLock during any update. This ensures that + updaters have sole use of the index. Note that this implicitly assumes + that index scans are read-only; an access method that might modify the + index during a scan will still have to do its own locking to handle the + case of concurrent scans. + + + + Recall that a backend's own locks never conflict; therefore, even a + non-concurrent index type must be prepared to handle the case where + a backend is inserting or deleting entries in an index that it is itself + scanning. (This is of course necessary to support an UPDATE + that uses the index to find the rows to be updated.) + + + + Building an index type that supports concurrent updates usually requires + extensive and subtle analysis of the required behavior. For the b-tree + and hash index types, you can read about the design decisions involved in + src/backend/access/nbtree/README and + src/backend/access/hash/README. + + + + Aside from the index's own internal consistency requirements, concurrent + updates create issues about consistency between the parent table (the + heap) and the index. Because + PostgreSQL separates accesses + and updates of the heap from those of the index, there are windows in + which the index may be inconsistent with the heap. We handle this problem + with the following rules: + + + + + A new heap entry is made before making its index entries. (Therefore + a concurrent index scan is likely to fail to see the heap entry. + This is okay because the index reader would be uninterested in an + uncommitted row anyway. But see .) + + + + + When a heap entry is to be deleted (by VACUUM), all its + index entries must be removed first. + + + + + For concurrent index types, an index scan must maintain a pin + on the index page holding the item last returned by + amgettuple, and ambulkdelete cannot delete + entries from pages that are pinned by other backends. The need + for this rule is explained below. + + + + + If an index is concurrent then it is possible for an index reader to + see an index entry just before it is removed by VACUUM, and + then to arrive at the corresponding heap entry after that was removed by + VACUUM. (With a nonconcurrent index, this is not possible + because of the conflicting index-level locks that will be taken out.) + This creates no serious problems if that item + number is still unused when the reader reaches it, since an empty + item slot will be ignored by heap_fetch(). But what if a + third backend has already re-used the item slot for something else? + When using an MVCC-compliant snapshot, there is no problem because + the new occupant of the slot is certain to be too new to pass the + snapshot test. However, with a non-MVCC-compliant snapshot (such as + SnapshotNow), it would be possible to accept and return + a row that does not in fact match the scan keys. We could defend + against this scenario by requiring the scan keys to be rechecked + against the heap row in all cases, but that is too expensive. Instead, + we use a pin on an index page as a proxy to indicate that the reader + may still be in flight from the index entry to the matching + heap entry. Making ambulkdelete block on such a pin ensures + that VACUUM cannot delete the heap entry before the reader + is done with it. This solution costs little in run time, and adds blocking + overhead only in the rare cases where there actually is a conflict. + + + + This solution requires that index scans be synchronous: we have + to fetch each heap tuple immediately after scanning the corresponding index + entry. This is expensive for a number of reasons. An + asynchronous scan in which we collect many TIDs from the index, + and only visit the heap tuples sometime later, requires much less index + locking overhead and may allow a more efficient heap access pattern. + Per the above analysis, we must use the synchronous approach for + non-MVCC-compliant snapshots, but an asynchronous scan is workable + for a query using an MVCC snapshot. + + + + In an amgetmulti index scan, the access method need not + guarantee to keep an index pin on any of the returned tuples. (It would be + impractical to pin more than the last one anyway.) Therefore + it is only safe to use such scans with MVCC-compliant snapshots. + + + + + + Index Uniqueness Checks + + + PostgreSQL enforces SQL uniqueness constraints + using unique indexes, which are indexes that disallow + multiple entries with identical keys. An access method that supports this + feature sets pg_am.amcanunique true. + (At present, only b-tree supports it.) + + + + Because of MVCC, it is always necessary to allow duplicate entries to + exist physically in an index: the entries might refer to successive + versions of a single logical row. The behavior we actually want to + enforce is that no MVCC snapshot could include two rows with equal + index keys. This breaks down into the following cases that must be + checked when inserting a new row into a unique index: + + + + + If a conflicting valid row has been deleted by the current transaction, + it's okay. (In particular, since an UPDATE always deletes the old row + version before inserting the new version, this will allow an UPDATE on + a row without changing the key.) + + + + + If a conflicting row has been inserted by an as-yet-uncommitted + transaction, the would-be inserter must wait to see if that transaction + commits. If it rolls back then there is no conflict. If it commits + without deleting the conflicting row again, there is a uniqueness + violation. (In practice we just wait for the other transaction to + end and then redo the visibility check in toto.) + + + + + Similarly, if a conflicting valid row has been deleted by an + as-yet-uncommitted transaction, the would-be inserter must wait + for that transaction to commit or abort, and then repeat the test. + + + + + + + We require the index access method to apply these tests itself, which + means that it must reach into the heap to check the commit status of + any row that is shown to have a duplicate key according to the index + contents. This is without a doubt ugly and non-modular, but it saves + redundant work: if we did a separate probe then the index lookup for + a conflicting row would be essentially repeated while finding the place to + insert the new row's index entry. What's more, there is no obvious way + to avoid race conditions unless the conflict check is an integral part + of insertion of the new index entry. + + + + The main limitation of this scheme is that it has no convenient way + to support deferred uniqueness checks. + + + + + + Index Cost Estimation Functions + + + The amcostestimate function is given a list of WHERE clauses that have + been determined to be usable with the index. It must return estimates + of the cost of accessing the index and the selectivity of the WHERE + clauses (that is, the fraction of parent-table rows that will be + retrieved during the index scan). For simple cases, nearly all the + work of the cost estimator can be done by calling standard routines + in the optimizer; the point of having an amcostestimate function is + to allow index access methods to provide index-type-specific knowledge, + in case it is possible to improve on the standard estimates. + + + + Each amcostestimate function must have the signature: + + +void +amcostestimate (PlannerInfo *root, + IndexOptInfo *index, + List *indexQuals, + Cost *indexStartupCost, + Cost *indexTotalCost, + Selectivity *indexSelectivity, + double *indexCorrelation); + + + The first four parameters are inputs: + + + + root + + + The planner's information about the query being processed. + + + + + + index + + + The index being considered. + + + + + + indexQuals + + + List of index qual clauses (implicitly ANDed); + a NIL list indicates no qualifiers are available. + Note that the list contains expression trees, not ScanKeys. + + + + + + + + The last four parameters are pass-by-reference outputs: + + + + *indexStartupCost + + + Set to cost of index start-up processing + + + + + + *indexTotalCost + + + Set to total cost of index processing + + + + + + *indexSelectivity + + + Set to index selectivity + + + + + + *indexCorrelation + + + Set to correlation coefficient between index scan order and + underlying table's order + + + + + + + + Note that cost estimate functions must be written in C, not in SQL or + any available procedural language, because they must access internal + data structures of the planner/optimizer. + + + + The index access costs should be computed in the units used by + src/backend/optimizer/path/costsize.c: a sequential + disk block fetch has cost 1.0, a nonsequential fetch has cost + random_page_cost, and the cost of processing one index row + should usually be taken as cpu_index_tuple_cost. In addition, + an appropriate multiple of cpu_operator_cost should be charged + for any comparison operators invoked during index processing (especially + evaluation of the indexQuals themselves). + + + + The access costs should include all disk and CPU costs associated with + scanning the index itself, but not the costs of retrieving or + processing the parent-table rows that are identified by the index. + + + + The start-up cost is the part of the total scan cost that must be expended + before we can begin to fetch the first row. For most indexes this can + be taken as zero, but an index type with a high start-up cost might want + to set it nonzero. + + + + The indexSelectivity should be set to the estimated fraction of the parent + table rows that will be retrieved during the index scan. In the case + of a lossy index, this will typically be higher than the fraction of + rows that actually pass the given qual conditions. + + + + The indexCorrelation should be set to the correlation (ranging between + -1.0 and 1.0) between the index order and the table order. This is used + to adjust the estimate for the cost of fetching rows from the parent + table. + + + + Cost Estimation + + A typical cost estimator will proceed as follows: + + + + + Estimate and return the fraction of parent-table rows that will be visited + based on the given qual conditions. In the absence of any index-type-specific + knowledge, use the standard optimizer function clauselist_selectivity(): + + +*indexSelectivity = clauselist_selectivity(root, indexQuals, + index->rel->relid, JOIN_INNER); + + + + + + + Estimate the number of index rows that will be visited during the + scan. For many index types this is the same as indexSelectivity times + the number of rows in the index, but it might be more. (Note that the + index's size in pages and rows is available from the IndexOptInfo struct.) + + + + + + Estimate the number of index pages that will be retrieved during the scan. + This might be just indexSelectivity times the index's size in pages. + + + + + + Compute the index access cost. A generic estimator might do this: + + + /* + * Our generic assumption is that the index pages will be read + * sequentially, so they have cost 1.0 each, not random_page_cost. + * Also, we charge for evaluation of the indexquals at each index row. + * All the costs are assumed to be paid incrementally during the scan. + */ + cost_qual_eval(&index_qual_cost, indexQuals); + *indexStartupCost = index_qual_cost.startup; + *indexTotalCost = numIndexPages + + (cpu_index_tuple_cost + index_qual_cost.per_tuple) * numIndexTuples; + + + + + + + Estimate the index correlation. For a simple ordered index on a single + field, this can be retrieved from pg_statistic. If the correlation + is not known, the conservative estimate is zero (no correlation). + + + + + + Examples of cost estimator functions can be found in + src/backend/utils/adt/selfuncs.c. + + + + + diff --git a/doc/src/sgml/planstats.sgml b/doc/src/sgml/planstats.sgml new file mode 100644 index 0000000..0190708 --- /dev/null +++ b/doc/src/sgml/planstats.sgml @@ -0,0 +1,370 @@ + + + + How the Planner Uses Statistics + + + This chapter builds on the material covered in + and , and shows how the planner uses the + system statistics to estimate the number of rows each stage in a query might + return. This is a significant part of the planning / optimizing process, + providing much of the raw material for cost calculation. + + + + The intent of this chapter is not to document the code — + better done in the code itself, but to present an overview of how it works. + This will perhaps ease the learning curve for someone who subsequently + wishes to read the code. As a consequence, the approach chosen is to analyze + a series of incrementally more complex examples. + + + + The outputs and algorithms shown below are taken from version 8.0. + The behavior of earlier (or later) versions may vary. + + + + Row Estimation Examples + + + row estimation + planner + + + + Using examples drawn from the regression test database, let's start with a + very simple query: + +EXPLAIN SELECT * FROM tenk1; + + QUERY PLAN +------------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..445.00 rows=10000 width=244) + + + How the planner determines the cardinality of tenk1 + is covered in , but is repeated here for + completeness. The number of rows is looked up from + pg_class: + + +SELECT reltuples, relpages FROM pg_class WHERE relname = 'tenk1'; + + relpages | reltuples +----------+----------- + 345 | 10000 + + The planner will check the relpages + estimate (this is a cheap operation) and if incorrect may scale + reltuples to obtain a row estimate. In this + case it does not, thus: + + +rows = 10000 + + + + + + let's move on to an example with a range condition in its + WHERE clause: + + +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000; + + QUERY PLAN +------------------------------------------------------------ + Seq Scan on tenk1 (cost=0.00..470.00 rows=1031 width=244) + Filter: (unique1 < 1000) + + + The planner examines the WHERE clause condition: + + +unique1 < 1000 + + + and looks up the restriction function for the operator + < in pg_operator. + This is held in the column oprrest, + and the result in this case is scalarltsel. + The scalarltsel function retrieves the histogram for + unique1 from pg_statistics + - we can follow this by using the simpler pg_stats + view: + + +SELECT histogram_bounds FROM pg_stats +WHERE tablename='tenk1' AND attname='unique1'; + + histogram_bounds +------------------------------------------------------ + {1,970,1943,2958,3971,5069,6028,7007,7919,8982,9995} + + + Next the fraction of the histogram occupied by < 1000 + is worked out. This is the selectivity. The histogram divides the range + into equal frequency buckets, so all we have to do is locate the bucket + that our value is in and count part of it and + all of the ones before. The value 1000 is clearly in + the second (970 - 1943) bucket, so by assuming a linear distribution of + values inside each bucket we can calculate the selectivity as: + + +selectivity = (1 + (1000 - bckt[2].min)/(bckt[2].max - bckt[2].min))/num_bckts + = (1 + (1000 - 970)/(1943 - 970))/10 + = 0.1031 + + + that is, one whole bucket plus a linear fraction of the second, divided by + the number of buckets. The estimated number of rows can now be calculated as + the product of the selectivity and the cardinality of + tenk1: + + +rows = rel_cardinality * selectivity + = 10000 * 0.1031 + = 1031 + + + + + + Next let's consider an example with equality condition in its + WHERE clause: + + +EXPLAIN SELECT * FROM tenk1 WHERE stringu1 = 'ATAAAA'; + + QUERY PLAN +---------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..470.00 rows=31 width=244) + Filter: (stringu1 = 'ATAAAA'::name) + + + Again the planner examines the WHERE clause condition: + + +stringu1 = 'ATAAAA' + + + and looks up the restriction function for =, which is + eqsel. This case is a bit different, as the most + common values — MCVs, are used to determine the + selectivity. Let's have a look at these, with some extra columns that will + be useful later: + + +SELECT null_frac, n_distinct, most_common_vals, most_common_freqs FROM pg_stats +WHERE tablename='tenk1' AND attname='stringu1'; + +null_frac | 0 +n_distinct | 672 +most_common_vals | {FDAAAA,NHAAAA,ATAAAA,BGAAAA,EBAAAA,MOAAAA,NDAAAA,OWAAAA,BHAAAA,BJAAAA} +most_common_freqs | {0.00333333,0.00333333,0.003,0.003,0.003,0.003,0.003,0.003,0.00266667,0.00266667} + + + The selectivity is merely the most common frequency (MCF) + corresponding to the third MCV — 'ATAAAA': + + +selectivity = mcf[3] + = 0.003 + + + The estimated number of rows is just the product of this with the + cardinality of tenk1 as before: + + +rows = 10000 * 0.003 + = 30 + + + The number displayed by EXPLAIN is one more than this, + due to some post estimation checks. + + + + Now consider the same query, but with a constant that is not in the + MCV list: + + +EXPLAIN SELECT * FROM tenk1 WHERE stringu1 = 'xxx'; + + QUERY PLAN +---------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..470.00 rows=15 width=244) + Filter: (stringu1 = 'xxx'::name) + + + This is quite a different problem, how to estimate the selectivity when the + value is not in the MCV list. + The approach is to use the fact that the value is not in the list, + combined with the knowledge of the frequencies for all of the + MCVs: + + +selectivity = (1 - sum(mvf))/(num_distinct - num_mcv) + = (1 - (0.00333333 + 0.00333333 + 0.003 + 0.003 + 0.003 + + 0.003 + 0.003 + 0.003 + 0.00266667 + 0.00266667))/(672 - 10) + = 0.001465 + + + That is, add up all the frequencies for the MCVs and + subtract them from one — because it is not one + of these, and divide by the remaining distinct values. + Notice that there are no null values so we don't have to worry about those. + The estimated number of rows is calculated as usual: + + +rows = 10000 * 0.001465 + = 15 + + + + + + Let's increase the complexity to consider a case with more than one + condition in the WHERE clause: + + +EXPLAIN SELECT * FROM tenk1 WHERE unique1 < 1000 AND stringu1 = 'xxx'; + + QUERY PLAN +----------------------------------------------------------- + Seq Scan on tenk1 (cost=0.00..495.00 rows=2 width=244) + Filter: ((unique1 < 1000) AND (stringu1 = 'xxx'::name)) + + + An assumption of independence is made and the selectivities of the + individual restrictions are multiplied together: + + +selectivity = selectivity(unique1 < 1000) * selectivity(stringu1 = 'xxx') + = 0.1031 * 0.001465 + = 0.00015104 + + + The row estimates are calculated as before: + + +rows = 10000 * 0.00015104 + = 2 + + + + + Finally we will examine a query that includes a JOIN + together with a WHERE clause: + + +EXPLAIN SELECT * FROM tenk1 t1, tenk2 t2 +WHERE t1.unique1 < 50 AND t1.unique2 = t2.unique2; + + QUERY PLAN +----------------------------------------------------------------------------------------- + Nested Loop (cost=0.00..346.90 rows=51 width=488) + -> Index Scan using tenk1_unique1 on tenk1 t1 (cost=0.00..192.57 rows=51 width=244) + Index Cond: (unique1 < 50) + -> Index Scan using tenk2_unique2 on tenk2 t2 (cost=0.00..3.01 rows=1 width=244) + Index Cond: ("outer".unique2 = t2.unique2) + + + The restriction on tenk1 + unique1 < 50 is evaluated before the nested-loop join. + This is handled analogously to the previous range example. The restriction + operator for < is scalarlteqsel + as before, but this time the value 50 is in the first bucket of the + unique1 histogram: + + +selectivity = (0 + (50 - bckt[1].min)/(bckt[1].max - bckt[1].min))/num_bckts + = (0 + (50 - 1)/(970 - 1))/10 + = 0.005057 + +rows = 10000 * 0.005057 + = 51 + + + The restriction for the join is: + + +t2.unique2 = t1.unique2 + + + This is due to the join method being nested-loop, with + tenk1 being in the outer loop. The operator is just + our familiar =, however the restriction function is + obtained from the oprjoin column of + pg_operator - and is eqjoinsel. + Additionally we use the statistical information for both + tenk2 and tenk1: + + +SELECT tablename, null_frac,n_distinct, most_common_vals FROM pg_stats +WHERE tablename IN ('tenk1', 'tenk2') AND attname='unique2'; + +tablename | null_frac | n_distinct | most_common_vals +-----------+-----------+------------+------------------ + tenk1 | 0 | -1 | + tenk2 | 0 | -1 | + + + In this case there is no MCV information for + unique2 because all the values appear to be + unique, so we can use an algorithm that relies only on the number of + distinct values for both relations together with their null fractions: + + +selectivity = (1 - null_frac1) * (1 - null_frac2) * min(1/num_distinct1, 1/num_distinct2) + = (1 - 0) * (1 - 0) * min(1/10000, 1/1000) + = 0.0001 + + + This is, subtract the null fraction from one for each of the relations, + and divide by the maximum of the two distinct values. The number of rows + that the join is likely to emit is calculated as the cardinality of + Cartesian product of the two nodes in the nested-loop, multiplied by the + selectivity: + + +rows = (outer_cardinality * inner_cardinality) * selectivity + = (51 * 10000) * 0.0001 + = 51 + + + + + For those interested in further details, estimation of the number of rows in + a relation is covered in + src/backend/optimizer/util/plancat.c. The calculation + logic for clause selectivities is in + src/backend/optimizer/path/clausesel.c. The actual + implementations of the operator and join restriction functions can be found + in src/backend/utils/adt/selfuncs.c. + + + + + + + + diff --git a/doc/src/sgml/ref/alter_role.sgml b/doc/src/sgml/ref/alter_role.sgml new file mode 100644 index 0000000..d8384c0 --- /dev/null +++ b/doc/src/sgml/ref/alter_role.sgml @@ -0,0 +1,274 @@ + + + + + ALTER ROLE + SQL - Language Statements + + + + ALTER ROLE + change a database role + + + + ALTER ROLE + + + + +ALTER ROLE name [ [ WITH ] option [ ... ] ] + +where option can be: + + SUPERUSER | NOSUPERUSER + | CREATEDB | NOCREATEDB + | CREATEROLE | NOCREATEROLE + | CREATEUSER | NOCREATEUSER + | INHERIT | NOINHERIT + | LOGIN | NOLOGIN + | CONNECTION LIMIT connlimit + | [ ENCRYPTED | UNENCRYPTED ] PASSWORD 'password' + | VALID UNTIL 'timestamp' + +ALTER ROLE name RENAME TO newname + +ALTER ROLE name SET parameter { TO | = } { value | DEFAULT } +ALTER ROLE name RESET parameter + + + + + Description + + + ALTER ROLE changes the attributes of a + PostgreSQL role. + + + + The first variant of this command listed in the synopsis can change + many of the role attributes that can be specified in + , + which see for details. (All the possible attributes are covered, + except that there are no options for adding or removing memberships; use + and + for that.) + Attributes not mentioned in the command retain their previous settings. + Database superusers can change any of these settings for any role. + Roles having CREATEROLE privilege can change any of these + settings, but only for non-superuser roles. + Ordinary roles can only change their own password. + + + + The second variant changes the name of the role. + Database superusers can rename any role. + Roles having CREATEROLE privilege can rename non-superuser + roles. + The current session user cannot be renamed. + (Connect as a different user if you need to do that.) + Because MD5-encrypted passwords use the role name as + cryptographic salt, renaming a role clears its password if the + password is MD5-encrypted. + + + + The third and the fourth variant change a role's session default for + a specified configuration variable. Whenever the role subsequently + starts a new session, the specified value becomes the session default, + overriding whatever setting is present in postgresql.conf + or has been received from the postmaster command line. + (For a role without LOGIN privilege, session defaults have + no effect.) + Ordinary roles can change their own session defaults. + Superusers can change anyone's session defaults. + Roles having CREATEROLE privilege can change defaults for + non-superuser roles. + Certain variables cannot be set this way, or can only be + set if a superuser issues the command. + + + + + Parameters + + + + name + + + The name of the role whose attributes are to be altered. + + + + + + SUPERUSER + NOSUPERUSER + CREATEDB + NOCREATEDB + CREATEROLE + NOCREATEROLE + CREATEUSER + NOCREATEUSER + INHERIT + NOINHERIT + LOGIN + NOLOGIN + CONNECTION LIMIT connlimit + PASSWORD password + ENCRYPTED + UNENCRYPTED + VALID UNTIL 'timestamp' + + + These clauses alter attributes originally set by + , + which see for more information. + + + + + + newname + + + The new name of the role. + + + + + + parameter + value + + + Set this role's session default for the specified configuration + parameter to the given value. If + value is DEFAULT + or, equivalently, RESET is used, the + role-specific variable setting is removed, so the role will + inherit the system-wide default setting in new sessions. Use + RESET ALL to clear all role-specific settings. + + + + See and for more information about allowed + parameter names and values. + + + + + + + + Notes + + + Use + to add new roles, and to remove a role. + + + + ALTER ROLE cannot change a role's memberships. + Use and + + to do that. + + + + It is also possible to tie a + session default to a specific database rather than to a role; see + . + Role-specific settings override database-specific + ones if there is a conflict. + + + + + Examples + + + Change a role's password: + + +ALTER ROLE davide WITH PASSWORD 'hu8jmn3'; + + + + + Change a password expiration date, specifying that the password + should expire at midday on 4th May 2015 using + the time zone which is one hour ahead of UTC: + +ALTER ROLE chris VALID UNTIL 'May 4 12:00:00 2015 +1'; + + + + + Make a password valid forever: + +ALTER ROLE fred VALID UNTIL 'infinity'; + + + + + Give a role the ability to create other roles and new databases: + + +ALTER ROLE miriam CREATEROLE CREATEDB; + + + + + Give a role a non-default setting of the + parameter: + + +ALTER ROLE worker_bee SET maintenance_work_mem = 100000; + + + + + + Compatibility + + + The ALTER ROLE statement is a + PostgreSQL extension. + + + + + See Also + + + + + + + + + + diff --git a/doc/src/sgml/ref/commit_prepared.sgml b/doc/src/sgml/ref/commit_prepared.sgml new file mode 100644 index 0000000..73c6c87 --- /dev/null +++ b/doc/src/sgml/ref/commit_prepared.sgml @@ -0,0 +1,111 @@ + + + + + COMMIT PREPARED + SQL - Language Statements + + + + COMMIT PREPARED + commit a transaction that was earlier prepared for two-phase commit + + + + COMMIT PREPARED + + + + +COMMIT PREPARED transaction_id + + + + + Description + + + COMMIT PREPARED commits a transaction that is in + prepared state. + + + + + Parameters + + + + transaction_id + + + The transaction identifier of the transaction that is to be + committed. + + + + + + + + Notes + + + To commit a prepared transaction, you must be either the same user that + executed the transaction originally, or a superuser. But you do not + have to be in the same session that executed the transaction. + + + + This command cannot be executed inside a transaction block. The prepared + transaction is committed immediately. + + + + All currently available prepared transactions are listed in the + pg_prepared_xacts system view. + + + + + Examples + + Commit the transaction identified by the transaction + identifier foobar: + + +COMMIT PREPARED 'foobar'; + + + + + + + See Also + + + + + + + + + + diff --git a/doc/src/sgml/ref/drop_role.sgml b/doc/src/sgml/ref/drop_role.sgml new file mode 100644 index 0000000..9fb7cc2 --- /dev/null +++ b/doc/src/sgml/ref/drop_role.sgml @@ -0,0 +1,126 @@ + + + + + DROP ROLE + SQL - Language Statements + + + + DROP ROLE + remove a database role + + + + DROP ROLE + + + + +DROP ROLE name [, ...] + + + + + Description + + + DROP ROLE removes the specified role(s). + To drop a superuser role, you must be a superuser yourself; + to drop non-superuser roles, you must have CREATEROLE + privilege. + + + + A role cannot be removed if it is still referenced in any database + of the cluster; an error will be raised if so. Before dropping the role, + you must drop all the objects it owns (or reassign their ownership) + and revoke any privileges the role has been granted. + + + + However, it is not necessary to remove role memberships involving + the role; DROP ROLE automatically revokes any memberships + of the target role in other roles, and of other roles in the target role. + The other roles are not dropped nor otherwise affected. + + + + + Parameters + + + + name + + + The name of the role to remove. + + + + + + + + Notes + + + PostgreSQL includes a program that has the + same functionality as this command (in fact, it calls this command) + but can be run from the command shell. + + + + + Examples + + + To drop a role: + +DROP ROLE jonathan; + + + + + + Compatibility + + + The SQL standard defines DROP ROLE, but it allows + only one role to be dropped at a time, and it specifies different + privilege requirements than PostgreSQL uses. + + + + + See Also + + + + + + + + + + + diff --git a/doc/src/sgml/ref/reindexdb.sgml b/doc/src/sgml/ref/reindexdb.sgml new file mode 100644 index 0000000..d52fe6a --- /dev/null +++ b/doc/src/sgml/ref/reindexdb.sgml @@ -0,0 +1,294 @@ + + + + + reindexdb + 1 + Application + + + + reindexdb + reindex a PostgreSQL database + + + + reindexdb + + + + + reindexdb + connection-option + --table | -t table + --index | -i index + dbname + + reindexdb + connection-option + --all | -a + + reindexdb + connection-option + --system | -s + dbname + + + + + + Description + + + reindexdb is a utility for rebuilding indexes + in a PostgreSQL database. + + + + reindexdb is a wrapper around the SQL + command . + There is no effective difference between reindexing databases via + this utility and via other methods for accessing the server. + + + + + + + Options + + + reindexdb accepts the following command-line arguments: + + + + + + + + Reindex all databases. + + + + + + + + + + Reindex database's system catalogs. + + + + + + + + + + Reindex table only. + + + + + + + + + + Recreate index only. + + + + + + + + + + Specifies the name of the database to be reindexed. + If this is not specified and (or + ) is not used, the database name is read + from the environment variable PGDATABASE. If + that is not set, the user name specified for the connection is + used. + + + + + + + + + + Echo the commands that reindexdb generates + and sends to the server. + + + + + + + + + + Do not display a response. + + + + + + + + + reindexdb also accepts + the following command-line arguments for connection parameters: + + + + + + + + Specifies the host name of the machine on which the server is + running. If the value begins with a slash, it is used as the + directory for the Unix domain socket. + + + + + + + + + + Specifies the TCP port or local Unix domain socket file + extension on which the server + is listening for connections. + + + + + + + + + + User name to connect as. + + + + + + + + + + Force password prompt. + + + + + + + + + + Environment + + + + PGDATABASE + PGHOST + PGPORT + PGUSER + + + + Default connection parameters + + + + + + + + + Diagnostics + + + In case of difficulty, see and for + discussions of potential problems and error messages. + The database server must be running at the + targeted host. Also, any default connection settings and environment + variables used by the libpq front-end + library will apply. + + + + + + + Notes + + + reindexdb might need to connect several + times to the PostgreSQL server, asking + for a password each time. It is convenient to have a + ~/.pgpass file in such cases. See for more information. + + + + + + Examples + + + To reindex the database test: + +$ reindexdb test + + + + + To reindex the table foo and the index + bar in a database named abcd: + +$ reindexdb --table foo --index bar abcd + + + + + + + See Also + + + + Environment Variables () + + + + + + diff --git a/doc/src/sgml/ref/rollback_prepared.sgml b/doc/src/sgml/ref/rollback_prepared.sgml new file mode 100644 index 0000000..3034c03 --- /dev/null +++ b/doc/src/sgml/ref/rollback_prepared.sgml @@ -0,0 +1,111 @@ + + + + + ROLLBACK PREPARED + SQL - Language Statements + + + + ROLLBACK PREPARED + cancel a transaction that was earlier prepared for two-phase commit + + + + ROLLBACK PREPARED + + + + +ROLLBACK PREPARED transaction_id + + + + + Description + + + ROLLBACK PREPARED rolls back a transaction that is in + prepared state. + + + + + Parameters + + + + transaction_id + + + The transaction identifier of the transaction that is to be + rolled back. + + + + + + + + Notes + + + To roll back a prepared transaction, you must be either the same user that + executed the transaction originally, or a superuser. But you do not + have to be in the same session that executed the transaction. + + + + This command cannot be executed inside a transaction block. The prepared + transaction is rolled back immediately. + + + + All currently available prepared transactions are listed in the + pg_prepared_xacts system view. + + + + + Examples + + Roll back the transaction identified by the transaction + identifier foobar: + + +ROLLBACK PREPARED 'foobar'; + + + + + + + See Also + + + + + + + + + + diff --git a/src/backend/access/gist/README b/src/backend/access/gist/README new file mode 100644 index 0000000..feb18b7 --- /dev/null +++ b/src/backend/access/gist/README @@ -0,0 +1,234 @@ +$PostgreSQL$ + +This directory contains an implementation of GiST indexing for Postgres. + +GiST stands for Generalized Search Tree. It was introduced in the seminal paper +"Generalized Search Trees for Database Systems", 1995, Joseph M. Hellerstein, +Jeffrey F. Naughton, Avi Pfeffer: + + http://www.sai.msu.su/~megera/postgres/gist/papers/gist.ps + +and implemented by J. Hellerstein and P. Aoki in an early version of +PostgreSQL (more details are available from The GiST Indexing Project +at Berkeley at http://gist.cs.berkeley.edu/). As a "university" +project it had a limited number of features and was in rare use. + +The current implementation of GiST supports: + + * Variable length keys + * Composite keys (multi-key) + * provides NULL-safe interface to GiST core + * Concurrency + * Recovery support via WAL logging + +The support for concurrency implemented in PostgreSQL was developed based on +the paper "Access Methods for Next-Generation Database Systems" by +Marcel Kornaker: + + http://www.sai.msu.su/~megera/postgres/gist/papers/concurrency/access-methods-for-next-generation.pdf.gz + +The original algorithms were modified in several ways: + +* They should be adapted to PostgreSQL conventions. For example, the SEARCH + algorithm was considerably changed, because in PostgreSQL function search + should return one tuple (next), not all tuples at once. Also, it should + release page locks between calls. +* Since we added support for variable length keys, it's not possible to + guarantee enough free space for all keys on pages after splitting. User + defined function picksplit doesn't have information about size of tuples + (each tuple may contain several keys as in multicolumn index while picksplit + could work with only one key) and pages. +* We modified original INSERT algorithm for performance reason. In particular, + it is now a single-pass algorithm. +* Since the papers were theoretical, some details were omitted and we + have to find out ourself how to solve some specific problems. + +Because of the above reasons, we have to revised interaction of GiST +core and PostgreSQL WAL system. Moreover, we encountered (and solved) +a problem of uncompleted insertions when recovering after crash, which +was not touched in the paper. + +SEARCH ALGORITHM + +Function gettuple finds a tuple which satisfies the search +predicate. It store their state and returns next tuple under +subsequent calls. Stack contains page, its LSN and LSN of parent page +and currentposition is saved between calls. + +gettuple(search-pred) + if ( firsttime ) + push(stack, [root, 0, 0]) // page, LSN, parentLSN + currentposition=0 + end + ptr = top of stack + while(true) + latch( ptr->page, S-mode ) + if ( ptr->page->lsn != ptr->lsn ) + ptr->lsn = ptr->page->lsn + currentposition=0 + if ( ptr->parentlsn < ptr->page->nsn ) + add to stack rightlink + else + currentposition++ + end + + while(true) + currentposition = find_first_match( currentposition ) + if ( currentposition is invalid ) + unlatch( ptr->page ) + pop stack + ptr = top of stack + if (ptr is NULL) + return NULL + break loop + else if ( ptr->page is leaf ) + unlatch( ptr->page ) + return tuple + else + add to stack child page + end + currentposition++ + end + end + + +INSERT ALGORITHM + +INSERT guarantees that the GiST tree remains balanced. User defined key method +Penalty is used for choosing a subtree to insert; method PickSplit is used for +the node splitting algorithm; method Union is used for propagating changes +upward to maintain the tree properties. + +NOTICE: We modified original INSERT algorithm for performance reason. In +particularly, it is now a single-pass algorithm. + +Function findLeaf is used to identify subtree for insertion. Page, in which +insertion is proceeded, is locked as well as its parent page. Functions +findParent and findPath are used to find parent pages, which could be changed +because of concurrent access. Function pageSplit is reccurrent and could split +page by more than 2 pages, which could be necessary if keys have different +lengths or more than one key are inserted (in such situation, user defined +function pickSplit cannot guarantee free space on page). + +findLeaf(new-key) + push(stack, [root, 0]) //page, LSN + while(true) + ptr = top of stack + latch( ptr->page, S-mode ) + ptr->lsn = ptr->page->lsn + if ( exists ptr->parent AND ptr->parent->lsn < ptr->page->nsn ) + unlatch( ptr->page ) + pop stack + else if ( ptr->page is not leaf ) + push( stack, [get_best_child(ptr->page, new-key), 0] ) + unlatch( ptr->page ) + else + unlatch( ptr->page ) + latch( ptr->page, X-mode ) + if ( ptr->page is not leaf ) + //the only root page can become a non-leaf + unlatch( ptr->page ) + else if ( ptr->parent->lsn < ptr->page->nsn ) + unlatch( ptr->page ) + pop stack + else + return stack + end + end + end + +findPath( stack item ) + push stack, [root, 0, 0] // page, LSN, parent + while( stack ) + ptr = top of stack + latch( ptr->page, S-mode ) + if ( ptr->parent->page->lsn < ptr->page->nsn ) + push stack, [ ptr->page->rightlink, 0, ptr->parent ] + end + for( each tuple on page ) + if ( tuple->pagepointer == item->page ) + return stack + else + add to stack at the end [tuple->pagepointer,0, ptr] + end + end + unlatch( ptr->page ) + pop stack + end + +findParent( stack item ) + parent = item->parent + latch( parent->page, X-mode ) + if ( parent->page->lsn != parent->lsn ) + while(true) + search parent tuple on parent->page, if found the return + rightlink = parent->page->rightlink + unlatch( parent->page ) + if ( rightlink is incorrect ) + break loop + end + parent->page = rightlink + latch( parent->page, X-mode ) + end + newstack = findPath( item->parent ) + replace part of stack to new one + return findParent( item ) + end + +pageSplit(page, allkeys) + (lkeys, rkeys) = pickSplit( allkeys ) + if ( page is root ) + lpage = new page + else + lpage = page + rpage = new page + if ( no space left on rpage ) + newkeys = pageSplit( rpage, rkeys ) + else + push newkeys, union(rkeys) + end + if ( no space left on lpage ) + push newkeys, pageSplit( lpage, lkeys ) + else + push newkeys, union(lkeys) + end + return newkeys + + +placetopage(page, keysarray) + if ( no space left on page ) + keysarray = pageSplit(page, [ extract_keys(page), keysarray]) + last page in chain gets old NSN, + original and others - new NSN equals to LSN + if ( page is root ) + make new root with keysarray + end + else + put keysarray on page + if ( length of keysarray > 1 ) + keysarray = [ union(keysarray) ] + end + end + +insert(new-key) + stack = findLeaf(new-key) + keysarray = [new-key] + ptr = top of stack + while(true) + findParent( ptr ) //findParent latches parent page + keysarray = placetopage(ptr->page, keysarray) + unlatch( ptr->page ) + pop stack; + ptr = top of stack + if (length of keysarray == 1) + newboundingkey = union(oldboundingkey, keysarray) + if (newboundingkey == oldboundingkey) + unlatch ptr->page + break loop + end + end + end + +Authors: + Teodor Sigaev + Oleg Bartunov diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c new file mode 100644 index 0000000..9a47e39 --- /dev/null +++ b/src/backend/access/gist/gistxlog.c @@ -0,0 +1,953 @@ +/*------------------------------------------------------------------------- + * + * gistxlog.c + * WAL replay logic for GiST. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * $PostgreSQL$ + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/gist_private.h" +#include "access/gistscan.h" +#include "access/heapam.h" +#include "catalog/index.h" +#include "commands/vacuum.h" +#include "miscadmin.h" +#include "utils/memutils.h" + + +typedef struct +{ + gistxlogEntryUpdate *data; + int len; + IndexTuple *itup; + OffsetNumber *todelete; +} EntryUpdateRecord; + +typedef struct +{ + gistxlogPage *header; + IndexTuple *itup; +} NewPage; + +typedef struct +{ + gistxlogPageSplit *data; + NewPage *page; +} PageSplitRecord; + +/* track for incomplete inserts, idea was taken from nbtxlog.c */ + +typedef struct gistIncompleteInsert +{ + RelFileNode node; + BlockNumber origblkno; /* for splits */ + ItemPointerData key; + int lenblk; + BlockNumber *blkno; + XLogRecPtr lsn; + BlockNumber *path; + int pathlen; +} gistIncompleteInsert; + + +MemoryContext opCtx; +MemoryContext insertCtx; +static List *incomplete_inserts; + + +#define ItemPointerEQ( a, b ) \ + ( \ + ItemPointerGetOffsetNumber(a) == ItemPointerGetOffsetNumber(b) && \ + ItemPointerGetBlockNumber (a) == ItemPointerGetBlockNumber(b) \ + ) + +static void +pushIncompleteInsert(RelFileNode node, XLogRecPtr lsn, ItemPointerData key, + BlockNumber *blkno, int lenblk, + PageSplitRecord *xlinfo /* to extract blkno info */ ) +{ + MemoryContext oldCxt = MemoryContextSwitchTo(insertCtx); + gistIncompleteInsert *ninsert = (gistIncompleteInsert *) palloc(sizeof(gistIncompleteInsert)); + + ninsert->node = node; + ninsert->key = key; + ninsert->lsn = lsn; + + if (lenblk && blkno) + { + ninsert->lenblk = lenblk; + ninsert->blkno = (BlockNumber *) palloc(sizeof(BlockNumber) * ninsert->lenblk); + memcpy(ninsert->blkno, blkno, sizeof(BlockNumber) * ninsert->lenblk); + ninsert->origblkno = *blkno; + } + else + { + int i; + + Assert(xlinfo); + ninsert->lenblk = xlinfo->data->npage; + ninsert->blkno = (BlockNumber *) palloc(sizeof(BlockNumber) * ninsert->lenblk); + for (i = 0; i < ninsert->lenblk; i++) + ninsert->blkno[i] = xlinfo->page[i].header->blkno; + ninsert->origblkno = xlinfo->data->origblkno; + } + Assert(ninsert->lenblk > 0); + + incomplete_inserts = lappend(incomplete_inserts, ninsert); + MemoryContextSwitchTo(oldCxt); +} + +static void +forgetIncompleteInsert(RelFileNode node, ItemPointerData key) +{ + ListCell *l; + + foreach(l, incomplete_inserts) + { + gistIncompleteInsert *insert = (gistIncompleteInsert *) lfirst(l); + + if (RelFileNodeEquals(node, insert->node) && ItemPointerEQ(&(insert->key), &(key))) + { + + /* found */ + pfree(insert->blkno); + incomplete_inserts = list_delete_ptr(incomplete_inserts, insert); + pfree(insert); + break; + } + } +} + +static void +decodeEntryUpdateRecord(EntryUpdateRecord *decoded, XLogRecord *record) +{ + char *begin = XLogRecGetData(record), + *ptr; + int i = 0, + addpath = 0; + + decoded->data = (gistxlogEntryUpdate *) begin; + + if (decoded->data->ntodelete) + { + decoded->todelete = (OffsetNumber *) (begin + sizeof(gistxlogEntryUpdate) + addpath); + addpath = MAXALIGN(sizeof(OffsetNumber) * decoded->data->ntodelete); + } + else + decoded->todelete = NULL; + + decoded->len = 0; + ptr = begin + sizeof(gistxlogEntryUpdate) + addpath; + while (ptr - begin < record->xl_len) + { + decoded->len++; + ptr += IndexTupleSize((IndexTuple) ptr); + } + + decoded->itup = (IndexTuple *) palloc(sizeof(IndexTuple) * decoded->len); + + ptr = begin + sizeof(gistxlogEntryUpdate) + addpath; + while (ptr - begin < record->xl_len) + { + decoded->itup[i] = (IndexTuple) ptr; + ptr += IndexTupleSize(decoded->itup[i]); + i++; + } +} + +/* + * redo any page update (except page split) + */ +static void +gistRedoEntryUpdateRecord(XLogRecPtr lsn, XLogRecord *record, bool isnewroot) +{ + EntryUpdateRecord xlrec; + Relation reln; + Buffer buffer; + Page page; + + decodeEntryUpdateRecord(&xlrec, record); + + reln = XLogOpenRelation(xlrec.data->node); + if (!RelationIsValid(reln)) + return; + buffer = XLogReadBuffer(false, reln, xlrec.data->blkno); + if (!BufferIsValid(buffer)) + elog(PANIC, "block %u unfound", xlrec.data->blkno); + page = (Page) BufferGetPage(buffer); + + if (isnewroot) + { + if (!PageIsNew((PageHeader) page) && XLByteLE(lsn, PageGetLSN(page))) + { + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + return; + } + } + else + { + if (PageIsNew((PageHeader) page)) + elog(PANIC, "uninitialized page %u", xlrec.data->blkno); + if (XLByteLE(lsn, PageGetLSN(page))) + { + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + return; + } + } + + if (xlrec.data->isemptypage) + { + while (!PageIsEmpty(page)) + PageIndexTupleDelete(page, FirstOffsetNumber); + + if (xlrec.data->blkno == GIST_ROOT_BLKNO) + GistPageSetLeaf(page); + else + GistPageSetDeleted(page); + } + else + { + if (isnewroot) + GISTInitBuffer(buffer, 0); + else if (xlrec.data->ntodelete) + { + int i; + + for (i = 0; i < xlrec.data->ntodelete; i++) + PageIndexTupleDelete(page, xlrec.todelete[i]); + if (GistPageIsLeaf(page)) + GistMarkTuplesDeleted(page); + } + + /* add tuples */ + if (xlrec.len > 0) + gistfillbuffer(reln, page, xlrec.itup, xlrec.len, InvalidOffsetNumber); + + /* + * special case: leafpage, nothing to insert, nothing to delete, then + * vacuum marks page + */ + if (GistPageIsLeaf(page) && xlrec.len == 0 && xlrec.data->ntodelete == 0) + GistClearTuplesDeleted(page); + } + + PageSetLSN(page, lsn); + PageSetTLI(page, ThisTimeLineID); + GistPageGetOpaque(page)->rightlink = InvalidBlockNumber; + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + WriteBuffer(buffer); + + if (ItemPointerIsValid(&(xlrec.data->key))) + { + if (incomplete_inserts != NIL) + forgetIncompleteInsert(xlrec.data->node, xlrec.data->key); + + if (!isnewroot && xlrec.data->blkno != GIST_ROOT_BLKNO) + pushIncompleteInsert(xlrec.data->node, lsn, xlrec.data->key, + &(xlrec.data->blkno), 1, + NULL); + } +} + +static void +decodePageSplitRecord(PageSplitRecord *decoded, XLogRecord *record) +{ + char *begin = XLogRecGetData(record), + *ptr; + int j, + i = 0; + + decoded->data = (gistxlogPageSplit *) begin; + decoded->page = (NewPage *) palloc(sizeof(NewPage) * decoded->data->npage); + + ptr = begin + sizeof(gistxlogPageSplit); + for (i = 0; i < decoded->data->npage; i++) + { + Assert(ptr - begin < record->xl_len); + decoded->page[i].header = (gistxlogPage *) ptr; + ptr += sizeof(gistxlogPage); + + decoded->page[i].itup = (IndexTuple *) + palloc(sizeof(IndexTuple) * decoded->page[i].header->num); + j = 0; + while (j < decoded->page[i].header->num) + { + Assert(ptr - begin < record->xl_len); + decoded->page[i].itup[j] = (IndexTuple) ptr; + ptr += IndexTupleSize((IndexTuple) ptr); + j++; + } + } +} + +static void +gistRedoPageSplitRecord(XLogRecPtr lsn, XLogRecord *record) +{ + PageSplitRecord xlrec; + Relation reln; + Buffer buffer; + Page page; + int i; + int flags = 0; + + decodePageSplitRecord(&xlrec, record); + reln = XLogOpenRelation(xlrec.data->node); + if (!RelationIsValid(reln)) + return; + + /* first of all wee need get F_LEAF flag from original page */ + buffer = XLogReadBuffer(false, reln, xlrec.data->origblkno); + if (!BufferIsValid(buffer)) + elog(PANIC, "block %u unfound", xlrec.data->origblkno); + page = (Page) BufferGetPage(buffer); + if (PageIsNew((PageHeader) page)) + elog(PANIC, "uninitialized page %u", xlrec.data->origblkno); + + flags = (GistPageIsLeaf(page)) ? F_LEAF : 0; + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + + /* loop around all pages */ + for (i = 0; i < xlrec.data->npage; i++) + { + NewPage *newpage = xlrec.page + i; + bool isorigpage = (xlrec.data->origblkno == newpage->header->blkno) ? true : false; + + buffer = XLogReadBuffer(!isorigpage, reln, newpage->header->blkno); + if (!BufferIsValid(buffer)) + elog(PANIC, "block %u unfound", newpage->header->blkno); + page = (Page) BufferGetPage(buffer); + + if (XLByteLE(lsn, PageGetLSN(page))) + { + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + continue; + } + + /* ok, clear buffer */ + GISTInitBuffer(buffer, flags); + + /* and fill it */ + gistfillbuffer(reln, page, newpage->itup, newpage->header->num, FirstOffsetNumber); + + PageSetLSN(page, lsn); + PageSetTLI(page, ThisTimeLineID); + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + WriteBuffer(buffer); + } + + if (ItemPointerIsValid(&(xlrec.data->key))) + { + if (incomplete_inserts != NIL) + forgetIncompleteInsert(xlrec.data->node, xlrec.data->key); + + pushIncompleteInsert(xlrec.data->node, lsn, xlrec.data->key, + NULL, 0, + &xlrec); + } +} + +static void +gistRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record) +{ + RelFileNode *node = (RelFileNode *) XLogRecGetData(record); + Relation reln; + Buffer buffer; + Page page; + + reln = XLogOpenRelation(*node); + if (!RelationIsValid(reln)) + return; + buffer = XLogReadBuffer(true, reln, GIST_ROOT_BLKNO); + if (!BufferIsValid(buffer)) + elog(PANIC, "root block unfound"); + page = (Page) BufferGetPage(buffer); + + if (!PageIsNew((PageHeader) page) && XLByteLE(lsn, PageGetLSN(page))) + { + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + return; + } + + GISTInitBuffer(buffer, F_LEAF); + + PageSetLSN(page, lsn); + PageSetTLI(page, ThisTimeLineID); + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + WriteBuffer(buffer); +} + +static void +gistRedoCompleteInsert(XLogRecPtr lsn, XLogRecord *record) +{ + char *begin = XLogRecGetData(record), + *ptr; + gistxlogInsertComplete *xlrec; + + xlrec = (gistxlogInsertComplete *) begin; + + ptr = begin + sizeof(gistxlogInsertComplete); + while (ptr - begin < record->xl_len) + { + Assert(record->xl_len - (ptr - begin) >= sizeof(ItemPointerData)); + forgetIncompleteInsert(xlrec->node, *((ItemPointerData *) ptr)); + ptr += sizeof(ItemPointerData); + } +} + +void +gist_redo(XLogRecPtr lsn, XLogRecord *record) +{ + uint8 info = record->xl_info & ~XLR_INFO_MASK; + + MemoryContext oldCxt; + + oldCxt = MemoryContextSwitchTo(opCtx); + switch (info) + { + case XLOG_GIST_ENTRY_UPDATE: + case XLOG_GIST_ENTRY_DELETE: + gistRedoEntryUpdateRecord(lsn, record, false); + break; + case XLOG_GIST_NEW_ROOT: + gistRedoEntryUpdateRecord(lsn, record, true); + break; + case XLOG_GIST_PAGE_SPLIT: + gistRedoPageSplitRecord(lsn, record); + break; + case XLOG_GIST_CREATE_INDEX: + gistRedoCreateIndex(lsn, record); + break; + case XLOG_GIST_INSERT_COMPLETE: + gistRedoCompleteInsert(lsn, record); + break; + default: + elog(PANIC, "gist_redo: unknown op code %u", info); + } + + MemoryContextSwitchTo(oldCxt); + MemoryContextReset(opCtx); +} + +static void +out_target(char *buf, RelFileNode node, ItemPointerData key) +{ + sprintf(buf + strlen(buf), "rel %u/%u/%u; tid %u/%u", + node.spcNode, node.dbNode, node.relNode, + ItemPointerGetBlockNumber(&key), + ItemPointerGetOffsetNumber(&key)); +} + +static void +out_gistxlogEntryUpdate(char *buf, gistxlogEntryUpdate *xlrec) +{ + out_target(buf, xlrec->node, xlrec->key); + sprintf(buf + strlen(buf), "; block number %u", + xlrec->blkno); +} + +static void +out_gistxlogPageSplit(char *buf, gistxlogPageSplit *xlrec) +{ + strcat(buf, "page_split: "); + out_target(buf, xlrec->node, xlrec->key); + sprintf(buf + strlen(buf), "; block number %u splits to %d pages", + xlrec->origblkno, xlrec->npage); +} + +void +gist_desc(char *buf, uint8 xl_info, char *rec) +{ + uint8 info = xl_info & ~XLR_INFO_MASK; + + switch (info) + { + case XLOG_GIST_ENTRY_UPDATE: + strcat(buf, "entry_update: "); + out_gistxlogEntryUpdate(buf, (gistxlogEntryUpdate *) rec); + break; + case XLOG_GIST_ENTRY_DELETE: + strcat(buf, "entry_delete: "); + out_gistxlogEntryUpdate(buf, (gistxlogEntryUpdate *) rec); + break; + case XLOG_GIST_NEW_ROOT: + strcat(buf, "new_root: "); + out_target(buf, ((gistxlogEntryUpdate *) rec)->node, ((gistxlogEntryUpdate *) rec)->key); + break; + case XLOG_GIST_PAGE_SPLIT: + out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec); + break; + case XLOG_GIST_CREATE_INDEX: + sprintf(buf + strlen(buf), "create_index: rel %u/%u/%u", + ((RelFileNode *) rec)->spcNode, + ((RelFileNode *) rec)->dbNode, + ((RelFileNode *) rec)->relNode); + break; + case XLOG_GIST_INSERT_COMPLETE: + sprintf(buf + strlen(buf), "complete_insert: rel %u/%u/%u", + ((gistxlogInsertComplete *) rec)->node.spcNode, + ((gistxlogInsertComplete *) rec)->node.dbNode, + ((gistxlogInsertComplete *) rec)->node.relNode); + break; + default: + elog(PANIC, "gist_desc: unknown op code %u", info); + } +} + +IndexTuple +gist_form_invalid_tuple(BlockNumber blkno) +{ + /* + * we don't alloc space for null's bitmap, this is invalid tuple, be + * carefull in read and write code + */ + Size size = IndexInfoFindDataOffset(0); + IndexTuple tuple = (IndexTuple) palloc0(size); + + tuple->t_info |= size; + + ItemPointerSetBlockNumber(&(tuple->t_tid), blkno); + GistTupleSetInvalid(tuple); + + return tuple; +} + +static Buffer +gistXLogReadAndLockBuffer(Relation r, BlockNumber blkno) +{ + Buffer buffer = XLogReadBuffer(false, r, blkno); + + if (!BufferIsValid(buffer)) + elog(PANIC, "block %u unfound", blkno); + if (PageIsNew((PageHeader) (BufferGetPage(buffer)))) + elog(PANIC, "uninitialized page %u", blkno); + + return buffer; +} + + +static void +gixtxlogFindPath(Relation index, gistIncompleteInsert *insert) +{ + GISTInsertStack *top; + + insert->pathlen = 0; + insert->path = NULL; + + if ((top = gistFindPath(index, insert->origblkno, gistXLogReadAndLockBuffer)) != NULL) + { + int i; + GISTInsertStack *ptr = top; + + while (ptr) + { + insert->pathlen++; + ptr = ptr->parent; + } + + insert->path = (BlockNumber *) palloc(sizeof(BlockNumber) * insert->pathlen); + + i = 0; + ptr = top; + while (ptr) + { + insert->path[i] = ptr->blkno; + i++; + ptr = ptr->parent; + } + } + else + elog(LOG, "lost parent for block %u", insert->origblkno); +} + +/* + * Continue insert after crash. In normal situation, there isn't any incomplete + * inserts, but if it might be after crash, WAL may has not a record of completetion. + * + * Although stored LSN in gistIncompleteInsert is a LSN of child page, + * we can compare it with LSN of parent, because parent is always locked + * while we change child page (look at gistmakedeal). So if parent's LSN is + * lesser than stored lsn then changes in parent doesn't do yet. + */ +static void +gistContinueInsert(gistIncompleteInsert *insert) +{ + IndexTuple *itup; + int i, + lenitup; + Relation index; + + index = XLogOpenRelation(insert->node); + if (!RelationIsValid(index)) + return; + + /* + * needed vector itup never will be more than initial lenblkno+2, because + * during this processing Indextuple can be only smaller + */ + lenitup = insert->lenblk; + itup = (IndexTuple *) palloc(sizeof(IndexTuple) * (lenitup + 2 /* guarantee root split */ )); + + for (i = 0; i < insert->lenblk; i++) + itup[i] = gist_form_invalid_tuple(insert->blkno[i]); + + if (insert->origblkno == GIST_ROOT_BLKNO) + { + /* + * it was split root, so we should only make new root. it can't be + * simple insert into root, look at call pushIncompleteInsert in + * gistRedoPageSplitRecord + */ + Buffer buffer = XLogReadBuffer(true, index, GIST_ROOT_BLKNO); + Page page; + + if (!BufferIsValid(buffer)) + elog(PANIC, "root block unfound"); + + page = BufferGetPage(buffer); + if (XLByteLE(insert->lsn, PageGetLSN(page))) + { + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + return; + } + + GISTInitBuffer(buffer, 0); + page = BufferGetPage(buffer); + gistfillbuffer(index, page, itup, lenitup, FirstOffsetNumber); + PageSetLSN(page, insert->lsn); + PageSetTLI(page, ThisTimeLineID); + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + WriteBuffer(buffer); + } + else + { + Buffer *buffers; + Page *pages; + int numbuffer; + + /* construct path */ + gixtxlogFindPath(index, insert); + + Assert(insert->pathlen > 0); + + buffers = (Buffer *) palloc(sizeof(Buffer) * (insert->lenblk + 2 /* guarantee root split */ )); + pages = (Page *) palloc(sizeof(Page) * (insert->lenblk + 2 /* guarantee root split */ )); + + for (i = 0; i < insert->pathlen; i++) + { + int j, + k, + pituplen = 0, + childfound = 0; + + numbuffer = 1; + buffers[numbuffer - 1] = XLogReadBuffer(false, index, insert->path[i]); + if (!BufferIsValid(buffers[numbuffer - 1])) + elog(PANIC, "block %u unfound", insert->path[i]); + pages[numbuffer - 1] = BufferGetPage(buffers[numbuffer - 1]); + if (PageIsNew((PageHeader) (pages[numbuffer - 1]))) + elog(PANIC, "uninitialized page %u", insert->path[i]); + + if (XLByteLE(insert->lsn, PageGetLSN(pages[numbuffer - 1]))) + { + LockBuffer(buffers[numbuffer - 1], BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffers[numbuffer - 1]); + return; + } + + pituplen = PageGetMaxOffsetNumber(pages[numbuffer - 1]); + + /* remove old IndexTuples */ + for (j = 0; j < pituplen && childfound < lenitup; j++) + { + BlockNumber blkno; + ItemId iid = PageGetItemId(pages[numbuffer - 1], j + FirstOffsetNumber); + IndexTuple idxtup = (IndexTuple) PageGetItem(pages[numbuffer - 1], iid); + + blkno = ItemPointerGetBlockNumber(&(idxtup->t_tid)); + + for (k = 0; k < lenitup; k++) + if (ItemPointerGetBlockNumber(&(itup[k]->t_tid)) == blkno) + { + PageIndexTupleDelete(pages[numbuffer - 1], j + FirstOffsetNumber); + j--; + pituplen--; + childfound++; + break; + } + } + + if (gistnospace(pages[numbuffer - 1], itup, lenitup)) + { + /* no space left on page, so we should split */ + buffers[numbuffer] = XLogReadBuffer(true, index, P_NEW); + if (!BufferIsValid(buffers[numbuffer])) + elog(PANIC, "could not obtain new block"); + GISTInitBuffer(buffers[numbuffer], 0); + pages[numbuffer] = BufferGetPage(buffers[numbuffer]); + gistfillbuffer(index, pages[numbuffer], itup, lenitup, FirstOffsetNumber); + numbuffer++; + + if (BufferGetBlockNumber(buffers[0]) == GIST_ROOT_BLKNO) + { + IndexTuple *parentitup; + + /* + * we split root, just copy tuples from old root to new + * page + */ + parentitup = gistextractbuffer(buffers[numbuffer - 1], &pituplen); + + /* sanity check */ + if (i + 1 != insert->pathlen) + elog(PANIC, "unexpected pathlen in index \"%s\"", + RelationGetRelationName(index)); + + /* fill new page */ + buffers[numbuffer] = XLogReadBuffer(true, index, P_NEW); + if (!BufferIsValid(buffers[numbuffer])) + elog(PANIC, "could not obtain new block"); + GISTInitBuffer(buffers[numbuffer], 0); + pages[numbuffer] = BufferGetPage(buffers[numbuffer]); + gistfillbuffer(index, pages[numbuffer], parentitup, pituplen, FirstOffsetNumber); + numbuffer++; + + /* fill root page */ + GISTInitBuffer(buffers[0], 0); + for (j = 1; j < numbuffer; j++) + { + IndexTuple tuple = gist_form_invalid_tuple(BufferGetBlockNumber(buffers[j])); + + if (PageAddItem(pages[0], + (Item) tuple, + IndexTupleSize(tuple), + (OffsetNumber) j, + LP_USED) == InvalidOffsetNumber) + elog(PANIC, "failed to add item to index page in \"%s\"", + RelationGetRelationName(index)); + } + } + } + else + gistfillbuffer(index, pages[numbuffer - 1], itup, lenitup, InvalidOffsetNumber); + + lenitup = numbuffer; + for (j = 0; j < numbuffer; j++) + { + itup[j] = gist_form_invalid_tuple(BufferGetBlockNumber(buffers[j])); + PageSetLSN(pages[j], insert->lsn); + PageSetTLI(pages[j], ThisTimeLineID); + GistPageGetOpaque(pages[j])->rightlink = InvalidBlockNumber; + LockBuffer(buffers[j], BUFFER_LOCK_UNLOCK); + WriteBuffer(buffers[j]); + } + } + } + + ereport(LOG, + (errmsg("index %u/%u/%u needs VACUUM or REINDEX to finish crash recovery", + insert->node.spcNode, insert->node.dbNode, insert->node.relNode), + errdetail("Incomplete insertion detected during crash replay."))); +} + +void +gist_xlog_startup(void) +{ + incomplete_inserts = NIL; + insertCtx = AllocSetContextCreate(CurrentMemoryContext, + "GiST recovery temporary context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + opCtx = createTempGistContext(); +} + +void +gist_xlog_cleanup(void) +{ + ListCell *l; + List *reverse = NIL; + MemoryContext oldCxt = MemoryContextSwitchTo(insertCtx); + + /* we should call gistContinueInsert in reverse order */ + + foreach(l, incomplete_inserts) + reverse = lappend(reverse, lfirst(l)); + + MemoryContextSwitchTo(opCtx); + foreach(l, reverse) + { + gistIncompleteInsert *insert = (gistIncompleteInsert *) lfirst(l); + + gistContinueInsert(insert); + MemoryContextReset(opCtx); + } + MemoryContextSwitchTo(oldCxt); + + MemoryContextDelete(opCtx); + MemoryContextDelete(insertCtx); +} + + +XLogRecData * +formSplitRdata(RelFileNode node, BlockNumber blkno, + ItemPointer key, SplitedPageLayout *dist) +{ + + XLogRecData *rdata; + gistxlogPageSplit *xlrec = (gistxlogPageSplit *) palloc(sizeof(gistxlogPageSplit)); + SplitedPageLayout *ptr; + int npage = 0, + cur = 1; + + ptr = dist; + while (ptr) + { + npage++; + ptr = ptr->next; + } + + rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (npage * 2 + 2)); + + xlrec->node = node; + xlrec->origblkno = blkno; + xlrec->npage = (uint16) npage; + if (key) + xlrec->key = *key; + else + ItemPointerSetInvalid(&(xlrec->key)); + + rdata[0].buffer = InvalidBuffer; + rdata[0].data = (char *) xlrec; + rdata[0].len = sizeof(gistxlogPageSplit); + rdata[0].next = NULL; + + ptr = dist; + while (ptr) + { + rdata[cur].buffer = InvalidBuffer; + rdata[cur].data = (char *) &(ptr->block); + rdata[cur].len = sizeof(gistxlogPage); + rdata[cur - 1].next = &(rdata[cur]); + cur++; + + rdata[cur].buffer = InvalidBuffer; + rdata[cur].data = (char *) (ptr->list); + rdata[cur].len = ptr->lenlist; + rdata[cur - 1].next = &(rdata[cur]); + rdata[cur].next = NULL; + cur++; + ptr = ptr->next; + } + + return rdata; +} + + +XLogRecData * +formUpdateRdata(RelFileNode node, BlockNumber blkno, + OffsetNumber *todelete, int ntodelete, bool emptypage, + IndexTuple *itup, int ituplen, ItemPointer key) +{ + XLogRecData *rdata; + gistxlogEntryUpdate *xlrec = (gistxlogEntryUpdate *) palloc(sizeof(gistxlogEntryUpdate)); + + xlrec->node = node; + xlrec->blkno = blkno; + if (key) + xlrec->key = *key; + else + ItemPointerSetInvalid(&(xlrec->key)); + + if (emptypage) + { + xlrec->isemptypage = true; + xlrec->ntodelete = 0; + + rdata = (XLogRecData *) palloc(sizeof(XLogRecData)); + rdata->buffer = InvalidBuffer; + rdata->data = (char *) xlrec; + rdata->len = sizeof(gistxlogEntryUpdate); + rdata->next = NULL; + } + else + { + int cur = 1, + i; + + xlrec->isemptypage = false; + xlrec->ntodelete = ntodelete; + + rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (2 + ituplen)); + + rdata->buffer = InvalidBuffer; + rdata->data = (char *) xlrec; + rdata->len = sizeof(gistxlogEntryUpdate); + rdata->next = NULL; + + if (ntodelete) + { + rdata[cur - 1].next = &(rdata[cur]); + rdata[cur].buffer = InvalidBuffer; + rdata[cur].data = (char *) todelete; + rdata[cur].len = MAXALIGN(sizeof(OffsetNumber) * ntodelete); + rdata[cur].next = NULL; + cur++; + } + + /* new tuples */ + for (i = 0; i < ituplen; i++) + { + rdata[cur].buffer = InvalidBuffer; + rdata[cur].data = (char *) (itup[i]); + rdata[cur].len = IndexTupleSize(itup[i]); + rdata[cur].next = NULL; + rdata[cur - 1].next = &(rdata[cur]); + cur++; + } + } + + return rdata; +} + +XLogRecPtr +gistxlogInsertCompletion(RelFileNode node, ItemPointerData *keys, int len) +{ + gistxlogInsertComplete xlrec; + XLogRecData rdata[2]; + XLogRecPtr recptr; + + Assert(len > 0); + xlrec.node = node; + + rdata[0].buffer = InvalidBuffer; + rdata[0].data = (char *) &xlrec; + rdata[0].len = sizeof(gistxlogInsertComplete); + rdata[0].next = &(rdata[1]); + + rdata[1].buffer = InvalidBuffer; + rdata[1].data = (char *) keys; + rdata[1].len = sizeof(ItemPointerData) * len; + rdata[1].next = NULL; + + START_CRIT_SECTION(); + + recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_INSERT_COMPLETE, rdata); + + END_CRIT_SECTION(); + + return recptr; +} diff --git a/src/backend/access/transam/twophase_rmgr.c b/src/backend/access/transam/twophase_rmgr.c new file mode 100644 index 0000000..c358890 --- /dev/null +++ b/src/backend/access/transam/twophase_rmgr.c @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * twophase_rmgr.c + * Two-phase-commit resource managers tables + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/twophase_rmgr.h" +#include "commands/async.h" +#include "storage/lock.h" +#include "utils/flatfiles.h" +#include "utils/inval.h" + + +const TwoPhaseCallback twophase_recover_callbacks[TWOPHASE_RM_MAX_ID + 1] = +{ + NULL, /* END ID */ + lock_twophase_recover, /* Lock */ + NULL, /* Inval */ + NULL, /* flat file update */ + NULL /* notify/listen */ +}; + +const TwoPhaseCallback twophase_postcommit_callbacks[TWOPHASE_RM_MAX_ID + 1] = +{ + NULL, /* END ID */ + lock_twophase_postcommit, /* Lock */ + inval_twophase_postcommit, /* Inval */ + flatfile_twophase_postcommit, /* flat file update */ + notify_twophase_postcommit /* notify/listen */ +}; + +const TwoPhaseCallback twophase_postabort_callbacks[TWOPHASE_RM_MAX_ID + 1] = +{ + NULL, /* END ID */ + lock_twophase_postabort, /* Lock */ + NULL, /* Inval */ + NULL, /* flat file update */ + NULL /* notify/listen */ +}; diff --git a/src/backend/executor/nodeBitmapAnd.c b/src/backend/executor/nodeBitmapAnd.c new file mode 100644 index 0000000..f64d9de --- /dev/null +++ b/src/backend/executor/nodeBitmapAnd.c @@ -0,0 +1,221 @@ +/*------------------------------------------------------------------------- + * + * nodeBitmapAnd.c + * routines to handle BitmapAnd nodes. + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +/* INTERFACE ROUTINES + * ExecInitBitmapAnd - initialize the BitmapAnd node + * MultiExecBitmapAnd - retrieve the result bitmap from the node + * ExecEndBitmapAnd - shut down the BitmapAnd node + * ExecReScanBitmapAnd - rescan the BitmapAnd node + * + * NOTES + * BitmapAnd nodes don't make use of their left and right + * subtrees, rather they maintain a list of subplans, + * much like Append nodes. The logic is much simpler than + * Append, however, since we needn't cope with forward/backward + * execution. + */ + +#include "postgres.h" + +#include "executor/execdebug.h" +#include "executor/instrument.h" +#include "executor/nodeBitmapAnd.h" + + +/* ---------------------------------------------------------------- + * ExecInitBitmapAnd + * + * Begin all of the subscans of the BitmapAnd node. + * ---------------------------------------------------------------- + */ +BitmapAndState * +ExecInitBitmapAnd(BitmapAnd *node, EState *estate) +{ + BitmapAndState *bitmapandstate = makeNode(BitmapAndState); + PlanState **bitmapplanstates; + int nplans; + int i; + ListCell *l; + Plan *initNode; + + CXT1_printf("ExecInitBitmapAnd: context is %d\n", CurrentMemoryContext); + + /* + * Set up empty vector of subplan states + */ + nplans = list_length(node->bitmapplans); + + bitmapplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); + + /* + * create new BitmapAndState for our BitmapAnd node + */ + bitmapandstate->ps.plan = (Plan *) node; + bitmapandstate->ps.state = estate; + bitmapandstate->bitmapplans = bitmapplanstates; + bitmapandstate->nplans = nplans; + + /* + * Miscellaneous initialization + * + * BitmapAnd plans don't have expression contexts because they never call + * ExecQual or ExecProject. They don't need any tuple slots either. + */ + +#define BITMAPAND_NSLOTS 0 + + /* + * call ExecInitNode on each of the plans to be executed and save the + * results into the array "bitmapplanstates". + */ + i = 0; + foreach(l, node->bitmapplans) + { + initNode = (Plan *) lfirst(l); + bitmapplanstates[i] = ExecInitNode(initNode, estate); + i++; + } + + return bitmapandstate; +} + +int +ExecCountSlotsBitmapAnd(BitmapAnd *node) +{ + ListCell *plan; + int nSlots = 0; + + foreach(plan, node->bitmapplans) + nSlots += ExecCountSlotsNode((Plan *) lfirst(plan)); + return nSlots + BITMAPAND_NSLOTS; +} + +/* ---------------------------------------------------------------- + * MultiExecBitmapAnd + * ---------------------------------------------------------------- + */ +Node * +MultiExecBitmapAnd(BitmapAndState *node) +{ + PlanState **bitmapplans; + int nplans; + int i; + TIDBitmap *result = NULL; + + /* must provide our own instrumentation support */ + if (node->ps.instrument) + InstrStartNode(node->ps.instrument); + + /* + * get information from the node + */ + bitmapplans = node->bitmapplans; + nplans = node->nplans; + + /* + * Scan all the subplans and AND their result bitmaps + */ + for (i = 0; i < nplans; i++) + { + PlanState *subnode = bitmapplans[i]; + TIDBitmap *subresult; + + subresult = (TIDBitmap *) MultiExecProcNode(subnode); + + if (!subresult || !IsA(subresult, TIDBitmap)) + elog(ERROR, "unrecognized result from subplan"); + + if (result == NULL) + result = subresult; /* first subplan */ + else + { + tbm_intersect(result, subresult); + tbm_free(subresult); + } + + /* + * If at any stage we have a completely empty bitmap, we can fall out + * without evaluating the remaining subplans, since ANDing them can no + * longer change the result. (Note: the fact that indxpath.c orders + * the subplans by selectivity should make this case more likely to + * occur.) + */ + if (tbm_is_empty(result)) + break; + } + + if (result == NULL) + elog(ERROR, "BitmapAnd doesn't support zero inputs"); + + /* must provide our own instrumentation support */ + if (node->ps.instrument) + InstrStopNodeMulti(node->ps.instrument, 0 /* XXX */ ); + + return (Node *) result; +} + +/* ---------------------------------------------------------------- + * ExecEndBitmapAnd + * + * Shuts down the subscans of the BitmapAnd node. + * + * Returns nothing of interest. + * ---------------------------------------------------------------- + */ +void +ExecEndBitmapAnd(BitmapAndState *node) +{ + PlanState **bitmapplans; + int nplans; + int i; + + /* + * get information from the node + */ + bitmapplans = node->bitmapplans; + nplans = node->nplans; + + /* + * shut down each of the subscans (that we've initialized) + */ + for (i = 0; i < nplans; i++) + { + if (bitmapplans[i]) + ExecEndNode(bitmapplans[i]); + } +} + +void +ExecReScanBitmapAnd(BitmapAndState *node, ExprContext *exprCtxt) +{ + int i; + + for (i = 0; i < node->nplans; i++) + { + PlanState *subnode = node->bitmapplans[i]; + + /* + * ExecReScan doesn't know about my subplans, so I have to do + * changed-parameter signaling myself. + */ + if (node->ps.chgParam != NULL) + UpdateChangedParamSet(subnode, node->ps.chgParam); + + /* + * Always rescan the inputs immediately, to ensure we can pass down + * any outer tuple that might be used in index quals. + */ + ExecReScan(subnode, exprCtxt); + } +} diff --git a/src/backend/executor/nodeBitmapOr.c b/src/backend/executor/nodeBitmapOr.c new file mode 100644 index 0000000..3522bc2 --- /dev/null +++ b/src/backend/executor/nodeBitmapOr.c @@ -0,0 +1,237 @@ +/*------------------------------------------------------------------------- + * + * nodeBitmapOr.c + * routines to handle BitmapOr nodes. + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +/* INTERFACE ROUTINES + * ExecInitBitmapOr - initialize the BitmapOr node + * MultiExecBitmapOr - retrieve the result bitmap from the node + * ExecEndBitmapOr - shut down the BitmapOr node + * ExecReScanBitmapOr - rescan the BitmapOr node + * + * NOTES + * BitmapOr nodes don't make use of their left and right + * subtrees, rather they maintain a list of subplans, + * much like Append nodes. The logic is much simpler than + * Append, however, since we needn't cope with forward/backward + * execution. + */ + +#include "postgres.h" + +#include "executor/execdebug.h" +#include "executor/instrument.h" +#include "executor/nodeBitmapOr.h" +#include "miscadmin.h" + + +/* ---------------------------------------------------------------- + * ExecInitBitmapOr + * + * Begin all of the subscans of the BitmapOr node. + * ---------------------------------------------------------------- + */ +BitmapOrState * +ExecInitBitmapOr(BitmapOr *node, EState *estate) +{ + BitmapOrState *bitmaporstate = makeNode(BitmapOrState); + PlanState **bitmapplanstates; + int nplans; + int i; + ListCell *l; + Plan *initNode; + + CXT1_printf("ExecInitBitmapOr: context is %d\n", CurrentMemoryContext); + + /* + * Set up empty vector of subplan states + */ + nplans = list_length(node->bitmapplans); + + bitmapplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *)); + + /* + * create new BitmapOrState for our BitmapOr node + */ + bitmaporstate->ps.plan = (Plan *) node; + bitmaporstate->ps.state = estate; + bitmaporstate->bitmapplans = bitmapplanstates; + bitmaporstate->nplans = nplans; + + /* + * Miscellaneous initialization + * + * BitmapOr plans don't have expression contexts because they never call + * ExecQual or ExecProject. They don't need any tuple slots either. + */ + +#define BITMAPOR_NSLOTS 0 + + /* + * call ExecInitNode on each of the plans to be executed and save the + * results into the array "bitmapplanstates". + */ + i = 0; + foreach(l, node->bitmapplans) + { + initNode = (Plan *) lfirst(l); + bitmapplanstates[i] = ExecInitNode(initNode, estate); + i++; + } + + return bitmaporstate; +} + +int +ExecCountSlotsBitmapOr(BitmapOr *node) +{ + ListCell *plan; + int nSlots = 0; + + foreach(plan, node->bitmapplans) + nSlots += ExecCountSlotsNode((Plan *) lfirst(plan)); + return nSlots + BITMAPOR_NSLOTS; +} + +/* ---------------------------------------------------------------- + * MultiExecBitmapOr + * ---------------------------------------------------------------- + */ +Node * +MultiExecBitmapOr(BitmapOrState *node) +{ + PlanState **bitmapplans; + int nplans; + int i; + TIDBitmap *result = NULL; + + /* must provide our own instrumentation support */ + if (node->ps.instrument) + InstrStartNode(node->ps.instrument); + + /* + * get information from the node + */ + bitmapplans = node->bitmapplans; + nplans = node->nplans; + + /* + * Scan all the subplans and OR their result bitmaps + */ + for (i = 0; i < nplans; i++) + { + PlanState *subnode = bitmapplans[i]; + TIDBitmap *subresult; + + /* + * We can special-case BitmapIndexScan children to avoid an explicit + * tbm_union step for each child: just pass down the current result + * bitmap and let the child OR directly into it. + */ + if (IsA(subnode, BitmapIndexScanState)) + { + if (result == NULL) /* first subplan */ + { + /* XXX should we use less than work_mem for this? */ + result = tbm_create(work_mem * 1024L); + } + + ((BitmapIndexScanState *) subnode)->biss_result = result; + + subresult = (TIDBitmap *) MultiExecProcNode(subnode); + + if (subresult != result) + elog(ERROR, "unrecognized result from subplan"); + } + else + { + /* standard implementation */ + subresult = (TIDBitmap *) MultiExecProcNode(subnode); + + if (!subresult || !IsA(subresult, TIDBitmap)) + elog(ERROR, "unrecognized result from subplan"); + + if (result == NULL) + result = subresult; /* first subplan */ + else + { + tbm_union(result, subresult); + tbm_free(subresult); + } + } + } + + /* We could return an empty result set here? */ + if (result == NULL) + elog(ERROR, "BitmapOr doesn't support zero inputs"); + + /* must provide our own instrumentation support */ + if (node->ps.instrument) + InstrStopNodeMulti(node->ps.instrument, 0 /* XXX */ ); + + return (Node *) result; +} + +/* ---------------------------------------------------------------- + * ExecEndBitmapOr + * + * Shuts down the subscans of the BitmapOr node. + * + * Returns nothing of interest. + * ---------------------------------------------------------------- + */ +void +ExecEndBitmapOr(BitmapOrState *node) +{ + PlanState **bitmapplans; + int nplans; + int i; + + /* + * get information from the node + */ + bitmapplans = node->bitmapplans; + nplans = node->nplans; + + /* + * shut down each of the subscans (that we've initialized) + */ + for (i = 0; i < nplans; i++) + { + if (bitmapplans[i]) + ExecEndNode(bitmapplans[i]); + } +} + +void +ExecReScanBitmapOr(BitmapOrState *node, ExprContext *exprCtxt) +{ + int i; + + for (i = 0; i < node->nplans; i++) + { + PlanState *subnode = node->bitmapplans[i]; + + /* + * ExecReScan doesn't know about my subplans, so I have to do + * changed-parameter signaling myself. + */ + if (node->ps.chgParam != NULL) + UpdateChangedParamSet(subnode, node->ps.chgParam); + + /* + * Always rescan the inputs immediately, to ensure we can pass down + * any outer tuple that might be used in index quals. + */ + ExecReScan(subnode, exprCtxt); + } +} diff --git a/src/backend/optimizer/plan/README b/src/backend/optimizer/plan/README index d3a5168..a63596f 100644 --- a/src/backend/optimizer/plan/README +++ b/src/backend/optimizer/plan/README @@ -6,7 +6,7 @@ From owner-pgsql-hackers@hub.org Fri Feb 13 09:01:19 1998 Received: from renoir.op.net (root@renoir.op.net [209.152.193.4]) by candle.pha.pa.us (8.8.5/8.8.5) with ESMTP id JAA11576 for ; Fri, 13 Feb 1998 09:01:17 -0500 (EST) -Received: from hub.org (hub.org [209.47.148.200]) by renoir.op.net (o1/$Revision: 1.1 $) with ESMTP id IAA09761 for ; Fri, 13 Feb 1998 08:41:22 -0500 (EST) +Received: from hub.org (hub.org [209.47.148.200]) by renoir.op.net (o1/$Revision$) with ESMTP id IAA09761 for ; Fri, 13 Feb 1998 08:41:22 -0500 (EST) Received: from localhost (majordom@localhost) by hub.org (8.8.8/8.7.5) with SMTP id IAA08135; Fri, 13 Feb 1998 08:40:17 -0500 (EST) Received: by hub.org (TLB v0.10a (1.23 tibbs 1997/01/09 00:29:32)); Fri, 13 Feb 1998 08:38:42 -0500 (EST) Received: (from majordom@localhost) by hub.org (8.8.8/8.7.5) id IAA06646 for pgsql-hackers-outgoing; Fri, 13 Feb 1998 08:38:35 -0500 (EST) diff --git a/src/backend/port/darwin/system.c b/src/backend/port/darwin/system.c index cfeaaa0..d05fa17 100644 --- a/src/backend/port/darwin/system.c +++ b/src/backend/port/darwin/system.c @@ -34,7 +34,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD$ + * $FreeBSD: src/lib/libc/stdlib/system.c,v 1.6 2000/03/16 02:14:41 jasone Exp $ */ #if defined(LIBC_SCCS) && !defined(lint) diff --git a/src/backend/postmaster/fork_process.c b/src/backend/postmaster/fork_process.c new file mode 100644 index 0000000..eb62aaf --- /dev/null +++ b/src/backend/postmaster/fork_process.c @@ -0,0 +1,85 @@ +/* + * fork_process.c + * A simple wrapper on top of fork(). This does not handle the + * EXEC_BACKEND case; it might be extended to do so, but it would be + * considerably more complex. + * + * Copyright (c) 1996-2005, PostgreSQL Global Development Group + * + * IDENTIFICATION + * $PostgreSQL$ + */ +#include "postgres.h" +#include "postmaster/fork_process.h" + +#include +#include +#include + +#ifndef WIN32 +/* + * Wrapper for fork(). Return values are the same as those for fork(): + * -1 if the fork failed, 0 in the child process, and the PID of the + * child in the parent process. + */ +pid_t +fork_process(void) +{ + pid_t result; + +#ifdef LINUX_PROFILE + struct itimerval prof_itimer; +#endif + + /* + * Flush stdio channels just before fork, to avoid double-output problems. + * Ideally we'd use fflush(NULL) here, but there are still a few non-ANSI + * stdio libraries out there (like SunOS 4.1.x) that coredump if we do. + * Presently stdout and stderr are the only stdio output channels used by + * the postmaster, so fflush'ing them should be sufficient. + */ + fflush(stdout); + fflush(stderr); + +#ifdef LINUX_PROFILE + + /* + * Linux's fork() resets the profiling timer in the child process. If we + * want to profile child processes then we need to save and restore the + * timer setting. This is a waste of time if not profiling, however, so + * only do it if commanded by specific -DLINUX_PROFILE switch. + */ + getitimer(ITIMER_PROF, &prof_itimer); +#endif + +#ifdef __BEOS__ + /* Specific beos actions before backend startup */ + beos_before_backend_startup(); +#endif + + result = fork(); + if (result == (pid_t) -1) + { + /* fork failed */ +#ifdef __BEOS__ + /* Specific beos backend startup actions */ + beos_backend_startup_failed(); +#endif + } + else if (result == 0) + { + /* fork succeeded, in child */ +#ifdef LINUX_PROFILE + setitimer(ITIMER_PROF, &prof_itimer, NULL); +#endif + +#ifdef __BEOS__ + /* Specific beos backend startup actions */ + beos_backend_startup(); +#endif + } + + return result; +} + +#endif /* ! WIN32 */ diff --git a/src/backend/utils/mb/Unicode/utf8_to_win1258.map b/src/backend/utils/mb/Unicode/utf8_to_win1258.map new file mode 100644 index 0000000..84cefd9 --- /dev/null +++ b/src/backend/utils/mb/Unicode/utf8_to_win1258.map @@ -0,0 +1,122 @@ +static pg_utf_to_local ULmapWIN1258[ 120 ] = { + {0x0000, 0x0081}, + {0xc2a0, 0x00a0}, + {0xc2a1, 0x00a1}, + {0xc2a2, 0x00a2}, + {0xc2a3, 0x00a3}, + {0xc2a4, 0x00a4}, + {0xc2a5, 0x00a5}, + {0xc2a6, 0x00a6}, + {0xc2a7, 0x00a7}, + {0xc2a8, 0x00a8}, + {0xc2a9, 0x00a9}, + {0xc2aa, 0x00aa}, + {0xc2ab, 0x00ab}, + {0xc2ac, 0x00ac}, + {0xc2ad, 0x00ad}, + {0xc2ae, 0x00ae}, + {0xc2af, 0x00af}, + {0xc2b0, 0x00b0}, + {0xc2b1, 0x00b1}, + {0xc2b2, 0x00b2}, + {0xc2b3, 0x00b3}, + {0xc2b4, 0x00b4}, + {0xc2b5, 0x00b5}, + {0xc2b6, 0x00b6}, + {0xc2b7, 0x00b7}, + {0xc2b8, 0x00b8}, + {0xc2b9, 0x00b9}, + {0xc2ba, 0x00ba}, + {0xc2bb, 0x00bb}, + {0xc2bc, 0x00bc}, + {0xc2bd, 0x00bd}, + {0xc2be, 0x00be}, + {0xc2bf, 0x00bf}, + {0xc380, 0x00c0}, + {0xc381, 0x00c1}, + {0xc382, 0x00c2}, + {0xc384, 0x00c4}, + {0xc385, 0x00c5}, + {0xc386, 0x00c6}, + {0xc387, 0x00c7}, + {0xc388, 0x00c8}, + {0xc389, 0x00c9}, + {0xc38a, 0x00ca}, + {0xc38b, 0x00cb}, + {0xc38d, 0x00cd}, + {0xc38e, 0x00ce}, + {0xc38f, 0x00cf}, + {0xc391, 0x00d1}, + {0xc393, 0x00d3}, + {0xc394, 0x00d4}, + {0xc396, 0x00d6}, + {0xc397, 0x00d7}, + {0xc398, 0x00d8}, + {0xc399, 0x00d9}, + {0xc39a, 0x00da}, + {0xc39b, 0x00db}, + {0xc39c, 0x00dc}, + {0xc39f, 0x00df}, + {0xc3a0, 0x00e0}, + {0xc3a1, 0x00e1}, + {0xc3a2, 0x00e2}, + {0xc3a4, 0x00e4}, + {0xc3a5, 0x00e5}, + {0xc3a6, 0x00e6}, + {0xc3a7, 0x00e7}, + {0xc3a8, 0x00e8}, + {0xc3a9, 0x00e9}, + {0xc3aa, 0x00ea}, + {0xc3ab, 0x00eb}, + {0xc3ad, 0x00ed}, + {0xc3ae, 0x00ee}, + {0xc3af, 0x00ef}, + {0xc3b1, 0x00f1}, + {0xc3b3, 0x00f3}, + {0xc3b4, 0x00f4}, + {0xc3b6, 0x00f6}, + {0xc3b7, 0x00f7}, + {0xc3b8, 0x00f8}, + {0xc3b9, 0x00f9}, + {0xc3ba, 0x00fa}, + {0xc3bb, 0x00fb}, + {0xc3bc, 0x00fc}, + {0xc3bf, 0x00ff}, + {0xc482, 0x00c3}, + {0xc483, 0x00e3}, + {0xc490, 0x00d0}, + {0xc491, 0x00f0}, + {0xc592, 0x008c}, + {0xc593, 0x009c}, + {0xc5b8, 0x009f}, + {0xc692, 0x0083}, + {0xc6a0, 0x00d5}, + {0xc6a1, 0x00f5}, + {0xc6af, 0x00dd}, + {0xc6b0, 0x00fd}, + {0xcb86, 0x0088}, + {0xcb9c, 0x0098}, + {0xcc80, 0x00cc}, + {0xcc81, 0x00ec}, + {0xcc83, 0x00de}, + {0xcc89, 0x00d2}, + {0xcca3, 0x00f2}, + {0xe28093, 0x0096}, + {0xe28094, 0x0097}, + {0xe28098, 0x0091}, + {0xe28099, 0x0092}, + {0xe2809a, 0x0082}, + {0xe2809c, 0x0093}, + {0xe2809d, 0x0094}, + {0xe2809e, 0x0084}, + {0xe280a0, 0x0086}, + {0xe280a1, 0x0087}, + {0xe280a2, 0x0095}, + {0xe280a6, 0x0085}, + {0xe280b0, 0x0089}, + {0xe280b9, 0x008b}, + {0xe280ba, 0x009b}, + {0xe282ab, 0x00fe}, + {0xe282ac, 0x0080}, + {0xe284a2, 0x0099} +}; diff --git a/src/backend/utils/mb/Unicode/utf8_to_win866.map b/src/backend/utils/mb/Unicode/utf8_to_win866.map new file mode 100644 index 0000000..cac06a8 --- /dev/null +++ b/src/backend/utils/mb/Unicode/utf8_to_win866.map @@ -0,0 +1,130 @@ +static pg_utf_to_local ULmap_WIN866[ 128 ] = { + {0xc2a0, 0x00ff}, + {0xc2a4, 0x00fd}, + {0xc2b0, 0x00f8}, + {0xc2b7, 0x00fa}, + {0xd081, 0x00f0}, + {0xd084, 0x00f2}, + {0xd087, 0x00f4}, + {0xd08e, 0x00f6}, + {0xd090, 0x0080}, + {0xd091, 0x0081}, + {0xd092, 0x0082}, + {0xd093, 0x0083}, + {0xd094, 0x0084}, + {0xd095, 0x0085}, + {0xd096, 0x0086}, + {0xd097, 0x0087}, + {0xd098, 0x0088}, + {0xd099, 0x0089}, + {0xd09a, 0x008a}, + {0xd09b, 0x008b}, + {0xd09c, 0x008c}, + {0xd09d, 0x008d}, + {0xd09e, 0x008e}, + {0xd09f, 0x008f}, + {0xd0a0, 0x0090}, + {0xd0a1, 0x0091}, + {0xd0a2, 0x0092}, + {0xd0a3, 0x0093}, + {0xd0a4, 0x0094}, + {0xd0a5, 0x0095}, + {0xd0a6, 0x0096}, + {0xd0a7, 0x0097}, + {0xd0a8, 0x0098}, + {0xd0a9, 0x0099}, + {0xd0aa, 0x009a}, + {0xd0ab, 0x009b}, + {0xd0ac, 0x009c}, + {0xd0ad, 0x009d}, + {0xd0ae, 0x009e}, + {0xd0af, 0x009f}, + {0xd0b0, 0x00a0}, + {0xd0b1, 0x00a1}, + {0xd0b2, 0x00a2}, + {0xd0b3, 0x00a3}, + {0xd0b4, 0x00a4}, + {0xd0b5, 0x00a5}, + {0xd0b6, 0x00a6}, + {0xd0b7, 0x00a7}, + {0xd0b8, 0x00a8}, + {0xd0b9, 0x00a9}, + {0xd0ba, 0x00aa}, + {0xd0bb, 0x00ab}, + {0xd0bc, 0x00ac}, + {0xd0bd, 0x00ad}, + {0xd0be, 0x00ae}, + {0xd0bf, 0x00af}, + {0xd180, 0x00e0}, + {0xd181, 0x00e1}, + {0xd182, 0x00e2}, + {0xd183, 0x00e3}, + {0xd184, 0x00e4}, + {0xd185, 0x00e5}, + {0xd186, 0x00e6}, + {0xd187, 0x00e7}, + {0xd188, 0x00e8}, + {0xd189, 0x00e9}, + {0xd18a, 0x00ea}, + {0xd18b, 0x00eb}, + {0xd18c, 0x00ec}, + {0xd18d, 0x00ed}, + {0xd18e, 0x00ee}, + {0xd18f, 0x00ef}, + {0xd191, 0x00f1}, + {0xd194, 0x00f3}, + {0xd197, 0x00f5}, + {0xd19e, 0x00f7}, + {0xe28496, 0x00fc}, + {0xe28899, 0x00f9}, + {0xe2889a, 0x00fb}, + {0xe29480, 0x00c4}, + {0xe29482, 0x00b3}, + {0xe2948c, 0x00da}, + {0xe29490, 0x00bf}, + {0xe29494, 0x00c0}, + {0xe29498, 0x00d9}, + {0xe2949c, 0x00c3}, + {0xe294a4, 0x00b4}, + {0xe294ac, 0x00c2}, + {0xe294b4, 0x00c1}, + {0xe294bc, 0x00c5}, + {0xe29590, 0x00cd}, + {0xe29591, 0x00ba}, + {0xe29592, 0x00d5}, + {0xe29593, 0x00d6}, + {0xe29594, 0x00c9}, + {0xe29595, 0x00b8}, + {0xe29596, 0x00b7}, + {0xe29597, 0x00bb}, + {0xe29598, 0x00d4}, + {0xe29599, 0x00d3}, + {0xe2959a, 0x00c8}, + {0xe2959b, 0x00be}, + {0xe2959c, 0x00bd}, + {0xe2959d, 0x00bc}, + {0xe2959e, 0x00c6}, + {0xe2959f, 0x00c7}, + {0xe295a0, 0x00cc}, + {0xe295a1, 0x00b5}, + {0xe295a2, 0x00b6}, + {0xe295a3, 0x00b9}, + {0xe295a4, 0x00d1}, + {0xe295a5, 0x00d2}, + {0xe295a6, 0x00cb}, + {0xe295a7, 0x00cf}, + {0xe295a8, 0x00d0}, + {0xe295a9, 0x00ca}, + {0xe295aa, 0x00d8}, + {0xe295ab, 0x00d7}, + {0xe295ac, 0x00ce}, + {0xe29680, 0x00df}, + {0xe29684, 0x00dc}, + {0xe29688, 0x00db}, + {0xe2968c, 0x00dd}, + {0xe29690, 0x00de}, + {0xe29691, 0x00b0}, + {0xe29692, 0x00b1}, + {0xe29693, 0x00b2}, + {0xe296a0, 0x00fe} +}; diff --git a/src/backend/utils/mb/Unicode/win1258_to_utf8.map b/src/backend/utils/mb/Unicode/win1258_to_utf8.map new file mode 100644 index 0000000..0596d8b --- /dev/null +++ b/src/backend/utils/mb/Unicode/win1258_to_utf8.map @@ -0,0 +1,130 @@ +static pg_local_to_utf LUmapWIN1258[ 128 ] = { + {0x0080, 0xe282ac}, + {0x0081, 0x0000}, + {0x0082, 0xe2809a}, + {0x0083, 0xc692}, + {0x0084, 0xe2809e}, + {0x0085, 0xe280a6}, + {0x0086, 0xe280a0}, + {0x0087, 0xe280a1}, + {0x0088, 0xcb86}, + {0x0089, 0xe280b0}, + {0x008a, 0x0000}, + {0x008b, 0xe280b9}, + {0x008c, 0xc592}, + {0x008d, 0x0000}, + {0x008e, 0x0000}, + {0x008f, 0x0000}, + {0x0090, 0x0000}, + {0x0091, 0xe28098}, + {0x0092, 0xe28099}, + {0x0093, 0xe2809c}, + {0x0094, 0xe2809d}, + {0x0095, 0xe280a2}, + {0x0096, 0xe28093}, + {0x0097, 0xe28094}, + {0x0098, 0xcb9c}, + {0x0099, 0xe284a2}, + {0x009a, 0x0000}, + {0x009b, 0xe280ba}, + {0x009c, 0xc593}, + {0x009d, 0x0000}, + {0x009e, 0x0000}, + {0x009f, 0xc5b8}, + {0x00a0, 0xc2a0}, + {0x00a1, 0xc2a1}, + {0x00a2, 0xc2a2}, + {0x00a3, 0xc2a3}, + {0x00a4, 0xc2a4}, + {0x00a5, 0xc2a5}, + {0x00a6, 0xc2a6}, + {0x00a7, 0xc2a7}, + {0x00a8, 0xc2a8}, + {0x00a9, 0xc2a9}, + {0x00aa, 0xc2aa}, + {0x00ab, 0xc2ab}, + {0x00ac, 0xc2ac}, + {0x00ad, 0xc2ad}, + {0x00ae, 0xc2ae}, + {0x00af, 0xc2af}, + {0x00b0, 0xc2b0}, + {0x00b1, 0xc2b1}, + {0x00b2, 0xc2b2}, + {0x00b3, 0xc2b3}, + {0x00b4, 0xc2b4}, + {0x00b5, 0xc2b5}, + {0x00b6, 0xc2b6}, + {0x00b7, 0xc2b7}, + {0x00b8, 0xc2b8}, + {0x00b9, 0xc2b9}, + {0x00ba, 0xc2ba}, + {0x00bb, 0xc2bb}, + {0x00bc, 0xc2bc}, + {0x00bd, 0xc2bd}, + {0x00be, 0xc2be}, + {0x00bf, 0xc2bf}, + {0x00c0, 0xc380}, + {0x00c1, 0xc381}, + {0x00c2, 0xc382}, + {0x00c3, 0xc482}, + {0x00c4, 0xc384}, + {0x00c5, 0xc385}, + {0x00c6, 0xc386}, + {0x00c7, 0xc387}, + {0x00c8, 0xc388}, + {0x00c9, 0xc389}, + {0x00ca, 0xc38a}, + {0x00cb, 0xc38b}, + {0x00cc, 0xcc80}, + {0x00cd, 0xc38d}, + {0x00ce, 0xc38e}, + {0x00cf, 0xc38f}, + {0x00d0, 0xc490}, + {0x00d1, 0xc391}, + {0x00d2, 0xcc89}, + {0x00d3, 0xc393}, + {0x00d4, 0xc394}, + {0x00d5, 0xc6a0}, + {0x00d6, 0xc396}, + {0x00d7, 0xc397}, + {0x00d8, 0xc398}, + {0x00d9, 0xc399}, + {0x00da, 0xc39a}, + {0x00db, 0xc39b}, + {0x00dc, 0xc39c}, + {0x00dd, 0xc6af}, + {0x00de, 0xcc83}, + {0x00df, 0xc39f}, + {0x00e0, 0xc3a0}, + {0x00e1, 0xc3a1}, + {0x00e2, 0xc3a2}, + {0x00e3, 0xc483}, + {0x00e4, 0xc3a4}, + {0x00e5, 0xc3a5}, + {0x00e6, 0xc3a6}, + {0x00e7, 0xc3a7}, + {0x00e8, 0xc3a8}, + {0x00e9, 0xc3a9}, + {0x00ea, 0xc3aa}, + {0x00eb, 0xc3ab}, + {0x00ec, 0xcc81}, + {0x00ed, 0xc3ad}, + {0x00ee, 0xc3ae}, + {0x00ef, 0xc3af}, + {0x00f0, 0xc491}, + {0x00f1, 0xc3b1}, + {0x00f2, 0xcca3}, + {0x00f3, 0xc3b3}, + {0x00f4, 0xc3b4}, + {0x00f5, 0xc6a1}, + {0x00f6, 0xc3b6}, + {0x00f7, 0xc3b7}, + {0x00f8, 0xc3b8}, + {0x00f9, 0xc3b9}, + {0x00fa, 0xc3ba}, + {0x00fb, 0xc3bb}, + {0x00fc, 0xc3bc}, + {0x00fd, 0xc6b0}, + {0x00fe, 0xe282ab}, + {0x00ff, 0xc3bf} +}; diff --git a/src/backend/utils/mb/Unicode/win866_to_utf8.map b/src/backend/utils/mb/Unicode/win866_to_utf8.map new file mode 100644 index 0000000..d2a377a --- /dev/null +++ b/src/backend/utils/mb/Unicode/win866_to_utf8.map @@ -0,0 +1,130 @@ +static pg_local_to_utf LUmapWIN866[ 128 ] = { + {0x0080, 0xd090}, + {0x0081, 0xd091}, + {0x0082, 0xd092}, + {0x0083, 0xd093}, + {0x0084, 0xd094}, + {0x0085, 0xd095}, + {0x0086, 0xd096}, + {0x0087, 0xd097}, + {0x0088, 0xd098}, + {0x0089, 0xd099}, + {0x008a, 0xd09a}, + {0x008b, 0xd09b}, + {0x008c, 0xd09c}, + {0x008d, 0xd09d}, + {0x008e, 0xd09e}, + {0x008f, 0xd09f}, + {0x0090, 0xd0a0}, + {0x0091, 0xd0a1}, + {0x0092, 0xd0a2}, + {0x0093, 0xd0a3}, + {0x0094, 0xd0a4}, + {0x0095, 0xd0a5}, + {0x0096, 0xd0a6}, + {0x0097, 0xd0a7}, + {0x0098, 0xd0a8}, + {0x0099, 0xd0a9}, + {0x009a, 0xd0aa}, + {0x009b, 0xd0ab}, + {0x009c, 0xd0ac}, + {0x009d, 0xd0ad}, + {0x009e, 0xd0ae}, + {0x009f, 0xd0af}, + {0x00a0, 0xd0b0}, + {0x00a1, 0xd0b1}, + {0x00a2, 0xd0b2}, + {0x00a3, 0xd0b3}, + {0x00a4, 0xd0b4}, + {0x00a5, 0xd0b5}, + {0x00a6, 0xd0b6}, + {0x00a7, 0xd0b7}, + {0x00a8, 0xd0b8}, + {0x00a9, 0xd0b9}, + {0x00aa, 0xd0ba}, + {0x00ab, 0xd0bb}, + {0x00ac, 0xd0bc}, + {0x00ad, 0xd0bd}, + {0x00ae, 0xd0be}, + {0x00af, 0xd0bf}, + {0x00b0, 0xe29691}, + {0x00b1, 0xe29692}, + {0x00b2, 0xe29693}, + {0x00b3, 0xe29482}, + {0x00b4, 0xe294a4}, + {0x00b5, 0xe295a1}, + {0x00b6, 0xe295a2}, + {0x00b7, 0xe29596}, + {0x00b8, 0xe29595}, + {0x00b9, 0xe295a3}, + {0x00ba, 0xe29591}, + {0x00bb, 0xe29597}, + {0x00bc, 0xe2959d}, + {0x00bd, 0xe2959c}, + {0x00be, 0xe2959b}, + {0x00bf, 0xe29490}, + {0x00c0, 0xe29494}, + {0x00c1, 0xe294b4}, + {0x00c2, 0xe294ac}, + {0x00c3, 0xe2949c}, + {0x00c4, 0xe29480}, + {0x00c5, 0xe294bc}, + {0x00c6, 0xe2959e}, + {0x00c7, 0xe2959f}, + {0x00c8, 0xe2959a}, + {0x00c9, 0xe29594}, + {0x00ca, 0xe295a9}, + {0x00cb, 0xe295a6}, + {0x00cc, 0xe295a0}, + {0x00cd, 0xe29590}, + {0x00ce, 0xe295ac}, + {0x00cf, 0xe295a7}, + {0x00d0, 0xe295a8}, + {0x00d1, 0xe295a4}, + {0x00d2, 0xe295a5}, + {0x00d3, 0xe29599}, + {0x00d4, 0xe29598}, + {0x00d5, 0xe29592}, + {0x00d6, 0xe29593}, + {0x00d7, 0xe295ab}, + {0x00d8, 0xe295aa}, + {0x00d9, 0xe29498}, + {0x00da, 0xe2948c}, + {0x00db, 0xe29688}, + {0x00dc, 0xe29684}, + {0x00dd, 0xe2968c}, + {0x00de, 0xe29690}, + {0x00df, 0xe29680}, + {0x00e0, 0xd180}, + {0x00e1, 0xd181}, + {0x00e2, 0xd182}, + {0x00e3, 0xd183}, + {0x00e4, 0xd184}, + {0x00e5, 0xd185}, + {0x00e6, 0xd186}, + {0x00e7, 0xd187}, + {0x00e8, 0xd188}, + {0x00e9, 0xd189}, + {0x00ea, 0xd18a}, + {0x00eb, 0xd18b}, + {0x00ec, 0xd18c}, + {0x00ed, 0xd18d}, + {0x00ee, 0xd18e}, + {0x00ef, 0xd18f}, + {0x00f0, 0xd081}, + {0x00f1, 0xd191}, + {0x00f2, 0xd084}, + {0x00f3, 0xd194}, + {0x00f4, 0xd087}, + {0x00f5, 0xd197}, + {0x00f6, 0xd08e}, + {0x00f7, 0xd19e}, + {0x00f8, 0xc2b0}, + {0x00f9, 0xe28899}, + {0x00fa, 0xc2b7}, + {0x00fb, 0xe2889a}, + {0x00fc, 0xe28496}, + {0x00fd, 0xc2a4}, + {0x00fe, 0xe296a0}, + {0x00ff, 0xc2a0} +}; diff --git a/src/backend/utils/mb/win866.c b/src/backend/utils/mb/win866.c new file mode 100644 index 0000000..6504e4a --- /dev/null +++ b/src/backend/utils/mb/win866.c @@ -0,0 +1,74 @@ +/* + * make KOI8->CP866(ALT) and CP866(ALT)->KOI8 translation table + * from koi-alt.tab. + * + * Tatsuo Ishii + * + * $PostgreSQL$ + */ + +#include + + +main() +{ + int i; + char koitab[128], + alttab[128]; + char buf[4096]; + int koi, + alt; + + for (i = 0; i < 128; i++) + koitab[i] = alttab[i] = 0; + + while (fgets(buf, sizeof(buf), stdin) != NULL) + { + if (*buf == '#') + continue; + sscanf(buf, "%d %d", &koi, &alt); + if (koi < 128 || koi > 255 || alt < 128 || alt > 255) + { + fprintf(stderr, "invalid value %d\n", koi); + exit(1); + } + koitab[koi - 128] = alt; + alttab[alt - 128] = koi; + } + + i = 0; + printf("static char koi2alt[] = {\n"); + while (i < 128) + { + int j = 0; + + while (j < 8) + { + printf("0x%02x", koitab[i++]); + j++; + if (i >= 128) + break; + printf(", "); + } + printf("\n"); + } + printf("};\n"); + + i = 0; + printf("static char alt2koi[] = {\n"); + while (i < 128) + { + int j = 0; + + while (j < 8) + { + printf("0x%02x", alttab[i++]); + j++; + if (i >= 128) + break; + printf(", "); + } + printf("\n"); + } + printf("};\n"); +} diff --git a/src/backend/utils/misc/pg_rusage.c b/src/backend/utils/misc/pg_rusage.c new file mode 100644 index 0000000..afd170d --- /dev/null +++ b/src/backend/utils/misc/pg_rusage.c @@ -0,0 +1,73 @@ +/*------------------------------------------------------------------------- + * + * pg_rusage.c + * Resource usage measurement support routines. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include + +#include "utils/pg_rusage.h" + + +/* + * Initialize usage snapshot. + */ +void +pg_rusage_init(PGRUsage *ru0) +{ + getrusage(RUSAGE_SELF, &ru0->ru); + gettimeofday(&ru0->tv, NULL); +} + +/* + * Compute elapsed time since ru0 usage snapshot, and format into + * a displayable string. Result is in a static string, which is + * tacky, but no one ever claimed that the Postgres backend is + * threadable... + */ +const char * +pg_rusage_show(const PGRUsage *ru0) +{ + static char result[100]; + PGRUsage ru1; + + pg_rusage_init(&ru1); + + if (ru1.tv.tv_usec < ru0->tv.tv_usec) + { + ru1.tv.tv_sec--; + ru1.tv.tv_usec += 1000000; + } + if (ru1.ru.ru_stime.tv_usec < ru0->ru.ru_stime.tv_usec) + { + ru1.ru.ru_stime.tv_sec--; + ru1.ru.ru_stime.tv_usec += 1000000; + } + if (ru1.ru.ru_utime.tv_usec < ru0->ru.ru_utime.tv_usec) + { + ru1.ru.ru_utime.tv_sec--; + ru1.ru.ru_utime.tv_usec += 1000000; + } + + snprintf(result, sizeof(result), + "CPU %d.%02ds/%d.%02du sec elapsed %d.%02d sec", + (int) (ru1.ru.ru_stime.tv_sec - ru0->ru.ru_stime.tv_sec), + (int) (ru1.ru.ru_stime.tv_usec - ru0->ru.ru_stime.tv_usec) / 10000, + (int) (ru1.ru.ru_utime.tv_sec - ru0->ru.ru_utime.tv_sec), + (int) (ru1.ru.ru_utime.tv_usec - ru0->ru.ru_utime.tv_usec) / 10000, + (int) (ru1.tv.tv_sec - ru0->tv.tv_sec), + (int) (ru1.tv.tv_usec - ru0->tv.tv_usec) / 10000); + + return result; +} diff --git a/src/bin/initdb/po/pl.po b/src/bin/initdb/po/pl.po new file mode 100644 index 0000000..0ad11c3 --- /dev/null +++ b/src/bin/initdb/po/pl.po @@ -0,0 +1,592 @@ +# INITDB Translated Messages into the Polish Language (ISO-8859-2) +# +# Copyright (c) 2005 toczek, xxxtoczekxxx@wp.pl +# Distributed under the same licensing terms as PostgreSQL itself. +# +# +msgid "" +msgstr "" +"Project-Id-Version: initdb-cs\n" +"POT-Creation-Date: 2005-01-08 13:03+0000\n" +"PO-Revision-Date: 2005-01-12 15:24+0100\n" +"Last-Translator: toczek \n" +"Language-Team:\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" + +#: initdb.c:251 initdb.c:265 +#, c-format +msgid "%s: out of memory\n" +msgstr "%s: brak pamici\n" + +#: initdb.c:372 initdb.c:1406 +#, c-format +msgid "%s: could not open file \"%s\" for reading: %s\n" +msgstr "%s: nie mona otworzy pliku \"%s\" do odczytu: %s\n" + +#: initdb.c:433 initdb.c:1033 initdb.c:1060 +#, c-format +msgid "%s: could not open file \"%s\" for writing: %s\n" +msgstr "%s: nie mona otworzy pliku \"%s\" do zapisu: %s\n" + +#: initdb.c:441 initdb.c:449 initdb.c:1040 initdb.c:1066 +#, c-format +msgid "%s: could not write file \"%s\": %s\n" +msgstr "%s: nie mona zapisa pliku \"%s\": %s\n" + +#: initdb.c:468 +#, c-format +msgid "%s: could not execute command \"%s\": %s\n" +msgstr "%s: nie mona wykona komendy \"%s\": %s\n" + +#: initdb.c:591 +#, c-format +msgid "%s: removing data directory \"%s\"\n" +msgstr "%s: usuwanie katalogu danych \"%s\"\n" + +#: initdb.c:594 +#, c-format +msgid "%s: failed to remove data directory\n" +msgstr "%s: nie udao si usunicie katalogu danych\n" + +#: initdb.c:600 +#, c-format +msgid "%s: removing contents of data directory \"%s\"\n" +msgstr "%s: usuwanie zawartoci w katalogu danych \"%s\"\n" + +#: initdb.c:603 +#, c-format +msgid "%s: failed to remove contents of data directory\n" +msgstr "%s: nie udao si usun zawartoci w katalogu danych\n" + +#: initdb.c:612 +#, c-format +msgid "%s: data directory \"%s\" not removed at user's request\n" +msgstr "%s: katalog \"%s\" nie zosta usunity na adanie uytkownika\n" + +#: initdb.c:638 +#, c-format +msgid "" +"%s: cannot be run as root\n" +"Please log in (using, e.g., \"su\") as the (unprivileged) user that will\n" +"own the server process.\n" +msgstr "" +"%s: nie mona uruchomi jako root\n" +"Prosz zalogowa (uywajac np: \"su\") si na uytkownika ktry\n" +"bdzie wacicielem procesu.\n" + +#: initdb.c:687 +#, c-format +msgid "%s: \"%s\" is not a valid server encoding name\n" +msgstr "%s: \"%s\" nie jest poprawn nazw kodowania\n" + +#: initdb.c:842 +#, c-format +msgid "%s: warning: encoding mismatch\n" +msgstr "%s: uwaga: bdne kodowanie\n" + +#: initdb.c:844 +#, c-format +msgid "" +"The encoding you selected (%s) and the encoding that the selected\n" +"locale uses (%s) are not known to match. This may lead to\n" +"misbehavior in various character string processing functions. To fix\n" +"this situation, rerun %s and either do not specify an encoding\n" +"explicitly, or choose a matching combination.\n" +msgstr "" +"Kodowanie ktre wybrae (%s) i kodowanie ktre jest uywane przez\n" +"reguy jzykowe (%s) nie zgadzaj si. Moe to prowadzi\n" +"do bdw w wielu funkcjach operujcuch na stringach.\n" +"Aby poprawi ten bd uruchom ponownie %s i albo nie ustawiaj kodowania\n" +"lub wybierz pasujc kombinacj.\n" + +#: initdb.c:974 +#, c-format +msgid "%s: could not create directory \"%s\": %s\n" +msgstr "%s: nie mona utworzy katalogu \"%s\": %s\n" + +#: initdb.c:1002 +#, c-format +msgid "" +"%s: file \"%s\" does not exist\n" +"This means you have a corrupted installation or identified\n" +"the wrong directory with the invocation option -L.\n" +msgstr "" +"%s: plik \"%s\" nie istnieje\n" +"Oznacza to i posiadasz uszkodzon instalacj lub wskazae\n" +"zy katalog przy uyciu opcji -L.\n" + +#: initdb.c:1084 +msgid "selecting default max_connections ... " +msgstr "wybieranie standardowej wartoci max_connections ... " + +#: initdb.c:1120 +msgid "selecting default shared_buffers ... " +msgstr "wybieranie standardowej wartoci shared_buffers ... " + +#: initdb.c:1153 +msgid "creating configuration files ... " +msgstr "tworzenie plikw konfiguracyjnych ... " + +#: initdb.c:1254 +#, c-format +msgid "creating template1 database in %s/base/1 ... " +msgstr "tworzenie bazy template1 w folderze %s/base/1 ... " + +#: initdb.c:1270 +#, c-format +msgid "" +"%s: input file \"%s\" does not belong to PostgreSQL %s\n" +"Check your installation or specify the correct path using the option -L.\n" +msgstr "" +"%s: plik wejciowy \"%s\" nie naley do bazy danych PostgreSQL %s\n" +"Sprawd swoj instalacj lub podaj popraw sciek przy pomocy zmiennej -L.\n" + +#: initdb.c:1345 +msgid "initializing pg_shadow ... " +msgstr "przygotowywanie pg_shadow ... " + +#: initdb.c:1381 +msgid "Enter new superuser password: " +msgstr "Podaj haso superuytkownika: " + +#: initdb.c:1382 +msgid "Enter it again: " +msgstr "Powtrz podane haso: " + +#: initdb.c:1385 +msgid "Passwords didn't match.\n" +msgstr "Podane hasa rni si.\n" + +#: initdb.c:1412 +#, c-format +msgid "%s: could not read password from file \"%s\": %s\n" +msgstr "%s: nie mona odczyta hasa z pliku \"%s\": %s\n" + +#: initdb.c:1425 +msgid "setting password ... " +msgstr "ustawianie hasa ... " + +#: initdb.c:1446 +#, c-format +msgid "%s: The password file was not generated. Please report this problem.\n" +msgstr "%s: Plik z hasem nie zosta stworzony. Prosz zgosi ten problem.\n" + +#: initdb.c:1474 +msgid "enabling unlimited row size for system tables ... " +msgstr "umoliwienie nieskoczonego rozmiaru wiersza w tabeli systemowej ... " + +#: initdb.c:1547 +msgid "initializing pg_depend ... " +msgstr "przygotowywanie pg_depend ... " + +#: initdb.c:1575 +msgid "creating system views ... " +msgstr "tworzenie widokw systemowych ... " + +#: initdb.c:1611 +msgid "loading pg_description ... " +msgstr "adowanie pg_decription ... " + +#: initdb.c:1650 +msgid "creating conversions ... " +msgstr "tworzenie konwersji ... " + +#: initdb.c:1704 +msgid "setting privileges on built-in objects ... " +msgstr "ustawianie uprawnie dla wbudowanych obiektw ... " + +#: initdb.c:1762 +msgid "creating information schema ... " +msgstr "tworzenie schematu informacyjnego ... " + +#: initdb.c:1819 +msgid "vacuuming database template1 ... " +msgstr "czyszczenie bazy template1 ... " + +#: initdb.c:1873 +msgid "copying template1 to template0 ... " +msgstr "kopiowanie bazy template1 do bazy template0 ... " + +#: initdb.c:1930 +msgid "caught signal\n" +msgstr "sygna otrzymany\n" + +#: initdb.c:1936 +#, c-format +msgid "could not write to child process: %s\n" +msgstr "nie mona zapisa do procesu potomnego: %s\n" + +#: initdb.c:1944 +msgid "ok\n" +msgstr "ok\n" + +#: initdb.c:1992 +#, c-format +msgid "%s: invalid locale name \"%s\"\n" +msgstr "%s: bdna nazwa lokalna \"%s\"\n" + +#: initdb.c:2059 +#, c-format +msgid "" +"%s initializes a PostgreSQL database cluster.\n" +"\n" +msgstr "" +"%s Przygotowanie bazy danych PostgreSQL.\n" +"\n" + +#: initdb.c:2060 +msgid "Usage:\n" +msgstr "Skadnia:\n" + +#: initdb.c:2061 +#, c-format +msgid " %s [OPTION]... [DATADIR]\n" +msgstr " %s [OPCJA]... [KATALOG-DOCELOWY]\n" + +#: initdb.c:2062 +msgid "" +"\n" +"Options:\n" +msgstr "" +"\n" +"Opcje:\n" + +#: initdb.c:2063 +msgid " [-D, --pgdata=]DATADIR location for this database cluster\n" +msgstr " [-D, --pgdata=]KATALOG-DOCELOWY lokalizacja bazy danych\n" + +#: initdb.c:2064 +msgid " -E, --encoding=ENCODING set default encoding for new databases\n" +msgstr " -E, --encoding=KODOWANIE ustawia podstawowe kodowanie dla nowej bazy\n" + +#: initdb.c:2065 +msgid "" +" --locale=LOCALE initialize database cluster with given locale\n" +msgstr "" +" --locale=LOCALE przygotowanie klastra bazy danych z podanymi reguami jzykowymi\n" + +#: initdb.c:2066 +msgid "" +" --lc-collate, --lc-ctype, --lc-messages=LOCALE\n" +" --lc-monetary, --lc-numeric, --lc-time=LOCALE\n" +" initialize database cluster with given locale\n" +" in the respective category (default taken from\n" +" environment)\n" +msgstr "" +" --lc-collate, --lc-ctype, --lc-messages=LOCALE\n" +" --lc-monetary, --lc-numeric, --lc-time=LOCALE\n" +" przygotowanie klastra bazy danych z podamymi\n" +" reguami jzykowymi dla poszczeglnych kategori\n" + +#: initdb.c:2071 +msgid " --no-locale equivalent to --locale=C\n" +msgstr " --no-locale rwnowane z opcj --locale=C\n" + +#: initdb.c:2072 +msgid "" +" -A, --auth=METHOD default authentication method for local " +"connections\n" +msgstr "" +" -A, --auth=METODA podstawowa metoda autoryzacji dla lokalnych " +"pocze\n" + +#: initdb.c:2073 +msgid " -U, --username=NAME database superuser name\n" +msgstr " -U, --username=NAZWA waciciel bazy danych\n" + +#: initdb.c:2074 +msgid "" +" -W, --pwprompt prompt for a password for the new superuser\n" +msgstr "" +" -W, --pwprompt pro o haso dla waciciela bazy danych\n" + +#: initdb.c:2075 +msgid "" +" --pwfile=FILE read password for the new superuser from file\n" +msgstr "" +" --pwfile=PLIK czytaj haso dla waciciela bazy z pliku\n" + +#: initdb.c:2076 +msgid " -?, --help show this help, then exit\n" +msgstr " -?, --help poka t pomoc i zakocz dziaanie\n" + +#: initdb.c:2077 +msgid " -V, --version output version information, then exit\n" +msgstr " -V, --versin poka informacje o wersji i zakocz\n" + +#: initdb.c:2078 +msgid "" +"\n" +"Less commonly used options:\n" +msgstr "" +"\n" +"Rzadziej uywane opcje:\n" + +#: initdb.c:2079 +msgid " -d, --debug generate lots of debugging output\n" +msgstr " -d, --debug wywietlanie informacji debugger'a\n" + +#: initdb.c:2080 +msgid " -s, --show show internal settings\n" +msgstr " -s, --show poka wewntrzne ustawienia\n" + +#: initdb.c:2081 +msgid " -L DIRECTORY where to find the input files\n" +msgstr " -L KATALOG gdzie szuka plikw wejciowych\n" + +#: initdb.c:2082 +msgid " -n, --noclean do not clean up after errors\n" +msgstr " -n, --noclean bdy nie bd porzdkowane\n" + +#: initdb.c:2083 +msgid "" +"\n" +"If the data directory is not specified, the environment variable PGDATA\n" +"is used.\n" +msgstr "" +"\n" +"Jeli katalog nie jest wskazany wtedy uywana jest zmienna PGDATA\n" +"do okrelenia tego katalogu.\n" + +#: initdb.c:2085 +msgid "" +"\n" +"Report bugs to .\n" +msgstr "" +"\n" +"Bdy prosz przesya na adres .\n" + +#: initdb.c:2178 +msgid "Running in debug mode.\n" +msgstr "Dziaanie w trybie debug.\n" + +#: initdb.c:2182 +msgid "Running in noclean mode. Mistakes will not be cleaned up.\n" +msgstr "Dziaanie w trybie nonclean. Bdy nie bd porzdkowane.\n" + +#: initdb.c:2219 initdb.c:2236 initdb.c:2456 +#, c-format +msgid "Try \"%s --help\" for more information.\n" +msgstr "Sprbuj \"%s --help\" aby uzyka wiecej informacji.\n" + +#: initdb.c:2234 +#, c-format +msgid "%s: too many command-line arguments (first is \"%s\")\n" +msgstr "%s: za dua ilo parametrw (pierwszy to \"%s\")\n" + +#: initdb.c:2242 +#, c-format +msgid "%s: password prompt and password file may not be specified together\n" +msgstr "%s: podane haso i plik hasa nie mog by podane jednoczenie\n" + +#: initdb.c:2248 +msgid "" +"\n" +"WARNING: enabling \"trust\" authentication for local connections\n" +"You can change this by editing pg_hba.conf or using the -A option the\n" +"next time you run initdb.\n" +msgstr "" +"\n" +"UWAGA: metoda autoryzacji ustawiona jako \"trust\" dla pocze.\n" +"Metod autoryzacji moesz zmieni edytujc plik pg_hba.conf\n" +"lub uywajc opcji -A przy uruchomieniu initdb.\n" + +#: initdb.c:2272 +#, c-format +msgid "%s: unrecognized authentication method \"%s\"\n" +msgstr "%s: bdny sposb autoryzacji \"%s\"\n" + +#: initdb.c:2282 +#, c-format +msgid "" +"%s: must specify a password for the superuser to enable %s authentication\n" +msgstr "" +"%s: musisz poda haso superuytkownika aby aktywowa %s autoryzacj\n" + +#: initdb.c:2297 +#, c-format +msgid "" +"%s: no data directory specified\n" +"You must identify the directory where the data for this database system\n" +"will reside. Do this with either the invocation option -D or the\n" +"environment variable PGDATA.\n" +msgstr "" +"%s: nie ustawiony katalog danych\n" +"Musisz poda katalog gdzie dane bazy danych bd przechowywane.\n" +"Moesz tego dokona uywajc opcj -D lub przy pomocy\n" +"zmiennej rodowiskowej PGDATA.\n" + +#: initdb.c:2329 +#, c-format +msgid "" +"The program \"postgres\" is needed by %s but was not found in the\n" +"same directory as \"%s\".\n" +"Check your installation.\n" +msgstr "" +"Program \"postgres\" jest wymagany przez %s ale nie zosta znaleziony \n" +"w tym samym folderze co \"%s\".\n" +"Sprawd instalcj.\n" + +#: initdb.c:2336 +#, c-format +msgid "" +"The program \"postgres\" was found by \"%s\"\n" +"but was not the same version as %s.\n" +"Check your installation.\n" +msgstr "" +"Program \"postgres\" zosta znaleziony przez \"%s\"n" +"ale nie jest w tej samej wersji co %s.\n" +"Sprawd instalacj.\n" + +#: initdb.c:2355 +#, c-format +msgid "%s: input file location must be an absolute path\n" +msgstr "%s: lokalizacja plikw wejciowych musi by bezwzgldna\n" + +#: initdb.c:2363 +#, c-format +msgid "%s: could not determine valid short version string\n" +msgstr "%s: nie mona ustali poprawnego skrconego opisu wersji\n" + +#: initdb.c:2416 +#, c-format +msgid "" +"The files belonging to this database system will be owned by user \"%s\".\n" +"This user must also own the server process.\n" +"\n" +msgstr "" +"Wacicielem plikw nalecych do sytemu bazy danych bdzie uytkownik \"%s\".\n" +"Ten uytkownik musi jednoczenie by wacicielem procesu serwera.\n" + +#: initdb.c:2426 +#, c-format +msgid "The database cluster will be initialized with locale %s.\n" +msgstr "Klaster bazy zostanie utworzony z zestawem regu jzykowych %s.\n" + +#: initdb.c:2429 +#, c-format +msgid "" +"The database cluster will be initialized with locales\n" +" COLLATE: %s\n" +" CTYPE: %s\n" +" MESSAGES: %s\n" +" MONETARY: %s\n" +" NUMERIC: %s\n" +" TIME: %s\n" +msgstr "" +"Klaster bazy danych zostanie utowrzony z zestawem regu jzykowych\n" +" COLLATE: %s\n" +" CTYPE: %s\n" +" MESSAGES: %s\n" +" MONETARY: %s\n" +" NUMERIC: %s\n" +" TIME: %s\n" + +#: initdb.c:2454 +#, c-format +msgid "%s: could not find suitable encoding for locale \"%s\"\n" +msgstr "%s: nie mona znale odpowiedniego kodowania dla regu jzykowych \"%s\"\n" + +#: initdb.c:2455 +#, c-format +msgid "Rerun %s with the -E option.\n" +msgstr "Wcz polecenie %s ponownie z opcj -E.\n" + +#: initdb.c:2462 +#, c-format +msgid "The default database encoding has accordingly been set to %s.\n" +msgstr "Podstawowe kodowanie bazy danych zostao ustawione jako %s.\n" + +#: initdb.c:2503 +#, c-format +msgid "creating directory %s ... " +msgstr "tworzenie katalogu %s ... " + +#: initdb.c:2517 +#, c-format +msgid "fixing permissions on existing directory %s ... " +msgstr "ustalanie uprawnie katalogu %s ... " + +#: initdb.c:2523 +#, c-format +msgid "%s: could not change permissions of directory \"%s\": %s\n" +msgstr "%s: nie mona zmieni uprawnie katalogu \"%s\": %s\n" + +#: initdb.c:2536 +#, c-format +msgid "" +"%s: directory \"%s\" exists but is not empty\n" +"If you want to create a new database system, either remove or empty\n" +"the directory \"%s\" or run %s\n" +"with an argument other than \"%s\".\n" +msgstr "" +"%s: katalog \"%s\" istnieje ale nie jest pusty\n" +"Jeli chcesz stworzy now baz danych usu ten katalog,\n" +"wyczy katalog \"%s\" lub uruchom program %s\n" +"z innym parametrem okrelajcym katalog ni \"%s\".\n" + +#: initdb.c:2545 +#, c-format +msgid "%s: could not access directory \"%s\": %s\n" +msgstr "%s: brak dostepu do katalogu \"%s\": %s\n" + +#: initdb.c:2554 +#, c-format +msgid "creating directory %s/%s ... " +msgstr "tworzenie katalogu %s/%s ... " + +#: initdb.c:2622 +#, c-format +msgid "" +"\n" +"Success. You can now start the database server using:\n" +"\n" +" %s%s%s%spostmaster -D %s%s%s\n" +"or\n" +" %s%s%s%spg_ctl -D %s%s%s -l logfile start\n" +"\n" +msgstr "" +"\n" +"Utworzono. Teraz moesz uruchomi serwer bazy danych uywajc:\n" +"\n" +" %s%s%s%spostmaster -D %s%s%s\n" +"lub\n" +" %s%s%s%spg_ctl -D %s%s%s -l plik_z_logami start\n" +"\n" + +#: ../../port/dirmod.c:75 ../../port/dirmod.c:88 ../../port/dirmod.c:101 +msgid "out of memory\n" +msgstr "brak pamici\n" + +#: ../../port/exec.c:193 ../../port/exec.c:306 ../../port/exec.c:349 +#, c-format +msgid "could not identify current directory: %s" +msgstr "nie mona zidentyfikowa aktualnego katalogu: %s" + + +#: ../../port/exec.c:322 ../../port/exec.c:358 +#, c-format +msgid "could not change directory to \"%s\"" +msgstr "nie mona zmieni katalogu na \"%s\"" + +#: ../../port/exec.c:337 +#, c-format +msgid "could not read symbolic link \"%s\"" +msgstr "nie mona odczyta odwoania symbolicznego \"%s\"" + +#: ../../port/exec.c:585 +#, c-format +msgid "child process exited with exit code %d" +msgstr "proces potomny zakoczyl dziaanie z kodem %d" + +#: ../../port/exec.c:588 +#, c-format +msgid "child process was terminated by signal %d" +msgstr "proces potomny zosta przerwany przez sygna %d" + +#: ../../port/exec.c:591 +#, c-format +msgid "child process exited with unrecognized status %d" +msgstr "proces potomny zakoczy dziaanie z nieznanym stanem %d" diff --git a/src/bin/pg_config/po/pl.po b/src/bin/pg_config/po/pl.po new file mode 100644 index 0000000..220b2f4 --- /dev/null +++ b/src/bin/pg_config/po/pl.po @@ -0,0 +1,151 @@ +# PG_CONFIG Translated Messages into the Polish Language (ISO-8859-2) +# +# Copyright (c) 2005 toczek, xxxtoczekxxx@wp.pl +# Distributed under the same licensing terms as PostgreSQL itself. +# +# +msgid "" +msgstr "" +"Project-Id-Version: pg_config\n" +"POT-Creation-Date: 2005-01-11 19:05+0000\n" +"PO-Revision-Date: 2005-01-12 16:0+0100\n" +"Last-Translator: toczek \n" +"Language-Team:\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" + +#: pg_config.c:36 +#, c-format +msgid "" +"\n" +"%s provides information about the installed version of PostgreSQL.\n" +"\n" +msgstr "" +"\n" +"%s dostarcza informacji na temat zainstalowanej wersji PostgreSQL.\n" +"\n" + +#: pg_config.c:37 +msgid "Usage:\n" +msgstr "Skadnia:\n" + +#: pg_config.c:38 +#, c-format +msgid "" +" %s OPTION...\n" +"\n" +msgstr "" +" %s OPCJA...\n" +"\n" + +#: pg_config.c:39 +msgid "Options:\n" +msgstr "Opcje:\n" + +#: pg_config.c:40 +msgid " --bindir show location of user executables\n" +msgstr " --bindir poka lokalizacj plikw uytkownika\n" + +#: pg_config.c:41 +msgid "" +" --includedir show location of C header files of the client\n" +" interfaces\n" +msgstr "" +" --includedir poka lokalizacj nagwkw C interfejsu \n" +" uytkownika\n" + +#: pg_config.c:43 +msgid "" +" --includedir-server show location of C header files for the server\n" +msgstr " --includedir-server poka lokalizacj nagwkw C dla serwera\n" + +#: pg_config.c:44 +msgid " --libdir show location of object code libraries\n" +msgstr " --libdir poka lokalizacj bibliotek obiektw\n" + +#: pg_config.c:45 +msgid " --pkglibdir show location of dynamically loadable modules\n" +msgstr " --pkglibdir poka lokalizacj dynamicznych moduw\n" + +#: pg_config.c:46 +msgid " --pgxs show location of extension makefile\n" +msgstr " --pgxs poka lokalizacj makefile\n" + +#: pg_config.c:47 +msgid "" +" --configure show options given to \"configure\" script when\n" +" PostgreSQL was built\n" +msgstr "" +" --configure poka opcje uyte przy skrypcie \"configure\" \n" +" podczas budowania PostgreSQL\n" + +#: pg_config.c:49 +msgid " --version show the PostgreSQL version, then exit\n" +msgstr " --version poka wersj PostgreSQL i zakocz\n" + +#: pg_config.c:50 +msgid "" +" --help show this help, then exit\n" +"\n" +msgstr "" +" --help poka pomoc i zakocz\n" +"\n" + +#: pg_config.c:51 +msgid "Report bugs to .\n" +msgstr "Bdy prosz przesya na adres .\n" + +#: pg_config.c:57 +#, c-format +msgid "" +"\n" +"Try \"%s --help\" for more information\n" +msgstr "" +"\n" +"Sprbuj \"%s --help\" aby uzyka wicej informacji.\n" + +#: pg_config.c:75 +#, c-format +msgid "%s: argument required\n" +msgstr "%s: parametr wymagany\n" + +#: pg_config.c:104 +#, c-format +msgid "%s: invalid argument: %s\n" +msgstr "%s: niepoprawny parametr: %s\n" + +#: pg_config.c:113 +#, c-format +msgid "%s: could not find own executable\n" +msgstr "%s: nie mona znale pliku wykonywalnego\n" + +#: ../../port/exec.c:193 ../../port/exec.c:306 ../../port/exec.c:349 +#, c-format +msgid "could not identify current directory: %s" +msgstr "nie mona zidentyfikowa biecego katalogu: %s" + +#: ../../port/exec.c:322 ../../port/exec.c:358 +#, c-format +msgid "could not change directory to \"%s\"" +msgstr "nie mona zmieni katalogu na \"%s\"" + +#: ../../port/exec.c:337 +#, c-format +msgid "could not read symbolic link \"%s\"" +msgstr "nie mona odczyta odwoania symbolicznego \"%s\"" + +#: ../../port/exec.c:585 +#, c-format +msgid "child process exited with exit code %d" +msgstr "proces potomny zakoczyl dziaanie z kodem %d" + +#: ../../port/exec.c:588 +#, c-format +msgid "child process was terminated by signal %d" +msgstr "proces potomny zosta przerwany przez sygna %d" + +#: ../../port/exec.c:591 +#, c-format +msgid "child process exited with unrecognized status %d" +msgstr "proces potomny zakoczy dziaanie z nieznanym stanem %d" diff --git a/src/bin/pg_controldata/po/pl.po b/src/bin/pg_controldata/po/pl.po new file mode 100644 index 0000000..bac6205 --- /dev/null +++ b/src/bin/pg_controldata/po/pl.po @@ -0,0 +1,244 @@ +# PG_CONTROLDATA Translated Messages into the Polish Language (ISO-8859-2) +# +# Copyright (c) 2005 toczek, xxxtoczekxxx@wp.pl +# Distributed under the same licensing terms as PostgreSQL itself. +# +# +msgid "" +msgstr "" +"Project-Id-Version: pg_controldata\n" +"POT-Creation-Date: 2005-01-09 19:03+0000\n" +"PO-Revision-Date: 2005-01-10 1:47+0100\n" +"Last-Translator: toczek \n" +"Language-Team:\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" + +#: pg_controldata.c:26 +#, c-format +msgid "" +"%s displays control information of a PostgreSQL database cluster.\n" +"\n" +msgstr "" +"%s wywietla informacje kontrolne klastra bazy danych PostgreSQL.\n" +"\n" + +#: pg_controldata.c:30 +#, c-format +msgid "" +"Usage:\n" +" %s [OPTION] [DATADIR]\n" +"\n" +"Options:\n" +" --help show this help, then exit\n" +" --version output version information, then exit\n" +msgstr "" +"Skadnia:\n" +" %s [OPCJA] [KATALOG]\n" +"\n" +"Opcje:\n" +" --help poka ekran pomocy i zakocz\n" +" --version wywietl informacje o wersji i zakocz\n" + +#: pg_controldata.c:38 +msgid "" +"\n" +"If no data directory (DATADIR) is specified, the environment variable " +"PGDATA\n" +"is used.\n" +"\n" +msgstr "" +"\n" +"W przypadku gdy katalog danych nie jest podany (DATADIR), zmienna " +"rodowiskowa PGDATA\n" +"jest uywana.\n" +"\n" + +#: pg_controldata.c:40 +msgid "Report bugs to .\n" +msgstr "Bdy prosz przesya na adres .\n" + +#: pg_controldata.c:50 +msgid "starting up" +msgstr "wczanie" + +#: pg_controldata.c:52 +msgid "shut down" +msgstr "wycz baz danych" + +#: pg_controldata.c:54 +msgid "shutting down" +msgstr "wyczanie bazy danych" + +#: pg_controldata.c:56 +msgid "in recovery" +msgstr "baza danych w trybie odzyskiwania" + +#: pg_controldata.c:58 +msgid "in production" +msgstr "baza danych w trybie produkcji" + +#: pg_controldata.c:60 +msgid "unrecognized status code" +msgstr "nieznany kod statusu" + +#: pg_controldata.c:102 +#, c-format +msgid "%s: no data directory specified\n" +msgstr "%s: katalog danych nie zosta ustawiony\n" + +#: pg_controldata.c:103 +#, c-format +msgid "Try \"%s --help\" for more information.\n" +msgstr "Sprbuj \"%s --help\" aby uzyka wicej informacji.\n" + +#: pg_controldata.c:111 +#, c-format +msgid "%s: could not open file \"%s\" for reading: %s\n" +msgstr "%s: nie mona otworzy pliku \"%s\" do odczytu: %s\n" + +#: pg_controldata.c:118 +#, c-format +msgid "%s: could not read file \"%s\": %s\n" +msgstr "%s: nie mona otworzy pliku \"%s\" do zapisu: %s\n" + +#: pg_controldata.c:132 +msgid "" +"WARNING: Calculated CRC checksum does not match value stored in file.\n" +"Either the file is corrupt, or it has a different layout than this program\n" +"is expecting. The results below are untrustworthy.\n" +"\n" +msgstr "" +"UWAGA: obliczona suma kontrolna CRC pliku nie zgadza si.\n" +"Albo plik jest uszkodzony albo posiada inny ukad ni program spodziewa si.\n" +"Rezultaty mog by niepewne.\n" +"\n" + +#: pg_controldata.c:152 +#, c-format +msgid "pg_control version number: %u\n" +msgstr "pg_control w wersji numer: %u\n" + +#: pg_controldata.c:153 +#, c-format +msgid "Catalog version number: %u\n" +msgstr "Katalog w wersji numer: %u\n" + +#: pg_controldata.c:154 +#, c-format +msgid "Database system identifier: %s\n" +msgstr "Identyfikator systemu bazy danych: %s\n" + +#: pg_controldata.c:155 +#, c-format +msgid "Database cluster state: %s\n" +msgstr "Stan klastra bazy danych: %s\n" + +#: pg_controldata.c:156 +#, c-format +msgid "pg_control last modified: %s\n" +msgstr "pg_control ostantio modyfikowano: %s\n" + +#: pg_controldata.c:157 +#, c-format +msgid "Current log file ID: %u\n" +msgstr "Aktualne ID pliku logw: %u\n" + +#: pg_controldata.c:158 +#, c-format +msgid "Next log file segment: %u\n" +msgstr "Nastpny segment pliku logw: %u\n" + +#: pg_controldata.c:159 +#, c-format +msgid "Latest checkpoint location: %X/%X\n" +msgstr "Najnowsza lokalizacja punktu kontrolnego: %X/%X\n" + +#: pg_controldata.c:161 +#, c-format +msgid "Prior checkpoint location: %X/%X\n" +msgstr "Uprzednia lokalizacja punktu kontrolnego: %X/%X\n" + +#: pg_controldata.c:163 +#, c-format +msgid "Latest checkpoint's REDO location: %X/%X\n" +msgstr "Najnowsza lokalizacja punktu kontrolnego REDO: %X/%X\n" + +#: pg_controldata.c:165 +#, c-format +msgid "Latest checkpoint's UNDO location: %X/%X\n" +msgstr "Najnowsza lokalizacja punktu kontrolnego UNDO: %X/%X\n" + +#: pg_controldata.c:167 +#, c-format +msgid "Latest checkpoint's TimeLineID: %u\n" +msgstr "TimeLineID najnowszego punktu kontrolnego: %u\n" + +#: pg_controldata.c:168 +#, c-format +msgid "Latest checkpoint's NextXID: %u\n" +msgstr "NextXID najnowszego punktu kontrolnego: %u\n" + +#: pg_controldata.c:169 +#, c-format +msgid "Latest checkpoint's NextOID: %u\n" +msgstr "NextOID najnowszego punktu kontrolnego: %u\n" + +#: pg_controldata.c:170 +#, c-format +msgid "Time of latest checkpoint: %s\n" +msgstr "Czas najnowszego punktu kontrolnego: %s\n" + +#: pg_controldata.c:171 +#, c-format +msgid "Database block size: %u\n" +msgstr "Wielko bloku bazy danych: %u\n" + +#: pg_controldata.c:172 +#, c-format +msgid "Blocks per segment of large relation: %u\n" +msgstr "Bloki na segment s w relacji: %u\n" + +#: pg_controldata.c:173 +#, c-format +msgid "Bytes per WAL segment: %u\n" +msgstr "Bajtw na segment WAL: %u\n" + +#: pg_controldata.c:174 +#, c-format +msgid "Maximum length of identifiers: %u\n" +msgstr "Maksymalna dugo identyfikatorw: %u\n" + +#: pg_controldata.c:175 +#, c-format +msgid "Maximum number of function arguments: %u\n" +msgstr "Maksymalna ilo argumentw funkcji: %u\n" + +#: pg_controldata.c:176 +#, c-format +msgid "Date/time type storage: %s\n" +msgstr "Typ przechowywania daty/czasu: %s\n" + +#: pg_controldata.c:177 +msgid "64-bit integers" +msgstr "64-bit'owe zmienne integer" + +#: pg_controldata.c:177 +msgid "floating-point numbers" +msgstr "liczby zmiennoprzecinkowe" + +#: pg_controldata.c:178 +#, c-format +msgid "Maximum length of locale name: %u\n" +msgstr "Maksymalna dugo nazwy lokalnej: %u\n" + +#: pg_controldata.c:179 +#, c-format +msgid "LC_COLLATE: %s\n" +msgstr "LC_COLLATE: %s\n" + +#: pg_controldata.c:180 +#, c-format +msgid "LC_CTYPE: %s\n" +msgstr "LC_CTYPE: %s\n" diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c new file mode 100644 index 0000000..230ba88 --- /dev/null +++ b/src/bin/scripts/reindexdb.c @@ -0,0 +1,351 @@ +/*------------------------------------------------------------------------- + * + * reindexdb + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" +#include "common.h" +#include "dumputils.h" + + +static void reindex_one_database(const char *name, const char *dbname, + const char *type, const char *host, + const char *port, const char *username, + bool password, const char *progname, + bool echo, bool quiet); +static void reindex_all_databases(const char *host, const char *port, + const char *username, bool password, + const char *progname, bool echo, + bool quiet); +static void reindex_system_catalogs(const char *dbname, + const char *host, const char *port, + const char *username, bool password, + const char *progname, bool echo, + bool quiet); +static void help(const char *progname); + +int +main(int argc, char *argv[]) +{ + static struct option long_options[] = { + {"host", required_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {"username", required_argument, NULL, 'U'}, + {"password", no_argument, NULL, 'W'}, + {"echo", no_argument, NULL, 'e'}, + {"quiet", no_argument, NULL, 'q'}, + {"dbname", required_argument, NULL, 'd'}, + {"all", no_argument, NULL, 'a'}, + {"system", no_argument, NULL, 's'}, + {"table", required_argument, NULL, 't'}, + {"index", required_argument, NULL, 'i'}, + {NULL, 0, NULL, 0} + }; + + const char *progname; + int optindex; + int c; + + const char *dbname = NULL; + const char *host = NULL; + const char *port = NULL; + const char *username = NULL; + bool password = false; + bool syscatalog = false; + bool alldb = false; + bool echo = false; + bool quiet = false; + const char *table = NULL; + const char *index = NULL; + + progname = get_progname(argv[0]); + set_pglocale_pgservice(argv[0], "pgscripts"); + + handle_help_version_opts(argc, argv, "reindexdb", help); + + /* process command-line options */ + while ((c = getopt_long(argc, argv, "h:p:U:Weqd:ast:i:", long_options, &optindex)) != -1) + { + switch (c) + { + case 'h': + host = optarg; + break; + case 'p': + port = optarg; + break; + case 'U': + username = optarg; + break; + case 'W': + password = true; + break; + case 'e': + echo = true; + break; + case 'q': + quiet = true; + break; + case 'd': + dbname = optarg; + break; + case 'a': + alldb = true; + break; + case 's': + syscatalog = true; + break; + case 't': + table = optarg; + break; + case 'i': + index = optarg; + break; + default: + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(1); + } + } + + switch (argc - optind) + { + case 0: + break; + case 1: + dbname = argv[optind]; + break; + default: + fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind + 1]); + fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname); + exit(1); + } + + if (alldb) + { + if (dbname) + { + fprintf(stderr, _("%s: cannot reindex all databases and a specific one at the same time\n"), progname); + exit(1); + } + if (syscatalog) + { + fprintf(stderr, _("%s: cannot reindex all databases and system catalogs at the same time\n"), progname); + exit(1); + } + if (table) + { + fprintf(stderr, _("%s: cannot reindex a specific table in all databases\n"), progname); + exit(1); + } + if (index) + { + fprintf(stderr, _("%s: cannot reindex a specific index in all databases\n"), progname); + exit(1); + } + + reindex_all_databases(host, port, username, password, + progname, echo, quiet); + } + else if (syscatalog) + { + if (table) + { + fprintf(stderr, _("%s: cannot reindex a specific table and system catalogs at the same time\n"), progname); + exit(1); + } + if (index) + { + fprintf(stderr, _("%s: cannot reindex a specific index and system catalogs at the same time\n"), progname); + exit(1); + } + + if (dbname == NULL) + { + if (getenv("PGDATABASE")) + dbname = getenv("PGDATABASE"); + else if (getenv("PGUSER")) + dbname = getenv("PGUSER"); + else + dbname = get_user_name(progname); + } + + reindex_system_catalogs(dbname, host, port, username, password, + progname, echo, quiet); + } + else + { + if (dbname == NULL) + { + if (getenv("PGDATABASE")) + dbname = getenv("PGDATABASE"); + else if (getenv("PGUSER")) + dbname = getenv("PGUSER"); + else + dbname = get_user_name(progname); + } + + if (index) + reindex_one_database(index, dbname, "INDEX", host, port, + username, password, progname, echo, quiet); + if (table) + reindex_one_database(table, dbname, "TABLE", host, port, + username, password, progname, echo, quiet); + /* reindex database only if index or table is not specified */ + if (index == NULL && table == NULL) + reindex_one_database(dbname, dbname, "DATABASE", host, port, + username, password, progname, echo, quiet); + } + + exit(0); +} + +static void +reindex_one_database(const char *name, const char *dbname, const char *type, + const char *host, const char *port, const char *username, + bool password, const char *progname, bool echo, + bool quiet) +{ + PQExpBufferData sql; + + PGconn *conn; + PGresult *result; + + initPQExpBuffer(&sql); + + appendPQExpBuffer(&sql, "REINDEX"); + if (strcmp(type, "TABLE") == 0) + appendPQExpBuffer(&sql, " TABLE %s", fmtId(name)); + else if (strcmp(type, "INDEX") == 0) + appendPQExpBuffer(&sql, " INDEX %s", fmtId(name)); + else if (strcmp(type, "DATABASE") == 0) + appendPQExpBuffer(&sql, " DATABASE %s", fmtId(name)); + appendPQExpBuffer(&sql, ";\n"); + + conn = connectDatabase(dbname, host, port, username, password, progname); + + if (echo) + printf("%s", sql.data); + result = PQexec(conn, sql.data); + + if (PQresultStatus(result) != PGRES_COMMAND_OK) + { + if (strcmp(type, "TABLE") == 0) + fprintf(stderr, _("%s: reindexing of table \"%s\" in database \"%s\" failed: %s"), + progname, name, dbname, PQerrorMessage(conn)); + if (strcmp(type, "INDEX") == 0) + fprintf(stderr, _("%s: reindexing of index \"%s\" in database \"%s\" failed: %s"), + progname, name, dbname, PQerrorMessage(conn)); + else + fprintf(stderr, _("%s: reindexing of database \"%s\" failed: %s"), + progname, dbname, PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + + PQclear(result); + PQfinish(conn); + termPQExpBuffer(&sql); + + if (!quiet) + { + puts("REINDEX"); + fflush(stdout); + } +} + +static void +reindex_all_databases(const char *host, const char *port, + const char *username, bool password, + const char *progname, bool echo, bool quiet) +{ + PGconn *conn; + PGresult *result; + int i; + + conn = connectDatabase("postgres", host, port, username, password, progname); + result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn;", progname, echo); + PQfinish(conn); + + for (i = 0; i < PQntuples(result); i++) + { + char *dbname = PQgetvalue(result, i, 0); + + if (!quiet) + fprintf(stderr, _("%s: reindexing database \"%s\"\n"), progname, dbname); + + reindex_one_database(dbname, dbname, "DATABASE", host, port, username, + password, progname, echo, quiet); + } + + PQclear(result); +} + +static void +reindex_system_catalogs(const char *dbname, const char *host, const char *port, + const char *username, bool password, + const char *progname, bool echo, bool quiet) +{ + PQExpBufferData sql; + + PGconn *conn; + PGresult *result; + + initPQExpBuffer(&sql); + + appendPQExpBuffer(&sql, "REINDEX SYSTEM %s;\n", dbname); + + conn = connectDatabase(dbname, host, port, username, password, progname); + + if (echo) + printf("%s", sql.data); + result = PQexec(conn, sql.data); + + if (PQresultStatus(result) != PGRES_COMMAND_OK) + { + fprintf(stderr, _("%s: reindexing of system catalogs failed: %s"), + progname, PQerrorMessage(conn)); + PQfinish(conn); + exit(1); + } + + PQclear(result); + PQfinish(conn); + termPQExpBuffer(&sql); + + if (!quiet) + { + puts("REINDEX"); + fflush(stdout); + } +} + +static void +help(const char *progname) +{ + printf(_("%s reindexes a PostgreSQL database.\n\n"), progname); + printf(_("Usage:\n")); + printf(_(" %s [OPTION]... [DBNAME]\n"), progname); + printf(_("\nOptions:\n")); + printf(_(" -a, --all reindex all databases\n")); + printf(_(" -s, --system reindex system catalogs\n")); + printf(_(" -d, --dbname=DBNAME database to reindex\n")); + printf(_(" -t, --table=TABLE reindex specific table only\n")); + printf(_(" -i, --index=INDEX recreate specific index only\n")); + printf(_(" -e, --echo show the commands being sent to the server\n")); + printf(_(" -q, --quiet don't write any messages\n")); + printf(_(" --help show this help, then exit\n")); + printf(_(" --version output version information, then exit\n")); + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); + printf(_(" -p, --port=PORT database server port\n")); + printf(_(" -U, --username=USERNAME user name to connect as\n")); + printf(_(" -W, --password prompt for password\n")); + printf(_("\nRead the description of the SQL command REINDEX for details.\n")); + printf(_("\nReport bugs to .\n")); +} diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h new file mode 100644 index 0000000..d25b9a4 --- /dev/null +++ b/src/include/access/twophase.h @@ -0,0 +1,52 @@ +/*------------------------------------------------------------------------- + * + * twophase.h + * Two-phase-commit related declarations. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef TWOPHASE_H +#define TWOPHASE_H + +#include "storage/proc.h" +#include "utils/timestamp.h" + + +/* + * GlobalTransactionData is defined in twophase.c; other places have no + * business knowing the internal definition. + */ +typedef struct GlobalTransactionData *GlobalTransaction; + +/* GUC variable */ +extern int max_prepared_xacts; + +extern Size TwoPhaseShmemSize(void); +extern void TwoPhaseShmemInit(void); + +extern PGPROC *TwoPhaseGetDummyProc(TransactionId xid); + +extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid, + TimestampTz prepared_at, + Oid owner, Oid databaseid); + +extern void StartPrepare(GlobalTransaction gxact); +extern void EndPrepare(GlobalTransaction gxact); + +extern TransactionId PrescanPreparedTransactions(void); +extern void RecoverPreparedTransactions(void); + +extern void RecreateTwoPhaseFile(TransactionId xid, void *content, int len); +extern void RemoveTwoPhaseFile(TransactionId xid, bool giveWarning); + +extern void CheckPointTwoPhase(XLogRecPtr redo_horizon); + +extern void FinishPreparedTransaction(const char *gid, bool isCommit); + +#endif /* TWOPHASE_H */ diff --git a/src/include/access/twophase_rmgr.h b/src/include/access/twophase_rmgr.h new file mode 100644 index 0000000..bbda68d --- /dev/null +++ b/src/include/access/twophase_rmgr.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * twophase_rmgr.h + * Two-phase-commit resource managers definition + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef TWOPHASE_RMGR_H +#define TWOPHASE_RMGR_H + +typedef void (*TwoPhaseCallback) (TransactionId xid, uint16 info, + void *recdata, uint32 len); +typedef uint8 TwoPhaseRmgrId; + +/* + * Built-in resource managers + */ +#define TWOPHASE_RM_END_ID 0 +#define TWOPHASE_RM_LOCK_ID 1 +#define TWOPHASE_RM_INVAL_ID 2 +#define TWOPHASE_RM_FLATFILES_ID 3 +#define TWOPHASE_RM_NOTIFY_ID 4 +#define TWOPHASE_RM_MAX_ID TWOPHASE_RM_NOTIFY_ID + +extern const TwoPhaseCallback twophase_recover_callbacks[]; +extern const TwoPhaseCallback twophase_postcommit_callbacks[]; +extern const TwoPhaseCallback twophase_postabort_callbacks[]; + + +extern void RegisterTwoPhaseRecord(TwoPhaseRmgrId rmid, uint16 info, + const void *data, uint32 len); + +#endif /* TWOPHASE_RMGR_H */ diff --git a/src/include/catalog/pg_auth_members.h b/src/include/catalog/pg_auth_members.h new file mode 100644 index 0000000..bb04130 --- /dev/null +++ b/src/include/catalog/pg_auth_members.h @@ -0,0 +1,54 @@ +/*------------------------------------------------------------------------- + * + * pg_auth_members.h + * definition of the system "authorization identifier members" relation + * (pg_auth_members) along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + * NOTES + * the genbki.sh script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_AUTH_MEMBERS_H +#define PG_AUTH_MEMBERS_H + +/* ---------------- + * pg_auth_members definition. cpp turns this into + * typedef struct FormData_pg_auth_members + * ---------------- + */ +#define AuthMemRelationId 1261 + +CATALOG(pg_auth_members,1261) BKI_SHARED_RELATION BKI_WITHOUT_OIDS +{ + Oid roleid; /* ID of a role */ + Oid member; /* ID of a member of that role */ + Oid grantor; /* who granted the membership */ + bool admin_option; /* granted with admin option? */ +} FormData_pg_auth_members; + +/* ---------------- + * Form_pg_auth_members corresponds to a pointer to a tuple with + * the format of pg_auth_members relation. + * ---------------- + */ +typedef FormData_pg_auth_members *Form_pg_auth_members; + +/* ---------------- + * compiler constants for pg_auth_members + * ---------------- + */ +#define Natts_pg_auth_members 4 +#define Anum_pg_auth_members_roleid 1 +#define Anum_pg_auth_members_member 2 +#define Anum_pg_auth_members_grantor 3 +#define Anum_pg_auth_members_admin_option 4 + +#endif /* PG_AUTH_MEMBERS_H */ diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h new file mode 100644 index 0000000..cd9006c --- /dev/null +++ b/src/include/catalog/pg_authid.h @@ -0,0 +1,98 @@ +/*------------------------------------------------------------------------- + * + * pg_authid.h + * definition of the system "authorization identifier" relation (pg_authid) + * along with the relation's initial contents. + * + * pg_shadow and pg_group are now publicly accessible views on pg_authid. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + * NOTES + * the genbki.sh script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_AUTHID_H +#define PG_AUTHID_H + +/* + * The CATALOG definition has to refer to the type of rolvaliduntil as + * "timestamptz" (lower case) so that bootstrap mode recognizes it. But + * the C header files define this type as TimestampTz. Since the field is + * potentially-null and therefore can't be accessed directly from C code, + * there is no particular need for the C struct definition to show the + * field type as TimestampTz --- instead we just make it Datum. + */ + +#define timestamptz Datum + + +/* ---------------- + * pg_authid definition. cpp turns this into + * typedef struct FormData_pg_authid + * ---------------- + */ +#define AuthIdRelationId 1260 + +CATALOG(pg_authid,1260) BKI_SHARED_RELATION +{ + NameData rolname; /* name of role */ + bool rolsuper; /* read this field via superuser() only! */ + bool rolinherit; /* inherit privileges from other roles? */ + bool rolcreaterole; /* allowed to create more roles? */ + bool rolcreatedb; /* allowed to create databases? */ + bool rolcatupdate; /* allowed to alter catalogs manually? */ + bool rolcanlogin; /* allowed to log in as session user? */ + int4 rolconnlimit; /* max connections allowed (-1=no limit) */ + + /* remaining fields may be null; use heap_getattr to read them! */ + text rolpassword; /* password, if any */ + timestamptz rolvaliduntil; /* password expiration time, if any */ + text rolconfig[1]; /* GUC settings to apply at login */ +} FormData_pg_authid; + +#undef timestamptz + + +/* ---------------- + * Form_pg_authid corresponds to a pointer to a tuple with + * the format of pg_authid relation. + * ---------------- + */ +typedef FormData_pg_authid *Form_pg_authid; + +/* ---------------- + * compiler constants for pg_authid + * ---------------- + */ +#define Natts_pg_authid 11 +#define Anum_pg_authid_rolname 1 +#define Anum_pg_authid_rolsuper 2 +#define Anum_pg_authid_rolinherit 3 +#define Anum_pg_authid_rolcreaterole 4 +#define Anum_pg_authid_rolcreatedb 5 +#define Anum_pg_authid_rolcatupdate 6 +#define Anum_pg_authid_rolcanlogin 7 +#define Anum_pg_authid_rolconnlimit 8 +#define Anum_pg_authid_rolpassword 9 +#define Anum_pg_authid_rolvaliduntil 10 +#define Anum_pg_authid_rolconfig 11 + +/* ---------------- + * initial contents of pg_authid + * + * The uppercase quantities will be replaced at initdb time with + * user choices. + * ---------------- + */ +DATA(insert OID = 10 ( "POSTGRES" t t t t t t -1 _null_ _null_ _null_ )); + +#define BOOTSTRAP_SUPERUSERID 10 + +#endif /* PG_AUTHID_H */ diff --git a/src/include/catalog/pg_autovacuum.h b/src/include/catalog/pg_autovacuum.h new file mode 100644 index 0000000..d39ff0a --- /dev/null +++ b/src/include/catalog/pg_autovacuum.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * pg_autovacuum.h + * definition of the system "autovacuum" relation (pg_autovacuum) + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef PG_AUTOVACUUM_H +#define PG_AUTOVACUUM_H + +/* ---------------- + * postgres.h contains the system type definitions and the + * CATALOG(), BOOTSTRAP and DATA() sugar words so this file + * can be read by both genbki.sh and the C compiler. + * ---------------- + */ + +/* ---------------- + * pg_autovacuum definition. cpp turns this into + * typedef struct FormData_pg_autovacuum + * ---------------- + */ +#define AutovacuumRelationId 1248 +CATALOG(pg_autovacuum,1248) BKI_WITHOUT_OIDS +{ + Oid vacrelid; /* OID of table */ + bool enabled; /* enabled for this table? */ + int4 vac_base_thresh; /* base threshold value */ + float4 vac_scale_factor; /* reltuples scaling factor */ + int4 anl_base_thresh; /* base threshold value */ + float4 anl_scale_factor; /* reltuples scaling factor */ + int4 vac_cost_delay; /* vacuum cost-based delay */ + int4 vac_cost_limit; /* vacuum cost limit */ +} FormData_pg_autovacuum; + +/* ---------------- + * Form_pg_autovacuum corresponds to a pointer to a tuple with + * the format of pg_autovacuum relation. + * ---------------- + */ +typedef FormData_pg_autovacuum *Form_pg_autovacuum; + +/* ---------------- + * compiler constants for pg_autovacuum + * ---------------- + */ +#define Natts_pg_autovacuum 8 +#define Anum_pg_autovacuum_vacrelid 1 +#define Anum_pg_autovacuum_enabled 2 +#define Anum_pg_autovacuum_vac_base_thresh 3 +#define Anum_pg_autovacuum_vac_scale_factor 4 +#define Anum_pg_autovacuum_anl_base_thresh 5 +#define Anum_pg_autovacuum_anl_scale_factor 6 +#define Anum_pg_autovacuum_vac_cost_delay 7 +#define Anum_pg_autovacuum_vac_cost_limit 8 + +/* There are no preloaded tuples in pg_autovacuum.h */ + +#endif /* PG_AUTOVACUUM_H */ diff --git a/src/include/catalog/pg_pltemplate.h b/src/include/catalog/pg_pltemplate.h new file mode 100644 index 0000000..8f7faa3 --- /dev/null +++ b/src/include/catalog/pg_pltemplate.h @@ -0,0 +1,78 @@ +/*------------------------------------------------------------------------- + * + * pg_pltemplate.h + * definition of the system "PL template" relation (pg_pltemplate) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + * NOTES + * the genbki.sh script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_PLTEMPLATE_H +#define PG_PLTEMPLATE_H + +/* ---------------- + * postgres.h contains the system type definitions and the + * CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file + * can be read by both genbki.sh and the C compiler. + * ---------------- + */ + +/* ---------------- + * pg_pltemplate definition. cpp turns this into + * typedef struct FormData_pg_pltemplate + * ---------------- + */ +#define PLTemplateRelationId 1136 + +CATALOG(pg_pltemplate,1136) BKI_SHARED_RELATION BKI_WITHOUT_OIDS +{ + NameData tmplname; /* name of PL */ + bool tmpltrusted; /* PL is trusted? */ + text tmplhandler; /* name of call handler function */ + text tmplvalidator; /* name of validator function, or NULL */ + text tmpllibrary; /* path of shared library */ + aclitem tmplacl[1]; /* access privileges for template */ +} FormData_pg_pltemplate; + +/* ---------------- + * Form_pg_pltemplate corresponds to a pointer to a row with + * the format of pg_pltemplate relation. + * ---------------- + */ +typedef FormData_pg_pltemplate *Form_pg_pltemplate; + +/* ---------------- + * compiler constants for pg_pltemplate + * ---------------- + */ +#define Natts_pg_pltemplate 6 +#define Anum_pg_pltemplate_tmplname 1 +#define Anum_pg_pltemplate_tmpltrusted 2 +#define Anum_pg_pltemplate_tmplhandler 3 +#define Anum_pg_pltemplate_tmplvalidator 4 +#define Anum_pg_pltemplate_tmpllibrary 5 +#define Anum_pg_pltemplate_tmplacl 6 + + +/* ---------------- + * initial contents of pg_pltemplate + * ---------------- + */ + +DATA(insert ( "plpgsql" t "plpgsql_call_handler" "plpgsql_validator" "$libdir/plpgsql" _null_ )); +DATA(insert ( "pltcl" t "pltcl_call_handler" _null_ "$libdir/pltcl" _null_ )); +DATA(insert ( "pltclu" f "pltclu_call_handler" _null_ "$libdir/pltcl" _null_ )); +DATA(insert ( "plperl" t "plperl_call_handler" "plperl_validator" "$libdir/plperl" _null_ )); +DATA(insert ( "plperlu" f "plperl_call_handler" "plperl_validator" "$libdir/plperl" _null_ )); +DATA(insert ( "plpythonu" f "plpython_call_handler" _null_ "$libdir/plpython" _null_ )); + +#endif /* PG_PLTEMPLATE_H */ diff --git a/src/include/executor/nodeBitmapAnd.h b/src/include/executor/nodeBitmapAnd.h new file mode 100644 index 0000000..c9b0a94 --- /dev/null +++ b/src/include/executor/nodeBitmapAnd.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * nodeBitmapAnd.h + * + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef NODEBITMAPAND_H +#define NODEBITMAPAND_H + +#include "nodes/execnodes.h" + +extern int ExecCountSlotsBitmapAnd(BitmapAnd *node); +extern BitmapAndState *ExecInitBitmapAnd(BitmapAnd *node, EState *estate); +extern Node *MultiExecBitmapAnd(BitmapAndState *node); +extern void ExecEndBitmapAnd(BitmapAndState *node); +extern void ExecReScanBitmapAnd(BitmapAndState *node, ExprContext *exprCtxt); + +#endif /* NODEBITMAPAND_H */ diff --git a/src/include/executor/nodeBitmapHeapscan.h b/src/include/executor/nodeBitmapHeapscan.h new file mode 100644 index 0000000..7240818 --- /dev/null +++ b/src/include/executor/nodeBitmapHeapscan.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * nodeBitmapHeapscan.h + * + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef NODEBITMAPHEAPSCAN_H +#define NODEBITMAPHEAPSCAN_H + +#include "nodes/execnodes.h" + +extern int ExecCountSlotsBitmapHeapScan(BitmapHeapScan *node); +extern BitmapHeapScanState *ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate); +extern TupleTableSlot *ExecBitmapHeapScan(BitmapHeapScanState *node); +extern void ExecEndBitmapHeapScan(BitmapHeapScanState *node); +extern void ExecBitmapHeapReScan(BitmapHeapScanState *node, ExprContext *exprCtxt); + +#endif /* NODEBITMAPHEAPSCAN_H */ diff --git a/src/include/executor/nodeBitmapIndexscan.h b/src/include/executor/nodeBitmapIndexscan.h new file mode 100644 index 0000000..a2eb481 --- /dev/null +++ b/src/include/executor/nodeBitmapIndexscan.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * nodeBitmapIndexscan.h + * + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef NODEBITMAPINDEXSCAN_H +#define NODEBITMAPINDEXSCAN_H + +#include "nodes/execnodes.h" + +extern int ExecCountSlotsBitmapIndexScan(BitmapIndexScan *node); +extern BitmapIndexScanState *ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate); +extern Node *MultiExecBitmapIndexScan(BitmapIndexScanState *node); +extern void ExecEndBitmapIndexScan(BitmapIndexScanState *node); +extern void ExecBitmapIndexReScan(BitmapIndexScanState *node, ExprContext *exprCtxt); + +#endif /* NODEBITMAPINDEXSCAN_H */ diff --git a/src/include/executor/nodeBitmapOr.h b/src/include/executor/nodeBitmapOr.h new file mode 100644 index 0000000..4a73855 --- /dev/null +++ b/src/include/executor/nodeBitmapOr.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * nodeBitmapOr.h + * + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef NODEBITMAPOR_H +#define NODEBITMAPOR_H + +#include "nodes/execnodes.h" + +extern int ExecCountSlotsBitmapOr(BitmapOr *node); +extern BitmapOrState *ExecInitBitmapOr(BitmapOr *node, EState *estate); +extern Node *MultiExecBitmapOr(BitmapOrState *node); +extern void ExecEndBitmapOr(BitmapOrState *node); +extern void ExecReScanBitmapOr(BitmapOrState *node, ExprContext *exprCtxt); + +#endif /* NODEBITMAPOR_H */ diff --git a/src/include/nodes/tidbitmap.h b/src/include/nodes/tidbitmap.h new file mode 100644 index 0000000..0da7827 --- /dev/null +++ b/src/include/nodes/tidbitmap.h @@ -0,0 +1,57 @@ +/*------------------------------------------------------------------------- + * + * tidbitmap.h + * PostgreSQL tuple-id (TID) bitmap package + * + * This module provides bitmap data structures that are spiritually + * similar to Bitmapsets, but are specially adapted to store sets of + * tuple identifiers (TIDs), or ItemPointers. In particular, the division + * of an ItemPointer into BlockNumber and OffsetNumber is catered for. + * Also, since we wish to be able to store very large tuple sets in + * memory with this data structure, we support "lossy" storage, in which + * we no longer remember individual tuple offsets on a page but only the + * fact that a particular page needs to be visited. + * + * + * Copyright (c) 2003-2005, PostgreSQL Global Development Group + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef TIDBITMAP_H +#define TIDBITMAP_H + +#include "storage/itemptr.h" + + +/* + * Actual bitmap representation is private to tidbitmap.c. Callers can + * do IsA(x, TIDBitmap) on it, but nothing else. + */ +typedef struct TIDBitmap TIDBitmap; + +/* Result structure for tbm_iterate */ +typedef struct +{ + BlockNumber blockno; /* page number containing tuples */ + int ntuples; /* -1 indicates lossy result */ + OffsetNumber offsets[1]; /* VARIABLE LENGTH ARRAY */ +} TBMIterateResult; /* VARIABLE LENGTH STRUCT */ + +/* function prototypes in nodes/tidbitmap.c */ + +extern TIDBitmap *tbm_create(long maxbytes); +extern void tbm_free(TIDBitmap *tbm); + +extern void tbm_add_tuples(TIDBitmap *tbm, const ItemPointer tids, int ntids); + +extern void tbm_union(TIDBitmap *a, const TIDBitmap *b); +extern void tbm_intersect(TIDBitmap *a, const TIDBitmap *b); + +extern bool tbm_is_empty(const TIDBitmap *tbm); + +extern void tbm_begin_iterate(TIDBitmap *tbm); +extern TBMIterateResult *tbm_iterate(TIDBitmap *tbm); + +#endif /* TIDBITMAP_H */ diff --git a/src/include/optimizer/predtest.h b/src/include/optimizer/predtest.h new file mode 100644 index 0000000..dd3ff10 --- /dev/null +++ b/src/include/optimizer/predtest.h @@ -0,0 +1,25 @@ +/*------------------------------------------------------------------------- + * + * predtest.h + * prototypes for predtest.c + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef PREDTEST_H +#define PREDTEST_H + +#include "nodes/primnodes.h" + + +extern bool predicate_implied_by(List *predicate_list, + List *restrictinfo_list); +extern bool predicate_refuted_by(List *predicate_list, + List *restrictinfo_list); + +#endif /* PREDTEST_H */ diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h new file mode 100644 index 0000000..8fa575d --- /dev/null +++ b/src/include/postmaster/autovacuum.h @@ -0,0 +1,40 @@ +/*------------------------------------------------------------------------- + * + * autovacuum.h + * header file for integrated autovacuum daemon + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef AUTOVACUUM_H +#define AUTOVACUUM_H + +/* GUC variables */ +extern bool autovacuum_start_daemon; +extern int autovacuum_naptime; +extern int autovacuum_vac_thresh; +extern double autovacuum_vac_scale; +extern int autovacuum_anl_thresh; +extern double autovacuum_anl_scale; +extern int autovacuum_vac_cost_delay; +extern int autovacuum_vac_cost_limit; + +/* Status inquiry functions */ +extern bool AutoVacuumingActive(void); +extern bool IsAutoVacuumProcess(void); + +/* Functions to start autovacuum process, called from postmaster */ +extern void autovac_init(void); +extern int autovac_start(void); +extern void autovac_stopped(void); + +#ifdef EXEC_BACKEND +extern void AutoVacMain(int argc, char *argv[]); +#endif + +#endif /* AUTOVACUUM_H */ diff --git a/src/include/postmaster/fork_process.h b/src/include/postmaster/fork_process.h new file mode 100644 index 0000000..1d30d9f --- /dev/null +++ b/src/include/postmaster/fork_process.h @@ -0,0 +1,17 @@ +/*------------------------------------------------------------------------- + * + * fork_process.h + * Exports from postmaster/fork_process.c. + * + * Copyright (c) 1996-2005, PostgreSQL Global Development Group + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef FORK_PROCESS_H +#define FORK_PROCESS_H + +extern pid_t fork_process(void); + +#endif /* FORK_PROCESS_H */ diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h new file mode 100644 index 0000000..87b9bee --- /dev/null +++ b/src/include/storage/procarray.h @@ -0,0 +1,41 @@ +/*------------------------------------------------------------------------- + * + * procarray.h + * POSTGRES process array definitions. + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef PROCARRAY_H +#define PROCARRAY_H + +#include "storage/lock.h" + + +extern Size ProcArrayShmemSize(void); +extern void CreateSharedProcArray(void); +extern void ProcArrayAdd(PGPROC *proc); +extern void ProcArrayRemove(PGPROC *proc); + +extern bool TransactionIdIsInProgress(TransactionId xid); +extern bool TransactionIdIsActive(TransactionId xid); +extern TransactionId GetOldestXmin(bool allDbs); + +extern PGPROC *BackendPidGetProc(int pid); +extern int BackendXidGetPid(TransactionId xid); +extern bool IsBackendPid(int pid); +extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself); + +extern int CountActiveBackends(void); +extern int CountDBBackends(Oid databaseid); +extern int CountUserBackends(Oid roleid); + +extern void XidCacheRemoveRunningXids(TransactionId xid, + int nxids, TransactionId *xids); + +#endif /* PROCARRAY_H */ diff --git a/src/include/utils/flatfiles.h b/src/include/utils/flatfiles.h new file mode 100644 index 0000000..36f47b8 --- /dev/null +++ b/src/include/utils/flatfiles.h @@ -0,0 +1,35 @@ +/*------------------------------------------------------------------------- + * + * flatfiles.h + * Routines for maintaining "flat file" images of the shared catalogs. + * + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef FLATFILES_H +#define FLATFILES_H + +#include "fmgr.h" + +extern void database_file_update_needed(void); +extern void auth_file_update_needed(void); + +extern char *database_getflatfilename(void); +extern char *auth_getflatfilename(void); + +extern void BuildFlatFiles(bool database_only); + +extern void AtPrepare_UpdateFlatFiles(void); +extern void AtEOXact_UpdateFlatFiles(bool isCommit); +extern void AtEOSubXact_UpdateFlatFiles(bool isCommit, + SubTransactionId mySubid, + SubTransactionId parentSubid); + +extern Datum flatfile_update_trigger(PG_FUNCTION_ARGS); + +extern void flatfile_twophase_postcommit(TransactionId xid, uint16 info, + void *recdata, uint32 len); + +#endif /* FLATFILES_H */ diff --git a/src/include/utils/pg_rusage.h b/src/include/utils/pg_rusage.h new file mode 100644 index 0000000..68eedf5 --- /dev/null +++ b/src/include/utils/pg_rusage.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------- + * + * pg_rusage.h + * header file for resource usage measurement support routines + * + * + * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ +#ifndef PG_RUSAGE_H +#define PG_RUSAGE_H + +#include + +#ifdef HAVE_GETRUSAGE +#include +#else +#include "rusagestub.h" +#endif + + +/* State structure for pg_rusage_init/pg_rusage_show */ +typedef struct PGRUsage +{ + struct timeval tv; + struct rusage ru; +} PGRUsage; + + +extern void pg_rusage_init(PGRUsage *ru0); +extern const char *pg_rusage_show(const PGRUsage *ru0); + +#endif /* PG_RUSAGE_H */ diff --git a/src/interfaces/libpq/po/pl.po b/src/interfaces/libpq/po/pl.po new file mode 100644 index 0000000..7e2aa26 --- /dev/null +++ b/src/interfaces/libpq/po/pl.po @@ -0,0 +1,624 @@ +# LIBPQ Translated Messages into the Polish Language (ISO-8859-2) +# +# Copyright (c) 2005 toczek, xxxtoczekxxx@wp.pl +# Distributed under the same licensing terms as PostgreSQL itself. +# +# +msgid "" +msgstr "" +"Project-Id-Version: libpq\n" +"POT-Creation-Date: 2005-01-09 19:05+0000\n" +"PO-Revision-Date: 2005-01-10 01:27+0100\n" +"Last-Translator: toczek \n" +"Language-Team:\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" + +#: fe-auth.c:232 +#, c-format +msgid "Kerberos 4 error: %s\n" +msgstr "Bd programu Kerberos 4: %s\n" + +#: fe-auth.c:393 +#, c-format +msgid "could not set socket to blocking mode: %s\n" +msgstr "nie mona ustawi gniazda w tryb blokowy: %s\n" + +#: fe-auth.c:410 fe-auth.c:414 +#, c-format +msgid "Kerberos 5 authentication rejected: %*s\n" +msgstr "Kerberos 5 autoryzacja odrzucona: %*s\n" + +#: fe-auth.c:440 +#, c-format +msgid "could not restore non-blocking mode on socket: %s\n" +msgstr "nie mona odtworzy trybu nieblokowego gniazda: %s\n" + +#: fe-auth.c:507 +msgid "SCM_CRED authentication method not supported\n" +msgstr "Metoda autoryzacji SCM_CRED nie jest dostpna\n" + +#: fe-auth.c:529 fe-connect.c:1420 fe-connect.c:2631 fe-connect.c:2640 +#: fe-lobj.c:556 fe-protocol2.c:1007 fe-protocol3.c:958 +msgid "out of memory\n" +msgstr "brak pamici\n" + +#: fe-auth.c:599 +msgid "Kerberos 4 authentication failed\n" +msgstr "Kerberos 4: autoryzacja odrzucona\n" + +#: fe-auth.c:607 +msgid "Kerberos 4 authentication not supported\n" +msgstr "Metoda autoryzacji Kerberos 4 nie jest dostpna\n" + +#: fe-auth.c:618 +msgid "Kerberos 5 authentication failed\n" +msgstr "Kerberos 5: autoryzacja odrzucona\n" + +#: fe-auth.c:626 +msgid "Kerberos 5 authentication not supported\n" +msgstr "Metoda autoryzacji Kerberos 5 nie jest dostpna\n" + +#: fe-auth.c:654 +#, c-format +msgid "authentication method %u not supported\n" +msgstr "Metoda autoryzacji %u nie jest dostpna\n" + +#: fe-auth.c:691 +#, c-format +msgid "invalid authentication service name \"%s\", ignored\n" +msgstr "bdna nazwa usugi autoryzacji \"%s\", pomijam\n" + +#: fe-auth.c:764 +#, c-format +msgid "fe_getauthname: invalid authentication system: %d\n" +msgstr "fe_getauthname: bdy system autoryzacji: %d\n" + +#: fe-connect.c:469 +#, c-format +msgid "invalid sslmode value: \"%s\"\n" +msgstr "bdna warto sslmode: \"%s\"\n" + +#: fe-connect.c:489 +#, c-format +msgid "sslmode value \"%s\" invalid when SSL support is not compiled in\n" +msgstr "bdna warto sslmode \"%s\" gdy obsuga SSL nie zostaa skompilowana\n" + +#: fe-connect.c:805 +#, c-format +msgid "could not set socket to TCP no delay mode: %s\n" +msgstr "nie mona ustawi gniazda TCP w tryb bez opnie: %s\n" + +#: fe-connect.c:836 +#, c-format +msgid "" +"could not connect to server: %s\n" +"\tIs the server running locally and accepting\n" +"\tconnections on Unix domain socket \"%s\"?\n" +msgstr "" +"nie mona poczy si z serwerem: %s\n" +"\tCzy serwer dziaa lokalnie i akceptuje\n" +"\tpoczenia przy pomocy gniazd dziedziny uniksa \"%s\"?\n" + +#: fe-connect.c:848 +#, c-format +msgid "" +"could not connect to server: %s\n" +"\tIs the server running on host \"%s\" and accepting\n" +"\tTCP/IP connections on port %s?\n" +msgstr "" +"nie mona poczy si z serwerem: %s\n" +"\tCzy serwer dziaa na stacji siecowej \"%s\" i akceptuje\n" +"\tpoczenia TCP/IP na porcie %s?\n" + +#: fe-connect.c:936 +#, c-format +msgid "could not translate host name \"%s\" to address: %s\n" +msgstr "nie mona przetumaczy nazwy host'a \"%s\" na adres: %s\n" + +#: fe-connect.c:940 +#, c-format +msgid "could not translate Unix-domain socket path \"%s\" to address: %s\n" +msgstr "nie mona przetumaczy cieki gniazda Unix-domain \"%s\" na adres: %s\n" + +#: fe-connect.c:1144 +msgid "invalid connection state, probably indicative of memory corruption\n" +msgstr "bdny stan poczenia, prawdopodobnie oznajmiajcy uszkodzenie pamici\n" + +#: fe-connect.c:1187 +#, c-format +msgid "could not create socket: %s\n" +msgstr "nie mona utworzy gniazda: %s\n" + +#: fe-connect.c:1210 +#, c-format +msgid "could not set socket to non-blocking mode: %s\n" +msgstr "nie mona ustawi gniazda w tryb non-blocking: %s\n" + +#: fe-connect.c:1222 +#, c-format +msgid "could not set socket to close-on-exec mode: %s\n" +msgstr "nie mona ustawi gniazda osugi zamknicia przy uruchomieniu: %s\n" + +#: fe-connect.c:1314 +#, c-format +msgid "could not get socket error status: %s\n" +msgstr "nie mona otrzyma bdu gniazda: %s\n" + +#: fe-connect.c:1353 +#, c-format +msgid "could not get client address from socket: %s\n" +msgstr "nie mona otrzyma adresu klienta z gniazda: %s\n" + +#: fe-connect.c:1398 +#, c-format +msgid "could not send SSL negotiation packet: %s\n" +msgstr "nie mona wysa pakietu negcjacji SSL: %s\n" + +#: fe-connect.c:1433 +#, c-format +msgid "could not send startup packet: %s\n" +msgstr "nie mona wysa pakietu rozpoczynajcego: %s\n" + +#: fe-connect.c:1498 fe-connect.c:1515 +msgid "server does not support SSL, but SSL was required\n" +msgstr "serwer nie obsuguje SSL, ale SSL jest wymagane\n" + +#: fe-connect.c:1531 +#, c-format +msgid "received invalid response to SSL negotiation: %c\n" +msgstr "otrzymano niepoprawn odpowiedzied negocjacji SSL: %c\n" + +#: fe-connect.c:1588 fe-connect.c:1620 +#, c-format +msgid "expected authentication request from server, but received %c\n" +msgstr "oczekiwano proby autoryzacji z serwera ale otrzymano %c\n" + +#: fe-connect.c:1856 +msgid "unexpected message from server during startup\n" +msgstr "niespodziewana wiadomo z serwera podczas startu\n" + +#: fe-connect.c:1926 +#, c-format +msgid "invalid connection state %c, probably indicative of memory corruption\n" +msgstr "nieprawidowy stan poczenia %c, prawdopodobnie oznajmiajcy uszkodzenie pamici\n" + +#: fe-connect.c:2679 +#, c-format +msgid "missing \"=\" after \"%s\" in connection info string\n" +msgstr "brakujce \"=\" po \"%s\" w acuchu informacyjnym poczenia\n" + +#: fe-connect.c:2728 +msgid "unterminated quoted string in connection info string\n" +msgstr "" + +#: fe-connect.c:2762 +#, c-format +msgid "invalid connection option \"%s\"\n" +msgstr "bdna opcja poczenia \"%s\"\n" + +#: fe-connect.c:2984 +msgid "connection pointer is NULL\n" +msgstr "wskanik poczenia ma warto NULL\n" + +#: fe-connect.c:3223 +#, c-format +msgid "" +"WARNING: Password file %s has world or group read access; permission should " +"be u=rw (0600)\n" +msgstr "" +"UWAGA: Plik hasa %s posiada globalne lub grupowe uprawnienia odczytu.\n" +"Uprawniania powinny by usawione: u=rw (0600)\n" + +#: fe-exec.c:479 +msgid "NOTICE" +msgstr "OSTRZEENIE" + +#: fe-exec.c:648 fe-exec.c:700 fe-exec.c:740 +msgid "command string is a null pointer\n" +msgstr "acuch polecenia jest wskanikiem null\n" + +#: fe-exec.c:733 fe-exec.c:823 +msgid "statement name is a null pointer\n" +msgstr "nazwa instrukcji jest wskanikiem null\n" + +#: fe-exec.c:748 fe-exec.c:897 fe-exec.c:1572 +msgid "function requires at least protocol version 3.0\n" +msgstr "funkcja wymaga przynajmniej protokou w wersji 3.0\n" + +#: fe-exec.c:854 +msgid "no connection to the server\n" +msgstr "brak poczenia z serwerem\n" + +#: fe-exec.c:861 +msgid "another command is already in progress\n" +msgstr "inne polecenie jest aktualnie wykonywane\n" + +#: fe-exec.c:1199 +#, c-format +msgid "unexpected asyncStatus: %d\n" +msgstr "nieoczekiwany asyncStatus: %d\n" + +#: fe-exec.c:1326 +msgid "COPY terminated by new PQexec" +msgstr "KOPIOWANIE zakoczone przez nowe PQexec" + +#: fe-exec.c:1334 +msgid "COPY IN state must be terminated first\n" +msgstr "stan COPY IN musi zosta wczeniej zakoczony\n" + +#: fe-exec.c:1354 +msgid "COPY OUT state must be terminated first\n" +msgstr "stan COPY OUT musi zosta wczeniej zakoczony\n" + +#: fe-exec.c:1464 fe-exec.c:1529 fe-exec.c:1614 fe-protocol2.c:1153 +#: fe-protocol3.c:1115 +msgid "no COPY in progress\n" +msgstr "" + +#: fe-exec.c:1806 +msgid "connection in wrong state\n" +msgstr "poczenie posiada bdny stan\n" + +#: fe-exec.c:1837 +msgid "invalid ExecStatusType code" +msgstr "bedny kod ExecStatusType" + +#: fe-exec.c:1901 fe-exec.c:1924 +#, c-format +msgid "column number %d is out of range 0..%d" +msgstr "numer kolumny %d wykracza poza zakres 0..%d" + +#: fe-exec.c:1917 +#, c-format +msgid "row number %d is out of range 0..%d" +msgstr "numer wiersza %d wykracza poza zakres 0..%d" + +#: fe-exec.c:2199 +#, c-format +msgid "could not interpret result from server: %s" +msgstr "nie mona zinterpretowa wynikw z serwera: %s" + +#: fe-lobj.c:410 fe-lobj.c:495 +#, c-format +msgid "could not open file \"%s\": %s\n" +msgstr "nie mona otworzy pliku \"%s\": %s\n" + +#: fe-lobj.c:422 +#, c-format +msgid "could not create large object for file \"%s\"\n" +msgstr "nie mona utworzy duego obiektu dla pliku \"%s\"\n" + +#: fe-lobj.c:432 fe-lobj.c:482 +#, c-format +msgid "could not open large object %u\n" +msgstr "nie mona otworzy duego obiektu %u\n" + +#: fe-lobj.c:447 +#, c-format +msgid "error while reading file \"%s\"\n" +msgstr "bd podczas odczytu pliku \"%s\"\n" + +#: fe-lobj.c:510 fe-lobj.c:523 +#, c-format +msgid "error while writing to file \"%s\"\n" +msgstr "bd podczas zapisu do pliku \"%s\"\n" + +#: fe-lobj.c:601 +msgid "query to initialize large object functions did not return data\n" +msgstr "zapytanie inicjalizujce duy obiekt nie zwrcio adnych danych\n" + +#: fe-lobj.c:639 +msgid "cannot determine OID of function lo_open\n" +msgstr "nie mona ustali OID funkcji lo_open\n" + +#: fe-lobj.c:646 +msgid "cannot determine OID of function lo_close\n" +msgstr "nie mona ustali OID funkcji lo_close\n" + +#: fe-lobj.c:653 +msgid "cannot determine OID of function lo_creat\n" +msgstr "nie mona ustali OID funkcji lo_creat\n" + +#: fe-lobj.c:660 +msgid "cannot determine OID of function lo_unlink\n" +msgstr "nie mona ustali OID funkcji lo_unlink\n" + +#: fe-lobj.c:667 +msgid "cannot determine OID of function lo_lseek\n" +msgstr "nie mona ustali OID funkcji lo_lseek\n" + +#: fe-lobj.c:674 +msgid "cannot determine OID of function lo_tell\n" +msgstr "nie mona ustali OID funkcji lo_tell\n" + +#: fe-lobj.c:681 +msgid "cannot determine OID of function loread\n" +msgstr "nie mona ustali OID funkcji loread\n" + +#: fe-lobj.c:688 +msgid "cannot determine OID of function lowrite\n" +msgstr "nie mona ustali OID funkcji lowrite\n" + +#: fe-misc.c:228 +#, c-format +msgid "integer of size %lu not supported by pqGetInt" +msgstr "typ integer o rozmiarze %lu nie jest obsugiwany przez pqGetInt" + +#: fe-misc.c:264 +#, c-format +msgid "integer of size %lu not supported by pqPutInt" +msgstr "typ intiger o rozmiarze %lu nie jest obsugiwany przez pqPutInt" + +#: fe-misc.c:544 fe-misc.c:748 +msgid "connection not open\n" +msgstr "poczenie nie jest otwarte\n" + +#: fe-misc.c:610 fe-misc.c:701 +#, c-format +msgid "could not receive data from server: %s\n" +msgstr "nie mona otrzyma danych z serwera: %s\n" + +#: fe-misc.c:718 fe-misc.c:786 +msgid "" +"server closed the connection unexpectedly\n" +"\tThis probably means the server terminated abnormally\n" +"\tbefore or while processing the request.\n" +msgstr "" +"serwer zamkn poczenie niespodziewanie\n" +"\tOznacza to prawdopodobnie i serwer zakoczy dziaanie niepoprawnie\n" +"\tprzed lub podczas przetwarzania zapytania.\n" + +#: fe-misc.c:803 +#, c-format +msgid "could not send data to server: %s\n" +msgstr "nie mona wysa danych do serwera: %s\n" + +#: fe-misc.c:923 +msgid "timeout expired\n" +msgstr "upyn limit czasu rzdania\n" + +#: fe-misc.c:968 +msgid "socket not open\n" +msgstr "gniazdo nie jest otwarte\n" + +#: fe-misc.c:991 +#, c-format +msgid "select() failed: %s\n" +msgstr "select() nie udao si: %s\n" + +#: fe-protocol2.c:91 +#, c-format +msgid "invalid setenv state %c, probably indicative of memory corruption\n" +msgstr "niepoprawny stan setenv %c, prawdopodobnie oznajmiajcy uszkodzenie pamici\n" + +#: fe-protocol2.c:333 +#, c-format +msgid "invalid state %c, probably indicative of memory corruption\n" +msgstr "nieporawny stan %c, prawdopodobnie oznajmiajcy uszkodzenie pamici\n" + +#: fe-protocol2.c:423 fe-protocol3.c:183 +#, c-format +msgid "message type 0x%02x arrived from server while idle" +msgstr "otrzymano wiadomo typu 0x%02x z serwera podczas procesu bezczynnoci" + +#: fe-protocol2.c:462 +#, c-format +msgid "unexpected character %c following empty query response (\"I\" message)" +msgstr "nieznany znak %c nastpujcy po odpowiedzi pustego zapytania ( wiadomo\"I\") " + +#: fe-protocol2.c:517 +msgid "" +"server sent data (\"D\" message) without prior row description (\"T\" " +"message)" +msgstr "" +"serwer wysa dane (wiadomo \"D\") bez wczeniejszego opisu wiersza (wiadomo \"T\")" + +#: fe-protocol2.c:533 +msgid "" +"server sent binary data (\"B\" message) without prior row description (\"T\" " +"message)" +msgstr "" +"serwer wysa dane binarne (wiadomo \"B\") bez wczeniejszego opisu " +"wiersza (wiadomo \"T\")" + +#: fe-protocol2.c:548 fe-protocol3.c:344 +#, c-format +msgid "unexpected response from server; first received character was \"%c\"\n" +msgstr "nieznana odpowied z serwera: pierszym znakiem otrzymanym by \"%c\"\n" + +#: fe-protocol2.c:760 fe-protocol3.c:577 +msgid "out of memory for query result\n" +msgstr "brak pamici dla rezultatw zapytania\n" + +#: fe-protocol2.c:1196 fe-protocol3.c:1184 +#, c-format +msgid "%s" +msgstr "%s" + +#: fe-protocol2.c:1208 +msgid "lost synchronization with server, resetting connection" +msgstr "utracono synchronizacj z serwerem, resetuj poczenie" + +#: fe-protocol2.c:1343 fe-protocol2.c:1375 fe-protocol3.c:1387 +#, c-format +msgid "protocol error: id=0x%x\n" +msgstr "bd protokou: id=0x%x\n" + +#: fe-protocol3.c:306 +msgid "" +"server sent data (\"D\" message) without prior row description (\"T\" " +"message)\n" +msgstr "" +"serwer wysa dane (wiadmo \"D\") bez wczeniejszego opisu wiersza (wiadomo \"T\")\n" + +#: fe-protocol3.c:365 +#, c-format +msgid "message contents do not agree with length in message type \"%c\"\n" +msgstr "zawarto wiadomoci nie zgadza si z dugoci wiadomoci typu \"%c\"\n" + +#: fe-protocol3.c:386 +#, c-format +msgid "lost synchronization with server: got message type \"%c\", length %d\n" +msgstr "utracono synchronizacj z serwerm: otrzymano wiadomo typu\"%c\", dugo %d\n" + +#: fe-protocol3.c:522 +msgid "unexpected field count in \"D\" message\n" +msgstr "nieznane pole wyliczone w wiadomoci \"D\"\n" + +#. translator: %s represents a digit string +#: fe-protocol3.c:651 fe-protocol3.c:659 +#, c-format +msgid " at character %s" +msgstr " znak %s" + +#: fe-protocol3.c:668 +#, c-format +msgid "DETAIL: %s\n" +msgstr "SZCZEGӣY: %s\n" + +#: fe-protocol3.c:671 +#, c-format +msgid "HINT: %s\n" +msgstr "PODPOWIED: %s\n" + +#: fe-protocol3.c:674 +#, c-format +msgid "QUERY: %s\n" +msgstr "ZAPYTANOE: %s\n" + +#: fe-protocol3.c:677 +#, c-format +msgid "CONTEXT: %s\n" +msgstr "KONTEKST: %s\n" + +#: fe-protocol3.c:689 +msgid "LOCATION: " +msgstr "LOKALIZACJA: " + +#: fe-protocol3.c:691 +#, c-format +msgid "%s, " +msgstr "%s, " + +#: fe-protocol3.c:693 +#, c-format +msgid "%s:%s" +msgstr "%s:%s" + +#: fe-protocol3.c:1000 +msgid "PQgetline: not doing text COPY OUT\n" +msgstr "PQgetline: nie dziaam aktualnie w stanie COPY OUT\n" + +#: fe-secure.c:273 +#, c-format +msgid "could not establish SSL connection: %s\n" +msgstr "nie mona ustanowi poczenia SSL: %s\n" + +#: fe-secure.c:344 fe-secure.c:439 fe-secure.c:1042 +#, c-format +msgid "SSL SYSCALL error: %s\n" +msgstr "bd SSL SYSCALL: %s\n" + +#: fe-secure.c:349 fe-secure.c:445 fe-secure.c:1046 +msgid "SSL SYSCALL error: EOF detected\n" +msgstr "bd SSL SYSCALL: wykryto EOF\n" + +#: fe-secure.c:361 fe-secure.c:456 fe-secure.c:1065 +#, c-format +msgid "SSL error: %s\n" +msgstr "bd SSL: %s\n" + +#: fe-secure.c:371 fe-secure.c:466 fe-secure.c:1075 +#, c-format +msgid "unrecognized SSL error code: %d\n" +msgstr "nieznany bd SSL o kodzie: %d\n" + +#: fe-secure.c:536 +#, c-format +msgid "error querying socket: %s\n" +msgstr "bd zapytania gniazda: %s\n" + +#: fe-secure.c:564 +#, c-format +msgid "could not get information about host \"%s\": %s\n" +msgstr "nie mona otrzyma informacji o stacji siecowej \"%s\": %s\n" + +#: fe-secure.c:583 +msgid "unsupported protocol\n" +msgstr "nieobsugiwany protok\n" + +#: fe-secure.c:605 +#, c-format +msgid "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n" +msgstr "nazwa serwera \"%s\" nie odpowiada %ld.%ld.%ld.%ld\n" + +#: fe-secure.c:612 +#, c-format +msgid "server common name \"%s\" does not resolve to peer address\n" +msgstr "" + +#: fe-secure.c:782 +msgid "could not get user information\n" +msgstr "nie mona uzyka informacji o uytkowniku\n" + +#: fe-secure.c:791 +#, c-format +msgid "could not open certificate file \"%s\": %s\n" +msgstr "nie mona otworzy pliku certyfikatu \"%s\": %s\n" + +#: fe-secure.c:800 +#, c-format +msgid "could not read certificate file \"%s\": %s\n" +msgstr "nie mona odczyta pliku certyfikatu \"%s\": %s\n" + +#: fe-secure.c:813 +#, c-format +msgid "certificate present, but not private key file \"%s\"\n" +msgstr "znaleziono certyfikat ale nie znaleziono pliku z prywatnym kluczem \"%s\"\n" + +#: fe-secure.c:822 +#, c-format +msgid "private key file \"%s\" has wrong permissions\n" +msgstr "plik z prywatnym kluczem \"%s\" posiada bdne uprawnienia\n" + +#: fe-secure.c:830 +#, c-format +msgid "could not open private key file \"%s\": %s\n" +msgstr "nie mona otworzy pliku z prywatnym kluczem \"%s\": %s\n" + +#: fe-secure.c:838 +#, c-format +msgid "private key file \"%s\" changed during execution\n" +msgstr "plik z prywatnym kluczem \"%s\" zmieniony podczas wykonywania\n" + +#: fe-secure.c:846 +#, c-format +msgid "could not read private key file \"%s\": %s\n" +msgstr "nie mona odczyta pliku z prywatnym kluczem \"%s\": %s\n" + +#: fe-secure.c:860 +#, c-format +msgid "certificate does not match private key file \"%s\": %s\n" +msgstr "certyfikat nie pokrywa si z prywatnym kluczem w pliku \"%s\": %s\n" + +#: fe-secure.c:943 +#, c-format +msgid "could not create SSL context: %s\n" +msgstr "nie mona utworzy kontekstu SSL: %s\n" + +#: fe-secure.c:982 +#, c-format +msgid "could not read root certificate file \"%s\": %s\n" +msgstr "nie mona odczyta pliku z certyfikatem uytkownika root \"%s\": %s\n" + +#: fe-secure.c:1095 +#, c-format +msgid "certificate could not be validated: %s\n" +msgstr "certyfikat nie moe zosta potwierdzony: %s\n" + +#: fe-secure.c:1109 +#, c-format +msgid "certificate could not be obtained: %s\n" +msgstr "certygikat nie moe zosta otrzymany: %s\n" diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out new file mode 100644 index 0000000..a2b34a7 --- /dev/null +++ b/src/pl/plperl/expected/plperl.out @@ -0,0 +1,422 @@ +-- +-- checkpoint so that if we have a crash in the tests, replay of the +-- just-completed CREATE DATABASE won't discard the core dump file +-- +checkpoint; +-- +-- Test result value processing +-- +CREATE OR REPLACE FUNCTION perl_int(int) RETURNS INTEGER AS $$ +return undef; +$$ LANGUAGE plperl; +SELECT perl_int(11); + perl_int +---------- + +(1 row) + +SELECT * FROM perl_int(42); + perl_int +---------- + +(1 row) + +CREATE OR REPLACE FUNCTION perl_int(int) RETURNS INTEGER AS $$ +return $_[0] + 1; +$$ LANGUAGE plperl; +SELECT perl_int(11); + perl_int +---------- + 12 +(1 row) + +SELECT * FROM perl_int(42); + perl_int +---------- + 43 +(1 row) + +CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$ +return undef; +$$ LANGUAGE plperl; +SELECT perl_set_int(5); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_set_int(5); + perl_set_int +-------------- +(0 rows) + +CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$ +return [0..$_[0]]; +$$ LANGUAGE plperl; +SELECT perl_set_int(5); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_set_int(5); + perl_set_int +-------------- + 0 + 1 + 2 + 3 + 4 + 5 +(6 rows) + +CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text); +CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$ + return undef; +$$ LANGUAGE plperl; +SELECT perl_row(); + perl_row +---------- + +(1 row) + +SELECT * FROM perl_row(); + f1 | f2 | f3 +----+----+---- + | | +(1 row) + +CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; +SELECT perl_row(); + perl_row +----------------- + (1,hello,world) +(1 row) + +SELECT * FROM perl_row(); + f1 | f2 | f3 +----+-------+------- + 1 | hello | world +(1 row) + +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return undef; +$$ LANGUAGE plperl; +SELECT perl_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_set(); + f1 | f2 | f3 +----+----+---- +(0 rows) + +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + undef, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; +SELECT perl_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_set(); +ERROR: setof-composite-returning Perl function must call return_next with reference to hash +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; +SELECT perl_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_set(); + f1 | f2 | f3 +----+-------+------------ + 1 | Hello | World + 2 | Hello | PostgreSQL + 3 | Hello | PL/Perl +(3 rows) + +CREATE OR REPLACE FUNCTION perl_record() RETURNS record AS $$ + return undef; +$$ LANGUAGE plperl; +SELECT perl_record(); + perl_record +------------- + +(1 row) + +SELECT * FROM perl_record(); +ERROR: a column definition list is required for functions returning "record" +SELECT * FROM perl_record() AS (f1 integer, f2 text, f3 text); + f1 | f2 | f3 +----+----+---- + | | +(1 row) + +CREATE OR REPLACE FUNCTION perl_record() RETURNS record AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; +SELECT perl_record(); +ERROR: function returning record called in context that cannot accept type record +SELECT * FROM perl_record(); +ERROR: a column definition list is required for functions returning "record" +SELECT * FROM perl_record() AS (f1 integer, f2 text, f3 text); + f1 | f2 | f3 +----+-------+------- + 1 | hello | world +(1 row) + +CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$ + return undef; +$$ LANGUAGE plperl; +SELECT perl_record_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_record_set(); +ERROR: a column definition list is required for functions returning "record" +SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text); + f1 | f2 | f3 +----+----+---- +(0 rows) + +CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + undef, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; +SELECT perl_record_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_record_set(); +ERROR: a column definition list is required for functions returning "record" +SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text); +ERROR: setof-composite-returning Perl function must call return_next with reference to hash +CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; +SELECT perl_record_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_record_set(); +ERROR: a column definition list is required for functions returning "record" +SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text); + f1 | f2 | f3 +----+-------+------------ + 1 | Hello | World + 2 | Hello | PostgreSQL + 3 | Hello | PL/Perl +(3 rows) + +CREATE OR REPLACE FUNCTION +perl_out_params(f1 out integer, f2 out text, f3 out text) AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; +SELECT perl_out_params(); + perl_out_params +----------------- + (1,hello,world) +(1 row) + +SELECT * FROM perl_out_params(); + f1 | f2 | f3 +----+-------+------- + 1 | hello | world +(1 row) + +SELECT (perl_out_params()).f2; + f2 +------- + hello +(1 row) + +CREATE OR REPLACE FUNCTION +perl_out_params_set(out f1 integer, out f2 text, out f3 text) +RETURNS SETOF record AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; +SELECT perl_out_params_set(); +ERROR: set-valued function called in context that cannot accept a set +SELECT * FROM perl_out_params_set(); + f1 | f2 | f3 +----+-------+------------ + 1 | Hello | World + 2 | Hello | PostgreSQL + 3 | Hello | PL/Perl +(3 rows) + +SELECT (perl_out_params_set()).f3; +ERROR: set-valued function called in context that cannot accept a set +-- +-- Check behavior with erroneous return values +-- +CREATE TYPE footype AS (x INTEGER, y INTEGER); +CREATE OR REPLACE FUNCTION foo_good() RETURNS SETOF footype AS $$ +return [ + {x => 1, y => 2}, + {x => 3, y => 4} +]; +$$ LANGUAGE plperl; +SELECT * FROM foo_good(); + x | y +---+--- + 1 | 2 + 3 | 4 +(2 rows) + +CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$ + return {y => 3, z => 4}; +$$ LANGUAGE plperl; +SELECT * FROM foo_bad(); +ERROR: Perl hash contains nonexistent column "z" +CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$ +return 42; +$$ LANGUAGE plperl; +SELECT * FROM foo_bad(); +ERROR: composite-returning Perl function must return reference to hash +CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$ +return [ + [1, 2], + [3, 4] +]; +$$ LANGUAGE plperl; +SELECT * FROM foo_bad(); +ERROR: composite-returning Perl function must return reference to hash +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ + return 42; +$$ LANGUAGE plperl; +SELECT * FROM foo_set_bad(); +ERROR: set-returning Perl function must return reference to array or use return_next +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ + return {y => 3, z => 4}; +$$ LANGUAGE plperl; +SELECT * FROM foo_set_bad(); +ERROR: set-returning Perl function must return reference to array or use return_next +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ +return [ + [1, 2], + [3, 4] +]; +$$ LANGUAGE plperl; +SELECT * FROM foo_set_bad(); +ERROR: setof-composite-returning Perl function must call return_next with reference to hash +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ +return [ + {y => 3, z => 4} +]; +$$ LANGUAGE plperl; +SELECT * FROM foo_set_bad(); +ERROR: Perl hash contains nonexistent column "z" +-- +-- Check passing a tuple argument +-- +CREATE OR REPLACE FUNCTION perl_get_field(footype, text) RETURNS integer AS $$ + return $_[0]->{$_[1]}; +$$ LANGUAGE plperl; +SELECT perl_get_field((11,12), 'x'); + perl_get_field +---------------- + 11 +(1 row) + +SELECT perl_get_field((11,12), 'y'); + perl_get_field +---------------- + 12 +(1 row) + +SELECT perl_get_field((11,12), 'z'); + perl_get_field +---------------- + +(1 row) + +-- +-- Test return_next +-- +CREATE OR REPLACE FUNCTION perl_srf_rn() RETURNS SETOF RECORD AS $$ +my $i = 0; +for ("World", "PostgreSQL", "PL/Perl") { + return_next({f1=>++$i, f2=>'Hello', f3=>$_}); +} +return; +$$ language plperl; +SELECT * from perl_srf_rn() AS (f1 INTEGER, f2 TEXT, f3 TEXT); + f1 | f2 | f3 +----+-------+------------ + 1 | Hello | World + 2 | Hello | PostgreSQL + 3 | Hello | PL/Perl +(3 rows) + +-- +-- Test spi_query/spi_fetchrow +-- +CREATE OR REPLACE FUNCTION perl_spi_func() RETURNS SETOF INTEGER AS $$ +my $x = spi_query("select 1 as a union select 2 as a"); +while (defined (my $y = spi_fetchrow($x))) { + return_next($y->{a}); +} +return; +$$ LANGUAGE plperl; +SELECT * from perl_spi_func(); + perl_spi_func +--------------- + 1 + 2 +(2 rows) + +--- +--- Test recursion via SPI +--- +CREATE OR REPLACE FUNCTION recurse(i int) RETURNS SETOF TEXT LANGUAGE plperl +AS $$ + + my $i = shift; + foreach my $x (1..$i) + { + return_next "hello $x"; + } + if ($i > 2) + { + my $z = $i-1; + my $cursor = spi_query("select * from recurse($z)"); + while (defined(my $row = spi_fetchrow($cursor))) + { + return_next "recurse $i: $row->{recurse}"; + } + } + return undef; + +$$; +SELECT * FROM recurse(2); + recurse +--------- + hello 1 + hello 2 +(2 rows) + +SELECT * FROM recurse(3); + recurse +-------------------- + hello 1 + hello 2 + hello 3 + recurse 3: hello 1 + recurse 3: hello 2 +(5 rows) + +--- +--- Test arrary return +--- +CREATE OR REPLACE FUNCTION array_of_text() RETURNS TEXT[][] +LANGUAGE plperl as $$ + return [['a"b','c,d'],['e\\f','g']]; +$$; +SELECT array_of_text(); + array_of_text +----------------------------- + {{"a\"b","c,d"},{"e\\f",g}} +(1 row) + diff --git a/src/pl/plperl/expected/plperl_elog.out b/src/pl/plperl/expected/plperl_elog.out new file mode 100644 index 0000000..6132513 --- /dev/null +++ b/src/pl/plperl/expected/plperl_elog.out @@ -0,0 +1,56 @@ +-- test warnings and errors from plperl +create or replace function perl_elog(text) returns void language plperl as $$ + + my $msg = shift; + elog(NOTICE,$msg); + +$$; +select perl_elog('explicit elog'); +NOTICE: explicit elog + perl_elog +----------- + +(1 row) + +create or replace function perl_warn(text) returns void language plperl as $$ + + my $msg = shift; + warn($msg); + +$$; +select perl_warn('implicit elog via warn'); +NOTICE: implicit elog via warn at line 4. + + perl_warn +----------- + +(1 row) + +-- test strict mode on/off +SET plperl.use_strict = true; +create or replace function uses_global() returns text language plperl as $$ + + $global = 1; + $other_global = 2; + return 'uses_global worked'; + +$$; +ERROR: creation of Perl function failed: Global symbol "$global" requires explicit package name at line 3. +Global symbol "$other_global" requires explicit package name at line 4. +select uses_global(); +ERROR: function uses_global() does not exist +HINT: No function matches the given name and argument types. You may need to add explicit type casts. +SET plperl.use_strict = false; +create or replace function uses_global() returns text language plperl as $$ + + $global = 1; + $other_global=2; + return 'uses_global worked'; + +$$; +select uses_global(); + uses_global +-------------------- + uses_global worked +(1 row) + diff --git a/src/pl/plperl/expected/plperl_shared.out b/src/pl/plperl/expected/plperl_shared.out new file mode 100644 index 0000000..72ae1ba --- /dev/null +++ b/src/pl/plperl/expected/plperl_shared.out @@ -0,0 +1,26 @@ +-- test the shared hash +create function setme(key text, val text) returns void language plperl as $$ + + my $key = shift; + my $val = shift; + $_SHARED{$key}= $val; + +$$; +create function getme(key text) returns text language plperl as $$ + + my $key = shift; + return $_SHARED{$key}; + +$$; +select setme('ourkey','ourval'); + setme +------- + +(1 row) + +select getme('ourkey'); + getme +-------- + ourval +(1 row) + diff --git a/src/pl/plperl/expected/plperl_trigger.out b/src/pl/plperl/expected/plperl_trigger.out new file mode 100644 index 0000000..9c0bae9 --- /dev/null +++ b/src/pl/plperl/expected/plperl_trigger.out @@ -0,0 +1,67 @@ +-- test plperl triggers +CREATE TABLE trigger_test ( + i int, + v varchar +); +CREATE OR REPLACE FUNCTION valid_id() RETURNS trigger AS $$ + + if (($_TD->{new}{i}>=100) || ($_TD->{new}{i}<=0)) + { + return "SKIP"; # Skip INSERT/UPDATE command + } + elsif ($_TD->{new}{v} ne "immortal") + { + $_TD->{new}{v} .= "(modified by trigger)"; + return "MODIFY"; # Modify tuple and proceed INSERT/UPDATE command + } + else + { + return; # Proceed INSERT/UPDATE command + } +$$ LANGUAGE plperl; +CREATE TRIGGER "test_valid_id_trig" BEFORE INSERT OR UPDATE ON trigger_test +FOR EACH ROW EXECUTE PROCEDURE "valid_id"(); +INSERT INTO trigger_test (i, v) VALUES (1,'first line'); +INSERT INTO trigger_test (i, v) VALUES (2,'second line'); +INSERT INTO trigger_test (i, v) VALUES (3,'third line'); +INSERT INTO trigger_test (i, v) VALUES (4,'immortal'); +INSERT INTO trigger_test (i, v) VALUES (101,'bad id'); +SELECT * FROM trigger_test; + i | v +---+---------------------------------- + 1 | first line(modified by trigger) + 2 | second line(modified by trigger) + 3 | third line(modified by trigger) + 4 | immortal +(4 rows) + +UPDATE trigger_test SET i = 5 where i=3; +UPDATE trigger_test SET i = 100 where i=1; +SELECT * FROM trigger_test; + i | v +---+------------------------------------------------------ + 1 | first line(modified by trigger) + 2 | second line(modified by trigger) + 4 | immortal + 5 | third line(modified by trigger)(modified by trigger) +(4 rows) + +CREATE OR REPLACE FUNCTION immortal() RETURNS trigger AS $$ + if ($_TD->{old}{v} eq $_TD->{args}[0]) + { + return "SKIP"; # Skip DELETE command + } + else + { + return; # Proceed DELETE command + }; +$$ LANGUAGE plperl; +CREATE TRIGGER "immortal_trig" BEFORE DELETE ON trigger_test +FOR EACH ROW EXECUTE PROCEDURE immortal('immortal'); +DELETE FROM trigger_test; +SELECT * FROM trigger_test; + i | v +---+---------- + 4 | immortal +(1 row) + diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql new file mode 100644 index 0000000..e6fc5c3 --- /dev/null +++ b/src/pl/plperl/sql/plperl.sql @@ -0,0 +1,303 @@ +-- +-- checkpoint so that if we have a crash in the tests, replay of the +-- just-completed CREATE DATABASE won't discard the core dump file +-- +checkpoint; + +-- +-- Test result value processing +-- + +CREATE OR REPLACE FUNCTION perl_int(int) RETURNS INTEGER AS $$ +return undef; +$$ LANGUAGE plperl; + +SELECT perl_int(11); +SELECT * FROM perl_int(42); + +CREATE OR REPLACE FUNCTION perl_int(int) RETURNS INTEGER AS $$ +return $_[0] + 1; +$$ LANGUAGE plperl; + +SELECT perl_int(11); +SELECT * FROM perl_int(42); + + +CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$ +return undef; +$$ LANGUAGE plperl; + +SELECT perl_set_int(5); +SELECT * FROM perl_set_int(5); + +CREATE OR REPLACE FUNCTION perl_set_int(int) RETURNS SETOF INTEGER AS $$ +return [0..$_[0]]; +$$ LANGUAGE plperl; + +SELECT perl_set_int(5); +SELECT * FROM perl_set_int(5); + + +CREATE TYPE testrowperl AS (f1 integer, f2 text, f3 text); + +CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$ + return undef; +$$ LANGUAGE plperl; + +SELECT perl_row(); +SELECT * FROM perl_row(); + +CREATE OR REPLACE FUNCTION perl_row() RETURNS testrowperl AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; + +SELECT perl_row(); +SELECT * FROM perl_row(); + + +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return undef; +$$ LANGUAGE plperl; + +SELECT perl_set(); +SELECT * FROM perl_set(); + +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + undef, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; + +SELECT perl_set(); +SELECT * FROM perl_set(); + +CREATE OR REPLACE FUNCTION perl_set() RETURNS SETOF testrowperl AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; + +SELECT perl_set(); +SELECT * FROM perl_set(); + + + +CREATE OR REPLACE FUNCTION perl_record() RETURNS record AS $$ + return undef; +$$ LANGUAGE plperl; + +SELECT perl_record(); +SELECT * FROM perl_record(); +SELECT * FROM perl_record() AS (f1 integer, f2 text, f3 text); + +CREATE OR REPLACE FUNCTION perl_record() RETURNS record AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; + +SELECT perl_record(); +SELECT * FROM perl_record(); +SELECT * FROM perl_record() AS (f1 integer, f2 text, f3 text); + + +CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$ + return undef; +$$ LANGUAGE plperl; + +SELECT perl_record_set(); +SELECT * FROM perl_record_set(); +SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text); + +CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + undef, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; + +SELECT perl_record_set(); +SELECT * FROM perl_record_set(); +SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text); + +CREATE OR REPLACE FUNCTION perl_record_set() RETURNS SETOF record AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; + +SELECT perl_record_set(); +SELECT * FROM perl_record_set(); +SELECT * FROM perl_record_set() AS (f1 integer, f2 text, f3 text); + +CREATE OR REPLACE FUNCTION +perl_out_params(f1 out integer, f2 out text, f3 out text) AS $$ + return {f2 => 'hello', f1 => 1, f3 => 'world'}; +$$ LANGUAGE plperl; + +SELECT perl_out_params(); +SELECT * FROM perl_out_params(); +SELECT (perl_out_params()).f2; + +CREATE OR REPLACE FUNCTION +perl_out_params_set(out f1 integer, out f2 text, out f3 text) +RETURNS SETOF record AS $$ + return [ + { f1 => 1, f2 => 'Hello', f3 => 'World' }, + { f1 => 2, f2 => 'Hello', f3 => 'PostgreSQL' }, + { f1 => 3, f2 => 'Hello', f3 => 'PL/Perl' } + ]; +$$ LANGUAGE plperl; + +SELECT perl_out_params_set(); +SELECT * FROM perl_out_params_set(); +SELECT (perl_out_params_set()).f3; + +-- +-- Check behavior with erroneous return values +-- + +CREATE TYPE footype AS (x INTEGER, y INTEGER); + +CREATE OR REPLACE FUNCTION foo_good() RETURNS SETOF footype AS $$ +return [ + {x => 1, y => 2}, + {x => 3, y => 4} +]; +$$ LANGUAGE plperl; + +SELECT * FROM foo_good(); + +CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$ + return {y => 3, z => 4}; +$$ LANGUAGE plperl; + +SELECT * FROM foo_bad(); + +CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$ +return 42; +$$ LANGUAGE plperl; + +SELECT * FROM foo_bad(); + +CREATE OR REPLACE FUNCTION foo_bad() RETURNS footype AS $$ +return [ + [1, 2], + [3, 4] +]; +$$ LANGUAGE plperl; + +SELECT * FROM foo_bad(); + +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ + return 42; +$$ LANGUAGE plperl; + +SELECT * FROM foo_set_bad(); + +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ + return {y => 3, z => 4}; +$$ LANGUAGE plperl; + +SELECT * FROM foo_set_bad(); + +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ +return [ + [1, 2], + [3, 4] +]; +$$ LANGUAGE plperl; + +SELECT * FROM foo_set_bad(); + +CREATE OR REPLACE FUNCTION foo_set_bad() RETURNS SETOF footype AS $$ +return [ + {y => 3, z => 4} +]; +$$ LANGUAGE plperl; + +SELECT * FROM foo_set_bad(); + +-- +-- Check passing a tuple argument +-- + +CREATE OR REPLACE FUNCTION perl_get_field(footype, text) RETURNS integer AS $$ + return $_[0]->{$_[1]}; +$$ LANGUAGE plperl; + +SELECT perl_get_field((11,12), 'x'); +SELECT perl_get_field((11,12), 'y'); +SELECT perl_get_field((11,12), 'z'); + +-- +-- Test return_next +-- + +CREATE OR REPLACE FUNCTION perl_srf_rn() RETURNS SETOF RECORD AS $$ +my $i = 0; +for ("World", "PostgreSQL", "PL/Perl") { + return_next({f1=>++$i, f2=>'Hello', f3=>$_}); +} +return; +$$ language plperl; +SELECT * from perl_srf_rn() AS (f1 INTEGER, f2 TEXT, f3 TEXT); + +-- +-- Test spi_query/spi_fetchrow +-- + +CREATE OR REPLACE FUNCTION perl_spi_func() RETURNS SETOF INTEGER AS $$ +my $x = spi_query("select 1 as a union select 2 as a"); +while (defined (my $y = spi_fetchrow($x))) { + return_next($y->{a}); +} +return; +$$ LANGUAGE plperl; +SELECT * from perl_spi_func(); + + +--- +--- Test recursion via SPI +--- + + +CREATE OR REPLACE FUNCTION recurse(i int) RETURNS SETOF TEXT LANGUAGE plperl +AS $$ + + my $i = shift; + foreach my $x (1..$i) + { + return_next "hello $x"; + } + if ($i > 2) + { + my $z = $i-1; + my $cursor = spi_query("select * from recurse($z)"); + while (defined(my $row = spi_fetchrow($cursor))) + { + return_next "recurse $i: $row->{recurse}"; + } + } + return undef; + +$$; + +SELECT * FROM recurse(2); +SELECT * FROM recurse(3); + + +--- +--- Test arrary return +--- +CREATE OR REPLACE FUNCTION array_of_text() RETURNS TEXT[][] +LANGUAGE plperl as $$ + return [['a"b','c,d'],['e\\f','g']]; +$$; + +SELECT array_of_text(); diff --git a/src/pl/plperl/sql/plperl_elog.sql b/src/pl/plperl/sql/plperl_elog.sql new file mode 100644 index 0000000..4f1c014 --- /dev/null +++ b/src/pl/plperl/sql/plperl_elog.sql @@ -0,0 +1,45 @@ +-- test warnings and errors from plperl + +create or replace function perl_elog(text) returns void language plperl as $$ + + my $msg = shift; + elog(NOTICE,$msg); + +$$; + +select perl_elog('explicit elog'); + +create or replace function perl_warn(text) returns void language plperl as $$ + + my $msg = shift; + warn($msg); + +$$; + +select perl_warn('implicit elog via warn'); + +-- test strict mode on/off + +SET plperl.use_strict = true; + +create or replace function uses_global() returns text language plperl as $$ + + $global = 1; + $other_global = 2; + return 'uses_global worked'; + +$$; + +select uses_global(); + +SET plperl.use_strict = false; + +create or replace function uses_global() returns text language plperl as $$ + + $global = 1; + $other_global=2; + return 'uses_global worked'; + +$$; + +select uses_global(); diff --git a/src/pl/plperl/sql/plperl_shared.sql b/src/pl/plperl/sql/plperl_shared.sql new file mode 100644 index 0000000..3e99e59 --- /dev/null +++ b/src/pl/plperl/sql/plperl_shared.sql @@ -0,0 +1,22 @@ +-- test the shared hash + +create function setme(key text, val text) returns void language plperl as $$ + + my $key = shift; + my $val = shift; + $_SHARED{$key}= $val; + +$$; + +create function getme(key text) returns text language plperl as $$ + + my $key = shift; + return $_SHARED{$key}; + +$$; + +select setme('ourkey','ourval'); + +select getme('ourkey'); + + diff --git a/src/pl/plperl/sql/plperl_trigger.sql b/src/pl/plperl/sql/plperl_trigger.sql new file mode 100644 index 0000000..34ce9c4 --- /dev/null +++ b/src/pl/plperl/sql/plperl_trigger.sql @@ -0,0 +1,61 @@ +-- test plperl triggers + +CREATE TABLE trigger_test ( + i int, + v varchar +); + +CREATE OR REPLACE FUNCTION valid_id() RETURNS trigger AS $$ + + if (($_TD->{new}{i}>=100) || ($_TD->{new}{i}<=0)) + { + return "SKIP"; # Skip INSERT/UPDATE command + } + elsif ($_TD->{new}{v} ne "immortal") + { + $_TD->{new}{v} .= "(modified by trigger)"; + return "MODIFY"; # Modify tuple and proceed INSERT/UPDATE command + } + else + { + return; # Proceed INSERT/UPDATE command + } +$$ LANGUAGE plperl; + +CREATE TRIGGER "test_valid_id_trig" BEFORE INSERT OR UPDATE ON trigger_test +FOR EACH ROW EXECUTE PROCEDURE "valid_id"(); + +INSERT INTO trigger_test (i, v) VALUES (1,'first line'); +INSERT INTO trigger_test (i, v) VALUES (2,'second line'); +INSERT INTO trigger_test (i, v) VALUES (3,'third line'); +INSERT INTO trigger_test (i, v) VALUES (4,'immortal'); + +INSERT INTO trigger_test (i, v) VALUES (101,'bad id'); + +SELECT * FROM trigger_test; + +UPDATE trigger_test SET i = 5 where i=3; + +UPDATE trigger_test SET i = 100 where i=1; + +SELECT * FROM trigger_test; + +CREATE OR REPLACE FUNCTION immortal() RETURNS trigger AS $$ + if ($_TD->{old}{v} eq $_TD->{args}[0]) + { + return "SKIP"; # Skip DELETE command + } + else + { + return; # Proceed DELETE command + }; +$$ LANGUAGE plperl; + +CREATE TRIGGER "immortal_trig" BEFORE DELETE ON trigger_test +FOR EACH ROW EXECUTE PROCEDURE immortal('immortal'); + +DELETE FROM trigger_test; + + +SELECT * FROM trigger_test; + diff --git a/src/pl/plpython/expected/plpython_drop.out b/src/pl/plpython/expected/plpython_drop.out new file mode 100644 index 0000000..fef642f --- /dev/null +++ b/src/pl/plpython/expected/plpython_drop.out @@ -0,0 +1,5 @@ +-- +-- For paranoia's sake, don't leave an untrusted language sitting around +-- +SET client_min_messages = WARNING; +DROP PROCEDURAL LANGUAGE plpythonu CASCADE; diff --git a/src/pl/plpython/expected/plpython_error.out b/src/pl/plpython/expected/plpython_error.out new file mode 100644 index 0000000..8a0b08f --- /dev/null +++ b/src/pl/plpython/expected/plpython_error.out @@ -0,0 +1,38 @@ +-- test error handling, i forgot to restore Warn_restart in +-- the trigger handler once. the errors and subsequent core dump were +-- interesting. +SELECT invalid_type_uncaught('rick'); +WARNING: plpython: in function invalid_type_uncaught: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT invalid_type_caught('rick'); +WARNING: plpython: in function invalid_type_caught: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT invalid_type_reraised('rick'); +WARNING: plpython: in function invalid_type_reraised: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT valid_type('rick'); + valid_type +------------ + +(1 row) + +-- +-- Test Unicode error handling. +-- +SELECT unicode_return_error(); +ERROR: plpython: function "unicode_return_error" could not create return value +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +INSERT INTO unicode_test (testvalue) VALUES ('test'); +ERROR: plpython: function "unicode_trigger_error" could not modify tuple +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +SELECT unicode_plan_error1(); +WARNING: plpython: in function unicode_plan_error1: +DETAIL: plpy.Error: Unknown error in PLy_spi_execute_plan +ERROR: plpython: function "unicode_plan_error1" could not execute plan +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) +SELECT unicode_plan_error2(); +ERROR: plpython: function "unicode_plan_error2" could not execute plan +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\x80' in position 0: ordinal not in range(128) diff --git a/src/pl/plpython/expected/plpython_error_1.out b/src/pl/plpython/expected/plpython_error_1.out new file mode 100644 index 0000000..7b1e587 --- /dev/null +++ b/src/pl/plpython/expected/plpython_error_1.out @@ -0,0 +1,38 @@ +-- test error handling, i forgot to restore Warn_restart in +-- the trigger handler once. the errors and subsequent core dump were +-- interesting. +SELECT invalid_type_uncaught('rick'); +WARNING: plpython: in function invalid_type_uncaught: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT invalid_type_caught('rick'); +WARNING: plpython: in function invalid_type_caught: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT invalid_type_reraised('rick'); +WARNING: plpython: in function invalid_type_reraised: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT valid_type('rick'); + valid_type +------------ + +(1 row) + +-- +-- Test Unicode error handling. +-- +SELECT unicode_return_error(); +ERROR: plpython: function "unicode_return_error" could not create return value +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character '\u80' in position 0: ordinal not in range(128) +INSERT INTO unicode_test (testvalue) VALUES ('test'); +ERROR: plpython: function "unicode_trigger_error" could not modify tuple +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character '\u80' in position 0: ordinal not in range(128) +SELECT unicode_plan_error1(); +WARNING: plpython: in function unicode_plan_error1: +DETAIL: plpy.Error: Unknown error in PLy_spi_execute_plan +ERROR: plpython: function "unicode_plan_error1" could not execute plan +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character '\u80' in position 0: ordinal not in range(128) +SELECT unicode_plan_error2(); +ERROR: plpython: function "unicode_plan_error2" could not execute plan +DETAIL: exceptions.UnicodeEncodeError: 'ascii' codec can't encode character '\u80' in position 0: ordinal not in range(128) diff --git a/src/pl/plpython/expected/plpython_error_2.out b/src/pl/plpython/expected/plpython_error_2.out new file mode 100644 index 0000000..fd97a52 --- /dev/null +++ b/src/pl/plpython/expected/plpython_error_2.out @@ -0,0 +1,38 @@ +-- test error handling, i forgot to restore Warn_restart in +-- the trigger handler once. the errors and subsequent core dump were +-- interesting. +SELECT invalid_type_uncaught('rick'); +WARNING: plpython: in function invalid_type_uncaught: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT invalid_type_caught('rick'); +WARNING: plpython: in function invalid_type_caught: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT invalid_type_reraised('rick'); +WARNING: plpython: in function invalid_type_reraised: +DETAIL: plpy.SPIError: Unknown error in PLy_spi_prepare +ERROR: type "test" does not exist +SELECT valid_type('rick'); + valid_type +------------ + +(1 row) + +-- +-- Test Unicode error handling. +-- +SELECT unicode_return_error(); +ERROR: plpython: function "unicode_return_error" could not create return value +DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) +INSERT INTO unicode_test (testvalue) VALUES ('test'); +ERROR: plpython: function "unicode_trigger_error" could not modify tuple +DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) +SELECT unicode_plan_error1(); +WARNING: plpython: in function unicode_plan_error1: +DETAIL: plpy.Error: Unknown error in PLy_spi_execute_plan +ERROR: plpython: function "unicode_plan_error1" could not execute plan +DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) +SELECT unicode_plan_error2(); +ERROR: plpython: function "unicode_plan_error2" could not execute plan +DETAIL: exceptions.UnicodeError: ASCII encoding error: ordinal not in range(128) diff --git a/src/pl/plpython/expected/plpython_populate.out b/src/pl/plpython/expected/plpython_populate.out new file mode 100644 index 0000000..4db75b0 --- /dev/null +++ b/src/pl/plpython/expected/plpython_populate.out @@ -0,0 +1,22 @@ +INSERT INTO users (fname, lname, username) VALUES ('jane', 'doe', 'j_doe'); +INSERT INTO users (fname, lname, username) VALUES ('john', 'doe', 'johnd'); +INSERT INTO users (fname, lname, username) VALUES ('willem', 'doe', 'w_doe'); +INSERT INTO users (fname, lname, username) VALUES ('rick', 'smith', 'slash'); +-- multi table tests +-- +INSERT INTO taxonomy (name) VALUES ('HIV I') ; +INSERT INTO taxonomy (name) VALUES ('HIV II') ; +INSERT INTO taxonomy (name) VALUES ('HCV') ; +INSERT INTO entry (accession, txid) VALUES ('A00001', '1') ; +INSERT INTO entry (accession, txid) VALUES ('A00002', '1') ; +INSERT INTO entry (accession, txid) VALUES ('A00003', '1') ; +INSERT INTO entry (accession, txid) VALUES ('A00004', '2') ; +INSERT INTO entry (accession, txid) VALUES ('A00005', '2') ; +INSERT INTO entry (accession, txid) VALUES ('A00006', '3') ; +INSERT INTO sequences (sequence, eid, product, multipart) VALUES ('ABCDEF', 1, 'env', 'true') ; +INSERT INTO xsequences (sequence, pid) VALUES ('GHIJKL', 1) ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 2, 'env') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 3, 'env') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 4, 'gag') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 5, 'env') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 6, 'ns1') ; diff --git a/src/pl/plpython/expected/plpython_schema.out b/src/pl/plpython/expected/plpython_schema.out new file mode 100644 index 0000000..727e4b8 --- /dev/null +++ b/src/pl/plpython/expected/plpython_schema.out @@ -0,0 +1,46 @@ +CREATE TABLE users ( + fname text not null, + lname text not null, + username text, + userid serial, + PRIMARY KEY(lname, fname) + ) ; +NOTICE: CREATE TABLE will create implicit sequence "users_userid_seq" for serial column "users.userid" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "users_pkey" for table "users" +CREATE INDEX users_username_idx ON users(username); +CREATE INDEX users_fname_idx ON users(fname); +CREATE INDEX users_lname_idx ON users(lname); +CREATE INDEX users_userid_idx ON users(userid); +CREATE TABLE taxonomy ( + id serial primary key, + name text unique + ) ; +NOTICE: CREATE TABLE will create implicit sequence "taxonomy_id_seq" for serial column "taxonomy.id" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "taxonomy_pkey" for table "taxonomy" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "taxonomy_name_key" for table "taxonomy" +CREATE TABLE entry ( + accession text not null primary key, + eid serial unique, + txid int2 not null references taxonomy(id) + ) ; +NOTICE: CREATE TABLE will create implicit sequence "entry_eid_seq" for serial column "entry.eid" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "entry_pkey" for table "entry" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "entry_eid_key" for table "entry" +CREATE TABLE sequences ( + eid int4 not null references entry(eid), + pid serial primary key, + product text not null, + sequence text not null, + multipart bool default 'false' + ) ; +NOTICE: CREATE TABLE will create implicit sequence "sequences_pid_seq" for serial column "sequences.pid" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "sequences_pkey" for table "sequences" +CREATE INDEX sequences_product_idx ON sequences(product) ; +CREATE TABLE xsequences ( + pid int4 not null references sequences(pid), + sequence text not null + ) ; +CREATE INDEX xsequences_pid_idx ON xsequences(pid) ; +CREATE TABLE unicode_test ( + testvalue text NOT NULL +); diff --git a/src/pl/plpython/expected/plpython_test.out b/src/pl/plpython/expected/plpython_test.out new file mode 100644 index 0000000..08704cb --- /dev/null +++ b/src/pl/plpython/expected/plpython_test.out @@ -0,0 +1,184 @@ +-- first some tests of basic functionality +-- +-- better succeed +-- +select stupid(); + stupid +-------- + zarkon +(1 row) + +-- check static and global data +-- +SELECT static_test(); + static_test +------------- + 1 +(1 row) + +SELECT static_test(); + static_test +------------- + 2 +(1 row) + +SELECT global_test_one(); + global_test_one +-------------------------------------------------------- + SD: set by global_test_one, GD: set by global_test_one +(1 row) + +SELECT global_test_two(); + global_test_two +-------------------------------------------------------- + SD: set by global_test_two, GD: set by global_test_one +(1 row) + +-- import python modules +-- +SELECT import_fail(); +NOTICE: ('import socket failed -- No module named foosocket',) + import_fail +-------------------- + failed as expected +(1 row) + +SELECT import_succeed(); + import_succeed +------------------------ + succeeded, as expected +(1 row) + +-- test import and simple argument handling +-- +SELECT import_test_one('sha hash of this string'); + import_test_one +------------------------------------------ + a04e23cb9b1a09cd1051a04a7c571aae0f90346c +(1 row) + +-- test import and tuple argument handling +-- +select import_test_two(users) from users where fname = 'willem'; + import_test_two +------------------------------------------------------------------- + sha hash of willemdoe is 3cde6b574953b0ca937b4d76ebc40d534d910759 +(1 row) + +-- test multiple arguments +-- +select argument_test_one(users, fname, lname) from users where lname = 'doe' order by 1; + argument_test_one +----------------------------------------------------------------------- + jane doe => {fname: jane, lname: doe, userid: 1, username: j_doe} + john doe => {fname: john, lname: doe, userid: 2, username: johnd} + willem doe => {fname: willem, lname: doe, userid: 3, username: w_doe} +(3 rows) + +-- spi and nested calls +-- +select nested_call_one('pass this along'); + nested_call_one +----------------------------------------------------------------- + {'nested_call_two': "{'nested_call_three': 'pass this along'}"} +(1 row) + +select spi_prepared_plan_test_one('doe'); + spi_prepared_plan_test_one +---------------------------- + there are 3 does +(1 row) + +select spi_prepared_plan_test_one('smith'); + spi_prepared_plan_test_one +---------------------------- + there are 1 smiths +(1 row) + +select spi_prepared_plan_test_nested('smith'); + spi_prepared_plan_test_nested +------------------------------- + there are 1 smiths +(1 row) + +-- quick peek at the table +-- +SELECT * FROM users; + fname | lname | username | userid +--------+-------+----------+-------- + jane | doe | j_doe | 1 + john | doe | johnd | 2 + willem | doe | w_doe | 3 + rick | smith | slash | 4 +(4 rows) + +-- should fail +-- +UPDATE users SET fname = 'william' WHERE fname = 'willem'; +-- should modify william to willem and create username +-- +INSERT INTO users (fname, lname) VALUES ('william', 'smith'); +INSERT INTO users (fname, lname, username) VALUES ('charles', 'darwin', 'beagle'); +SELECT * FROM users; + fname | lname | username | userid +---------+--------+----------+-------- + jane | doe | j_doe | 1 + john | doe | johnd | 2 + willem | doe | w_doe | 3 + rick | smith | slash | 4 + willem | smith | w_smith | 5 + charles | darwin | beagle | 6 +(6 rows) + +SELECT join_sequences(sequences) FROM sequences; + join_sequences +---------------- + ABCDEFGHIJKL + ABCDEF + ABCDEF + ABCDEF + ABCDEF + ABCDEF +(6 rows) + +SELECT join_sequences(sequences) FROM sequences + WHERE join_sequences(sequences) ~* '^A'; + join_sequences +---------------- + ABCDEFGHIJKL + ABCDEF + ABCDEF + ABCDEF + ABCDEF + ABCDEF +(6 rows) + +SELECT join_sequences(sequences) FROM sequences + WHERE join_sequences(sequences) ~* '^B'; + join_sequences +---------------- +(0 rows) + +-- error in trigger +-- +-- +-- Check Universal Newline Support +-- +SELECT newline_lf(); + newline_lf +------------ + 123 +(1 row) + +SELECT newline_cr(); + newline_cr +------------ + 123 +(1 row) + +SELECT newline_crlf(); + newline_crlf +-------------- + 123 +(1 row) + diff --git a/src/pl/plpython/sql/plpython_drop.sql b/src/pl/plpython/sql/plpython_drop.sql new file mode 100644 index 0000000..319d5e0 --- /dev/null +++ b/src/pl/plpython/sql/plpython_drop.sql @@ -0,0 +1,6 @@ +-- +-- For paranoia's sake, don't leave an untrusted language sitting around +-- +SET client_min_messages = WARNING; + +DROP PROCEDURAL LANGUAGE plpythonu CASCADE; diff --git a/src/pl/plpython/sql/plpython_error.sql b/src/pl/plpython/sql/plpython_error.sql new file mode 100644 index 0000000..b04b23e --- /dev/null +++ b/src/pl/plpython/sql/plpython_error.sql @@ -0,0 +1,18 @@ + +-- test error handling, i forgot to restore Warn_restart in +-- the trigger handler once. the errors and subsequent core dump were +-- interesting. + +SELECT invalid_type_uncaught('rick'); +SELECT invalid_type_caught('rick'); +SELECT invalid_type_reraised('rick'); +SELECT valid_type('rick'); + +-- +-- Test Unicode error handling. +-- + +SELECT unicode_return_error(); +INSERT INTO unicode_test (testvalue) VALUES ('test'); +SELECT unicode_plan_error1(); +SELECT unicode_plan_error2(); diff --git a/src/pl/plpython/sql/plpython_populate.sql b/src/pl/plpython/sql/plpython_populate.sql new file mode 100644 index 0000000..a1963e7 --- /dev/null +++ b/src/pl/plpython/sql/plpython_populate.sql @@ -0,0 +1,28 @@ + +INSERT INTO users (fname, lname, username) VALUES ('jane', 'doe', 'j_doe'); +INSERT INTO users (fname, lname, username) VALUES ('john', 'doe', 'johnd'); +INSERT INTO users (fname, lname, username) VALUES ('willem', 'doe', 'w_doe'); +INSERT INTO users (fname, lname, username) VALUES ('rick', 'smith', 'slash'); + + +-- multi table tests +-- + +INSERT INTO taxonomy (name) VALUES ('HIV I') ; +INSERT INTO taxonomy (name) VALUES ('HIV II') ; +INSERT INTO taxonomy (name) VALUES ('HCV') ; + +INSERT INTO entry (accession, txid) VALUES ('A00001', '1') ; +INSERT INTO entry (accession, txid) VALUES ('A00002', '1') ; +INSERT INTO entry (accession, txid) VALUES ('A00003', '1') ; +INSERT INTO entry (accession, txid) VALUES ('A00004', '2') ; +INSERT INTO entry (accession, txid) VALUES ('A00005', '2') ; +INSERT INTO entry (accession, txid) VALUES ('A00006', '3') ; + +INSERT INTO sequences (sequence, eid, product, multipart) VALUES ('ABCDEF', 1, 'env', 'true') ; +INSERT INTO xsequences (sequence, pid) VALUES ('GHIJKL', 1) ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 2, 'env') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 3, 'env') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 4, 'gag') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 5, 'env') ; +INSERT INTO sequences (sequence, eid, product) VALUES ('ABCDEF', 6, 'ns1') ; \ No newline at end of file diff --git a/src/pl/plpython/sql/plpython_schema.sql b/src/pl/plpython/sql/plpython_schema.sql new file mode 100644 index 0000000..1f5ee6e --- /dev/null +++ b/src/pl/plpython/sql/plpython_schema.sql @@ -0,0 +1,44 @@ + +CREATE TABLE users ( + fname text not null, + lname text not null, + username text, + userid serial, + PRIMARY KEY(lname, fname) + ) ; + +CREATE INDEX users_username_idx ON users(username); +CREATE INDEX users_fname_idx ON users(fname); +CREATE INDEX users_lname_idx ON users(lname); +CREATE INDEX users_userid_idx ON users(userid); + + +CREATE TABLE taxonomy ( + id serial primary key, + name text unique + ) ; + +CREATE TABLE entry ( + accession text not null primary key, + eid serial unique, + txid int2 not null references taxonomy(id) + ) ; + +CREATE TABLE sequences ( + eid int4 not null references entry(eid), + pid serial primary key, + product text not null, + sequence text not null, + multipart bool default 'false' + ) ; +CREATE INDEX sequences_product_idx ON sequences(product) ; + +CREATE TABLE xsequences ( + pid int4 not null references sequences(pid), + sequence text not null + ) ; +CREATE INDEX xsequences_pid_idx ON xsequences(pid) ; + +CREATE TABLE unicode_test ( + testvalue text NOT NULL +); diff --git a/src/pl/plpython/sql/plpython_test.sql b/src/pl/plpython/sql/plpython_test.sql new file mode 100644 index 0000000..17d6b2e --- /dev/null +++ b/src/pl/plpython/sql/plpython_test.sql @@ -0,0 +1,70 @@ +-- first some tests of basic functionality +-- +-- better succeed +-- +select stupid(); + +-- check static and global data +-- +SELECT static_test(); +SELECT static_test(); +SELECT global_test_one(); +SELECT global_test_two(); + +-- import python modules +-- +SELECT import_fail(); +SELECT import_succeed(); + +-- test import and simple argument handling +-- +SELECT import_test_one('sha hash of this string'); + +-- test import and tuple argument handling +-- +select import_test_two(users) from users where fname = 'willem'; + +-- test multiple arguments +-- +select argument_test_one(users, fname, lname) from users where lname = 'doe' order by 1; + + +-- spi and nested calls +-- +select nested_call_one('pass this along'); +select spi_prepared_plan_test_one('doe'); +select spi_prepared_plan_test_one('smith'); +select spi_prepared_plan_test_nested('smith'); + +-- quick peek at the table +-- +SELECT * FROM users; + +-- should fail +-- +UPDATE users SET fname = 'william' WHERE fname = 'willem'; + +-- should modify william to willem and create username +-- +INSERT INTO users (fname, lname) VALUES ('william', 'smith'); +INSERT INTO users (fname, lname, username) VALUES ('charles', 'darwin', 'beagle'); + +SELECT * FROM users; + + +SELECT join_sequences(sequences) FROM sequences; +SELECT join_sequences(sequences) FROM sequences + WHERE join_sequences(sequences) ~* '^A'; +SELECT join_sequences(sequences) FROM sequences + WHERE join_sequences(sequences) ~* '^B'; + +-- error in trigger +-- + +-- +-- Check Universal Newline Support +-- + +SELECT newline_lf(); +SELECT newline_cr(); +SELECT newline_crlf(); diff --git a/src/pl/tcl/expected/pltcl_queries.out b/src/pl/tcl/expected/pltcl_queries.out new file mode 100644 index 0000000..600b158 --- /dev/null +++ b/src/pl/tcl/expected/pltcl_queries.out @@ -0,0 +1,185 @@ +-- suppress CONTEXT so that function OIDs aren't in output +\set VERBOSITY terse +insert into T_pkey1 values (1, 'key1-1', 'test key'); +insert into T_pkey1 values (1, 'key1-2', 'test key'); +insert into T_pkey1 values (1, 'key1-3', 'test key'); +insert into T_pkey1 values (2, 'key2-1', 'test key'); +insert into T_pkey1 values (2, 'key2-2', 'test key'); +insert into T_pkey1 values (2, 'key2-3', 'test key'); +insert into T_pkey2 values (1, 'key1-1', 'test key'); +insert into T_pkey2 values (1, 'key1-2', 'test key'); +insert into T_pkey2 values (1, 'key1-3', 'test key'); +insert into T_pkey2 values (2, 'key2-1', 'test key'); +insert into T_pkey2 values (2, 'key2-2', 'test key'); +insert into T_pkey2 values (2, 'key2-3', 'test key'); +select * from T_pkey1; + key1 | key2 | txt +------+----------------------+------------------------------------------ + 1 | key1-1 | test key + 1 | key1-2 | test key + 1 | key1-3 | test key + 2 | key2-1 | test key + 2 | key2-2 | test key + 2 | key2-3 | test key +(6 rows) + +-- key2 in T_pkey2 should have upper case only +select * from T_pkey2; + key1 | key2 | txt +------+----------------------+------------------------------------------ + 1 | KEY1-1 | test key + 1 | KEY1-2 | test key + 1 | KEY1-3 | test key + 2 | KEY2-1 | test key + 2 | KEY2-2 | test key + 2 | KEY2-3 | test key +(6 rows) + +insert into T_pkey1 values (1, 'KEY1-3', 'should work'); +-- Due to the upper case translation in trigger this must fail +insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); +ERROR: duplicate key '1', 'KEY1-3' for T_pkey2 +insert into T_dta1 values ('trec 1', 1, 'key1-1'); +insert into T_dta1 values ('trec 2', 1, 'key1-2'); +insert into T_dta1 values ('trec 3', 1, 'key1-3'); +-- Must fail due to unknown key in T_pkey1 +insert into T_dta1 values ('trec 4', 1, 'key1-4'); +ERROR: key for t_dta1 not in t_pkey1 +insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); +insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); +insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); +-- Must fail due to unknown key in T_pkey2 +insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); +ERROR: key for t_dta2 not in t_pkey2 +select * from T_dta1; + tkey | ref1 | ref2 +------------+------+---------------------- + trec 1 | 1 | key1-1 + trec 2 | 1 | key1-2 + trec 3 | 1 | key1-3 +(3 rows) + +select * from T_dta2; + tkey | ref1 | ref2 +------------+------+---------------------- + trec 1 | 1 | KEY1-1 + trec 2 | 1 | KEY1-2 + trec 3 | 1 | KEY1-3 +(3 rows) + +update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; +update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; +ERROR: key '1', 'key1-1 ' referenced by T_dta1 +delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; +delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; +ERROR: key '1', 'key1-2 ' referenced by T_dta1 +update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; +update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; +NOTICE: updated 1 entries in T_dta2 for new key in T_pkey2 +delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; +delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; +NOTICE: deleted 1 entries from T_dta2 +select * from T_pkey1; + key1 | key2 | txt +------+----------------------+------------------------------------------ + 1 | key1-1 | test key + 1 | key1-2 | test key + 1 | key1-3 | test key + 2 | key2-3 | test key + 1 | KEY1-3 | should work + 2 | key2-9 | test key +(6 rows) + +select * from T_pkey2; + key1 | key2 | txt +------+----------------------+------------------------------------------ + 1 | KEY1-3 | test key + 2 | KEY2-3 | test key + 2 | KEY2-9 | test key + 1 | KEY1-9 | test key +(4 rows) + +select * from T_dta1; + tkey | ref1 | ref2 +------------+------+---------------------- + trec 1 | 1 | key1-1 + trec 2 | 1 | key1-2 + trec 3 | 1 | key1-3 +(3 rows) + +select * from T_dta2; + tkey | ref1 | ref2 +------------+------+---------------------- + trec 3 | 1 | KEY1-3 + trec 1 | 1 | KEY1-9 +(2 rows) + +select tcl_avg(key1) from T_pkey1; + tcl_avg +--------- + 1 +(1 row) + +select tcl_sum(key1) from T_pkey1; + tcl_sum +--------- + 8 +(1 row) + +select tcl_avg(key1) from T_pkey2; + tcl_avg +--------- + 1 +(1 row) + +select tcl_sum(key1) from T_pkey2; + tcl_sum +--------- + 6 +(1 row) + +-- The following should return NULL instead of 0 +select tcl_avg(key1) from T_pkey1 where key1 = 99; + tcl_avg +--------- + +(1 row) + +select tcl_sum(key1) from T_pkey1 where key1 = 99; + tcl_sum +--------- + 0 +(1 row) + +select 1 @< 2; + ?column? +---------- + t +(1 row) + +select 100 @< 4; + ?column? +---------- + f +(1 row) + +select * from T_pkey1 order by key1 using @<, key2; + key1 | key2 | txt +------+----------------------+------------------------------------------ + 1 | KEY1-3 | should work + 1 | key1-1 | test key + 1 | key1-2 | test key + 1 | key1-3 | test key + 2 | key2-3 | test key + 2 | key2-9 | test key +(6 rows) + +select * from T_pkey2 order by key1 using @<, key2; + key1 | key2 | txt +------+----------------------+------------------------------------------ + 1 | KEY1-3 | test key + 1 | KEY1-9 | test key + 2 | KEY2-3 | test key + 2 | KEY2-9 | test key +(4 rows) + diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out new file mode 100644 index 0000000..e168b12 --- /dev/null +++ b/src/pl/tcl/expected/pltcl_setup.out @@ -0,0 +1,404 @@ +-- +-- checkpoint so that if we have a crash in the tests, replay of the +-- just-completed CREATE DATABASE won't discard the core dump file +-- +checkpoint; +-- +-- Create the tables used in the test queries +-- +-- T_pkey1 is the primary key table for T_dta1. Entries from T_pkey1 +-- Cannot be changed or deleted if they are referenced from T_dta1. +-- +-- T_pkey2 is the primary key table for T_dta2. If the key values in +-- T_pkey2 are changed, the references in T_dta2 follow. If entries +-- are deleted, the referencing entries from T_dta2 are deleted too. +-- The values for field key2 in T_pkey2 are silently converted to +-- upper case on insert/update. +-- +create table T_pkey1 ( + key1 int4, + key2 char(20), + txt char(40) +); +create table T_pkey2 ( + key1 int4, + key2 char(20), + txt char(40) +); +create table T_dta1 ( + tkey char(10), + ref1 int4, + ref2 char(20) +); +create table T_dta2 ( + tkey char(10), + ref1 int4, + ref2 char(20) +); +-- +-- Function to check key existance in T_pkey1 +-- +create function check_pkey1_exists(int4, bpchar) returns bool as ' + if {![info exists GD]} { + set GD(plan) [spi_prepare \\ + "select 1 from T_pkey1 \\ + where key1 = \\$1 and key2 = \\$2" \\ + {int4 bpchar}] + } + + set n [spi_execp -count 1 $GD(plan) [list $1 $2]] + + if {$n > 0} { + return "t" + } + return "f" +' language 'pltcl'; +-- +-- Trigger function on every change to T_pkey1 +-- +create function trig_pkey1_before() returns trigger as ' + # + # Create prepared plans on the first call + # + if {![info exists GD]} { + # + # Plan to check for duplicate key in T_pkey1 + # + set GD(plan_pkey1) [spi_prepare \\ + "select check_pkey1_exists(\\$1, \\$2) as ret" \\ + {int4 bpchar}] + # + # Plan to check for references from T_dta1 + # + set GD(plan_dta1) [spi_prepare \\ + "select 1 from T_dta1 \\ + where ref1 = \\$1 and ref2 = \\$2" \\ + {int4 bpchar}] + } + + # + # Initialize flags + # + set check_old_ref 0 + set check_new_dup 0 + + switch $TG_op { + INSERT { + # + # Must check for duplicate key on INSERT + # + set check_new_dup 1 + } + UPDATE { + # + # Must check for duplicate key on UPDATE only if + # the key changes. In that case we must check for + # references to OLD values too. + # + if {[string compare $NEW(key1) $OLD(key1)] != 0} { + set check_old_ref 1 + set check_new_dup 1 + } + if {[string compare $NEW(key2) $OLD(key2)] != 0} { + set check_old_ref 1 + set check_new_dup 1 + } + } + DELETE { + # + # Must only check for references to OLD on DELETE + # + set check_old_ref 1 + } + } + + if {$check_new_dup} { + # + # Check for duplicate key + # + spi_execp -count 1 $GD(plan_pkey1) [list $NEW(key1) $NEW(key2)] + if {$ret == "t"} { + elog ERROR \\ + "duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey1" + } + } + + if {$check_old_ref} { + # + # Check for references to OLD + # + set n [spi_execp -count 1 $GD(plan_dta1) [list $OLD(key1) $OLD(key2)]] + if {$n > 0} { + elog ERROR \\ + "key ''$OLD(key1)'', ''$OLD(key2)'' referenced by T_dta1" + } + } + + # + # Anything is fine - let operation pass through + # + return OK +' language 'pltcl'; +create trigger pkey1_before before insert or update or delete on T_pkey1 + for each row execute procedure + trig_pkey1_before(); +-- +-- Trigger function to check for duplicate keys in T_pkey2 +-- and to force key2 to be upper case only without leading whitespaces +-- +create function trig_pkey2_before() returns trigger as ' + # + # Prepare plan on first call + # + if {![info exists GD]} { + set GD(plan_pkey2) [spi_prepare \\ + "select 1 from T_pkey2 \\ + where key1 = \\$1 and key2 = \\$2" \\ + {int4 bpchar}] + } + + # + # Convert key2 value + # + set NEW(key2) [string toupper [string trim $NEW(key2)]] + + # + # Check for duplicate key + # + set n [spi_execp -count 1 $GD(plan_pkey2) [list $NEW(key1) $NEW(key2)]] + if {$n > 0} { + elog ERROR \\ + "duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey2" + } + + # + # Return modified tuple in NEW + # + return [array get NEW] +' language 'pltcl'; +create trigger pkey2_before before insert or update on T_pkey2 + for each row execute procedure + trig_pkey2_before(); +-- +-- Trigger function to force references from T_dta2 follow changes +-- in T_pkey2 or be deleted too. This must be done AFTER the changes +-- in T_pkey2 are done so the trigger for primkey check on T_dta2 +-- fired on our updates will see the new key values in T_pkey2. +-- +create function trig_pkey2_after() returns trigger as ' + # + # Prepare plans on first call + # + if {![info exists GD]} { + # + # Plan to update references from T_dta2 + # + set GD(plan_dta2_upd) [spi_prepare \\ + "update T_dta2 set ref1 = \\$3, ref2 = \\$4 \\ + where ref1 = \\$1 and ref2 = \\$2" \\ + {int4 bpchar int4 bpchar}] + # + # Plan to delete references from T_dta2 + # + set GD(plan_dta2_del) [spi_prepare \\ + "delete from T_dta2 \\ + where ref1 = \\$1 and ref2 = \\$2" \\ + {int4 bpchar}] + } + + # + # Initialize flags + # + set old_ref_follow 0 + set old_ref_delete 0 + + switch $TG_op { + UPDATE { + # + # On update we must let old references follow + # + set NEW(key2) [string toupper $NEW(key2)] + + if {[string compare $NEW(key1) $OLD(key1)] != 0} { + set old_ref_follow 1 + } + if {[string compare $NEW(key2) $OLD(key2)] != 0} { + set old_ref_follow 1 + } + } + DELETE { + # + # On delete we must delete references too + # + set old_ref_delete 1 + } + } + + if {$old_ref_follow} { + # + # Let old references follow and fire NOTICE message if + # there where some + # + set n [spi_execp $GD(plan_dta2_upd) \\ + [list $OLD(key1) $OLD(key2) $NEW(key1) $NEW(key2)]] + if {$n > 0} { + elog NOTICE \\ + "updated $n entries in T_dta2 for new key in T_pkey2" + } + } + + if {$old_ref_delete} { + # + # delete references and fire NOTICE message if + # there where some + # + set n [spi_execp $GD(plan_dta2_del) \\ + [list $OLD(key1) $OLD(key2)]] + if {$n > 0} { + elog NOTICE \\ + "deleted $n entries from T_dta2" + } + } + + return OK +' language 'pltcl'; +create trigger pkey2_after after update or delete on T_pkey2 + for each row execute procedure + trig_pkey2_after(); +-- +-- Generic trigger function to check references in T_dta1 and T_dta2 +-- +create function check_primkey() returns trigger as ' + # + # For every trigger/relation pair we create + # a saved plan and hold them in GD + # + set plankey [list "plan" $TG_name $TG_relid] + set planrel [list "relname" $TG_relid] + + # + # Extract the pkey relation name + # + set keyidx [expr [llength $args] / 2] + set keyrel [string tolower [lindex $args $keyidx]] + + if {![info exists GD($plankey)]} { + # + # We must prepare a new plan. Build up a query string + # for the primary key check. + # + set keylist [lrange $args [expr $keyidx + 1] end] + + set query "select 1 from $keyrel" + set qual " where" + set typlist "" + set idx 1 + foreach key $keylist { + set key [string tolower $key] + # + # Add the qual part to the query string + # + append query "$qual $key = \\$$idx" + set qual " and" + + # + # Lookup the fields type in pg_attribute + # + set n [spi_exec "select T.typname \\ + from pg_catalog.pg_type T, pg_catalog.pg_attribute A, pg_catalog.pg_class C \\ + where C.relname = ''[quote $keyrel]'' \\ + and C.oid = A.attrelid \\ + and A.attname = ''[quote $key]'' \\ + and A.atttypid = T.oid"] + if {$n != 1} { + elog ERROR "table $keyrel doesn''t have a field named $key" + } + + # + # Append the fields type to the argument type list + # + lappend typlist $typname + incr idx + } + + # + # Prepare the plan + # + set GD($plankey) [spi_prepare $query $typlist] + + # + # Lookup and remember the table name for later error messages + # + spi_exec "select relname from pg_catalog.pg_class \\ + where oid = ''$TG_relid''::oid" + set GD($planrel) $relname + } + + # + # Build the argument list from the NEW row + # + incr keyidx -1 + set arglist "" + foreach arg [lrange $args 0 $keyidx] { + lappend arglist $NEW($arg) + } + + # + # Check for the primary key + # + set n [spi_execp -count 1 $GD($plankey) $arglist] + if {$n <= 0} { + elog ERROR "key for $GD($planrel) not in $keyrel" + } + + # + # Anything is fine + # + return OK +' language 'pltcl'; +create trigger dta1_before before insert or update on T_dta1 + for each row execute procedure + check_primkey('ref1', 'ref2', 'T_pkey1', 'key1', 'key2'); +create trigger dta2_before before insert or update on T_dta2 + for each row execute procedure + check_primkey('ref1', 'ref2', 'T_pkey2', 'key1', 'key2'); +create function tcl_int4add(int4,int4) returns int4 as ' + return [expr $1 + $2] +' language 'pltcl'; +-- We use split(n) as a quick-and-dirty way of parsing the input array +-- value, which comes in as a string like '{1,2}'. There are better ways... +create function tcl_int4_accum(int4[], int4) returns int4[] as ' + set state [split $1 "{,}"] + set newsum [expr {[lindex $state 1] + $2}] + set newcnt [expr {[lindex $state 2] + 1}] + return "{$newsum,$newcnt}" +' language 'pltcl'; +create function tcl_int4_avg(int4[]) returns int4 as ' + set state [split $1 "{,}"] + if {[lindex $state 2] == 0} { return_null } + return [expr {[lindex $state 1] / [lindex $state 2]}] +' language 'pltcl'; +create aggregate tcl_avg ( + sfunc = tcl_int4_accum, + basetype = int4, + stype = int4[], + finalfunc = tcl_int4_avg, + initcond = '{0,0}' + ); +create aggregate tcl_sum ( + sfunc = tcl_int4add, + basetype = int4, + stype = int4, + initcond1 = 0 + ); +create function tcl_int4lt(int4,int4) returns bool as ' + if {$1 < $2} { + return t + } + return f +' language 'pltcl'; +create operator @< ( + leftarg = int4, + rightarg = int4, + procedure = tcl_int4lt + ); diff --git a/src/pl/tcl/sql/pltcl_queries.sql b/src/pl/tcl/sql/pltcl_queries.sql new file mode 100644 index 0000000..9cb059e --- /dev/null +++ b/src/pl/tcl/sql/pltcl_queries.sql @@ -0,0 +1,75 @@ +-- suppress CONTEXT so that function OIDs aren't in output +\set VERBOSITY terse + +insert into T_pkey1 values (1, 'key1-1', 'test key'); +insert into T_pkey1 values (1, 'key1-2', 'test key'); +insert into T_pkey1 values (1, 'key1-3', 'test key'); +insert into T_pkey1 values (2, 'key2-1', 'test key'); +insert into T_pkey1 values (2, 'key2-2', 'test key'); +insert into T_pkey1 values (2, 'key2-3', 'test key'); + +insert into T_pkey2 values (1, 'key1-1', 'test key'); +insert into T_pkey2 values (1, 'key1-2', 'test key'); +insert into T_pkey2 values (1, 'key1-3', 'test key'); +insert into T_pkey2 values (2, 'key2-1', 'test key'); +insert into T_pkey2 values (2, 'key2-2', 'test key'); +insert into T_pkey2 values (2, 'key2-3', 'test key'); + +select * from T_pkey1; + +-- key2 in T_pkey2 should have upper case only +select * from T_pkey2; + +insert into T_pkey1 values (1, 'KEY1-3', 'should work'); + +-- Due to the upper case translation in trigger this must fail +insert into T_pkey2 values (1, 'KEY1-3', 'should fail'); + +insert into T_dta1 values ('trec 1', 1, 'key1-1'); +insert into T_dta1 values ('trec 2', 1, 'key1-2'); +insert into T_dta1 values ('trec 3', 1, 'key1-3'); + +-- Must fail due to unknown key in T_pkey1 +insert into T_dta1 values ('trec 4', 1, 'key1-4'); + +insert into T_dta2 values ('trec 1', 1, 'KEY1-1'); +insert into T_dta2 values ('trec 2', 1, 'KEY1-2'); +insert into T_dta2 values ('trec 3', 1, 'KEY1-3'); + +-- Must fail due to unknown key in T_pkey2 +insert into T_dta2 values ('trec 4', 1, 'KEY1-4'); + +select * from T_dta1; + +select * from T_dta2; + +update T_pkey1 set key2 = 'key2-9' where key1 = 2 and key2 = 'key2-1'; +update T_pkey1 set key2 = 'key1-9' where key1 = 1 and key2 = 'key1-1'; +delete from T_pkey1 where key1 = 2 and key2 = 'key2-2'; +delete from T_pkey1 where key1 = 1 and key2 = 'key1-2'; + +update T_pkey2 set key2 = 'KEY2-9' where key1 = 2 and key2 = 'KEY2-1'; +update T_pkey2 set key2 = 'KEY1-9' where key1 = 1 and key2 = 'KEY1-1'; +delete from T_pkey2 where key1 = 2 and key2 = 'KEY2-2'; +delete from T_pkey2 where key1 = 1 and key2 = 'KEY1-2'; + +select * from T_pkey1; +select * from T_pkey2; +select * from T_dta1; +select * from T_dta2; + +select tcl_avg(key1) from T_pkey1; +select tcl_sum(key1) from T_pkey1; +select tcl_avg(key1) from T_pkey2; +select tcl_sum(key1) from T_pkey2; + +-- The following should return NULL instead of 0 +select tcl_avg(key1) from T_pkey1 where key1 = 99; +select tcl_sum(key1) from T_pkey1 where key1 = 99; + +select 1 @< 2; +select 100 @< 4; + +select * from T_pkey1 order by key1 using @<, key2; +select * from T_pkey2 order by key1 using @<, key2; + diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql new file mode 100644 index 0000000..78ddd86 --- /dev/null +++ b/src/pl/tcl/sql/pltcl_setup.sql @@ -0,0 +1,438 @@ +-- +-- checkpoint so that if we have a crash in the tests, replay of the +-- just-completed CREATE DATABASE won't discard the core dump file +-- +checkpoint; + +-- +-- Create the tables used in the test queries +-- +-- T_pkey1 is the primary key table for T_dta1. Entries from T_pkey1 +-- Cannot be changed or deleted if they are referenced from T_dta1. +-- +-- T_pkey2 is the primary key table for T_dta2. If the key values in +-- T_pkey2 are changed, the references in T_dta2 follow. If entries +-- are deleted, the referencing entries from T_dta2 are deleted too. +-- The values for field key2 in T_pkey2 are silently converted to +-- upper case on insert/update. +-- +create table T_pkey1 ( + key1 int4, + key2 char(20), + txt char(40) +); + +create table T_pkey2 ( + key1 int4, + key2 char(20), + txt char(40) +); + +create table T_dta1 ( + tkey char(10), + ref1 int4, + ref2 char(20) +); + +create table T_dta2 ( + tkey char(10), + ref1 int4, + ref2 char(20) +); + + +-- +-- Function to check key existance in T_pkey1 +-- +create function check_pkey1_exists(int4, bpchar) returns bool as ' + if {![info exists GD]} { + set GD(plan) [spi_prepare \\ + "select 1 from T_pkey1 \\ + where key1 = \\$1 and key2 = \\$2" \\ + {int4 bpchar}] + } + + set n [spi_execp -count 1 $GD(plan) [list $1 $2]] + + if {$n > 0} { + return "t" + } + return "f" +' language 'pltcl'; + + +-- +-- Trigger function on every change to T_pkey1 +-- +create function trig_pkey1_before() returns trigger as ' + # + # Create prepared plans on the first call + # + if {![info exists GD]} { + # + # Plan to check for duplicate key in T_pkey1 + # + set GD(plan_pkey1) [spi_prepare \\ + "select check_pkey1_exists(\\$1, \\$2) as ret" \\ + {int4 bpchar}] + # + # Plan to check for references from T_dta1 + # + set GD(plan_dta1) [spi_prepare \\ + "select 1 from T_dta1 \\ + where ref1 = \\$1 and ref2 = \\$2" \\ + {int4 bpchar}] + } + + # + # Initialize flags + # + set check_old_ref 0 + set check_new_dup 0 + + switch $TG_op { + INSERT { + # + # Must check for duplicate key on INSERT + # + set check_new_dup 1 + } + UPDATE { + # + # Must check for duplicate key on UPDATE only if + # the key changes. In that case we must check for + # references to OLD values too. + # + if {[string compare $NEW(key1) $OLD(key1)] != 0} { + set check_old_ref 1 + set check_new_dup 1 + } + if {[string compare $NEW(key2) $OLD(key2)] != 0} { + set check_old_ref 1 + set check_new_dup 1 + } + } + DELETE { + # + # Must only check for references to OLD on DELETE + # + set check_old_ref 1 + } + } + + if {$check_new_dup} { + # + # Check for duplicate key + # + spi_execp -count 1 $GD(plan_pkey1) [list $NEW(key1) $NEW(key2)] + if {$ret == "t"} { + elog ERROR \\ + "duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey1" + } + } + + if {$check_old_ref} { + # + # Check for references to OLD + # + set n [spi_execp -count 1 $GD(plan_dta1) [list $OLD(key1) $OLD(key2)]] + if {$n > 0} { + elog ERROR \\ + "key ''$OLD(key1)'', ''$OLD(key2)'' referenced by T_dta1" + } + } + + # + # Anything is fine - let operation pass through + # + return OK +' language 'pltcl'; + + +create trigger pkey1_before before insert or update or delete on T_pkey1 + for each row execute procedure + trig_pkey1_before(); + + +-- +-- Trigger function to check for duplicate keys in T_pkey2 +-- and to force key2 to be upper case only without leading whitespaces +-- +create function trig_pkey2_before() returns trigger as ' + # + # Prepare plan on first call + # + if {![info exists GD]} { + set GD(plan_pkey2) [spi_prepare \\ + "select 1 from T_pkey2 \\ + where key1 = \\$1 and key2 = \\$2" \\ + {int4 bpchar}] + } + + # + # Convert key2 value + # + set NEW(key2) [string toupper [string trim $NEW(key2)]] + + # + # Check for duplicate key + # + set n [spi_execp -count 1 $GD(plan_pkey2) [list $NEW(key1) $NEW(key2)]] + if {$n > 0} { + elog ERROR \\ + "duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey2" + } + + # + # Return modified tuple in NEW + # + return [array get NEW] +' language 'pltcl'; + + +create trigger pkey2_before before insert or update on T_pkey2 + for each row execute procedure + trig_pkey2_before(); + + +-- +-- Trigger function to force references from T_dta2 follow changes +-- in T_pkey2 or be deleted too. This must be done AFTER the changes +-- in T_pkey2 are done so the trigger for primkey check on T_dta2 +-- fired on our updates will see the new key values in T_pkey2. +-- +create function trig_pkey2_after() returns trigger as ' + # + # Prepare plans on first call + # + if {![info exists GD]} { + # + # Plan to update references from T_dta2 + # + set GD(plan_dta2_upd) [spi_prepare \\ + "update T_dta2 set ref1 = \\$3, ref2 = \\$4 \\ + where ref1 = \\$1 and ref2 = \\$2" \\ + {int4 bpchar int4 bpchar}] + # + # Plan to delete references from T_dta2 + # + set GD(plan_dta2_del) [spi_prepare \\ + "delete from T_dta2 \\ + where ref1 = \\$1 and ref2 = \\$2" \\ + {int4 bpchar}] + } + + # + # Initialize flags + # + set old_ref_follow 0 + set old_ref_delete 0 + + switch $TG_op { + UPDATE { + # + # On update we must let old references follow + # + set NEW(key2) [string toupper $NEW(key2)] + + if {[string compare $NEW(key1) $OLD(key1)] != 0} { + set old_ref_follow 1 + } + if {[string compare $NEW(key2) $OLD(key2)] != 0} { + set old_ref_follow 1 + } + } + DELETE { + # + # On delete we must delete references too + # + set old_ref_delete 1 + } + } + + if {$old_ref_follow} { + # + # Let old references follow and fire NOTICE message if + # there where some + # + set n [spi_execp $GD(plan_dta2_upd) \\ + [list $OLD(key1) $OLD(key2) $NEW(key1) $NEW(key2)]] + if {$n > 0} { + elog NOTICE \\ + "updated $n entries in T_dta2 for new key in T_pkey2" + } + } + + if {$old_ref_delete} { + # + # delete references and fire NOTICE message if + # there where some + # + set n [spi_execp $GD(plan_dta2_del) \\ + [list $OLD(key1) $OLD(key2)]] + if {$n > 0} { + elog NOTICE \\ + "deleted $n entries from T_dta2" + } + } + + return OK +' language 'pltcl'; + + +create trigger pkey2_after after update or delete on T_pkey2 + for each row execute procedure + trig_pkey2_after(); + + +-- +-- Generic trigger function to check references in T_dta1 and T_dta2 +-- +create function check_primkey() returns trigger as ' + # + # For every trigger/relation pair we create + # a saved plan and hold them in GD + # + set plankey [list "plan" $TG_name $TG_relid] + set planrel [list "relname" $TG_relid] + + # + # Extract the pkey relation name + # + set keyidx [expr [llength $args] / 2] + set keyrel [string tolower [lindex $args $keyidx]] + + if {![info exists GD($plankey)]} { + # + # We must prepare a new plan. Build up a query string + # for the primary key check. + # + set keylist [lrange $args [expr $keyidx + 1] end] + + set query "select 1 from $keyrel" + set qual " where" + set typlist "" + set idx 1 + foreach key $keylist { + set key [string tolower $key] + # + # Add the qual part to the query string + # + append query "$qual $key = \\$$idx" + set qual " and" + + # + # Lookup the fields type in pg_attribute + # + set n [spi_exec "select T.typname \\ + from pg_catalog.pg_type T, pg_catalog.pg_attribute A, pg_catalog.pg_class C \\ + where C.relname = ''[quote $keyrel]'' \\ + and C.oid = A.attrelid \\ + and A.attname = ''[quote $key]'' \\ + and A.atttypid = T.oid"] + if {$n != 1} { + elog ERROR "table $keyrel doesn''t have a field named $key" + } + + # + # Append the fields type to the argument type list + # + lappend typlist $typname + incr idx + } + + # + # Prepare the plan + # + set GD($plankey) [spi_prepare $query $typlist] + + # + # Lookup and remember the table name for later error messages + # + spi_exec "select relname from pg_catalog.pg_class \\ + where oid = ''$TG_relid''::oid" + set GD($planrel) $relname + } + + # + # Build the argument list from the NEW row + # + incr keyidx -1 + set arglist "" + foreach arg [lrange $args 0 $keyidx] { + lappend arglist $NEW($arg) + } + + # + # Check for the primary key + # + set n [spi_execp -count 1 $GD($plankey) $arglist] + if {$n <= 0} { + elog ERROR "key for $GD($planrel) not in $keyrel" + } + + # + # Anything is fine + # + return OK +' language 'pltcl'; + + +create trigger dta1_before before insert or update on T_dta1 + for each row execute procedure + check_primkey('ref1', 'ref2', 'T_pkey1', 'key1', 'key2'); + + +create trigger dta2_before before insert or update on T_dta2 + for each row execute procedure + check_primkey('ref1', 'ref2', 'T_pkey2', 'key1', 'key2'); + + +create function tcl_int4add(int4,int4) returns int4 as ' + return [expr $1 + $2] +' language 'pltcl'; + +-- We use split(n) as a quick-and-dirty way of parsing the input array +-- value, which comes in as a string like '{1,2}'. There are better ways... + +create function tcl_int4_accum(int4[], int4) returns int4[] as ' + set state [split $1 "{,}"] + set newsum [expr {[lindex $state 1] + $2}] + set newcnt [expr {[lindex $state 2] + 1}] + return "{$newsum,$newcnt}" +' language 'pltcl'; + +create function tcl_int4_avg(int4[]) returns int4 as ' + set state [split $1 "{,}"] + if {[lindex $state 2] == 0} { return_null } + return [expr {[lindex $state 1] / [lindex $state 2]}] +' language 'pltcl'; + +create aggregate tcl_avg ( + sfunc = tcl_int4_accum, + basetype = int4, + stype = int4[], + finalfunc = tcl_int4_avg, + initcond = '{0,0}' + ); + +create aggregate tcl_sum ( + sfunc = tcl_int4add, + basetype = int4, + stype = int4, + initcond1 = 0 + ); + +create function tcl_int4lt(int4,int4) returns bool as ' + if {$1 < $2} { + return t + } + return f +' language 'pltcl'; + +create operator @< ( + leftarg = int4, + rightarg = int4, + procedure = tcl_int4lt + ); + diff --git a/src/port/crypt.c b/src/port/crypt.c index 2cc11a6..d3c01d2 100644 --- a/src/port/crypt.c +++ b/src/port/crypt.c @@ -1,4 +1,4 @@ -/* $NetBSD$ */ +/* $NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $ */ /* * Copyright (c) 1989, 1993 @@ -36,7 +36,7 @@ #if 0 static char sccsid[] = "@(#)crypt.c 8.1.1.1 (Berkeley) 8/18/93"; #else -__RCSID("$NetBSD$"); +__RCSID("$NetBSD: crypt.c,v 1.18 2001/03/01 14:37:35 wiz Exp $"); #endif #endif /* not lint */ diff --git a/src/port/qsort.c b/src/port/qsort.c index d3630c2..a7a6ebe 100644 --- a/src/port/qsort.c +++ b/src/port/qsort.c @@ -8,7 +8,7 @@ * $PostgreSQL$ */ -/* $NetBSD$ */ +/* $NetBSD: qsort.c,v 1.13 2003/08/07 16:43:42 agc Exp $ */ /*- * Copyright (c) 1992, 1993 diff --git a/src/test/mb/expected/utf8.out b/src/test/mb/expected/utf8.out new file mode 100644 index 0000000..8f9f63c --- /dev/null +++ b/src/test/mb/expected/utf8.out @@ -0,0 +1,87 @@ +drop table 計算機用語; +ERROR: table "計算機用語" does not exist +create table 計算機用語 (用語 text, 分類コード varchar, 備考1Aだよ char(16)); +create index 計算機用語index1 on 計算機用語 using btree (用語); +create index 計算機用語index2 on 計算機用語 using hash (分類コード); +insert into 計算機用語 values('コンピュータディスプレイ','機A01上'); +insert into 計算機用語 values('コンピュータグラフィックス','分B10中'); +insert into 計算機用語 values('コンピュータプログラマー','人Z01下'); +vacuum 計算機用語; +select * from 計算機用語; + 用語 | 分類コード | 備考1aだよ +----------------------------+------------+------------ + コンピュータディスプレイ | 機A01上 | + コンピュータグラフィックス | 分B10中 | + コンピュータプログラマー | 人Z01下 | +(3 rows) + +select * from 計算機用語 where 分類コード = '人Z01下'; + 用語 | 分類コード | 備考1aだよ +--------------------------+------------+------------ + コンピュータプログラマー | 人Z01下 | +(1 row) + +select * from 計算機用語 where 分類コード ~* '人z01下'; + 用語 | 分類コード | 備考1aだよ +--------------------------+------------+------------ + コンピュータプログラマー | 人Z01下 | +(1 row) + +select * from 計算機用語 where 分類コード like '_Z01_'; + 用語 | 分類コード | 備考1aだよ +--------------------------+------------+------------ + コンピュータプログラマー | 人Z01下 | +(1 row) + +select * from 計算機用語 where 分類コード like '_Z%'; + 用語 | 分類コード | 備考1aだよ +--------------------------+------------+------------ + コンピュータプログラマー | 人Z01下 | +(1 row) + +select * from 計算機用語 where 用語 ~ 'コンピュータ[デグ]'; + 用語 | 分類コード | 備考1aだよ +----------------------------+------------+------------ + コンピュータディスプレイ | 機A01上 | + コンピュータグラフィックス | 分B10中 | +(2 rows) + +select * from 計算機用語 where 用語 ~* 'コンピュータ[デグ]'; + 用語 | 分類コード | 備考1aだよ +----------------------------+------------+------------ + コンピュータディスプレイ | 機A01上 | + コンピュータグラフィックス | 分B10中 | +(2 rows) + +select *,character_length(用語) from 計算機用語; + 用語 | 分類コード | 備考1aだよ | character_length +----------------------------+------------+------------+------------------ + コンピュータディスプレイ | 機A01上 | | 12 + コンピュータグラフィックス | 分B10中 | | 13 + コンピュータプログラマー | 人Z01下 | | 12 +(3 rows) + +select *,octet_length(用語) from 計算機用語; + 用語 | 分類コード | 備考1aだよ | octet_length +----------------------------+------------+------------+-------------- + コンピュータディスプレイ | 機A01上 | | 36 + コンピュータグラフィックス | 分B10中 | | 39 + コンピュータプログラマー | 人Z01下 | | 36 +(3 rows) + +select *,position('デ' in 用語) from 計算機用語; + 用語 | 分類コード | 備考1aだよ | position +----------------------------+------------+------------+---------- + コンピュータディスプレイ | 機A01上 | | 7 + コンピュータグラフィックス | 分B10中 | | 0 + コンピュータプログラマー | 人Z01下 | | 0 +(3 rows) + +select *,substring(用語 from 10 for 4) from 計算機用語; + 用語 | 分類コード | 備考1aだよ | substring +----------------------------+------------+------------+----------- + コンピュータディスプレイ | 機A01上 | | プレイ + コンピュータグラフィックス | 分B10中 | | ィックス + コンピュータプログラマー | 人Z01下 | | ラマー +(3 rows) + diff --git a/src/test/mb/sql/utf8.sql b/src/test/mb/sql/utf8.sql new file mode 100644 index 0000000..e722d3d --- /dev/null +++ b/src/test/mb/sql/utf8.sql @@ -0,0 +1,19 @@ +drop table 計算機用語; +create table 計算機用語 (用語 text, 分類コード varchar, 備考1Aだよ char(16)); +create index 計算機用語index1 on 計算機用語 using btree (用語); +create index 計算機用語index2 on 計算機用語 using hash (分類コード); +insert into 計算機用語 values('コンピュータディスプレイ','機A01上'); +insert into 計算機用語 values('コンピュータグラフィックス','分B10中'); +insert into 計算機用語 values('コンピュータプログラマー','人Z01下'); +vacuum 計算機用語; +select * from 計算機用語; +select * from 計算機用語 where 分類コード = '人Z01下'; +select * from 計算機用語 where 分類コード ~* '人z01下'; +select * from 計算機用語 where 分類コード like '_Z01_'; +select * from 計算機用語 where 分類コード like '_Z%'; +select * from 計算機用語 where 用語 ~ 'コンピュータ[デグ]'; +select * from 計算機用語 where 用語 ~* 'コンピュータ[デグ]'; +select *,character_length(用語) from 計算機用語; +select *,octet_length(用語) from 計算機用語; +select *,position('デ' in 用語) from 計算機用語; +select *,substring(用語 from 10 for 4) from 計算機用語; diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out new file mode 100644 index 0000000..2c31e58 --- /dev/null +++ b/src/test/regress/expected/dependency.out @@ -0,0 +1,43 @@ +-- +-- DEPENDENCIES +-- +CREATE USER regression_user; +CREATE USER regression_user2; +CREATE USER regression_user3; +CREATE GROUP regression_group; +CREATE TABLE deptest (f1 serial primary key, f2 text); +NOTICE: CREATE TABLE will create implicit sequence "deptest_f1_seq" for serial column "deptest.f1" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "deptest_pkey" for table "deptest" +GRANT SELECT ON TABLE deptest TO GROUP regression_group; +GRANT ALL ON TABLE deptest TO regression_user, regression_user2; +-- can't drop neither because they have privileges somewhere +DROP USER regression_user; +ERROR: role "regression_user" cannot be dropped because some objects depend on it +DETAIL: access to table deptest +DROP GROUP regression_group; +ERROR: role "regression_group" cannot be dropped because some objects depend on it +DETAIL: access to table deptest +-- if we revoke the privileges we can drop the group +REVOKE SELECT ON deptest FROM GROUP regression_group; +DROP GROUP regression_group; +-- can't drop the user if we revoke the privileges partially +REVOKE SELECT, INSERT, UPDATE, DELETE, RULE, REFERENCES ON deptest FROM regression_user; +DROP USER regression_user; +ERROR: role "regression_user" cannot be dropped because some objects depend on it +DETAIL: access to table deptest +-- now we are OK to drop him +REVOKE TRIGGER ON deptest FROM regression_user; +DROP USER regression_user; +-- we are OK too if we drop the privileges all at once +REVOKE ALL ON deptest FROM regression_user2; +DROP USER regression_user2; +-- can't drop the owner of an object +-- the error message detail here would include a pg_toast_nnn name that +-- is not constant, so suppress it +\set VERBOSITY terse +ALTER TABLE deptest OWNER TO regression_user3; +DROP USER regression_user3; +ERROR: role "regression_user3" cannot be dropped because some objects depend on it +-- if we drop the object, we can drop the user too +DROP TABLE deptest; +DROP USER regression_user3; diff --git a/src/test/regress/expected/prepared_xacts.out b/src/test/regress/expected/prepared_xacts.out new file mode 100644 index 0000000..514a706 --- /dev/null +++ b/src/test/regress/expected/prepared_xacts.out @@ -0,0 +1,213 @@ +-- +-- PREPARED TRANSACTIONS (two-phase commit) +-- +-- We can't readily test persistence of prepared xacts within the +-- regression script framework, unfortunately. Note that a crash +-- isn't really needed ... stopping and starting the postmaster would +-- be enough, but we can't even do that here. +-- create a simple table that we'll use in the tests +CREATE TABLE pxtest1 (foobar VARCHAR(10)); +INSERT INTO pxtest1 VALUES ('aaa'); +-- Test PREPARE TRANSACTION +BEGIN; +UPDATE pxtest1 SET foobar = 'bbb' WHERE foobar = 'aaa'; +SELECT * FROM pxtest1; + foobar +-------- + bbb +(1 row) + +PREPARE TRANSACTION 'foo1'; +SELECT * FROM pxtest1; + foobar +-------- + aaa +(1 row) + +-- Test pg_prepared_xacts system view +SELECT gid FROM pg_prepared_xacts; + gid +------ + foo1 +(1 row) + +-- Test ROLLBACK PREPARED +ROLLBACK PREPARED 'foo1'; +SELECT * FROM pxtest1; + foobar +-------- + aaa +(1 row) + +SELECT gid FROM pg_prepared_xacts; + gid +----- +(0 rows) + +-- Test COMMIT PREPARED +BEGIN; +INSERT INTO pxtest1 VALUES ('ddd'); +SELECT * FROM pxtest1; + foobar +-------- + aaa + ddd +(2 rows) + +PREPARE TRANSACTION 'foo2'; +SELECT * FROM pxtest1; + foobar +-------- + aaa +(1 row) + +COMMIT PREPARED 'foo2'; +SELECT * FROM pxtest1; + foobar +-------- + aaa + ddd +(2 rows) + +-- Test duplicate gids +BEGIN; +UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd'; +SELECT * FROM pxtest1; + foobar +-------- + aaa + eee +(2 rows) + +PREPARE TRANSACTION 'foo3'; +SELECT gid FROM pg_prepared_xacts; + gid +------ + foo3 +(1 row) + +BEGIN; +INSERT INTO pxtest1 VALUES ('fff'); +SELECT * FROM pxtest1; + foobar +-------- + aaa + ddd + fff +(3 rows) + +-- This should fail, because the gid foo3 is already in use +PREPARE TRANSACTION 'foo3'; +ERROR: transaction identifier "foo3" is already in use +SELECT * FROM pxtest1; + foobar +-------- + aaa + ddd +(2 rows) + +ROLLBACK PREPARED 'foo3'; +SELECT * FROM pxtest1; + foobar +-------- + aaa + ddd +(2 rows) + +-- Clean up +DROP TABLE pxtest1; +-- Test subtransactions +BEGIN; + CREATE TABLE pxtest2 (a int); + INSERT INTO pxtest2 VALUES (1); + SAVEPOINT a; + INSERT INTO pxtest2 VALUES (2); + ROLLBACK TO a; + SAVEPOINT b; + INSERT INTO pxtest2 VALUES (3); +PREPARE TRANSACTION 'regress-one'; +CREATE TABLE pxtest3(fff int); +-- Test shared invalidation +BEGIN; + DROP TABLE pxtest3; + CREATE TABLE pxtest4 (a int); + INSERT INTO pxtest4 VALUES (1); + INSERT INTO pxtest4 VALUES (2); + DECLARE foo CURSOR FOR SELECT * FROM pxtest4; + -- Fetch 1 tuple, keeping the cursor open + FETCH 1 FROM foo; + a +--- + 1 +(1 row) + +PREPARE TRANSACTION 'regress-two'; +-- No such cursor +FETCH 1 FROM foo; +ERROR: cursor "foo" does not exist +-- Table doesn't exist, the creation hasn't been committed yet +SELECT * FROM pxtest2; +ERROR: relation "pxtest2" does not exist +-- There should be two prepared transactions +SELECT gid FROM pg_prepared_xacts; + gid +------------- + regress-one + regress-two +(2 rows) + +-- pxtest3 should be locked because of the pending DROP +set statement_timeout to 1000; +SELECT * FROM pxtest3; +ERROR: canceling statement due to statement timeout +reset statement_timeout; +-- Disconnect, we will continue testing in a different backend +\c - +-- There should still be two prepared transactions +SELECT gid FROM pg_prepared_xacts; + gid +------------- + regress-one + regress-two +(2 rows) + +-- pxtest3 should still be locked because of the pending DROP +set statement_timeout to 1000; +SELECT * FROM pxtest3; +ERROR: canceling statement due to statement timeout +reset statement_timeout; +-- Commit table creation +COMMIT PREPARED 'regress-one'; +\d pxtest2 + Table "public.pxtest2" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + +SELECT * FROM pxtest2; + a +--- + 1 + 3 +(2 rows) + +-- There should be one prepared transaction +SELECT gid FROM pg_prepared_xacts; + gid +------------- + regress-two +(1 row) + +-- Commit table drop +COMMIT PREPARED 'regress-two'; +SELECT * FROM pxtest3; +ERROR: relation "pxtest3" does not exist +-- There should be no prepared transactions +SELECT gid FROM pg_prepared_xacts; + gid +----- +(0 rows) + +-- Clean up +DROP TABLE pxtest2; +DROP TABLE pxtest4; diff --git a/src/test/regress/sql/dependency.sql b/src/test/regress/sql/dependency.sql new file mode 100644 index 0000000..3e4a232 --- /dev/null +++ b/src/test/regress/sql/dependency.sql @@ -0,0 +1,44 @@ +-- +-- DEPENDENCIES +-- + +CREATE USER regression_user; +CREATE USER regression_user2; +CREATE USER regression_user3; +CREATE GROUP regression_group; + +CREATE TABLE deptest (f1 serial primary key, f2 text); + +GRANT SELECT ON TABLE deptest TO GROUP regression_group; +GRANT ALL ON TABLE deptest TO regression_user, regression_user2; + +-- can't drop neither because they have privileges somewhere +DROP USER regression_user; +DROP GROUP regression_group; + +-- if we revoke the privileges we can drop the group +REVOKE SELECT ON deptest FROM GROUP regression_group; +DROP GROUP regression_group; + +-- can't drop the user if we revoke the privileges partially +REVOKE SELECT, INSERT, UPDATE, DELETE, RULE, REFERENCES ON deptest FROM regression_user; +DROP USER regression_user; + +-- now we are OK to drop him +REVOKE TRIGGER ON deptest FROM regression_user; +DROP USER regression_user; + +-- we are OK too if we drop the privileges all at once +REVOKE ALL ON deptest FROM regression_user2; +DROP USER regression_user2; + +-- can't drop the owner of an object +-- the error message detail here would include a pg_toast_nnn name that +-- is not constant, so suppress it +\set VERBOSITY terse +ALTER TABLE deptest OWNER TO regression_user3; +DROP USER regression_user3; + +-- if we drop the object, we can drop the user too +DROP TABLE deptest; +DROP USER regression_user3; diff --git a/src/test/regress/sql/prepared_xacts.sql b/src/test/regress/sql/prepared_xacts.sql new file mode 100644 index 0000000..39de88f --- /dev/null +++ b/src/test/regress/sql/prepared_xacts.sql @@ -0,0 +1,137 @@ +-- +-- PREPARED TRANSACTIONS (two-phase commit) +-- +-- We can't readily test persistence of prepared xacts within the +-- regression script framework, unfortunately. Note that a crash +-- isn't really needed ... stopping and starting the postmaster would +-- be enough, but we can't even do that here. + + +-- create a simple table that we'll use in the tests +CREATE TABLE pxtest1 (foobar VARCHAR(10)); + +INSERT INTO pxtest1 VALUES ('aaa'); + + +-- Test PREPARE TRANSACTION +BEGIN; +UPDATE pxtest1 SET foobar = 'bbb' WHERE foobar = 'aaa'; +SELECT * FROM pxtest1; +PREPARE TRANSACTION 'foo1'; + +SELECT * FROM pxtest1; + +-- Test pg_prepared_xacts system view +SELECT gid FROM pg_prepared_xacts; + +-- Test ROLLBACK PREPARED +ROLLBACK PREPARED 'foo1'; + +SELECT * FROM pxtest1; + +SELECT gid FROM pg_prepared_xacts; + + +-- Test COMMIT PREPARED +BEGIN; +INSERT INTO pxtest1 VALUES ('ddd'); +SELECT * FROM pxtest1; +PREPARE TRANSACTION 'foo2'; + +SELECT * FROM pxtest1; + +COMMIT PREPARED 'foo2'; + +SELECT * FROM pxtest1; + +-- Test duplicate gids +BEGIN; +UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd'; +SELECT * FROM pxtest1; +PREPARE TRANSACTION 'foo3'; + +SELECT gid FROM pg_prepared_xacts; + +BEGIN; +INSERT INTO pxtest1 VALUES ('fff'); +SELECT * FROM pxtest1; + +-- This should fail, because the gid foo3 is already in use +PREPARE TRANSACTION 'foo3'; + +SELECT * FROM pxtest1; + +ROLLBACK PREPARED 'foo3'; + +SELECT * FROM pxtest1; + +-- Clean up +DROP TABLE pxtest1; + +-- Test subtransactions +BEGIN; + CREATE TABLE pxtest2 (a int); + INSERT INTO pxtest2 VALUES (1); + SAVEPOINT a; + INSERT INTO pxtest2 VALUES (2); + ROLLBACK TO a; + SAVEPOINT b; + INSERT INTO pxtest2 VALUES (3); +PREPARE TRANSACTION 'regress-one'; + +CREATE TABLE pxtest3(fff int); + +-- Test shared invalidation +BEGIN; + DROP TABLE pxtest3; + CREATE TABLE pxtest4 (a int); + INSERT INTO pxtest4 VALUES (1); + INSERT INTO pxtest4 VALUES (2); + DECLARE foo CURSOR FOR SELECT * FROM pxtest4; + -- Fetch 1 tuple, keeping the cursor open + FETCH 1 FROM foo; +PREPARE TRANSACTION 'regress-two'; + +-- No such cursor +FETCH 1 FROM foo; + +-- Table doesn't exist, the creation hasn't been committed yet +SELECT * FROM pxtest2; + +-- There should be two prepared transactions +SELECT gid FROM pg_prepared_xacts; + +-- pxtest3 should be locked because of the pending DROP +set statement_timeout to 1000; +SELECT * FROM pxtest3; +reset statement_timeout; + +-- Disconnect, we will continue testing in a different backend +\c - + +-- There should still be two prepared transactions +SELECT gid FROM pg_prepared_xacts; + +-- pxtest3 should still be locked because of the pending DROP +set statement_timeout to 1000; +SELECT * FROM pxtest3; +reset statement_timeout; + +-- Commit table creation +COMMIT PREPARED 'regress-one'; +\d pxtest2 +SELECT * FROM pxtest2; + +-- There should be one prepared transaction +SELECT gid FROM pg_prepared_xacts; + +-- Commit table drop +COMMIT PREPARED 'regress-two'; +SELECT * FROM pxtest3; + +-- There should be no prepared transactions +SELECT gid FROM pg_prepared_xacts; + +-- Clean up +DROP TABLE pxtest2; +DROP TABLE pxtest4; diff --git a/src/tools/codelines b/src/tools/codelines new file mode 100755 index 0000000..c72cab4 --- /dev/null +++ b/src/tools/codelines @@ -0,0 +1,5 @@ +: + +# This script is used to compute the total number of "C" lines in the release +# This should be run from the top of the CVS tree after a 'make distclean' +find . -name '*.[chyl]' | xargs cat| wc -l diff --git a/src/tools/find_gt_lt b/src/tools/find_gt_lt new file mode 100755 index 0000000..66919dd --- /dev/null +++ b/src/tools/find_gt_lt @@ -0,0 +1,2 @@ +grep '[^]a-z0-9"/!-]>' *.sgml ref/*.sgml +grep '<[^]a-z0-9"/!-]' *.sgml ref/*.sgml diff --git a/src/tools/findoidjoins/Makefile b/src/tools/findoidjoins/Makefile new file mode 100644 index 0000000..98fd496 --- /dev/null +++ b/src/tools/findoidjoins/Makefile @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/tools/findoidjoins +# +# Copyright (c) 2003-2005, PostgreSQL Global Development Group +# +# $PostgreSQL$ +# +#------------------------------------------------------------------------- + +subdir = src/tools/findoidjoins +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +override CPPFLAGS := -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS) + +OBJS= findoidjoins.o + +all: submake-libpq submake-libpgport findoidjoins + +findoidjoins: findoidjoins.o $(libpq_builddir)/libpq.a + $(CC) $(CFLAGS) findoidjoins.o $(libpq_pgport) $(LDFLAGS) $(LIBS) -o $@$(X) + +clean distclean maintainer-clean: + rm -f findoidjoins$(X) $(OBJS) diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README new file mode 100644 index 0000000..ce31b19 --- /dev/null +++ b/src/tools/findoidjoins/README @@ -0,0 +1,119 @@ + + findoidjoins + +This program scans a database and prints oid fields (also reg* fields) +and the tables they join to. We don't really recommend running it on +anything but an empty database, such as template1; else it's likely to +be very slow. + +Run on an empty database, it returns the system join relationships (shown +below for 8.1). Note that unexpected matches may indicate bogus entries +in system tables --- don't accept a peculiar match without question. +In particular, a field shown as joining to more than one target table is +probably messed up. In 8.1, the *only* fields that should join to more +than one target are pg_description.objoid, pg_depend.objid, and +pg_depend.refobjid. (Running make_oidjoins_check is an easy way to spot +fields joining to more than one table, BTW.) + +The shell script make_oidjoins_check converts findoidjoins' output +into an SQL script that checks for dangling links (entries in an +OID or REG* column that don't match any row in the expected table). +Note that fields joining to more than one table are NOT processed. + +The result of make_oidjoins_check should be installed as the "oidjoins" +regression test. The oidjoins test should be updated after any +revision in the patterns of cross-links between system tables. +(Ideally we'd just regenerate the script as part of the regression +tests themselves, but that seems too slow...) + +NOTE: in 8.1, make_oidjoins_check produces two bogus join checks: +Join pg_catalog.pg_class.relfilenode => pg_catalog.pg_class.oid +Join pg_catalog.pg_database.datlastsysoid => pg_catalog.pg_database.oid +These are artifacts and should not be added to the oidjoins regress test. + +--------------------------------------------------------------------------- + +Join pg_catalog.pg_aggregate.aggfnoid => pg_catalog.pg_proc.oid +Join pg_catalog.pg_aggregate.aggtransfn => pg_catalog.pg_proc.oid +Join pg_catalog.pg_aggregate.aggfinalfn => pg_catalog.pg_proc.oid +Join pg_catalog.pg_aggregate.aggsortop => pg_catalog.pg_operator.oid +Join pg_catalog.pg_aggregate.aggtranstype => pg_catalog.pg_type.oid +Join pg_catalog.pg_am.aminsert => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.ambeginscan => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amgettuple => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amgetmulti => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amrescan => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amendscan => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.ammarkpos => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amrestrpos => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.ambuild => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.ambulkdelete => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amvacuumcleanup => pg_catalog.pg_proc.oid +Join pg_catalog.pg_am.amcostestimate => pg_catalog.pg_proc.oid +Join pg_catalog.pg_amop.amopclaid => pg_catalog.pg_opclass.oid +Join pg_catalog.pg_amop.amopsubtype => pg_catalog.pg_type.oid +Join pg_catalog.pg_amop.amopopr => pg_catalog.pg_operator.oid +Join pg_catalog.pg_amproc.amopclaid => pg_catalog.pg_opclass.oid +Join pg_catalog.pg_amproc.amprocsubtype => pg_catalog.pg_type.oid +Join pg_catalog.pg_amproc.amproc => pg_catalog.pg_proc.oid +Join pg_catalog.pg_attribute.attrelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_attribute.atttypid => pg_catalog.pg_type.oid +Join pg_catalog.pg_cast.castsource => pg_catalog.pg_type.oid +Join pg_catalog.pg_cast.casttarget => pg_catalog.pg_type.oid +Join pg_catalog.pg_cast.castfunc => pg_catalog.pg_proc.oid +Join pg_catalog.pg_class.relnamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_class.reltype => pg_catalog.pg_type.oid +Join pg_catalog.pg_class.relam => pg_catalog.pg_am.oid +Join pg_catalog.pg_class.reltablespace => pg_catalog.pg_tablespace.oid +Join pg_catalog.pg_class.reltoastrelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_class.reltoastidxid => pg_catalog.pg_class.oid +Join pg_catalog.pg_constraint.connamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_constraint.contypid => pg_catalog.pg_type.oid +Join pg_catalog.pg_conversion.connamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_conversion.conproc => pg_catalog.pg_proc.oid +Join pg_catalog.pg_database.dattablespace => pg_catalog.pg_tablespace.oid +Join pg_catalog.pg_depend.classid => pg_catalog.pg_class.oid +Join pg_catalog.pg_depend.refclassid => pg_catalog.pg_class.oid +Join pg_catalog.pg_description.classoid => pg_catalog.pg_class.oid +Join pg_catalog.pg_index.indexrelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_index.indrelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_language.lanvalidator => pg_catalog.pg_proc.oid +Join pg_catalog.pg_opclass.opcamid => pg_catalog.pg_am.oid +Join pg_catalog.pg_opclass.opcnamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_opclass.opcintype => pg_catalog.pg_type.oid +Join pg_catalog.pg_operator.oprnamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_operator.oprleft => pg_catalog.pg_type.oid +Join pg_catalog.pg_operator.oprright => pg_catalog.pg_type.oid +Join pg_catalog.pg_operator.oprresult => pg_catalog.pg_type.oid +Join pg_catalog.pg_operator.oprcom => pg_catalog.pg_operator.oid +Join pg_catalog.pg_operator.oprnegate => pg_catalog.pg_operator.oid +Join pg_catalog.pg_operator.oprlsortop => pg_catalog.pg_operator.oid +Join pg_catalog.pg_operator.oprrsortop => pg_catalog.pg_operator.oid +Join pg_catalog.pg_operator.oprltcmpop => pg_catalog.pg_operator.oid +Join pg_catalog.pg_operator.oprgtcmpop => pg_catalog.pg_operator.oid +Join pg_catalog.pg_operator.oprcode => pg_catalog.pg_proc.oid +Join pg_catalog.pg_operator.oprrest => pg_catalog.pg_proc.oid +Join pg_catalog.pg_operator.oprjoin => pg_catalog.pg_proc.oid +Join pg_catalog.pg_proc.pronamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_proc.prolang => pg_catalog.pg_language.oid +Join pg_catalog.pg_proc.prorettype => pg_catalog.pg_type.oid +Join pg_catalog.pg_rewrite.ev_class => pg_catalog.pg_class.oid +Join pg_catalog.pg_statistic.starelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_statistic.staop1 => pg_catalog.pg_operator.oid +Join pg_catalog.pg_statistic.staop2 => pg_catalog.pg_operator.oid +Join pg_catalog.pg_statistic.staop3 => pg_catalog.pg_operator.oid +Join pg_catalog.pg_trigger.tgrelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_trigger.tgfoid => pg_catalog.pg_proc.oid +Join pg_catalog.pg_type.typnamespace => pg_catalog.pg_namespace.oid +Join pg_catalog.pg_type.typrelid => pg_catalog.pg_class.oid +Join pg_catalog.pg_type.typelem => pg_catalog.pg_type.oid +Join pg_catalog.pg_type.typinput => pg_catalog.pg_proc.oid +Join pg_catalog.pg_type.typoutput => pg_catalog.pg_proc.oid +Join pg_catalog.pg_type.typreceive => pg_catalog.pg_proc.oid +Join pg_catalog.pg_type.typsend => pg_catalog.pg_proc.oid +Join pg_catalog.pg_type.typbasetype => pg_catalog.pg_type.oid + +--------------------------------------------------------------------------- + +Bruce Momjian (root@candle.pha.pa.us) +Updated for 7.3 by Joe Conway (mail@joeconway.com) diff --git a/src/tools/findoidjoins/findoidjoins.c b/src/tools/findoidjoins/findoidjoins.c new file mode 100644 index 0000000..3f6a768 --- /dev/null +++ b/src/tools/findoidjoins/findoidjoins.c @@ -0,0 +1,147 @@ +/* + * findoidjoins.c + * + * Copyright (c) 2002-2005, PostgreSQL Global Development Group + * + * $PostgreSQL$ + */ +#include "postgres_fe.h" + +#include "libpq-fe.h" +#include "pqexpbuffer.h" + + +int +main(int argc, char **argv) +{ + PGconn *conn; + PQExpBufferData sql; + PGresult *res; + PGresult *pkrel_res; + PGresult *fkrel_res; + char *fk_relname; + char *fk_nspname; + char *fk_attname; + char *pk_relname; + char *pk_nspname; + int fk, + pk; /* loop counters */ + + if (argc != 2) + { + fprintf(stderr, "Usage: %s database\n", argv[0]); + exit(EXIT_FAILURE); + } + + initPQExpBuffer(&sql); + + appendPQExpBuffer(&sql, "dbname=%s", argv[1]); + + conn = PQconnectdb(sql.data); + if (PQstatus(conn) == CONNECTION_BAD) + { + fprintf(stderr, "connection error: %s\n", PQerrorMessage(conn)); + exit(EXIT_FAILURE); + } + + /* Get a list of relations that have OIDs */ + + resetPQExpBuffer(&sql); + + appendPQExpBuffer(&sql, "%s", + "SET search_path = public;" + "SELECT c.relname, (SELECT nspname FROM " + "pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace) AS nspname " + "FROM pg_catalog.pg_class c " + "WHERE c.relkind = 'r' " + "AND c.relhasoids " + "ORDER BY nspname, c.relname" + ); + + res = PQexec(conn, sql.data); + if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "sql error: %s\n", PQerrorMessage(conn)); + exit(EXIT_FAILURE); + } + pkrel_res = res; + + /* Get a list of columns of OID type (or any OID-alias type) */ + + resetPQExpBuffer(&sql); + + appendPQExpBuffer(&sql, "%s", + "SELECT c.relname, " + "(SELECT nspname FROM pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace) AS nspname, " + "a.attname " + "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a " + "WHERE a.attnum > 0 AND c.relkind = 'r' " + "AND a.attrelid = c.oid " + "AND a.atttypid IN ('pg_catalog.oid'::regtype, " + " 'pg_catalog.regclass'::regtype, " + " 'pg_catalog.regoper'::regtype, " + " 'pg_catalog.regoperator'::regtype, " + " 'pg_catalog.regproc'::regtype, " + " 'pg_catalog.regprocedure'::regtype, " + " 'pg_catalog.regtype'::regtype) " + "ORDER BY nspname, c.relname, a.attnum" + ); + + res = PQexec(conn, sql.data); + if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "sql error: %s\n", PQerrorMessage(conn)); + exit(EXIT_FAILURE); + } + fkrel_res = res; + + /* + * For each column and each relation-having-OIDs, look to see if the + * column contains any values matching entries in the relation. + */ + + for (fk = 0; fk < PQntuples(fkrel_res); fk++) + { + fk_relname = PQgetvalue(fkrel_res, fk, 0); + fk_nspname = PQgetvalue(fkrel_res, fk, 1); + fk_attname = PQgetvalue(fkrel_res, fk, 2); + + for (pk = 0; pk < PQntuples(pkrel_res); pk++) + { + pk_relname = PQgetvalue(pkrel_res, pk, 0); + pk_nspname = PQgetvalue(pkrel_res, pk, 1); + + resetPQExpBuffer(&sql); + + appendPQExpBuffer(&sql, + "SELECT 1 " + "FROM \"%s\".\"%s\" t1, " + "\"%s\".\"%s\" t2 " + "WHERE t1.\"%s\"::pg_catalog.oid = t2.oid " + "LIMIT 1", + fk_nspname, fk_relname, pk_nspname, pk_relname, fk_attname); + + res = PQexec(conn, sql.data); + if (!res || PQresultStatus(res) != PGRES_TUPLES_OK) + { + fprintf(stderr, "sql error: %s\n", PQerrorMessage(conn)); + exit(EXIT_FAILURE); + } + + if (PQntuples(res) != 0) + printf("Join %s.%s.%s => %s.%s.oid\n", + fk_nspname, fk_relname, fk_attname, + pk_nspname, pk_relname); + + PQclear(res); + } + } + + PQclear(pkrel_res); + PQclear(fkrel_res); + PQfinish(conn); + + termPQExpBuffer(&sql); + + exit(EXIT_SUCCESS); +} diff --git a/src/tools/findoidjoins/make_oidjoins_check b/src/tools/findoidjoins/make_oidjoins_check new file mode 100755 index 0000000..b6a720a --- /dev/null +++ b/src/tools/findoidjoins/make_oidjoins_check @@ -0,0 +1,68 @@ +#! /bin/sh + +# You first run findoidjoins on the template1 database, and send that +# output into this script to generate a list of SQL statements. + +# NOTE: any field that findoidjoins thinks joins to more than one table +# will NOT be checked by the output of this script. You should be +# suspicious of multiple entries in findoidjoins' output. + +# Caution: you may need to use GNU awk. +AWK=${AWK:-awk} + +TMP="${TMPDIR:-/tmp}/make_oidjoins_check.$$" +trap "rm -rf $TMP" 0 1 2 3 15 + +# Create a temporary directory with the proper permissions so no one can +# intercept our temporary files and cause a security breach. +OMASK="`umask`" +umask 077 +if ! mkdir $TMP +then echo "Can't create temporary directory $TMP." 1>&2 + exit 1 +fi +umask "$OMASK" +unset OMASK + +INPUTFILE="$TMP/a" +DUPSFILE="$TMP/b" +NONDUPSFILE="$TMP/c" + +# Read input +cat "$@" >$INPUTFILE + +# Look for fields with multiple references. +cat $INPUTFILE | cut -d' ' -f2 | sort | uniq -d >$DUPSFILE +if [ -s $DUPSFILE ] ; then + echo "Ignoring these fields that link to multiple tables:" 1>&2 + cat $DUPSFILE 1>&2 +fi + +# Get the non-multiply-referenced fields. +cat $INPUTFILE | while read LINE +do + set -- $LINE + grep "^$2\$" $DUPSFILE >/dev/null 2>&1 || echo $LINE +done >$NONDUPSFILE + +# Generate the output. +cat $NONDUPSFILE | +$AWK -F'[ \.]' '\ + BEGIN \ + { + printf "\ +--\n\ +-- This is created by pgsql/contrib/findoidjoins/make_oidjoin_check\n\ +--\n"; + } + { + printf "\ +SELECT ctid, %s \n\ +FROM %s.%s fk \n\ +WHERE %s != 0 AND \n\ + NOT EXISTS(SELECT 1 FROM %s.%s pk WHERE pk.oid = fk.%s);\n", + $4, $2, $3, $4, + $6, $7, $4; + }' + +exit 0