]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
pgmysqlbackend added
authorBert Hubert <bert.hubert@netherlabs.nl>
Wed, 27 Nov 2002 15:23:16 +0000 (15:23 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Wed, 27 Nov 2002 15:23:16 +0000 (15:23 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@6 d19b8d6e-7fed-0310-83ef-9ca221ded41b

modules/gmysqlbackend/INSTALL [new file with mode: 0644]
modules/gmysqlbackend/Makefile [new file with mode: 0644]
modules/gmysqlbackend/gmysqlbackend.cc [new file with mode: 0644]
modules/gmysqlbackend/gmysqlbackend.hh [new file with mode: 0644]
modules/gmysqlbackend/smysql.cc [new file with mode: 0644]
modules/gmysqlbackend/smysql.hh [new file with mode: 0644]
modules/gmysqlbackend/spgsql.cc [new file with mode: 0644]
modules/gmysqlbackend/spgsql.hh [new file with mode: 0644]
modules/gmysqlbackend/ssql.hh [new file with mode: 0644]

diff --git a/modules/gmysqlbackend/INSTALL b/modules/gmysqlbackend/INSTALL
new file mode 100644 (file)
index 0000000..427fff1
--- /dev/null
@@ -0,0 +1,5 @@
+Edit the Makefile so it can find the main PowerDNS sources. This is the
+first line of the Makefile. 
+
+You may want to change other things so it can find your PostgreSQL and MySQL
+sources.
diff --git a/modules/gmysqlbackend/Makefile b/modules/gmysqlbackend/Makefile
new file mode 100644 (file)
index 0000000..e6792ca
--- /dev/null
@@ -0,0 +1,42 @@
+PDNS_HOME              = ../../pdns
+PDNS_LIBDIR            = /usr/lib/powerdns
+
+MYSQL_INCLUDES      = /usr/include/mysql
+MYSQL_LIBDIR         = ./
+MYSQL_LIBS           = -lmysqlclient 
+
+POSTGRES_INCLUDES      = /opt/postgresql/include
+POSTGRES_LIBDIR         = /opt/postgresql-with-3.2/lib
+POSTGRES_LIBS           = -Wl,-Bstatic  -lpq++ -lpq -Wl,-Bdynamic -lssl -lcrypt -lcrypto
+VERSION                        = 2.9
+
+all: libgmysqlbackend.so smysql.o
+
+CPPFLAGS=-I$(PDNS_HOME) -I$(MYSQL_INCLUDES) -I$(POSTGRES_INCLUDES)
+
+LIBS= -L$(POSTGRES_LIBDIR) -L$(MYSQL_LIBDIR)  $(MYSQL_LIBS)  $(POSTGRES_LIBS)
+DIRNAME=pdns-gmypgsqlbackend-$(VERSION)
+
+dist:
+       mkdir $(DIRNAME)
+       cp COPYING smysql.cc spgsql.cc gmysqlbackend.cc gmysqlbackend.hh smysql.hh ssql.hh spgsql.hh Makefile INSTALL $(DIRNAME)
+       tar cvzf $(DIRNAME).tar.gz $(DIRNAME)
+       rm -rf $(DIRNAME)
+       
+deps:
+       g++ -M -g -c $(CXXFLAGS) $(CPPFLAGS) *.cc > deps
+
+-include deps
+
+
+libgmysqlbackend.so: gmysqlbackend.o smysql.o spgsql.o 
+       g++ gmysqlbackend.o -Wl,-rpath -Wl,$(POSTGRES_LIBDIR) smysql.o spgsql.o $(LIBS) -Wl,-soname -Wl,libgmysqlbackend.so.0 -shared  -o libgmysqlbackend.so 
+
+.cc.o:
+       g++ -MD -g -c $(CXXFLAGS) $(CPPFLAGS) $<
+
+clean:
+       rm -f *.o *.so *~ *.d deps
+
+install: libgmysqlbackend.so
+       mv $< $(PDNS_LIBDIR)
\ No newline at end of file
diff --git a/modules/gmysqlbackend/gmysqlbackend.cc b/modules/gmysqlbackend/gmysqlbackend.cc
new file mode 100644 (file)
index 0000000..a1a75b1
--- /dev/null
@@ -0,0 +1,470 @@
+// $Id: gmysqlbackend.cc,v 1.1 2002/11/27 15:23:16 ahu Exp $ 
+#include <string>
+#include <map>
+
+using namespace std;
+
+#include "dns.hh"
+#include "dnsbackend.hh"
+#include "gmysqlbackend.hh"
+#include "dnspacket.hh"
+#include "ueberbackend.hh"
+#include "ahuexception.hh"
+#include "logger.hh"
+#include "arguments.hh"
+#include "smysql.hh"
+#include "spgsql.hh"
+#include <sstream>
+
+
+void gMySQLBackend::setNotified(u_int32_t domain_id, u_int32_t serial)
+{
+  try {
+    d_db->doQuery("update domains set notified_serial="+itoa(serial)+" where id="+itoa(domain_id));
+  }
+  catch(SSqlException &e) {
+    throw AhuException("gMySQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
+  }
+}
+
+void gMySQLBackend::setFresh(u_int32_t domain_id)
+{
+  try {
+    d_db->doQuery("update domains set last_check="+itoa(time(0))+" where id="+itoa(domain_id));
+  }
+  catch (SSqlException &e) {
+    throw AhuException("gMySQLBackend unable to refresh domain_id "+itoa(domain_id)+": "+e.txtReason());
+  }
+}
+
+bool gMySQLBackend::isMaster(const string &domain, const string &ip)
+{
+  try {
+    d_db->doQuery("select master from domains where name='"+sqlEscape(domain)+"' and type='SLAVE'", d_result);
+  }
+  catch (SSqlException &e) {
+    throw AhuException("gMySQLBackend unable to retrieve list of slave domains: "+e.txtReason());
+  }
+
+  if(d_result.empty())
+    return 0;
+  
+  return !strcmp(ip.c_str(),d_result[0][0].c_str());
+}
+
+bool gMySQLBackend::getDomainInfo(const string &domain, DomainInfo &di)
+{
+  /* list all domains that need refreshing for which we are slave, and insert into SlaveDomain:
+     id,name,master IP,serial */
+  
+  try {
+    d_db->doQuery("select id,name,master,last_check,notified_serial,type from domains where name='"+sqlEscape(domain)+"'",d_result);
+  }
+  catch(SSqlException &e) {
+    throw AhuException("gMySQLBackend unable to retrieve information about a domain: "+e.txtReason());
+  }
+
+  int numanswers=d_result.size();
+  if(!numanswers)
+    return false;
+  
+  di.id=atol(d_result[0][0].c_str());
+  di.zone=d_result[0][1];
+  di.master=d_result[0][2];
+  di.last_check=atol(d_result[0][3].c_str());
+  di.backend=this;
+  
+  string type=d_result[0][4];
+  if(type=="SLAVE")
+    di.kind=DomainInfo::Slave;
+  else if(type=="MASTER")
+    di.kind=DomainInfo::Slave;
+  else 
+    di.kind=DomainInfo::Native;
+  
+  return true;
+}
+
+void gMySQLBackend::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("select id,name,master,last_check,type from domains where type='SLAVE'",d_result);
+  }
+  catch (SSqlException &e) {
+    throw AhuException("gMySQLBackend unable to retrieve list of slave domains: "+e.txtReason());
+  }
+
+  vector<DomainInfo>allSlaves;
+  int numanswers=d_result.size();
+  for(int n=0;n<numanswers;++n) { // id,name,master,last_check
+    DomainInfo sd;
+    sd.id=atol(d_result[n][0].c_str());
+    sd.zone=d_result[n][1];
+    sd.master=d_result[n][2];
+    sd.last_check=atol(d_result[n][3].c_str());
+    sd.backend=this;
+    sd.kind=DomainInfo::Slave;
+    allSlaves.push_back(sd);
+  }
+
+  for(vector<DomainInfo>::iterator i=allSlaves.begin();i!=allSlaves.end();++i) {
+    SOAData sdata;
+    sdata.serial=0;
+    sdata.refresh=0;
+    getSOA(i->zone,sdata);
+    if(i->last_check+sdata.refresh<time(0)) {
+      i->serial=sdata.serial;
+      unfreshDomains->push_back(*i);
+    }
+  }
+}
+
+void gMySQLBackend::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("select id,name,master,last_check,notified_serial,type from domains where type='MASTER'",d_result);
+  }
+  catch(SSqlException &e) {
+    throw AhuException("gMySQLBackend unable to retrieve list of master domains: "+e.txtReason());
+  }
+
+  vector<DomainInfo>allMasters;
+  int numanswers=d_result.size();
+  for(int n=0;n<numanswers;++n) { // id,name,master,last_check
+    DomainInfo sd;
+    sd.id=atol(d_result[n][0].c_str());
+    sd.zone=d_result[n][1];
+    sd.master=d_result[n][2];
+    sd.last_check=atol(d_result[n][3].c_str());
+    sd.notified_serial=atoi(d_result[n][4].c_str());
+    sd.backend=this;
+    sd.kind=DomainInfo::Master;
+    allMasters.push_back(sd);
+  }
+
+  for(vector<DomainInfo>::iterator i=allMasters.begin();i!=allMasters.end();++i) {
+    SOAData sdata;
+    sdata.serial=0;
+    sdata.refresh=0;
+    getSOA(i->zone,sdata);
+    if(i->notified_serial!=sdata.serial) {
+      i->serial=sdata.serial;
+      updatedDomains->push_back(*i);
+    }
+  }
+}
+
+
+string gMySQLBackend::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;
+}
+
+
+gMySQLBackend::gMySQLBackend(const string &mode, const string &suffix)
+{
+  setArgPrefix(mode+suffix);
+
+  d_logprefix="["+mode+"Backend"+suffix+"] ";
+  d_db=0;
+  try {
+    if(mode=="gmysql")
+      d_db=new SMySQL(getArg("dbname"),
+                   getArg("host"),
+                   getArg("socket"),
+                   getArg("user"),
+                   getArg("password"));
+    else if(mode=="gpgsql2")
+      d_db=new SPgSQL(getArg("dbname"),
+                     getArg("host"),
+                     getArg("socket"),
+                     getArg("user"),
+                     getArg("password"));
+    else {
+      L<<Logger::Error<<d_logprefix<<"Generic backend does not support database '"<<mode<<"'"<<endl;
+      exit(1);
+    }
+      
+  }
+  catch(SSqlException &e) {
+    L<<Logger::Error<<d_logprefix<<"Connection failed: "<<e.txtReason()<<endl;
+    throw AhuException("Unable to launch "+mode+" connection: "+e.txtReason());
+  }
+                 
+  d_noWildCardNoIDQuery=getArg("basic-query");
+  d_noWildCardIDQuery=getArg("id-query");
+  d_wildCardNoIDQuery=getArg("wildcard-query");
+  d_wildCardIDQuery=getArg("wildcard-id-query");
+
+  d_noWildCardANYNoIDQuery=getArg("any-query");
+  d_noWildCardANYIDQuery=getArg("any-id-query");
+  d_wildCardANYNoIDQuery=getArg("wildcard-any-query");
+  d_wildCardANYIDQuery=getArg("wildcard-any-id-query");
+  
+  d_listQuery=getArg("list-query");
+  L<<Logger::Warning<<d_logprefix<<"Connection succesful"<<endl;
+}
+
+gMySQLBackend::~gMySQLBackend()
+{
+  if(d_db)
+    delete d_db;
+  L<<Logger::Error<<d_logprefix<<"Closing connection"<<endl;
+}
+
+void gMySQLBackend::lookup(const QType &qtype,const string &qname, DNSPacket *pkt_p, int domain_id)
+{
+  string format;
+  char output[1024];
+
+  d_db->setLog(arg().mustDo("query-logging"));
+
+  string lcqname=toLower(qname);
+  
+  if(qtype.getCode()!=QType::ANY) {
+    // qtype qname domain_id
+    if(domain_id<0) {
+      if(qname[0]=='%')
+       format=d_wildCardNoIDQuery;
+      else
+       format=d_noWildCardNoIDQuery;
+
+      snprintf(output,1023, format.c_str(),sqlEscape(qtype.getName()).c_str(), sqlEscape(lcqname).c_str());
+    }
+    else {
+      if(qname[0]!='%')
+       format=d_noWildCardIDQuery;
+      else
+       format=d_wildCardIDQuery;
+      snprintf(output,1023, format.c_str(),sqlEscape(qtype.getName()).c_str(),sqlEscape(lcqname).c_str(),domain_id);
+    }
+  }
+  else {
+    // qtype==ANY
+    // qname domain_id
+    if(domain_id<0) {
+      if(qname[0]=='%')
+       format=d_wildCardANYNoIDQuery;
+      else
+       format=d_noWildCardANYNoIDQuery;
+
+      snprintf(output,1023, format.c_str(),sqlEscape(lcqname).c_str());
+    }
+    else {
+      if(qname[0]!='%')
+       format=d_noWildCardANYIDQuery;
+      else
+       format=d_wildCardANYIDQuery;
+      snprintf(output,1023, format.c_str(),sqlEscape(lcqname).c_str(),domain_id);
+    }
+  }
+  DLOG(L<< "Query: '" << output << "'"<<endl);
+
+  try {
+    d_db->doQuery(output);
+  }
+  catch(SSqlException &e) {
+    throw AhuException(e.txtReason());
+  }
+
+  d_qname=qname;
+
+  d_qtype=qtype;
+  d_count=0;
+}
+bool gMySQLBackend::list(int domain_id )
+{
+  DLOG(L<<"gMySQLBackend constructing handle for list of domain id'"<<domain_id<<"'"<<endl);
+
+  char output[1024];
+  snprintf(output,1023,d_listQuery.c_str(),domain_id);
+  try {
+    d_db->doQuery(output);
+  }
+  catch(SSqlException &e) {
+    throw AhuException("gMySQLBackend list query: "+e.txtReason());
+  }
+
+  d_qname="";
+  d_count=0;
+  return true;
+}
+
+bool gMySQLBackend::superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **ddb)
+{
+  // check if we know the ip/ns couple in the database
+  for(vector<DNSResourceRecord>::const_iterator i=nsset.begin();i!=nsset.end();++i) {
+    try {
+      d_db->doQuery(("select account from supermasters where ip='"+sqlEscape(ip)+"' and nameserver='"+sqlEscape(i->content)+"'"),
+                   d_result);
+    }
+    catch (SSqlException &e) {
+      throw AhuException("gMySQLBackend unable to search for a domain: "+e.txtReason());
+    }
+
+    if(!d_result.empty()) {
+      *account=d_result[0][0];
+      *ddb=this;
+      return true;
+    }
+  }
+  return false;
+}
+
+bool gMySQLBackend::createSlaveDomain(const string &ip, const string &domain, const string &account)
+{
+  try {
+    d_db->doQuery(("insert into domains (type,name,master,account) values('SLAVE','"+
+                  sqlEscape(domain)+"','"+
+                  sqlEscape(ip)+"','"+sqlEscape(account)+"')"));
+  }
+  catch(SSqlException &e) {
+    throw AhuException("Database error trying to insert new slave '"+domain+"': "+ e.txtReason());
+  }
+  return true;
+}
+
+
+bool gMySQLBackend::get(DNSResourceRecord &r)
+{
+  // L << "gMySQLBackend get() was called for "<<qtype.getName() << " record: ";
+  SMySQL::row_t row;
+  if(d_db->getRow(row)) {
+    r.content=row[0];
+    r.ttl=atol(row[1].c_str());
+    r.priority=atol(row[2].c_str());
+    if(!d_qname.empty())
+      r.qname=d_qname;
+    else
+      r.qname=row[5];
+    r.qtype=row[3];
+    
+    r.domain_id=atoi(row[4].c_str());
+    return true;
+  }
+  
+  return false;
+}
+
+bool gMySQLBackend::feedRecord(const DNSResourceRecord &r)
+{
+  ostringstream os;
+  
+  os<<"insert into records (content,ttl,prio,type,domain_id,name) values ('"<<
+    sqlEscape(r.content)<<"', "<<
+    r.ttl<<", "<<
+    r.priority<<", '"<<sqlEscape(r.qtype.getName())<<"', "<<
+    r.domain_id<<
+    ", '"<<sqlEscape(r.qname)<<"')";
+  
+  //  L<<Logger::Error<<"Trying: '"<<os.str()<<"'"<<endl;
+
+  try {
+    d_db->doQuery(os.str());
+  }
+  catch (SSqlException &e) {
+    throw AhuException(e.txtReason());
+  }
+
+}
+
+bool gMySQLBackend::startTransaction(const string &domain, int domain_id)
+{
+  try {
+    d_db->doQuery("begin");
+    d_db->doQuery("delete from records where domain_id="+itoa(domain_id));
+  }
+  catch (SSqlException &e) {
+    throw AhuException("Database failed to start transaction: "+e.txtReason());
+  }
+
+  return true;
+}
+
+bool gMySQLBackend::commitTransaction()
+{
+  try {
+    d_db->doQuery("commit");
+  }
+  catch (SSqlException &e) {
+    throw AhuException("Database failed to commit transaction: "+e.txtReason());
+  }
+  return true;
+}
+
+bool gMySQLBackend::abortTransaction()
+{
+  try {
+    d_db->doQuery("rollback");
+  }
+  catch(SSqlException &e) {
+    throw AhuException("MySQL failed to abort transaction: "+string(e.txtReason()));
+  }
+  return true;
+}
+
+
+class gMySQLFactory : public BackendFactory
+{
+public:
+  gMySQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {}
+  
+  void declareArguments(const string &suffix="")
+  {
+    declare(suffix,"dbname","Pdns backend database name to connect to","powerdns");
+    declare(suffix,"user","Pdns backend user to connect as","powerdns");
+    declare(suffix,"host","Pdns backend host to connect to","");
+    declare(suffix,"socket","Pdns backend socket to connect to","");
+    declare(suffix,"password","Pdns backend password to connect with","");
+
+    declare(suffix,"basic-query","Basic query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name='%s'");
+    declare(suffix,"id-query","Basic with ID query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name='%s' and domain_id=%d");
+    declare(suffix,"wildcard-query","Wildcard query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name like '%s'");
+    declare(suffix,"wildcard-id-query","Wildcard with ID query","select content,ttl,prio,type,domain_id,name from records where type='%s' and name like '%s' and domain_id='%d'");
+
+    declare(suffix,"any-query","Any query","select content,ttl,prio,type,domain_id,name from records where name='%s'");
+    declare(suffix,"any-id-query","Any with ID query","select content,ttl,prio,type,domain_id,name from records where name='%s' and domain_id=%d");
+    declare(suffix,"wildcard-any-query","Wildcard ANY query","select content,ttl,prio,type,domain_id,name from records where name like '%s'");
+    declare(suffix,"wildcard-any-id-query","Wildcard ANY with ID query","select content,ttl,prio,type,domain_id,name from records where like '%s' and domain_id='%d'");
+
+    declare(suffix,"list-query","AXFR query", "select content,ttl,prio,type,domain_id,name from records where domain_id='%d'");
+
+  }
+  
+  DNSBackend *make(const string &suffix="")
+  {
+    return new gMySQLBackend(d_mode,suffix);
+  }
+private:
+  const string d_mode;
+};
+
+
+//! Magic class that is activated when the dynamic library is loaded
+class gMySQLLoader
+{
+public:
+  //! This reports us to the main UeberBackend class
+  gMySQLLoader()
+  {
+    BackendMakers().report(new gMySQLFactory("gmysql"));
+    BackendMakers().report(new gMySQLFactory("gpgsql2"));
+    L<<Logger::Warning<<"This is module gmysqlbackend.so reporting"<<endl;
+  }
+};
+static gMySQLLoader gmysqlloader;
diff --git a/modules/gmysqlbackend/gmysqlbackend.hh b/modules/gmysqlbackend/gmysqlbackend.hh
new file mode 100644 (file)
index 0000000..b36cfa6
--- /dev/null
@@ -0,0 +1,49 @@
+#include <string>
+#include <map>
+#include "smysql.hh"
+
+using namespace std;
+
+/** The gMySQLBackend is a DNSBackend that can answer DNS related questions. It looks up data
+    in PostgreSQL */
+class gMySQLBackend : public DNSBackend
+{
+public:
+  gMySQLBackend(const string &mode, const string &suffix); //!< Makes our connection to the database. Throws an exception if it fails.
+  ~gMySQLBackend();
+  
+  string sqlEscape(const string &name);
+  void lookup(const QType &, const string &qdomain, DNSPacket *p=0, int zoneId=-1);
+  bool list(int domain_id);
+  bool get(DNSResourceRecord &r);
+  bool isMaster(const string &domain, const string &ip);
+
+  bool startTransaction(const string &domain, int domain_id=-1);
+  bool commitTransaction();
+  bool abortTransaction();
+  bool feedRecord(const DNSResourceRecord &r);
+  bool createSlaveDomain(const string &ip, const string &domain, const string &account);
+  bool superMasterBackend(const string &ip, const string &domain, const vector<DNSResourceRecord>&nsset, string *account, DNSBackend **db);
+  void setFresh(u_int32_t domain_id);
+  void getUnfreshSlaveInfos(vector<DomainInfo> *domains);
+  void getUpdatedMasters(vector<DomainInfo> *updatedDomains);
+  bool getDomainInfo(const string &domain, DomainInfo &di);
+  void setNotified(u_int32_t domain_id, u_int32_t serial);
+private:
+  string d_qname;
+  QType d_qtype;
+  int d_count;
+  SSql *d_db;
+  SSql::result_t d_result;
+
+  string d_wildCardNoIDQuery;
+  string d_noWildCardNoIDQuery;
+  string d_noWildCardIDQuery;
+  string d_wildCardIDQuery;
+  string d_wildCardANYNoIDQuery;
+  string d_noWildCardANYNoIDQuery;
+  string d_noWildCardANYIDQuery;
+  string d_wildCardANYIDQuery;
+  string d_listQuery;
+  string d_logprefix;
+};
diff --git a/modules/gmysqlbackend/smysql.cc b/modules/gmysqlbackend/smysql.cc
new file mode 100644 (file)
index 0000000..e5e9aa7
--- /dev/null
@@ -0,0 +1,126 @@
+/* Copyright 2001 Netherlabs BV, bert.hubert@netherlabs.nl. See LICENSE 
+   for more information.
+   $Id: smysql.cc,v 1.1 2002/11/27 15:23:16 ahu Exp $  */
+#include "smysql.hh"
+#include <string>
+#include <iostream>
+#include "logger.hh"
+#include "dns.hh"
+using namespace std;
+
+bool SMySQL::s_dolog;
+
+SMySQL::SMySQL(const string &database, const string &host, const string &msocket, const string &user, 
+              const string &password)
+{
+  mysql_init(&d_db);
+  if (!mysql_real_connect(&d_db, host.empty() ? 0 : host.c_str(), 
+                         user.empty() ? 0 : user.c_str(), 
+                         password.empty() ? 0 : password.c_str(),
+                         database.c_str(), 0,
+                         msocket.empty() ? 0 : msocket.c_str(),
+                         0)) {
+    throw sPerrorException("Unable to connect to database");
+  }
+  d_rres=0;
+}
+
+void SMySQL::setLog(bool state)
+{
+  s_dolog=state;
+}
+
+SMySQL::~SMySQL()
+{
+  mysql_close(&d_db);
+}
+
+SSqlException SMySQL::sPerrorException(const string &reason)
+{
+  return SSqlException(reason+string(": ")+mysql_error(&d_db));
+}
+
+int SMySQL::doQuery(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;
+
+  if(mysql_query(&d_db,query.c_str())) 
+    throw sPerrorException("Failed to execute mysql_query");
+
+
+  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();
+}
+
+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);  
+  d_rres=0;
+  return false;
+}
+
+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;
+}
+
+
+#if 0
+int main()
+{
+  try {
+    SMySQL s("kkfnetmail","127.0.0.1","readonly");
+    SSql::result_t juh;
+    
+    int num=s.doQuery("select *, from mboxes", juh);
+    cout<<num<<" responses"<<endl;
+    
+    for(int i=0;i<num;i++) {
+      const SSql::row_t &row=juh[i];
+
+      for(SSql::row_t::const_iterator j=row.begin();j!=row.end();++j)
+       cout <<"'"<< *j<<"', ";
+      cout<<endl;
+    }
+  }
+  catch(SSqlException &e) {
+    cerr<<e.txtReason()<<endl;
+  }
+}
+
+
+#endif
diff --git a/modules/gmysqlbackend/smysql.hh b/modules/gmysqlbackend/smysql.hh
new file mode 100644 (file)
index 0000000..f0eca50
--- /dev/null
@@ -0,0 +1,31 @@
+/* Copyright 2001 Netherlabs BV, bert.hubert@netherlabs.nl. See LICENSE 
+   for more information.
+   $Id: smysql.hh,v 1.1 2002/11/27 15:23:16 ahu Exp $  */
+#ifndef SMYSQL_HH
+#define SMYSQL_HH
+
+#include <mysql/mysql.h>
+#include "ssql.hh"
+
+class SMySQL : public SSql
+{
+public:
+  SMySQL(const string &database, const string &host="", 
+        const string &msocket="",const string &user="", 
+        const string &password="");
+
+  ~SMySQL();
+  
+  SSqlException sPerrorException(const string &reason);
+  int doQuery(const string &query, result_t &result);
+  int doQuery(const string &query);
+  bool getRow(row_t &row);
+  string escape(const string &str);    
+  void setLog(bool state);
+private:
+  MYSQL d_db;
+  MYSQL_RES *d_rres;
+  static bool s_dolog;
+};
+      
+#endif /* SSMYSQL_HH */
diff --git a/modules/gmysqlbackend/spgsql.cc b/modules/gmysqlbackend/spgsql.cc
new file mode 100644 (file)
index 0000000..8f39baf
--- /dev/null
@@ -0,0 +1,105 @@
+/* Copyright 200w Netherlabs BV, bert.hubert@netherlabs.nl. See LICENSE 
+   for more information.
+   $Id: spgsql.cc,v 1.1 2002/11/27 15:23:16 ahu Exp $  */
+#include "spgsql.hh"
+#include <string>
+#include <iostream>
+#include "logger.hh"
+#include "dns.hh"
+using namespace std;
+
+bool SPgSQL::s_dolog;
+
+SPgSQL::SPgSQL(const string &database, const string &host, const string &msocket, const string &user, 
+              const string &password)
+{
+  string connectstr;
+
+  connectstr="dbname=";
+  connectstr+=database;
+  connectstr+=" user=";
+  connectstr+=user;
+
+  if(!host.empty())
+    connectstr+=" host="+host;
+
+  if(!password.empty())
+    connectstr+=" password="+password;
+
+  d_db=new PgDatabase(connectstr.c_str());
+  
+  // Check to see that the backend connection was successfully made
+  if (d_db->ConnectionBad() ) {
+    throw sPerrorException("Unable to connect to database");
+  }
+
+}
+
+void SPgSQL::setLog(bool state)
+{
+  s_dolog=state;
+}
+
+SPgSQL::~SPgSQL()
+{
+  delete d_db;
+}
+
+SSqlException SPgSQL::sPerrorException(const string &reason)
+{
+  return SSqlException(reason+string(": ")+d_db->ErrorMessage());
+}
+
+int SPgSQL::doQuery(const string &query)
+{
+  if(s_dolog)
+    L<<Logger::Warning<<"Query: "<<query<<endl;
+
+  if(!d_db->Exec(query.c_str())) {
+    throw sPerrorException("PostgreSQL failed to execute command");
+  }
+  d_count=0;
+  return 0;
+}
+
+int SPgSQL::doQuery(const string &query, result_t &result)
+{
+  result.clear();
+  if(s_dolog)
+    L<<Logger::Warning<<"Query: "<<query<<endl;
+
+  if(!d_db->ExecTuplesOk(query.c_str()))
+    throw sPerrorException("gPgSQLBackend failed to execute command that expected results");
+  d_count=0;
+
+  row_t row;
+  while(getRow(row))
+    result.push_back(row);
+
+  return result.size();
+}
+
+bool SPgSQL::getRow(row_t &row)
+{
+  row.clear();
+  if(d_count>=d_db->Tuples())
+    return false;
+  
+  for(unsigned int i=0;i<d_db->Fields();i++)
+    row.push_back(d_db->GetValue(d_count,i) ?: "");
+  d_count++;
+  return true;
+}
+
+string SPgSQL::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;
+}
diff --git a/modules/gmysqlbackend/spgsql.hh b/modules/gmysqlbackend/spgsql.hh
new file mode 100644 (file)
index 0000000..b9d8d17
--- /dev/null
@@ -0,0 +1,30 @@
+/* Copyright 2001 Netherlabs BV, bert.hubert@netherlabs.nl. See LICENSE 
+   for more information.
+   $Id: spgsql.hh,v 1.1 2002/11/27 15:23:16 ahu Exp $  */
+#ifndef SPGSQL_HH
+#define SPGSQL_HH
+#include <libpq++.h>
+#include "ssql.hh"
+
+class SPgSQL : public SSql
+{
+public:
+  SPgSQL(const string &database, const string &host="", 
+        const string &msocket="",const string &user="", 
+        const string &password="");
+
+  ~SPgSQL();
+  
+  SSqlException sPerrorException(const string &reason);
+  int doQuery(const string &query, result_t &result);
+  int doQuery(const string &query);
+  bool getRow(row_t &row);
+  string escape(const string &str);    
+  void setLog(bool state);
+private:
+  PgDatabase *d_db; 
+  int d_count;
+  static bool s_dolog;
+};
+      
+#endif /* SPGSQL_HH */
diff --git a/modules/gmysqlbackend/ssql.hh b/modules/gmysqlbackend/ssql.hh
new file mode 100644 (file)
index 0000000..9837466
--- /dev/null
@@ -0,0 +1,42 @@
+/* Copyright 2001 Netherlabs BV, bert.hubert@netherlabs.nl. See LICENSE 
+   for more information.
+   $Id: ssql.hh,v 1.1 2002/11/27 15:23:16 ahu Exp $  */
+#ifndef SSQL_HH
+#define SSQL_HH
+
+#include <string>
+#include <vector>
+using namespace std;
+
+
+class SSqlException 
+{
+public: 
+  SSqlException(const string &reason) 
+  {
+      d_reason=reason;
+  }
+  
+  string txtReason()
+  {
+    return d_reason;
+  }
+private:
+  string d_reason;
+};
+
+class SSql
+{
+public:
+  typedef vector<string> row_t;
+  typedef vector<row_t> result_t;
+  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 bool getRow(row_t &row)=0;
+  virtual string escape(const string &name)=0;
+  virtual void setLog(bool state){}
+  virtual ~SSql(){};
+};
+
+#endif /* SSQL_HH */