]> the.earth.li Git - onak.git/blob - keydb/keydb_pg.c
0.6.3 release
[onak.git] / keydb / keydb_pg.c
1 /*
2  * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
3  *
4  * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <postgresql/libpq-fe.h>
20 #include <postgresql/libpq/libpq-fs.h>
21
22 #include <sys/types.h>
23 #include <sys/uio.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "build-config.h"
32 #include "hash.h"
33 #include "keydb.h"
34 #include "keyid.h"
35 #include "decodekey.h"
36 #include "keystructs.h"
37 #include "log.h"
38 #include "mem.h"
39 #include "onak-conf.h"
40 #include "parsekey.h"
41
42 struct pg_fc_ctx {
43         PGconn *dbconn;
44         int fd;
45 };
46
47 /**
48  *      keydb_fetchchar - Fetches a char from a file.
49  */
50 static size_t keydb_fetchchar(void *_ctx, size_t count, void *c)
51 {
52         int ret;
53
54         struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
55
56         ret = lo_read(ctx->dbconn, ctx->fd, (char *) c, count);
57
58         return (ret > 0) ? ret : 0;
59 }
60
61 /**
62  *      keydb_putchar - Puts a char to a file.
63  */
64 static size_t keydb_putchar(void *_ctx, size_t count, void *c)
65 {
66         struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
67         int ret;
68
69         ret = lo_write(ctx->dbconn, ctx->fd, (char *) c, count);
70
71         return (ret > 0) ? ret : 0;
72 }
73
74 /**
75  *      starttrans - Start a transaction.
76  *
77  *      Start a transaction. Intended to be used if we're about to perform many
78  *      operations on the database to help speed it all up, or if we want
79  *      something to only succeed if all relevant operations are successful.
80  */
81 static bool pg_starttrans(struct onak_dbctx *dbctx)
82 {
83         PGconn *dbconn = (PGconn *) dbctx->priv;
84         PGresult *result = NULL;
85         
86         result = PQexec(dbconn, "BEGIN");
87         PQclear(result);
88
89         return true;
90 }
91
92 /**
93  *      endtrans - End a transaction.
94  *
95  *      Ends a transaction.
96  */
97 static void pg_endtrans(struct onak_dbctx *dbctx)
98 {
99         PGconn *dbconn = (PGconn *) dbctx->priv;
100         PGresult *result = NULL;
101
102         result = PQexec(dbconn, "COMMIT");
103         PQclear(result);
104
105         return;
106 }
107
108 /**
109  *      fetch_key_id - Given a keyid fetch the key from storage.
110  *      @keyid: The keyid to fetch.
111  *      @publickey: A pointer to a structure to return the key in.
112  *      @intrans: If we're already in a transaction.
113  *
114  *      We use the hex representation of the keyid as the filename to fetch the
115  *      key from. The key is stored in the file as a binary OpenPGP stream of
116  *      packets, so we can just use read_openpgp_stream() to read the packets
117  *      in and then parse_keys() to parse the packets into a publickey
118  *      structure.
119  */
120 static int pg_fetch_key_id(struct onak_dbctx *dbctx,
121                 uint64_t keyid,
122                 struct openpgp_publickey **publickey,
123                 bool intrans)
124 {
125         struct openpgp_packet_list *packets = NULL;
126         PGconn *dbconn = (PGconn *) dbctx->priv;
127         PGresult *result = NULL;
128         char *oids = NULL;
129         char statement[1024];
130         int i = 0;
131         int numkeys = 0;
132         Oid key_oid;
133         struct pg_fc_ctx fcctx;
134
135         if (!intrans) {
136                 result = PQexec(dbconn, "BEGIN");
137                 PQclear(result);
138         }
139         
140         if (keyid > 0xFFFFFFFF) {
141                 snprintf(statement, 1023,
142                         "SELECT keydata FROM onak_keys WHERE keyid = '%"
143                         PRIX64 "'",
144                         keyid);
145         } else {
146                 snprintf(statement, 1023,
147                         "SELECT keydata FROM onak_keys WHERE keyid "
148                         "LIKE '%%%" PRIX64 "'",
149                         keyid);
150         }
151         result = PQexec(dbconn, statement);
152
153         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
154                 numkeys = PQntuples(result);
155                 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
156                         oids = PQgetvalue(result, i, 0);
157                         key_oid = (Oid) atoi(oids);
158
159                         fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
160                         if (fcctx.fd < 0) {
161                                 logthing(LOGTHING_ERROR,
162                                                 "Can't open large object.");
163                         } else {
164                                 fcctx.dbconn = dbconn;
165                                 read_openpgp_stream(keydb_fetchchar, &fcctx,
166                                                 &packets, 0);
167                                 parse_keys(packets, publickey);
168                                 lo_close(dbconn, fcctx.fd);
169                                 free_packet_list(packets);
170                                 packets = NULL;
171                         }
172                 }
173         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
174                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
175         }
176
177         PQclear(result);
178
179         if (!intrans) {
180                 result = PQexec(dbconn, "COMMIT");
181                 PQclear(result);
182         }
183         return (numkeys);
184 }
185
186 /**
187  *      fetch_key_text - Trys to find the keys that contain the supplied text.
188  *      @search: The text to search for.
189  *      @publickey: A pointer to a structure to return the key in.
190  *
191  *      This function searches for the supplied text and returns the keys that
192  *      contain it.
193  */
194 static int pg_fetch_key_text(struct onak_dbctx *dbctx,
195                 const char *search,
196                 struct openpgp_publickey **publickey)
197 {
198         struct openpgp_packet_list *packets = NULL;
199         PGconn *dbconn = (PGconn *) dbctx->priv;
200         PGresult *result = NULL;
201         char *oids = NULL;
202         char statement[1024];
203         int i = 0;
204         int numkeys = 0;
205         Oid key_oid;
206         char *newsearch = NULL;
207         struct pg_fc_ctx fcctx;
208
209         result = PQexec(dbconn, "BEGIN");
210         PQclear(result);
211
212         newsearch = malloc(strlen(search) * 2 + 1);
213         memset(newsearch, 0, strlen(search) * 2 + 1);
214         PQescapeStringConn(dbconn, newsearch, search, strlen(search), NULL);
215         snprintf(statement, 1023,
216                         "SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
217                         "onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
218                         "AND onak_uids.uid LIKE '%%%s%%'",
219                         newsearch);
220         result = PQexec(dbconn, statement);
221         free(newsearch);
222         newsearch = NULL;
223
224         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
225                 numkeys = PQntuples(result);
226                 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
227                         oids = PQgetvalue(result, i, 0);
228                         key_oid = (Oid) atoi(oids);
229
230                         fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
231                         if (fcctx.fd < 0) {
232                                 logthing(LOGTHING_ERROR,
233                                                 "Can't open large object.");
234                         } else {
235                                 fcctx.dbconn = dbconn;
236                                 read_openpgp_stream(keydb_fetchchar, &fcctx,
237                                                 &packets,
238                                                 0);
239                                 parse_keys(packets, publickey);
240                                 lo_close(dbconn, fcctx.fd);
241                                 free_packet_list(packets);
242                                 packets = NULL;
243                         }
244                 }
245         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
246                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
247         }
248
249         PQclear(result);
250
251         result = PQexec(dbconn, "COMMIT");
252         PQclear(result);
253         return (numkeys);
254 }
255
256 /**
257  *      delete_key - Given a keyid delete the key from storage.
258  *      @fp: The fingerprint of the key to delete.
259  *      @intrans: If we're already in a transaction.
260  *
261  *      This function deletes a public key from whatever storage mechanism we
262  *      are using. Returns 0 if the key existed.
263  */
264 static int pg_delete_key(struct onak_dbctx *dbctx,
265                 struct openpgp_fingerprint *fp, bool intrans)
266 {
267         PGconn *dbconn = (PGconn *) dbctx->priv;
268         PGresult *result = NULL;
269         char *oids = NULL;
270         char statement[1024];
271         int found = 1;
272         int i;
273         Oid key_oid;
274         uint64_t keyid;
275
276         if (!intrans) {
277                 result = PQexec(dbconn, "BEGIN");
278                 PQclear(result);
279         }
280
281         keyid = fingerprint2keyid(fp);
282         
283         snprintf(statement, 1023,
284                         "SELECT keydata FROM onak_keys WHERE keyid = '%"
285                         PRIX64 "'",
286                         keyid);
287         result = PQexec(dbconn, statement);
288
289         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
290                 found = 0;
291                 i = PQntuples(result);
292                 while (i > 0) {
293                         oids = PQgetvalue(result, i-1, 0);
294                         key_oid = (Oid) atoi(oids);
295                         lo_unlink(dbconn, key_oid);
296                         i--;
297                 }
298                 PQclear(result);
299
300                 snprintf(statement, 1023,
301                         "DELETE FROM onak_sigs WHERE signee = '%" PRIX64 "'",
302                         keyid);
303                 result = PQexec(dbconn, statement);
304                 PQclear(result);
305
306                 snprintf(statement, 1023,
307                         "DELETE FROM onak_uids WHERE keyid = '%" PRIX64 "'",
308                         keyid);
309                 result = PQexec(dbconn, statement);
310                 PQclear(result);
311
312                 snprintf(statement, 1023,
313                         "DELETE FROM onak_keys WHERE keyid = '%" PRIX64 "'",
314                         keyid);
315                 result = PQexec(dbconn, statement);
316         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
317                 logthing(LOGTHING_ERROR,
318                                 "Problem retrieving key (%" PRIX64
319                                 ") from DB.",
320                                 keyid);
321         }
322
323         PQclear(result);
324
325         if (!intrans) {
326                 result = PQexec(dbconn, "COMMIT");
327                 PQclear(result);
328         }
329         return (found);
330 }
331
332 /**
333  *      store_key - Takes a key and stores it.
334  *      @publickey: A pointer to the public key to store.
335  *      @intrans: If we're already in a transaction.
336  *      @update: If true the key exists and should be updated.
337  *
338  *      Again we just use the hex representation of the keyid as the filename
339  *      to store the key to. We flatten the public key to a list of OpenPGP
340  *      packets and then use write_openpgp_stream() to write the stream out to
341  *      the file. If update is true then we delete the old key first, otherwise
342  *      we trust that it doesn't exist.
343  */
344 static int pg_store_key(struct onak_dbctx *dbctx,
345                 struct openpgp_publickey *publickey, bool intrans,
346                 bool update)
347 {
348         struct openpgp_packet_list *packets = NULL;
349         struct openpgp_packet_list *list_end = NULL;
350         struct openpgp_publickey *next = NULL;
351         struct openpgp_signedpacket_list *curuid = NULL;
352         PGconn *dbconn = (PGconn *) dbctx->priv;
353         PGresult *result = NULL;
354         char statement[1024];
355         Oid key_oid;
356         char **uids = NULL;
357         char *primary = NULL;
358         char *safeuid = NULL;
359         int i;
360         uint64_t keyid;
361         struct pg_fc_ctx fcctx;
362         struct openpgp_fingerprint fp;
363
364         if (!intrans) {
365                 result = PQexec(dbconn, "BEGIN");
366                 PQclear(result);
367         }
368
369         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
370                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
371                 return 0;
372         }
373
374         /*
375          * Delete the key if we already have it.
376          *
377          * TODO: Can we optimize this perhaps? Possibly when other data is
378          * involved as well? I suspect this is easiest and doesn't make a lot
379          * of difference though - the largest chunk of data is the keydata and
380          * it definitely needs updated.
381          */
382         if (update) {
383                 get_fingerprint(publickey->publickey, &fp);
384                 pg_delete_key(dbctx, &fp, true);
385         }
386
387         next = publickey->next;
388         publickey->next = NULL;
389         flatten_publickey(publickey, &packets, &list_end);
390         publickey->next = next;
391                 
392         key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
393         if (key_oid == 0) {
394                 logthing(LOGTHING_ERROR, "Can't create key OID");
395         } else {
396                 fcctx.fd = lo_open(dbconn, key_oid, INV_WRITE);
397                 fcctx.dbconn = dbconn;
398                 write_openpgp_stream(keydb_putchar, &fcctx, packets);
399                 lo_close(dbconn, fcctx.fd);
400         }
401         free_packet_list(packets);
402         packets = NULL;
403
404         snprintf(statement, 1023, 
405                         "INSERT INTO onak_keys (keyid, keydata) VALUES "
406                         "('%" PRIX64 "', '%d')", 
407                         keyid,
408                         key_oid);
409         result = PQexec(dbconn, statement);
410
411         if (PQresultStatus(result) != PGRES_COMMAND_OK) {
412                 logthing(LOGTHING_ERROR, "Problem storing key in DB.");
413                 logthing(LOGTHING_ERROR, "%s", PQresultErrorMessage(result));
414         }
415         PQclear(result);
416
417         uids = keyuids(publickey, &primary);
418         if (uids != NULL) {
419                 for (i = 0; uids[i] != NULL; i++) {
420                         safeuid = malloc(strlen(uids[i]) * 2 + 1);
421                         if (safeuid != NULL) {
422                                 memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
423                                 PQescapeStringConn(dbconn, safeuid, uids[i],
424                                                 strlen(uids[i]), NULL);
425
426                                 snprintf(statement, 1023,
427                                         "INSERT INTO onak_uids "
428                                         "(keyid, uid, pri) "
429                                         "VALUES ('%" PRIX64 "', '%s', '%c')",
430                                         keyid,
431                                         safeuid,
432                                         (uids[i] == primary) ? 't' : 'f');
433                                 result = PQexec(dbconn, statement);
434
435                                 free(safeuid);
436                                 safeuid = NULL;
437                         }
438                         if (uids[i] != NULL) {
439                                 free(uids[i]);
440                                 uids[i] = NULL;
441                         }
442
443                         if (PQresultStatus(result) != PGRES_COMMAND_OK) {
444                                 logthing(LOGTHING_ERROR,
445                                                 "Problem storing key in DB.");
446                                 logthing(LOGTHING_ERROR, "%s",
447                                                 PQresultErrorMessage(result));
448                         }
449                         /*
450                          * TODO: Check result.
451                          */
452                         PQclear(result);
453                 }
454                 free(uids);
455                 uids = NULL;
456         }
457
458         for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
459                 for (packets = curuid->sigs; packets != NULL; 
460                                 packets = packets->next) {
461                         snprintf(statement, 1023,
462                                 "INSERT INTO onak_sigs (signer, signee) "
463                                 "VALUES ('%" PRIX64 "', '%" PRIX64 "')",
464                                 sig_keyid(packets->packet),
465                                 keyid);
466                         result = PQexec(dbconn, statement);
467                         PQclear(result);
468                 }
469         }
470
471         if (!intrans) {
472                 result = PQexec(dbconn, "COMMIT");
473                 PQclear(result);
474         }
475         
476         return 0;
477 }
478
479 /**
480  *      keyid2uid - Takes a keyid and returns the primary UID for it.
481  *      @keyid: The keyid to lookup.
482  */
483 static char *pg_keyid2uid(struct onak_dbctx *dbctx, uint64_t keyid)
484 {
485         PGconn *dbconn = (PGconn *) dbctx->priv;
486         PGresult *result = NULL;
487         char statement[1024];
488         char *uid = NULL;
489
490         snprintf(statement, 1023,
491                 "SELECT uid FROM onak_uids WHERE keyid = '%" PRIX64
492                 "' AND pri = 't'",
493                 keyid);
494         result = PQexec(dbconn, statement);
495
496         /*
497          * Technically we only expect one response to the query; a key only has
498          * one primary ID. Better to return something than nothing though.
499          *
500          * TODO: Log if we get more than one response? Needs logging framework
501          * first though.
502          */
503         if (PQresultStatus(result) == PGRES_TUPLES_OK &&
504                         PQntuples(result) >= 1) {
505                 uid = strdup(PQgetvalue(result, 0, 0));
506         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
507                 logthing(LOGTHING_ERROR,
508                                 "Problem retrieving key (%" PRIX64
509                                 ") from DB.",
510                                 keyid);
511         }
512
513         PQclear(result);
514
515         return uid;
516 }
517
518 /**
519  *      getkeysigs - Gets a linked list of the signatures on a key.
520  *      @keyid: The keyid to get the sigs for.
521  *      @revoked: If the key is revoked.
522  *
523  *      This function gets the list of signatures on a key. Used for key 
524  *      indexing and doing stats bits.
525  */
526 static struct ll *pg_getkeysigs(struct onak_dbctx *dbctx,
527                         uint64_t keyid, bool *revoked)
528 {
529         struct ll *sigs = NULL;
530         PGconn *dbconn = (PGconn *) dbctx->priv;
531         PGresult *result = NULL;
532         uint64_t signer;
533         char statement[1024];
534         int i, j;
535         int numsigs = 0;
536         bool intrans = false;
537         char *str;
538
539         if (!intrans) {
540                 result = PQexec(dbconn, "BEGIN");
541                 PQclear(result);
542         }
543
544         snprintf(statement, 1023,
545                 "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%"
546                 PRIX64 "'",
547                 keyid);
548         result = PQexec(dbconn, statement);
549
550         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
551                 numsigs = PQntuples(result);
552                 for (i = 0; i < numsigs;  i++) {
553                         j = 0;
554                         signer = 0;
555                         str = PQgetvalue(result, i, 0);
556                         while (str[j] != 0) {
557                                 signer <<= 4;
558                                 if (str[j] >= '0' && str[j] <= '9') {
559                                         signer += str[j] - '0';
560                                 } else {
561                                         signer += str[j] - 'A' + 10;
562                                 }
563                                 j++;
564                         }
565                         sigs = lladd(sigs, createandaddtohash(signer));
566                 }
567         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
568                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
569         }
570
571         PQclear(result);
572
573         if (!intrans) {
574                 result = PQexec(dbconn, "COMMIT");
575                 PQclear(result);
576         }
577
578         /*
579          * TODO: What do we do about revocations? We don't have the details
580          * stored in a separate table, so we'd have to grab the key and decode
581          * it, which we're trying to avoid by having a signers table.
582          */
583         if (revoked != NULL) {
584                 *revoked = false;
585         }
586         
587         return sigs;
588 }
589
590 /**
591  *      iterate_keys - call a function once for each key in the db.
592  *      @iterfunc: The function to call.
593  *      @ctx: A context pointer
594  *
595  *      Calls iterfunc once for each key in the database. ctx is passed
596  *      unaltered to iterfunc. This function is intended to aid database dumps
597  *      and statistic calculations.
598  *
599  *      Returns the number of keys we iterated over.
600  */
601 static int pg_iterate_keys(struct onak_dbctx *dbctx,
602                 void (*iterfunc)(void *ctx,
603                 struct openpgp_publickey *key), void *ctx)
604 {
605         struct openpgp_packet_list *packets = NULL;
606         struct openpgp_publickey *key = NULL;
607         PGconn *dbconn = (PGconn *) dbctx->priv;
608         PGresult *result = NULL;
609         char *oids = NULL;
610         int i = 0;
611         int numkeys = 0;
612         Oid key_oid;
613         struct pg_fc_ctx fcctx;
614
615         result = PQexec(dbconn, "SELECT keydata FROM onak_keys;");
616
617         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
618                 numkeys = PQntuples(result);
619                 for (i = 0; i < numkeys; i++) {
620                         oids = PQgetvalue(result, i, 0);
621                         key_oid = (Oid) atoi(oids);
622
623                         fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
624                         if (fcctx.fd < 0) {
625                                 logthing(LOGTHING_ERROR,
626                                                 "Can't open large object.");
627                         } else {
628                                 fcctx.dbconn = dbconn;
629                                 read_openpgp_stream(keydb_fetchchar, &fcctx,
630                                                 &packets, 0);
631                                 parse_keys(packets, &key);
632                                 lo_close(dbconn, fcctx.fd);
633
634                                 iterfunc(ctx, key);
635                                         
636                                 free_publickey(key);
637                                 key = NULL;
638                                 free_packet_list(packets);
639                                 packets = NULL;
640                         }
641                 }
642         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
643                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
644         }
645
646         PQclear(result);
647
648         return (numkeys);
649 }
650
651 /*
652  * Include the basic keydb routines.
653  */
654 #define NEED_UPDATEKEYS 1
655 #define NEED_GET 1
656 #define NEED_GET_FP 1
657 #include "keydb.c"
658
659 /**
660  *      cleanupdb - De-initialize the key database.
661  *
662  *      This function should be called upon program exit to allow the DB to
663  *      cleanup after itself.
664  */
665 static void pg_cleanupdb(struct onak_dbctx *dbctx)
666 {
667         PGconn *dbconn = (PGconn *) dbctx->priv;
668
669         PQfinish(dbconn);
670         dbconn = NULL;
671
672         free(dbctx);
673 }
674
675 /**
676  *      initdb - Initialize the key database.
677  *
678  *      This function should be called before any of the other functions in
679  *      this file are called in order to allow the DB to be initialized ready
680  *      for access.
681  */
682 struct onak_dbctx *keydb_pg_init(struct onak_db_config *dbcfg,
683                 __unused bool readonly)
684 {
685         struct onak_dbctx *dbctx;
686         PGconn *dbconn;
687
688         dbctx = malloc(sizeof(struct onak_dbctx));
689         if (dbctx == NULL) {
690                 return NULL;
691         }
692         dbctx->config = dbcfg;
693
694         dbconn = PQsetdbLogin(dbcfg->hostname, // host
695                         NULL, // port
696                         NULL, // options
697                         NULL, // tty
698                         dbcfg->location, // database
699                         dbcfg->username,  //login
700                         dbcfg->password); // password
701
702         if (PQstatus(dbconn) == CONNECTION_BAD) {
703                 logthing(LOGTHING_CRITICAL, "Connection to database failed.");
704                 logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
705                 PQfinish(dbconn);
706                 dbconn = NULL;
707                 exit(1);
708         }
709
710         dbctx->priv = dbconn;
711
712         dbctx->cleanupdb                = pg_cleanupdb;
713         dbctx->starttrans               = pg_starttrans;
714         dbctx->endtrans                 = pg_endtrans;
715         dbctx->fetch_key                = generic_fetch_key;
716         dbctx->fetch_key_fp             = generic_fetch_key_fp;
717         dbctx->fetch_key_id             = pg_fetch_key_id;
718         dbctx->fetch_key_text           = pg_fetch_key_text;
719         dbctx->store_key                = pg_store_key;
720         dbctx->update_keys              = generic_update_keys;
721         dbctx->delete_key               = pg_delete_key;
722         dbctx->getkeysigs               = pg_getkeysigs;
723         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
724         dbctx->keyid2uid                = pg_keyid2uid;
725         dbctx->iterate_keys             = pg_iterate_keys;
726
727         return dbctx;
728 }