root/trunk/pdns/modules/oraclebackend/oraclebackend.cc @ 10

Revision 10, 15.9 KB (checked in by ahu, 10 years ago)

oracle

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1// $Id: oraclebackend.cc,v 1.1 2002/11/27 15:35:52 ahu Exp $
2
3#include <string>
4#include <map>
5#include <unistd.h>
6#include <stdlib.h>
7#include <ctype.h>
8#include <sys/time.h>
9
10using namespace std;
11
12#include "dns.hh"
13#include "dnsbackend.hh"
14#include "ahuexception.hh"
15#include "logger.hh"
16#include "oraclebackend.hh"
17
18#include <oci.h>
19
20static const char *kDefaultQueries[kNumQueries] =
21{
22  // ForwardQuery
23  "select content, TimeToLive, Priority, type, ZoneId, nvl(ChangeDate,0) from Records where name = :name and type = :type",
24 
25  // ForwardQueryByZone
26 
27  "select content, TimeToLive, Priority, type, ZoneId, nvl(ChangeDate,0) from records where name = :name and type = :type and ZoneId = :id",
28
29  // ForwardAnyQuery
30 
31  "select content, TimeToLive, Priority, type, ZoneId, nvl(ChangeDate,0) from records where name = :name",
32
33  // ForwardWildcardQuery
34
35  "select content, TimeToLive, Priority, type, ZoneId, nvl(ChangeDate,0) from records where name like :name and type = :type",
36
37  // ForwardWildcardAnyQuery
38  "select content, TimeToLive, Priority, type, ZoneId, nvl(ChangeDate,0) from records where name like :name",
39
40  // ListQuery
41  "select content, TimeToLive, Priority, type, ZoneId, nvl(ChangeDate, 0), name from records where ZoneId = :id"
42};
43
44static const char* kModuleId = "[OracleBackend] ";
45
46OracleBackend::OracleBackend(const string &suffix)
47{
48   setArgPrefix(string("oracle")+suffix);
49   dsword err;
50
51   if (!getArg("home").empty() && setenv("ORACLE_HOME", getArg("home").c_str(), 1) == -1) {
52      throw OracleException("Cannot set ORACLE_HOME");
53   }
54
55   if (!getArg("sid").empty() && setenv("ORACLE_SID", getArg("sid").c_str(), 1) == -1) {
56      throw OracleException("Cannot set ORACLE_SID");
57   }
58
59   //
60   // Initialize everything in a known state
61   //
62
63   mEnvironmentHandle = NULL;
64   mErrorHandle = NULL;
65   mServiceContextHandle = NULL;
66   
67   for (int i = 0; i < kNumQueries; i++) {
68     mStatementHandles[i] = NULL;
69   }
70
71   // Process configuration options
72   
73   mQueries[0] = getArg("forward-query").c_str();
74   mQueries[1] = getArg("forward-query-by-zone").c_str();
75   mQueries[2] = getArg("forward-any-query").c_str();
76   mQueries[3] = getArg("forward-wildcard-query").c_str();
77   mQueries[4] = getArg("forward-wildcard-any-query").c_str();
78   mQueries[5] = getArg("list-query").c_str();
79
80   mUpperCase = mustDo("uppercase");
81   mDebugQueries = mustDo("debug-queries");
82   mTimeQueries = mustDo("time-queries");
83
84   if (mTimeQueries == true) {
85      mTimeQueriesFile = getArg("time-queries");
86      L << Logger::Error << kModuleId << "Logging SQL query statistics to: " << mTimeQueriesFile << endl;
87      mTimeQueriesStream.open(mTimeQueriesFile.c_str(), ios::out | ios::app);
88   }
89   
90   try
91   {     
92      // Initialize and create the environment
93 
94      err = OCIInitialize(OCI_THREADED, 0,  NULL, NULL, NULL);
95      if (err) {
96         throw OracleException("OCIInitialize");
97      }
98
99      err = OCIEnvInit(&mEnvironmentHandle, OCI_DEFAULT, 0, 0);
100      if (err) {
101         throw OracleException("OCIEnvInit");
102      }
103 
104      // Allocate an error handle
105     
106      err = OCIHandleAlloc(mEnvironmentHandle, (dvoid**) &mErrorHandle, OCI_HTYPE_ERROR, 0, NULL);
107      if (err) {
108         throw OracleException("OCIHandleAlloc");
109      }
110 
111      // Logon to the database
112     
113      const char *username = getArg("username").c_str();
114      const char *password = getArg("password").c_str();
115      const char *database = getArg("database").c_str();
116
117      err = OCILogon(mEnvironmentHandle, mErrorHandle, &mServiceContextHandle, (OraText*) username, strlen(username),
118                     (OraText*) password,  strlen(password), (OraText*) database, strlen(database));
119     
120      if (err) {
121         throw OracleException(mErrorHandle);
122      }
123
124      // Allocate the statement handles, and prepare the statements
125
126      for (int i = 0; i < kNumQueries; i++)
127      {
128         err = OCIHandleAlloc(mEnvironmentHandle, (dvoid **) &mStatementHandles[i], OCI_HTYPE_STMT, 0, NULL);
129         
130         if (err) {
131            throw OracleException(mErrorHandle);
132         }
133
134         err = OCIStmtPrepare(mStatementHandles[i], mErrorHandle, (text*) mQueries[i], strlen(mQueries[i]),
135           OCI_NTV_SYNTAX, OCI_DEFAULT);
136
137         if (err) {
138            throw OracleException(mErrorHandle);
139         }
140
141         // Bind query arguments
142         OCIBind *theBindHandle = NULL;
143
144         // Only the kListQuery and kForwardQueryByZone have an :id field
145
146         if (i == kListQuery || i == kForwardQueryByZone)
147         {
148            err = OCIBindByName(mStatementHandles[i], &theBindHandle, mErrorHandle, (OraText*) ":id", strlen(":id"),
149                                (ub1 *) &mQueryId, sizeof(mQueryId), SQLT_INT, NULL, NULL, 0, 0, NULL, OCI_DEFAULT);
150           
151            if (err) {
152               throw OracleException(mErrorHandle);
153            }       
154         }
155
156         // For all the other queries, except for kList Query we have more complex bindings
157
158         if (i != kListQuery)
159         {
160            // All queries have a name: field.
161
162            if (i < kListQuery)
163            {
164               err = OCIBindByName(mStatementHandles[i], &theBindHandle, mErrorHandle, (OraText*) ":name", strlen(":name"),
165                                   (ub1 *) mQueryName, sizeof(mQueryName), SQLT_STR, NULL, NULL, 0, 0, NULL, OCI_DEFAULT);
166               
167               if (err) {
168                  throw OracleException(mErrorHandle);
169               }
170            }
171           
172            // Only these queries have a type: field
173
174            if (i == kForwardQuery || i == kForwardQueryByZone || i == kForwardWildcardQuery )
175            {
176               err = OCIBindByName(mStatementHandles[i], &theBindHandle, mErrorHandle, (OraText*) ":type", strlen(":type"),
177                                   (ub1 *) mQueryType, sizeof(mQueryType), SQLT_STR, NULL, NULL, 0, 0, NULL, OCI_DEFAULT);
178               
179               if (err) {
180                  throw OracleException(mErrorHandle);
181               }           
182            }
183         }
184         
185         // Define the output
186         OCIDefine *theDefineHandle;
187
188         mResultContentIndicator = mResultTTLIndicator = mResultPriorityIndicator = mResultTypeIndicator
189           = mResultDomainIdIndicator = mResultChangeDateIndicator = 0;
190
191         theDefineHandle = NULL; 
192         err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 1, mResultContent,
193           sizeof(mResultContent) - 1, SQLT_STR, (dvoid*) &mResultContentIndicator, NULL, NULL, OCI_DEFAULT);
194         
195         if (err) {
196            throw OracleException(mErrorHandle);
197         }
198
199         theDefineHandle = NULL;
200         err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 2, &mResultTTL,
201           sizeof(mResultTTL), SQLT_INT, (dvoid*) &mResultTTLIndicator, NULL, NULL, OCI_DEFAULT);
202         
203         if (err) {
204           throw OracleException(mErrorHandle);
205         }
206         theDefineHandle = NULL;
207         err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 3, &mResultPriority,
208           sizeof(mResultPriority), SQLT_INT, (dvoid*) &mResultPriorityIndicator, NULL, NULL, OCI_DEFAULT);
209         
210         if (err) {
211            throw OracleException(mErrorHandle);
212         }
213         
214         theDefineHandle = NULL;
215         err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 4, mResultType,
216           sizeof(mResultType) - 1, SQLT_STR, (dvoid*) &mResultTypeIndicator, NULL, NULL, OCI_DEFAULT);
217         
218         if (err) {
219            throw OracleException(mErrorHandle);
220         }
221
222         theDefineHandle = NULL;
223         err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 5, &mResultDomainId,
224           sizeof(mResultDomainId), SQLT_INT, (dvoid*) &mResultDomainIdIndicator, NULL, NULL, OCI_DEFAULT);
225         
226         if (err) {
227            throw OracleException(mErrorHandle);
228         }
229         
230         theDefineHandle = NULL;
231         err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 6, &mResultChangeDate,
232           sizeof(mResultChangeDate), SQLT_INT, (dvoid*) &mResultChangeDateIndicator, NULL, NULL, OCI_DEFAULT);
233         
234         if (err) {
235            throw OracleException(mErrorHandle);
236         }
237
238         if (i == kListQuery)
239         {
240            theDefineHandle = NULL; 
241            err = OCIDefineByPos(mStatementHandles[i], &theDefineHandle, mErrorHandle, 7, mResultName,
242              sizeof(mResultName) - 1, SQLT_STR, (dvoid*) &mResultNameIndicator, NULL, NULL, OCI_DEFAULT);
243           
244            if (err) {
245               throw OracleException(mErrorHandle);
246            }
247         }
248      }     
249
250   }
251   
252   catch (OracleException &theException)
253   {
254     L << Logger::Error << kModuleId << "Connection to database failed: " << theException.Reason() << endl;
255     //     Cleanup();
256     throw AhuException("Unable to create a connection: " + theException.Reason());
257   }
258   
259   L << Logger::Warning << kModuleId << "Oracle Backend up and running" << endl;
260}
261
262OracleBackend::~OracleBackend()
263{
264  L << Logger::Warning << kModuleId << "Destructing Oracle Backend" << endl;
265  Cleanup();
266}
267
268void OracleBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int zoneId)
269{
270   //
271   // Choose the right query
272   //
273
274   int theQueryType = -1;
275
276   if (qname[0] == '%') {
277     if (qtype.getCode() == 255) {
278       theQueryType = kForwardWildcardAnyQuery;
279     } else {
280       theQueryType = kForwardWildcardQuery;
281     }
282   } else {
283     if (qtype.getCode() == 255) {
284       theQueryType = kForwardAnyQuery;
285     } else {
286       if (zoneId != -1) {
287         theQueryType = kForwardQueryByZone;
288       } else {
289         theQueryType = kForwardQuery;
290       }
291     }
292   }
293
294
295   // Fill in the correct query parameters
296   
297   if (mDebugQueries) {
298      printf(">>> executing query: %s\n", mQueries[theQueryType]);
299   }
300
301   switch (theQueryType)
302   {
303      case kForwardQuery:
304      case kForwardWildcardQuery:
305         strncpy(mQueryName, qname.c_str(), sizeof(mQueryName));
306         strncpy(mQueryType, qtype.getName().c_str(), sizeof(mQueryType));
307         if (mDebugQueries) {
308            printf(">>> :name = '%s' :type = '%s'\n", mQueryName, mQueryType);
309         }
310         break;
311
312      case kForwardQueryByZone:
313         strncpy(mQueryName, qname.c_str(), sizeof(mQueryName));
314         strncpy(mQueryType, qtype.getName().c_str(), sizeof(mQueryType));       
315         mQueryId = zoneId;
316         if (mDebugQueries) {
317            printf(">>> :name = '%s' :type = '%s' :id = '%d'\n", mQueryName, mQueryType, mQueryId);
318         }
319         break;
320
321      case kForwardAnyQuery:
322      case kForwardWildcardAnyQuery:
323         strncpy(mQueryName, qname.c_str(), sizeof(mQueryName));
324         if (mDebugQueries) {
325            printf(">>> :name = '%s'\n", mQueryName);
326         }
327         break;
328
329   }
330
331   if(mUpperCase == true) {
332      char *p = mQueryName;
333      while (*p != 0x00) {
334         *p++ = std::toupper(*p);
335      }
336   }
337   
338   // Execute the query
339
340   struct timeval theStartTime;
341
342   if (mTimeQueries == true) {
343      gettimeofday(&theStartTime, NULL);
344   }
345
346   try
347   {     
348      mActiveQuery = theQueryType;
349   
350      mQueryResult = OCIStmtExecute(mServiceContextHandle, mStatementHandles[theQueryType], mErrorHandle, 1, 0,
351        (OCISnapshot *)NULL, (OCISnapshot*) NULL, OCI_DEFAULT);
352   
353      if (mQueryResult != OCI_SUCCESS && mQueryResult != OCI_SUCCESS_WITH_INFO && mQueryResult != OCI_NO_DATA) {
354         throw OracleException(mErrorHandle);
355      }
356   }
357   
358   catch (OracleException &theException)
359   {
360      L << Logger::Error << kModuleId << "Execute failed: " << theException.Reason() << endl;
361      throw AhuException("Execute failed: " + theException.Reason());
362   }
363
364   if (mTimeQueries == true)
365   {
366      struct timeval theTime;
367     
368      gettimeofday(&theTime, NULL);
369
370      double theDifference = theTime.tv_sec - theStartTime.tv_sec
371         + (theTime.tv_usec - theStartTime.tv_usec) / 1000000.0;
372
373      mTimeQueriesStream << theTime.tv_sec << "." << theTime.tv_sec << "\t" << theQueryType << "\t"
374                         << theDifference << endl;
375   }
376}
377
378bool OracleBackend::list(int domain_id)
379{
380   mQueryId = domain_id;
381
382   if (mDebugQueries) {
383      printf(">>> executing query: %s\n", mQueries[kListQuery]);
384      printf(">>> :id = '%d'\n", mQueryId);
385   }
386   
387   mActiveQuery = kListQuery;
388
389   try
390   {
391      mQueryResult = OCIStmtExecute(mServiceContextHandle, mStatementHandles[kListQuery], mErrorHandle, 1, 0,
392        (OCISnapshot *)NULL, (OCISnapshot*) NULL, OCI_DEFAULT);
393   
394      if (mQueryResult != OCI_SUCCESS && mQueryResult != OCI_SUCCESS_WITH_INFO && mQueryResult != OCI_NO_DATA) {
395         throw OracleException(mErrorHandle);
396      }
397   }
398
399   catch (OracleException &theException)
400   {
401     L << Logger::Error << kModuleId << "Execute failed: " << theException.Reason() << endl;
402     throw AhuException("Execute failed: " + theException.Reason());
403   }
404
405   return true;
406}
407
408bool OracleBackend::get(DNSResourceRecord &theRecord)
409{
410   if (mQueryResult == OCI_NO_DATA) 
411      return false;
412   
413   theRecord.content       = mResultContent;
414   theRecord.ttl           = mResultTTL;
415   theRecord.priority      = mResultPriority;
416   theRecord.qtype         = mResultType;
417   theRecord.domain_id     = mResultDomainId;
418   theRecord.last_modified = mResultChangeDate;
419
420   // use this to distinguish between select with 'name' field (list()) and one without
421
422   if (mActiveQuery != kListQuery) {
423      theRecord.qname = mQueryName;
424   } else {
425      theRecord.qname = mResultName;
426   }
427   
428   // Try to fetch the next one. We look at the result the next time we're being
429   // called.
430   
431   try
432   {
433      mQueryResult = OCIStmtFetch(mStatementHandles[mActiveQuery], mErrorHandle, 1, 0, 0);
434      if (mQueryResult != OCI_SUCCESS && mQueryResult != OCI_SUCCESS_WITH_INFO && mQueryResult != OCI_NO_DATA) {
435         new OracleException(mErrorHandle);  // ? - ahu
436      }
437    }
438   catch (OracleException &theException)
439   {
440      L << Logger::Error << kModuleId << "Fetch failed: " << theException.Reason() << endl;
441      throw AhuException("Execute failed: " + theException.Reason());
442   }
443   
444   return true;
445}
446
447void OracleBackend::Cleanup()
448{
449   sword theError;
450
451   L << Logger::Warning << kModuleId << "Cleaning up Oracle Backend" << endl;
452   
453   if (mTimeQueries == true) {
454      mTimeQueriesStream.close();
455   }
456   
457   for (int i = 0; i < kNumQueries; i++) {
458      if (mStatementHandles[i] != NULL) {
459         OCIHandleFree(mStatementHandles[i], OCI_HTYPE_STMT);
460         mStatementHandles[i] = NULL;
461      }
462   }
463
464   if (mServiceContextHandle != NULL) {
465      theError = OCILogoff(mServiceContextHandle, mErrorHandle);
466      if (theError != 0) {
467         L << Logger::Warning << kModuleId << "OCILogoff returned a error (" << theError << ")" << endl;
468      }
469   }
470
471/*
472#if DITHOEFTNIETMEERNAEENOCILOGOFF
473   if (mServiceContextHandle != NULL) {
474      OCIHandleFree(mServiceContextHandle, OCI_HTYPE_SVCCTX);
475      mServiceContextHandle = NULL;
476   }
477#endif
478*/
479   
480   if (mErrorHandle != NULL) {
481      OCIHandleFree(mErrorHandle, OCI_HTYPE_ERROR);
482      mErrorHandle = NULL;
483   }
484   
485   if (mEnvironmentHandle != NULL) {
486      OCIHandleFree(mEnvironmentHandle, OCI_HTYPE_ENV);
487      mEnvironmentHandle = NULL;
488   }
489}
490
491
492class OracleFactory : public BackendFactory
493{
494   public:
495      OracleFactory() : BackendFactory("oracle") {}
496
497      void declareArguments(const string &suffix="")
498      {
499         declare(suffix,"debug-queries","Debugging output","no");
500         declare(suffix,"time-queries","Output query timings to a file","no");
501         declare(suffix,"uppercase","Uppercase database","no");
502         declare(suffix,"database","Database to connect to","powerdns");
503         declare(suffix,"username","Username to connect as","powerdns");
504         declare(suffix,"password","Password to connect with","");
505         declare(suffix,"home","Set and override ORACLE_HOME from within PDNS","");
506         declare(suffix,"sid","Set and override ORACLE_SID from within PDNS","");
507
508         declare(suffix, "forward-query", "", kDefaultQueries[0]);
509         declare(suffix, "forward-query-by-zone", "", kDefaultQueries[1]);
510         declare(suffix, "forward-any-query", "", kDefaultQueries[2]);
511         declare(suffix, "forward-wildcard-query", "", kDefaultQueries[3]);
512         declare(suffix, "forward-wildcard-any-query", "", kDefaultQueries[4]);
513         declare(suffix, "list-query", "", kDefaultQueries[5]);
514      }
515
516      DNSBackend *make(const string &suffix="")
517      {
518        try {
519          return new OracleBackend(suffix);
520        }
521        catch(...) {}
522        return 0;
523      }
524};
525
526
527//! Magic class that is activated when the dynamic library is loaded
528class OracleLoader
529{
530   public:
531     
532      //! This reports us to the main UeberBackend class
533     
534      OracleLoader()
535      {
536        BackendMakers().report(new OracleFactory);
537        L << Logger::Warning << kModuleId << "Oracle Backend loaded." << endl;
538      }
539     
540};
541static OracleLoader loader;
Note: See TracBrowser for help on using the browser.