diff options
| -rw-r--r-- | server/src/Makefile.am | 5 | ||||
| -rw-r--r-- | server/src/database.cc | 286 | ||||
| -rw-r--r-- | server/src/database.h | 82 | ||||
| -rw-r--r-- | server/src/dbtypes.h | 40 | ||||
| -rw-r--r-- | server/src/journal_commit.cc | 22 | ||||
| -rw-r--r-- | server/src/pracrodao.cc | 35 | ||||
| -rw-r--r-- | server/src/pracrodao.h | 46 | ||||
| -rw-r--r-- | server/src/pracrodaopgsql.cc | 256 | ||||
| -rw-r--r-- | server/src/pracrodaopgsql.h | 40 | ||||
| -rw-r--r-- | server/src/saxparser.cc | 7 | ||||
| -rw-r--r-- | server/src/server.cc | 40 | ||||
| -rw-r--r-- | server/src/templateparser.cc | 33 | 
12 files changed, 513 insertions, 379 deletions
| diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 7daa3be..a4f2fd7 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -20,6 +20,8 @@ pracrod_SOURCES = \  	luaquerymapper.cc \  	luaresume.cc \  	macroparser.cc \ +	pracrodao.cc \ +	pracrodaopgsql.cc \  	resumeparser.cc \  	saxparser.cc \  	server.cc \ @@ -34,6 +36,7 @@ EXTRA_DIST = \  	configurationparser.h \  	daemon.h \  	database.h \ +	dbtypes.h \  	debug.h \  	exception.h \  	queryhandler.h \ @@ -43,6 +46,8 @@ EXTRA_DIST = \  	luaquerymapper.h \  	luaresume.h \  	macroparser.h \ +	pracrodao.h \ +	pracrodaopgsql.h \  	resumeparser.h \  	saxparser.h \  	server.h \ diff --git a/server/src/database.cc b/server/src/database.cc index 9406e1e..74579c1 100644 --- a/server/src/database.cc +++ b/server/src/database.cc @@ -1,4 +1,5 @@  /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */  /***************************************************************************   *            database.cc   * @@ -30,288 +31,19 @@  #include <stdlib.h>  #include "debug.h" +#include "pracrodaopgsql.h" -Database::Database(std::string hostname, std::string user, std::string password) -#ifndef WITHOUT_DB -  : c("host=" + hostname + " user=" + user + " password=" + password + " dbname=pracro") -#endif/*WITHOUT_DB*/ +Database::Database(std::string _backend, std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname)  { -} - -Database::~Database() -{ -} - -void Database::commit(std::string user, -                      std::string cpr, -                      Macro &_macro, -                      Fields &fields, -                      time_t now) -{ -  if(fields.size() == 0) return; - -  std::string version = _macro.attributes["version"]; -  std::string macro = _macro.attributes["name"]; -  std::stringstream timestamp; timestamp << now; - -#ifndef WITHOUT_DB -  std::string ts; -  try { -    pqxx::work W(c); -    ts = "INSERT INTO transactions VALUES (" -      " '" + W.esc(cpr) + "', " -      " '" + W.esc(macro) + "', " -      " '" + W.esc(version) + "', " -      " '" + W.esc(timestamp.str()) + "', " -      " '" + W.esc(user) + "' " -      ")" -      ; -    pqxx::result R = W.exec(ts); -    std::stringstream oid; -    oid << R.inserted_oid(); - -    if(fields.size() > 0) { -      // field table lookup -      ts = "SELECT name FROM fieldnames WHERE name IN ( "; -      std::map< std::string, std::string >::iterator i = fields.begin(); -      ts += "'" + W.esc(i->first) + "'"; -      i++; -      while(i != fields.end()) { -        ts += ", '" + W.esc(i->first) + "'"; -        i++; -      } -      ts += ")"; -      R = W.exec(ts); -      PRACRO_DEBUG(db, "Database::commit: input fields: %d, output fields: %lu\n", fields.size(), R.size()); - -      pqxx::result::const_iterator ri = R.begin(); -      // Store known fields -      while(ri != R.end()) { -        pqxx::result::tuple t = *ri; -        std::string name = t[0].c_str(); - -        PRACRO_DEBUG(db, "Storing: %s with value %s\n", name.c_str(), fields[name].c_str()); - -        ts = "INSERT INTO fields VALUES (" -          " '" + W.esc(oid.str()) + "', " -          " '" + W.esc(name) + "', " -          " '" + W.esc(fields[name]) + "'" -          ")" -          ; -        W.exec(ts); -        ri++; -      } -    } -    W.commit(); -  } catch(std::exception &e) { -    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str()); -  } -#else -#ifdef WITH_DEBUG -  std::map< std::string, std::string >::iterator i = fields.begin(); -  while(i != fields.end()) { -    PRACRO_DEBUG(db, "Storing field '%s': '%s'\n", i->first, i->second); -    i++; -  } -#endif/*WITH_DEBUG*/ -#endif/*WITHOUT_DB*/ -} - -void Database::putResume(std::string user, -                         std::string cpr, -                         Macro &_macro, -                         std::string resume, -                         time_t now, -                         bool store_in_journal) -{ -  std::string version = _macro.attributes["version"]; -  std::string macro = _macro.attributes["name"]; -  std::stringstream timestamp; timestamp << now; - -#ifndef WITHOUT_DB -  std::string ts; -  try { -    pqxx::work W(c); -    ts = "INSERT INTO journal VALUES (" -         "'" + W.esc(cpr) + "', " -         "'" + W.esc(macro) + "', " -         "'" + W.esc(version) + "', " -         "'" + W.esc(timestamp.str()) + "', " -         "'" + W.esc(user) + "', " -         "'" + W.esc(resume)+"'" -         ")"; -    pqxx::result R = W.exec(ts); -    W.commit(); -  } catch(std::exception &e) { -    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str()); +  dao = NULL; +  if(_backend == "pgsql") { +    PRACRO_DEBUG(db, "construct(%s, %s, %s, %s, %s)\n", _host.c_str(), _port.c_str(), _user.c_str(), _passwd.c_str(), _dbname.c_str()); +    dao = new PracroDAOPgsql(_host, _port, _user, _passwd, _dbname);    } -#else -#ifdef WITH_DEBUG -  PRACRO_DEBUG(db, "INSERT INTO journal VALUES ('%s', '%s', '%s', '%s', '%s', '%s')\n", -         cpr.c_str(), -         macro.c_str(), -         version.c_str(), -         timestamp.str().c_str(), -         user.c_str(), -         resume.c_str() -         ); -#endif/*WITH_DEBUG*/ -#endif  } - -std::string Database::getResume(std::string cpr, -                                std::string macro, -                                time_t oldest) -{ -  std::string resume; - -#ifndef WITHOUT_DB -  std::string query; -  try { -    pqxx::work W(c); -    /* FIXME: argument 'oldest' is not used anywhere */ -    query = "SELECT journal FROM journal" -      " WHERE cpr = '" + W.esc(cpr) + "'" -      "  AND macro = '" + W.esc(macro) + "'" -      " ORDER BY timestamp" -      ; -    pqxx::result R = W.exec(query); -    pqxx::result::const_iterator ri = R.begin(); -    /* FIXME: This will only get the *last* entry of the journal in var resume due to '=' and not '+=' */ -    while(ri != R.end()) { -      pqxx::result::tuple t = *ri; -      resume = t[0].c_str(); -      ri++; -    } -  } catch (std::exception &e) { -    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); -  } -#else -#ifdef WITH_DEBUG -  PRACRO_DEBUG(db, "SELECT journal FROM journal WHERE cpr = '%s' AND macro = '%s' ORDER BY timestamp", cpr.c_str(), macro.c_str()); -#endif/*WITH_DEBUG*/ -#endif/*WITHOUT_DB*/ -   -  return resume; -} - - -Values Database::getValues(std::string cpr, -                           Fieldnames &fields, -                           time_t oldest) -{ -  Values values; - -#ifndef WITHOUT_DB -  std::string query; -  std::stringstream soldest; soldest << oldest; -  try { -    pqxx::work W(c); -    query = "SELECT f.name, f.value, t.timestamp FROM fields f JOIN transactions t ON t.oid = f.transaction " -      " WHERE t.cpr = '" + W.esc(cpr) + "' " -      "  AND t.timestamp >= " + soldest.str(); -    if(fields.size() > 0) { -      std::vector< std::string >::iterator i = fields.begin(); -      query += " AND f.name IN ('" + W.esc(*i) + "'"; -      i++; -      while(i != fields.end()) { -        query += ", '" + W.esc(*i) + "'"; -        i++; -      } -      query += ')'; -    } -    query += " ORDER BY t.timestamp "; -    pqxx::result R = W.exec(query); -    pqxx::result::const_iterator ri = R.begin(); -    while(ri != R.end()) { -      pqxx::result::tuple t = *ri; -      Value v; -      v.value = t[1].c_str(); -      v.timestamp = atol(t[2].c_str()); -      if(values.find(t[0].c_str()) == values.end() || -         (values.find(t[0].c_str()) != values.end() && -          values[t[0].c_str()].timestamp <= v.timestamp) ) { -        values[t[0].c_str()] = v; -      } -      ri++; -    } -  } catch (std::exception &e) { -    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); -  } -#else -#ifdef WITH_DEBUG -  PRACRO_DEBUG(db, "getValues(%s, <fields...>, %ld) -- not implemented without database...\n", cpr.c_str(), oldest); -#endif/*WITH_DEBUG*/ -#endif/*WITHOUT_DB*/ - -  return values; -} - -bool Database::checkMacro(std::string cpr, -                          std::string macro, -                          time_t oldest) -{ -#ifndef WITHOUT_DB -  std::string query; -  std::stringstream soldest; soldest << oldest; -  try { -    pqxx::work W(c); -    query = "SELECT oid FROM transactions" -      " WHERE cpr = '" + W.esc(cpr) + "'" -      "  AND macro = '" + W.esc(macro) + "'" -      "  AND timestamp >= " + soldest.str() + "" -      " ORDER BY timestamp" -      ; -    pqxx::result R = W.exec(query); -    return R.size() != 0; -  } catch(std::exception &e) { -    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); -  } -#else -#ifdef WITH_DEBUG -    PRACRO_DEBUG(db, "SELECT oid FROM transactions WHERE cpr = '%s' AND macro = '%s' AND timestamp >= %ld ORDER BY timestamp\n", -      cpr.c_str(), macro.c_str(), oldest); -#endif/*WITH_DEBUG*/ -#endif/*WITHOUT_DB*/ - -  return false; -} - - - - -#ifdef TEST_DATABASE - -int main() +Database::~Database()  { -  Database db("localhost", "pracro", "pracro"); - -  time_t now = time(NULL); -  /* -  Macro macro; -  macro.attributes["name"] = "testmacro"; -  macro.attributes["version"] = "1.0"; -   -  Fields fields; -  fields["themeaning"] = "42"; -  fields["microsoft"] = "waste of money"; - -  db.commit("testuser", "1505050505", macro, fields, now); - -  std::vector< std::string > fieldnames; -  fieldnames.push_back("microsoft"); -  fieldnames.push_back("themeaning"); - -  Values results = db.getValues("1505050505", fieldnames, now); -  Values::iterator i = results.begin(); -  while(i != results.end()) { -    Value v = i->second; -    printf("%s -> %s (%u)\n", i->first.c_str(), v.value.c_str(), (unsigned int)v.timestamp); -    i++; -  } -  */ -  printf(db.getResume("1505050505", "B.2.5", now - 60 * 60 * 24).c_str()); +  if(dao) delete dao;  } -#endif/*TEST_DATABASE*/ diff --git a/server/src/database.h b/server/src/database.h index e311584..85d4cc4 100644 --- a/server/src/database.h +++ b/server/src/database.h @@ -1,4 +1,5 @@  /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */  /***************************************************************************   *            database.h   * @@ -27,74 +28,51 @@  #ifndef __PRACRO_DATABASE_H__  #define __PRACRO_DATABASE_H__ -#include <config.h> - -#include <pqxx/pqxx> - -#include <string> +#include <time.h> +#include "pracrodao.h"  #include "transaction.h"  #include "template.h" -#include <time.h> - -#include <map> - -class Value { -public: -  Value() : value(""), timestamp(0) {} -  std::string value; -  time_t timestamp; -}; -typedef std::map< std::string, Value > Values; - -typedef std::vector< std::string > Fieldnames; +#include "debug.h"  class Database {  public: -  Database(std::string hostname, -           std::string user, -           std::string password); +  Database(std::string _backend, std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname);    ~Database();    // Make a commit to the db -  void commit(std::string user, -              std::string cpr, -              Macro ¯o, -              Fields &fields, -              time_t now = time(NULL)); +  void commitTransaction(std::string user, std::string cpr, Macro ¯o, Fields &fields, time_t now = time(NULL)) { +    PRACRO_DEBUG(db, "%s, %s, %s,...\n", user.c_str(), cpr.c_str(), macro.attributes["name"].c_str()); +    if(dao) dao->commitTransaction(user, cpr, macro, fields, now); +  }    // Get a list of values from the db -  Values getValues(std::string cpr, -                   Fieldnames &fieldnames, -                   time_t oldest = 0); +  Values getValues(std::string cpr, Fieldnames &fieldnames, time_t oldest = 0) { +    PRACRO_DEBUG(db, "%s, <%u fieldnames>, %ld\n", cpr.c_str(), fieldnames.size(), oldest); +    if(dao) return dao->getLatestValues(cpr, NULL, fieldnames, oldest); +    else return Values(); +  }    // Check if a macro has been committed. -  bool checkMacro(std::string cpr, -                  std::string macro, -                  time_t oldest = 0); +  bool checkMacro(std::string cpr, std::string macro, time_t oldest = 0) { +    PRACRO_DEBUG(db, "%s, %s, %ld\n", cpr.c_str(), macro.c_str(), oldest); +    if(!dao) return false; +    return dao->nrOfCommits(cpr, macro, oldest) > 0; +  } -  // Put an entry in the journal table -  void putResume(std::string user, -                 std::string cpr, -                 Macro &_macro, -                 std::string resume, -                 time_t now, -                 bool store_in_journal); -    // Get latest resume of a given macro -  std::string getResume(std::string cpr, -                        std::string macro, -                        time_t oldest); - -  // Connect to the db -  void connect() {} - -  // Disconnect from the db -  void disconnect() {} +  std::string getResume(std::string cpr, Macro ¯o, time_t oldest) { +    PRACRO_DEBUG(db, "%s, %s, %ld\n", cpr.c_str(), macro.attributes["name"].c_str(), oldest); +    if(!dao) return ""; +    Fieldnames fn; +    fn.push_back("journal.resume"); +    Values v = dao->getLatestValues(cpr, ¯o, fn, oldest); +    Values::iterator i = v.find("journal.resume"); +    if(i != v.end())  return i->second.value; +    else return ""; +  }  private: -#ifndef WITHOUT_DB -  pqxx::connection c; -#endif/*WITHOUT_DB*/ +  PracroDAO *dao;  };  #endif/*__PRACRO_DATABASE_H__*/ diff --git a/server/src/dbtypes.h b/server/src/dbtypes.h new file mode 100644 index 0000000..fc12728 --- /dev/null +++ b/server/src/dbtypes.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/* + *  This file is part of Pracro. + * + *  Pracro is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  Pracro is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Pracro; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#ifndef __PRACRO_DBTYPES_H__ +#define __PRACRO_DBTYPES_H__ + +#include <config.h> + +#include <string> +#include <time.h> +#include <vector> +#include <map> + +class Value { +public: +  Value() : value(""), timestamp(0) {} +  std::string value; +  time_t timestamp; +}; +typedef std::map< std::string, Value > Values; + +typedef std::vector< std::string > Fieldnames; + +#endif diff --git a/server/src/journal_commit.cc b/server/src/journal_commit.cc index c10e8c7..cb9769e 100644 --- a/server/src/journal_commit.cc +++ b/server/src/journal_commit.cc @@ -28,6 +28,8 @@  #include <config.h> +#include "debug.h" +  // for gethostbyname  #include <netdb.h> @@ -57,15 +59,12 @@ static int mwrite(int sock, const char *fmt, ...)    va_end(args);    if(sock != -1 && write(sock, buffer, l) != l) { -    fprintf(stderr, "write did not write all the bytes in the buffer.\n"); +    PRACRO_ERR_LOG(journal, "write did not write all the bytes in the buffer.\n");    } +  PRACRO_DEBUG(journal, "%s", buffer);    free(buffer); -#ifdef WITH_DEBUG -  printf(buffer); -#endif/*WITH_DEBUG*/ -    return l;  } @@ -84,8 +83,7 @@ int journal_commit(const char *cpr, const char *user,    struct hostent *he;    he = gethostbyname(addr);    if(!he || !he->h_length) { -    fprintf(stderr, "gethostbyname(%s) failed!\n", addr); -    perror(":"); +    PRACRO_ERR_LOG(journal, "gethostbyname(%s) failed (errno=%d)!\n", addr, errno);      return -1;    } @@ -100,12 +98,12 @@ int journal_commit(const char *cpr, const char *user,    sin.sin_port = htons(port);    if( (sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { -    fprintf(stderr, "Socket() failed!\n"); +    PRACRO_ERR_LOG(journal, "Socket() failed!\n");      return -1;    }    if(connect(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { -    fprintf(stderr, "Connect() failed!\n"); +    PRACRO_ERR_LOG(journal, "Connect() failed!\n");      perror(":");      return -1;    } @@ -123,11 +121,9 @@ int journal_commit(const char *cpr, const char *user,    // send body    if(sock != -1 && write(sock, buf, size) != (ssize_t)size) { -    fprintf(stderr, "write did not write all the bytes in the buffer.\n"); +    PRACRO_ERR_LOG(journal, "write did not write all the bytes in the buffer.\n");    } -#ifdef WITH_DEBUG -  printf(buf); -#endif/*WITH_DEBUG*/ +  PRACRO_DEBUG(journal, "%s", buf);  #ifndef WITHOUT_UPLOADSERVER    // close socket diff --git a/server/src/pracrodao.cc b/server/src/pracrodao.cc new file mode 100644 index 0000000..4dcaae1 --- /dev/null +++ b/server/src/pracrodao.cc @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/* + *  This file is part of Pracro. + * + *  Pracro is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  Pracro is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Pracro; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ + +#include "pracrodao.h" + +PracroDAO::PracroDAO(std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname) +{ +  host = _host; +  port = _port; +  user = _user; +  passwd = _passwd; +  dbname = _dbname; +} + +PracroDAO::~PracroDAO() +{ +} + diff --git a/server/src/pracrodao.h b/server/src/pracrodao.h new file mode 100644 index 0000000..285b310 --- /dev/null +++ b/server/src/pracrodao.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/* + *  This file is part of Pracro. + * + *  Pracro is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  Pracro is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Pracro; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#ifndef __PRACRO_PRACRODAO_H +#define __PRACRO_PRACRODAO_H + +#include <string> +#include "dbtypes.h" +#include "template.h" +#include "transaction.h" + +class PracroDAO +{ +public: +  PracroDAO(std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname); +  virtual ~PracroDAO(); + +  virtual void commitTransaction(std::string user, std::string cpr, Macro ¯o, Fields &fields, time_t now) = 0; +  virtual Values getLatestValues(std::string cpr, Macro *macro, Fieldnames &fieldnames, time_t oldest) = 0; +  virtual unsigned nrOfCommits(std::string cpr, std::string macroname, time_t oldest) = 0; + +protected: +  std::string host; +  std::string port; +  std::string user; +  std::string passwd; +  std::string dbname; +}; + +#endif diff --git a/server/src/pracrodaopgsql.cc b/server/src/pracrodaopgsql.cc new file mode 100644 index 0000000..d5646bb --- /dev/null +++ b/server/src/pracrodaopgsql.cc @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/* + *  This file is part of Pracro. + * + *  Pracro is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  Pracro is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Pracro; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ + +/* + * Updating the old tables; + * + * ALTER TABLE transactions ADD COLUMN uid bigint; + * CREATE SEQUENCE 'trseq'; + * SELECT setval('trseq', (SELECT MAX(oid) FROM transactions)); + * UPDATE transactions SET uid = oid; + * INSERT INTO fieldnames (name, description, timestamp) VALUES ('journal.resume', 'Journal resume text', (SELECT EXTRACT(EPOCH FROM now())::integer)); + */ + +#include <config.h> + +#include <stdlib.h> + +#include "pracrodaopgsql.h" +#include "debug.h" + +PracroDAOPgsql::PracroDAOPgsql(std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname) +  : PracroDAO(_host, _port, _user, _passwd, _dbname) +{ +  conn = NULL; +  std::string cs; +  if(host.size()) cs += " host=" + host; +  if(port.size()) cs += " port=" + port; +  if(user.size()) cs += " user=" + user; +  if(passwd.size()) cs += " password=" + passwd; +  cs += " dbname=" + (dbname.size() ? dbname : "pracro"); +  conn = new pqxx::connection(cs); +  PRACRO_DEBUG(db, "Pgsql connection %p (%s)\n", conn, cs.c_str()); +} + +PracroDAOPgsql::~PracroDAOPgsql() +{ +  if(conn)  delete conn; +} + +void PracroDAOPgsql::commitTransaction(std::string user, std::string cpr, Macro &_macro, Fields &fields, time_t now) +{ +  PRACRO_DEBUG(db, "(%s, %s, %s, <%u fields>, %ld)\n", user.c_str(), cpr.c_str(), _macro.attributes["name"].c_str(), fields.size(), now); +  if(!conn) PRACRO_DEBUG(db, "No pgsql connection\n"); +  if(fields.size() == 0) return; + +  std::string version = _macro.attributes["version"]; +  std::string macro = _macro.attributes["name"]; +  std::stringstream timestamp; timestamp << now; + +#ifndef WITHOUT_DB +  std::string ts; +  try { +    pqxx::work W(*conn); +    ts = "INSERT INTO transactions (uid, cpr, macro, version, \"timestamp\", \"user\") VALUES (" +      " nextval('trseq'), " +      " '" + W.esc(cpr) + "', " +      " '" + W.esc(macro) + "', " +      " '" + W.esc(version) + "', " +      " '" + W.esc(timestamp.str()) + "', " +      " '" + W.esc(user) + "' " +      ")" +      ; +    PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str()); +    pqxx::result R = W.exec(ts); + +    if(fields.size() > 0) { +      // field table lookup +      ts = "SELECT name FROM fieldnames WHERE name IN ( "; +      std::map< std::string, std::string >::iterator i = fields.begin(); +      ts += "'" + W.esc(i->first) + "'"; +      i++; +      while(i != fields.end()) { +        ts += ", '" + W.esc(i->first) + "'"; +        i++; +      } +      ts += ")"; +      PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str()); +      R = W.exec(ts); +      PRACRO_DEBUG(db, "input fields: %d, output fields: %lu\n", fields.size(), R.size()); + +      // Store known fields +      pqxx::result::const_iterator ri = R.begin(); +      if(ri != R.end()) { +        std::string name = (*ri)[0].c_str(); +        PRACRO_DEBUG(db, "Storing: %s with value %s\n", name.c_str(), fields[name].c_str()); +        ts = "INSERT INTO fields (transaction, name, value) VALUES ( currval('trseq'), '" + W.esc(name) + "', '" + W.esc(fields[name]) + "')"; +        ri++; +        while(ri != R.end()) { +          name = (*ri)[0].c_str(); + +          PRACRO_DEBUG(db, "Storing: %s with value %s\n", name.c_str(), fields[name].c_str()); + +          ts += ", (currval('trseq'), '" + W.esc(name) + "', '" + W.esc(fields[name]) + "')"; +          ri++; +        } +        PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str()); +        W.exec(ts); +      } +    } +    W.commit(); +  } catch(std::exception &e) { +    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str()); +  } +#else +#ifdef WITH_DEBUG +  std::map< std::string, std::string >::iterator i = fields.begin(); +  while(i != fields.end()) { +    PRACRO_DEBUG(db, "Storing field '%s': '%s'\n", i->first, i->second); +    i++; +  } +#endif/*WITH_DEBUG*/ +#endif/*WITHOUT_DB*/ +} + + +/* + * The following select finds the newest timestamps for each fieldname + * belonging to the designated patient and from a set of fieldnames in the + * inner query. The outer query then finds the corresponding field values. + * Note: there is no protection agains duplicate fields that could + * theoretically occur within the same transaction. + * + * SELECT ff.name, ff.value, tt.timestamp FROM ( + *    SELECT f.name, max(t.timestamp) AS ts + *      FROM fields f, transactions t + *      WHERE t.oid = f.transaction + *        AND t.cpr = '1505050505' + *        AND t.timestamp >= 0 + *        AND f.name IN ('current_eye_disease') + *      GROUP BY f.name) xx, + *  transactions tt, fields ff + *  WHERE xx.ts = tt.timestamp + *    AND xx.name = ff.name + *    AND tt.oid = ff.transaction + *    AND tt.cpr = '1505050505' + */ +Values PracroDAOPgsql::getLatestValues(std::string cpr, Macro *macro, Fieldnames &fieldnames, time_t oldest) +{ +  PRACRO_DEBUG(db, "(%s, %s, <%u fieldnames>, %ld)\n", cpr.c_str(), macro ? macro->attributes["name"].c_str() : "(null)", fieldnames.size(), oldest); +  if(!conn) PRACRO_DEBUG(db, "No pgsql connection\n"); +  Values values; + +#ifndef WITHOUT_DB +  std::string query; +  std::stringstream soldest; soldest << oldest; +  try { +    std::string namecond; + +    pqxx::work W(*conn); +    if(fieldnames.size() > 0) { +      std::vector< std::string >::iterator i = fieldnames.begin(); +      namecond += " AND f.name IN ('" + W.esc(*i) + "'"; +      i++; +      while(i != fieldnames.end()) { +        namecond += ", '" + W.esc(*i) + "'"; +        i++; +      } +      namecond += ')'; +    } +    query = "SELECT ff.name, ff.value, tt.timestamp FROM " +    // Begin inner query +      " (SELECT f.name, MAX(t.timestamp) AS ts FROM fields f, transactions t " +      " WHERE t.uid = f.transaction AND t.timestamp >= " + soldest.str() + +      " AND t.cpr = '" + W.esc(cpr) + "' " +      + namecond; +    if(macro) { +      query += " AND t.macro = '" + macro->attributes["name"] + "'"; +      if(macro->attributes["version"].size() > 0) +        query += " AND t.version = '" + macro->attributes["version"] + "'"; +    } +    query += " GROUP BY f.name) xx, " +    // End inner query +      " transactions tt, fields ff " +      " WHERE xx.ts = tt.timestamp " +      "   AND xx.name = ff.name " +      "   AND tt.uid = ff.transaction " +      "   AND tt.cpr = '" + W.esc(cpr) + "' " +      ; +    if(macro) { +      query += " AND tt.macro = '" + macro->attributes["name"] + "'"; +      if(macro->attributes["version"].size() > 0) +        query += " AND tt.version = '" + macro->attributes["version"] + "'"; +    } + +    PRACRO_DEBUG(sql, "Query: %s\n", query.c_str()); +    pqxx::result R = W.exec(query); +    pqxx::result::const_iterator ri = R.begin(); +    while(ri != R.end()) { +      Value v; +      v.value = (*ri)[1].c_str(); +      v.timestamp = atol((*ri)[2].c_str()); +      values[(*ri)[0].c_str()] = v; +      ri++; +    } +  } catch (std::exception &e) { +    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); +  } +#else +#ifdef WITH_DEBUG +  PRACRO_DEBUG(db, "getLatestValues(%s, <fields...>, %ld) -- not implemented without database...\n", cpr.c_str(), oldest); +#endif/*WITH_DEBUG*/ +#endif/*WITHOUT_DB*/ + +  return values; +} + + +unsigned PracroDAOPgsql::nrOfCommits(std::string cpr, std::string macroname, time_t oldest) +{ +#ifndef WITHOUT_DB +  std::string query; +  std::stringstream soldest; soldest << oldest; +  try { +    pqxx::work W(*conn); +    query = "SELECT count(*) FROM transactions " +      " WHERE cpr = '" + W.esc(cpr) + "' " +      " AND macro = '" + W.esc(macroname) + "' " +      " AND timestamp >= " + soldest.str() +      ; +    PRACRO_DEBUG(sql, "Query: %s\n", query.c_str()); +    pqxx::result R = W.exec(query); +    if(R.size() != 1) { +      PRACRO_ERR_LOG(db, "No result set; expected one row with one column\n"); +      return 0; +    } +    unsigned n = (unsigned)atol((*R.begin())[0].c_str()); +    PRACRO_DEBUG(db, "Found %u commits for %s(%s) from %ld\n", n, cpr.c_str(), macroname.c_str(), oldest); +    return n; +  } catch (std::exception &e) { +    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); +  } +#else +#ifdef WITH_DEBUG +  PRACRO_DEBUG(db, "Returning 0 commits without database\n"); +#endif/*WITH_DEBUG*/ +#endif/*WITHOUT_DB*/ +  return 0; +} + diff --git a/server/src/pracrodaopgsql.h b/server/src/pracrodaopgsql.h new file mode 100644 index 0000000..e5d75d5 --- /dev/null +++ b/server/src/pracrodaopgsql.h @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/* + *  This file is part of Pracro. + * + *  Pracro is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  Pracro is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Pracro; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. + */ +#ifndef __PRACRO_PRACRODAOPGSQL_H +#define __PRACRO_PRACRODAOPGSQL_H + +#include "pracrodao.h" +#include <pqxx/pqxx> + +class PracroDAOPgsql : public PracroDAO +{ +public: +  PracroDAOPgsql(std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname); +  virtual ~PracroDAOPgsql(); + +  virtual void commitTransaction(std::string user, std::string cpr, Macro ¯o, Fields &fields, time_t now); +  virtual Values getLatestValues(std::string cpr, Macro *macro, Fieldnames &fieldnames, time_t oldest); +  virtual unsigned nrOfCommits(std::string cpr, std::string macroname, time_t oldest); + +private: +  pqxx::connection  *conn; +}; + +#endif diff --git a/server/src/saxparser.cc b/server/src/saxparser.cc index fc1803e..f728928 100644 --- a/server/src/saxparser.cc +++ b/server/src/saxparser.cc @@ -25,6 +25,7 @@   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.   */  #include "saxparser.h" +#include "debug.h"  #include <string.h> @@ -73,7 +74,7 @@ SAXParser::SAXParser()  {    p = XML_ParserCreate(NULL);    if(!p) { -    fprintf(stderr, "Couldn't allocate memory for parser\n"); +    PRACRO_ERR_LOG(sax, "Couldn't allocate memory for parser\n");      // throw Exception(...);      return;    } @@ -114,7 +115,7 @@ int SAXParser::parse()  bool SAXParser::parse(char *data, size_t size)  { -  printf("parse %d bytes\n", size); +  PRACRO_DEBUG(sax, "parse %d bytes\n", size);    bufferbytes = size;    totalbytes += bufferbytes; @@ -133,7 +134,7 @@ bool SAXParser::parse(char *data, size_t size)      }    } -  if(done) printf("Got END_OF_DOCUMENT [%s] at %ld\n", outertag.c_str(), XML_GetCurrentByteIndex(p)); +  if(done) PRACRO_DEBUG(sax, "Got END_OF_DOCUMENT [%s] at %ld\n", outertag.c_str(), XML_GetCurrentByteIndex(p));    return done;  } diff --git a/server/src/server.cc b/server/src/server.cc index 3e41d67..b49f5b2 100644 --- a/server/src/server.cc +++ b/server/src/server.cc @@ -93,9 +93,10 @@ static std::string handleTransaction(Transaction *transaction,          mp.parse();          Macro *macro = mp.getMacro(); -        db->commit(transaction->user, transaction->cpr, *macro, commit.fields); -                  std::string resume = resume_parser(macro->resume, commit); +	commit.fields["journal.resume"] = resume; +        db->commitTransaction(transaction->user, transaction->cpr, *macro, commit.fields); +                  bool store_in_journal = true;          // We always need to store in journal! @@ -108,9 +109,6 @@ static std::string handleTransaction(Transaction *transaction,                           resume.c_str(), resume.length());          } -        db->putResume(transaction->user, transaction->cpr, -                      *macro, resume, time(NULL), store_in_journal); -          i++;        }      } @@ -122,7 +120,7 @@ static std::string handleTransaction(Transaction *transaction,      while(i != transaction->requests.end()) {        Request &request = *i; -      printf("Handling request - macro: %s, course: %s\n", +      PRACRO_DEBUG(server, "Handling request - macro: %s, course: %s\n",               request.macro.c_str(), request.course.c_str());        // Read and parse the template file. @@ -219,7 +217,7 @@ static std::string handleTransaction(Transaction *transaction,          if(completed) {            answer += "      <resume>"; -          answer += db->getResume(transaction->cpr, macro.attributes["name"], time(NULL) - Conf::db_max_ttl); +          answer += db->getResume(transaction->cpr, macro, time(NULL) - Conf::db_max_ttl);            answer += "</resume>\n";          } @@ -238,14 +236,12 @@ static std::string handleTransaction(Transaction *transaction,      answer += "</pracro>\n"; -  } catch( PGSTD::runtime_error &e ) { -    answer = error_box(xml_encode(std::string("PostgreSQL server error:\n") + e.what()));    } catch( std::exception &e ) {      answer = error_box(xml_encode(e.what()));    } -  printf("Done handling transaction\n"); - +  PRACRO_DEBUG(server, "Done handling transaction\n"); +  PRACRO_DEBUG(serverxml, "%s\n", answer.c_str());    return answer;  } @@ -257,7 +253,7 @@ static void handleConnection(TCPSocket *socket)    pentominos_socket.connect(Conf::pentominos_addr, Conf::pentominos_port);  #endif/*WITHOUT_PENTOMINOS*/ -  Database db(Conf::database_addr, Conf::database_user, Conf::database_passwd); +  Database *db = new Database("pgsql", Conf::database_addr, "", Conf::database_user, Conf::database_passwd, "");    ssize_t size;    char buf[4096]; @@ -268,7 +264,7 @@ static void handleConnection(TCPSocket *socket)    //  while( (size = socket->read(buf, sizeof(buf))) != -1) {    while( (size = socket->read(buf, sizeof(buf))) > 0) { -    printf("Read %d bytes from network\n", size); +    PRACRO_DEBUG(server, "Read %d bytes from network\n", size);      while(size) { @@ -280,13 +276,11 @@ static void handleConnection(TCPSocket *socket)          parser = new TransactionParser(transaction);        } -      printf("Got %d bytes in read loop\n", size); +      PRACRO_DEBUG(server, "Got %d bytes in read loop\n", size);        if(parser->parse(buf, size)) { -        printf("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" -               "!! Got complete XML document %d bytes used, %d bytes in current buffer.\n" -               "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", parser->usedBytes(), size); +        PRACRO_DEBUG(server, "Got complete XML document %d bytes used, %d bytes in current buffer.\n", parser->usedBytes(), size); -        socket->write(handleTransaction(transaction, &pentominos_socket, &db)); +        socket->write(handleTransaction(transaction, &pentominos_socket, db));          size = size - parser->usedBytes();          delete transaction; transaction = NULL; @@ -297,7 +291,7 @@ static void handleConnection(TCPSocket *socket)        if(size > 0) {          strcpy(buf, buf + size); -        printf("Replaying %d bytes.\n", size); +        PRACRO_DEBUG(server, "Replaying %d bytes.\n", size);        }      }    } @@ -312,7 +306,7 @@ static void handleConnection(TCPSocket *socket)      parser = NULL;    } -  printf("Out of read loop!\n"); +  PRACRO_DEBUG(server, "Out of read loop!\n");  }  //#define NON_FORKING @@ -327,7 +321,7 @@ void server()      socket = new TCPSocket("Listen socket");      socket->listen(port);    } catch (Exception &e) { -    fprintf(stderr, "Error during parsing:\n%s\n", +    PRACRO_ERR_LOG(server, "Error during parsing:\n%s\n",              e.what());      delete socket;      socket = NULL; @@ -354,7 +348,7 @@ void server()  #ifndef NON_FORKING        switch(fork()) {        case -1: // error -        fprintf(stderr, "Could not fork: %s\n", strerror(errno)); +        PRACRO_ERR_LOG(server, "Could not fork: %s\n", strerror(errno));          break;        case 0: // child @@ -377,7 +371,7 @@ void server()    //socket->shutdown();    delete socket; -  printf("Server gracefully shut down.\n"); +  PRACRO_DEBUG(server, "Server gracefully shut down.\n");  } diff --git a/server/src/templateparser.cc b/server/src/templateparser.cc index 412924c..140051e 100644 --- a/server/src/templateparser.cc +++ b/server/src/templateparser.cc @@ -26,6 +26,7 @@   */  #include "templateparser.h"  #include "configuration.h" +#include "debug.h"  // For assert  #include <assert.h> @@ -53,15 +54,25 @@ void TemplateParser::error(const char* fmt, ...)  {    // TODO: Throw exception here. -  fprintf(stderr, "Error in TemplateParser: "); -    va_list argp; -  va_start(argp, fmt); -  vfprintf(stderr, fmt, argp); -  va_end(argp); - -  fprintf(stderr, "\n"); - +  int s = 256; +  char *p = new char[s]; +  /* See printf(3) for details on the loop how to get the right buffersize. */ +  while(1) { +    va_start(argp, fmt); +    int n = vsnprintf(p, s, fmt, argp); +    va_end(argp); +    if(n > -1 && n < s) +      break; +    if(n > -1)  /* glibc 2.1 */ +      s = n+1;  /* precisely what is needed */ +    else        /* glibc 2.0 */ +      s *= 2;   /* twice the old size */ +    delete[] p; +    p = new char[s]; +  } +  PRACRO_ERR_LOG(macro, "Error in TemplateParser: %s\n", p); +  delete[] p;    throw Exception("Error in TemplateParser");  } @@ -73,7 +84,7 @@ TemplateParser::TemplateParser(std::string course)    file = Conf::xml_basedir + "/templates/" + course + ".xml"; -  printf("Using template file: %s\n", file.c_str()); +  PRACRO_DEBUG(macro, "Using template file: %s\n", file.c_str());    fd = open(file.c_str(), O_RDONLY);    if(fd == -1) error("Could not open file %s", file.c_str()); @@ -133,12 +144,12 @@ void TemplateParser::endTag(std::string name)  int TemplateParser::readData(char *data, size_t size)  {    if(fd == -1) { -    fprintf(stderr, "Invalid file descriptor.\n"); fflush(stderr); +    PRACRO_ERR_LOG(macro, "Invalid file descriptor.\n");      return 0;    }    ssize_t r = read(fd, data, size);    if(r == -1) { -    printf("Could not read...%s\n", strerror(errno)); fflush(stdout); +    PRACRO_ERR_LOG(macro, "Could not read...%s\n", strerror(errno));      return 0;    }    return r; | 
