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