]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Switch gsql* and mydns backends to prepared statements
authorAki Tuomi <cmouse@cmouse.fi>
Sun, 5 Oct 2014 15:14:16 +0000 (18:14 +0300)
committerPeter van Dijk <peter.van.dijk@netherlabs.nl>
Mon, 12 Jan 2015 09:33:13 +0000 (10:33 +0100)
Closes #1783 which is the PR this code came from.

28 files changed:
m4/pdns_with_oracle.m4
modules/bindbackend/bindbackend2.cc
modules/bindbackend/bindbackend2.hh
modules/bindbackend/binddnssec.cc
modules/gmysqlbackend/gmysqlbackend.cc
modules/gmysqlbackend/smysql.cc
modules/gmysqlbackend/smysql.hh
modules/goraclebackend/Makefile.am
modules/goraclebackend/goraclebackend.cc
modules/goraclebackend/goraclebackend.hh
modules/goraclebackend/soracle.cc
modules/goraclebackend/soracle.hh
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gpgsqlbackend/spgsql.cc
modules/gpgsqlbackend/spgsql.hh
modules/gsqlite3backend/gsqlite3backend.cc
modules/gsqlite3backend/gsqlite3backend.hh
modules/mydnsbackend/mydnsbackend.cc
modules/mydnsbackend/mydnsbackend.hh
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/backends/gsql/ssql.hh
pdns/pdnssec.cc
pdns/ssqlite3.cc
pdns/ssqlite3.hh
regression-tests/backends/goracle-master
regression-tests/backends/goracle-slave
regression-tests/backends/gpgsql-master

index 08d73540dec63b61c43e5260adbf1ff69fac51d2..d8428f9cbf90b38d9f30d978e99d718ffc576047 100644 (file)
@@ -50,9 +50,9 @@ AC_DEFUN([PDNS_WITH_ORACLE],[
   # we have to check for client9 as well...
   # test -lclntsh
   old_LDFLAGS="$LDFLAGS"
-  LDFLAGS="-L$with_oracle_libs -lnnz11 -locci"
+  LDFLAGS="-L$with_oracle_libs -locci"
   AC_CHECK_LIB([clntsh],[OCIEnvInit],
-    [ORACLE_LIBS="-L$with_oracle_libs -lnnz11 -lclntsh -locci"],
+    [ORACLE_LIBS="-L$with_oracle_libs -lclntsh -locci"],
     AC_CHECK_LIB([client9], [OCIEnvInit],
       [ORACLE_LIBS="-L$with_oracle_libs -lclient9 -lclntsh9"],
       [AC_MSG_ERROR([Could not find client libraries])]
index 3e737b67495cfc9d968d3df9a1018bdc7849def2..cbef60a160d0201f5a3c6880d8ee5b622e7775ff 100644 (file)
@@ -1,4 +1,4 @@
-/*
+  /*
     PowerDNS Versatile Database Driven Nameserver
     Copyright (C) 2002 - 2014  PowerDNS.COM BV
 
@@ -587,6 +587,20 @@ string Bind2Backend::DLAddDomainHandler(const vector<string>&parts, Utility::pid
 
 Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
 {
+  d_getAllDomainMetadataQuery_stmt = NULL;
+  d_getDomainMetadataQuery_stmt = NULL;
+  d_deleteDomainMetadataQuery_stmt = NULL;
+  d_insertDomainMetadataQuery_stmt = NULL;
+  d_getDomainKeysQuery_stmt = NULL;
+  d_deleteDomainKeyQuery_stmt = NULL;
+  d_insertDomainKeyQuery_stmt = NULL;
+  d_activateDomainKeyQuery_stmt = NULL;
+  d_deactivateDomainKeyQuery_stmt = NULL;
+  d_getTSIGKeyQuery_stmt = NULL;
+  d_setTSIGKeyQuery_stmt = NULL;
+  d_deleteTSIGKeyQuery_stmt = NULL;
+  d_getTSIGKeysQuery_stmt = NULL;
+
   setArgPrefix("bind"+suffix);
   d_logprefix="[bind"+suffix+"backend]";
   d_hybrid=mustDo("hybrid");
@@ -616,7 +630,7 @@ Bind2Backend::Bind2Backend(const string &suffix, bool loadZones)
 }
 
 Bind2Backend::~Bind2Backend()
-{}
+{ freeStatements(); } // deallocate statements 
 
 void Bind2Backend::rediscover(string *status)
 {
index 9ced301b998430477af2b3a7d0b29834dda5866b..2bbc0420948d9b519a0acb0b18f33d97fdca0d10 100644 (file)
@@ -39,8 +39,9 @@
 #include "pdns/lock.hh"
 #include "pdns/misc.hh"
 #include "pdns/dnsbackend.hh"
-
 #include "pdns/namespaces.hh"
+#include "pdns/backends/gsql/ssql.hh"
+
 using namespace ::boost::multi_index;
 
 /** This struct is used within the Bind2Backend to store DNS information. 
@@ -237,6 +238,9 @@ public:
 
 private:
   void setupDNSSEC();
+  void setupStatements();
+  void freeStatements();
+  void release(SSqlStatement**);
   static bool safeGetBBDomainInfo(int id, BB2DomainInfo* bbd);
   static void safePutBBDomainInfo(const BB2DomainInfo& bbd);
   static bool safeGetBBDomainInfo(const std::string& name, BB2DomainInfo* bbd);
@@ -301,4 +305,18 @@ private:
   void doEmptyNonTerminals(BB2DomainInfo& bbd, bool nsec3zone, NSEC3PARAMRecordContent ns3pr);
   void loadConfig(string *status=0);
   static void nukeZoneRecords(BB2DomainInfo *bbd);
+
+  SSqlStatement* d_getAllDomainMetadataQuery_stmt;
+  SSqlStatement* d_getDomainMetadataQuery_stmt;
+  SSqlStatement* d_deleteDomainMetadataQuery_stmt;
+  SSqlStatement* d_insertDomainMetadataQuery_stmt;
+  SSqlStatement* d_getDomainKeysQuery_stmt;
+  SSqlStatement* d_deleteDomainKeyQuery_stmt;
+  SSqlStatement* d_insertDomainKeyQuery_stmt;
+  SSqlStatement* d_activateDomainKeyQuery_stmt;
+  SSqlStatement* d_deactivateDomainKeyQuery_stmt;
+  SSqlStatement* d_getTSIGKeyQuery_stmt;
+  SSqlStatement* d_setTSIGKeyQuery_stmt;
+  SSqlStatement* d_deleteTSIGKeyQuery_stmt;
+  SSqlStatement* d_getTSIGKeysQuery_stmt;
 };
index f1806025849f50700e1e5ba18114534932c07ef9..c5c63700c71e9fdf6078126f5b6879f7a7df89f5 100644 (file)
@@ -74,6 +74,11 @@ bool Bind2Backend::deleteTSIGKey(const string& name)
 
 bool Bind2Backend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
 { return false; }
+void Bind2Backend::setupStatements() 
+{ return; }
+void Bind2Backend::freeStatements()
+{ return; }
+
 #else
 
 #include "pdns/ssqlite3.hh"
@@ -84,6 +89,7 @@ void Bind2Backend::setupDNSSEC()
     return;
   try {
     d_dnssecdb = shared_ptr<SSQLite3>(new SSQLite3(getArg("dnssec-db")));
+    setupStatements();
   }
   catch(SSqlException& se) {
     // this error is meant to kill the server dead - it makes no sense to continue..
@@ -93,6 +99,44 @@ void Bind2Backend::setupDNSSEC()
   d_dnssecdb->setLog(::arg().mustDo("query-logging"));
 }
 
+void Bind2Backend::setupStatements() 
+{
+  d_getAllDomainMetadataQuery_stmt = d_dnssecdb->prepare("select kind, content from domainmetadata where domain=:domain",1);
+  d_getDomainMetadataQuery_stmt = d_dnssecdb->prepare("select content from domainmetadata where domain=:domain and kind=:kind",2);
+  d_deleteDomainMetadataQuery_stmt = d_dnssecdb->prepare("delete from domainmetadata where domain=:domain and kind=:kind",2);
+  d_insertDomainMetadataQuery_stmt = d_dnssecdb->prepare("insert into domainmetadata (domain, kind, content) values (:domain,:kind,:content)",3);
+  d_getDomainKeysQuery_stmt = d_dnssecdb->prepare("select id,flags, active, content from cryptokeys where domain=:domain",1);
+  d_deleteDomainKeyQuery_stmt = d_dnssecdb->prepare("delete from cryptokeys where domain=:domain and id=:key_id",2);
+  d_insertDomainKeyQuery_stmt = d_dnssecdb->prepare("insert into cryptokeys (domain, flags, active, content) values (:domain, :flags, :active, :content)", 4);
+  d_activateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=1 where domain=:domain and id=:key_id", 2);
+  d_deactivateDomainKeyQuery_stmt = d_dnssecdb->prepare("update cryptokeys set active=0 where domain=:domain and id=:key_id", 2);
+  d_getTSIGKeyQuery_stmt = d_dnssecdb->prepare("select algorithm, secret from tsigkeys where name=:key_name", 1);
+  d_setTSIGKeyQuery_stmt = d_dnssecdb->prepare("replace into tsigkeys (name,algorithm,secret) values(:key_name, :algorithm, :content)", 3);
+  d_deleteTSIGKeyQuery_stmt = d_dnssecdb->prepare("delete from tsigkeys where name=:key_name", 1);
+  d_getTSIGKeysQuery_stmt = d_dnssecdb->prepare("select name,algorithm,secret from tsigkeys", 0);
+}
+
+void Bind2Backend::release(SSqlStatement** stmt) {
+  delete *stmt;
+  *stmt = NULL;
+}
+
+void Bind2Backend::freeStatements() 
+{
+    release(&d_getAllDomainMetadataQuery_stmt);
+    release(&d_getDomainMetadataQuery_stmt);
+    release(&d_deleteDomainMetadataQuery_stmt);
+    release(&d_insertDomainMetadataQuery_stmt);
+    release(&d_getDomainKeysQuery_stmt);
+    release(&d_deleteDomainKeyQuery_stmt);
+    release(&d_insertDomainKeyQuery_stmt);
+    release(&d_activateDomainKeyQuery_stmt);
+    release(&d_deactivateDomainKeyQuery_stmt);
+    release(&d_getTSIGKeyQuery_stmt);
+    release(&d_setTSIGKeyQuery_stmt);
+    release(&d_deleteTSIGKeyQuery_stmt);
+    release(&d_getTSIGKeysQuery_stmt);
+}
 bool Bind2Backend::doesDNSSEC()
 {
   return d_dnssecdb || d_hybrid;
@@ -128,14 +172,18 @@ bool Bind2Backend::getAllDomainMetadata(const string& name, std::map<std::string
 
   // cerr<<"Asked to get metadata for zone '"<<name<<"'|"<<kind<<"\n";
 
-  boost::format fmt("select kind, content from domainmetadata where domain='%s'");
   try {
-    d_dnssecdb->doQuery((fmt % d_dnssecdb->escape(name)).str());
+    d_getAllDomainMetadataQuery_stmt->
+      bind("domain", name)->
+      execute();
 
-    vector<string> row;
-    while(d_dnssecdb->getRow(row)) {
+    SSqlStatement::row_t row;
+    while(d_getAllDomainMetadataQuery_stmt->hasNextRow()) {
+      d_getAllDomainMetadataQuery_stmt->nextRow(row);
       meta[row[0]].push_back(row[1]);
     }
+
+    d_getAllDomainMetadataQuery_stmt->reset();
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
@@ -150,14 +198,19 @@ bool Bind2Backend::getDomainMetadata(const string& name, const std::string& kind
     
   // cerr<<"Asked to get metadata for zone '"<<name<<"'|"<<kind<<"\n";
   
-  boost::format fmt("select content from domainmetadata where domain='%s' and kind='%s'");
   try {
-    d_dnssecdb->doQuery((fmt % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind)).str());
+    d_getDomainMetadataQuery_stmt->
+      bind("domain", name)->
+      bind("kind", kind)->
+      execute(); 
   
-    vector<string> row;
-    while(d_dnssecdb->getRow(row)) {
+    SSqlStatement::row_t row;
+    while(d_getDomainMetadataQuery_stmt->hasNextRow()) {
+      d_getDomainMetadataQuery_stmt->nextRow(row);
       meta.push_back(row[0]);
     }
+
+    d_getDomainMetadataQuery_stmt->reset();
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
@@ -170,12 +223,22 @@ bool Bind2Backend::setDomainMetadata(const string& name, const std::string& kind
   if(!d_dnssecdb || d_hybrid)
     return false;
   
-  boost::format fmt("delete from domainmetadata where domain='%s' and kind='%s'");
-  boost::format fmt2("insert into domainmetadata (domain, kind, content) values ('%s','%s', '%s')");
   try {
-    d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind)).str());
-    if(!meta.empty())
-      d_dnssecdb->doCommand((fmt2 % d_dnssecdb->escape(name) % d_dnssecdb->escape(kind) % d_dnssecdb->escape(meta.begin()->c_str())).str());
+    d_deleteDomainMetadataQuery_stmt->
+      bind("domain", name)->
+      bind("kind", kind)->
+      execute()->
+      reset();
+    if(!meta.empty()) {
+      BOOST_FOREACH(const string& value, meta) {
+        d_insertDomainMetadataQuery_stmt->
+          bind("domain", name)->
+          bind("kind", kind)->
+          bind("content", value)->
+          execute()->
+          reset();
+      }
+    }
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
@@ -189,18 +252,21 @@ bool Bind2Backend::getDomainKeys(const string& name, unsigned int kind, std::vec
   // cerr<<"Asked to get keys for zone '"<<name<<"'\n";
   if(!d_dnssecdb || d_hybrid)
     return false;
-  boost::format fmt("select id,flags, active, content from cryptokeys where domain='%s'");
   try {
-    d_dnssecdb->doQuery((fmt % d_dnssecdb->escape(name)).str());
+    d_getDomainKeysQuery_stmt->
+      bind("domain", name)->
+      execute();
     KeyData kd;
-    vector<string> row;
-    while(d_dnssecdb->getRow(row)) {
+    SSqlStatement::row_t row;
+    while(d_getDomainKeysQuery_stmt->hasNextRow()) {
+      d_getDomainKeysQuery_stmt->nextRow(row);
       kd.id = atoi(row[0].c_str());
       kd.flags = atoi(row[1].c_str());
       kd.active = atoi(row[2].c_str());
       kd.content = row[3];
       keys.push_back(kd);
     }
+    d_getDomainKeysQuery_stmt->reset();
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
@@ -216,9 +282,12 @@ bool Bind2Backend::removeDomainKey(const string& name, unsigned int id)
 
   // cerr<<"Asked to remove key "<<id<<" in zone '"<<name<<"'\n";
 
-  boost::format fmt("delete from cryptokeys where domain='%s' and id=%d");
   try {
-    d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % id).str());
+    d_deleteDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
   }
   catch(SSqlException& se) {
     cerr<<se.txtReason()  <<endl;
@@ -234,9 +303,14 @@ int Bind2Backend::addDomainKey(const string& name, const KeyData& key)
   
   //cerr<<"Asked to add a key to zone '"<<name<<"'\n";
   
-  boost::format fmt("insert into cryptokeys (domain, flags, active, content) values ('%s', %d, %d, '%s')");
   try {
-    d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % key.flags % key.active % d_dnssecdb->escape(key.content)).str());
+    d_insertDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("flags", key.flags)->
+      bind("active", key.active)->
+      bind("content", key.content)->
+      execute()->
+      reset();
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());    
@@ -251,9 +325,12 @@ bool Bind2Backend::activateDomainKey(const string& name, unsigned int id)
   if(!d_dnssecdb || d_hybrid)
     return false;
   
-  boost::format fmt("update cryptokeys set active=1 where domain='%s' and id=%d");
   try {
-    d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % id).str());
+    d_activateDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());    
@@ -268,9 +345,12 @@ bool Bind2Backend::deactivateDomainKey(const string& name, unsigned int id)
   if(!d_dnssecdb || d_hybrid)
     return false;
     
-  boost::format fmt("update cryptokeys set active=0 where domain='%s' and id=%d");
   try {
-    d_dnssecdb->doCommand((fmt % d_dnssecdb->escape(name) % id).str());
+    d_deactivateDomainKeyQuery_stmt->
+      bind("domain", name)->
+      bind("key_id", id)->
+      execute()->
+      reset();
   }
   catch(SSqlException& se) {
     throw PDNSException("Error accessing DNSSEC database in BIND backend: "+se.txtReason());
@@ -283,36 +363,41 @@ bool Bind2Backend::getTSIGKey(const string& name, string* algorithm, string* con
 {
   if(!d_dnssecdb || d_hybrid)
     return false;
-  boost::format fmt("select algorithm, secret from tsigkeys where name='%s'");
   
   try {
-    d_dnssecdb->doQuery( (fmt % d_dnssecdb->escape(name)).str());
+    d_getTSIGKeyQuery_stmt->
+      bind("key_name", name)->
+      execute();
+    SSqlStatement::row_t row;
+    content->clear();
+    while(d_getTSIGKeyQuery_stmt->hasNextRow()) {
+      d_getTSIGKeyQuery_stmt->nextRow(row);
+      if(row.size() >= 2 && (algorithm->empty() || pdns_iequals(*algorithm, row[0]))) {
+        *algorithm = row[0];
+        *content = row[1];
+      }
+    }
+    d_getTSIGKeyQuery_stmt->reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("BindBackend unable to retrieve named TSIG key: "+e.txtReason());
   }
-  
-  SSql::row_t row;
-  
-  content->clear();
-  while(d_dnssecdb->getRow(row)) {
-    if(row.size() >= 2 && (algorithm->empty() || pdns_iequals(*algorithm, row[0]))) {
-      *algorithm = row[0];
-      *content = row[1];
-    }
-  }
 
   return !content->empty();
-
 }
 
 bool Bind2Backend::setTSIGKey(const string& name, const string& algorithm, const string& content)
 {
   if(!d_dnssecdb || d_hybrid)
     return false;
-  boost::format fmt("replace into tsigkeys (name,algorithm,secret) values('%s', '%s', '%s')");
+
   try {
-    d_dnssecdb->doCommand( (fmt % d_dnssecdb->escape(name) % d_dnssecdb->escape(algorithm) % d_dnssecdb->escape(content)).str() );
+    d_setTSIGKeyQuery_stmt->
+      bind("key_name", name)->
+      bind("algorithm", algorithm)->
+      bind("content", content)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("BindBackend unable to retrieve named TSIG key: "+e.txtReason());
@@ -325,10 +410,12 @@ bool Bind2Backend::deleteTSIGKey(const string& name)
 {
   if(!d_dnssecdb || d_hybrid)
     return false;
-  boost::format fmt("delete from tsigkeys where name='%s'");
 
   try {
-    d_dnssecdb->doCommand( (fmt % d_dnssecdb->escape(name)).str() );
+    d_deleteTSIGKeyQuery_stmt->
+      bind("key_name", name)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("BindBackend unable to retrieve named TSIG key: "+e.txtReason());
@@ -343,20 +430,24 @@ bool Bind2Backend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
     return false;
 
   try {
-    d_dnssecdb->doQuery( "select name,algorithm,secret from tsigkeys" );
+    d_getTSIGKeysQuery_stmt->
+      execute(); 
+
+    SSqlStatement::row_t row;
+
+    while(d_getTSIGKeysQuery_stmt->hasNextRow()) {
+      d_getTSIGKeysQuery_stmt->nextRow(row);
+      struct TSIGKey key;
+      key.name = row[0];
+      key.algorithm = row[1];
+      key.key = row[2];
+      keys.push_back(key);
+    }
+
+    d_getTSIGKeysQuery_stmt->reset();
   }
   catch (SSqlException &e) {
-    throw PDNSException("GSQLBackend unable to retrieve named TSIG key: "+e.txtReason());
-  }
-
-  SSql::row_t row;
-
-  while(d_dnssecdb->getRow(row)) {
-     struct TSIGKey key;
-     key.name = row[0];
-     key.algorithm = row[1];
-     key.key = row[2];
-     keys.push_back(key);
+    throw PDNSException("GSQLBackend unable to retrieve all TSIG keys: "+e.txtReason());
   }
 
   return !keys.empty();
index 949194c5cefb887a55ce72e6f3970fb1d4973361..316e060353867063f165df7144aa9b90b66f88a1 100644 (file)
@@ -52,77 +52,77 @@ public:
 
     string record_query = "SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE";
 
-    declare(suffix, "basic-query", "Basic query", record_query+" disabled=0 and type='%s' and name='%s'");
-    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=0 and type='%s' and name='%s' and domain_id=%d");
-    declare(suffix, "any-query", "Any query", record_query+" disabled=0 and name='%s'");
-    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=0 and name='%s' and domain_id=%d");
+    declare(suffix, "basic-query", "Basic query", record_query+" disabled=0 and type=? and name=?");
+    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=0 and type=? and name=? and domain_id=?");
+    declare(suffix, "any-query", "Any query", record_query+" disabled=0 and name=?");
+    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=0 and name=? and domain_id=?");
 
-    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=0 OR %d) and domain_id='%d' order by name, type");
-    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=0 and (name='%s' OR name like '%s') and domain_id='%d'");
+    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=0 OR ?) and domain_id=? order by name, type");
+    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=0 and (name=? OR name like ?) and domain_id=?");
 
-    declare(suffix, "remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id='%d' and type is null");
-    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (domain_id,name,type,disabled,auth) values ('%d','%s',null,0,'1')");
-    declare(suffix, "delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id='%d' and name='%s' and type is null");
+    declare(suffix, "remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id=? and type is null");
+    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (domain_id,name,type,disabled,auth) values (?,?,null,0,1)");
+    declare(suffix, "delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id=? and name=? and type is null");
 
-    declare(suffix,"master-zone-query","Data", "select master from domains where name='%s' and type='SLAVE'");
+    declare(suffix,"master-zone-query","Data", "select master from domains where name=? and type='SLAVE'");
 
-    declare(suffix,"info-zone-query","","select id,name,master,last_check,notified_serial,type from domains where name='%s'");
+    declare(suffix,"info-zone-query","","select id,name,master,last_check,notified_serial,type from domains where name=?");
 
     declare(suffix,"info-all-slaves-query","","select id,name,master,last_check,type from domains where type='SLAVE'");
-    declare(suffix,"supermaster-query","", "select account from supermasters where ip='%s' and nameserver='%s'");
-    declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver='%s' and account='%s'");
-
-    declare(suffix,"insert-zone-query","", "insert into domains (type,name) values('NATIVE','%s')");
-    declare(suffix,"insert-slave-query","", "insert into domains (type,name,master,account) values('SLAVE','%s','%s','%s')");
-
-    declare(suffix, "insert-record-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,auth) values ('%s',%d,%d,'%s',%d,%d,'%s','%d')");
-    declare(suffix, "insert-record-order-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values ('%s',%d,%d,'%s',%d,%d,'%s','%s','%d')");
-    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,auth) values (null,'%d',0,'%s','%d')");
-    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,ordername,auth) values (null,'%d',0,'%s','%s','%d')");
-
-    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, first", "select ordername, name from records where domain_id=%d and disabled=0 and ordername is not null order by 1 asc limit 1");
-    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select ordername, name from records where ordername <= '%s' and domain_id=%d and disabled=0 and ordername is not null order by 1 desc limit 1");
-    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select min(ordername) from records where ordername > '%s' and domain_id=%d and disabled=0 and ordername is not null");
-    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select ordername, name from records where ordername != '' and domain_id=%d and disabled=0 and ordername is not null order by 1 desc limit 1");
-    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername='%s',auth=%d where name='%s' and domain_id='%d' and disabled=0");
-    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=1 where domain_id='%d' and name='%s' and type='DS' and disabled=0");
-
-    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=%d where domain_id='%d' and name='%s' and disabled=0");
-    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=0 where name='%s' and type='%s' and domain_id='%d' and disabled=0");
-
-    declare(suffix,"update-master-query","", "update domains set master='%s' where name='%s'");
-    declare(suffix,"update-kind-query","", "update domains set type='%s' where name='%s'");
-    declare(suffix,"update-serial-query","", "update domains set notified_serial=%d where id=%d");
-    declare(suffix,"update-lastcheck-query","", "update domains set last_check=%d where id=%d");
-    declare(suffix,"zone-lastchange-query", "", "select max(change_date) from records where domain_id=%d");
+    declare(suffix,"supermaster-query","", "select account from supermasters where ip=? and nameserver=?");
+    declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?");
+
+    declare(suffix,"insert-zone-query","", "insert into domains (type,name) values('NATIVE',?)");
+    declare(suffix,"insert-slave-query","", "insert into domains (type,name,master,account) values('SLAVE',?,?,?)");
+
+    declare(suffix, "insert-record-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,auth) values (?,?,?,?,?,?,?,?)");
+    declare(suffix, "insert-record-order-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (?,?,?,?,?,?,?,?,?)");
+    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,auth) values (null,?,0,?,?)");
+    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,ordername,auth) values (null,?,0,?,?,?)");
+
+    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, first", "select ordername, name from records where domain_id=? and disabled=0 and ordername is not null order by 1 asc limit 1");
+    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select ordername, name from records where ordername <= ? and domain_id=? and disabled=0 and ordername is not null order by 1 desc limit 1");
+    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select min(ordername) from records where ordername > ? and domain_id=? and disabled=0 and ordername is not null");
+    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select ordername, name from records where ordername != '' and domain_id=? and disabled=0 and ordername is not null order by 1 desc limit 1");
+    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername=?,auth=? where name=? and domain_id=? and disabled=0");
+    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=1 where domain_id=? and name=? and type='DS' and disabled=0");
+
+    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=? where domain_id=? and name=? and disabled=0");
+    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=0 where name=? and type=? and domain_id=? and disabled=0");
+
+    declare(suffix,"update-master-query","", "update domains set master=? where name=?");
+    declare(suffix,"update-kind-query","", "update domains set type=? where name=?");
+    declare(suffix,"update-serial-query","", "update domains set notified_serial=? where id=?");
+    declare(suffix,"update-lastcheck-query","", "update domains set last_check=? where id=?");
+    declare(suffix,"zone-lastchange-query", "", "select max(change_date) from records where domain_id=?");
     declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
-    declare(suffix,"delete-domain-query","", "delete from domains where name='%s'");
-    declare(suffix,"delete-zone-query","", "delete from records where domain_id=%d");
-    declare(suffix,"delete-rrset-query","","delete from records where domain_id=%d and name='%s' and type='%s'");
-    declare(suffix,"delete-names-query","","delete from records where domain_id = %d and name='%s'");
-
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, %d, %d, '%s' from domains where name='%s'");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name='%s'");
-    declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name='%s'");
-    declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name='%s' and domainmetadata.kind='%s'");
-    declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name='%s') and domainmetadata.kind='%s'");
-    declare(suffix,"clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name='%s')");
-    declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, '%s', '%s' from domains where name='%s'");
-    declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name='%s') and  cryptokeys.id=%d");
-    declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name='%s') and  cryptokeys.id=%d");
-    declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name='%s') and cryptokeys.id=%d");
-    declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name='%s')");
-    declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name='%s'");
-    declare(suffix,"set-tsig-key-query","", "replace into tsigkeys (name,algorithm,secret) values('%s','%s','%s')");
-    declare(suffix,"delete-tsig-key-query","", "delete from tsigkeys where name='%s'");
+    declare(suffix,"delete-domain-query","", "delete from domains where name=?");
+    declare(suffix,"delete-zone-query","", "delete from records where domain_id=?");
+    declare(suffix,"delete-rrset-query","","delete from records where domain_id=? and name=? and type=?");
+    declare(suffix,"delete-names-query","","delete from records where domain_id=? and name=?");
+
+    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, ?, ?, ? from domains where name=?");
+    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=?");
+    declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=?");
+    declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=? and domainmetadata.kind=?");
+    declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=?) and domainmetadata.kind=?");
+    declare(suffix,"clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=?)");
+    declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, ?, ? from domains where name=?");
+    declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
+    declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name=?) and  cryptokeys.id=?");
+    declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=?) and cryptokeys.id=?");
+    declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=?)");
+    declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=?");
+    declare(suffix,"set-tsig-key-query","", "replace into tsigkeys (name,algorithm,secret) values(?,?,?)");
+    declare(suffix,"delete-tsig-key-query","", "delete from tsigkeys where name=?");
     declare(suffix,"get-tsig-keys-query","", "select name,algorithm, secret from tsigkeys");
 
-    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=0 OR %d");
+    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=0 OR ?");
 
-    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=%d");
-    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES (%d, '%s', '%s', %d, '%s', '%s')");
-    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=%d AND name='%s' AND type='%s'");
-    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=%d");
+    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=?");
+    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES (?, ?, ?, ?, ?, ?)");
+    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=? AND name=? AND type=?");
+    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=?");
   }
 
   DNSBackend *make(const string &suffix="")
index 58ad89d639973fe1480f9935764488c540d07e1a..6510dde4686efca318e43bd8751e11d40415b724 100644 (file)
 bool SMySQL::s_dolog;
 pthread_mutex_t SMySQL::s_myinitlock = PTHREAD_MUTEX_INITIALIZER;
 
+class SMySQLStatement: public SSqlStatement
+{
+public:
+  SMySQLStatement(const string& query, bool dolog, int nparams, MYSQL* db) 
+  {
+    int err;
+    d_db = db;
+    d_dolog = dolog;
+    d_query = query;
+    d_parnum = d_paridx = d_fnum = d_resnum = d_residx = 0;
+    d_req_bind = d_res_bind = NULL;
+    d_stmt = NULL;
+
+    if (query.empty()) {
+      return;
+    }
+
+    if ((d_stmt = mysql_stmt_init(d_db))==NULL) 
+      throw SSqlException("Could not initialize mysql statement, out of memory");
+    
+    if ((err = mysql_stmt_prepare(d_stmt, query.c_str(), query.size()))) {
+      string error(mysql_stmt_error(d_stmt));
+      throw SSqlException("Could not prepare statement: " + error);
+    }
+
+    if (static_cast<int>(mysql_stmt_param_count(d_stmt)) != nparams) 
+      throw SSqlException("Provided parameter count does not match statement");
+   
+    d_parnum = nparams;
+    if (d_parnum>0) {
+      d_req_bind = new MYSQL_BIND[d_parnum];
+      memset(d_req_bind, 0, sizeof(MYSQL_BIND)*d_parnum);
+    }
+  }
+
+  SSqlStatement* bind(const string& name, bool value) {
+    if (d_paridx >= d_parnum) 
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_TINY;
+    d_req_bind[d_paridx].buffer = new char[1];
+    *((char*)d_req_bind[d_paridx].buffer) = (value?1:0);
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, int value) {
+    return bind(name, (long)value);
+  }
+  SSqlStatement* bind(const string& name, uint32_t value) {
+    return bind(name, (unsigned long)value);
+  }
+  SSqlStatement* bind(const string& name, long value) {
+    if (d_paridx >= d_parnum)
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_LONG;
+    d_req_bind[d_paridx].buffer = new long[1];
+    *((long*)d_req_bind[d_paridx].buffer) = value;
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, unsigned long value) {
+    if (d_paridx >= d_parnum)
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_LONG;
+    d_req_bind[d_paridx].buffer = new unsigned long[1];
+    d_req_bind[d_paridx].is_unsigned = 1;
+    *((unsigned long*)d_req_bind[d_paridx].buffer) = value;
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, long long value) {
+    if (d_paridx >= d_parnum)
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_LONGLONG;
+    d_req_bind[d_paridx].buffer = new long long[1];
+    *((long long*)d_req_bind[d_paridx].buffer) = value;
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, unsigned long long value) {
+    if (d_paridx >= d_parnum)
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_LONGLONG;
+    d_req_bind[d_paridx].buffer = new unsigned long long[1];
+    d_req_bind[d_paridx].is_unsigned = 1;
+    *((unsigned long long*)d_req_bind[d_paridx].buffer) = value;
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, const std::string& value) {
+    if (d_paridx >= d_parnum)
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_STRING;
+    d_req_bind[d_paridx].buffer = new char[value.size()+1];
+    d_req_bind[d_paridx].length = new unsigned long[1];
+    *d_req_bind[d_paridx].length = value.size();
+    d_req_bind[d_paridx].buffer_length = *d_req_bind[d_paridx].length+1;
+    memset(d_req_bind[d_paridx].buffer, 0, value.size()+1);
+    value.copy((char*)d_req_bind[d_paridx].buffer, value.size());
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bindNull(const string& name) { 
+    if (d_paridx >= d_parnum)
+      throw SSqlException("Attempt to bind more parameters than query has");
+    d_req_bind[d_paridx].buffer_type = MYSQL_TYPE_NULL;
+    d_paridx++;
+    return this;
+  }
+
+  SSqlStatement* execute() {
+    int err;
+
+    if (!d_stmt) return this;
+
+    if (d_dolog) {
+      L<<Logger::Warning<<"Query: " << d_query <<endl;
+    }
+
+    if ((err = mysql_stmt_bind_param(d_stmt, d_req_bind))) {
+      string error(mysql_stmt_error(d_stmt));
+      throw SSqlException("Could not bind mysql statement: " + error);
+    }
+
+    if ((err = mysql_stmt_execute(d_stmt))) {
+      string error(mysql_stmt_error(d_stmt));
+      throw SSqlException("Could not execute mysql statement: " + error);
+    }
+
+    if ((d_fnum = static_cast<int>(mysql_stmt_field_count(d_stmt)))>0) {
+      // prepare for result
+      if ((err = mysql_stmt_store_result(d_stmt))) {
+        string error(mysql_stmt_error(d_stmt));
+        throw SSqlException("Could not store mysql statement: " + error);
+      }
+      d_resnum = mysql_stmt_num_rows(d_stmt);
+      
+      if (d_resnum>0 && d_res_bind == NULL) {
+        d_res_bind = new MYSQL_BIND[d_fnum];
+        memset(d_res_bind, 0, sizeof(MYSQL_BIND)*d_fnum);
+        MYSQL_RES* meta = mysql_stmt_result_metadata(d_stmt);
+        MYSQL_FIELD* fields = mysql_fetch_fields(meta);
+
+        for(int i = 0; i < d_fnum; i++) {
+          unsigned long len = std::max(fields[i].max_length, fields[i].length)+1;
+          d_res_bind[i].is_null = new my_bool[1];
+          d_res_bind[i].error = new my_bool[1];
+          d_res_bind[i].length = new unsigned long[1];
+          d_res_bind[i].buffer = new char[len];
+          d_res_bind[i].buffer_length = len;
+          d_res_bind[i].buffer_type = MYSQL_TYPE_STRING;
+        }
+  
+        mysql_free_result(meta);
+  
+        if ((err = mysql_stmt_bind_result(d_stmt, d_res_bind))) {
+          string error(mysql_stmt_error(d_stmt));
+          throw SSqlException("Could not bind parameters to mysql statement: " + error);
+        }
+      }
+    }
+
+    return this;
+  }
+
+  bool hasNextRow() {
+    return d_residx < d_resnum;
+  }
+
+  SSqlStatement* nextRow(row_t& row) {
+    int err;
+    row.clear();
+    if (d_residx >= d_resnum) return this;
+
+    if ((err =mysql_stmt_fetch(d_stmt))) {
+      if (err != MYSQL_DATA_TRUNCATED) {
+        string error(mysql_stmt_error(d_stmt));
+        throw SSqlException("Could not fetch result: " + error);
+      }
+    }
+
+    row.reserve(d_fnum);
+
+    for(int i=0;i<d_fnum;i++) {
+      if (*d_res_bind[i].error) {
+        L<<Logger::Warning<<"Result field at row " << d_residx << " column " << i << " has errno " << *d_res_bind[i].error << endl;
+      }
+      if (*d_res_bind[i].is_null) {
+        row.push_back("");
+        continue;
+      } else {
+        row.push_back(string((char*)d_res_bind[i].buffer, std::min(d_res_bind[i].buffer_length, *d_res_bind[i].length)));
+      }
+    }
+
+    d_residx++;
+    return this; 
+  }
+
+  SSqlStatement* getResult(result_t& result) { 
+    result.clear();
+    result.reserve(d_resnum);
+    row_t row;
+
+    while(hasNextRow()) {
+      nextRow(row);
+      result.push_back(row); 
+    }
+
+    return this; 
+  }
+
+  SSqlStatement* reset() {
+    if (!d_stmt) return this;
+
+    mysql_stmt_reset(d_stmt);
+    if (d_req_bind) {
+      for(int i=0;i<d_parnum;i++) {
+        if (d_req_bind[i].buffer) delete [] (char*)d_req_bind[i].buffer;
+        if (d_req_bind[i].length) delete [] d_req_bind[i].length;
+      }
+      memset(d_req_bind, 0, sizeof(MYSQL_BIND)*d_parnum);
+    }
+    d_residx = d_resnum = 0;
+    d_paridx = 0;
+    return this;
+  }
+
+  const std::string& getQuery() { return d_query; }
+
+  ~SMySQLStatement() {
+    if (d_stmt)
+      mysql_stmt_close(d_stmt);
+    d_stmt = NULL;
+    if (d_req_bind) {
+      for(int i=0;i<d_parnum;i++) {
+        if (d_req_bind[i].buffer) delete [] (char*)d_req_bind[i].buffer;
+        if (d_req_bind[i].length) delete [] d_req_bind[i].length;
+      }
+      delete [] d_req_bind;
+      d_req_bind = NULL;
+    }
+    if (d_res_bind) {
+      for(int i=0;i<d_fnum;i++) {
+        if (d_res_bind[i].buffer) delete [] (char*)d_res_bind[i].buffer;
+        if (d_res_bind[i].length) delete [] d_res_bind[i].length;
+        if (d_res_bind[i].is_null) delete [] d_res_bind[i].is_null;
+      }
+      delete [] d_res_bind;
+      d_res_bind = NULL;
+    }
+  }
+private:
+  MYSQL* d_db;
+
+  MYSQL_STMT* d_stmt;
+  MYSQL_BIND* d_req_bind;
+  MYSQL_BIND* d_res_bind;
+
+  string d_query;
+  
+  bool d_dolog;
+  int d_parnum;
+  int d_paridx;
+  int d_fnum;
+  int d_resnum;
+  int d_residx;
+};
+
 SMySQL::SMySQL(const string &database, const string &host, uint16_t port, const string &msocket, const string &user,
                const string &password, const string &group, bool setIsolation)
 {
@@ -63,8 +331,6 @@ SMySQL::SMySQL(const string &database, const string &host, uint16_t port, const
       retry=-1;
     }
   } while (retry >= 0);
-
-  d_rres=0;
 }
 
 void SMySQL::setLog(bool state)
@@ -82,72 +348,29 @@ SSqlException SMySQL::sPerrorException(const string &reason)
   return SSqlException(reason+string(": ")+mysql_error(&d_db));
 }
 
-int SMySQL::doCommand(const string &query)
+SSqlStatement* SMySQL::prepare(const string& query, int nparams)
 {
-  return doQuery(query);
+  return new SMySQLStatement(query, s_dolog, nparams, &d_db);
 }
 
-int SMySQL::doQuery(const string &query)
+void SMySQL::execute(const string& query)
 {
-  if(d_rres)
-    throw SSqlException("Attempt to start new MySQL query while old one still in progress");
-
   if(s_dolog)
     L<<Logger::Warning<<"Query: "<<query<<endl;
 
   int err;
   if((err=mysql_query(&d_db,query.c_str())))
     throw sPerrorException("Failed to execute mysql_query, perhaps connection died? Err="+itoa(err));
-
-  return 0;
 }
 
-int SMySQL::doQuery(const string &query, result_t &result)
-{
-  result.clear();
-  doQuery(query);
-
-  row_t row;
-  while(getRow(row))
-    result.push_back(row);
-
-  return result.size();
+void SMySQL::startTransaction() {
+  execute("begin");
 }
 
-bool SMySQL::getRow(row_t &row)
-{
-  row.clear();
-  if(!d_rres)
-    if(!(d_rres = mysql_use_result(&d_db)))
-      throw sPerrorException("Failed on mysql_use_result");
-
-  MYSQL_ROW rrow;
-
-  if((rrow = mysql_fetch_row(d_rres))) {
-    for(unsigned int i=0;i<mysql_num_fields(d_rres);i++)
-      row.push_back(rrow[i] ?: "");
-    return true;
-  }
-  mysql_free_result(d_rres);
-
-  while (mysql_next_result(&d_db) == 0) {
-    if ((d_rres = mysql_use_result(&d_db))) {
-      mysql_free_result(d_rres);
-    }
-  }
-
-  d_rres=0;
-  return false;
+void SMySQL::commit() {
+  execute("commit");
 }
 
-string SMySQL::escape(const string &name)
-{
-  string a;
-
-  for(string::const_iterator i=name.begin();i!=name.end();++i) {
-    if(*i=='\'' || *i=='\\')
-      a+='\\';
-    a+=*i;
-  }
-  return a;
+void SMySQL::rollback() {
+  execute("rollback");
 }
index 6d798a388de12f3715bf3f1608ecc3206c15660d..0968722d32f83c25091c519903abef07fd6c9458 100644 (file)
@@ -19,15 +19,16 @@ public:
   ~SMySQL();
 
   SSqlException sPerrorException(const string &reason);
-  int doQuery(const string &query, result_t &result);
-  int doQuery(const string &query);
-  int doCommand(const string &query);
-  bool getRow(row_t &row);
-  string escape(const string &str);
   void setLog(bool state);
+  SSqlStatement* prepare(const string& query, int nparams);
+  void execute(const string& query);
+
+  void startTransaction();
+  void commit();
+  void rollback();
+
 private:
   MYSQL d_db;
-  MYSQL_RES *d_rres;
   static bool s_dolog;
   static pthread_mutex_t s_myinitlock;
 };
index f4fdf924789dae329801c569abf7bb84c9e4b82b..fcd988f5ae632ba2cca9a8098c56056ed5475d7b 100644 (file)
@@ -1,4 +1,4 @@
-AM_CPPFLAGS += $(ORACLE_CFLAGS)
+AM_CPPFLAGS += $(ORACLE_CFLAGS) $(POLARSSL_CFLAGS)
 
 pkglib_LTLIBRARIES = libgoraclebackend.la
 
index bc3ec65ac1e5cefcbb0541d77139f873735673f5..718f4a2beb6ff38f7ad58a0bfced83218637411b 100644 (file)
@@ -9,22 +9,36 @@
 #include "pdns/pdnsexception.hh"
 #include "pdns/logger.hh"
 #include "pdns/arguments.hh"
+#include "pdns/lock.hh"
 #include "goraclebackend.hh"
 #include "soracle.hh"
 #include <sstream>
 
+static OCIEnv* d_environmentHandle = 0;
+static pthread_mutex_t s_goracle_lock=PTHREAD_MUTEX_INITIALIZER;
+
 gOracleBackend::gOracleBackend(const string &mode, const string &suffix)  : GSQLBackend(mode, suffix)
 {
-  try {
-    // set Oracle envionment variables
+  Lock gl(&s_goracle_lock);
+  if (d_environmentHandle == 0) {
     setenv("ORACLE_HOME", getArg("home").c_str(), 1);
     setenv("ORACLE_SID", getArg("sid").c_str(), 1);
     setenv("NLS_LANG", getArg("nls-lang").c_str(), 1);
+  
+    int err = OCIEnvCreate(&d_environmentHandle, OCI_THREADED, NULL, NULL, NULL, NULL, 0, NULL); 
 
+    if (err) {
+      throw PDNSException("OCIEnvCraete failed");
+    }
+  }
+
+  try {
+    // set Oracle envionment variables
     setDB(new SOracle(getArg("tnsname"),
                       getArg("user"),
-                      getArg("password")));
-
+                      getArg("password"), 
+                      mustDo("release-statements"),
+                      d_environmentHandle));
   }
 
   catch (SSqlException &e) {
@@ -34,13 +48,6 @@ gOracleBackend::gOracleBackend(const string &mode, const string &suffix)  : GSQL
   L<<Logger::Info << mode << " Connection successful" << endl;
 }
 
-
-string gOracleBackend::sqlEscape(const string &name)
-{
-  return boost::replace_all_copy(name, "'", "''");
-}
-
-
 class gOracleFactory : public BackendFactory
 {
 public:
@@ -56,80 +63,80 @@ public:
     declare(suffix,"password","Pdns backend password to connect with","");
 
     declare(suffix,"dnssec","Enable DNSSEC processing","no");
+    declare(suffix,"release-statements","Release statements between executions, uses less resources","no");
 
     string record_query = "SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE";
 
-    declare(suffix, "basic-query", "Basic query", record_query+" disabled=0 and type='%s' and name='%s'");
-    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=0 and type='%s' and name='%s' and domain_id=%d");
-    declare(suffix, "any-query", "Any query", record_query+" disabled=0 and name='%s'");
-    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=0 and name='%s' and domain_id=%d");
-
-    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=0 OR 1=%d) and domain_id='%d' order by name, type");
-    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=0 and (name='%s' OR name like '%s') and domain_id='%d'");
-
-    declare(suffix, "remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id='%d' and type is null");
-    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (id,domain_id,name,type,disabled,auth) values (records_id_sequence.nextval,'%d','%s',null,0,'1')");
-    declare(suffix,"delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id='%d' and name='%s' and type is null");
-
-    declare(suffix,"master-zone-query","Data", "select master from domains where name='%s' and type='SLAVE'");
-
-    declare(suffix,"info-zone-query","","select id,name,master,last_check,notified_serial,type from domains where name='%s'");
-
-    declare(suffix,"info-all-slaves-query","","select id,name,master,last_check,type from domains where type='SLAVE'");
-    declare(suffix,"supermaster-query","", "select account from supermasters where ip='%s' and nameserver='%s'");
-    declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver='%s' and account='%s'");
-
-    declare(suffix,"insert-zone-query","", "insert into domains (id, type, name) values(domain_id_sequence.nextval, 'NATIVE','%s')");
-    declare(suffix,"insert-slave-query","", "insert into domains (id, type,name,master,account) values(domain_id_sequence.nextval, 'SLAVE','%s','%s','%s')");
-
-    declare(suffix, "insert-record-query", "", "insert into records (id, content,ttl,prio,type,domain_id,disabled,name,auth) values (records_id_sequence.nextval, '%s',%d,%d,'%s',%d,%d,'%s','%d')");
-    declare(suffix, "insert-record-order-query", "", "insert into records (id, content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (records_id_sequence.nextval, '%s',%d,%d,'%s',%d,%d,'%s','%s ','%d')");
-    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (id, type,domain_id,disabled,name,auth) values (records_id_sequence.nextval, null,'%d',0,'%s','%d')");
-    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (id, type,domain_id,disabled,name,ordername,auth) values (records_id_sequence.nextval, null,'%d',0,'%s','%s','%d')");
-
-    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, first", "select trim(ordername),name from records where disabled=0 and domain_id=%d and ordername is not null and rownum=1 order by ordername asc");
-    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select trim(ordername), name from records where disabled=0 and ordername <= '%s ' and domain_id=%d and ordername is not null and rownum=1 order by ordername desc");
-    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select trim(min(ordername)) from records where disabled=0 and ordername > '%s ' and domain_id=%d and ordername is not null");
-    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select trim(ordername), name from records where disabled=0 and ordername != ' ' and domain_id=%d and ordername is not null and rownum=1 order by ordername desc");
-    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername='%s ',auth=%d where name='%s' and domain_id='%d' and disabled=0");
-    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=1 where domain_id='%d' and name='%s' and type='DS' and disabled=0");
-
-    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=%d where domain_id='%d' and name='%s' and disabled=0");
-    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=0 where name='%s' and type='%s' and domain_id='%d' and disabled=0");
-
-    declare(suffix,"update-master-query","", "update domains set master='%s' where name='%s'");
-    declare(suffix,"update-kind-query","", "update domains set type='%s' where name='%s'");
-    declare(suffix,"update-serial-query","", "update domains set notified_serial=%d where id=%d");
-    declare(suffix,"update-lastcheck-query","", "update domains set last_check=%d where id=%d");
-    declare(suffix,"zone-lastchange-query", "", "select max(change_date) from records where domain_id=%d");
-    declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
-    declare(suffix,"delete-domain-query","", "delete from domains where name='%s'");
-    declare(suffix,"delete-zone-query","", "delete from records where domain_id=%d");
-    declare(suffix,"delete-rrset-query","","delete from records where domain_id=%d and name='%s' and type='%s'");
-    declare(suffix,"delete-names-query","","delete from records where domain_id=%d and name='%s'");
-
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (id, domain_id, flags, active, content) select cryptokeys_id_sequence.nextval, id, %d, %d, '%s' from domains where name='%s'");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name='%s'");
-    declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name='%s'");
-    declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name='%s' and domainmetadata.kind='%s'");
-    declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name='%s') and domainmetadata.kind='%s'");
-    declare(suffix,"clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name='%s')");
-    declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (id, domain_id, kind, content) select domainmetadata_id_sequence.nextval, id, '%s', '%s' from domains where name='%s'");
-    declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name='%s') and  cryptokeys.id=%d");
-    declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name='%s') and  cryptokeys.id=%d");
-    declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name='%s') and cryptokeys.id=%d");
-    declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name='%s')");
-    declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name='%s'");
-    declare(suffix,"set-tsig-key-query","", "insert into tsigkeys (id,name,algorithm,secret) VALUES(tsigkeys_id_sequence.nextval,'%s','%s','%s')");
-    declare(suffix,"delete-tsig-key-query","", "delete from tsigkeys where name='%s'");
-    declare(suffix,"get-tsig-keys-query","", "select name,algorithm, secret from tsigkeys");
-
-    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=0 OR 1=%d");
-
-    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,\"comment\" FROM comments WHERE domain_id=%d");
-    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (id, domain_id, name, type, modified_at, account, \"comment\") VALUES (comments_id_sequence.nextval, %d, '%s', '%s', %d, '%s', '%s')");
-    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=%d AND name='%s' AND type='%s'");
-    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=%d");
+    declare(suffix, "basic-query", "Basic query", record_query+" disabled=0 and type=:qtype and name=:qname");
+    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=0 and type=:qtype and name=:qname and domain_id=:domain_id");
+    declare(suffix, "any-query", "Any query", record_query+" disabled=0 and name=:qname");
+    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=0 and name=:qname and domain_id=:domain_id");
+
+    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=0 OR disabled=:include_disabled) and domain_id=:domain_id order by name, type");
+    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=0 and (name=:zone OR name like :wildzone) and domain_id=:domain_id");
+
+    declare(suffix, "remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id=:domain_id and type is null");
+    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (id,domain_id,name,type,disabled,auth) values (records_id_sequence.nextval,:domain_id,:qname,null,0,'1')");
+    declare(suffix, "delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id=:domain_id and name=:qname and type is null");
+
+    declare(suffix, "master-zone-query", "Data", "select master from domains where name=:domain and type='SLAVE'");
+
+    declare(suffix, "info-zone-query", "","select id,name,master,last_check,notified_serial,type from domains where name=:domain");
+
+    declare(suffix, "info-all-slaves-query", "","select id,name,master,last_check,type from domains where type='SLAVE'");
+    declare(suffix, "supermaster-query", "", "select account from supermasters where ip=:ip and nameserver=:nameserver");
+    declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=:nameserver and account=:account");
+    declare(suffix, "insert-zone-query", "", "insert into domains (id,type,name) values(domains_id_sequence.nextval,'NATIVE',:domain)");
+    declare(suffix, "insert-slave-query", "", "insert into domains (id,type,name,master,account) values(domains_id_sequence.nextval,'SLAVE',:domain,:masters,:account)");
+    declare(suffix, "insert-record-query", "", "insert into records (id,content,ttl,prio,type,domain_id,disabled,name,auth) values (records_id_sequence.nextval,:content,:ttl,:priority,:qtype,:domain_id,:disabled,:qname,:auth)");
+    declare(suffix, "insert-record-order-query", "", "insert into records (id,content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (records_id_sequence.nextval,:content,:ttl,:priority,:qtype,:domain_id,:disabled,:qname,:ordername || ' ',:auth)");
+    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (id,type,domain_id,disabled,name,auth) values (records_id_sequence.nextval,null,:domain_id,0,:qname,:auth)");
+    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (id,type,domain_id,disabled,name,ordername,auth) values (records_id_sequence.nextval,null,:domain_id,0,:qname,:ordername,:auth)");
+
+    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, first", "select * FROM (select trim(ordername),name from records where disabled=0 and domain_id=:domain_id and ordername is not null order by ordername asc) where rownum=1");
+    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select * FROM (select trim(ordername), name from records where disabled=0 and ordername <= :ordername || ' ' and domain_id=:domain_id and ordername is not null order by ordername desc) where rownum=1");
+    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select trim(min(ordername)) from records where disabled=0 and ordername > :ordername || ' ' and domain_id=:domain_id and ordername is not null");
+    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select * from (select trim(ordername), name from records where disabled=0 and ordername != ' ' and domain_id=:domain_id and ordername is not null order by ordername desc) where rownum=1");
+    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername=:ordername || ' ',auth=:auth where name=:qname and domain_id=:domain_id and disabled=0");
+    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=1 where domain_id=:domain_id and name=:qname and type='DS' and disabled=0");
+
+    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=:auth where domain_id=:domain_id and name=:qname and disabled=0");
+    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=0 where name=:qname and type=:qtype and domain_id=:domain_id and disabled=0");
+
+    declare(suffix, "update-master-query", "", "update domains set master=:master where name=:domain");
+    declare(suffix, "update-kind-query", "", "update domains set type=:kind where name=:domain");
+    declare(suffix, "update-serial-query", "", "update domains set notified_serial=:serial where id=:domain_id");
+    declare(suffix, "update-lastcheck-query", "", "update domains set last_check=:last_check where id=:domain_id");
+    declare(suffix, "zone-lastchange-query", "", "select max(change_date) from records where domain_id=:domain_id");
+    declare(suffix, "info-all-master-query", "", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix, "delete-domain-query","", "delete from domains where name=:domain");
+    declare(suffix, "delete-zone-query", "", "delete from records where domain_id=:domain_id");
+    declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=:domain_id and name=:qname and type=:qtype");
+    declare(suffix, "delete-names-query", "", "delete from records where domain_id=:domain_id and name=:qname");
+
+    declare(suffix, "add-domain-key-query","", "insert into cryptokeys (id, domain_id, flags, active, content) select cryptokeys_id_sequence.nextval, id, :flags,:active, :content from domains where name=:domain");
+    declare(suffix, "list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=:domain");
+    declare(suffix, "get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=:domain");
+    declare(suffix, "get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=:domain and domainmetadata.kind=:kind");
+    declare(suffix, "clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=:domain) and domainmetadata.kind=:kind");
+    declare(suffix, "clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=:domain)");
+    declare(suffix, "set-domain-metadata-query","", "insert into domainmetadata (id, domain_id, kind, content) select domainmetadata_id_sequence.nextval, id, :kind, :content from domains where name=:domain");
+    declare(suffix, "activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
+    declare(suffix, "deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
+    declare(suffix, "remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=:domain) and cryptokeys.id=:key_id");
+    declare(suffix, "clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=:domain)");
+    declare(suffix, "get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=:key_name");
+    declare(suffix, "set-tsig-key-query","", "merge into tsigkeys tk using dual on (name = :key_name and algorithm = :algorithm) when not matched then insert (id, name, algorithm, secret) values(tsigkeys_id_sequence.nextval, :key_name, :algorithm, :content) when matched then update set secret = :content");
+    declare(suffix, "delete-tsig-key-query","", "delete from tsigkeys where name=:key_name");
+    declare(suffix, "get-tsig-keys-query","", "select name,algorithm, secret from tsigkeys");
+
+    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=0 OR records.disabled=:include_disabled");
+
+    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,\"comment\" FROM comments WHERE domain_id=:domain_id");
+    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (id, domain_id, name, type, modified_at, account, \"comment\") VALUES (comments_id_sequence.nextval, :domain_id, :qname, :qtype, :modified_at, :account, :content)");
+    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=:domain_id AND name=:qname AND type=:qtype");
+    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=:domain_id");
+
   }
 
   DNSBackend* make(const string &suffix="") {
index d8e9f6309de1fc234d0234561bfef712fe0c1db5..1ca3b8b05f6233f2d340d21863fa3decde0756e4 100644 (file)
@@ -10,5 +10,4 @@ class gOracleBackend : public GSQLBackend
 {
 public:
   gOracleBackend(const string &mode, const string &suffix); //!< Makes our connection to the database. Throws an exception if it fails.
-  virtual string sqlEscape(const string &name);
 };
index 926f96dcdd77be8a29e8728fef132f47cb33db1f..0759dfd29036bde3c0b04a5a7967eac6a47428f7 100644 (file)
 #include "pdns/misc.hh"
 #include "pdns/logger.hh"
 #include "pdns/dns.hh"
-#include <regex.h>
 #include "pdns/namespaces.hh"
+#include "pdns/md5.hh"
 
 bool SOracle::s_dolog;
 
+class SOracleStatement: public SSqlStatement {
+public:
+  SOracleStatement(const string& query, bool dolog, int nparams, OCIEnv *ctx, OCISvcCtx *svc_ctx, bool release_stmt) {
+    d_query = query;
+    d_ctx = ctx;
+    d_svcctx = svc_ctx;
+    d_dolog = dolog;
+    d_res = NULL;
+    d_bind = NULL;
+    d_stmt = NULL;
+    d_err = NULL;
+    d_queryResult = OCI_NO_DATA;
+    d_paridx = d_parnum = d_resnum = d_residx = 0;
+    d_release_stmt = release_stmt;
+    d_non_null_ind = 0;
+    d_null_ind = -1;
+    d_init = false;
+
+    if (query.size() == 0) return;
+
+    // create a key
+    string key = pdns_md5sum(query);
+    d_stmt_keysize = std::min(key.size()*2, sizeof(d_stmt_key));
+    for(string::size_type i = 0; i < key.size() && i*2 < d_stmt_keysize; i++)
+      snprintf((char*)&(d_stmt_key[i*2]), 3, "%02x", (unsigned char)key[i]);
+    d_stmt_key[d_stmt_keysize] = 0;
+
+    if (OCIHandleAlloc(d_ctx, (dvoid**)&d_err, OCI_HTYPE_ERROR, 0, NULL)) 
+      throw SSqlException("Cannot allocate statement error handle");
+
+    if (d_release_stmt) {
+      if (OCIStmtPrepare2(d_svcctx, &d_stmt, d_err, (text*)query.c_str(), query.size(), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) != OCI_SUCCESS) {
+        // failed.
+        throw SSqlException("Cannot prepare statement: " + OCIErrStr());
+      }
+      d_init = true;
+    } else d_init = false;
+    
+    d_parnum = nparams;
+    d_bind = new struct obind[d_parnum];
+    memset(d_bind, 0, sizeof(struct obind)*d_parnum);
+    // and we are done.
+  }
 
-string SOracle::getOracleError()
-{
-  string mReason = "ORA-UNKNOWN";
-
-  if (d_errorHandle != NULL) {
-    text  msg[512];
-    sb4   errcode = 0;
-
-    memset(msg, 0, 512);
-
-    OCIErrorGet((dvoid*) d_errorHandle,1, NULL, &errcode, msg, sizeof(msg), OCI_HTYPE_ERROR);
-    if (errcode) {
-      char* p = (char*) msg;
-      while (*p++ != 0x00) {
-        if (*p == '\n' || *p == '\r') {
-          *p = ';';
-        }
+  void prepareStatement() {
+    if (d_stmt) return; // no-op 
+    if (d_query.size()==0) return;
+    if (d_init == false) {
+      if (OCIStmtPrepare2(d_svcctx, &d_stmt, d_err, (text*)d_query.c_str(), d_query.size(), NULL, 0, OCI_NTV_SYNTAX, OCI_DEFAULT) != OCI_SUCCESS) {
+        throw SSqlException("Cannot prepare statement: " + OCIErrStr());
+      }
+      d_init = true;
+    } else {
+      if (OCIStmtPrepare2(d_svcctx, &d_stmt, d_err, (text*)d_query.c_str(), d_query.size(), d_stmt_key, d_stmt_keysize, OCI_NTV_SYNTAX, OCI_DEFAULT) != OCI_SUCCESS) {
+        throw SSqlException("Cannot prepare statement: " + OCIErrStr());
       }
+    }
+  }
 
-      mReason = (char*) msg;
+  SSqlStatement* bind(const string& name, bool value) 
+  {  
+    return bind(name, (int)value);
+  }
+  SSqlStatement* bind(const string& name, int value) 
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].val4 = value;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), &(d_bind[d_paridx].val4), sizeof(sb4), SQLT_INT, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
     }
+    d_paridx++;
+    return this;
   }
-  return mReason;
-}
+  SSqlStatement* bind(const string& name, uint32_t value)
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].val4 = value;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), (ub4*)&(d_bind[d_paridx].val4), sizeof(ub4), SQLT_UIN, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, long value)
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].val4 = value;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), (ub4*)&(d_bind[d_paridx].val4), sizeof(sb4), SQLT_INT, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, unsigned long value) 
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].val4 = value;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), (ub4*)&(d_bind[d_paridx].val4), sizeof(ub4), SQLT_UIN, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, long long value)
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].val8 = value;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), (orasb8*)&(d_bind[d_paridx].val8), sizeof(orasb8), SQLT_INT, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, unsigned long long value)
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].val8 = value;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), (oraub8*)&(d_bind[d_paridx].val8), sizeof(oraub8), SQLT_UIN, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bind(const string& name, const std::string& value) 
+  {
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    d_bind[d_paridx].vals = new text[value.size()+1];
+    memset(d_bind[d_paridx].vals, 0, value.size()+1);
+    value.copy((char*)d_bind[d_paridx].vals, value.size());
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), (text*)d_bind[d_paridx].vals, value.size()+1, SQLT_STR, &d_non_null_ind,0,0,0,0,OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bindNull(const string& name) 
+  { 
+    if (d_paridx >= d_parnum)
+     throw SSqlException("Attempt to bind more parameters than query has");
+    prepareStatement();
+    string zName = string(":") + name;
+    if (OCIBindByName(d_stmt, &(d_bind[d_paridx].handle), d_err, (text*)zName.c_str(), zName.size(), NULL, 0, SQLT_STR, &d_null_ind, 0, 0, 0, 0, OCI_DEFAULT) != OCI_SUCCESS) {
+      throw SSqlException(string("Cannot bind parameter ") + name + string(": ") + OCIErrStr());
+    }
+    d_bind[d_paridx].release = true; // remember to free this
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* execute() 
+  {
+    if (d_query.size() == 0) return this; // do not execute empty queries
+    prepareStatement();
 
-SOracle::SOracle(const string &database,
-                 const string &user,
-                 const string &password)
-{
-  d_environmentHandle = NULL;
-  d_errorHandle = NULL;
-  d_serviceContextHandle = NULL;
-  d_handle = NULL;
+    if (d_dolog) 
+      L<<Logger::Warning<<"Query: "<<d_query<<endl;
+    ub2 fntype;
+    ub4 iters;
 
-  int err = OCIInitialize(OCI_THREADED, 0,  NULL, NULL, NULL);
-  if (err) {
-    throw sPerrorException("OCIInitialize");
-  }
+    if (OCIAttrGet(d_stmt, OCI_HTYPE_STMT, (dvoid*)&fntype, 0, OCI_ATTR_STMT_TYPE, d_err))
+      throw SSqlException("Cannot get statement type: " + OCIErrStr());
 
-  err = OCIEnvInit(&d_environmentHandle, OCI_DEFAULT, 0, 0);
-  if (err) {
-    throw sPerrorException("OCIEnvInit");
-  }
+    if (fntype == OCI_STMT_SELECT) iters = 0;
+    else iters = 1;
 
-  // Allocate an error handle
+    d_queryResult = OCIStmtExecute(d_svcctx, d_stmt, d_err, iters, 0, NULL, NULL, OCI_DEFAULT);
+    if (d_queryResult != OCI_NO_DATA && d_queryResult != OCI_SUCCESS && d_queryResult != OCI_SUCCESS_WITH_INFO) {
+      throw SSqlException("Cannot execute statement: " + OCIErrStr());
+    }
 
-  err = OCIHandleAlloc(d_environmentHandle, (dvoid**) &d_errorHandle, OCI_HTYPE_ERROR, 0, NULL);
-  if (err) {
-    throw sPerrorException("OCIHandleAlloc");
-  }
+    d_resnum = d_residx = 0; 
 
-  // Logon to the database
+    if (fntype == OCI_STMT_SELECT) {
+      ub4 o_fnum;
+      ub4 o_resnum;
 
-  const char* username = user.c_str();
+      // figure out what the result looks like
+      if (OCIAttrGet(d_stmt, OCI_HTYPE_STMT, (dvoid*)&o_resnum, 0, OCI_ATTR_ROW_COUNT, d_err)) 
+        throw SSqlException("Cannot get statement result row count: " + OCIErrStr()); // this returns 0 
+      if (OCIAttrGet(d_stmt, OCI_HTYPE_STMT, (dvoid*)&o_fnum, 0, OCI_ATTR_PARAM_COUNT, d_err)) 
+        throw SSqlException("Cannot get statement result column count: " + OCIErrStr());
 
-  err = OCILogon(d_environmentHandle, d_errorHandle, &d_serviceContextHandle, (OraText*) username, strlen(username),
-                 (OraText*) password.c_str(),  strlen(password.c_str()), (OraText*) database.c_str(), strlen(database.c_str()));
+      d_residx = 0;
+      d_resnum = o_resnum;
+      d_fnum = o_fnum;
 
-  if (err) {
-    throw sPerrorException("Loging in to Oracle gave error: " + getOracleError());
-  }
-}
+      if (d_res == NULL && d_fnum > 0) {
+        ub2 o_attrtype;
+        OCIParam *parms = NULL;
+        d_res = new struct oresult[d_fnum];
+        memset(d_res, 0, sizeof(struct oresult)*d_fnum);
 
-void SOracle::setLog(bool state)
-{
-  s_dolog=state;
-}
+        for(int i=0; i < d_fnum; i++) {
+          if (OCIParamGet(d_stmt, OCI_HTYPE_STMT, d_err, (dvoid**)&parms, (ub4)i+1) != OCI_SUCCESS) {
+            throw SSqlException("Cannot get statement result column information: " + OCIErrStr());
+          }
 
-SOracle::~SOracle()
-{
-  if (d_handle) {
-    OCIHandleFree(d_handle, OCI_HTYPE_STMT);
-    d_handle=0;
-  }
+          if (OCIAttrGet(parms, OCI_DTYPE_PARAM, (dvoid*)&(d_res[i].colsize), 0, OCI_ATTR_DATA_SIZE, d_err) != OCI_SUCCESS) {
+            throw SSqlException("Cannot get statement result column information: " + OCIErrStr());
+          }
+          
+          if (d_res[i].colsize == 0) {
+            if (OCIAttrGet(parms, OCI_DTYPE_PARAM, (dvoid*)&o_attrtype, 0, OCI_ATTR_DATA_TYPE, d_err) != OCI_SUCCESS) {
+              throw SSqlException("Cannot get statement result column information: " + OCIErrStr());
+            }
+
+            // oracle 11g returns 0 for integer fields - we know oracle should return 22.
+            if (o_attrtype == OCI_TYPECODE_INTEGER ||
+                o_attrtype == OCI_TYPECODE_SMALLINT ||
+                o_attrtype == OCI_TYPECODE_REAL ||
+                o_attrtype == OCI_TYPECODE_DOUBLE ||
+                o_attrtype == OCI_TYPECODE_FLOAT ||
+                o_attrtype == OCI_TYPECODE_NUMBER ||
+                o_attrtype == OCI_TYPECODE_DECIMAL) d_res[i].colsize = 22;
+          }
+          d_res[i].content = new char[d_res[i].colsize+1];
+        }
+      }
 
-  int err;
-  if (d_serviceContextHandle != NULL) {
-    err=OCILogoff(d_serviceContextHandle, d_errorHandle);
-    if (err) {
-      cerr<<"Problems logging out: "+getOracleError()<<endl;
+      if (d_fnum > 0) {
+        for(int i=0;i<d_fnum;i++) {
+          if (OCIDefineByPos(d_stmt, &(d_res[i].handle), d_err, i+1, d_res[i].content, d_res[i].colsize+1, SQLT_STR, (dvoid*)&(d_res[i].ind), NULL, NULL, OCI_DEFAULT)) 
+            throw SSqlException("Cannot bind result column: " + OCIErrStr());
+        }
+      }
+
+      d_queryResult = OCIStmtFetch2(d_stmt, d_err, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
     }
+
+    return this;
   }
 
-  if (d_errorHandle != NULL) {
-    OCIHandleFree(d_errorHandle, OCI_HTYPE_ERROR);
-    d_errorHandle = NULL;
+  string OCIErrStr() 
+  {
+    string mReason = "ORA-UNKNOWN";
+    if (d_err != NULL) {
+      text  msg[512];
+      sb4   errcode = 0;
+      memset(msg, 0, 512);
+      OCIErrorGet((dvoid*) d_err,1, NULL, &errcode, msg, sizeof(msg), OCI_HTYPE_ERROR);
+      if (errcode) {
+        char* p = (char*) msg;
+        while (*p++ != 0x00) {
+          if (*p == '\n' || *p == '\r') {
+            *p = ';';
+          }
+        }
+        mReason = (char*) msg;
+       }
+     }
+    return mReason;
   }
 
-  if (d_environmentHandle != NULL) {
-    OCIHandleFree(d_environmentHandle, OCI_HTYPE_ENV);
-    d_environmentHandle = NULL;
+  bool hasNextRow() {
+    if (d_queryResult == OCI_NO_DATA) return false;
+    return true;
   }
-}
 
-SSqlException SOracle::sPerrorException(const string &reason)
-{
-  return SSqlException(reason);
-}
+  SSqlStatement* nextRow(row_t& row) {
+    row.clear();
 
-int SOracle::doCommand(const string &query)
-{
-  int retval = doQuery(query);
-  if (d_handle) {
-    OCIHandleFree(d_handle, OCI_HTYPE_STMT);
-    d_handle=0;
-  }
+    if (d_stmt == NULL) return this;
 
-  return retval;
-}
+    if (d_queryResult == OCI_NO_DATA) return this;
 
-int getNumFields(const string &query)
-{
-  string lquery=toLower(query);
-  const char* delim[]= {" from ", "\tfrom\t", "\tfrom ", " from\t", 0};
-  int n=0;
-  string::size_type pos;
-  for (n=0; delim[n] && (pos=lquery.find(delim[n]))==string::npos; ++n)
-    ;
-
-  if (!delim[n]) {
-    return -1;
-  }
+    if (d_queryResult != OCI_SUCCESS && d_queryResult != OCI_SUCCESS_WITH_INFO) {
+      throw SSqlException("Cannot get next row: " + OCIErrStr());
+    }
+
+    row.reserve(d_fnum);
 
-  unsigned int num=1;
+    for (int i=0; i < d_fnum ; i++) {
 
-  for (unsigned int n=0; n < pos; ++n)
-    if (lquery[n]==',') {
-      num++;
+      if (d_res[i].ind>=0) {
+        row.push_back(d_res[i].content);
+      } else {
+        row.push_back("");
+      }
     }
 
-  return num;
-}
+    d_queryResult = OCIStmtFetch2(d_stmt, d_err, 1, OCI_FETCH_NEXT, 0, OCI_DEFAULT);
 
-int SOracle::doQuery(const string &query)
-{
-  if (query=="begin") { // oracle does this implicitly
-    return 0;
+    d_residx++;
+    return this;
   }
 
-  if (s_dolog) {
-    L<<Logger::Warning<<"Query: "<<query<<endl;
-  }
+  SSqlStatement* getResult(result_t& result) {
+    row_t row;
 
-  int err = OCIHandleAlloc(d_environmentHandle, (dvoid**) &d_handle, OCI_HTYPE_STMT, 0, NULL);
+    result.reserve(d_resnum);
+    while(hasNextRow()) {
+      nextRow(row);
+      result.push_back(row);
+    }
 
-  if (err) {
-    throw sPerrorException("Allocating a query handle: "+getOracleError());
+    return this;
   }
 
-  err = OCIStmtPrepare(d_handle, d_errorHandle, (text*) query.c_str(), strlen(query.c_str()),
-                       OCI_NTV_SYNTAX, OCI_DEFAULT);
+  SSqlStatement* reset() {
+    d_paridx = 0;
+    d_residx = d_resnum = 0;
 
-  if (err) {
-    throw sPerrorException("Preparing statement: "+getOracleError());
+    if (d_bind) {
+      for(int i=0;i<d_parnum;i++) {
+        if (d_bind[i].vals && d_bind[i].release) delete [] (text*)d_bind[i].vals;
+      }
+    }
+    d_bind = new struct obind[d_parnum];
+    memset(d_bind, 0, sizeof(struct obind)*d_parnum);
+  
+    if (d_release_stmt) {
+      if (OCIStmtRelease(d_stmt, d_err, (text*)d_stmt_key, d_stmt_keysize, OCI_DEFAULT) != OCI_SUCCESS)
+        throw SSqlException("Could not release statement: " + OCIErrStr());
+      d_stmt = NULL;
+    }
+    return this;
   }
 
-  ub4 prefetch=1000;
-  err=OCIAttrSet(d_handle, (ub4) OCI_HTYPE_STMT,
-                 (dvoid*) &prefetch, (ub4) sizeof(ub4),
-                 (ub4) OCI_ATTR_PREFETCH_ROWS, d_errorHandle);
+  const std::string& getQuery() {
+    return d_query;
+  }
 
-  if (err) {
-    throw sPerrorException("setting prefetch: "+getOracleError());
+  ~SOracleStatement() { 
+    if (d_stmt)
+      OCIStmtRelease(d_stmt, d_err, d_stmt_key, d_stmt_keysize, OCI_STRLS_CACHE_DELETE);
+    if (d_err) 
+      OCIHandleFree(d_err, OCI_HTYPE_ERROR);
+    if (d_res) {
+      for(int i=0;i<d_fnum;i++)
+        if (d_res[i].content) delete [] d_res[i].content;
+      delete [] d_res;
+    }
+    if (d_bind) {
+      for(int i=0;i<d_parnum;i++) {
+        if (d_bind[i].vals && d_bind[i].release) delete [] (text*)d_bind[i].vals;
+      }
+    }
   }
 
+private:
+  string d_query;
+  OCIEnv *d_ctx;
+  OCISvcCtx *d_svcctx;
+  bool d_dolog;
+  bool d_release_stmt;
+  bool d_init;
+  OCIStmt* d_stmt;
+  OCIError* d_err;
+  int d_parnum;
+  int d_paridx;
+  int d_resnum;
+  int d_residx;
+  int d_fnum;
+  dsword d_queryResult;
+  struct oresult {
+    OCIDefine *handle;
+    char* content;
+    sb4 ind;
+    ub4 colsize;
+  }* d_res;
+  struct obind {
+    OCIBind *handle;
+    ub4 val4;
+    oraub8 val8;
+    void* vals;
+    bool release;
+  }* d_bind;
+
+  sb4 d_non_null_ind;
+  sb4 d_null_ind;
+  text d_stmt_key[64];
+  size_t d_stmt_keysize;
+};
 
-  //  cerr<<"Done preparing '"<<query<<"'"<<endl;
 
-  d_numfields=getNumFields(query);
+SOracle::SOracle(const string &database,
+                 const string &user,
+                 const string &password,
+                 bool releaseStatements,
+                 OCIEnv* oraenv)
+{
+  d_environmentHandle = oraenv;
+  d_errorHandle = NULL;
+  d_serviceContextHandle = NULL;
+  d_release_stmt = releaseStatements;
 
-  for (int n=0; n < d_numfields ; ++n) {
-    //    cerr<<"bind: "<<n<<endl;
-    OCIDefine* theDefineHandle = NULL;
-    err = OCIDefineByPos(d_handle, &theDefineHandle, d_errorHandle, n+1, d_fields[n].content,
-                         sizeof(d_fields[n].content) - 1, SQLT_STR, (dvoid*) &d_fields[n].indicator, NULL, NULL, OCI_DEFAULT);
+  // Allocate an error handle
 
-    if (err) {
-      throw sPerrorException("Error binding returns: "+getOracleError());
-    }
+  int err = OCIHandleAlloc(d_environmentHandle, (dvoid**) &d_errorHandle, OCI_HTYPE_ERROR, 0, NULL);
+  if (err) {
+    throw sPerrorException("OCIHandleAlloc(errorHandle)");
   }
 
-  //  cerr<<"Done binding fields"<<endl;
-
-  d_queryResult = OCIStmtExecute(d_serviceContextHandle, d_handle, d_errorHandle, 1, 0,
-                                 (OCISnapshot*)NULL, (OCISnapshot*) NULL, OCI_DEFAULT);
-
-  if (d_queryResult != OCI_SUCCESS && d_queryResult != OCI_SUCCESS_WITH_INFO && d_queryResult != OCI_NO_DATA) {
-    throw sPerrorException("executing oracle query: "+getOracleError());
+  err = OCILogon2(d_environmentHandle, d_errorHandle, &d_serviceContextHandle, (OraText*)user.c_str(), user.size(), 
+                 (OraText*) password.c_str(),  strlen(password.c_str()), (OraText*) database.c_str(), strlen(database.c_str()), OCI_LOGON2_STMTCACHE);
+  // increase statement cache to 100
+  if (err) {
+    throw sPerrorException("OCILogon2");
   }
 
-  //  cerr<<"Done executing: "<<d_queryResult<<endl;
-
-
+  ub4 cacheSize = 100;
+  err = OCIAttrSet(d_serviceContextHandle, OCI_HTYPE_SVCCTX, &cacheSize, sizeof(ub4), OCI_ATTR_STMTCACHESIZE, d_errorHandle);
+  if (err) {
+    throw sPerrorException("OCIAttrSet(stmtcachesize)");
+  }
 
-  return 0;
 }
 
-int SOracle::doQuery(const string &query, result_t &result)
+void SOracle::setLog(bool state)
 {
-  result.clear();
-  doQuery(query);
-
-  row_t row;
-  while (getRow(row)) {
-    result.push_back(row);
-  }
-
-  return result.size();
+  s_dolog=state;
 }
 
-bool SOracle::getRow(row_t &row)
+SOracle::~SOracle()
 {
-  row.clear();
-
-  if (d_queryResult == OCI_NO_DATA) {
-    OCIHandleFree(d_handle, OCI_HTYPE_STMT);
-    d_handle=0;
-    return false;
-  } else {
-    for (int n=0; n < d_numfields ; ++n)
-      if (!d_fields[n].indicator) {
-        row.push_back(d_fields[n].content);
-      } else {
-        row.push_back("");
-      }
+  int err;
+  if (d_serviceContextHandle != NULL) {
+    err=OCILogoff(d_serviceContextHandle, d_errorHandle);
+    if (err) {
+      L<<Logger::Warning<<"Problems logging out: "+getOracleError()<<endl;
+    }
   }
 
-  d_queryResult = OCIStmtFetch(d_handle, d_errorHandle, 1, 0, 0);
-  if (d_queryResult != OCI_SUCCESS && d_queryResult != OCI_SUCCESS_WITH_INFO && d_queryResult != OCI_NO_DATA) {
-    throw sPerrorException("fetching next row of oracle query: "+getOracleError());
+  if (d_errorHandle != NULL) {
+    OCIHandleFree(d_errorHandle, OCI_HTYPE_ERROR);
+    d_errorHandle = NULL;
   }
-
-
-  return true;
 }
 
-string SOracle::escape(const string &name)
+SSqlException SOracle::sPerrorException(const string &reason)
 {
-  string a;
+  return SSqlException(reason);
+}
 
-  for (string::const_iterator i=name.begin(); i!=name.end(); ++i) {
-    if (*i=='\\' || *i=='\'') {
-      a+='\\';
-    }
-    a+=*i;
-  }
-  return a;
+SSqlStatement* SOracle::prepare(const string& query, int nparams) {
+  return new SOracleStatement(query, s_dolog, nparams, d_environmentHandle, d_serviceContextHandle, d_release_stmt);
 }
 
-#if 0
+void SOracle::execute(const string& query) {
+  SOracleStatement(query, s_dolog, 0, d_environmentHandle, d_serviceContextHandle, true).execute();
+}
 
-int main(int argc, char** argv)
+string SOracle::getOracleError()
 {
-  for (int outer=0; outer<2; ++outer) {
-    try {
-      SOracle s(argv[1],"",0,"",argv[2],argv[3]);
-
-      cerr<<"Ready to do queries"<<endl;
-      time_t then=time(0);
-
-      int loops;
-      for (loops=0; loops < 6; ++loops) {
-        s.doQuery("select id, content from records");
-
-        SSql::row_t row;
-
-        while (s.getRow(row)) {
-          for (SSql::row_t::const_iterator j=row.begin(); j!=row.end(); ++j) {
-            cout <<"'"<< *j<<"', ";
-          }
-          cout<<"\n";
+  string mReason = "ORA-UNKNOWN";
+  if (d_errorHandle != NULL) {
+    text  msg[512];
+    sb4   errcode = 0;
+    memset(msg, 0, 512);
+    OCIErrorGet((dvoid*) d_errorHandle,1, NULL, &errcode, msg, sizeof(msg), OCI_HTYPE_ERROR);
+    if (errcode) {
+      char* p = (char*) msg;
+      while (*p++ != 0x00) {
+        if (*p == '\n' || *p == '\r') {
+          *p = ';';
         }
       }
-      time_t spent=time(0)-then;
-      if (spent) {
-        cerr<<"Loops per second: "<< loops/spent<<endl;
-      }
-    } catch (string &e) {
-      cerr<<"fatal: "<<e<<endl;
-    } catch (SSqlException &e) {
-      cerr<<e.txtReason()<<endl;
-    }
-  }
+      mReason = (char*) msg;
+     }
+   }
+  return mReason;
 }
-
-#endif
index 4c9579a8c46cb21fa795907473631296ac0a526a..689cb7403f02e22dce75cc4088bf558b5f4162f1 100644 (file)
@@ -7,6 +7,7 @@
 #include "pdns/backends/gsql/ssql.hh"
 #include "pdns/utility.hh"
 #include <oci.h>
+#include <oratypes.h>
 
 #ifndef dsword
 typedef sb4 dsword;
@@ -17,36 +18,28 @@ class SOracle : public SSql
 public:
   SOracle(const string &database,
           const string &user="",
-          const string &password="");
+          const string &password="", 
+          bool releaseStatements=false,
+          OCIEnv* oraenv=NULL);
 
   ~SOracle();
 
   SSqlException sPerrorException(const string &reason);
-  int doQuery(const string &query, result_t &result);
-  int doQuery(const string &query);
-  int doCommand(const string &query);
-  bool getRow(row_t &row);
-  string escape(const string &str);
   void setLog(bool state);
+  SSqlStatement* prepare(const string& query, int nparams);
+  void execute(const string& query);
+
+  void startTransaction() {}
+  void commit() {}
+  void rollback() {}
 private:
   OCIEnv*    d_environmentHandle;
   OCIError*  d_errorHandle;
   OCISvcCtx* d_serviceContextHandle;
-  OCIStmt*   d_statementHandles[10];
-
-  struct oresult {
-    char content[4000];
-    sb2 indicator;
-  } d_fields[10];
-  OCIStmt* d_handle;
-
-  dsword d_queryResult;
 
   string getOracleError();
-
   static bool s_dolog;
-  int d_numfields;
-  //  int getNumFields(const string& query);
+  bool d_release_stmt;
 };
 
 #endif /* SSORACLE_HH */
index b1282c16d2b055d6d1dabc95e4e7935dc0098593..cf3d8c8c4a6ba0b4645ec3d6d82d5c8f278bca90 100644 (file)
@@ -46,77 +46,77 @@ public:
 
     string record_query = "SELECT content,ttl,prio,type,domain_id,disabled::int,name,auth::int FROM records WHERE";
 
-    declare(suffix, "basic-query", "Basic query", record_query+" disabled=false and type='%s' and name=E'%s'");
-    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=false and type='%s' and name=E'%s' and domain_id=%d");
-    declare(suffix, "any-query", "Any query", record_query+" disabled=false and name=E'%s'");
-    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=false and name=E'%s' and domain_id=%d");
+    declare(suffix, "basic-query", "Basic query", record_query+" disabled=false and type=$1 and name=$2");
+    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=false and type=$1 and name=$2 and domain_id=$3");
+    declare(suffix, "any-query", "Any query", record_query+" disabled=false and name=$1");
+    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=false and name=$1 and domain_id=$2");
 
-    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=false OR %d::bool) and domain_id='%d' order by name, type");
-    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=false and (name=E'%s' OR name like E'%s') and domain_id='%d'");
+    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=false OR $1) and domain_id=$2 order by name, type");
+    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=false and (name=$1 OR name like $2) and domain_id=$3");
 
-    declare(suffix,"remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id='%d' and type is null");
-    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (domain_id,name,type,disabled,auth) values ('%d','%s',null,false,true)");
-    declare(suffix,"delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id='%d' and name='%s' and type is null");
+    declare(suffix,"remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id=$1 and type is null");
+    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (domain_id,name,type,disabled,auth) values ($1,$2,null,false,true)");
+    declare(suffix,"delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id=$1 and name=$2 and type is null");
 
-    declare(suffix,"master-zone-query","Data", "select master from domains where name=E'%s' and type='SLAVE'");
+    declare(suffix,"master-zone-query","Data", "select master from domains where name=$1 and type='SLAVE'");
 
-    declare(suffix,"info-zone-query","","select id,name,master,last_check,notified_serial,type from domains where name=E'%s'");
+    declare(suffix,"info-zone-query","","select id,name,master,last_check,notified_serial,type from domains where name=$1");
 
     declare(suffix,"info-all-slaves-query","","select id,name,master,last_check,type from domains where type='SLAVE'");
-    declare(suffix,"supermaster-query","", "select account from supermasters where ip='%s' and nameserver=E'%s'");
-    declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=E'%s' and account=E'%s'");
-
-    declare(suffix,"insert-zone-query","", "insert into domains (type,name) values('NATIVE',E'%s')");
-    declare(suffix,"insert-slave-query","", "insert into domains (type,name,master,account) values('SLAVE',E'%s',E'%s',E'%s')");
-
-    declare(suffix, "insert-record-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,auth) values (E'%s',%d,%d,'%s',%d,%d::bool,E'%s','%d')");
-    declare(suffix, "insert-record-order-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (E'%s',%d,%d,'%s',%d,%d::bool,E'%s',E'%s','%d')");
-    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,auth) values (null,'%d',false,E'%s','%d')");
-    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,ordername,auth) values (null,'%d',false,E'%s',E'%s','%d')");
-
-    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, last", "select ordername, name from records where disabled=false and domain_id=%d and ordername is not null order by 1 using ~<~ limit 1");
-    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select ordername, name from records where disabled=false and ordername ~<=~ E'%s' and domain_id=%d and ordername is not null order by 1 using ~>~ limit 1");
-    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select ordername from records where disabled=false and ordername ~>~ E'%s' and domain_id=%d and ordername is not null order by 1 using ~<~ limit 1");
-    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select ordername, name from records where disabled=false and ordername != '' and domain_id=%d and ordername is not null order by 1 using ~>~ limit 1");
-    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername=E'%s',auth=%d::bool where name=E'%s' and domain_id='%d' and disabled=false");
-    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=true where domain_id='%d' and name='%s' and type='DS' and disabled=false");
-
-    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=%d::bool where domain_id='%d' and name='%s' and disabled=false");
-    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=false where name=E'%s' and type=E'%s' and domain_id='%d' and disabled=false");
-
-    declare(suffix,"update-master-query","", "update domains set master='%s' where name='%s'");
-    declare(suffix,"update-kind-query","", "update domains set type='%s' where name='%s'");
-    declare(suffix,"update-serial-query","", "update domains set notified_serial=%d where id=%d");
-    declare(suffix,"update-lastcheck-query","", "update domains set last_check=%d where id=%d");
-    declare(suffix,"zone-lastchange-query", "", "select max(change_date) from records where domain_id=%d");
+    declare(suffix,"supermaster-query","", "select account from supermasters where ip=$1 and nameserver=$2");
+    declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=$1 and account=$2");
+
+    declare(suffix,"insert-zone-query","", "insert into domains (type,name) values('NATIVE',$1)");
+    declare(suffix,"insert-slave-query","", "insert into domains (type,name,master,account) values('SLAVE',$1,$2,$3)");
+
+    declare(suffix, "insert-record-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,auth) values ($1,$2,$3,$4,$5,$6,$7,$8)");
+    declare(suffix, "insert-record-order-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values ($1,$2,$3,$4,$5,$6,$7,$8,$9)");
+    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,auth) values (null,$1,false,$2,$3)");
+    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,ordername,auth) values (null,$1,false,$2,$3,$4)");
+
+    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, last", "select ordername, name from records where disabled=false and domain_id=$1 and ordername is not null order by 1 using ~<~ limit 1");
+    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select ordername, name from records where disabled=false and ordername ~<=~ $1 and domain_id=$2 and ordername is not null order by 1 using ~>~ limit 1");
+    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select ordername from records where disabled=false and ordername ~>~ $1 and domain_id=$2 and ordername is not null order by 1 using ~<~ limit 1");
+    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select ordername, name from records where disabled=false and ordername != '' and domain_id=$1 and ordername is not null order by 1 using ~>~ limit 1");
+    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername=$1,auth=$2 where name=$3 and domain_id=$4 and disabled=false");
+    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=true where domain_id=$1 and name=$2 and type='DS' and disabled=false");
+
+    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=$1 where domain_id=$2 and name=$3 and disabled=false");
+    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=false where name=$1 and type=$2 and domain_id=$3 and disabled=false");
+
+    declare(suffix,"update-master-query","", "update domains set master=$1 where name=$2");
+    declare(suffix,"update-kind-query","", "update domains set type=$1 where name=$2");
+    declare(suffix,"update-serial-query","", "update domains set notified_serial=$1 where id=$2");
+    declare(suffix,"update-lastcheck-query","", "update domains set last_check=$1 where id=$2");
+    declare(suffix,"zone-lastchange-query", "", "select max(change_date) from records where domain_id=$1");
     declare(suffix,"info-all-master-query","", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
-    declare(suffix,"delete-domain-query","", "delete from domains where name=E'%s'");
-    declare(suffix,"delete-zone-query","", "delete from records where domain_id=%d");
-    declare(suffix,"delete-rrset-query","","delete from records where domain_id=%d and name=E'%s' and type=E'%s'");
-    declare(suffix,"delete-names-query","","delete from records where domain_id=%d and name=E'%s'");
-
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, %d, (%d = 1), '%s' from domains where name=E'%s'");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, case when active then 1 else 0 end as active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=E'%s'");
-    declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=E'%s'");
-    declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=E'%s' and domainmetadata.kind=E'%s'");
-    declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=E'%s') and domainmetadata.kind=E'%s'");
-    declare(suffix,"clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=E'%s')");
-    declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, '%s', '%s' from domains where name=E'%s'");
-    declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=true where domain_id=(select id from domains where name=E'%s') and  cryptokeys.id=%d");
-    declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=false where domain_id=(select id from domains where name=E'%s') and  cryptokeys.id=%d");
-    declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=E'%s') and cryptokeys.id=%d");    
-    declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=E'%s')");
-    declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=E'%s'");
-    declare(suffix,"set-tsig-key-query","", "insert into tsigkeys (name,algorithm,secret) values('%s','%s','%s')");
-    declare(suffix,"delete-tsig-key-query","", "delete from tsigkeys where name='%s'");
+    declare(suffix,"delete-domain-query","", "delete from domains where name=$1");
+    declare(suffix,"delete-zone-query","", "delete from records where domain_id=$1");
+    declare(suffix,"delete-rrset-query","","delete from records where domain_id=$1 and name=$2 and type=$3");
+    declare(suffix,"delete-names-query","","delete from records where domain_id=$1 and name=$2");
+
+    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, $1, $2, $3 from domains where name=$4");
+    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, case when active then 1 else 0 end as active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=$1");
+    declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=$1");
+    declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=$1 and domainmetadata.kind=$2");
+    declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=$1) and domainmetadata.kind=$2");
+    declare(suffix,"clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=$1)");
+    declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, $1, $2 from domains where name=$3");
+    declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=true where domain_id=(select id from domains where name=$1) and  cryptokeys.id=$2");
+    declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=false where domain_id=(select id from domains where name=$1) and  cryptokeys.id=$2");
+    declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=$1) and cryptokeys.id=$2");    
+    declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=$1)");
+    declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=$1");
+    declare(suffix,"set-tsig-key-query","", "insert into tsigkeys (name,algorithm,secret) values($1,$2,$3)");
+    declare(suffix,"delete-tsig-key-query","", "delete from tsigkeys where name=$1");
     declare(suffix,"get-tsig-keys-query","", "select name,algorithm, secret from tsigkeys");
 
-    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=false OR %d::bool");
+    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=false OR $1");
 
-    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=%d");
-    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES (%d, E'%s', E'%s', %d, E'%s', E'%s')");
-    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=%d AND name=E'%s' AND type=E'%s'");
-    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=%d");
+    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=$1");
+    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES ($1, $2, $3, $4, $5, $6)");
+    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=$1 AND name=$2 AND type=$3");
+    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=$1");
   }
 
   DNSBackend *make(const string &suffix="")
index 84bff3cccb64ea7e79f48c3c783172892e7e5185..f30a3dadd4250dd60b1dad6d08155001c9a3b479 100644 (file)
@@ -7,6 +7,150 @@
 #include "pdns/logger.hh"
 #include "pdns/dns.hh"
 #include "pdns/namespaces.hh"
+#include <algorithm>
+#include <boost/foreach.hpp>
+
+class SPgSQLStatement: public SSqlStatement
+{
+public:
+  SPgSQLStatement(const string& query, bool dolog, int nparams, PGconn* db) {
+    struct timeval tv;
+
+    d_query = query;
+    d_dolog = dolog;
+    d_db = db;
+
+    // prepare a statement
+    gettimeofday(&tv,NULL);
+    this->d_stmt = string("stmt") + boost::lexical_cast<string>(tv.tv_sec) + boost::lexical_cast<string>(tv.tv_usec);
+
+    d_nparams = nparams;
+    PGresult* res = PQprepare(d_db, d_stmt.c_str(), d_query.c_str(), d_nparams, NULL);
+    ExecStatusType status = PQresultStatus(res);
+    string errmsg(PQresultErrorMessage(res));
+    PQclear(res);
+    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
+      throw SSqlException("Fatal error during prepare: " + errmsg);
+    } 
+    paramValues=NULL;
+    d_paridx=d_residx=d_resnum=0;
+    paramLengths=NULL;
+    d_res=NULL;
+  }
+
+  SSqlStatement* bind(const string& name, bool value) { return bind(name, string(value ? "t" : "f")); }
+  SSqlStatement* bind(const string& name, int value) { return bind(name, boost::lexical_cast<string>(value)); }
+  SSqlStatement* bind(const string& name, uint32_t value) { return bind(name, boost::lexical_cast<string>(value)); }
+  SSqlStatement* bind(const string& name, long value) { return bind(name, boost::lexical_cast<string>(value)); }
+  SSqlStatement* bind(const string& name, unsigned long value) { return bind(name, boost::lexical_cast<string>(value)); }
+  SSqlStatement* bind(const string& name, long long value) { return bind(name, boost::lexical_cast<string>(value)); }
+  SSqlStatement* bind(const string& name, unsigned long long value) { return bind(name, boost::lexical_cast<string>(value)); }
+  SSqlStatement* bind(const string& name, const std::string& value) {
+    allocate();
+    if (d_paridx>=d_nparams) 
+      throw SSqlException("Attempt to bind more parameters than query has");
+    paramValues[d_paridx] = new char[value.size()+1];
+    memset(paramValues[d_paridx], 0, sizeof(char)*(value.size()+1));
+    value.copy(paramValues[d_paridx], value.size());
+    paramLengths[d_paridx] = value.size();
+    d_paridx++;
+    return this;
+  }
+  SSqlStatement* bindNull(const string& name) { d_paridx++; return this; } // these are set null in allocate()
+  SSqlStatement* execute() {
+    if (d_dolog) {
+      L<<Logger::Warning<<"Query: "<<d_query<<endl;
+    }
+    d_res = PQexecPrepared(d_db, d_stmt.c_str(), d_nparams, paramValues, paramLengths, NULL, 0);
+    ExecStatusType status = PQresultStatus(d_res);
+    string errmsg(PQresultErrorMessage(d_res));
+    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
+      string errmsg(PQresultErrorMessage(d_res));
+      PQclear(d_res);
+      d_res = NULL;
+      throw SSqlException("Fatal error during query: " + errmsg);
+    }
+    d_resnum = PQntuples(d_res);
+    return this;
+  }
+
+  bool hasNextRow() 
+  {
+    return d_residx<d_resnum;
+  }
+
+  SSqlStatement* nextRow(row_t& row) {
+    int i;
+    row.clear();
+    if (d_residx>=d_resnum || !d_res) return this;
+    row.reserve(PQnfields(d_res));
+    for(i=0;i<PQnfields(d_res);i++) {
+      if (PQgetisnull(d_res, d_residx, i)) {
+        row.push_back("");
+      } else {
+        row.push_back(string(PQgetvalue(d_res, d_residx, i)));
+      }
+    }
+    d_residx++;
+    if (d_residx >= d_resnum) {
+      PQclear(d_res);
+      d_res = NULL;
+    }
+    return this;
+  }
+
+  SSqlStatement* getResult(result_t& result) {
+    result.clear();
+    if (d_res == NULL) return this;
+    result.reserve(d_resnum);
+    row_t row;
+    while(hasNextRow()) { nextRow(row); result.push_back(row); }
+    return this;
+  }
+
+  SSqlStatement* reset() {
+     int i;
+     if (d_res) 
+       PQclear(d_res);
+     d_res = NULL;
+     d_paridx = d_residx = d_resnum = 0;
+     if (paramValues) 
+       for(i=0;i<d_nparams;i++) 
+         if (paramValues[i]) delete [] paramValues[i];
+     delete [] paramValues;
+     paramValues = NULL;
+     delete [] paramLengths;
+     paramLengths = NULL;
+     return this;
+  }
+
+  const std::string& getQuery() { return d_query; }
+
+  ~SPgSQLStatement() {
+    reset();
+  }
+private:
+  void allocate() {
+     if (paramValues != NULL) return;
+     paramValues = new char*[d_nparams];
+     paramLengths = new int[d_nparams];
+     memset(paramValues, 0, sizeof(char*)*d_nparams);
+     memset(paramLengths, 0, sizeof(int)*d_nparams);
+  }
+
+  string d_query;
+  string d_stmt;
+  PGconn *d_db;
+  PGresult *d_res;
+  bool d_dolog;
+  int d_nparams;
+  int d_paridx;
+  char **paramValues;
+  int *paramLengths;
+  int d_residx;
+  int d_resnum;
+};
 
 bool SPgSQL::s_dolog;
 
@@ -35,28 +179,6 @@ SPgSQL::SPgSQL(const string &database, const string &host, const string& port, c
     d_connectstr+=" password="+password;
   }
   
-  ensureConnect();
-}
-
-void SPgSQL::setLog(bool state)
-{
-  s_dolog=state;
-}
-
-SPgSQL::~SPgSQL()
-{
-  PQfinish(d_db);
-}
-
-SSqlException SPgSQL::sPerrorException(const string &reason)
-{
-  return SSqlException(reason+string(": ")+(d_db ? PQerrorMessage(d_db) : "no connection"));
-}
-
-void SPgSQL::ensureConnect()
-{
-  if(d_db)
-    PQfinish(d_db);
   d_db=PQconnectdb(d_connectstr.c_str());
 
   if (!d_db || PQstatus(d_db)==CONNECTION_BAD) {
@@ -72,114 +194,45 @@ void SPgSQL::ensureConnect()
   }
 }
 
-int SPgSQL::doCommand(const string &query)
+void SPgSQL::setLog(bool state)
 {
-  if(s_dolog)
-    L<<Logger::Warning<<"Command: "<<query<<endl;
-
-  bool first = true;
-
-  retry:
-  
-  if(!(d_result=PQexec(d_db,query.c_str())) || PQresultStatus(d_result)!=PGRES_COMMAND_OK) { 
-    string error("unknown reason");
-    if(d_result) {
-      error=PQresultErrorMessage(d_result);
-      PQclear(d_result);
-    }
-    
-    if(PQstatus(d_db)==CONNECTION_BAD) {
-      ensureConnect();
-      if(first) {
-        first = false;
-        goto retry;
-      }
-    }
-    
-    throw SSqlException("PostgreSQL failed to execute command: "+error); 
-  }
-  if(d_result)
-    PQclear(d_result);
-  d_count=0;
-  return 0;
+  s_dolog=state;
 }
 
-
-int SPgSQL::doQuery(const string &query)
+SPgSQL::~SPgSQL()
 {
-  if(s_dolog)
-    L<<Logger::Warning<<"Query: "<<query<<endl;
-
-  bool first = true;
-retry:
-  if(!(d_result=PQexec(d_db,query.c_str())) || PQresultStatus(d_result)!=PGRES_TUPLES_OK) {
-    string error("unknown reason");
-    if(d_result) {
-      error=PQresultErrorMessage(d_result);
-      PQclear(d_result);
-    }
-    if(PQstatus(d_db)==CONNECTION_BAD) {
-      ensureConnect();
-      if(first) {
-        first = false;
-        goto retry;
-      }
-    }
-
-    throw SSqlException("PostgreSQL failed to execute command: "+error); 
-  }
+  PQfinish(d_db);
+}
 
-  d_count=0;
-  return 0;
+SSqlException SPgSQL::sPerrorException(const string &reason)
+{
+  return SSqlException(reason+string(": ")+(d_db ? PQerrorMessage(d_db) : "no connection"));
 }
 
-int SPgSQL::doQuery(const string &query, result_t &result)
+void SPgSQL::execute(const string& query) 
 {
-  result.clear();
-  if(s_dolog)
-    L<<Logger::Warning<<"Query: "<<query<<endl;
-
-  if(!(d_result=PQexec(d_db,query.c_str())) || PQresultStatus(d_result)!=PGRES_TUPLES_OK) {
-    string error("unknown reason");
-    if(d_result) {
-      error=PQresultErrorMessage(d_result);
-      PQclear(d_result);
-    }
-    throw SSqlException("PostgreSQL failed to execute command: "+error); 
+  PGresult* res = PQexec(d_db, query.c_str());
+  ExecStatusType status = PQresultStatus(res);
+  string errmsg(PQresultErrorMessage(res));
+  PQclear(res);
+  if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
+    throw sPerrorException("Fatal error during query: " + errmsg);
   }
-
-  d_count=0;
-
-  row_t row;
-  while(getRow(row))
-    result.push_back(row);
-
-  return result.size();
 }
 
-bool SPgSQL::getRow(row_t &row)
+SSqlStatement* SPgSQL::prepare(const string& query, int nparams) 
 {
-  row.clear();
+  return new SPgSQLStatement(query, s_dolog, nparams, d_db);
+}
 
-  if(d_count >= PQntuples(d_result)) {
-    PQclear(d_result);
-    return false;
-  }
-  
-  for(int i=0;i<PQnfields(d_result);i++)
-    row.push_back(PQgetvalue(d_result,d_count,i) ?: "");
-  d_count++;
-  return true;
+void SPgSQL::startTransaction() {
+  execute("begin");
 }
 
-string SPgSQL::escape(const string &name)
-{
-  string a;
+void SPgSQL::commit() {
+  execute("commit");
+}
 
-  for(string::const_iterator i=name.begin();i!=name.end();++i) {
-    if(*i=='\'' || *i=='\\')
-      a+='\\';
-    a+=*i;
-  }
-  return a;
+void SPgSQL::rollback() {
+  execute("rollback");
 }
index 1510d3c625d971e7a9350e2b7c4856252bd8bf35..6068bbc16ef031a20bf6f3dc1f2f344aee7d27ca 100644 (file)
@@ -15,19 +15,18 @@ public:
   ~SPgSQL();
   
   SSqlException sPerrorException(const string &reason);
-  int doQuery(const string &query, result_t &result);
-  int doQuery(const string &query);
-  int doCommand(const string &query);
-  bool getRow(row_t &row);
-  string escape(const string &str);    
   void setLog(bool state);
+  SSqlStatement* prepare(const string& query, int nparams);
+  void execute(const string& query);
+
+  void startTransaction();
+  void rollback();
+  void commit();
+
 private:
-  void ensureConnect();
   PGconn* d_db; 
   string d_connectstr;
   string d_connectlogstr;
-  PGresult* d_result;
-  int d_count;
   static bool s_dolog;
 };
       
index 7e885c725ef3539a44378b56260f906a8c3ef951..33aab48314ea06bb84c3f43809e5093b1cae4c2b 100644 (file)
@@ -27,17 +27,13 @@ gSQLite3Backend::gSQLite3Backend( const std::string & mode, const std::string &
   try
   {
     SSQLite3 *ptr = new SSQLite3( getArg( "database" ));
-    setDB( ptr);
+    setDB(ptr);
     if(!getArg("pragma-synchronous").empty()) {
-      SSQLite3::result_t res;
-      ptr->doQuery("PRAGMA synchronous="+getArg("pragma-synchronous"), res);
+      ptr->execute("PRAGMA synchronous="+getArg("pragma-synchronous"));
     }
-    if(mustDo("pragma-foreign-keys")) {
-      SSQLite3::result_t res;
-      ptr->doQuery("PRAGMA foreign_keys = ON", res);
-    }
-  }
-  catch( SSqlException & e )
+    ptr->execute("PRAGMA foreign_keys = 1");
+  }  
+  catch( SSqlException & e ) 
   {
     L << Logger::Error << mode << ": connection failed: " << e.txtReason() << std::endl;
     throw PDNSException( "Unable to launch " + mode + " connection: " + e.txtReason());
@@ -59,85 +55,85 @@ public:
   //! Declares all needed arguments.
   void declareArguments( const std::string & suffix = "" )
   {
-    declare( suffix, "database", "Filename of the SQLite3 database", "powerdns.sqlite" );
-    declare( suffix, "pragma-synchronous", "Set this to 0 for blazing speed", "" );
-    declare( suffix, "pragma-foreign-keys", "Enable foreign key constraints", "no" );
+    declare(suffix, "database", "Filename of the SQLite3 database", "powerdns.sqlite");
+    declare(suffix, "pragma-synchronous", "Set this to 0 for blazing speed", "");
+    declare(suffix, "pragma-foreign-keys", "Enable foreign key constraints", "no" );
 
     declare(suffix, "dnssec", "Enable DNSSEC processing","no");
 
     string record_query = "SELECT content,ttl,prio,type,domain_id,disabled,name,auth FROM records WHERE";
 
-    declare(suffix, "basic-query", "Basic query", record_query+" disabled=0 and type='%s' and name='%s'");
-    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=0 and type='%s' and name='%s' and domain_id=%d");
-    declare(suffix, "any-query", "Any query", record_query+" disabled=0 and name='%s'");
-    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=0 and name='%s' and domain_id=%d");
+    declare(suffix, "basic-query", "Basic query", record_query+" disabled=0 and type=:qtype and name=:qname");
+    declare(suffix, "id-query", "Basic with ID query", record_query+" disabled=0 and type=:qtype and name=:qname and domain_id=:domain_id");
+    declare(suffix, "any-query", "Any query", record_query+" disabled=0 and name=:qname");
+    declare(suffix, "any-id-query", "Any with ID query", record_query+" disabled=0 and name=:qname and domain_id=:domain_id");
 
-    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=0 OR %d) and domain_id='%d' order by name, type");
-    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=0 and (name='%s' OR name like '%s') and domain_id=%d");
+    declare(suffix, "list-query", "AXFR query", record_query+" (disabled=0 OR :include_disabled) and domain_id=:domain_id order by name, type");
+    declare(suffix, "list-subzone-query", "Subzone listing", record_query+" disabled=0 and (name=:zone OR name like :wildzone) and domain_id=:domain_id");
 
-    declare(suffix, "remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id='%d' and type is null");
-    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (domain_id,name,type,disabled,auth) values ('%d','%s',null,0,'1')");
-    declare(suffix, "delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id='%d' and name='%s' and type is null");
+    declare(suffix, "remove-empty-non-terminals-from-zone-query", "remove all empty non-terminals from zone", "delete from records where domain_id=:domain_id and type is null");
+    declare(suffix, "insert-empty-non-terminal-query", "insert empty non-terminal in zone", "insert into records (domain_id,name,type,disabled,auth) values (:domain_id,:qname,null,0,'1')");
+    declare(suffix, "delete-empty-non-terminal-query", "delete empty non-terminal from zone", "delete from records where domain_id=:domain_id and name=:qname and type is null");
     
-    declare( suffix, "master-zone-query", "Data", "select master from domains where name='%s' and type='SLAVE'");
-
-    declare( suffix, "info-zone-query", "","select id,name,master,last_check,notified_serial,type from domains where name='%s'");
-
-    declare( suffix, "info-all-slaves-query", "","select id,name,master,last_check,type from domains where type='SLAVE'");
-    declare( suffix, "supermaster-query", "", "select account from supermasters where ip='%s' and nameserver='%s'");
-    declare( suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver='%s' and account='%s'");
-
-    declare( suffix, "insert-zone-query", "", "insert into domains (type,name) values('NATIVE','%s')");
-    declare( suffix, "insert-slave-query", "", "insert into domains (type,name,master,account) values('SLAVE','%s','%s','%s')");
-
-    declare(suffix, "insert-record-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,auth) values ('%s',%d,%d,'%s',%d,%d,'%s',%d)");
-    declare(suffix, "insert-record-order-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values ('%s',%d,%d,'%s',%d,%d,'%s','%s','%d')");
-    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,auth) values (null,'%d',0,'%s','%d')");
-    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,ordername,auth) values (null,'%d',0,'%s','%s','%d')");
-
-    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, first", "select ordername, name from records where disabled=0 and domain_id=%d and ordername is not null order by 1 asc limit 1");
-    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select ordername, name from records where disabled=0 and ordername <= '%s' and domain_id=%d and ordername is not null order by 1 desc limit 1");
-    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select min(ordername) from records where disabled=0 and ordername > '%s' and domain_id=%d and ordername is not null");
-    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select ordername, name from records where disabled=0 and ordername != '' and domain_id=%d and ordername is not null order by 1 desc limit 1");
-    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername='%s',auth=%d where name='%s' and domain_id='%d' and disabled=0");
-    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=1 where domain_id='%d' and name='%s' and type='DS' and disabled=0");
-
-    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=%d where domain_id='%d' and name='%s' and disabled=0");
-    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=0 where name='%s' and type='%s' and domain_id='%d' and disabled=0");
-
-    declare( suffix, "update-master-query", "", "update domains set master='%s' where name='%s'");
-    declare( suffix, "update-kind-query", "", "update domains set type='%s' where name='%s'");
-    declare( suffix, "update-serial-query", "", "update domains set notified_serial=%d where id=%d");
-    declare( suffix, "update-lastcheck-query", "", "update domains set last_check=%d where id=%d");
-    declare (suffix, "zone-lastchange-query", "", "select max(change_date) from records where domain_id=%d");
-    declare( suffix, "info-all-master-query", "", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
-    declare(suffix,"delete-domain-query","", "delete from domains where name='%s'");
-    declare( suffix, "delete-zone-query", "", "delete from records where domain_id=%d");
-    declare( suffix, "delete-rrset-query", "", "delete from records where domain_id = %d and name='%s' and type='%s'");
-    declare( suffix, "delete-names-query", "", "delete from records where domain_id = %d and name='%s'");
-
-    declare(suffix,"add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, %d, %d, '%s' from domains where name='%s'");
-    declare(suffix,"list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name='%s'");
-    declare(suffix,"get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name='%s'");
-    declare(suffix,"get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name='%s' and domainmetadata.kind='%s'");
-    declare(suffix,"clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name='%s') and domainmetadata.kind='%s'");
-    declare(suffix,"clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name='%s')");
-    declare(suffix,"set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, '%s', '%s' from domains where name='%s'");
-    declare(suffix,"activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name='%s') and  cryptokeys.id=%d");
-    declare(suffix,"deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name='%s') and  cryptokeys.id=%d");
-    declare(suffix,"remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name='%s') and cryptokeys.id=%d");
-    declare(suffix,"clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name='%s')");
-    declare(suffix,"get-tsig-key-query","", "select algorithm, secret from tsigkeys where name='%s'");
-    declare(suffix,"set-tsig-key-query","", "replace into tsigkeys (name,algorithm,secret) values('%s','%s','%s')");
-    declare(suffix,"delete-tsig-key-query","", "delete from tsigkeys where name='%s'");
-    declare(suffix,"get-tsig-keys-query","", "select name,algorithm, secret from tsigkeys");
-
-    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=0 OR %d");
-
-    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=%d");
-    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES (%d, '%s', '%s', %d, '%s', '%s')");
-    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=%d AND name='%s' AND type='%s'");
-    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=%d");
+    declare(suffix, "master-zone-query", "Data", "select master from domains where name=:domain and type='SLAVE'");
+
+    declare(suffix, "info-zone-query", "","select id,name,master,last_check,notified_serial,type from domains where name=:domain");
+
+    declare(suffix, "info-all-slaves-query", "","select id,name,master,last_check,type from domains where type='SLAVE'");
+    declare(suffix, "supermaster-query", "", "select account from supermasters where ip=:ip and nameserver=:nameserver");
+    declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=:nameserver and account=:account");
+
+    declare(suffix, "insert-zone-query", "", "insert into domains (type,name) values('NATIVE',:domain)");
+    declare(suffix, "insert-slave-query", "", "insert into domains (type,name,master,account) values('SLAVE',:domain,:masters,:account)");
+
+    declare(suffix, "insert-record-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,auth) values (:content,:ttl,:priority,:qtype,:domain_id,:disabled,:qname,:auth)");
+    declare(suffix, "insert-record-order-query", "", "insert into records (content,ttl,prio,type,domain_id,disabled,name,ordername,auth) values (:content,:ttl,:priority,:qtype,:domain_id,:disabled,:qname,:ordername,:auth)");
+    declare(suffix, "insert-ent-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,auth) values (null,:domain_id,0,:qname,:auth)");
+    declare(suffix, "insert-ent-order-query", "insert empty non-terminal in zone", "insert into records (type,domain_id,disabled,name,ordername,auth) values (null,:domain_id,0,:qname,:ordername,:auth)");
+
+    declare(suffix, "get-order-first-query", "DNSSEC Ordering Query, first", "select ordername, name from records where disabled=0 and domain_id=:domain_id and ordername is not null order by 1 asc limit 1");
+    declare(suffix, "get-order-before-query", "DNSSEC Ordering Query, before", "select ordername, name from records where disabled=0 and ordername <= :ordername and domain_id=:domain_id and ordername is not null order by 1 desc limit 1");
+    declare(suffix, "get-order-after-query", "DNSSEC Ordering Query, after", "select min(ordername) from records where disabled=0 and ordername > :ordername and domain_id=:domain_id and ordername is not null");
+    declare(suffix, "get-order-last-query", "DNSSEC Ordering Query, last", "select ordername, name from records where disabled=0 and ordername != '' and domain_id=:domain_id and ordername is not null order by 1 desc limit 1");
+    declare(suffix, "set-order-and-auth-query", "DNSSEC set ordering query", "update records set ordername=:ordername,auth=:auth where name=:qname and domain_id=:domain_id and disabled=0");
+    declare(suffix, "set-auth-on-ds-record-query", "DNSSEC set auth on a DS record", "update records set auth=1 where domain_id=:domain_id and name=:qname and type='DS' and disabled=0");
+
+    declare(suffix, "nullify-ordername-and-update-auth-query", "DNSSEC nullify ordername and update auth query", "update records set ordername=NULL,auth=:auth where domain_id=:domain_id and name=:qname and disabled=0");
+    declare(suffix, "nullify-ordername-and-auth-query", "DNSSEC nullify ordername and auth query", "update records set ordername=NULL,auth=0 where name=:qname and type=:qtype and domain_id=:domain_id and disabled=0");
+
+    declare(suffix, "update-master-query", "", "update domains set master=:master where name=:domain");
+    declare(suffix, "update-kind-query", "", "update domains set type=:kind where name=:domain");
+    declare(suffix, "update-serial-query", "", "update domains set notified_serial=:serial where id=:domain_id");
+    declare(suffix, "update-lastcheck-query", "", "update domains set last_check=:last_check where id=:domain_id");
+    declare(suffix, "zone-lastchange-query", "", "select max(change_date) from records where domain_id=:domain_id");
+    declare(suffix, "info-all-master-query", "", "select id,name,master,last_check,notified_serial,type from domains where type='MASTER'");
+    declare(suffix, "delete-domain-query","", "delete from domains where name=:domain");
+    declare(suffix, "delete-zone-query", "", "delete from records where domain_id=:domain_id");
+    declare(suffix, "delete-rrset-query", "", "delete from records where domain_id=:domain_id and name=:qname and type=:qtype");
+    declare(suffix, "delete-names-query", "", "delete from records where domain_id=:domain_id and name=:qname");
+
+    declare(suffix, "add-domain-key-query","", "insert into cryptokeys (domain_id, flags, active, content) select id, :flags,:active, :content from domains where name=:domain");
+    declare(suffix, "list-domain-keys-query","", "select cryptokeys.id, flags, active, content from domains, cryptokeys where cryptokeys.domain_id=domains.id and name=:domain");
+    declare(suffix, "get-all-domain-metadata-query","", "select kind,content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=:domain");
+    declare(suffix, "get-domain-metadata-query","", "select content from domains, domainmetadata where domainmetadata.domain_id=domains.id and name=:domain and domainmetadata.kind=:kind");
+    declare(suffix, "clear-domain-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=:domain) and domainmetadata.kind=:kind");
+    declare(suffix, "clear-domain-all-metadata-query","", "delete from domainmetadata where domain_id=(select id from domains where name=:domain)");
+    declare(suffix, "set-domain-metadata-query","", "insert into domainmetadata (domain_id, kind, content) select id, :kind, :content from domains where name=:domain");
+    declare(suffix, "activate-domain-key-query","", "update cryptokeys set active=1 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
+    declare(suffix, "deactivate-domain-key-query","", "update cryptokeys set active=0 where domain_id=(select id from domains where name=:domain) and  cryptokeys.id=:key_id");
+    declare(suffix, "remove-domain-key-query","", "delete from cryptokeys where domain_id=(select id from domains where name=:domain) and cryptokeys.id=:key_id");
+    declare(suffix, "clear-domain-all-keys-query","", "delete from cryptokeys where domain_id=(select id from domains where name=:domain)");
+    declare(suffix, "get-tsig-key-query","", "select algorithm, secret from tsigkeys where name=:key_name");
+    declare(suffix, "set-tsig-key-query","", "replace into tsigkeys (name,algorithm,secret) values(:key_name,:algorithm,:content)");
+    declare(suffix, "delete-tsig-key-query","", "delete from tsigkeys where name=:key_name");
+    declare(suffix, "get-tsig-keys-query","", "select name,algorithm, secret from tsigkeys");
+
+    declare(suffix, "get-all-domains-query", "Retrieve all domains", "select domains.id, domains.name, records.content, domains.type, domains.master, domains.notified_serial, domains.last_check from domains LEFT JOIN records ON records.domain_id=domains.id AND records.type='SOA' AND records.name=domains.name WHERE records.disabled=0 OR :include_disabled");
+
+    declare(suffix, "list-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE domain_id=:domain_id");
+    declare(suffix, "insert-comment-query", "", "INSERT INTO comments (domain_id, name, type, modified_at, account, comment) VALUES (:domain_id, :qname, :qtype, :modified_at, :account, :content)");
+    declare(suffix, "delete-comment-rrset-query", "", "DELETE FROM comments WHERE domain_id=:domain_id AND name=:qname AND type=:qtype");
+    declare(suffix, "delete-comments-query", "", "DELETE FROM comments WHERE domain_id=:domain_id");
   }
 
   //! Constructs a new gSQLite3Backend object.
@@ -163,12 +159,6 @@ public:
   }
 };
 
-string gSQLite3Backend::sqlEscape(const string &name)
-{
-  return boost::replace_all_copy(name, "'", "''");
-}
-
-
 //! Reports the backendloader to the UeberBackend.
 static gSQLite3Loader gsqlite3loader;
 
index dc1fc415f68355917ebe8a5133b9e1836df18dbf..c58e2624687468280eff0b2cb3cb3750d74cf9ac 100644 (file)
 //! The gSQLiteBackend retrieves it's data from a SQLite database (http://www.sqlite.org/)
 class gSQLite3Backend : public GSQLBackend
 {
-private:
-protected:
 public:
   //! Constructs the backend, throws an exception if it failed..
   gSQLite3Backend( const std::string & mode, const std::string & suffix );
-  virtual string sqlEscape(const string &name);
 };
 
 #endif // GSQLITEBACKEND_HH
index b0953e62769b94c3dee094415aa76f2b654ea343..a46e350d76b566d7e2d8762a7e61723c31b1dc7d 100644 (file)
 static string backendName="[MyDNSbackend]";
 
 MyDNSBackend::MyDNSBackend(const string &suffix) {
-        setArgPrefix("mydns"+suffix);
-
-        try {
-               d_db = new SMySQL(getArg("dbname"),
-                               getArg("host"),
-                               getArgAsNum("port"),
-                               getArg("socket"),
-                               getArg("user"),
-                               getArg("password"));
-
-        }
-        catch(SSqlException &e) {
-               L<<Logger::Error<<backendName<<" Connection failed: "<<e.txtReason()<<endl;
-               throw PDNSException(backendName+"Unable to launch connection: "+e.txtReason());
-        }
-
-        d_rrtable=getArg("rr-table");
-        d_soatable=getArg("soa-table");
-        d_rrwhere=(mustDo("rr-active")?"(active = '1' or active = 'Y') and ":"")+getArg("rr-where");
-        d_soawhere=(mustDo("soa-active")?"(active = '1' or active = 'Y') and ":"")+getArg("soa-where");
-        d_useminimalttl=mustDo("use-minimal-ttl");
-        d_minimum=0;
-
-        L<<Logger::Warning<<backendName<<" Connection successful"<<endl;
+  setArgPrefix("mydns"+suffix);
+  d_domainIdQuery_stmt = NULL;
+  d_domainNoIdQuery_stmt = NULL;
+  d_listQuery_stmt = NULL;
+  d_soaQuery_stmt = NULL;
+  d_basicQuery_stmt = NULL;
+  d_anyQuery_stmt = NULL;
+  d_query_stmt = NULL;
+
+  try {
+    d_db = new SMySQL(getArg("dbname"),
+      getArg("host"),
+      getArgAsNum("port"),
+      getArg("socket"),
+      getArg("user"),
+      getArg("password"));
+    d_db->setLog(::arg().mustDo("query-logging"));
+  }
+  catch(SSqlException &e) {
+    L<<Logger::Error<<backendName<<" Connection failed: "<<e.txtReason()<<endl;
+    throw PDNSException(backendName+"Unable to launch connection: "+e.txtReason());
+  }
+
+  string rrtable=getArg("rr-table");
+  string soatable=getArg("soa-table");
+  string rrwhere=(mustDo("rr-active")?"(active = '1' or active = 'Y') and ":"")+getArg("rr-where");
+  string soawhere=(mustDo("soa-active")?"(active = '1' or active = 'Y') and ":"")+getArg("soa-where");
+
+  if (soatable.empty()) { throw PDNSException("SOA Table must not be empty"); }
+  if (rrtable.empty()) { throw PDNSException("Records table must not be empty"); }
+
+  d_useminimalttl=mustDo("use-minimal-ttl");
+  d_minimum=0;
+
+  L<<Logger::Warning<<backendName<<" Connection successful"<<endl;
+
+  try {
+
+    string domainIdQuery = "SELECT origin, minimum FROM `"+soatable+"` WHERE id = ?";
+    string domainNoIdQuery = "SELECT id, origin, minimum FROM `"+soatable+"` WHERE origin = ?";
+    string soaQuery = "SELECT id, mbox, serial, ns, refresh, retry, expire, minimum, ttl FROM `"+soatable+"` WHERE origin = ?";
+
+    if (!soawhere.empty()) {
+      domainIdQuery += " AND " + soawhere;  
+      domainNoIdQuery += " AND " + soawhere;
+      soaQuery += " AND "+soawhere;
+    }
+
+    d_domainIdQuery_stmt = d_db->prepare(domainIdQuery, 1);
+    d_domainNoIdQuery_stmt = d_db->prepare(domainNoIdQuery, 1);
+    d_soaQuery_stmt = d_db->prepare(soaQuery, 1);
+  
+    string listQuery = "SELECT type, data, aux, ttl, zone, name FROM `"+rrtable+"` WHERE zone = ?";
+    string basicQuery = "SELECT type, data, aux, ttl, zone FROM `"+rrtable+"` WHERE zone = ? AND (name = ? OR name = ?) AND type = ?";
+    string anyQuery = "(SELECT type, data, aux, ttl, zone FROM `"+rrtable+"` WHERE zone = ? AND (name = ? OR name = ?)";
+   if (!rrwhere.empty()) {
+     listQuery += " AND "+rrwhere;
+     basicQuery += " AND " + rrwhere;
+     anyQuery += " AND " + rrwhere;
+    }
+
+    d_listQuery_stmt = d_db->prepare(listQuery, 1);
+  
+    anyQuery += ") UNION (SELECT 'SOA' AS type, origin AS data, '0' AS aux, ttl, id AS zone FROM `"+soatable+"` WHERE id = ? AND origin = ?";
+
+    if (!soawhere.empty())
+      anyQuery += " AND "+soawhere;
+  
+    basicQuery += " ORDER BY type,aux,data";
+    anyQuery += ") ORDER BY type,aux,data";
+  
+    d_basicQuery_stmt = d_db->prepare(basicQuery, 4);
+    d_anyQuery_stmt = d_db->prepare(anyQuery, 5);
+  } catch (SSqlException &e) {
+    L<<Logger::Error<<"Cannot prepare statements: " << e.txtReason() <<endl;
+    throw PDNSException("Cannot prepare statements: " + e.txtReason());
+  }
 }
 
 MyDNSBackend::~MyDNSBackend() {
-        if (d_db)
-                delete(d_db);
+  delete d_domainIdQuery_stmt;
+  d_domainIdQuery_stmt = NULL;
+  delete d_domainNoIdQuery_stmt;
+  d_domainNoIdQuery_stmt = NULL;
+  delete d_listQuery_stmt;
+  d_listQuery_stmt = NULL;
+  delete d_soaQuery_stmt;
+  d_soaQuery_stmt = NULL;
+  delete d_basicQuery_stmt;
+  d_basicQuery_stmt = NULL;
+  delete d_anyQuery_stmt;
+  d_anyQuery_stmt = NULL;
+  delete(d_db);
 }
 
 
-void MyDNSBackend::Query(const string &query) {
-        try {
-               d_db->doQuery(query);
-        } catch (SSqlException &e) {
-               throw PDNSException("Query failed: "+e.txtReason());
-        }
-}
-
 bool MyDNSBackend::list(const string &target, int zoneId, bool include_disabled) {
-        string query;
-        string sname;
-        SSql::row_t rrow;
-
-        d_db->setLog(::arg().mustDo("query-logging"));
-
-        query = "select origin, minimum from "+d_soatable+" where id = ";
-        query+=itoa(zoneId);
-        if (!d_soawhere.empty())
-                query+= " and "+d_soawhere;
-
-        this->Query(query);
-
-        if(!d_db->getRow(rrow))
-               return false; // No such zone
-        
-        d_origin = rrow[0];
-        if (d_origin[d_origin.length()-1] == '.')
-               d_origin.erase(d_origin.length()-1);
-        d_minimum = atol(rrow[1].c_str());
-
-        while (d_db->getRow(rrow)) {
-               L<<Logger::Warning<<backendName<<" Found more than one matching origin for zone ID: "<<zoneId<<endl;
-        };
-
-        query = "select type, data, aux, ttl, zone, name from "+d_rrtable+" where zone = ";
-        query+=itoa(zoneId);
-        if (!d_rrwhere.empty())
-                query += " and "+d_rrwhere;
-        
-
-        this->Query(query);
-
-        d_qname = "";
-        return true;
-
+  string query;
+  string sname;
+  SSqlStatement::row_t rrow;
+
+  try {
+    d_domainIdQuery_stmt->
+      bind("domain_id", zoneId)->
+      execute()->
+      getResult(d_result)->
+      reset();
+  } 
+  catch (SSqlException &e) {
+    throw PDNSException("MyDNSBackend unable to list domain_id "+itoa(zoneId)+": "+e.txtReason());
+  }
+  
+  if (d_result.empty())
+    return false; // No such zone
+
+  d_origin = d_result[0][0];
+  if (d_origin[d_origin.length()-1] == '.')
+    d_origin.erase(d_origin.length()-1);
+  d_minimum = atol(d_result[0][1].c_str());
+
+  if (d_result.size()>1) {
+    L<<Logger::Warning<<backendName<<" Found more than one matching origin for zone ID: "<<zoneId<<endl;
+  };
+
+  try {
+    d_query_stmt = d_listQuery_stmt;
+    d_query_stmt->
+      bind("domain_id", zoneId)->
+      execute();
+  }
+  catch (SSqlException &e) {
+    throw PDNSException("MyDNSBackend unable to list domain_id "+itoa(zoneId)+": "+e.txtReason());
+  }
+
+  d_qname = "";
+  return true;
 }
 
 bool MyDNSBackend::getSOA(const string& name, SOAData& soadata, DNSPacket*) {
-        string query;
-        SSql::row_t rrow;
-
-        d_db->setLog(::arg().mustDo("query-logging"));
-
-        if (name.empty())
-               return false;
-
-        query = "select id, mbox, serial, ns, refresh, retry, expire, minimum, ttl from "+d_soatable+" where origin = '";
-
-        if (name.find_first_of("'\\")!=string::npos)
-               query+=d_db->escape(name);
-        else
-               query+=name;
-
-        query+=".'";
-        if (! d_soawhere.empty())
-                query += " and "+d_soawhere;
-
-        this->Query(query);
-
-        if(!(d_db->getRow(rrow))) {
-               return false;
-        }
-
-        soadata.qname = name;
-        soadata.domain_id = atol(rrow[0].c_str());
-        soadata.hostmaster = rrow[1];
-        soadata.serial = atol(rrow[2].c_str());
-        soadata.nameserver = rrow[3];
-        soadata.refresh = atol(rrow[4].c_str());
-        soadata.retry = atol(rrow[5].c_str());
-        soadata.expire = atol(rrow[6].c_str());
-        soadata.default_ttl = atol(rrow[7].c_str());
-        soadata.ttl = atol(rrow[8].c_str());
-        if (d_useminimalttl && soadata.ttl < soadata.default_ttl) {
-               soadata.ttl = soadata.default_ttl;
-        }
-        soadata.db = this;
-
-        while (d_db->getRow(rrow)) {
-               L<<Logger::Warning<<backendName<<" Found more than one matching zone for: "+name<<endl;
-        };
-
-        return true;
+  string query;
+  SSqlStatement::row_t rrow;
+
+  if (name.empty())
+    return false;
+
+  string dotname = name+".";
+
+  try {
+    d_soaQuery_stmt->
+      bind("origin", dotname)->
+      execute()->
+      getResult(d_result)->
+      reset();
+  }
+  catch (SSqlException &e) {
+    throw PDNSException("MyDNSBackend unable to get soa for domain "+name+": "+e.txtReason());
+  }
+
+  if (d_result.empty()) return false;
+
+  rrow = d_result[0];
+
+  soadata.qname = name;
+  soadata.domain_id = atol(rrow[0].c_str());
+  soadata.hostmaster = rrow[1];
+  soadata.serial = atol(rrow[2].c_str());
+  soadata.nameserver = rrow[3];
+  soadata.refresh = atol(rrow[4].c_str());
+  soadata.retry = atol(rrow[5].c_str());
+  soadata.expire = atol(rrow[6].c_str());
+  soadata.default_ttl = atol(rrow[7].c_str());
+  soadata.ttl = atol(rrow[8].c_str());
+  if (d_useminimalttl) {
+    soadata.ttl = std::min(soadata.ttl, soadata.default_ttl);
+  }
+  soadata.db = this;
+
+  if (d_result.size()>1) {
+    L<<Logger::Warning<<backendName<<" Found more than one matching zone for: "+name<<endl;
+  };
+
+  return true;
 }
 
 void MyDNSBackend::lookup(const QType &qtype, const string &qname, DNSPacket *p, int zoneId) {
-        string query;
-        string sname;
-        string zoneIdStr = itoa(zoneId);
-        SSql::row_t rrow;
-        bool found = false;
-
-        d_origin = "";
-
-        d_db->setLog(::arg().mustDo("query-logging"));
-
-        if (qname.empty())
-               return;
-
-        // Escape the name, after this point we only want to use it in queries
-        if (qname.find_first_of("'\\")!=string::npos)
-               sname=d_db->escape(qname);
-        else
-               sname = qname;
-        sname += ".";
-
-        if (zoneId < 0) {
-               // First off we need to work out what zone we're working with
-               // MyDNS records aren't always fully qualified, so we need to work out the zone ID.
-               
-               size_t pos;
-               string sdom;
-
-               pos = 0;
-               sdom = sname;
-               while (!sdom.empty() && pos != string::npos) {
-                       query = "select id, origin, minimum from "+d_soatable+" where origin = '"+sdom+"'";
-                        if (!d_soawhere.empty()) 
-                                query += " and "+d_soawhere;
-
-                       this->Query(query);
-                       if(d_db->getRow(rrow)) {
-                                zoneIdStr=rrow[0];
-                               d_origin = rrow[1];
-                               if (d_origin[d_origin.length()-1] == '.')
-                                       d_origin.erase(d_origin.length()-1);
-                               d_minimum = atol(rrow[2].c_str());
-                               found = true;
-                               break;
-                       }
-
-                       pos = sname.find_first_of(".",pos+1);
-                       sdom = sname.substr(pos+1);
-               }
-
-        } else {
-               query = "select origin, minimum from "+d_soatable+" where id = ";
-               query+=zoneIdStr;
-                if (!d_soawhere.empty()) 
-                      query+= " and "+d_soawhere;
-
-               this->Query(query);
-
-               if(!d_db->getRow(rrow)) {
-                       throw PDNSException("lookup() passed zoneId = "+zoneIdStr+" but no such zone!");
-               }
-               
-               found = true;
-               d_origin = rrow[0];
-               if (d_origin[d_origin.length()-1] == '.')
-                       d_origin.erase(d_origin.length()-1);
-               d_minimum = atol(rrow[1].c_str());
-        }
-
-
-        if (found) {
-
-               while (d_db->getRow(rrow)) {
-                       L<<Logger::Warning<<backendName<<" Found more than one matching zone for: "+d_origin<<endl;
-               };
-               // We found the zoneId, so we can work out how to find our rr
-               string host;
-
-               // The host part of the query is the name less the origin
-               if (qname.length() == d_origin.length())
-                       host = "";
-               else
-                       host = qname.substr(0, (qname.length() - d_origin.length())-1);
-
-               if (host.find_first_of("'\\")!=string::npos)
-                       host=d_db->escape(host);
-
-               query = "select type, data, aux, ttl, zone from "+d_rrtable+" where zone = ";
-               query+= zoneIdStr;
-               query += " and (name = '"+host+"' or name = '"+sname+"')";
-
-               if(qtype.getCode()!=255) {  // ANY
-                       query+=" and type='";
-                       query+=qtype.getName();
-                       query+="'";
-
-               }
-                if (!d_rrwhere.empty())
-                        query += " and "+d_rrwhere;
-
-
-                if (qtype.getCode() == 255) {
-                        query += " union select 'SOA' as type, origin as data, '0' as aux, ttl, id as zone from "+d_soatable+" where id= " + zoneIdStr + " and origin = '"+qname+".'";
-                        if (!d_soawhere.empty()) 
-                                query += " and " + d_soawhere;
-                }
-               query += " order by type,aux,data";
-
-               this->Query(query);
-
-               d_qname = qname;
-        }
+  string query;
+  string sname;
+  string zoneIdStr = itoa(zoneId);
+  SSqlStatement::row_t rrow;
+  bool found = false;
+
+  d_origin = "";
+
+  if (qname.empty())
+    return;
+
+  DLOG(L<<Logger::Debug<<"MyDNSBackend::lookup(" << qtype.getName() << "," << qname << ",p," << zoneId << ")" << endl);
+
+  sname = qname;
+  sname += ".";
+
+  if (zoneId < 0) {
+    // First off we need to work out what zone we're working with
+    // MyDNS records aren't always fully qualified, so we need to work out the zone ID.
+
+    size_t pos;
+    string sdom;
+
+    pos = 0;
+    sdom = sname;
+    while (!sdom.empty() && pos != string::npos) {
+      try {
+        d_domainNoIdQuery_stmt->
+          bind("domain", sdom)->
+          execute()->
+          getResult(d_result)->
+          reset();
+      }
+      catch (SSqlException &e) {
+        throw PDNSException("MyDNSBackend unable to lookup "+qname+": "+e.txtReason());
+      }
+
+      if (d_result.empty() == false) {
+        rrow = d_result[0];
+        zoneId = boost::lexical_cast<int>(rrow[0]);
+        d_origin = rrow[1];
+        if (d_origin[d_origin.length()-1] == '.')
+          d_origin.erase(d_origin.length()-1);
+        d_minimum = atol(rrow[2].c_str());
+        found = true;
+        break;
+      }
+
+      pos = sname.find_first_of(".",pos+1);
+      sdom = sname.substr(pos+1);
+    }
+
+  } else {
+    try {
+      d_domainIdQuery_stmt->
+        bind("domain_id", zoneId)->
+        execute()->
+        getResult(d_result)->
+        reset();
+    }
+    catch (SSqlException &e) {
+      throw PDNSException("MyDNSBackend unable to lookup "+qname+": "+e.txtReason());
+    }
+
+    if(d_result.empty()) {
+      throw PDNSException("lookup() passed zoneId = "+zoneIdStr+" but no such zone!");
+    }
+
+    rrow = d_result[0];
+
+    found = true;
+    d_origin = rrow[0];
+    if (d_origin[d_origin.length()-1] == '.')
+      d_origin.erase(d_origin.length()-1);
+    d_minimum = atol(rrow[1].c_str());
+  }
+
+
+  if (found) {
+
+    while (d_result.size()>1) {
+      L<<Logger::Warning<<backendName<<" Found more than one matching zone for: "+d_origin<<endl;
+    };
+    // We found the zoneId, so we can work out how to find our rr
+    string host;
+
+    // The host part of the query is the name less the origin
+    if (qname.length() == d_origin.length())
+      host = "";
+    else
+      host = qname.substr(0, (qname.length() - d_origin.length())-1);
+
+    try {
+
+      if (qtype.getCode()==QType::ANY) {
+        string dotqname = qname+".";
+        d_query_stmt = d_anyQuery_stmt;
+        d_query_stmt->
+          bind("domain_id", zoneId)->
+          bind("host", host)->
+          bind("qname", sname)->
+          bind("domain_id", zoneId)-> // this is because positional arguments
+          bind("qname2", dotqname)->
+          execute();
+      } else {
+        DLOG(L<<Logger::Debug<<"Running d_basicQuery_stmt with " << zoneId << ", " << host << ", " << sname << ", " << qtype.getName() << endl);
+        d_query_stmt = d_basicQuery_stmt;
+        d_query_stmt->
+          bind("domain_id", zoneId)->
+          bind("host", host)->
+          bind("qname", sname)->
+          bind("qtype", qtype.getName())->
+          execute();
+      }
+    }
+    catch (SSqlException &e) {
+      throw PDNSException("MyDNSBackend unable to lookup "+qname+": "+e.txtReason());
+    }
+
+    d_qname = qname;
+  }
 
 }
 
 bool MyDNSBackend::get(DNSResourceRecord &rr) {
-        if (d_origin.empty()) {
-               // This happens if lookup() couldn't find the zone
-               return false;
-        }
-
-        SSql::row_t rrow;
-
-        if(!d_db->getRow(rrow)) {
-               return false;
-        }
-
-        rr.qtype=rrow[0];
-        rr.content = rrow[1];
-
-        if(!d_qname.empty()) {
-               // use this to distinguish between select with 'name' field (list()) and one without
-               rr.qname=d_qname;
-        } else {
-               rr.qname=rrow[5];
-               if (!rr.qname.empty() && rr.qname[rr.qname.length()-1] == '.') {
-                       rr.qname.erase(rr.qname.length()-1); // Fully qualified, nuke the last .
-               } else {
-                       if (!rr.qname.empty())
-                               rr.qname += ".";
-                       rr.qname += d_origin; // Not fully qualified
-               }
-        }
-
-        if (rr.qtype.getCode() == QType::NS || rr.qtype.getCode()==QType::MX ||
-               rr.qtype.getCode() == QType::CNAME || rr.qtype.getCode() == QType::PTR) {
-               if (!rr.content.empty() && rr.content[rr.content.length()-1] == '.') {
-                       if (rr.content.length() > 1)
-                               rr.content.erase(rr.content.length()-1); // Fully qualified, nuke the last .
-               } else {
-                       if (rr.content != ".")
-                               rr.content += ".";
-                       rr.content += d_origin;
-               }
-        }
-
-        if (rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
-               rr.content=rrow[2]+" "+rr.content;
-        rr.ttl = atol(rrow[3].c_str());
-        if (d_useminimalttl && rr.ttl < d_minimum)
-               rr.ttl = d_minimum;
-        rr.domain_id=atol(rrow[4].c_str());
-
-        rr.last_modified=0;
-
-        return true;
-
+  if (d_origin.empty()) {
+    if (d_query_stmt) {
+      try {
+        d_query_stmt->reset();
+      } catch (SSqlException &e) {
+        throw PDNSException("MyDNSBackend unable to lookup "+d_qname+": "+e.txtReason());
+      }
+      d_query_stmt = NULL;
+    }
+    // This happens if lookup() couldn't find the zone
+    return false;
+  }
+
+  SSqlStatement::row_t rrow;
+
+  if (d_query_stmt->hasNextRow()) {
+    try {
+      d_query_stmt->nextRow(rrow);
+    } catch (SSqlException &e) {
+      throw PDNSException("MyDNSBackend unable to lookup "+d_qname+": "+e.txtReason());
+    }
+    rr.qtype=rrow[0];
+    rr.content = rrow[1];
+  
+    if(!d_qname.empty()) {
+      // use this to distinguish between select with 'name' field (list()) and one without
+      rr.qname=d_qname;
+    } else {
+      rr.qname=rrow[5];
+      if (!rr.qname.empty() && rr.qname[rr.qname.length()-1] == '.') {
+        rr.qname.erase(rr.qname.length()-1); // Fully qualified, nuke the last .
+      } else {
+        if (!rr.qname.empty())
+          rr.qname += ".";
+        rr.qname += d_origin; // Not fully qualified
+      }
+    }
+  
+    if (rr.qtype.getCode() == QType::NS || rr.qtype.getCode()==QType::MX || 
+          rr.qtype.getCode() == QType::CNAME || rr.qtype.getCode() == QType::PTR) {
+      if (!rr.content.empty() && rr.content[rr.content.length()-1] == '.') {
+        if (rr.content.length() > 1)
+          rr.content.erase(rr.content.length()-1); // Fully qualified, nuke the last .
+      } else {
+        if (rr.content != ".")
+          rr.content += ".";
+        rr.content += d_origin;
+      }
+    }
+    if (rr.qtype.getCode() == QType::MX || rr.qtype.getCode() == QType::SRV)
+      rr.content=rrow[2]+" "+rr.content;
+
+    rr.ttl = atol(rrow[3].c_str());
+    if (d_useminimalttl)
+      rr.ttl = std::min(rr.ttl, d_minimum);
+    rr.domain_id=atol(rrow[4].c_str());
+  
+    rr.last_modified=0;
+
+    return true;
+  }
+
+  try {
+    d_query_stmt->reset();
+  } catch (SSqlException &e) {
+    throw PDNSException("MyDNSBackend unable to lookup "+d_qname+": "+e.txtReason());
+  }
+
+  d_query_stmt = NULL;
+
+  return false;
 }
 
 class MyDNSFactory : public BackendFactory {
 
 public:
-        MyDNSFactory() : BackendFactory("mydns") {}
-
-        void declareArguments(const string &suffix = "") {
-               declare(suffix,"dbname","Pdns backend database name to connect to","mydns");
-               declare(suffix,"user","Pdns backend user to connect as","powerdns");
-               declare(suffix,"host","Pdns backend host to connect to","");
-               declare(suffix,"port","Pdns backend host to connect to","");
-               declare(suffix,"password","Pdns backend password to connect with","");
-               declare(suffix,"socket","Pdns backend socket to connect to","");
-               declare(suffix,"rr-table","Name of RR table to use","rr");
-               declare(suffix,"soa-table","Name of SOA table to use","soa");
-               declare(suffix,"soa-where","Additional WHERE clause for SOA","1 = 1");
-               declare(suffix,"rr-where","Additional WHERE clause for RR","1 = 1");
-               declare(suffix,"soa-active","Use the active column in the SOA table","yes");
-               declare(suffix,"rr-active","Use the active column in the RR table","yes");
-               declare(suffix,"use-minimal-ttl","Setting this to 'yes' will make the backend behave like MyDNS on the TTL values. Setting it to 'no' will make it ignore the minimal-ttl of the zone.","yes");
-        }
-
-        MyDNSBackend *make(const string &suffix = "") {
-               return new MyDNSBackend(suffix);
-        }
+  MyDNSFactory() : BackendFactory("mydns") {}
+
+  void declareArguments(const string &suffix = "") {
+    declare(suffix,"dbname","Pdns backend database name to connect to","mydns");
+    declare(suffix,"user","Pdns backend user to connect as","powerdns");
+    declare(suffix,"host","Pdns backend host to connect to","");
+    declare(suffix,"port","Pdns backend host to connect to","");
+    declare(suffix,"password","Pdns backend password to connect with","");
+    declare(suffix,"socket","Pdns backend socket to connect to","");
+    declare(suffix,"rr-table","Name of RR table to use","rr");
+    declare(suffix,"soa-table","Name of SOA table to use","soa");
+    declare(suffix,"soa-where","Additional WHERE clause for SOA","1 = 1");
+    declare(suffix,"rr-where","Additional WHERE clause for RR","1 = 1");
+    declare(suffix,"soa-active","Use the active column in the SOA table","yes");
+    declare(suffix,"rr-active","Use the active column in the RR table","yes");
+    declare(suffix,"use-minimal-ttl","Setting this to 'yes' will make the backend behave like MyDNS on the TTL values. Setting it to 'no' will make it ignore the minimal-ttl of the zone.","yes");
+  }
+
+  MyDNSBackend *make(const string &suffix = "") {
+    return new MyDNSBackend(suffix);
+  }
 
 };
 
 class MyDNSLoader {
 
 public:
-        MyDNSLoader() {
-               BackendMakers().report(new MyDNSFactory());
-               L << Logger::Info << "[mydnsbackend] This is the mydns backend version " VERSION " (" __DATE__ ", " __TIME__ ") reporting" << endl;
-        }
+  MyDNSLoader() {
+    BackendMakers().report(new MyDNSFactory());
+    L << Logger::Info << "[mydnsbackend] This is the mydns backend version " VERSION " (" __DATE__ ", " __TIME__ ") reporting" << endl;
+  }
 };
 
 static MyDNSLoader mydnsloader;
index 5caa80be9e2466500a865a994c23fb1f5d049086..b0be17350ec65973b449372ab2a4078a042bed0f 100644 (file)
 class MyDNSBackend : public DNSBackend
 {
 public:
-        MyDNSBackend(const string &suffix="");
-        ~MyDNSBackend();
-        
-        void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
-        bool list(const string &target, int domain_id, bool include_disabled=false);
-        bool get(DNSResourceRecord &r);
-        bool getSOA(const string& name, SOAData& soadata, DNSPacket*);
-          
+  MyDNSBackend(const string &suffix="");
+  ~MyDNSBackend();
+  
+  void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
+  bool list(const string &target, int domain_id, bool include_disabled=false);
+  bool get(DNSResourceRecord &r);
+  bool getSOA(const string& name, SOAData& soadata, DNSPacket*);
+    
 private:
-        void Query(const string& query);
-        SMySQL *d_db; 
+  SMySQL *d_db; 
 
-        string d_qname;
-        string d_rrtable;
-        string d_soatable;
-        string d_soawhere;
-        string d_rrwhere;
-        string d_origin;
-        bool d_useminimalttl;
-        unsigned int d_minimum;
+  string d_qname;
+  string d_origin;
+  bool d_useminimalttl;
+  unsigned int d_minimum;
 
+  SSqlStatement::result_t d_result;
+
+  SSqlStatement* d_query_stmt;
+  SSqlStatement* d_domainIdQuery_stmt;
+  SSqlStatement* d_domainNoIdQuery_stmt;
+  SSqlStatement* d_listQuery_stmt;
+  SSqlStatement* d_soaQuery_stmt;
+  SSqlStatement* d_basicQuery_stmt;
+  SSqlStatement* d_anyQuery_stmt;
 };
 #endif /* MYDNSBACKEND_HH */
index 4befdf896d4ab4ee7ed790d45195bf29f30e8847..bac0dc3511b43470cba0d98858e48e21fad0c9a0 100644 (file)
 #include <sstream>
 #include <boost/foreach.hpp>
 #include <boost/format.hpp>
+#include <boost/scoped_ptr.hpp>
 
+GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
+{
+  setArgPrefix(mode+suffix);
+  d_db=0;
+  d_logprefix="["+mode+"Backend"+suffix+"] ";
+       
+  try
+  {
+    d_dnssecQueries = mustDo("dnssec");
+  }
+  catch (ArgException e)
+  {
+    d_dnssecQueries = false;
+  }
+
+  d_NoIdQuery=getArg("basic-query");
+  d_IdQuery=getArg("id-query");
+  d_ANYNoIdQuery=getArg("any-query");
+  d_ANYIdQuery=getArg("any-id-query");
+
+  d_listQuery=getArg("list-query");
+  d_listSubZoneQuery=getArg("list-subzone-query");
+
+  d_MasterOfDomainsZoneQuery=getArg("master-zone-query");
+  d_InfoOfDomainsZoneQuery=getArg("info-zone-query");
+  d_InfoOfAllSlaveDomainsQuery=getArg("info-all-slaves-query");
+  d_SuperMasterInfoQuery=getArg("supermaster-query");
+  d_GetSuperMasterIPs=getArg("supermaster-name-to-ips");
+  d_InsertZoneQuery=getArg("insert-zone-query");
+  d_InsertSlaveZoneQuery=getArg("insert-slave-query");
+  d_InsertRecordQuery=getArg("insert-record-query");
+  d_InsertEntQuery=getArg("insert-ent-query");
+  d_UpdateMasterOfZoneQuery=getArg("update-master-query");
+  d_UpdateKindOfZoneQuery=getArg("update-kind-query");
+  d_UpdateSerialOfZoneQuery=getArg("update-serial-query");
+  d_UpdateLastCheckofZoneQuery=getArg("update-lastcheck-query");
+  d_ZoneLastChangeQuery=getArg("zone-lastchange-query");
+  d_InfoOfAllMasterDomainsQuery=getArg("info-all-master-query");
+  d_DeleteDomainQuery=getArg("delete-domain-query");
+  d_DeleteZoneQuery=getArg("delete-zone-query");
+  d_DeleteRRSetQuery=getArg("delete-rrset-query");
+  d_DeleteNamesQuery=getArg("delete-names-query");
+  d_getAllDomainsQuery=getArg("get-all-domains-query");
+
+  d_removeEmptyNonTerminalsFromZoneQuery = getArg("remove-empty-non-terminals-from-zone-query");
+  d_insertEmptyNonTerminalQuery = getArg("insert-empty-non-terminal-query");
+  d_deleteEmptyNonTerminalQuery = getArg("delete-empty-non-terminal-query");
 
-boost::format GSQLformat(const string &query) {
-  boost::format format(query);
-  format.exceptions(boost::io::no_error_bits);
-  return format;
+  d_ListCommentsQuery = getArg("list-comments-query");
+  d_InsertCommentQuery = getArg("insert-comment-query");
+  d_DeleteCommentRRsetQuery = getArg("delete-comment-rrset-query");
+  d_DeleteCommentsQuery = getArg("delete-comments-query");
+
+  d_InsertRecordOrderQuery=getArg("insert-record-order-query");
+  d_InsertEntOrderQuery=getArg("insert-ent-order-query");
+
+  d_firstOrderQuery = getArg("get-order-first-query");
+  d_beforeOrderQuery = getArg("get-order-before-query");
+  d_afterOrderQuery = getArg("get-order-after-query");
+  d_lastOrderQuery = getArg("get-order-last-query");
+  d_setOrderAuthQuery = getArg("set-order-and-auth-query");
+  d_nullifyOrderNameAndUpdateAuthQuery = getArg("nullify-ordername-and-update-auth-query");
+  d_nullifyOrderNameAndAuthQuery = getArg("nullify-ordername-and-auth-query");
+  d_setAuthOnDsRecordQuery = getArg("set-auth-on-ds-record-query");
+
+  d_AddDomainKeyQuery = getArg("add-domain-key-query");
+  d_ListDomainKeysQuery = getArg("list-domain-keys-query");
+
+  d_GetAllDomainMetadataQuery = getArg("get-all-domain-metadata-query");  
+  d_GetDomainMetadataQuery = getArg("get-domain-metadata-query");
+  d_ClearDomainMetadataQuery = getArg("clear-domain-metadata-query");
+  d_ClearDomainAllMetadataQuery = getArg("clear-domain-all-metadata-query");
+  d_SetDomainMetadataQuery = getArg("set-domain-metadata-query");
+
+  d_ActivateDomainKeyQuery = getArg("activate-domain-key-query");
+  d_DeactivateDomainKeyQuery = getArg("deactivate-domain-key-query");
+  d_RemoveDomainKeyQuery = getArg("remove-domain-key-query");
+  d_ClearDomainAllKeysQuery = getArg("clear-domain-all-keys-query");
+
+  d_getTSIGKeyQuery = getArg("get-tsig-key-query");
+  d_setTSIGKeyQuery = getArg("set-tsig-key-query");
+  d_deleteTSIGKeyQuery = getArg("delete-tsig-key-query");
+  d_getTSIGKeysQuery = getArg("get-tsig-keys-query");
+
+  d_NoIdQuery_stmt = NULL;
+  d_IdQuery_stmt = NULL;
+  d_ANYNoIdQuery_stmt = NULL;
+  d_ANYIdQuery_stmt = NULL;
+  d_listQuery_stmt = NULL;
+  d_listSubZoneQuery_stmt = NULL;
+  d_MasterOfDomainsZoneQuery_stmt = NULL;
+  d_InfoOfDomainsZoneQuery_stmt = NULL;
+  d_InfoOfAllSlaveDomainsQuery_stmt = NULL;
+  d_SuperMasterInfoQuery_stmt = NULL;
+  d_GetSuperMasterIPs_stmt = NULL;
+  d_InsertZoneQuery_stmt = NULL;
+  d_InsertSlaveZoneQuery_stmt = NULL;
+  d_InsertRecordQuery_stmt = NULL;
+  d_InsertEntQuery_stmt = NULL;
+  d_InsertRecordOrderQuery_stmt = NULL;
+  d_InsertEntOrderQuery_stmt = NULL;
+  d_UpdateMasterOfZoneQuery_stmt = NULL;
+  d_UpdateKindOfZoneQuery_stmt = NULL;
+  d_UpdateSerialOfZoneQuery_stmt = NULL;
+  d_UpdateLastCheckofZoneQuery_stmt = NULL;
+  d_InfoOfAllMasterDomainsQuery_stmt = NULL;
+  d_DeleteDomainQuery_stmt = NULL;
+  d_DeleteZoneQuery_stmt = NULL;
+  d_DeleteRRSetQuery_stmt = NULL;
+  d_DeleteNamesQuery_stmt = NULL;
+  d_ZoneLastChangeQuery_stmt = NULL;
+  d_firstOrderQuery_stmt = NULL;
+  d_beforeOrderQuery_stmt = NULL;
+  d_afterOrderQuery_stmt = NULL;
+  d_lastOrderQuery_stmt = NULL;
+  d_setOrderAuthQuery_stmt = NULL;
+  d_nullifyOrderNameAndUpdateAuthQuery_stmt = NULL;
+  d_nullifyOrderNameAndAuthQuery_stmt = NULL;
+  d_nullifyOrderNameAndAuthENTQuery_stmt = NULL;
+  d_setAuthOnDsRecordQuery_stmt = NULL;
+  d_removeEmptyNonTerminalsFromZoneQuery_stmt = NULL;
+  d_insertEmptyNonTerminalQuery_stmt = NULL;
+  d_deleteEmptyNonTerminalQuery_stmt = NULL;
+  d_AddDomainKeyQuery_stmt = NULL;
+  d_ListDomainKeysQuery_stmt = NULL;
+  d_GetAllDomainMetadataQuery_stmt = NULL;
+  d_GetDomainMetadataQuery_stmt = NULL;
+  d_ClearDomainMetadataQuery_stmt = NULL;
+  d_ClearDomainAllMetadataQuery_stmt = NULL;
+  d_SetDomainMetadataQuery_stmt = NULL;
+  d_RemoveDomainKeyQuery_stmt = NULL;
+  d_ActivateDomainKeyQuery_stmt = NULL;
+  d_DeactivateDomainKeyQuery_stmt = NULL;
+  d_ClearDomainAllKeysQuery_stmt = NULL;
+  d_getTSIGKeyQuery_stmt = NULL;
+  d_setTSIGKeyQuery_stmt = NULL;
+  d_deleteTSIGKeyQuery_stmt = NULL;
+  d_getTSIGKeysQuery_stmt = NULL;
+  d_getAllDomainsQuery_stmt = NULL;
+  d_ListCommentsQuery_stmt = NULL;
+  d_InsertCommentQuery_stmt = NULL;
+  d_DeleteCommentRRsetQuery_stmt = NULL;
+  d_DeleteCommentsQuery_stmt = NULL;
 }
 
 void GSQLBackend::setNotified(uint32_t domain_id, uint32_t serial)
 {
-  char output[1024];
-  snprintf(output,sizeof(output)-1,
-          d_UpdateSerialOfZoneQuery.c_str(),
-          serial, domain_id);
-
   try {
-    d_db->doCommand(output);
+    d_UpdateSerialOfZoneQuery_stmt->
+      bind("serial", serial)->
+      bind("domain_id", domain_id)->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -59,13 +197,12 @@ void GSQLBackend::setNotified(uint32_t domain_id, uint32_t serial)
 
 void GSQLBackend::setFresh(uint32_t domain_id)
 {
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_UpdateLastCheckofZoneQuery.c_str(),
-          time(0),
-          domain_id);
-
   try {
-    d_db->doCommand(output);
+    d_UpdateLastCheckofZoneQuery_stmt->
+      bind("last_check", time(0))->
+      bind("domain_id", domain_id)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -74,10 +211,12 @@ void GSQLBackend::setFresh(uint32_t domain_id)
 
 bool GSQLBackend::isMaster(const string &domain, const string &ip)
 {
-  string query = (GSQLformat(d_MasterOfDomainsZoneQuery) % sqlEscape(domain)).str();
-
   try {
-    d_db->doQuery(query, d_result);
+    d_MasterOfDomainsZoneQuery_stmt->
+      bind("domain", domain)->
+      execute()->
+      getResult(d_result)->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e.txtReason());
@@ -102,10 +241,12 @@ bool GSQLBackend::isMaster(const string &domain, const string &ip)
 
 bool GSQLBackend::setMaster(const string &domain, const string &ip)
 {
-  string query = (GSQLformat(d_UpdateMasterOfZoneQuery) % sqlEscape(ip) % sqlEscape(toLower(domain))).str();
-
   try {
-    d_db->doCommand(query);
+    d_UpdateMasterOfZoneQuery_stmt->
+      bind("master", ip)->
+      bind("domain", toLower(domain))->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to set master of domain \""+domain+"\": "+e.txtReason());
@@ -115,11 +256,12 @@ bool GSQLBackend::setMaster(const string &domain, const string &ip)
 
 bool GSQLBackend::setKind(const string &domain, const DomainInfo::DomainKind kind)
 {
-  string kind_str = toUpper(DomainInfo::getKindString(kind));
-  string query = (GSQLformat(d_UpdateKindOfZoneQuery) % sqlEscape(kind_str) % sqlEscape(toLower(domain))).str();
-
   try {
-    d_db->doCommand(query);
+    d_UpdateKindOfZoneQuery_stmt->
+      bind("kind", toUpper(DomainInfo::getKindString(kind)))->
+      bind("domain", toLower(domain))->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to set kind of domain \""+domain+"\": "+e.txtReason());
@@ -131,11 +273,12 @@ bool GSQLBackend::getDomainInfo(const string &domain, DomainInfo &di)
 {
   /* fill DomainInfo from database info:
      id,name,master IP(s),last_check,notified_serial,type */
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_InfoOfDomainsZoneQuery.c_str(),
-          sqlEscape(domain).c_str());
   try {
-    d_db->doQuery(output,d_result);
+    d_InfoOfDomainsZoneQuery_stmt->
+      bind("domain", toLower(domain))->
+      execute()->
+      getResult(d_result)->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve information about a domain: "+e.txtReason());
@@ -175,7 +318,10 @@ void GSQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
   /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
      id,name,master IP,serial */
   try {
-    d_db->doQuery(d_InfoOfAllSlaveDomainsQuery, d_result);
+    d_InfoOfAllSlaveDomainsQuery_stmt->
+      execute()->
+      getResult(d_result)->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve list of slave domains: "+e.txtReason());
@@ -211,7 +357,10 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
   /* list all domains that need notifications for which we are master, and insert into updatedDomains
      id,name,master IP,serial */
   try {
-    d_db->doQuery(d_InfoOfAllMasterDomainsQuery,d_result);
+    d_InfoOfAllMasterDomainsQuery_stmt->
+      execute()->
+      getResult(d_result)->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve list of master domains: "+e.txtReason());
@@ -242,107 +391,6 @@ void GSQLBackend::getUpdatedMasters(vector<DomainInfo> *updatedDomains)
   }
 }
 
-
-string GSQLBackend::sqlEscape(const string &name)
-{
-  string a;
-
-  for(string::const_iterator i=name.begin();i!=name.end();++i)
-    if(*i=='\'' || *i=='\\'){
-      a+='\\';
-      a+=*i;
-    }
-    else
-      a+=*i;
-  return a;
-}
-
-
-GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
-{
-  setArgPrefix(mode+suffix);
-  d_db=0;
-  d_logprefix="["+mode+"Backend"+suffix+"] ";
-       
-  try
-  {
-    d_dnssecQueries = mustDo("dnssec");
-  }
-  catch (ArgException e)
-  {
-    d_dnssecQueries = false;
-  }
-
-  d_NoIdQuery=getArg("basic-query");
-  d_IdQuery=getArg("id-query");
-  d_ANYNoIdQuery=getArg("any-query");
-  d_ANYIdQuery=getArg("any-id-query");
-
-  d_listQuery=getArg("list-query");
-  d_listSubZoneQuery=getArg("list-subzone-query");
-
-  d_MasterOfDomainsZoneQuery=getArg("master-zone-query");
-  d_InfoOfDomainsZoneQuery=getArg("info-zone-query");
-  d_InfoOfAllSlaveDomainsQuery=getArg("info-all-slaves-query");
-  d_SuperMasterInfoQuery=getArg("supermaster-query");
-  d_GetSuperMasterIPs=getArg("supermaster-name-to-ips");
-  d_InsertZoneQuery=getArg("insert-zone-query");
-  d_InsertSlaveZoneQuery=getArg("insert-slave-query");
-  d_InsertRecordQuery=getArg("insert-record-query");
-  d_InsertEntQuery=getArg("insert-ent-query");
-  d_UpdateMasterOfZoneQuery=getArg("update-master-query");
-  d_UpdateKindOfZoneQuery=getArg("update-kind-query");
-  d_UpdateSerialOfZoneQuery=getArg("update-serial-query");
-  d_UpdateLastCheckofZoneQuery=getArg("update-lastcheck-query");
-  d_ZoneLastChangeQuery=getArg("zone-lastchange-query");
-  d_InfoOfAllMasterDomainsQuery=getArg("info-all-master-query");
-  d_DeleteDomainQuery=getArg("delete-domain-query");
-  d_DeleteZoneQuery=getArg("delete-zone-query");
-  d_DeleteRRSetQuery=getArg("delete-rrset-query");
-  d_DeleteNamesQuery=getArg("delete-names-query");
-  d_getAllDomainsQuery=getArg("get-all-domains-query");
-
-  d_removeEmptyNonTerminalsFromZoneQuery = getArg("remove-empty-non-terminals-from-zone-query");
-  d_insertEmptyNonTerminalQuery = getArg("insert-empty-non-terminal-query");
-  d_deleteEmptyNonTerminalQuery = getArg("delete-empty-non-terminal-query");
-
-  d_ListCommentsQuery = getArg("list-comments-query");
-  d_InsertCommentQuery = getArg("insert-comment-query");
-  d_DeleteCommentRRsetQuery = getArg("delete-comment-rrset-query");
-  d_DeleteCommentsQuery = getArg("delete-comments-query");
-
-  d_InsertRecordOrderQuery=getArg("insert-record-order-query");
-  d_InsertEntOrderQuery=getArg("insert-ent-order-query");
-
-  d_firstOrderQuery = getArg("get-order-first-query");
-  d_beforeOrderQuery = getArg("get-order-before-query");
-  d_afterOrderQuery = getArg("get-order-after-query");
-  d_lastOrderQuery = getArg("get-order-last-query");
-  d_setOrderAuthQuery = getArg("set-order-and-auth-query");
-  d_nullifyOrderNameAndUpdateAuthQuery = getArg("nullify-ordername-and-update-auth-query");
-  d_nullifyOrderNameAndAuthQuery = getArg("nullify-ordername-and-auth-query");
-  d_setAuthOnDsRecordQuery = getArg("set-auth-on-ds-record-query");
-
-  d_AddDomainKeyQuery = getArg("add-domain-key-query");
-  d_ListDomainKeysQuery = getArg("list-domain-keys-query");
-
-  d_GetAllDomainMetadataQuery = getArg("get-all-domain-metadata-query");  
-  d_GetDomainMetadataQuery = getArg("get-domain-metadata-query");
-  d_ClearDomainMetadataQuery = getArg("clear-domain-metadata-query");
-  d_ClearDomainAllMetadataQuery = getArg("clear-domain-all-metadata-query");
-  d_SetDomainMetadataQuery = getArg("set-domain-metadata-query");
-
-  d_ActivateDomainKeyQuery = getArg("activate-domain-key-query");
-  d_DeactivateDomainKeyQuery = getArg("deactivate-domain-key-query");
-  d_RemoveDomainKeyQuery = getArg("remove-domain-key-query");
-  d_ClearDomainAllKeysQuery = getArg("clear-domain-all-keys-query");
-
-  d_getTSIGKeyQuery = getArg("get-tsig-key-query");
-  d_setTSIGKeyQuery = getArg("set-tsig-key-query");
-  d_deleteTSIGKeyQuery = getArg("delete-tsig-key-query");
-  d_getTSIGKeysQuery = getArg("get-tsig-keys-query");
-}
-
 bool GSQLBackend::updateDNSSECOrderAndAuth(uint32_t domain_id, const std::string& zonename, const std::string& qname, bool auth)
 {
   if(!d_dnssecQueries)
@@ -355,11 +403,15 @@ bool GSQLBackend::updateDNSSECOrderAndAuthAbsolute(uint32_t domain_id, const std
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
 
-  snprintf(output, sizeof(output)-1, d_setOrderAuthQuery.c_str(), sqlEscape(ordername).c_str(), auth, sqlEscape(qname).c_str(), domain_id);
   try {
-    d_db->doCommand(output);
+    d_setOrderAuthQuery_stmt->
+      bind("ordername", ordername)->
+      bind("auth", auth)->
+      bind("qname", qname)->
+      bind("domain_id", domain_id)->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to update ordername/auth for domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -371,11 +423,14 @@ bool GSQLBackend::nullifyDNSSECOrderNameAndUpdateAuth(uint32_t domain_id, const
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
 
-  snprintf(output, sizeof(output)-1, d_nullifyOrderNameAndUpdateAuthQuery.c_str(), auth, domain_id, sqlEscape(qname).c_str());
   try {
-    d_db->doCommand(output);
+    d_nullifyOrderNameAndUpdateAuthQuery_stmt->
+      bind("auth", auth)->
+      bind("domain_id", domain_id)->
+      bind("qname", qname)->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to nullify ordername and update auth for domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -387,11 +442,14 @@ bool GSQLBackend::nullifyDNSSECOrderNameAndAuth(uint32_t domain_id, const std::s
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
-
-  snprintf(output, sizeof(output)-1, d_nullifyOrderNameAndAuthQuery.c_str(), sqlEscape(qname).c_str(), sqlEscape(type).c_str(), domain_id);
+  
   try {
-    d_db->doCommand(output);
+    d_nullifyOrderNameAndAuthQuery_stmt->
+      bind("qname", qname)->
+      bind("qtype", type)->
+      bind("domain_id", domain_id)->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to nullify ordername/auth for domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -403,11 +461,13 @@ bool GSQLBackend::setDNSSECAuthOnDsRecord(uint32_t domain_id, const std::string&
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
 
-  snprintf(output, sizeof(output)-1, d_setAuthOnDsRecordQuery.c_str(), domain_id, sqlEscape(qname).c_str());
   try {
-    d_db->doCommand(output);
+    d_setAuthOnDsRecordQuery_stmt->
+      bind("domain_id", domain_id)->
+      bind("qname", qname)->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to set auth on DS record "+qname+" for domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -417,12 +477,12 @@ bool GSQLBackend::setDNSSECAuthOnDsRecord(uint32_t domain_id, const std::string&
 
 bool GSQLBackend::updateEmptyNonTerminals(uint32_t domain_id, const std::string& zonename, set<string>& insert, set<string>& erase, bool remove)
 {
-  char output[1024];
-
   if(remove) {
-    snprintf(output,sizeof(output)-1,d_removeEmptyNonTerminalsFromZoneQuery.c_str(), domain_id);
     try {
-      d_db->doCommand(output);
+      d_removeEmptyNonTerminalsFromZoneQuery_stmt->
+        bind("domain_id", domain_id)->
+        execute()->
+        reset();
     }
     catch (SSqlException &e) {
       throw PDNSException("GSQLBackend unable to delete empty non-terminal records from domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -432,9 +492,12 @@ bool GSQLBackend::updateEmptyNonTerminals(uint32_t domain_id, const std::string&
   else
   {
     BOOST_FOREACH(const string qname, erase) {
-      snprintf(output,sizeof(output)-1,d_deleteEmptyNonTerminalQuery.c_str(), domain_id, sqlEscape(qname).c_str());
       try {
-        d_db->doCommand(output);
+        d_deleteEmptyNonTerminalQuery_stmt->
+          bind("domain_id", domain_id)->
+          bind("qname", qname)->
+          execute()->
+          reset();
       }
       catch (SSqlException &e) {
         throw PDNSException("GSQLBackend unable to delete empty non-terminal rr "+qname+" from domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -444,9 +507,12 @@ bool GSQLBackend::updateEmptyNonTerminals(uint32_t domain_id, const std::string&
   }
 
   BOOST_FOREACH(const string qname, insert) {
-    snprintf(output,sizeof(output)-1,d_insertEmptyNonTerminalQuery.c_str(), domain_id, sqlEscape(qname).c_str());
     try {
-      d_db->doCommand(output);
+      d_insertEmptyNonTerminalQuery_stmt->
+        bind("domain_id", domain_id)->
+        bind("qname", qname)->
+        execute()->
+        reset();
     }
     catch (SSqlException &e) {
       throw PDNSException("GSQLBackend unable to insert empty non-terminal rr "+qname+" in domain_id "+itoa(domain_id)+": "+e.txtReason());
@@ -470,48 +536,56 @@ bool GSQLBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string&
   after.clear();
   string lcqname=toLower(qname);
 
-  SSql::row_t row;
-
-  char output[1024];
-
-  snprintf(output, sizeof(output)-1, d_afterOrderQuery.c_str(), sqlEscape(lcqname).c_str(), id);
+  SSqlStatement::row_t row;
   try {
-    d_db->doQuery(output);
+    d_afterOrderQuery_stmt->
+      bind("ordername", lcqname)->
+      bind("domain_id", id)->
+      execute();
+    while(d_afterOrderQuery_stmt->hasNextRow()) {
+      d_afterOrderQuery_stmt->nextRow(row);
+      after=row[0];
+    }
+    d_afterOrderQuery_stmt->reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend unable to find before/after (after) for domain_id "+itoa(id)+": "+e.txtReason());
   }
-  while(d_db->getRow(row)) {
-    after=row[0];
-  }
 
   if(after.empty() && !lcqname.empty()) {
-    snprintf(output, sizeof(output)-1, d_firstOrderQuery.c_str(), id);
     try {
-      d_db->doQuery(output);
+      d_firstOrderQuery_stmt->
+        bind("domain_id", id)->
+        execute();
+      while(d_firstOrderQuery_stmt->hasNextRow()) {
+        d_firstOrderQuery_stmt->nextRow(row);
+        after=row[0];
+      }
+      d_firstOrderQuery_stmt->reset();
     }
     catch(SSqlException &e) {
       throw PDNSException("GSQLBackend unable to find before/after (first) for domain_id "+itoa(id)+": "+e.txtReason());
     }
-    while(d_db->getRow(row)) {
-      after=row[0];
-    }
   }
 
   if (before.empty()) {
     unhashed.clear();
 
-    snprintf(output, sizeof(output)-1, d_beforeOrderQuery.c_str(), sqlEscape(lcqname).c_str(), id);
     try {
-      d_db->doQuery(output);
+      d_beforeOrderQuery_stmt->
+        bind("ordername", lcqname)->
+        bind("domain_id", id)->
+        execute();
+      while(d_beforeOrderQuery_stmt->hasNextRow()) {
+        d_beforeOrderQuery_stmt->nextRow(row);
+        before=row[0];
+        unhashed=row[1];
+      }
+      d_beforeOrderQuery_stmt->reset();
     }
     catch(SSqlException &e) {
       throw PDNSException("GSQLBackend unable to find before/after (before) for domain_id "+itoa(id)+": "+e.txtReason());
     }
-    while(d_db->getRow(row)) {
-      before=row[0];
-      unhashed=row[1];
-    }
 
     if(! unhashed.empty())
     {
@@ -519,17 +593,20 @@ bool GSQLBackend::getBeforeAndAfterNamesAbsolute(uint32_t id, const std::string&
       return true;
     }
 
-    snprintf(output, sizeof(output)-1, d_lastOrderQuery.c_str(), id);
     try {
-      d_db->doQuery(output);
+      d_lastOrderQuery_stmt->
+        bind("domain_id", id)->
+        execute();
+      while(d_lastOrderQuery_stmt->hasNextRow()) {
+        d_lastOrderQuery_stmt->nextRow(row);
+        before=row[0];
+        unhashed=row[1];
+      }
+      d_lastOrderQuery_stmt->reset();
     }
     catch(SSqlException &e) {
       throw PDNSException("GSQLBackend unable to find before/after (last) for domain_id "+itoa(id)+": "+e.txtReason());
     }
-    while(d_db->getRow(row)) {
-      before=row[0];
-      unhashed=row[1];
-    }
   } else {
     before=lcqname;
   }
@@ -541,12 +618,15 @@ int GSQLBackend::addDomainKey(const string& name, const KeyData& key)
 {
   if(!d_dnssecQueries)
     return -1;
-  char output[16384];  
-  snprintf(output,sizeof(output)-1,d_AddDomainKeyQuery.c_str(),
-          key.flags, (int)key.active, sqlEscape(key.content).c_str(), sqlEscape(toLower(name)).c_str());
 
   try {
-    d_db->doCommand(output);
+    d_AddDomainKeyQuery_stmt->
+      bind("flags", key.flags)->
+      bind("active", key.active)->
+      bind("content", key.content)->
+      bind("domain", toLower(name))->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to store key: "+e.txtReason());
@@ -558,11 +638,13 @@ bool GSQLBackend::activateDomainKey(const string& name, unsigned int id)
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_ActivateDomainKeyQuery.c_str(), sqlEscape(toLower(name)).c_str(), id);
 
   try {
-    d_db->doCommand(output);
+    d_ActivateDomainKeyQuery_stmt->
+      bind("domain", toLower(name))->
+      bind("key_id", id)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to activate key: "+e.txtReason());
@@ -574,11 +656,13 @@ bool GSQLBackend::deactivateDomainKey(const string& name, unsigned int id)
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_DeactivateDomainKeyQuery.c_str(), sqlEscape(toLower(name)).c_str(), id);
 
   try {
-    d_db->doCommand(output);
+    d_DeactivateDomainKeyQuery_stmt->
+      bind("domain", toLower(name))->
+      bind("key_id", id)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to deactivate key: "+e.txtReason());
@@ -590,11 +674,13 @@ bool GSQLBackend::removeDomainKey(const string& name, unsigned int id)
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_RemoveDomainKeyQuery.c_str(), sqlEscape(toLower(name)).c_str(), id);
 
   try {
-    d_db->doCommand(output);
+    d_RemoveDomainKeyQuery_stmt->
+      bind("domain", toLower(name))->
+      bind("key_id", id)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to remove key: "+e.txtReason());
@@ -604,35 +690,40 @@ bool GSQLBackend::removeDomainKey(const string& name, unsigned int id)
 
 bool GSQLBackend::getTSIGKey(const string& name, string* algorithm, string* content)
 {
-  char output[1024];  
-  snprintf(output,sizeof(output)-1,d_getTSIGKeyQuery.c_str(), sqlEscape(toLower(name)).c_str());
-
   try {
-    d_db->doQuery(output);
+    d_getTSIGKeyQuery_stmt->
+      bind("key_name", toLower(name))->
+      execute();
+  
+    SSqlStatement::row_t row;
+
+    content->clear();
+    while(d_getTSIGKeyQuery_stmt->hasNextRow()) {
+      d_getTSIGKeyQuery_stmt->nextRow(row);
+      if(row.size() >= 2 && (algorithm->empty() || pdns_iequals(*algorithm, row[0]))) {
+        *algorithm = row[0];
+        *content = row[1];
+      }
+    }
+
+    d_getTSIGKeyQuery_stmt->reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve named TSIG key: "+e.txtReason());
   }
-  
-  SSql::row_t row;
-
-  content->clear();
-  while(d_db->getRow(row)) {
-    if(row.size() >= 2 && (algorithm->empty() || pdns_iequals(*algorithm, row[0]))) {
-      *algorithm = row[0];
-      *content = row[1];
-    }
-  }
 
   return !content->empty();
 }
 
 bool GSQLBackend::setTSIGKey(const string& name, const string& algorithm, const string& content)
 {
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_setTSIGKeyQuery.c_str(), sqlEscape(toLower(name)).c_str(), sqlEscape(toLower(algorithm)).c_str(), sqlEscape(content).c_str());
   try {
-    d_db->doCommand(output);
+    d_setTSIGKeyQuery_stmt->
+      bind("key_name", toLower(name))->
+      bind("algorithm", toLower(algorithm))->
+      bind("content", content)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to store named TSIG key: "+e.txtReason());
@@ -642,10 +733,11 @@ bool GSQLBackend::setTSIGKey(const string& name, const string& algorithm, const
 
 bool GSQLBackend::deleteTSIGKey(const string& name)
 {
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_deleteTSIGKeyQuery.c_str(), sqlEscape(toLower(name)).c_str());
   try {
-    d_db->doCommand(output);
+    d_deleteTSIGKeyQuery_stmt->
+      bind("key_name", toLower(name))->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to store named TSIG key: "+e.txtReason());
@@ -655,26 +747,27 @@ bool GSQLBackend::deleteTSIGKey(const string& name)
 
 bool GSQLBackend::getTSIGKeys(std::vector< struct TSIGKey > &keys)
 {
-  char output[1024];
-  snprintf(output,sizeof(output)-1,"%s",d_getTSIGKeysQuery.c_str());
-
   try {
-    d_db->doQuery(output);
+    d_getTSIGKeysQuery_stmt->
+      execute();
+
+    SSqlStatement::row_t row;
+  
+    while(d_getTSIGKeysQuery_stmt->hasNextRow()) {
+      d_getTSIGKeysQuery_stmt->nextRow(row);
+      struct TSIGKey key;
+      key.name = row[0];
+      key.algorithm = row[1];
+      key.key = row[2];
+      keys.push_back(key);
+    }
+
+    d_getTSIGKeysQuery_stmt->reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to retrieve TSIG keys: "+e.txtReason());
   }
 
-  SSql::row_t row;
-
-  while(d_db->getRow(row)) {
-     struct TSIGKey key;
-     key.name = row[0];
-     key.algorithm = row[1];
-     key.key = row[2];
-     keys.push_back(key);
-  }
-
   return keys.empty();
 }
 
@@ -682,29 +775,32 @@ bool GSQLBackend::getDomainKeys(const string& name, unsigned int kind, std::vect
 {
   if(!d_dnssecQueries)
     return false;
-  char output[1024];  
-  snprintf(output,sizeof(output)-1,d_ListDomainKeysQuery.c_str(), sqlEscape(toLower(name)).c_str());
 
   try {
-    d_db->doQuery(output);
+    d_ListDomainKeysQuery_stmt->
+      bind("domain", toLower(name))->
+      execute();
+  
+    SSqlStatement::row_t row;
+    //  "select id, kind, active, content from domains, cryptokeys where domain_id=domains.id and name='%s'";
+    KeyData kd;
+    while(d_ListDomainKeysQuery_stmt->hasNextRow()) {
+      d_ListDomainKeysQuery_stmt->nextRow(row);
+      //~ BOOST_FOREACH(const std::string& val, row) {
+        //~ cerr<<"'"<<val<<"'"<<endl;
+      //~ }
+      kd.id = atoi(row[0].c_str());
+      kd.flags = atoi(row[1].c_str());
+      kd.active = atoi(row[2].c_str());
+      kd.content = row[3];
+      keys.push_back(kd);
+    }
+
+    d_ListDomainKeysQuery_stmt->reset();    
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to list keys: "+e.txtReason());
   }
-  
-  SSql::row_t row;
-  //  "select id, kind, active, content from domains, cryptokeys where domain_id=domains.id and name='%s'";
-  KeyData kd;
-  while(d_db->getRow(row)) {
-    //~ BOOST_FOREACH(const std::string& val, row) {
-      //~ cerr<<"'"<<val<<"'"<<endl;
-    //~ }
-    kd.id = atoi(row[0].c_str());
-    kd.flags = atoi(row[1].c_str());
-    kd.active = atoi(row[2].c_str());
-    kd.content = row[3];
-    keys.push_back(kd);
-  }
 
   return true;
 }
@@ -720,23 +816,25 @@ void GSQLBackend::alsoNotifies(const string &domain, set<string> *ips)
 
 bool GSQLBackend::getAllDomainMetadata(const string& name, std::map<std::string, std::vector<std::string> >& meta)
 {
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_GetAllDomainMetadataQuery.c_str(), sqlEscape(name).c_str());
-
   try {
-    d_db->doQuery(output);
+    d_GetAllDomainMetadataQuery_stmt->
+      bind("domain", toLower(name))->
+      execute();
+
+    SSqlStatement::row_t row;
+  
+    while(d_GetAllDomainMetadataQuery_stmt->hasNextRow()) {
+      d_GetAllDomainMetadataQuery_stmt->nextRow(row);
+      if (!isDnssecDomainMetadata(row[0]))
+        meta[row[0]].push_back(row[1]);
+    }
+
+    d_GetAllDomainMetadataQuery_stmt->reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to list metadata: "+e.txtReason());
   }
 
-  SSql::row_t row;
-
-  while(d_db->getRow(row)) {
-    if (!isDnssecDomainMetadata(row[0]))
-      meta[row[0]].push_back(row[1]);
-  }
-
   return true;
 }
 
@@ -746,21 +844,25 @@ bool GSQLBackend::getDomainMetadata(const string& name, const std::string& kind,
   if(!d_dnssecQueries && isDnssecDomainMetadata(kind))
     return false;
 
-  char output[1024];
-  snprintf(output,sizeof(output)-1,d_GetDomainMetadataQuery.c_str(), sqlEscape(toLower(name)).c_str(), sqlEscape(kind).c_str());
-
   try {
-    d_db->doQuery(output);
+    d_GetDomainMetadataQuery_stmt->
+      bind("domain", toLower(name))->
+      bind("kind", kind)->
+      execute();
+  
+    SSqlStatement::row_t row;
+    
+    while(d_GetDomainMetadataQuery_stmt->hasNextRow()) {
+      d_GetDomainMetadataQuery_stmt->nextRow(row);
+      meta.push_back(row[0]);
+    }
+
+    d_GetDomainMetadataQuery_stmt->reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to list metadata: "+e.txtReason());
   }
-  
-  SSql::row_t row;
-  
-  while(d_db->getRow(row)) {
-    meta.push_back(row[0]);
-  }
+
   return true;
 }
 
@@ -769,16 +871,20 @@ bool GSQLBackend::setDomainMetadata(const string& name, const std::string& kind,
   if(!d_dnssecQueries && isDnssecDomainMetadata(kind))
     return false;
 
-  char output[16384];  
-  string clearQuery = (GSQLformat(d_ClearDomainMetadataQuery) % sqlEscape(toLower(name)) % sqlEscape(kind)).str();
-
   try {
-    d_db->doCommand(clearQuery);
+    d_ClearDomainMetadataQuery_stmt->
+      bind("domain", toLower(name))->
+      bind("kind", kind)->
+      execute()->
+      reset();
     if(!meta.empty()) {
       BOOST_FOREACH(const std::string & value, meta) {
-         snprintf(output,sizeof(output)-1,d_SetDomainMetadataQuery.c_str(),
-            sqlEscape(kind).c_str(), sqlEscape(value).c_str(), sqlEscape(toLower(name)).c_str());
-         d_db->doCommand(output);
+         d_SetDomainMetadataQuery_stmt->
+           bind("kind", kind)->
+           bind("content", value)->
+           bind("domain", toLower(name))->
+           execute()->
+           reset();
       }
     }
   }
@@ -793,36 +899,36 @@ void GSQLBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_
 {
   string lcqname=toLower(qname);
 
-  string query;
-  if(qtype.getCode()!=QType::ANY) {
-    if(domain_id < 0) {
-      query = (GSQLformat(d_NoIdQuery)
-               % sqlEscape(qtype.getName())
-               % sqlEscape(lcqname)
-        ).str();
-    } else {
-      query = (GSQLformat(d_IdQuery)
-               % sqlEscape(qtype.getName())
-               % sqlEscape(lcqname)
-               % domain_id
-        ).str();
-    }
-  } else {
-    // qtype==ANY
-    if(domain_id < 0) {
-      query = (GSQLformat(d_ANYNoIdQuery)
-               % sqlEscape(lcqname)
-        ).str();
+  try {
+    if(qtype.getCode()!=QType::ANY) {
+      if(domain_id < 0) {
+        d_query_stmt = d_NoIdQuery_stmt;
+        d_query_stmt->
+          bind("qtype", qtype.getName())->
+          bind("qname", lcqname);
+      } else {
+        d_query_stmt = d_IdQuery_stmt;
+        d_query_stmt->
+          bind("qtype", qtype.getName())->
+          bind("qname", lcqname)->
+          bind("domain_id", domain_id);
+      }
     } else {
-      query = (GSQLformat(d_ANYIdQuery)
-               % sqlEscape(lcqname)
-               % domain_id
-        ).str();
+      // qtype==ANY
+      if(domain_id < 0) {
+        d_query_stmt = d_ANYNoIdQuery_stmt;
+        d_query_stmt->
+          bind("qname", lcqname);
+      } else {
+        d_query_stmt = d_ANYIdQuery_stmt;
+        d_query_stmt->
+          bind("qname", lcqname)->
+          bind("domain_id", domain_id);
+      }
     }
-  }
 
-  try {
-    d_db->doQuery(query);
+    d_query_stmt->
+      execute();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend lookup query:"+e.txtReason());
@@ -835,13 +941,12 @@ bool GSQLBackend::list(const string &target, int domain_id, bool include_disable
 {
   DLOG(L<<"GSQLBackend constructing handle for list of domain id '"<<domain_id<<"'"<<endl);
 
-  string query = (GSQLformat(d_listQuery)
-                  % (int)include_disabled
-                  % domain_id
-    ).str();
-
   try {
-    d_db->doQuery(query);
+    d_query_stmt = d_listQuery_stmt;
+    d_query_stmt->
+      bind("include_disabled", (int)include_disabled)->
+      bind("domain_id", domain_id)->
+      execute();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend list query: "+e.txtReason());
@@ -853,13 +958,14 @@ bool GSQLBackend::list(const string &target, int domain_id, bool include_disable
 
 bool GSQLBackend::listSubZone(const string &zone, int domain_id) {
   string wildzone = "%." + zone;
-  string query = (GSQLformat(d_listSubZoneQuery)
-                  % sqlEscape(zone)
-                  % sqlEscape(wildzone)
-                  % domain_id
-    ).str();
+
   try {
-    d_db->doQuery(query);
+    d_query_stmt = d_listSubZoneQuery_stmt;
+    d_query_stmt->
+      bind("zone", zone)->
+      bind("wildzone", wildzone)->
+      bind("domain_id", domain_id)->
+      execute();      
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend listSubZone query: "+e.txtReason());
@@ -868,18 +974,64 @@ bool GSQLBackend::listSubZone(const string &zone, int domain_id) {
   return true;
 }
 
+bool GSQLBackend::get(DNSResourceRecord &r)
+{
+  // L << "GSQLBackend get() was called for "<<qtype.getName() << " record: ";
+  SSqlStatement::row_t row;
+  if(d_query_stmt->hasNextRow()) {
+    try {
+      d_query_stmt->nextRow(row);
+    } catch (SSqlException &e) {
+      throw PDNSException("GSQLBackend get: "+e.txtReason());
+    }
+    if (row[1].empty())
+        r.ttl = ::arg().asNum( "default-ttl" );
+    else
+        r.ttl=atol(row[1].c_str());
+    if(!d_qname.empty())
+      r.qname=d_qname;
+    else
+      r.qname=row[6];
+    r.qtype=row[3];
+
+    if (r.qtype==QType::MX || r.qtype==QType::SRV)
+      r.content=row[2]+" "+row[0];
+    else
+      r.content=row[0];
 
+    r.last_modified=0;
+
+    if(d_dnssecQueries)
+      r.auth = !row[7].empty() && row[7][0]=='1';
+    else
+      r.auth = 1;
+
+    r.disabled = !row[5].empty() && row[5][0]=='1';
+
+    r.domain_id=atoi(row[4].c_str());
+    return true;
+  }
+
+  try {
+    d_query_stmt->reset();
+  } catch (SSqlException &e) {
+      throw PDNSException("GSQLBackend get: "+e.txtReason());
+  }
+  d_query_stmt = NULL;
+  return false;
+}
 
 bool GSQLBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **ddb)
 {
-  string format;
-  char output[1024];
-  format = d_SuperMasterInfoQuery;
   // check if we know the ip/ns couple in the database
   for(vector<DNSResourceRecord>::const_iterator i=nsset.begin();i!=nsset.end();++i) {
     try {
-      snprintf(output,sizeof(output)-1,format.c_str(),sqlEscape(ip).c_str(),sqlEscape(i->content).c_str());
-      d_db->doQuery(output, d_result);
+      d_SuperMasterInfoQuery_stmt->
+        bind("ip", ip)->
+        bind("nameserver", i->content)->
+        execute()->
+        getResult(d_result)->
+        reset();
     }
     catch (SSqlException &e) {
       throw PDNSException("GSQLBackend unable to search for a domain: "+e.txtReason());
@@ -897,9 +1049,11 @@ bool GSQLBackend::superMasterBackend(const string &ip, const string &domain, con
 
 bool GSQLBackend::createDomain(const string &domain)
 {
-  string query = (GSQLformat(d_InsertZoneQuery) % toLower(sqlEscape(domain))).str();
   try {
-    d_db->doCommand(query);
+    d_InsertZoneQuery_stmt->
+      bind("domain", toLower(domain))->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("Database error trying to insert new domain '"+domain+"': "+ e.txtReason());
@@ -909,21 +1063,21 @@ bool GSQLBackend::createDomain(const string &domain)
 
 bool GSQLBackend::createSlaveDomain(const string &ip, const string &domain, const string &nameserver, const string &account)
 {
-  string format;
   string name;
   string masters(ip);
-
-  char output[1024];
   try {
     if (!nameserver.empty()) {
       // figure out all IP addresses for the master
-      format = d_GetSuperMasterIPs;
-      snprintf(output,sizeof(output)-1,format.c_str(),sqlEscape(nameserver).c_str(),sqlEscape(account).c_str());
-      d_db->doQuery(output, d_result);
+      d_GetSuperMasterIPs_stmt->
+        bind("nameserver", nameserver)->
+        bind("account", account)->
+        execute()->
+        getResult(d_result)->
+        reset();
       if (!d_result.empty()) {
         // collect all IP addresses
         vector<string> tmp;
-        BOOST_FOREACH(SSql::row_t& row, d_result) {
+        BOOST_FOREACH(SSqlStatement::row_t& row, d_result) {
           if (account == row[1])
             tmp.push_back(row[0]);
         }
@@ -931,9 +1085,12 @@ bool GSQLBackend::createSlaveDomain(const string &ip, const string &domain, cons
         masters = boost::join(tmp, ", ");
       }
     }
-    format = d_InsertSlaveZoneQuery;
-    snprintf(output,sizeof(output)-1,format.c_str(),sqlEscape(domain).c_str(),sqlEscape(masters).c_str(),sqlEscape(account).c_str());
-    d_db->doCommand(output);
+    d_InsertSlaveZoneQuery_stmt->
+      bind("domain", toLower(domain))->
+      bind("masters", masters)->
+      bind("account", account)->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("Database error trying to insert new slave domain '"+domain+"': "+ e.txtReason());
@@ -943,28 +1100,32 @@ bool GSQLBackend::createSlaveDomain(const string &ip, const string &domain, cons
 
 bool GSQLBackend::deleteDomain(const string &domain)
 {
-  string sqlDomain = sqlEscape(toLower(domain));
-
   DomainInfo di;
   if (!getDomainInfo(domain, di)) {
     return false;
   }
 
-  string recordsQuery = (GSQLformat(d_DeleteZoneQuery) % di.id).str();
-  string metadataQuery;
-  string keysQuery;
-  string commentsQuery = (GSQLformat(d_DeleteCommentsQuery) % di.id).str();
-  string domainQuery = (GSQLformat(d_DeleteDomainQuery) % sqlDomain).str();
-
-  metadataQuery = (GSQLformat(d_ClearDomainAllMetadataQuery) % sqlDomain).str();
-  keysQuery = (GSQLformat(d_ClearDomainAllKeysQuery) % sqlDomain).str();
-
   try {
-    d_db->doCommand(recordsQuery);
-    d_db->doCommand(metadataQuery);
-    d_db->doCommand(keysQuery);
-    d_db->doCommand(commentsQuery);
-    d_db->doCommand(domainQuery);
+    d_DeleteZoneQuery_stmt->
+      bind("domain_id", di.id)->
+      execute()->
+      reset();
+    d_ClearDomainAllMetadataQuery_stmt->
+      bind("domain", toLower(domain))->
+      execute()->
+      reset();
+    d_ClearDomainAllKeysQuery_stmt->
+      bind("domain", toLower(domain))->
+      execute()->
+      reset();
+    d_DeleteCommentsQuery_stmt->
+      bind("domain_id", di.id)->
+      execute()->
+      reset();
+    d_DeleteDomainQuery_stmt->
+      bind("domain", toLower(domain))->
+      execute()->
+      reset();
   }
   catch(SSqlException &e) {
     throw PDNSException("Database error trying to delete domain '"+domain+"': "+ e.txtReason());
@@ -975,114 +1136,79 @@ bool GSQLBackend::deleteDomain(const string &domain)
 void GSQLBackend::getAllDomains(vector<DomainInfo> *domains, bool include_disabled)
 {
   DLOG(L<<"GSQLBackend retrieving all domains."<<endl);
-  string query = (GSQLformat(d_getAllDomainsQuery) % (int)include_disabled).str();
 
   try {
-    d_db->doQuery(query);
+    d_getAllDomainsQuery_stmt->
+      bind("include_disabled", (int)include_disabled)->
+      execute();
+
+    SSqlStatement::row_t row;
+    while (d_getAllDomainsQuery_stmt->hasNextRow()) {
+      d_getAllDomainsQuery_stmt->nextRow(row);
+      DomainInfo di;
+      di.id = atol(row[0].c_str());
+      di.zone = row[1];
+  
+      if (!row[4].empty()) {
+        stringtok(di.masters, row[4], " ,\t");
+      }
+      di.last_check=atol(row[6].c_str());
+  
+      SOAData sd;
+      fillSOAData(row[2], sd);
+      di.serial = sd.serial;
+      if (!row[5].empty()) {
+        di.notified_serial = atol(row[5].c_str());
+      }
+      
+      if (pdns_iequals(row[3], "MASTER"))
+        di.kind = DomainInfo::Master;
+      else if (pdns_iequals(row[3], "SLAVE"))
+        di.kind = DomainInfo::Slave;
+      else
+        di.kind = DomainInfo::Native;
+  
+      di.backend = this;
+  
+      domains->push_back(di);
+    }
+    d_getAllDomainsQuery_stmt->reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("Database error trying to retrieve all domains:" + e.txtReason());
   }
-
-  SSql::row_t row;
-  while (d_db->getRow(row)) {
-
-    DomainInfo di;
-    di.id = atol(row[0].c_str());
-    di.zone = row[1];
-
-    if (!row[4].empty()) {
-      stringtok(di.masters, row[4], " ,\t");
-    }
-    di.last_check=atol(row[6].c_str());
-
-    SOAData sd;
-    fillSOAData(row[2], sd);
-    di.serial = sd.serial;
-    if (!row[5].empty()) {
-      di.notified_serial = atol(row[5].c_str());
-    }
-    
-    if (pdns_iequals(row[3], "MASTER"))
-      di.kind = DomainInfo::Master;
-    else if (pdns_iequals(row[3], "SLAVE"))
-      di.kind = DomainInfo::Slave;
-    else
-      di.kind = DomainInfo::Native;
-
-    di.backend = this;
-
-    domains->push_back(di);
-  }
-}
-
-bool GSQLBackend::get(DNSResourceRecord &r)
-{
-  // L << "GSQLBackend get() was called for "<<qtype.getName() << " record: ";
-  SSql::row_t row;
-  if(d_db->getRow(row)) {
-    if (row[1].empty())
-        r.ttl = ::arg().asNum( "default-ttl" );
-    else
-        r.ttl=atol(row[1].c_str());
-    if(!d_qname.empty())
-      r.qname=d_qname;
-    else
-      r.qname=row[6];
-    r.qtype=row[3];
-
-    if (r.qtype==QType::MX || r.qtype==QType::SRV)
-      r.content=row[2]+" "+row[0];
-    else
-      r.content=row[0];
-
-    r.last_modified=0;
-
-    if(d_dnssecQueries)
-      r.auth = !row[7].empty() && row[7][0]=='1';
-    else
-      r.auth = 1; 
-
-    r.disabled = !row[5].empty() && row[5][0]=='1';
-
-    r.domain_id=atoi(row[4].c_str());
-    return true;
-  }
-  
-  return false;
 }
 
 bool GSQLBackend::replaceRRSet(uint32_t domain_id, const string& qname, const QType& qt, const vector<DNSResourceRecord>& rrset)
 {
-  string query;
-  if (qt != QType::ANY) {
-    query = (GSQLformat(d_DeleteRRSetQuery)
-             % domain_id
-             % sqlEscape(qname)
-             % sqlEscape(qt.getName())
-      ).str();
-  } else {
-    query = (GSQLformat(d_DeleteNamesQuery)
-             % domain_id
-             % sqlEscape(qname)
-      ).str();
-  }
   try {
-    d_db->doCommand(query);
+    if (qt != QType::ANY) {
+      d_DeleteRRSetQuery_stmt->
+        bind("domain_id", domain_id)->
+        bind("qname", qname)->
+        bind("qtype", qt.getName())->
+        execute()->
+        reset();
+    } else {
+      d_DeleteNamesQuery_stmt->
+        bind("domain_id", domain_id)->
+        bind("qname", qname)->
+        execute()->
+        reset();
+    }
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to delete RRSet: "+e.txtReason());
   }
 
   if (rrset.empty()) {
-    // zap comments for now non-existing rrset
-    query = (GSQLformat(d_DeleteCommentRRsetQuery)
-             % domain_id
-             % sqlEscape(qname)
-             % sqlEscape(qt.getName())
-      ).str();
     try {
-      d_db->doCommand(query);
+      d_DeleteCommentRRsetQuery_stmt->
+        bind("domain_id", domain_id)->
+        bind("qname", qname)->
+        bind("qtype", qt.getName())->
+        execute()->
+        reset();
     }
     catch (SSqlException &e) {
       throw PDNSException("GSQLBackend unable to delete comment: "+e.txtReason());
@@ -1099,44 +1225,48 @@ bool GSQLBackend::feedRecord(const DNSResourceRecord &r, string *ordername)
 {
   int prio=0;
   string content(r.content);
-
-  if(r.qtype == QType::MX || r.qtype == QType::SRV) {
+  if (r.qtype == QType::MX || r.qtype == QType::SRV) {
     prio=atoi(content.c_str());
-
     string::size_type pos = content.find_first_not_of("0123456789");
     if(pos != string::npos)
       boost::erase_head(content, pos);
     trim_left(content);
- }
-
-  string query;
-
-  if(d_dnssecQueries && ordername)
-    query = (GSQLformat(d_InsertRecordOrderQuery)
-             % sqlEscape(content)
-             % r.ttl
-             % prio
-             % sqlEscape(r.qtype.getName())
-             % r.domain_id
-             % (int)r.disabled
-             % toLower(sqlEscape(r.qname))
-             % sqlEscape(*ordername)
-             % (int)(r.auth)
-      ).str();
-  else
-    query = (GSQLformat(d_InsertRecordQuery)
-             % sqlEscape(content)
-             % r.ttl
-             % prio
-             % sqlEscape(r.qtype.getName())
-             % r.domain_id
-             % (int)r.disabled
-             % toLower(sqlEscape(r.qname))
-             % (int)(r.auth || !d_dnssecQueries)
-      ).str();
+  }
 
   try {
-    d_db->doCommand(query);
+    if(d_dnssecQueries && ordername)
+    {
+      d_InsertRecordOrderQuery_stmt->
+        bind("content",content)->
+        bind("ttl",r.ttl)->
+        bind("priority",prio)->
+        bind("qtype",r.qtype.getName())->
+        bind("domain_id",r.domain_id)->
+        bind("disabled",r.disabled)->
+        bind("qname",toLower(r.qname));
+        if (ordername == NULL)
+          d_InsertRecordOrderQuery_stmt->bindNull("ordername");
+        else 
+          d_InsertRecordOrderQuery_stmt->bind("ordername",*ordername);
+        d_InsertRecordOrderQuery_stmt->
+        bind("auth",r.auth)->
+        execute()->
+        reset();
+    }
+    else
+    {
+      d_InsertRecordQuery_stmt->
+        bind("content",content)->
+        bind("ttl",r.ttl)->
+        bind("priority",prio)->
+        bind("qtype",r.qtype.getName())-> 
+        bind("domain_id",r.domain_id)->
+        bind("disabled",r.disabled)->
+        bind("qname",toLower(r.qname))->
+        bind("auth", (r.auth || !d_dnssecQueries))->
+        execute()->
+        reset();
+    }
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to feed record: "+e.txtReason());
@@ -1150,15 +1280,13 @@ bool GSQLBackend::feedEnts(int domain_id, map<string,bool>& nonterm)
   pair<string,bool> nt;
 
   BOOST_FOREACH(nt, nonterm) {
-
-    query = (GSQLformat(d_InsertEntQuery)
-             % domain_id
-             % toLower(sqlEscape(nt.first))
-             % (int)(nt.second || !d_dnssecQueries)
-      ).str();
-
     try {
-      d_db->doCommand(query);
+      d_InsertEntQuery_stmt->
+        bind("domain_id",domain_id)->
+        bind("qname",toLower(nt.first))->
+        bind("auth",(nt.second || !d_dnssecQueries))->
+        execute()->
+        reset();       
     }
     catch (SSqlException &e) {
       throw PDNSException("GSQLBackend unable to feed empty non-terminal: "+e.txtReason());
@@ -1172,29 +1300,28 @@ bool GSQLBackend::feedEnts3(int domain_id, const string &domain, map<string,bool
   if(!d_dnssecQueries)
       return false;
 
-  string ordername, query;
+  string ordername;
   pair<string,bool> nt;
 
   BOOST_FOREACH(nt, nonterm) {
-
-    if(narrow || !nt.second) {
-      query = (GSQLformat(d_InsertEntQuery)
-               % domain_id
-               % toLower(sqlEscape(nt.first))
-               % nt.second
-       ).str();
-    } else {
-      ordername=toBase32Hex(hashQNameWithSalt(times, salt, nt.first));
-      query = (GSQLformat(d_InsertEntOrderQuery)
-               % domain_id
-               % toLower(sqlEscape(nt.first))
-               % toLower(sqlEscape(ordername))
-               % nt.second
-       ).str();
-    }
-
     try {
-      d_db->doCommand(query);
+      if(narrow || !nt.second) {
+        d_InsertEntQuery_stmt->
+          bind("domain_id",domain_id)->
+          bind("qname",toLower(nt.first))->
+          bind("auth", nt.second)->
+          execute()->
+          reset();
+      } else {
+        ordername=toBase32Hex(hashQNameWithSalt(times, salt, nt.first));
+        d_InsertEntOrderQuery_stmt->
+          bind("domain_id",domain_id)->
+          bind("qname",toLower(nt.first))->
+          bind("ordername",toLower(ordername))->
+          bind("auth",nt.second)->
+          execute()->
+          reset();
+      }
     }
     catch (SSqlException &e) {
       throw PDNSException("GSQLBackend unable to feed empty non-terminal: "+e.txtReason());
@@ -1205,13 +1332,14 @@ bool GSQLBackend::feedEnts3(int domain_id, const string &domain, map<string,bool
 
 bool GSQLBackend::startTransaction(const string &domain, int domain_id)
 {
-  char output[1024];
-  if(domain_id >= 0) 
-   snprintf(output,sizeof(output)-1,d_DeleteZoneQuery.c_str(),domain_id);
   try {
-    d_db->doCommand("begin");
-    if(domain_id >= 0)
-     d_db->doCommand(output);
+    d_db->startTransaction();
+    if(domain_id >= 0) {
+      d_DeleteZoneQuery_stmt->
+        bind("domain_id", domain_id)->
+        execute()->
+        reset();
+    }
   }
   catch (SSqlException &e) {
     throw PDNSException("Database failed to start transaction: "+e.txtReason());
@@ -1223,7 +1351,7 @@ bool GSQLBackend::startTransaction(const string &domain, int domain_id)
 bool GSQLBackend::commitTransaction()
 {
   try {
-    d_db->doCommand("commit");
+    d_db->commit();
   }
   catch (SSqlException &e) {
     throw PDNSException("Database failed to commit transaction: "+e.txtReason());
@@ -1234,7 +1362,7 @@ bool GSQLBackend::commitTransaction()
 bool GSQLBackend::abortTransaction()
 {
   try {
-    d_db->doCommand("rollback");
+    d_db->rollback();
   }
   catch(SSqlException &e) {
     throw PDNSException("Database failed to abort transaction: "+string(e.txtReason()));
@@ -1249,21 +1377,19 @@ bool GSQLBackend::calculateSOASerial(const string& domain, const SOAData& sd, ti
     return DNSBackend::calculateSOASerial(domain, sd, serial);
   }
   
-  char output[1024];
-  
-  snprintf(output, sizeof(output)-1,
-           d_ZoneLastChangeQuery.c_str(),
-           sd.domain_id);
-
   try {
-    d_db->doQuery(output, d_result);
+    d_ZoneLastChangeQuery_stmt->
+      bind("domain_id", sd.domain_id)->
+      execute()->
+      getResult(d_result)->
+      reset();
   }
   catch (const SSqlException& e) {
     //DLOG(L<<"GSQLBackend unable to calculate SOA serial: " << e.txtReason()<<endl);
     return false;
   }
-
-  if (not d_result.empty()) {
+  if (!d_result.empty()) {
     serial = atol(d_result[0][0].c_str());
     return true;
   }
@@ -1273,12 +1399,11 @@ bool GSQLBackend::calculateSOASerial(const string& domain, const SOAData& sd, ti
 
 bool GSQLBackend::listComments(const uint32_t domain_id)
 {
-  string query = (GSQLformat(d_ListCommentsQuery)
-                  % domain_id
-    ).str();
-
   try {
-    d_db->doQuery(query);
+    d_query_stmt = d_ListCommentsQuery_stmt;
+    d_query_stmt->
+      bind("domain_id", domain_id)->
+      execute();
   }
   catch(SSqlException &e) {
     throw PDNSException("GSQLBackend list comments query: "+e.txtReason());
@@ -1289,12 +1414,23 @@ bool GSQLBackend::listComments(const uint32_t domain_id)
 
 bool GSQLBackend::getComment(Comment& comment)
 {
-  SSql::row_t row;
+  SSqlStatement::row_t row;
 
-  if (!d_db->getRow(row)) {
+  if (!d_query_stmt->hasNextRow()) {
+    try {
+      d_query_stmt->reset();
+    } catch(SSqlException &e) {
+      throw PDNSException("GSQLBackend comment get: "+e.txtReason());
+    }
+    d_query_stmt = NULL;
     return false;
   }
 
+  try {
+    d_query_stmt->nextRow(row);
+  } catch(SSqlException &e) {
+    throw PDNSException("GSQLBackend comment get: "+e.txtReason());
+  }
   // domain_id,name,type,modified_at,account,comment
   comment.domain_id = atol(row[0].c_str());
   comment.qname = row[1];
@@ -1308,17 +1444,16 @@ bool GSQLBackend::getComment(Comment& comment)
 
 void GSQLBackend::feedComment(const Comment& comment)
 {
-  string query = (GSQLformat(d_InsertCommentQuery)
-                  % comment.domain_id
-                  % toLower(sqlEscape(comment.qname))
-                  % sqlEscape(comment.qtype.getName())
-                  % comment.modified_at
-                  % sqlEscape(comment.account)
-                  % sqlEscape(comment.content)
-    ).str();
-
   try {
-    d_db->doCommand(query);
+    d_InsertCommentQuery_stmt->
+      bind("domain_id",comment.domain_id)->
+      bind("qname",toLower(comment.qname))->
+      bind("qtype",comment.qtype.getName())->
+      bind("modified_at",comment.modified_at)->
+      bind("account",comment.account)->
+      bind("content",comment.content)->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to feed comment: "+e.txtReason());
@@ -1327,15 +1462,13 @@ void GSQLBackend::feedComment(const Comment& comment)
 
 bool GSQLBackend::replaceComments(const uint32_t domain_id, const string& qname, const QType& qt, const vector<Comment>& comments)
 {
-  string query;
-    query = (GSQLformat(d_DeleteCommentRRsetQuery)
-             % domain_id
-             % toLower(sqlEscape(qname))
-             % sqlEscape(qt.getName())
-      ).str();
-
   try {
-    d_db->doCommand(query);
+    d_DeleteCommentRRsetQuery_stmt->
+      bind("domain_id",domain_id)->
+      bind("qname",toLower(qname))->
+      bind("qtype",qt.getName())->
+      execute()->
+      reset();
   }
   catch (SSqlException &e) {
     throw PDNSException("GSQLBackend unable to delete comment: "+e.txtReason());
@@ -1347,3 +1480,7 @@ bool GSQLBackend::replaceComments(const uint32_t domain_id, const string& qname,
 
   return true;
 }
+
+SSqlStatement::~SSqlStatement() { 
+// make sure vtable won't break 
+}
index e8acf757d1493caab25186a5add1ee529c175508..ed091e110a676c6b61678109b1db4451f1a1423b 100644 (file)
@@ -16,19 +16,147 @@ public:
   GSQLBackend(const string &mode, const string &suffix); //!< Makes our connection to the database. Throws an exception if it fails.
   virtual ~GSQLBackend()
   {
+    freeStatements();
     if(d_db)
-      delete d_db;
+      delete d_db;    
   }
   
   void setDB(SSql *db)
   {
+    freeStatements();
+    delete d_db;
     d_db=db;
     if (d_db) {
       d_db->setLog(::arg().mustDo("query-logging"));
+      d_NoIdQuery_stmt = d_db->prepare(d_NoIdQuery, 2);
+      d_IdQuery_stmt = d_db->prepare(d_IdQuery, 3);
+      d_ANYNoIdQuery_stmt = d_db->prepare(d_ANYNoIdQuery, 1);
+      d_ANYIdQuery_stmt = d_db->prepare(d_ANYIdQuery, 2);
+      d_listQuery_stmt = d_db->prepare(d_listQuery, 2);
+      d_listSubZoneQuery_stmt = d_db->prepare(d_listSubZoneQuery, 3);
+      d_MasterOfDomainsZoneQuery_stmt = d_db->prepare(d_MasterOfDomainsZoneQuery, 1);
+      d_InfoOfDomainsZoneQuery_stmt = d_db->prepare(d_InfoOfDomainsZoneQuery, 1);
+      d_InfoOfAllSlaveDomainsQuery_stmt = d_db->prepare(d_InfoOfAllSlaveDomainsQuery, 0);
+      d_SuperMasterInfoQuery_stmt = d_db->prepare(d_SuperMasterInfoQuery, 2);
+      d_GetSuperMasterIPs_stmt = d_db->prepare(d_GetSuperMasterIPs, 2);
+      d_InsertZoneQuery_stmt = d_db->prepare(d_InsertZoneQuery, 1);
+      d_InsertSlaveZoneQuery_stmt = d_db->prepare(d_InsertSlaveZoneQuery, 3);
+      d_InsertRecordQuery_stmt = d_db->prepare(d_InsertRecordQuery, 8);
+      d_InsertEntQuery_stmt = d_db->prepare(d_InsertEntQuery, 3);
+      d_InsertRecordOrderQuery_stmt = d_db->prepare(d_InsertRecordOrderQuery, 9);
+      d_InsertEntOrderQuery_stmt = d_db->prepare(d_InsertEntOrderQuery, 4);
+      d_UpdateMasterOfZoneQuery_stmt = d_db->prepare(d_UpdateMasterOfZoneQuery, 2);
+      d_UpdateKindOfZoneQuery_stmt = d_db->prepare(d_UpdateKindOfZoneQuery, 2);
+      d_UpdateSerialOfZoneQuery_stmt = d_db->prepare(d_UpdateSerialOfZoneQuery, 2);
+      d_UpdateLastCheckofZoneQuery_stmt = d_db->prepare(d_UpdateLastCheckofZoneQuery, 2);
+      d_InfoOfAllMasterDomainsQuery_stmt = d_db->prepare(d_InfoOfAllMasterDomainsQuery, 0);
+      d_DeleteDomainQuery_stmt = d_db->prepare(d_DeleteDomainQuery, 1);
+      d_DeleteZoneQuery_stmt = d_db->prepare(d_DeleteZoneQuery, 1);
+      d_DeleteRRSetQuery_stmt = d_db->prepare(d_DeleteRRSetQuery, 3);
+      d_DeleteNamesQuery_stmt = d_db->prepare(d_DeleteNamesQuery, 2);
+      d_ZoneLastChangeQuery_stmt = d_db->prepare(d_ZoneLastChangeQuery, 1);
+      d_firstOrderQuery_stmt = d_db->prepare(d_firstOrderQuery, 1);
+      d_beforeOrderQuery_stmt = d_db->prepare(d_beforeOrderQuery, 2);
+      d_afterOrderQuery_stmt = d_db->prepare(d_afterOrderQuery, 2);
+      d_lastOrderQuery_stmt = d_db->prepare(d_lastOrderQuery, 1);
+      d_setOrderAuthQuery_stmt = d_db->prepare(d_setOrderAuthQuery, 4);
+      d_nullifyOrderNameAndUpdateAuthQuery_stmt = d_db->prepare(d_nullifyOrderNameAndUpdateAuthQuery, 3);
+      d_nullifyOrderNameAndAuthQuery_stmt = d_db->prepare(d_nullifyOrderNameAndAuthQuery, 3);
+      d_nullifyOrderNameAndAuthENTQuery_stmt = d_db->prepare(d_nullifyOrderNameAndAuthENTQuery, 0);
+      d_setAuthOnDsRecordQuery_stmt = d_db->prepare(d_setAuthOnDsRecordQuery, 2);
+      d_removeEmptyNonTerminalsFromZoneQuery_stmt = d_db->prepare(d_removeEmptyNonTerminalsFromZoneQuery, 1);
+      d_insertEmptyNonTerminalQuery_stmt = d_db->prepare(d_insertEmptyNonTerminalQuery, 2);
+      d_deleteEmptyNonTerminalQuery_stmt = d_db->prepare(d_deleteEmptyNonTerminalQuery, 2);
+      d_AddDomainKeyQuery_stmt = d_db->prepare(d_AddDomainKeyQuery, 4);
+      d_ListDomainKeysQuery_stmt = d_db->prepare(d_ListDomainKeysQuery, 1);
+      d_GetAllDomainMetadataQuery_stmt = d_db->prepare(d_GetAllDomainMetadataQuery, 1);
+      d_GetDomainMetadataQuery_stmt = d_db->prepare(d_GetDomainMetadataQuery, 2);
+      d_ClearDomainMetadataQuery_stmt = d_db->prepare(d_ClearDomainMetadataQuery, 2);
+      d_ClearDomainAllMetadataQuery_stmt = d_db->prepare(d_ClearDomainAllMetadataQuery, 1);
+      d_SetDomainMetadataQuery_stmt = d_db->prepare(d_SetDomainMetadataQuery, 3);
+      d_RemoveDomainKeyQuery_stmt = d_db->prepare(d_RemoveDomainKeyQuery, 2);
+      d_ActivateDomainKeyQuery_stmt = d_db->prepare(d_ActivateDomainKeyQuery, 2);
+      d_DeactivateDomainKeyQuery_stmt = d_db->prepare(d_DeactivateDomainKeyQuery, 2);
+      d_ClearDomainAllKeysQuery_stmt = d_db->prepare(d_ClearDomainAllKeysQuery, 1);
+      d_getTSIGKeyQuery_stmt = d_db->prepare(d_getTSIGKeyQuery, 1);
+      d_setTSIGKeyQuery_stmt = d_db->prepare(d_setTSIGKeyQuery, 3);
+      d_deleteTSIGKeyQuery_stmt = d_db->prepare(d_deleteTSIGKeyQuery, 1);
+      d_getTSIGKeysQuery_stmt = d_db->prepare(d_getTSIGKeysQuery, 0);
+      d_getAllDomainsQuery_stmt = d_db->prepare(d_getAllDomainsQuery, 1);
+      d_ListCommentsQuery_stmt = d_db->prepare(d_ListCommentsQuery, 1);
+      d_InsertCommentQuery_stmt = d_db->prepare(d_InsertCommentQuery, 6);
+      d_DeleteCommentRRsetQuery_stmt = d_db->prepare(d_DeleteCommentRRsetQuery, 3);
+      d_DeleteCommentsQuery_stmt = d_db->prepare(d_DeleteCommentsQuery, 1);
     }
   }
+
+  void release(SSqlStatement **stmt) {
+    delete *stmt;
+    *stmt = NULL;
+  }
   
-  virtual string sqlEscape(const string &name);
+  void freeStatements() {
+    release(&d_NoIdQuery_stmt);
+    release(&d_IdQuery_stmt);
+    release(&d_ANYNoIdQuery_stmt);
+    release(&d_ANYIdQuery_stmt);
+    release(&d_listQuery_stmt);
+    release(&d_listSubZoneQuery_stmt);
+    release(&d_MasterOfDomainsZoneQuery_stmt);
+    release(&d_InfoOfDomainsZoneQuery_stmt);
+    release(&d_InfoOfAllSlaveDomainsQuery_stmt);
+    release(&d_SuperMasterInfoQuery_stmt);
+    release(&d_GetSuperMasterIPs_stmt);
+    release(&d_InsertZoneQuery_stmt);
+    release(&d_InsertSlaveZoneQuery_stmt);
+    release(&d_InsertRecordQuery_stmt);
+    release(&d_InsertEntQuery_stmt);
+    release(&d_InsertRecordOrderQuery_stmt);
+    release(&d_InsertEntOrderQuery_stmt);
+    release(&d_UpdateMasterOfZoneQuery_stmt);
+    release(&d_UpdateKindOfZoneQuery_stmt);
+    release(&d_UpdateSerialOfZoneQuery_stmt);
+    release(&d_UpdateLastCheckofZoneQuery_stmt);
+    release(&d_InfoOfAllMasterDomainsQuery_stmt);
+    release(&d_DeleteDomainQuery_stmt);
+    release(&d_DeleteZoneQuery_stmt);
+    release(&d_DeleteRRSetQuery_stmt);
+    release(&d_DeleteNamesQuery_stmt);
+    release(&d_ZoneLastChangeQuery_stmt);
+    release(&d_firstOrderQuery_stmt);
+    release(&d_beforeOrderQuery_stmt);
+    release(&d_afterOrderQuery_stmt);
+    release(&d_lastOrderQuery_stmt);
+    release(&d_setOrderAuthQuery_stmt);
+    release(&d_nullifyOrderNameAndUpdateAuthQuery_stmt);
+    release(&d_nullifyOrderNameAndAuthQuery_stmt);
+    release(&d_nullifyOrderNameAndAuthENTQuery_stmt);
+    release(&d_setAuthOnDsRecordQuery_stmt);
+    release(&d_removeEmptyNonTerminalsFromZoneQuery_stmt);
+    release(&d_insertEmptyNonTerminalQuery_stmt);
+    release(&d_deleteEmptyNonTerminalQuery_stmt);
+    release(&d_AddDomainKeyQuery_stmt);
+    release(&d_ListDomainKeysQuery_stmt);
+    release(&d_GetAllDomainMetadataQuery_stmt);
+    release(&d_GetDomainMetadataQuery_stmt);
+    release(&d_ClearDomainMetadataQuery_stmt);
+    release(&d_ClearDomainAllMetadataQuery_stmt);
+    release(&d_SetDomainMetadataQuery_stmt);
+    release(&d_RemoveDomainKeyQuery_stmt);
+    release(&d_ActivateDomainKeyQuery_stmt);
+    release(&d_DeactivateDomainKeyQuery_stmt);
+    release(&d_ClearDomainAllKeysQuery_stmt);
+    release(&d_getTSIGKeyQuery_stmt);
+    release(&d_setTSIGKeyQuery_stmt);
+    release(&d_deleteTSIGKeyQuery_stmt);
+    release(&d_getTSIGKeysQuery_stmt);
+    release(&d_getAllDomainsQuery_stmt);
+    release(&d_ListCommentsQuery_stmt);
+    release(&d_InsertCommentQuery_stmt);
+    release(&d_DeleteCommentRRsetQuery_stmt);
+    release(&d_DeleteCommentsQuery_stmt);
+  }
+
   void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
   bool list(const string &target, int domain_id, bool include_disabled=false);
   bool get(DNSResourceRecord &r);
@@ -90,7 +218,7 @@ public:
 private:
   string d_qname;
   SSql *d_db;
-  SSql::result_t d_result;
+  SSqlStatement::result_t d_result;
 
   string d_NoIdQuery;
   string d_IdQuery;
@@ -163,6 +291,67 @@ private:
   string d_DeleteCommentRRsetQuery;
   string d_DeleteCommentsQuery;
 
+  SSqlStatement* d_query_stmt;
+
+  SSqlStatement* d_NoIdQuery_stmt;
+  SSqlStatement* d_IdQuery_stmt;
+  SSqlStatement* d_ANYNoIdQuery_stmt;
+  SSqlStatement* d_ANYIdQuery_stmt;
+  SSqlStatement* d_listQuery_stmt;
+  SSqlStatement* d_listSubZoneQuery_stmt;
+  SSqlStatement* d_MasterOfDomainsZoneQuery_stmt;
+  SSqlStatement* d_InfoOfDomainsZoneQuery_stmt;
+  SSqlStatement* d_InfoOfAllSlaveDomainsQuery_stmt;
+  SSqlStatement* d_SuperMasterInfoQuery_stmt;
+  SSqlStatement* d_GetSuperMasterIPs_stmt;
+  SSqlStatement* d_InsertZoneQuery_stmt;
+  SSqlStatement* d_InsertSlaveZoneQuery_stmt;
+  SSqlStatement* d_InsertRecordQuery_stmt;
+  SSqlStatement* d_InsertEntQuery_stmt;
+  SSqlStatement* d_InsertRecordOrderQuery_stmt;
+  SSqlStatement* d_InsertEntOrderQuery_stmt;
+  SSqlStatement* d_UpdateMasterOfZoneQuery_stmt;
+  SSqlStatement* d_UpdateKindOfZoneQuery_stmt;
+  SSqlStatement* d_UpdateSerialOfZoneQuery_stmt;
+  SSqlStatement* d_UpdateLastCheckofZoneQuery_stmt;
+  SSqlStatement* d_InfoOfAllMasterDomainsQuery_stmt;
+  SSqlStatement* d_DeleteDomainQuery_stmt;
+  SSqlStatement* d_DeleteZoneQuery_stmt;
+  SSqlStatement* d_DeleteRRSetQuery_stmt;
+  SSqlStatement* d_DeleteNamesQuery_stmt;
+  SSqlStatement* d_ZoneLastChangeQuery_stmt;
+  SSqlStatement* d_firstOrderQuery_stmt;
+  SSqlStatement* d_beforeOrderQuery_stmt;
+  SSqlStatement* d_afterOrderQuery_stmt;
+  SSqlStatement* d_lastOrderQuery_stmt;
+  SSqlStatement* d_setOrderAuthQuery_stmt;
+  SSqlStatement* d_nullifyOrderNameAndUpdateAuthQuery_stmt;
+  SSqlStatement* d_nullifyOrderNameAndAuthQuery_stmt;
+  SSqlStatement* d_nullifyOrderNameAndAuthENTQuery_stmt;
+  SSqlStatement* d_setAuthOnDsRecordQuery_stmt;
+  SSqlStatement* d_removeEmptyNonTerminalsFromZoneQuery_stmt;
+  SSqlStatement* d_insertEmptyNonTerminalQuery_stmt;
+  SSqlStatement* d_deleteEmptyNonTerminalQuery_stmt;
+  SSqlStatement* d_AddDomainKeyQuery_stmt;
+  SSqlStatement* d_ListDomainKeysQuery_stmt;
+  SSqlStatement* d_GetAllDomainMetadataQuery_stmt;
+  SSqlStatement* d_GetDomainMetadataQuery_stmt;
+  SSqlStatement* d_ClearDomainMetadataQuery_stmt;
+  SSqlStatement* d_ClearDomainAllMetadataQuery_stmt;
+  SSqlStatement* d_SetDomainMetadataQuery_stmt;
+  SSqlStatement* d_RemoveDomainKeyQuery_stmt;
+  SSqlStatement* d_ActivateDomainKeyQuery_stmt;
+  SSqlStatement* d_DeactivateDomainKeyQuery_stmt;
+  SSqlStatement* d_ClearDomainAllKeysQuery_stmt;
+  SSqlStatement* d_getTSIGKeyQuery_stmt;
+  SSqlStatement* d_setTSIGKeyQuery_stmt;
+  SSqlStatement* d_deleteTSIGKeyQuery_stmt;
+  SSqlStatement* d_getTSIGKeysQuery_stmt;
+  SSqlStatement* d_getAllDomainsQuery_stmt;
+  SSqlStatement* d_ListCommentsQuery_stmt;
+  SSqlStatement* d_InsertCommentQuery_stmt;
+  SSqlStatement* d_DeleteCommentRRsetQuery_stmt;
+  SSqlStatement* d_DeleteCommentsQuery_stmt;
 protected:
   bool d_dnssecQueries;
 };
index dbad606a1f05513c843ed7a349a1a9a9fa580920..dbf81d85963a271d79519e5b9919c4f9fed0f298 100644 (file)
@@ -12,7 +12,7 @@
 #include <string>
 #include <vector>
 #include "../../namespaces.hh"
-
+#include <inttypes.h>
 
 class SSqlException 
 {
@@ -29,18 +29,40 @@ public:
 private:
   string d_reason;
 };
-
-class SSql
+class SSqlStatement
 {
 public:
   typedef vector<string> row_t;
   typedef vector<row_t> result_t;
+
+  virtual SSqlStatement* bind(const string& name, bool value)=0;
+  virtual SSqlStatement* bind(const string& name, int value)=0;
+  virtual SSqlStatement* bind(const string& name, uint32_t value)=0;
+  virtual SSqlStatement* bind(const string& name, long value)=0;
+  virtual SSqlStatement* bind(const string& name, unsigned long value)=0;
+  virtual SSqlStatement* bind(const string& name, long long value)=0;;
+  virtual SSqlStatement* bind(const string& name, unsigned long long value)=0;
+  virtual SSqlStatement* bind(const string& name, const std::string& value)=0;
+  virtual SSqlStatement* bindNull(const string& name)=0;
+  virtual SSqlStatement* execute()=0;;
+  virtual bool hasNextRow()=0;
+  virtual SSqlStatement* nextRow(row_t& row)=0;
+  virtual SSqlStatement* getResult(result_t& result)=0;
+  virtual SSqlStatement* reset()=0;
+  virtual const std::string& getQuery()=0;
+  virtual ~SSqlStatement();
+};
+
+class SSql
+{
+public:
   virtual SSqlException sPerrorException(const string &reason)=0;
-  virtual int doQuery(const string &query, result_t &result)=0;
-  virtual int doQuery(const string &query)=0;
-  virtual int doCommand(const string &query)=0;
-  virtual bool getRow(row_t &row)=0;
-  virtual string escape(const string &name)=0;
+  virtual SSqlStatement* prepare(const string& query, int nparams)=0;
+  virtual void execute(const string& query)=0;
+  virtual void startTransaction()=0;
+  virtual void rollback()=0;
+  virtual void commit()=0;
   virtual void setLog(bool state){}
   virtual ~SSql(){};
 };
index abcfd0c10195d68869b83f5298abf709b54285e0..9d99b2d082b56847d479659e03e7f85bf7afb26e 100644 (file)
@@ -136,12 +136,15 @@ void loadMainConfig(const std::string& configdir)
   ::arg().set("max-ent-entries", "Maximum number of empty non-terminals in a zone")="100000";
   ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
   ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
-
+  ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
+  ::arg().set("loglevel","Amount of logging. Higher is more.")="0";
   ::arg().setSwitch("direct-dnskey","Fetch DNSKEY RRs from backend during DNSKEY synthesis")="no";
   ::arg().set("max-nsec3-iterations","Limit the number of NSEC3 hash iterations")="500"; // RFC5155 10.3
   ::arg().set("max-signature-cache-entries", "Maximum number of signatures cache entries")="";
   ::arg().laxFile(configname.c_str());
 
+  L.toConsole((Logger::Urgency)(::arg().asNum("loglevel")));  
+
   BackendMakers().launch(::arg()["launch"]); // vrooooom!
   ::arg().laxFile(configname.c_str());    
   //cerr<<"Backend: "<<::arg()["launch"]<<", '" << ::arg()["gmysql-dbname"] <<"'" <<endl;
@@ -164,7 +167,6 @@ void loadMainConfig(const std::string& configdir)
   ::arg().set("soa-refresh-default","Default SOA refresh")="10800";
   ::arg().set("soa-retry-default","Default SOA retry")="3600";
   ::arg().set("soa-expire-default","Default SOA expire")="604800";
-  ::arg().setSwitch("query-logging","Hint backends that queries should be logged")="no";
   ::arg().set("soa-minimum-ttl","Default SOA minimum ttl")="3600";    
 
   UeberBackend::go();
@@ -1325,8 +1327,9 @@ try
       SSQLite3 db(cmds[1], true); // create=ok
       vector<string> statements;
       stringtok(statements, sqlCreate, ";");
-      BOOST_FOREACH(const string& statement, statements)
-        db.doCommand(statement);
+      BOOST_FOREACH(const string& statement, statements) {
+        db.execute(statement);
+      }
     }
     catch(SSqlException& se) {
       throw PDNSException("Error creating database in BIND backend: "+se.txtReason());
index 9fecceaaa496a08a6340c959edbbe3d07956a188..e98933aea118a166b370446a89cb4e1e282dbf35 100644 (file)
 #include "misc.hh"
 #include <unistd.h>
 
+/*
+** Set all the parameters in the compiled SQL statement to NULL.
+*
+* copied from sqlite 3.3.6 // cmouse 
+*/
+int pdns_sqlite3_clear_bindings(sqlite3_stmt *pStmt){
+  int i;
+  int rc = SQLITE_OK;
+  for(i=1; rc==SQLITE_OK && i<=sqlite3_bind_parameter_count(pStmt); i++){
+    rc = sqlite3_bind_null(pStmt, i);
+  }
+  return rc;
+}
+
+void my_trace(void *foo, const char *sql) {
+  L<<Logger::Warning<< "Query: " << sql << endl;
+}
+
+class SSQLite3Statement: public SSqlStatement 
+{
+public:
+  SSQLite3Statement(SSQLite3 *db, bool dolog, const string& query) 
+  {
+    const char *pTail;
+    this->d_query = query;
+    this->d_dolog = dolog;
+    d_db = db;
+#if SQLITE_VERSION_NUMBER >= 3003009
+    if (sqlite3_prepare_v2(d_db->db(), query.c_str(), -1, &d_stmt, &pTail ) != SQLITE_OK)
+#else
+    if (sqlite3_prepare(d_db->db(), query.c_str(), -1, &d_stmt, &pTail ) != SQLITE_OK)
+#endif
+      throw SSqlException(string("Unable to compile SQLite statement : ")+sqlite3_errmsg(d_db->db()));
+    if (pTail && strlen(pTail)>0)
+      L<<Logger::Warning<<"Sqlite3 command partially processed. Unprocessed part: "<<pTail<<endl;
+  }
+
+  int name2idx(const string& name) {
+    string zName = string(":")+name;
+    return sqlite3_bind_parameter_index(d_stmt, zName.c_str());
+    // XXX: support @ and $?    
+  }
+
+  SSqlStatement* bind(const string& name, bool value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int(d_stmt, idx, value ? 1 : 0); }; return this; }
+  SSqlStatement* bind(const string& name, int value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int(d_stmt, idx, value); }; return this; }
+  SSqlStatement* bind(const string& name, uint32_t value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
+  SSqlStatement* bind(const string& name, long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
+  SSqlStatement* bind(const string& name, unsigned long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
+  SSqlStatement* bind(const string& name, long long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; };
+  SSqlStatement* bind(const string& name, unsigned long long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
+  SSqlStatement* bind(const string& name, const std::string& value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_text(d_stmt, idx, value.c_str(), value.size(), SQLITE_TRANSIENT); }; return this; }
+  SSqlStatement* bindNull(const string& name) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_null(d_stmt, idx); }; return this; }
+
+  SSqlStatement* execute() {
+    int attempts = d_db->inTransaction(); // try only once
+    while(attempts < 2 && (d_rc = sqlite3_step(d_stmt)) == SQLITE_BUSY) attempts++;
+
+    if (d_rc != SQLITE_ROW && d_rc != SQLITE_DONE) {
+      // failed.
+      if (d_rc == SQLITE_CANTOPEN) 
+        throw SSqlException(string("CANTOPEN error in sqlite3, often caused by unwritable sqlite3 db *directory*: ")+string(sqlite3_errmsg(d_db->db())));
+      throw SSqlException(string("Error while retrieving SQLite query results: ")+string(sqlite3_errmsg(d_db->db())));
+    }
+    return this;
+  }
+  bool hasNextRow() { return d_rc == SQLITE_ROW; }
+
+  SSqlStatement* nextRow(row_t& row) {
+    row.clear();
+    int numCols = sqlite3_column_count(d_stmt);
+    row.reserve(numCols); // preallocate memory
+    // Another row received, process it.
+    for ( int i=0; i<numCols; i++)
+    {
+      if (sqlite3_column_type(d_stmt,i) == SQLITE_NULL) {
+        row.push_back("");
+      } else {
+        const char *pData = (const char*) sqlite3_column_text(d_stmt, i);
+        row.push_back(string(pData, sqlite3_column_bytes(d_stmt, i))); 
+      }
+    }
+    d_rc = sqlite3_step(d_stmt);
+    return this;
+  }
+
+  SSqlStatement* getResult(result_t& result) {
+    result.clear();
+    while(hasNextRow()) {
+      row_t row;
+      nextRow(row);
+      result.push_back(row);
+    }
+    return this;
+  }
+
+  SSqlStatement* reset() {
+    sqlite3_reset(d_stmt);
+#if SQLITE_VERSION_NUMBER >= 3003009
+    sqlite3_clear_bindings(d_stmt);
+#else
+    pdns_sqlite3_clear_bindings(d_stmt);
+#endif
+    return this;
+  }
+
+  ~SSQLite3Statement() {
+    // deallocate if necessary
+    if (d_stmt) 
+      sqlite3_finalize(d_stmt);
+  }
+
+  const string& getQuery() { return d_query; };
+private:
+  string d_query;
+  sqlite3_stmt* d_stmt;
+  int d_rc;
+  SSQLite3* d_db;
+  bool d_dolog;
+};
+
 // Constructor.
 SSQLite3::SSQLite3( const std::string & database, bool creat )
 {
@@ -23,13 +143,15 @@ SSQLite3::SSQLite3( const std::string & database, bool creat )
 
   if ( sqlite3_open( database.c_str(), &m_pDB)!=SQLITE_OK )
     throw sPerrorException( "Could not connect to the SQLite database '" + database + "'" );
-  m_pStmt = 0;
   m_dolog = 0;
+  m_in_transaction = false;
   sqlite3_busy_handler(m_pDB, busyHandler, 0);
 }
 
 void SSQLite3::setLog(bool state)
-{
+{ 
+  if (state)
+      sqlite3_trace(m_pDB, my_trace, NULL);
   m_dolog=state;
 }
 
@@ -39,60 +161,31 @@ SSQLite3::~SSQLite3()
   int ret;
   for(int n = 0; n < 2 ; ++n) {
     if((ret =sqlite3_close( m_pDB )) != SQLITE_OK) {
-      if(n || !m_pStmt || ret != SQLITE_BUSY) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize
+      if(n || ret != SQLITE_BUSY) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize
         cerr<<"Unable to close down sqlite connection: "<<ret<<endl;
         abort();
       }
-      else {
-        sqlite3_finalize(m_pStmt);
-      }
     }
     else
       break;
   }
 }
 
-
-// Constructs a SSqlException object.
-SSqlException SSQLite3::sPerrorException( const std::string & reason )
-{
-  return SSqlException( reason );
-}
-
-
-// Performs a query.
-int SSQLite3::doQuery( const std::string & query, result_t & result )
-{
-  result.clear();
-
-  doQuery( query );
-
-  row_t row;
-  while( getRow( row ))
-    result.push_back( row );
-
-  return result.size();
+SSqlStatement* SSQLite3::prepare(const string& query, int nparams __attribute__((unused))) {
+  return new SSQLite3Statement(this, m_dolog, query);
 }
 
-
-// Performs a query.
-int SSQLite3::doQuery( const std::string & query )
-{
-  const char *pTail;
-
-  if(m_dolog)
-    L<<Logger::Warning<<"Query: "<<query<<endl;
-  
-  // Execute the query.
-
-#if SQLITE_VERSION_NUMBER >=  3003009
-  if ( sqlite3_prepare_v2( m_pDB, query.c_str(), -1, &m_pStmt, &pTail ) != SQLITE_OK )
-#else
-  if ( sqlite3_prepare( m_pDB, query.c_str(), -1, &m_pStmt, &pTail ) != SQLITE_OK )   
-#endif
-    throw sPerrorException( string("Unable to compile SQLite statement : ")+ sqlite3_errmsg( m_pDB ) );
-
-  return 0;
+void SSQLite3::execute(const string& query) {
+  char *errmsg;
+  int rc;
+  if (sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg) == SQLITE_BUSY) {
+    if (m_in_transaction) {
+        throw("Failed to execute query: " + string(errmsg));
+    } else {
+      if ((rc = sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) && rc != SQLITE_DONE && rc != SQLITE_ROW)
+        throw("Failed to execute query: " + string(errmsg));
+    }
+  }
 }
 
 int SSQLite3::busyHandler(void*, int)
@@ -101,66 +194,23 @@ int SSQLite3::busyHandler(void*, int)
   return 1;
 }
 
-// Returns a row from the result set.
-bool SSQLite3::getRow( row_t & row )
-{
-  int  numCols;
-  int  rc;
-  const char *pData;
-
-  row.clear();
-
-  rc = sqlite3_step( m_pStmt );
-
-  if ( rc == SQLITE_ROW )
-  {
-    numCols = sqlite3_column_count( m_pStmt );
-    // Another row received, process it.
-    for ( int i = 0; i < numCols; i++ )
-    {
-      pData = (const char*) sqlite3_column_text( m_pStmt, i );
-      row.push_back( pData ? pData : "" ); // NULL value to "".
-    }
-
-    return true;
-  }
-
-  if ( rc == SQLITE_DONE )
-  {
-    // We're done, clean up.
-    sqlite3_finalize( m_pStmt );
-    m_pStmt = NULL;
-    return false;
-  }
-  
-  if(rc == SQLITE_CANTOPEN) {
-    string error ="CANTOPEN error in sqlite3, often caused by unwritable sqlite3 db *directory*: "+string(sqlite3_errmsg(m_pDB));
-    sqlite3_finalize(m_pStmt);
-    m_pStmt = 0;
-    throw sPerrorException(error);
-  }
-  
-  // Something went wrong, complain.
-  throw sPerrorException( "Error while retrieving SQLite query results: "+string(sqlite3_errmsg(m_pDB) ));
+void SSQLite3::startTransaction() {
+  execute("begin");
+  m_in_transaction = true;
+}
 
-  // Prevent some compilers from complaining.
-  return false;
+void SSQLite3::rollback() {
+  execute("rollback");
+  m_in_transaction = false;
 }
 
+void SSQLite3::commit() {
+  execute("commit");
+  m_in_transaction = false;
+}
 
-// Escape a SQL query.
-std::string SSQLite3::escape( const std::string & name)
+// Constructs a SSqlException object.
+SSqlException SSQLite3::sPerrorException( const std::string & reason )
 {
-  std::string a;
-
-  for( std::string::const_iterator i = name.begin(); i != name.end(); ++i ) 
-  {
-    if( *i == '\'' || *i == '\\' )
-      a += '\\';
-
-    a += *i;
-  }
-
-  return a;
+  return SSqlException( reason );
 }
-
index 02670af5644ab1fdc79111df6e4be4dfd74403c1..13b6e45cbdb694b759160305dd224d9e7ca91e80 100644 (file)
@@ -20,7 +20,7 @@ private:
   sqlite3_stmt *m_pStmt;
 
   bool m_dolog;
-
+  bool m_in_transaction;
   static int busyHandler(void*, int);
 protected:
 public:
@@ -30,26 +30,17 @@ public:
   //! Destructor.
   ~SSQLite3();
 
-  //! Performs a query and puts answers in result
-  int doQuery( const std::string & query, result_t & result );
-
-  //! Performs a query, caller can retrieve answers with getRow
-  int doQuery( const std::string & query );
+  SSqlStatement* prepare(const string& query, int nparams);
+  void execute(const string& query);
+  void setLog(bool state);
 
-  //! Performs a command that does not return rows
-  int doCommand( const std::string & query )
-  {
-    result_t result;
-    return doQuery(query, result); // 'result' is necessary to force doQuery to do the work, closing Debian bug 280359
-  }
-  
-  //! Returns a row from a result set.
-  bool getRow( row_t & row );
+  void startTransaction();
+  void commit();
+  void rollback();
 
-  //! Escapes the SQL query.
-  std::string escape( const std::string & query );
+  sqlite3 *db() { return this->m_pDB; };
 
-  void setLog(bool state);
+  bool inTransaction() { return m_in_transaction; };
 
   //! Used to create an backend specific exception message.
   SSqlException sPerrorException( const std::string & reason );
index a672986874767a3745ce583195de641ea1386cb8..173d20a5a54127286ddd62ba5be378d908d36d32 100644 (file)
@@ -1,13 +1,26 @@
 source ./backends/gsql-common
 
+set +e
+SQLPLUS=`which sqlplus`
+if [ ! -x "$SQLPLUS" ]; then
+  SQLPLUS=`which sqlplus64`
+fi
+if [ ! -x "$SQLPLUS" ]; then
+  echo "Cannot find sqlplus or sqlplus64 in path"
+  exit 1
+fi
+set -e
+if [ "x$NLS_LANG" = "x" ]; then
+  NLS_LANG="AMERICAN_AMERICA.AL32UTF8"
+fi
 case $context in
        goracle-nodnssec | goracle | goracle-nsec3 | goracle-nsec3-optout | goracle-nsec3-narrow)
                [ -z "$GORACLEUSER" ] && GORACLEUSER=pdns
                [ -z "$GORACLEPASSWD" ] && GORACLEPASSWD=pdns
 
-               echo "START ../modules/goraclebackend/drop-schema.goracle.sql;" | sqlplus -S $GORACLEUSER/$GORACLEPASSWD@xe > goracle.log
-               echo "START ../modules/goraclebackend/schema.goracle.sql;" | sqlplus -S $GORACLEUSER/$GORACLEPASSWD@xe >> goracle.log
-               tosql goracle | sqlplus -S $GORACLEUSER/$GORACLEPASSWD@xe >> goracle.log
+               echo "START ../modules/goraclebackend/drop-schema.goracle.sql;" | $SQLPLUS -S $GORACLEUSER/$GORACLEPASSWD@xe > goracle.log
+               echo "START ../modules/goraclebackend/schema.goracle.sql;" | $SQLPLUS -S $GORACLEUSER/$GORACLEPASSWD@xe >> goracle.log
+               tosql goracle | $SQLPLUS -S $GORACLEUSER/$GORACLEPASSWD@xe >> goracle.log
 
                cat > pdns-goracle.conf << __EOF__
 module-dir=./modules
index cd0d2315c0da650b9b829a116271d9d27dadfce9..55dfb0e5f6a0548c8c9dfb814b08e319bb6fd954 100644 (file)
@@ -1,3 +1,6 @@
+if [ "x$NLS_LANG" = "x" ]; then
+  NLS_LANG="AMERICAN_AMERICA.AL32UTF8"
+fi
        context=${context}-presigned-goracle
        [ -z "$GORACLE2USER" ] && GORACLE2USER=pdns2
        [ -z "$GORACLE2PASSWD" ] && GORACLE2PASSWD=pdns
index edb188db25bcd38c9132428a48e5d098ee9e20d2..741e4dd7abbf8a01969c593e5d9e0c6631c14979 100644 (file)
@@ -8,8 +8,8 @@ case $context in
                dropdb --user="$GPGSQLUSER" "$GPGSQLDB" || echo ignoring mysqladmin drop failure
                createdb --user="$GPGSQLUSER" "$GPGSQLDB"       || echo ignoring mysqladmin drop failure
                psql --user="$GPGSQLUSER" "$GPGSQLDB" < ../modules/gpgsqlbackend/schema.pgsql.sql
-
                tosql gpgsql | psql --user="$GPGSQLUSER" "$GPGSQLDB" 2>&1 | uniq -c
+                psql --user="$GPGSQLUSER" "$GPGSQLDB" -c "ANALYZE"
 
                cat > pdns-gpgsql.conf << __EOF__
 module-dir=./modules