From bbe2b5f899a9c1bd7c99181f8702ec03c60f3028 Mon Sep 17 00:00:00 2001 From: bertho Date: Tue, 10 Feb 2009 13:39:25 +0000 Subject: - Rewrite part of the database backend setup and abstraction - Fix some printf's into debug statements --- server/src/Makefile.am | 5 + server/src/database.cc | 286 ++----------------------------------------- server/src/database.h | 82 +++++-------- server/src/dbtypes.h | 40 ++++++ server/src/journal_commit.cc | 22 ++-- server/src/pracrodao.cc | 35 ++++++ server/src/pracrodao.h | 46 +++++++ server/src/pracrodaopgsql.cc | 256 ++++++++++++++++++++++++++++++++++++++ server/src/pracrodaopgsql.h | 40 ++++++ server/src/saxparser.cc | 7 +- server/src/server.cc | 40 +++--- server/src/templateparser.cc | 33 +++-- 12 files changed, 513 insertions(+), 379 deletions(-) create mode 100644 server/src/dbtypes.h create mode 100644 server/src/pracrodao.cc create mode 100644 server/src/pracrodao.h create mode 100644 server/src/pracrodaopgsql.cc create mode 100644 server/src/pracrodaopgsql.h 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 #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, , %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 - -#include - -#include +#include +#include "pracrodao.h" #include "transaction.h" #include "template.h" -#include - -#include - -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 + +#include +#include +#include +#include + +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 +#include "debug.h" + // for gethostbyname #include @@ -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 +#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 + +#include + +#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, , %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 + +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 @@ -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 += " "; - 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 += "\n"; } @@ -238,14 +236,12 @@ static std::string handleTransaction(Transaction *transaction, answer += "\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 @@ -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; -- cgit v1.2.3