/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set et sw=2 ts=2: */ /*************************************************************************** * pracrodaopgsql.cc * * Wed Feb 11 11:18:26 CET 2009 * Copyright 2009 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * 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 "pracrodaopgsql.h" /* * 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 #ifndef WITHOUT_DB #include #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"); try { conn = new pqxx::connection(cs); } catch(std::exception &e) { PRACRO_ERR_LOG(db, "Postgresql init failed: %s\n", e.what()); conn = NULL; } 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 patientid, Macro &_macro, Fields &fields, time_t now) { PRACRO_DEBUG(db, "(%s, %s, %s, <%u fields>, %ld)\n", user.c_str(), patientid.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; std::string ts; try { pqxx::work W(*conn); ts = "INSERT INTO transactions (uid, patientid, macro, version, \"timestamp\", \"user\") VALUES (" " nextval('trseq'), " " '" + W.esc(patientid) + "', " " '" + 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()); } } /* * 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.uid = f.transaction * AND t.patientid = '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.uid = ff.transaction * AND tt.patientid = '1505050505' */ Values PracroDAOPgsql::getLatestValues(std::string patientid, Macro *macro, Fieldnames &fieldnames, time_t oldest) { PRACRO_DEBUG(db, "(%s, %s, <%u fieldnames>, %ld)\n", patientid.c_str(), macro ? macro->attributes["name"].c_str() : "(null)", fieldnames.size(), oldest); if(!conn) PRACRO_DEBUG(db, "No pgsql connection\n"); Values values; 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.patientid = '" + W.esc(patientid) + "' " + 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.patientid = '" + W.esc(patientid) + "' " ; 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()); } return values; } unsigned PracroDAOPgsql::nrOfCommits(std::string patientid, std::string macroname, time_t oldest) { std::string query; std::stringstream soldest; soldest << oldest; try { pqxx::work W(*conn); query = "SELECT count(*) FROM transactions " " WHERE patientid = '" + W.esc(patientid) + "' " " 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, patientid.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()); } return 0; } void PracroDAOPgsql::addFieldname(std::string name, std::string description) { std::stringstream timestamp; timestamp << time(NULL); std::string ts; try { pqxx::work W(*conn); ts = "INSERT INTO fieldnames (name, description, \"timestamp\") VALUES (" " '" + W.esc(name) + "', " " '" + W.esc(description) + "', " " '" + W.esc(timestamp.str()) + "' " ")" ; PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str()); 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()); } } void PracroDAOPgsql::delFieldname(std::string name) { std::string ts; try { pqxx::work W(*conn); ts = "DELETE FROM fieldnames WHERE name=" "'" + W.esc(name) + "' "; PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str()); 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()); } } std::vector PracroDAOPgsql::getFieldnames() { if(!conn) PRACRO_DEBUG(db, "No pgsql connection\n"); std::vector fieldnames; std::string query; try { pqxx::work W(*conn); query = "SELECT * FROM fieldnames"; 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()) { Fieldname f; f.name = (*ri)[0].c_str(); f.description = (*ri)[1].c_str(); f.timestamp = atol((*ri)[2].c_str()); fieldnames.push_back(f); ri++; } } catch (std::exception &e) { PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); } return fieldnames; } #endif/*WITHOUT_DB*/ #ifdef TEST_PRACRODAOPGSQL #include "configuration.h" #include "exception.h" int main() { #ifndef WITHOUT_DB try { PracroDAOPgsql db(Conf::database_addr, "", Conf::database_user, Conf::database_passwd, ""); } catch(Exception &e) { printf("ERROR: %s\n", e.what()); return 1; } #endif/*WITHOUT_DB*/ return 0; } #endif/*TEST_PRACRODAOPGSQL*/