ignore some files
[tridge/bind9.git] / contrib / dlz / drivers / dlz_postgres_driver.c
1 /*
2  * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the
6  * above copyright notice and this permission notice appear in all
7  * copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
10  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
11  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12  * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
16  * USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
19  * conceived and contributed by Rob Butler.
20  *
21  * Permission to use, copy, modify, and distribute this software for any
22  * purpose with or without fee is hereby granted, provided that the
23  * above copyright notice and this permission notice appear in all
24  * copies.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
27  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
29  * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
30  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
31  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
32  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
33  * USE OR PERFORMANCE OF THIS SOFTWARE.
34  */
35
36 /*
37  * Copyright (C) 1999-2001  Internet Software Consortium.
38  *
39  * Permission to use, copy, modify, and distribute this software for any
40  * purpose with or without fee is hereby granted, provided that the above
41  * copyright notice and this permission notice appear in all copies.
42  *
43  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
44  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
45  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
46  * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
47  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
48  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
49  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
50  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
51  */
52
53 #ifdef DLZ_POSTGRES
54
55 #include <config.h>
56 #include <stdio.h>
57 #include <string.h>
58 #include <stdlib.h>
59
60 #include <dns/log.h>
61 #include <dns/sdlz.h>
62 #include <dns/result.h>
63
64 #include <isc/mem.h>
65 #include <isc/platform.h>
66 #include <isc/print.h>
67 #include <isc/result.h>
68 #include <isc/string.h>
69 #include <isc/util.h>
70
71 #include <named/globals.h>
72
73 #include <dlz/sdlz_helper.h>
74 #include <dlz/dlz_postgres_driver.h>
75
76 /* temporarily include time. */
77 #include <time.h>
78
79 #include <libpq-fe.h>
80
81 static dns_sdlzimplementation_t *dlz_postgres = NULL;
82
83 #define dbc_search_limit 30
84 #define ALLNODES 1
85 #define ALLOWXFR 2
86 #define AUTHORITY 3
87 #define FINDZONE 4
88 #define LOOKUP 5
89
90 /*
91  * Private methods
92  */
93
94 /* ---------------
95  * Escaping arbitrary strings to get valid SQL strings/identifiers.
96  *
97  * Replaces "\\" with "\\\\" and "'" with "''".
98  * length is the length of the buffer pointed to by
99  * from.  The buffer at to must be at least 2*length + 1 characters
100  * long.  A terminating NUL character is written.
101  *
102  * NOTICE!!!
103  * This function was borrowed directly from PostgreSQL's libpq.
104  * The function was originally called PQescapeString and renamed
105  * to postgres_makesafe to avoid a naming collision.
106  * PQescapeString is a new function made available in Postgres 7.2.
107  * For some reason the function is not properly exported on Win32
108  * builds making the function unavailable on Windows.  Also, since
109  * this function is new it would require building this driver with
110  * the libpq 7.2.  By borrowing this function the Windows problem
111  * is solved, and the dependence on libpq 7.2 is removed.  Libpq is
112  * still required of course, but an older version should work now too.
113  *
114  * The copyright statements from the original file containing this
115  * function are included below:
116  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
117  * Portions Copyright (c) 1994, Regents of the University of California
118  * ---------------
119  */
120
121 static size_t
122 postgres_makesafe(char *to, const char *from, size_t length)
123 {
124         const char *source = from;
125         char       *target = to;
126         unsigned int remaining = length;
127
128         while (remaining > 0)
129         {
130                 switch (*source)
131                 {
132                 case '\\':
133                         *target = '\\';
134                         target++;
135                         *target = '\\';
136                         /* target and remaining are updated below. */
137                         break;
138
139                 case '\'':
140                         *target = '\'';
141                         target++;
142                         *target = '\'';
143                         /* target and remaining are updated below. */
144                         break;
145
146                 default:
147                         *target = *source;
148                         /* target and remaining are updated below. */
149                 }
150                 source++;
151                 target++;
152                 remaining--;
153         }
154
155         /* Write the terminating NUL character. */
156         *target = '\0';
157
158         return target - to;
159 }
160
161 #ifdef ISC_PLATFORM_USETHREADS
162
163 /*%
164  * Properly cleans up a list of database instances.
165  * This function is only used when the driver is compiled for
166  * multithreaded operation.
167  */
168 static void
169 postgres_destroy_dblist(db_list_t *dblist)
170 {
171
172         dbinstance_t *ndbi = NULL;
173         dbinstance_t *dbi = NULL;
174
175         /* get the first DBI in the list */
176         ndbi = ISC_LIST_HEAD(*dblist);
177
178         /* loop through the list */
179         while (ndbi != NULL) {
180                 dbi = ndbi;
181                 /* get the next DBI in the list */
182                 ndbi = ISC_LIST_NEXT(dbi, link);
183                 /* release DB connection */
184                 if (dbi->dbconn != NULL)
185                         PQfinish((PGconn *) dbi->dbconn);
186                 /* release all memory that comprised a DBI */
187                 destroy_sqldbinstance(dbi);
188         }
189         /* release memory for the list structure */
190         isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
191 }
192
193 /*%
194  * Loops through the list of DB instances, attempting to lock
195  * on the mutex.  If successful, the DBI is reserved for use
196  * and the thread can perform queries against the database.
197  * If the lock fails, the next one in the list is tried.
198  * looping continues until a lock is obtained, or until
199  * the list has been searched dbc_search_limit times.
200  * This function is only used when the driver is compiled for
201  * multithreaded operation.
202  */
203
204 static dbinstance_t *
205 postgres_find_avail_conn(db_list_t *dblist)
206 {
207         dbinstance_t *dbi = NULL;
208         dbinstance_t *head;
209         int count = 0;
210
211         /* get top of list */
212         head = dbi = ISC_LIST_HEAD(*dblist);
213
214         /* loop through list */
215         while (count < dbc_search_limit) {
216                 /* try to lock on the mutex */
217                 if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
218                         return dbi; /* success, return the DBI for use. */
219
220                 /* not successful, keep trying */
221                 dbi = ISC_LIST_NEXT(dbi, link);
222
223                 /* check to see if we have gone to the top of the list. */
224                 if (dbi == NULL) {
225                         count++;
226                         dbi = head;
227                 }
228         }
229         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
230                       DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
231                       "Postgres driver unable to find available connection "
232                       "after searching %d times",
233                       count);
234         return NULL;
235 }
236
237 #endif /* ISC_PLATFORM_USETHREADS */
238
239 /*%
240  * Allocates memory for a new string, and then constructs the new
241  * string by "escaping" the input string.  The new string is
242  * safe to be used in queries.  This is necessary because we cannot
243  * be sure of what types of strings are passed to us, and we don't
244  * want special characters in the string causing problems.
245  */
246
247 static char *
248 postgres_escape_string(const char *instr) {
249
250         char *outstr;
251         unsigned int len;
252
253         if (instr == NULL)
254                 return NULL;
255
256         len = strlen(instr);
257
258         outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
259         if (outstr == NULL)
260                 return NULL;
261
262         postgres_makesafe(outstr, instr, len);
263         /* PQescapeString(outstr, instr, len); */
264
265         return outstr;
266 }
267
268 /*%
269  * This function is the real core of the driver.   Zone, record
270  * and client strings are passed in (or NULL is passed if the
271  * string is not available).  The type of query we want to run
272  * is indicated by the query flag, and the dbdata object is passed
273  * passed in to.  dbdata really holds either:
274  *              1) a list of database instances (in multithreaded mode) OR
275  *              2) a single database instance (in single threaded mode)
276  * The function will construct the query and obtain an available
277  * database instance (DBI).  It will then run the query and hopefully
278  * obtain a result set.  Postgres is nice, in that once the result
279  * set is returned, we can make the db connection available for another
280  * thread to use, while this thread continues on.  So, the DBI is made
281  * available ASAP by unlocking the instance_lock after we have cleaned
282  * it up properly.
283  */
284 static isc_result_t
285 postgres_get_resultset(const char *zone, const char *record,
286                        const char *client, unsigned int query,
287                        void *dbdata, PGresult **rs)
288 {
289         isc_result_t result;
290         dbinstance_t *dbi = NULL;
291         char *querystring = NULL;
292         unsigned int i = 0;
293         unsigned int j = 0;
294
295         /* temporarily get a unique thread # */
296         unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));
297
298         REQUIRE(*rs == NULL);
299
300 #if 0
301         /* temporary logging message */
302         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
303                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
304                       "%d Getting DBI", dlz_thread_num);
305 #endif
306
307         /* get db instance / connection */
308 #ifdef ISC_PLATFORM_USETHREADS
309
310         /* find an available DBI from the list */
311         dbi = postgres_find_avail_conn((db_list_t *) dbdata);
312
313 #else /* ISC_PLATFORM_USETHREADS */
314
315         /*
316          * only 1 DBI - no need to lock instance lock either
317          * only 1 thread in the whole process, no possible contention.
318          */
319         dbi =  (dbinstance_t *) dbdata;
320
321 #endif /* ISC_PLATFORM_USETHREADS */
322
323 #if 0
324         /* temporary logging message */
325         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
326                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
327                       "%d Got DBI - checking query", dlz_thread_num);
328 #endif
329
330         /* if DBI is null, can't do anything else */
331         if (dbi == NULL) {
332                 result = ISC_R_FAILURE;
333                 goto cleanup;
334         }
335
336         /* what type of query are we going to run? */
337         switch(query) {
338         case ALLNODES:
339                 /*
340                  * if the query was not passed in from the config file
341                  * then we can't run it.  return not_implemented, so
342                  * it's like the code for that operation was never
343                  * built into the driver.... AHHH flexibility!!!
344                  */
345                 if (dbi->allnodes_q == NULL) {
346                         result = ISC_R_NOTIMPLEMENTED;
347                         goto cleanup;
348                 }
349                 break;
350         case ALLOWXFR:
351                 /* same as comments as ALLNODES */
352                 if (dbi->allowxfr_q == NULL) {
353                         result = ISC_R_NOTIMPLEMENTED;
354                         goto cleanup;
355                 }
356                 break;
357         case AUTHORITY:
358                 /* same as comments as ALLNODES */
359                 if (dbi->authority_q == NULL) {
360                         result = ISC_R_NOTIMPLEMENTED;
361                         goto cleanup;
362                 }
363                 break;
364         case FINDZONE:
365                 /* this is required.  It's the whole point of DLZ! */
366                 if (dbi->findzone_q == NULL) {
367                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
368                                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
369                                       "No query specified for findzone.  "
370                                       "Findzone requires a query");
371                         result = ISC_R_FAILURE;
372                         goto cleanup;
373                 }
374                 break;
375         case LOOKUP:
376                 /* this is required.  It's also a major point of DLZ! */
377                 if (dbi->lookup_q == NULL) {
378                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
379                                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
380                                       "No query specified for lookup.  "
381                                       "Lookup requires a query");
382                         result = ISC_R_FAILURE;
383                         goto cleanup;
384                 }
385                 break;
386         default:
387                 /*
388                  * this should never happen.  If it does, the code is
389                  * screwed up!
390                  */
391                 UNEXPECTED_ERROR(__FILE__, __LINE__,
392                                  "Incorrect query flag passed to "
393                                  "postgres_get_resultset");
394                 result = ISC_R_UNEXPECTED;
395                 goto cleanup;
396         }
397
398 #if 0
399         /* temporary logging message */
400         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
401                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
402                       "%d checked query", dlz_thread_num);
403 #endif
404
405         /*
406          * was a zone string passed?  If so, make it safe for use in
407          * queries.
408          */
409         if (zone != NULL) {
410                 dbi->zone = postgres_escape_string(zone);
411                 if (dbi->zone == NULL) {
412                         result = ISC_R_NOMEMORY;
413                         goto cleanup;
414                 }
415         } else {        /* no string passed, set the string pointer to NULL */
416                 dbi->zone = NULL;
417         }
418
419 #if 0
420         /* temporary logging message */
421         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
422                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
423                       "%d did zone", dlz_thread_num);
424 #endif
425
426         /*
427          * was a record string passed?  If so, make it safe for use in
428          * queries.
429          */
430         if (record != NULL) {
431                 dbi->record = postgres_escape_string(record);
432                 if (dbi->record == NULL) {
433                         result = ISC_R_NOMEMORY;
434                         goto cleanup;
435                 }
436         } else {        /* no string passed, set the string pointer to NULL */
437                 dbi->record = NULL;
438         }
439
440
441 #if 0
442         /* temporary logging message */
443         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
444                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
445                       "%d did record", dlz_thread_num);
446 #endif
447
448         /*
449          * was a client string passed?  If so, make it safe for use in
450          * queries.
451          */
452         if (client != NULL) {
453                 dbi->client = postgres_escape_string(client);
454                 if (dbi->client == NULL) {
455                         result = ISC_R_NOMEMORY;
456                         goto cleanup;
457                 }
458         } else {        /* no string passed, set the string pointer to NULL */
459                 dbi->client = NULL;
460         }
461
462 #if 0
463         /* temporary logging message */
464         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
465         DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
466                       "%d did client", dlz_thread_num);
467 #endif
468
469         /*
470          * what type of query are we going to run?
471          * this time we build the actual query to run.
472          */
473         switch(query) {
474         case ALLNODES:
475                 querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
476                 break;
477         case ALLOWXFR:
478                 querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
479                 break;
480         case AUTHORITY:
481                 querystring = build_querystring(ns_g_mctx, dbi->authority_q);
482                 break;
483         case FINDZONE:
484                 querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
485                 break;
486         case LOOKUP:
487                 querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
488                 break;
489         default:
490                 /*
491                  * this should never happen.  If it does, the code is
492                  * screwed up!
493                  */
494                 UNEXPECTED_ERROR(__FILE__, __LINE__,
495                                  "Incorrect query flag passed to "
496                                  "postgres_get_resultset");
497                 result = ISC_R_UNEXPECTED;
498                 goto cleanup;
499         }
500
501 #if 0
502         /* temporary logging message */
503         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
504                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
505                       "%d built query", dlz_thread_num);
506 #endif
507
508         /* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
509         if (querystring  == NULL) {
510                 result = ISC_R_NOMEMORY;
511                 goto cleanup;
512         }
513
514 #if 0
515         /* temporary logging message */
516         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
517                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
518                       "%d query is '%s'", dlz_thread_num, querystring);
519 #endif
520
521         /*
522          * output the full query string during debug so we can see
523          * what lame error the query has.
524          */
525         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
526                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
527                       "\nQuery String: %s\n", querystring);
528
529         /* attempt query up to 3 times. */
530         for (j=0; j < 3; j++) {
531 #if 0
532                 /* temporary logging message */
533                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
534                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
535                               "%d executing query for %d time",
536                               dlz_thread_num, j);
537 #endif
538                 /* try to get result set */
539                 *rs = PQexec((PGconn *)dbi->dbconn, querystring );
540                 result = ISC_R_SUCCESS;
541                 /*
542                  * if result set is null, reset DB connection, max 3
543                  * attempts.
544                  */
545                 for (i=0; *rs == NULL && i < 3; i++) {
546 #if 0
547                         /* temporary logging message */
548                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
549                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
550                                       "%d resetting connection",
551                                       dlz_thread_num);
552 #endif
553                         result = ISC_R_FAILURE;
554                         PQreset((PGconn *) dbi->dbconn);
555                         /* connection ok, break inner loop */
556                         if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK)
557                                 break;
558                 }
559                 /* result set ok, break outter loop */
560                 if (PQresultStatus(*rs) == PGRES_TUPLES_OK) {
561 #if 0
562                         /* temporary logging message */
563                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
564                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
565                                       "%d rs ok", dlz_thread_num);
566 #endif
567                         break;
568                 } else {
569                         /* we got a result set object, but it's not right. */
570 #if 0
571                         /* temporary logging message */
572                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
573                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
574                                       "%d clearing rs", dlz_thread_num);
575 #endif
576                         PQclear(*rs);   /* get rid of it */
577                         /* in case this was the last attempt */
578                         result = ISC_R_FAILURE;
579                 }
580         }
581
582  cleanup:
583         /* it's always good to cleanup after yourself */
584
585 #if 0
586         /* temporary logging message */
587         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
588                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
589                       "%d cleaning up", dlz_thread_num);
590 #endif
591
592         /* if we couldn't even allocate DBI, just return NULL */
593         if (dbi == NULL)
594                 return ISC_R_FAILURE;
595
596         /* free dbi->zone string */
597         if (dbi->zone != NULL)
598                 isc_mem_free(ns_g_mctx, dbi->zone);
599
600         /* free dbi->record string */
601         if (dbi->record != NULL)
602                 isc_mem_free(ns_g_mctx, dbi->record);
603
604         /* free dbi->client string */
605         if (dbi->client != NULL)
606                 isc_mem_free(ns_g_mctx, dbi->client);
607
608 #ifdef ISC_PLATFORM_USETHREADS
609
610 #if 0
611         /* temporary logging message */
612         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
613                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
614                       "%d unlocking mutex", dlz_thread_num);
615 #endif
616
617         /* release the lock so another thread can use this dbi */
618         isc_mutex_unlock(&dbi->instance_lock);
619
620 #endif /* ISC_PLATFORM_USETHREADS */
621
622         /* release query string */
623         if (querystring  != NULL)
624                 isc_mem_free(ns_g_mctx, querystring );
625
626 #if 0
627         /* temporary logging message */
628         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
629                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
630                       "%d returning", dlz_thread_num);
631 #endif
632
633         /* return result */
634         return result;
635 }
636
637 /*%
638  * The processing of result sets for lookup and authority are
639  * exactly the same.  So that functionality has been moved
640  * into this function to minimize code.
641  */
642
643 static isc_result_t
644 postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs)
645 {
646         isc_result_t result;
647         unsigned int i;
648         unsigned int rows;
649         unsigned int fields;
650         unsigned int j;
651         unsigned int len;
652         char *tmpString;
653         char *endp;
654         int ttl;
655
656         rows = PQntuples(rs);   /* how many rows in result set */
657         fields = PQnfields(rs); /* how many columns in result set */
658         for (i=0; i < rows; i++) {
659                 switch(fields) {
660                 case 1:
661                         /*
662                          * one column in rs, it's the data field.  use
663                          * default type of A record, and default TTL
664                          * of 86400
665                          */
666                         result = dns_sdlz_putrr(lookup, "a", 86400,
667                                                 PQgetvalue(rs, i, 0));
668                         break;
669                 case 2:
670                         /* two columns, data field, and data type.
671                          * use default TTL of 86400.
672                          */
673                         result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0),
674                                                 86400, PQgetvalue(rs, i, 1));
675                         break;
676                 case 3:
677                         /* three columns, all data no defaults.
678                          * convert text to int, make sure it worked
679                          * right.
680                          */
681                         ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
682                         if (*endp != '\0' || ttl < 0) {
683                                 isc_log_write(dns_lctx,
684                                               DNS_LOGCATEGORY_DATABASE,
685                                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
686                                               "Postgres driver ttl must be "
687                                               "a positive number");
688                         }
689                         result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
690                                                 ttl, PQgetvalue(rs, i, 2));
691                         break;
692                 default:
693                         /*
694                          * more than 3 fields, concatenate the last
695                          * ones together.  figure out how long to make
696                          * string
697                          */
698                         for (j=2, len=0; j < fields; j++) {
699                                 len += strlen(PQgetvalue(rs, i, j)) + 1;
700                         }
701                         /*
702                          * allocate string memory, allow for NULL to
703                          * term string
704                          */
705                         tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
706                         if (tmpString == NULL) {
707                                 /* major bummer, need more ram */
708                                 isc_log_write(dns_lctx,
709                                               DNS_LOGCATEGORY_DATABASE,
710                                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
711                                               "Postgres driver unable to "
712                                               "allocate memory for "
713                                               "temporary string");
714                                 PQclear(rs);
715                                 return (ISC_R_FAILURE); /* Yeah, I'd say! */
716                         }
717                         /* copy field to tmpString */
718                         strcpy(tmpString, PQgetvalue(rs, i, 2));
719                         /*
720                          * concat the rest of fields together, space
721                          * between each one.
722                          */
723                         for (j=3; j < fields; j++) {
724                                 strcat(tmpString, " ");
725                                 strcat(tmpString, PQgetvalue(rs, i, j));
726                         }
727                         /* convert text to int, make sure it worked right */
728                         ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
729                         if (*endp != '\0' || ttl < 0) {
730                                 isc_log_write(dns_lctx,
731                                               DNS_LOGCATEGORY_DATABASE,
732                                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
733                                               "Postgres driver ttl must be "
734                                               "a postive number");
735                         }
736                         /* ok, now tell Bind about it. */
737                         result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
738                                                 ttl, tmpString);
739                         /* done, get rid of this thing. */
740                         isc_mem_free(ns_g_mctx, tmpString);
741                 }
742                 /* I sure hope we were successful */
743                 if (result != ISC_R_SUCCESS) {
744                         /* nope, get rid of the Result set, and log a msg */
745                         PQclear(rs);
746                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
747                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
748                                       "dns_sdlz_putrr returned error. "
749                                       "Error code was: %s",
750                                       isc_result_totext(result));
751                         return (ISC_R_FAILURE);
752                 }
753         }
754
755         /* free result set memory */
756         PQclear(rs);
757
758         /* if we did return results, we are successful */
759         if (rows > 0)
760                 return (ISC_R_SUCCESS);
761
762         /* empty result set, no data found */
763         return (ISC_R_NOTFOUND);
764 }
765
766 /*
767  * SDLZ interface methods
768  */
769
770 /*% determine if the zone is supported by (in) the database */
771
772 static isc_result_t
773 postgres_findzone(void *driverarg, void *dbdata, const char *name)
774 {
775         isc_result_t result;
776         PGresult *rs = NULL;
777         unsigned int rows;
778         UNUSED(driverarg);
779
780         /* run the query and get the result set from the database. */
781         result = postgres_get_resultset(name, NULL, NULL,
782                                         FINDZONE, dbdata, &rs);
783         /* if we didn't get a result set, log an err msg. */
784         if (result != ISC_R_SUCCESS) {
785                 if (rs != NULL)
786                         PQclear(rs);
787                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
788                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
789                               "Postgres driver unable to return "
790                               "result set for findzone query");
791                 return (ISC_R_FAILURE);
792         }
793         /* count how many rows in result set */
794         rows = PQntuples(rs);
795         /* get rid of result set, we are done with it. */
796         PQclear(rs);
797
798         /* if we returned any rows, zone is supported. */
799         if (rows > 0)
800                 return (ISC_R_SUCCESS);
801
802         /* no rows returned, zone is not supported. */
803         return (ISC_R_NOTFOUND);
804 }
805
806 /*% Determine if the client is allowed to perform a zone transfer */
807 static isc_result_t
808 postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name,
809                       const char *client)
810 {
811         isc_result_t result;
812         PGresult *rs = NULL;
813         unsigned int rows;
814         UNUSED(driverarg);
815
816         /* first check if the zone is supported by the database. */
817         result = postgres_findzone(driverarg, dbdata, name);
818         if (result != ISC_R_SUCCESS)
819                 return (ISC_R_NOTFOUND);
820
821         /*
822          * if we get to this point we know the zone is supported by
823          * the database the only questions now are is the zone
824          * transfer is allowed for this client and did the config file
825          * have an allow zone xfr query.
826          *
827          * Run our query, and get a result set from the database.
828          */
829         result = postgres_get_resultset(name, NULL, client,
830                                         ALLOWXFR, dbdata, &rs);
831         /* if we get "not implemented", send it along. */
832         if (result == ISC_R_NOTIMPLEMENTED)
833                 return result;
834         /* if we didn't get a result set, log an err msg. */
835         if (result != ISC_R_SUCCESS) {
836                 if (rs != NULL)
837                         PQclear(rs);
838                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
839                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
840                               "Postgres driver unable to return "
841                               "result set for allow xfr query");
842                 return (ISC_R_FAILURE);
843         }
844         /* count how many rows in result set */
845         rows = PQntuples(rs);
846         /* get rid of result set, we are done with it. */
847         PQclear(rs);
848
849         /* if we returned any rows, zone xfr is allowed. */
850         if (rows > 0)
851                 return (ISC_R_SUCCESS);
852
853         /* no rows returned, zone xfr not allowed */
854         return (ISC_R_NOPERM);
855 }
856
857 /*%
858  * If the client is allowed to perform a zone transfer, the next order of
859  * business is to get all the nodes in the zone, so bind can respond to the
860  * query.
861  */
862 static isc_result_t
863 postgres_allnodes(const char *zone, void *driverarg, void *dbdata,
864                   dns_sdlzallnodes_t *allnodes)
865 {
866         isc_result_t result;
867         PGresult *rs = NULL;
868         unsigned int i;
869         unsigned int rows;
870         unsigned int fields;
871         unsigned int j;
872         unsigned int len;
873         char *tmpString;
874         char *endp;
875         int ttl;
876
877         UNUSED(driverarg);
878
879         /* run the query and get the result set from the database. */
880         result = postgres_get_resultset(zone, NULL, NULL,
881                                         ALLNODES, dbdata, &rs);
882         /* if we get "not implemented", send it along */
883         if (result == ISC_R_NOTIMPLEMENTED)
884                 return result;
885         /* if we didn't get a result set, log an err msg. */
886         if (result != ISC_R_SUCCESS) {
887                 if (rs != NULL)
888                         PQclear(rs);
889                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
890                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
891                               "Postgres driver unable to return "
892                               "result set for all nodes query");
893                 return (ISC_R_FAILURE);
894         }
895
896         rows = PQntuples(rs);   /* how many rows in result set */
897         fields = PQnfields(rs); /* how many columns in result set */
898         for (i=0; i < rows; i++) {
899                 if (fields < 4) {       /* gotta have at least 4 columns */
900                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
901                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
902                                       "Postgres driver too few fields "
903                                       "returned by all nodes query");
904                 }
905                 /* convert text to int, make sure it worked right  */
906                 ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
907                 if (*endp != '\0' || ttl < 0) {
908                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
909                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
910                                       "Postgres driver ttl must be "
911                                       "a postive number");
912                 }
913                 if (fields == 4) {
914                         /* tell Bind about it. */
915                         result = dns_sdlz_putnamedrr(allnodes,
916                                                      PQgetvalue(rs, i, 2),
917                                                      PQgetvalue(rs, i, 1),
918                                                      ttl,
919                                                      PQgetvalue(rs, i, 3));
920                 } else {
921                         /*
922                          * more than 4 fields, concatonat the last
923                          * ones together.  figure out how long to make
924                          * string
925                          */
926                         for (j=3, len=0; j < fields; j++) {
927                                 len += strlen(PQgetvalue(rs, i, j)) + 1;
928                         }
929                         /* allocate memory, allow for NULL to term string */
930                         tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
931                         if (tmpString == NULL) {        /* we need more ram. */
932                                 isc_log_write(dns_lctx,
933                                               DNS_LOGCATEGORY_DATABASE,
934                                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
935                                               "Postgres driver unable to "
936                                               "allocate memory for "
937                                               "temporary string");
938                                 PQclear(rs);
939                                 return (ISC_R_FAILURE);
940                         }
941                         /* copy this field to tmpString */
942                         strcpy(tmpString, PQgetvalue(rs, i, 3));
943                         /* concatonate the rest, with spaces between */
944                         for (j=4; j < fields; j++) {
945                                 strcat(tmpString, " ");
946                                 strcat(tmpString, PQgetvalue(rs, i, j));
947                         }
948                         /* tell Bind about it. */
949                         result = dns_sdlz_putnamedrr(allnodes,
950                                                      PQgetvalue(rs, i, 2),
951                                                      PQgetvalue(rs, i, 1),
952                                                      ttl, tmpString);
953                         isc_mem_free(ns_g_mctx, tmpString);
954                 }
955                 /* if we weren't successful, log err msg */
956                 if (result != ISC_R_SUCCESS) {
957                         PQclear(rs);
958                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
959                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
960                                       "dns_sdlz_putnamedrr returned error. "
961                                       "Error code was: %s",
962                                       isc_result_totext(result));
963                         return (ISC_R_FAILURE);
964                 }
965         }
966
967         /* free result set memory */
968         PQclear(rs);
969
970         /* if we did return results, we are successful */
971         if (rows > 0)
972                 return (ISC_R_SUCCESS);
973
974         /* empty result set, no data found */
975         return (ISC_R_NOTFOUND);
976 }
977
978 /*%
979  * if the lookup function does not return SOA or NS records for the zone,
980  * use this function to get that information for Bind.
981  */
982
983 static isc_result_t
984 postgres_authority(const char *zone, void *driverarg, void *dbdata,
985                    dns_sdlzlookup_t *lookup)
986 {
987         isc_result_t result;
988         PGresult *rs = NULL;
989
990         UNUSED(driverarg);
991
992         /* run the query and get the result set from the database. */
993         result = postgres_get_resultset(zone, NULL, NULL,
994                                         AUTHORITY, dbdata, &rs);
995         /* if we get "not implemented", send it along */
996         if (result == ISC_R_NOTIMPLEMENTED)
997                 return result;
998         /* if we didn't get a result set, log an err msg. */
999         if (result != ISC_R_SUCCESS) {
1000                 if (rs != NULL)
1001                         PQclear(rs);
1002                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1003                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1004                               "Postgres driver unable to return "
1005                               "result set for authority query");
1006                 return (ISC_R_FAILURE);
1007         }
1008         /*
1009          * lookup and authority result sets are processed in the same
1010          * manner postgres_process_rs does the job for both
1011          * functions.
1012          */
1013         return postgres_process_rs(lookup, rs);
1014 }
1015
1016 /*% if zone is supported, lookup up a (or multiple) record(s) in it */
1017 static isc_result_t
1018 postgres_lookup(const char *zone, const char *name, void *driverarg,
1019                 void *dbdata, dns_sdlzlookup_t *lookup)
1020 {
1021         isc_result_t result;
1022         PGresult *rs = NULL;
1023
1024         UNUSED(driverarg);
1025
1026         /* run the query and get the result set from the database. */
1027         result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
1028         /* if we didn't get a result set, log an err msg. */
1029         if (result != ISC_R_SUCCESS) {
1030                 if (rs != NULL)
1031                         PQclear(rs);
1032                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1033                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1034                               "Postgres driver unable to "
1035                               "return result set for lookup query");
1036                 return (ISC_R_FAILURE);
1037         }
1038         /*
1039          * lookup and authority result sets are processed in the same
1040          * manner postgres_process_rs does the job for both functions.
1041          */
1042         return postgres_process_rs(lookup, rs);
1043 }
1044
1045 /*%
1046  * create an instance of the driver.  Remember, only 1 copy of the driver's
1047  * code is ever loaded, the driver has to remember which context it's
1048  * operating in.  This is done via use of the dbdata argument which is
1049  * passed into all query functions.
1050  */
1051 static isc_result_t
1052 postgres_create(const char *dlzname, unsigned int argc, char *argv[],
1053                 void *driverarg, void **dbdata)
1054 {
1055         isc_result_t result;
1056         dbinstance_t *dbi = NULL;
1057         unsigned int j;
1058
1059 #ifdef ISC_PLATFORM_USETHREADS
1060         /* if multi-threaded, we need a few extra variables. */
1061         int dbcount;
1062         db_list_t *dblist = NULL;
1063         int i;
1064         char *endp;
1065
1066 #endif /* ISC_PLATFORM_USETHREADS */
1067
1068         UNUSED(driverarg);
1069         UNUSED(dlzname);
1070
1071 /* seed random # generator */
1072         srand( (unsigned)time( NULL ) );
1073
1074
1075 #ifdef ISC_PLATFORM_USETHREADS
1076         /* if debugging, let user know we are multithreaded. */
1077         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1078                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1079                       "Postgres driver running multithreaded");
1080 #else /* ISC_PLATFORM_USETHREADS */
1081         /* if debugging, let user know we are single threaded. */
1082         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1083                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
1084                       "Postgres driver running single threaded");
1085 #endif /* ISC_PLATFORM_USETHREADS */
1086
1087         /* verify we have at least 5 arg's passed to the driver */
1088         if (argc < 5) {
1089                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1090                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1091                               "Postgres driver requires at least "
1092                               "4 command line args.");
1093                 return (ISC_R_FAILURE);
1094         }
1095
1096         /* no more than 8 arg's should be passed to the driver */
1097         if (argc > 8) {
1098                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1099                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1100                               "Postgres driver cannot accept more than "
1101                               "7 command line args.");
1102                 return (ISC_R_FAILURE);
1103         }
1104
1105         /* multithreaded build can have multiple DB connections */
1106 #ifdef ISC_PLATFORM_USETHREADS
1107
1108         /* check how many db connections we should create */
1109         dbcount = strtol(argv[1], &endp, 10);
1110         if (*endp != '\0' || dbcount < 0) {
1111                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1112                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1113                               "Postgres driver database connection count "
1114                               "must be positive.");
1115                 return (ISC_R_FAILURE);
1116         }
1117
1118         /* allocate memory for database connection list */
1119         dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
1120         if (dblist == NULL)
1121                 return (ISC_R_NOMEMORY);
1122
1123         /* initialize DB connection list */
1124         ISC_LIST_INIT(*dblist);
1125
1126         /*
1127          * create the appropriate number of database instances (DBI)
1128          * append each new DBI to the end of the list
1129          */
1130         for (i=0; i < dbcount; i++) {
1131
1132 #endif /* ISC_PLATFORM_USETHREADS */
1133
1134                 /* how many queries were passed in from config file? */
1135                 switch(argc) {
1136                 case 5:
1137                         result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1138                                                      NULL, argv[3], argv[4],
1139                                                      NULL, &dbi);
1140                         break;
1141                 case 6:
1142                         result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
1143                                                      argv[5], argv[3], argv[4],
1144                                                      NULL, &dbi);
1145                         break;
1146                 case 7:
1147                         result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
1148                                                      argv[5], argv[3], argv[4],
1149                                                      NULL, &dbi);
1150                         break;
1151                 case 8:
1152                         result = build_sqldbinstance(ns_g_mctx, argv[6],
1153                                                      argv[7], argv[5], argv[3],
1154                                                      argv[4], NULL, &dbi);
1155                         break;
1156                 default:
1157                         /* not really needed, should shut up compiler. */
1158                         result = ISC_R_FAILURE;
1159                 }
1160
1161
1162                 if (result == ISC_R_SUCCESS) {
1163                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1164                                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1165                                       "Postgres driver created database "
1166                                       "instance object.");
1167                 } else { /* unsuccessful?, log err msg and cleanup. */
1168                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1169                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1170                                       "Postgres driver could not create "
1171                                       "database instance object.");
1172                         goto cleanup;
1173                 }
1174
1175 #ifdef ISC_PLATFORM_USETHREADS
1176
1177                 /* when multithreaded, build a list of DBI's */
1178                 ISC_LINK_INIT(dbi, link);
1179                 ISC_LIST_APPEND(*dblist, dbi, link);
1180
1181 #endif
1182
1183                 /* create and set db connection */
1184                 dbi->dbconn = PQconnectdb(argv[2]);
1185                 /*
1186                  * if db connection cannot be created, log err msg and
1187                  * cleanup.
1188                  */
1189                 if (dbi->dbconn == NULL) {
1190                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1191                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1192                                       "Postgres driver could not allocate "
1193                                       "memory for database connection");
1194                         goto cleanup;
1195                 }
1196
1197                 /* if we cannot connect the first time, try 3 more times. */
1198                 for (j = 0;
1199                      PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK &&
1200                              j < 3;
1201                      j++)
1202                         PQreset((PGconn *) dbi->dbconn);
1203
1204
1205 #ifdef ISC_PLATFORM_USETHREADS
1206
1207                 /*
1208                  * if multi threaded, let user know which connection
1209                  * failed.  user could be attempting to create 10 db
1210                  * connections and for some reason the db backend only
1211                  * allows 9
1212                  */
1213                 if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
1214                         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1215                                       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1216                                       "Postgres driver failed to create "
1217                                       "database connection number %u "
1218                                       "after 4 attempts",
1219                                       i + 1);
1220                         goto cleanup;
1221                 }
1222
1223                 /* set DBI = null for next loop through. */
1224                 dbi = NULL;
1225         }       /* end for loop */
1226
1227                 /* set dbdata to the list we created. */
1228         *dbdata = dblist;
1229
1230 #else /* ISC_PLATFORM_USETHREADS */
1231         /* if single threaded, just let user know we couldn't connect. */
1232         if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
1233                 isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1234                               DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
1235                               "Postgres driver failed to create database "
1236                               "connection after 4 attempts");
1237                 goto cleanup;
1238         }
1239
1240         /*
1241          * single threaded build can only use 1 db connection, return
1242          * it via dbdata
1243          */
1244         *dbdata = dbi;
1245
1246 #endif /* ISC_PLATFORM_USETHREADS */
1247
1248         /* hey, we got through all of that ok, return success. */
1249         return(ISC_R_SUCCESS);
1250
1251  cleanup:
1252
1253 #ifdef ISC_PLATFORM_USETHREADS
1254         /*
1255          * if multithreaded, we could fail because only 1 connection
1256          * couldn't be made.  We should cleanup the other successful
1257          * connections properly.
1258          */
1259         postgres_destroy_dblist(dblist);
1260
1261 #else /* ISC_PLATFORM_USETHREADS */
1262         if (dbi != NULL)
1263                 destroy_sqldbinstance(dbi);
1264
1265 #endif /* ISC_PLATFORM_USETHREADS */
1266         return(ISC_R_FAILURE);
1267 }
1268
1269 /*%
1270  * destroy an instance of the driver.  Remember, only 1 copy of the driver's
1271  * code is ever loaded, the driver has to remember which context it's
1272  * operating in.  This is done via use of the dbdata argument.
1273  * so we really only need to clean it up since we are not using driverarg.
1274  */
1275 static void
1276 postgres_destroy(void *driverarg, void *dbdata)
1277 {
1278
1279 #ifdef ISC_PLATFORM_USETHREADS
1280
1281         UNUSED(driverarg);
1282         /* cleanup the list of DBI's */
1283         postgres_destroy_dblist((db_list_t *) dbdata);
1284
1285 #else /* ISC_PLATFORM_USETHREADS */
1286
1287         dbinstance_t *dbi;
1288
1289         UNUSED(driverarg);
1290
1291         dbi = (dbinstance_t *) dbdata;
1292
1293         /* release DB connection */
1294         if (dbi->dbconn != NULL)
1295                 PQfinish((PGconn *) dbi->dbconn);
1296
1297         /* destroy single DB instance */
1298         destroy_sqldbinstance(dbi);
1299
1300 #endif /* ISC_PLATFORM_USETHREADS */
1301 }
1302
1303 /* pointers to all our runtime methods. */
1304 /* this is used during driver registration */
1305 /* i.e. in dlz_postgres_init below. */
1306 static dns_sdlzmethods_t dlz_postgres_methods = {
1307         postgres_create,
1308         postgres_destroy,
1309         postgres_findzone,
1310         postgres_lookup,
1311         postgres_authority,
1312         postgres_allnodes,
1313         postgres_allowzonexfr
1314 };
1315
1316 /*%
1317  * Wrapper around dns_sdlzregister().
1318  */
1319 isc_result_t
1320 dlz_postgres_init(void) {
1321         isc_result_t result;
1322
1323         /*
1324          * Write debugging message to log
1325          */
1326         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1327                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1328                       "Registering DLZ postgres driver.");
1329
1330         /*
1331          * Driver is always threadsafe.  When multithreaded all
1332          * functions use multithreaded code.  When not multithreaded,
1333          * all functions can only be entered once, but only 1 thread
1334          * of operation is available in Bind.  So everything is still
1335          * threadsafe.
1336          */
1337         result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL,
1338                                   DNS_SDLZFLAG_RELATIVEOWNER |
1339                                   DNS_SDLZFLAG_RELATIVERDATA |
1340                                   DNS_SDLZFLAG_THREADSAFE,
1341                                   ns_g_mctx, &dlz_postgres);
1342         /* if we can't register the driver, there are big problems. */
1343         if (result != ISC_R_SUCCESS) {
1344                 UNEXPECTED_ERROR(__FILE__, __LINE__,
1345                                  "dns_sdlzregister() failed: %s",
1346                                  isc_result_totext(result));
1347                 result = ISC_R_UNEXPECTED;
1348         }
1349
1350
1351         return result;
1352 }
1353
1354 /*%
1355  * Wrapper around dns_sdlzunregister().
1356  */
1357 void
1358 dlz_postgres_clear(void) {
1359
1360         /*
1361          * Write debugging message to log
1362          */
1363         isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
1364                       DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
1365                       "Unregistering DLZ postgres driver.");
1366
1367         /* unregister the driver. */
1368         if (dlz_postgres != NULL)
1369                 dns_sdlzunregister(&dlz_postgres);
1370 }
1371
1372 #endif