summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
authorbertho <bertho>2009-02-10 13:39:25 +0000
committerbertho <bertho>2009-02-10 13:39:25 +0000
commitbbe2b5f899a9c1bd7c99181f8702ec03c60f3028 (patch)
tree6da04c039a0e04930bb1c7e4fea5b14e52f9bc57 /server
parent3f1b036458bf9b348b65c36fa30ecb177800d703 (diff)
- Rewrite part of the database backend setup and abstraction
- Fix some printf's into debug statements
Diffstat (limited to 'server')
-rw-r--r--server/src/Makefile.am5
-rw-r--r--server/src/database.cc286
-rw-r--r--server/src/database.h82
-rw-r--r--server/src/dbtypes.h40
-rw-r--r--server/src/journal_commit.cc22
-rw-r--r--server/src/pracrodao.cc35
-rw-r--r--server/src/pracrodao.h46
-rw-r--r--server/src/pracrodaopgsql.cc256
-rw-r--r--server/src/pracrodaopgsql.h40
-rw-r--r--server/src/saxparser.cc7
-rw-r--r--server/src/server.cc40
-rw-r--r--server/src/templateparser.cc33
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 &macro,
- Fields &fields,
- time_t now = time(NULL));
+ void commitTransaction(std::string user, std::string cpr, Macro &macro, 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 &macro, 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, &macro, 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 &macro, 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 &macro, 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;